Skip to content
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
5 changes: 5 additions & 0 deletions docs/hazmat/primitives/hpke.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ specifying auxiliary authenticated information.
ML-KEM-768. Post-quantum secure. Only available on backends that
support ML-KEM.

.. attribute:: MLKEM1024

ML-KEM-1024. Post-quantum secure. Only available on backends that
support ML-KEM.

.. class:: KDF

An enumeration of key derivation functions.
Expand Down
13 changes: 9 additions & 4 deletions src/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class KEM:
P384: KEM
P521: KEM
MLKEM768: KEM
MLKEM1024: KEM

class KDF:
HKDF_SHA256: KDF
Expand All @@ -31,15 +32,17 @@ class Suite:
plaintext: Buffer,
public_key: x25519.X25519PublicKey
| ec.EllipticCurvePublicKey
| mlkem.MLKEM768PublicKey,
| mlkem.MLKEM768PublicKey
| mlkem.MLKEM1024PublicKey,
info: Buffer | None = None,
) -> bytes: ...
def decrypt(
self,
ciphertext: Buffer,
private_key: x25519.X25519PrivateKey
| ec.EllipticCurvePrivateKey
| mlkem.MLKEM768PrivateKey,
| mlkem.MLKEM768PrivateKey
| mlkem.MLKEM1024PrivateKey,
info: Buffer | None = None,
) -> bytes: ...

Expand All @@ -48,7 +51,8 @@ def _encrypt_with_aad(
plaintext: Buffer,
public_key: x25519.X25519PublicKey
| ec.EllipticCurvePublicKey
| mlkem.MLKEM768PublicKey,
| mlkem.MLKEM768PublicKey
| mlkem.MLKEM1024PublicKey,
info: Buffer | None = None,
aad: Buffer | None = None,
) -> bytes: ...
Expand All @@ -57,7 +61,8 @@ def _decrypt_with_aad(
ciphertext: Buffer,
private_key: x25519.X25519PrivateKey
| ec.EllipticCurvePrivateKey
| mlkem.MLKEM768PrivateKey,
| mlkem.MLKEM768PrivateKey
| mlkem.MLKEM1024PrivateKey,
info: Buffer | None = None,
aad: Buffer | None = None,
) -> bytes: ...
68 changes: 51 additions & 17 deletions src/rust/src/backend/hpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ mod kem_params {
pub const MLKEM768_ID: u16 = 0x0041;
pub const MLKEM768_NSECRET: usize = 32;
pub const MLKEM768_NENC: usize = 1088;

pub const MLKEM1024_ID: u16 = 0x0042;
pub const MLKEM1024_NSECRET: usize = 32;
pub const MLKEM1024_NENC: usize = 1568;
}

mod kdf_params {
Expand Down Expand Up @@ -89,6 +93,7 @@ pub(crate) enum KEM {
P384,
P521,
MLKEM768,
MLKEM1024,
}

impl KEM {
Expand Down Expand Up @@ -153,6 +158,7 @@ impl KEM {
KEM::P384 => kem_params::P384_ID,
KEM::P521 => kem_params::P521_ID,
KEM::MLKEM768 => kem_params::MLKEM768_ID,
KEM::MLKEM1024 => kem_params::MLKEM1024_ID,
}
}

Expand All @@ -163,6 +169,7 @@ impl KEM {
KEM::P384 => kem_params::P384_NSECRET,
KEM::P521 => kem_params::P521_NSECRET,
KEM::MLKEM768 => kem_params::MLKEM768_NSECRET,
KEM::MLKEM1024 => kem_params::MLKEM1024_NSECRET,
}
}

Expand All @@ -173,6 +180,7 @@ impl KEM {
KEM::P384 => kem_params::P384_NENC,
KEM::P521 => kem_params::P521_NENC,
KEM::MLKEM768 => kem_params::MLKEM768_NENC,
KEM::MLKEM1024 => kem_params::MLKEM1024_NENC,
}
}

