Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hpke_open and generate_hpke_keypair functions #15

Merged
merged 1 commit into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions hpke_spec_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,10 @@ def test_wrong_pk_size(self):
raise AssertionError(
"hpke_seal failed to raise Exception on malformed public key"
)

def test_hpke_roundtrip(self):
skR, pkR = hpke_spec.generate_hpke_keypair()
ptxt = b"my name is Vincent Law"
ctxt = hpke_spec.hpke_seal(pkR, ptxt)
ptxt_roundtrip = hpke_spec.hpke_open(skR, ctxt)
assert ptxt == ptxt_roundtrip
65 changes: 61 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use hacspec_lib::{Seq, U8};
use hpke::{AdditionalData, HPKECiphertext, HPKEConfig, HpkePublicKey, HpkeSeal, Mode};
use hacspec_lib::{ByteSeq, Seq, U8};
use hpke::{
AdditionalData, Ciphertext, HPKECiphertext, HPKEConfig, HpkeOpen, HpkePrivateKey,
HpkePublicKey, HpkeSeal, KemOutput, Mode,
};
use hpke_aead::AEAD;
use hpke_errors::HpkeError;
use hpke_kdf::{Info, KDF};
use hpke_kem::{Randomness, KEM};
use hpke_kdf::{Info, InputKeyMaterial, KDF};
use hpke_kem::{DeriveKeyPair, Nsk, Randomness, KEM};
use pyo3::exceptions::PyRuntimeError;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
Expand All @@ -17,6 +20,28 @@ fn get_default_hpke_config() -> HPKEConfig {
HPKEConfig(mode, kem, kdf, aead)
}

fn hpke_open_bytes(ctxtb: &[u8], skb: &[u8]) -> Result<Vec<u8>, HpkeError> {
let hpke_config = get_default_hpke_config();
let kem_output = KemOutput::from_public_slice(&ctxtb[..32]);
let ctxt = Ciphertext::from_public_slice(&ctxtb[32..]);
let ciphertext = HPKECiphertext(kem_output, ctxt);
let sk_r = HpkePrivateKey::from_public_slice(skb);
let info = Info::new(0);
let aad = AdditionalData::new(0);
let result: ByteSeq = HpkeOpen(
hpke_config,
&ciphertext,
&sk_r,
&info,
&aad,
None,
None,
None,
)?;
let plaintext = result.into_native();
Ok(plaintext)
}

fn hpke_seal_bytes(pkb: &[u8], ptxtb: &[u8]) -> Result<Vec<u8>, HpkeError> {
let hpke_config = get_default_hpke_config();
let pk = HpkePublicKey::from_public_slice(pkb);
Expand All @@ -43,6 +68,26 @@ fn hpke_seal_bytes(pkb: &[u8], ptxtb: &[u8]) -> Result<Vec<u8>, HpkeError> {
Ok(encapsulated)
}

fn generate_keypair() -> Result<(Vec<u8>, Vec<u8>), HpkeError> {
let hpke_config = get_default_hpke_config();
let kem = hpke_config.1;
let nbytes = Nsk(kem);
let mut rand_bytes = vec![0u8; nbytes];
OsRng.fill_bytes(&mut rand_bytes);
let randomness = InputKeyMaterial::from_public_slice(&rand_bytes);
let keypair = DeriveKeyPair(kem, &randomness)?;
Ok((keypair.0.into_native(), keypair.1.into_native()))
}

#[pyfunction]
fn generate_hpke_keypair(py: Python) -> PyResult<(&PyBytes, &PyBytes)> {
let keypair_bytes = generate_keypair()
.map_err(|hpke_error| PyRuntimeError::new_err(format!("{hpke_error:?}")))?;
let sk_py = PyBytes::new(py, &keypair_bytes.0);
let pk_py = PyBytes::new(py, &keypair_bytes.1);
Ok((sk_py, pk_py))
}

/// Python binding to hpke-spec's Single-Shot API function hpke::HpkeSeal
#[pyfunction]
fn hpke_seal<'p>(py: Python<'p>, pk_py: &PyBytes, ptxt_py: &PyBytes) -> PyResult<&'p PyBytes> {
Expand All @@ -53,9 +98,21 @@ fn hpke_seal<'p>(py: Python<'p>, pk_py: &PyBytes, ptxt_py: &PyBytes) -> PyResult
Ok(PyBytes::new(py, &ciphertext_bytes))
}

/// Python binding to hpke-spec's Single-Shot API function hpke::HpkeOpen
#[pyfunction]
fn hpke_open<'p>(py: Python<'p>, sk_py: &PyBytes, ctxt_py: &PyBytes) -> PyResult<&'p PyBytes> {
let skb = sk_py.as_bytes();
let ctxtb = ctxt_py.as_bytes();
let plaintext_bytes = hpke_open_bytes(ctxtb, skb)
.map_err(|hpke_error| PyRuntimeError::new_err(format!("{hpke_error:?}")))?;
Ok(PyBytes::new(py, &plaintext_bytes))
}

/// A Python module implemented in Rust.
#[pymodule]
fn hpke_spec(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(generate_hpke_keypair, m)?)?;
m.add_function(wrap_pyfunction!(hpke_open, m)?)?;
m.add_function(wrap_pyfunction!(hpke_seal, m)?)?;
Ok(())
}