Expand Down Expand Up @@ -221,6 +229,15 @@ impl KEM {
));
}
}
KEM::MLKEM1024 => {
if !key.is_instance(&types::MLKEM1024_PUBLIC_KEY.get(py)?)? {
return Err(CryptographyError::from(
pyo3::exceptions::PyTypeError::new_err(
"Expected MLKEM1024PublicKey for KEM.MLKEM1024",
),
));
}
}
}
Ok(())
}
Expand Down Expand Up @@ -270,6 +287,15 @@ impl KEM {
));
}
}
KEM::MLKEM1024 => {
if !key.is_instance(&types::MLKEM1024_PRIVATE_KEY.get(py)?)? {
return Err(CryptographyError::from(
pyo3::exceptions::PyTypeError::new_err(
"Expected MLKEM1024PrivateKey for KEM.MLKEM1024",
),
));
}
}
}
Ok(())
}
Expand All @@ -284,7 +310,7 @@ impl KEM {
pyo3::Bound<'p, pyo3::types::PyBytes>,
)> {
match self {
KEM::MLKEM768 => {
KEM::MLKEM768 | KEM::MLKEM1024 => {
let result = pk_r.call_method0(pyo3::intern!(py, "encapsulate"))?;
Ok(result.extract()?)
}
Expand All @@ -302,7 +328,7 @@ impl KEM {
kem_suite_id: &[u8; 5],
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
match self {
KEM::MLKEM768 => {
KEM::MLKEM768 | KEM::MLKEM1024 => {
let enc_bytes = pyo3::types::PyBytes::new(py, enc);
Ok(sk_r
.call_method1(pyo3::intern!(py, "decapsulate"), (enc_bytes,))?
Expand Down Expand Up @@ -446,8 +472,8 @@ impl KEM {
.into_any(),
)
}
KEM::MLKEM768 => {
unreachable!("ML-KEM-768 does not generate an ephemeral DH key")
KEM::MLKEM768 | KEM::MLKEM1024 => {
unreachable!("ML-KEM does not generate an ephemeral DH key")
}
}
}
Expand All @@ -470,8 +496,8 @@ impl KEM {
),
)?
.extract()?),
KEM::MLKEM768 => {
unreachable!("ML-KEM-768 public keys are not serialized via this path")
KEM::MLKEM768 | KEM::MLKEM1024 => {
unreachable!("ML-KEM public keys are not serialized via this path")
}
}
}
Expand All @@ -495,8 +521,8 @@ impl KEM {
let secp521r1 = types::SECP521R1.get(py)?.call0()?;
Ok(pyo3::Bound::new(py, ec::from_public_bytes(py, secp521r1, data)?)?.into_any())
}
KEM::MLKEM768 => {
unreachable!("ML-KEM-768 encapsulated key is a ciphertext, not a public key")
KEM::MLKEM768 | KEM::MLKEM1024 => {
unreachable!("ML-KEM encapsulated key is a ciphertext, not a public key")
}
}
}
Expand All @@ -515,8 +541,8 @@ impl KEM {
let ecdh = types::ECDH.get(py)?.call0()?;
Ok(private_key.call_method1(pyo3::intern!(py, "exchange"), (&ecdh, public_key))?)
}
KEM::MLKEM768 => {
unreachable!("ML-KEM-768 does not perform a Diffie-Hellman exchange")
KEM::MLKEM768 | KEM::MLKEM1024 => {
unreachable!("ML-KEM does not perform a Diffie-Hellman exchange")
}
}
}
Expand All @@ -529,8 +555,8 @@ impl KEM {
KEM::X25519 | KEM::P256 => Ok(types::SHA256.get(py)?.call0()?),
KEM::P384 => Ok(types::SHA384.get(py)?.call0()?),
KEM::P521 => Ok(types::SHA512.get(py)?.call0()?),
KEM::MLKEM768 => {
unreachable!("ML-KEM-768 does not use a KEM hash algorithm")
KEM::MLKEM768 | KEM::MLKEM1024 => {
unreachable!("ML-KEM does not use a KEM hash algorithm")
}
}
}
Expand Down Expand Up @@ -994,7 +1020,15 @@ mod tests {
}

#[test]
#[should_panic(expected = "ML-KEM-768 does not generate an ephemeral DH key")]
fn test_mlkem1024_secret_length() {
assert_eq!(
KEM::MLKEM1024.secret_length(),
kem_params::MLKEM1024_NSECRET
);
}

#[test]
#[should_panic(expected = "ML-KEM does not generate an ephemeral DH key")]
fn test_mlkem768_generate_key_unreachable() {
pyo3::Python::initialize();

Expand All @@ -1004,7 +1038,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "ML-KEM-768 public keys are not serialized via this path")]
#[should_panic(expected = "ML-KEM public keys are not serialized via this path")]
fn test_mlkem768_serialize_public_key_unreachable() {
pyo3::Python::initialize();

Expand All @@ -1015,7 +1049,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "ML-KEM-768 encapsulated key is a ciphertext, not a public key")]
#[should_panic(expected = "ML-KEM encapsulated key is a ciphertext, not a public key")]
fn test_mlkem768_deserialize_public_key_unreachable() {
pyo3::Python::initialize();

Expand All @@ -1025,7 +1059,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "ML-KEM-768 does not perform a Diffie-Hellman exchange")]
#[should_panic(expected = "ML-KEM does not perform a Diffie-Hellman exchange")]
fn test_mlkem768_exchange_unreachable() {
pyo3::Python::initialize();

Expand All @@ -1036,7 +1070,7 @@ mod tests {
}

#[test]
#[should_panic(expected = "ML-KEM-768 does not use a KEM hash algorithm")]
#[should_panic(expected = "ML-KEM does not use a KEM hash algorithm")]
fn test_mlkem768_kem_hash_algorithm_unreachable() {
pyo3::Python::initialize();

Expand Down
8 changes: 8 additions & 0 deletions src/rust/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,14 @@ pub static MLKEM768_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mlkem",
&["MLKEM768PrivateKey"],
);
pub static MLKEM1024_PUBLIC_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mlkem",
&["MLKEM1024PublicKey"],
);
pub static MLKEM1024_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.mlkem",
&["MLKEM1024PrivateKey"],
);

pub static ED25519_PRIVATE_KEY: LazyPyImport = LazyPyImport::new(
"cryptography.hazmat.primitives.asymmetric.ed25519",
Expand Down
Loading