From 73351ee32338e9f64d024aac086b1c3cd8f2ac02 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 5 Aug 2024 14:55:48 +0200 Subject: [PATCH 01/29] Crypto/HPKE: introduce DHKEM computation + tests --- bindings/C/mla.h | 1 + bindings/C/mla.hpp | 1 + bindings/C/src/lib.rs | 2 + mla/Cargo.toml | 2 +- mla/src/crypto/hpke.rs | 154 +++++++++++++++++++++++++++++++++++++++++ mla/src/crypto/mod.rs | 1 + mla/src/errors.rs | 9 +++ 7 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 mla/src/crypto/hpke.rs diff --git a/bindings/C/mla.h b/bindings/C/mla.h index cd3783ee..08e044f3 100644 --- a/bindings/C/mla.h +++ b/bindings/C/mla.h @@ -41,6 +41,7 @@ enum MLAStatus { MLA_STATUS_DUPLICATE_FILENAME = 1376256, MLA_STATUS_AUTHENTICATED_DECRYPTION_WRONG_TAG = 1441792, MLA_STATUS_HKDF_INVALID_KEY_LENGTH = 1507328, + MLA_STATUS_HPKE_ERROR = 98304, MLA_STATUS_CURVE25519_PARSER_ERROR = 15794176, }; typedef uint64_t MLAStatus; diff --git a/bindings/C/mla.hpp b/bindings/C/mla.hpp index bb91b910..499f0a28 100644 --- a/bindings/C/mla.hpp +++ b/bindings/C/mla.hpp @@ -42,6 +42,7 @@ enum class MLAStatus : uint64_t { MLA_STATUS_DUPLICATE_FILENAME = 1376256, MLA_STATUS_AUTHENTICATED_DECRYPTION_WRONG_TAG = 1441792, MLA_STATUS_HKDF_INVALID_KEY_LENGTH = 1507328, + MLA_STATUS_HPKE_ERROR = 98304, MLA_STATUS_CURVE25519_PARSER_ERROR = 15794176, }; diff --git a/bindings/C/src/lib.rs b/bindings/C/src/lib.rs index 5e3cc0c0..85227165 100644 --- a/bindings/C/src/lib.rs +++ b/bindings/C/src/lib.rs @@ -57,6 +57,7 @@ pub enum MLAStatus { DuplicateFilename = 0x150000, AuthenticatedDecryptionWrongTag = 0x160000, HKDFInvalidKeyLength = 0x170000, + HPKEError = 0x18000, Curve25519ParserError = 0xF10000, } /// Implemented by the developper. Takes a buffer of a certain number of bytes of MLA @@ -193,6 +194,7 @@ impl From for MLAStatus { MLAError::DuplicateFilename => MLAStatus::DuplicateFilename, MLAError::AuthenticatedDecryptionWrongTag => MLAStatus::AuthenticatedDecryptionWrongTag, MLAError::HKDFInvalidKeyLength => MLAStatus::HKDFInvalidKeyLength, + MLAError::HPKEError(_) => MLAStatus::HPKEError, } } } diff --git a/mla/Cargo.toml b/mla/Cargo.toml index ee2bf249..05287696 100644 --- a/mla/Cargo.toml +++ b/mla/Cargo.toml @@ -36,7 +36,7 @@ zeroize = { version = "1", default-features = false} # Post-quantum ml-kem = { version = "0.1.1", default-features = false } kem = {version = "0.3.0-pre.0", default-features = false } - +hpke = { version = "0.12", default-features = false, features = ["alloc", "x25519"] } [dev-dependencies] hex-literal = { version = "0.4", default-features = false} diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs new file mode 100644 index 00000000..37d33d82 --- /dev/null +++ b/mla/src/crypto/hpke.rs @@ -0,0 +1,154 @@ +use hpke::Deserializable; +/// Implements RFC 9180 for MLA needs +use hpke::{ + kem::X25519HkdfSha256, + Kem as KemTrait, +}; +use rand::{CryptoRng, RngCore}; +use x25519_dalek::PublicKey as X25519PublicKey; + +use crate::errors::Error; +use crate::layers::encrypt::get_crypto_rng; + +type Kem = X25519HkdfSha256; +type WrappedPublicKey = ::PublicKey; +type WrappedPrivateKey = ::PrivateKey; +type DHKEMSharedSecret = hpke::kem::SharedSecret; +type DHKEMCiphertext = ::EncappedKey; + +// ----- DHKEM(X25519) ----- +// This implementation wraps https://github.com/rozbb/rust-hpke, which has been partially reviewed + +/// Provides DHKEM encapsulation over X25519 curve (RFC 9180 §4.1) from a given CryptoRng +/// +/// Return a shared secret and the corresponding ciphertext +pub(crate) fn dhkem_encap_from_rng( + pubkey: &X25519PublicKey, + csprng: &mut (impl CryptoRng + RngCore), +) -> Result<(DHKEMSharedSecret, DHKEMCiphertext), Error> { + let wrapped = WrappedPublicKey::from_bytes(&pubkey.to_bytes())?; + Ok(X25519HkdfSha256::encap(&wrapped, None, csprng)?) +} + +/// Provides DHKEM encapsulation over X25519 curve (RFC 9180 §4.1) +/// +/// Return a shared secret and the corresponding ciphertext +pub(crate) fn dhkem_encap( + pubkey: &X25519PublicKey, +) -> Result<(DHKEMSharedSecret, DHKEMCiphertext), Error> { + dhkem_encap_from_rng(pubkey, &mut get_crypto_rng()) +} + +/// Provides DHKEM decapsulation over X25519 curve (RFC 9180 §4.1) +/// +/// Returns the shared secret +pub(crate) fn dhkem_decap( + encapped_key: &DHKEMCiphertext, + private_key: &x25519_dalek::StaticSecret, +) -> Result { + let wrapped = WrappedPrivateKey::from_bytes(&private_key.to_bytes())?; + Ok(X25519HkdfSha256::decap(&wrapped, None, encapped_key)?) +} + +#[cfg(test)] +mod tests { + use std::io; + use std::io::{BufReader, Cursor}; + + use super::*; + use hex_literal::hex; + use hpke::Serializable; + use rand::{CryptoRng, RngCore}; + use x25519_dalek::StaticSecret; + + /// Rng used for tests, mocking the RngCore and CryptoRng trait + /// This RNG always returns the bytes provided during instanciation + /// + /// DO NOT USE OUTSIDE TESTS + struct MockRng { + buf: BufReader>>, + } + + impl MockRng { + fn new(data: &[u8]) -> Self { + MockRng { + buf: BufReader::new(Cursor::new(data.to_vec())), + } + } + } + + impl RngCore for MockRng { + fn fill_bytes(&mut self, mut dest: &mut [u8]) { + io::copy(&mut self.buf, &mut dest).unwrap(); + } + + fn next_u32(&mut self) -> u32 { + let mut buf = [0u8; 4]; + self.fill_bytes(&mut buf); + u32::from_le_bytes(buf) + } + + fn next_u64(&mut self) -> u64 { + let mut buf = [0u8; 8]; + self.fill_bytes(&mut buf); + u64::from_le_bytes(buf) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + self.fill_bytes(dest); + Ok(()) + } + } + + impl CryptoRng for MockRng {} + + /// RFC 9180 §A.1.1 + const RFC_IKME: [u8; 32] = + hex!("7268600d403fce431561aef583ee1613527cff655c1343f29812e66706df3234"); + const RFC_PKEM: [u8; 32] = + hex!("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"); + const RFC_SKEM: [u8; 32] = + hex!("52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"); + + const RFC_IKMR: [u8; 32] = + hex!("6db9df30aa07dd42ee5e8181afdb977e538f5e1fec8a06223f33f7013e525037"); + const RFC_PKRM: [u8; 32] = + hex!("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"); + const RFC_SKRM: [u8; 32] = + hex!("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8"); + + const RFC_ENC: [u8; 32] = + hex!("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"); + const RFC_SHARED_SECRET: [u8; 32] = + hex!("fe0e18c9f024ce43799ae393c7e8fe8fce9d218875e8227b0187c04e7d2ea1fc"); + #[test] + /// RFC 9180 §A.1.1 + /// + /// These test vectors are already tested by `hpke` crates, but we ensure + /// there is no regression change, even if we later change our base crate + fn rfc9180_dhkem_vector_tests() { + // Key derivation + let (privkey_em, pubkey_em) = X25519HkdfSha256::derive_keypair(&RFC_IKME); + assert_eq!(&pubkey_em.to_bytes().as_ref(), &RFC_PKEM); + assert_eq!(&privkey_em.to_bytes().as_ref(), &RFC_SKEM); + + let (privkey_rm, pubkey_rm) = X25519HkdfSha256::derive_keypair(&RFC_IKMR); + assert_eq!(&pubkey_rm.to_bytes().as_ref(), &RFC_PKRM); + assert_eq!(&privkey_rm.to_bytes().as_ref(), &RFC_SKRM); + + // DHKEM Encapsulation + let mut rng = MockRng::new(&RFC_IKME); + let (shared_secret, cipher_text) = + dhkem_encap_from_rng(&X25519PublicKey::from(RFC_PKRM), &mut rng).unwrap(); + assert_eq!(&cipher_text.to_bytes().as_ref(), &RFC_ENC); + assert_eq!(&shared_secret.0.to_vec(), &RFC_SHARED_SECRET); + + // DHKEM Decapsulation + let shared_secret = dhkem_decap( + &DHKEMCiphertext::from_bytes(&RFC_ENC).unwrap(), + &StaticSecret::from(RFC_SKRM), + ) + .unwrap(); + assert_eq!(&shared_secret.0.to_vec(), &RFC_SHARED_SECRET); + } +} diff --git a/mla/src/crypto/mod.rs b/mla/src/crypto/mod.rs index f59c2ce4..88a4037e 100644 --- a/mla/src/crypto/mod.rs +++ b/mla/src/crypto/mod.rs @@ -1,4 +1,5 @@ pub mod aesgcm; pub mod ecc; pub mod hash; +mod hpke; pub mod hybrid; diff --git a/mla/src/errors.rs b/mla/src/errors.rs index a98e7826..09fc91c0 100644 --- a/mla/src/errors.rs +++ b/mla/src/errors.rs @@ -1,5 +1,6 @@ use crate::ArchiveFileID; use hkdf::InvalidLength; +use hpke::HpkeError; use std::error; use std::fmt; use std::io; @@ -57,6 +58,8 @@ pub enum Error { AuthenticatedDecryptionWrongTag, /// Unable to expand while using the HKDF HKDFInvalidKeyLength, + /// Error during HPKE computation + HPKEError(HpkeError), } impl fmt::Display for Error { @@ -111,6 +114,12 @@ impl From for Error { } } +impl From for Error { + fn from(error: HpkeError) -> Self { + Error::HPKEError(error) + } +} + impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self { From a3ee8534e9b8bee334919c4073c172442a7baa01 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 5 Aug 2024 16:51:26 +0200 Subject: [PATCH 02/29] Crypto/hpke: wrap rust-hpke encapped-key and provides serialization --- mla/src/crypto/hpke.rs | 59 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs index 37d33d82..0f26759c 100644 --- a/mla/src/crypto/hpke.rs +++ b/mla/src/crypto/hpke.rs @@ -1,10 +1,8 @@ -use hpke::Deserializable; /// Implements RFC 9180 for MLA needs -use hpke::{ - kem::X25519HkdfSha256, - Kem as KemTrait, -}; +use hpke::{kem::X25519HkdfSha256, Kem as KemTrait}; +use hpke::{Deserializable, Serializable}; use rand::{CryptoRng, RngCore}; +use serde::{Deserialize, Serialize}; use x25519_dalek::PublicKey as X25519PublicKey; use crate::errors::Error; @@ -14,11 +12,41 @@ type Kem = X25519HkdfSha256; type WrappedPublicKey = ::PublicKey; type WrappedPrivateKey = ::PrivateKey; type DHKEMSharedSecret = hpke::kem::SharedSecret; -type DHKEMCiphertext = ::EncappedKey; // ----- DHKEM(X25519) ----- // This implementation wraps https://github.com/rozbb/rust-hpke, which has been partially reviewed +/// Wrap an `rust-hpke` `EncappedKey` to provide custom traits and prevent futur changes +pub(crate) struct DHKEMCiphertext(::EncappedKey); + +impl DHKEMCiphertext { + fn from_bytes(bytes: &[u8]) -> Result { + Ok(DHKEMCiphertext(::EncappedKey::from_bytes( + bytes, + )?)) + } + pub(crate) fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes().into() + } +} + +impl Serialize for DHKEMCiphertext { + fn serialize(&self, serializer: S) -> Result { + self.to_bytes().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for DHKEMCiphertext { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bytes = <[u8; 32]>::deserialize(deserializer)?; + DHKEMCiphertext::from_bytes(&bytes) + .or(Err(serde::de::Error::custom("Invalid DHKEMCiphertext"))) + } +} + /// Provides DHKEM encapsulation over X25519 curve (RFC 9180 §4.1) from a given CryptoRng /// /// Return a shared secret and the corresponding ciphertext @@ -27,7 +55,8 @@ pub(crate) fn dhkem_encap_from_rng( csprng: &mut (impl CryptoRng + RngCore), ) -> Result<(DHKEMSharedSecret, DHKEMCiphertext), Error> { let wrapped = WrappedPublicKey::from_bytes(&pubkey.to_bytes())?; - Ok(X25519HkdfSha256::encap(&wrapped, None, csprng)?) + let (shared_secret, ciphertext) = X25519HkdfSha256::encap(&wrapped, None, csprng)?; + Ok((shared_secret, DHKEMCiphertext(ciphertext))) } /// Provides DHKEM encapsulation over X25519 curve (RFC 9180 §4.1) @@ -47,7 +76,7 @@ pub(crate) fn dhkem_decap( private_key: &x25519_dalek::StaticSecret, ) -> Result { let wrapped = WrappedPrivateKey::from_bytes(&private_key.to_bytes())?; - Ok(X25519HkdfSha256::decap(&wrapped, None, encapped_key)?) + Ok(X25519HkdfSha256::decap(&wrapped, None, &encapped_key.0)?) } #[cfg(test)] @@ -121,11 +150,25 @@ mod tests { hex!("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"); const RFC_SHARED_SECRET: [u8; 32] = hex!("fe0e18c9f024ce43799ae393c7e8fe8fce9d218875e8227b0187c04e7d2ea1fc"); + + /// Test Serialization and Deserialization of DHKEMCiphertext #[test] + fn dhkem_ciphertext_serde() { + // from_bytes / to_bytes + let ciphertext = DHKEMCiphertext::from_bytes(&RFC_PKRM).unwrap(); + assert_eq!(ciphertext.to_bytes(), RFC_PKRM); + + // serialize / deserialize + let serialized = bincode::serialize(&ciphertext).unwrap(); + let deserialized: DHKEMCiphertext = bincode::deserialize(&serialized).unwrap(); + assert_eq!(ciphertext.to_bytes(), deserialized.to_bytes()); + } + /// RFC 9180 §A.1.1 /// /// These test vectors are already tested by `hpke` crates, but we ensure /// there is no regression change, even if we later change our base crate + #[test] fn rfc9180_dhkem_vector_tests() { // Key derivation let (privkey_em, pubkey_em) = X25519HkdfSha256::derive_keypair(&RFC_IKME); From 9f655b4c67c2a6d7be359660d8067b02bb9ae9c7 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 5 Aug 2024 16:53:03 +0200 Subject: [PATCH 03/29] Crypto/hybrid: use DHKEM instead of old ECC, ephemeral pubkey for each recipient --- mla/src/crypto/hybrid.rs | 43 +++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/mla/src/crypto/hybrid.rs b/mla/src/crypto/hybrid.rs index a5fa73e2..45a92101 100644 --- a/mla/src/crypto/hybrid.rs +++ b/mla/src/crypto/hybrid.rs @@ -1,5 +1,5 @@ use crate::crypto::aesgcm::{ConstantTimeEq, Key, KEY_SIZE, NONCE_AES_SIZE, TAG_LENGTH}; -use crate::crypto::ecc::{derive_key as ecc_derive_key, DHKEMCipherText}; +use crate::crypto::ecc::derive_key as ecc_derive_key; use crate::errors::ConfigError; use crate::layers::encrypt::get_crypto_rng; use hkdf::Hkdf; @@ -13,6 +13,8 @@ use sha2::Sha256; use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret as X25519StaticSecret}; use zeroize::Zeroize; +use super::hpke::{dhkem_decap, dhkem_encap, DHKEMCiphertext}; + /// Common structures for ML-KEM 1024 type MLKEMCiphertext = [u8; 1568]; /// ML-KEM 1024 "private key" @@ -92,6 +94,8 @@ struct HybridRecipientEncapsulatedKey { /// Ciphertext for ML-KEM #[serde(with = "BigArray")] ct_ml: MLKEMCiphertext, + /// Ciphertext for DH-KEM (actually an ECC ephemeral public key) + ct_ecc: DHKEMCiphertext, /// Wrapped (encrypted) version of the main key /// - Algorithm: AES-GCM 256 /// - Key: hybrid shared secret @@ -107,8 +111,6 @@ struct HybridRecipientEncapsulatedKey { /// Will be store in and load from the header #[derive(Serialize, Deserialize)] pub struct HybridMultiRecipientEncapsulatedKey { - /// Common ciphertext for DH-KEM (actually an ECC ephemeral public key) - ct_ecc: DHKEMCipherText, /// Key wrapping for each recipient recipients: Vec, } @@ -149,19 +151,20 @@ impl Decapsulate for HybridPrivateKey encapsulated_key: &HybridMultiRecipientEncapsulatedKey, ) -> Result { // For each possible recipient, compute the candidate hybrid shared secret - let ss_ecc = ecc_derive_key( - &self.private_key_ecc, - &X25519PublicKey::from(encapsulated_key.ct_ecc), - ) - .or(Err(ConfigError::ECIESComputationError))?; for recipient in &encapsulated_key.recipients { + let ss_ecc = dhkem_decap(&recipient.ct_ecc, &self.private_key_ecc) + .or(Err(ConfigError::DHKEMComputationError))?; let ss_ml = self .private_key_ml .decapsulate(&recipient.ct_ml.into()) .or(Err(ConfigError::MLKEMComputationError))?; - let shared_secret = - combine(&ss_ecc, &ss_ml, &encapsulated_key.ct_ecc, &recipient.ct_ml); + let shared_secret = combine( + &ss_ecc.0, + &ss_ml, + &recipient.ct_ecc.to_bytes(), + &recipient.ct_ml, + ); // Unwrap the candidate key and check it using AES-GCM tag validation let mut cipher = crate::crypto::aesgcm::AesGcm256::new( @@ -210,16 +213,11 @@ impl Encapsulate for HybridMultiRecipi // Generate the final Key -- the one each recipient will finally retrieve let key = csprng.gen::(); - // A `StaticSecret` is used instead of an `EphemeralSecret` to allow for - // multiple diffie-hellman computation - let ephemeral = X25519StaticSecret::from(csprng.gen::<[u8; 32]>()); - let ct_ecc = X25519PublicKey::from(&ephemeral); - let mut recipients = Vec::new(); for recipient in &self.keys { // Compute the ECC shared secret - let ss_ecc = ecc_derive_key(&ephemeral, &recipient.public_key_ecc) - .or(Err(ConfigError::ECIESComputationError))?; + let (ss_ecc, ct_ecc) = dhkem_encap(&recipient.public_key_ecc) + .or(Err(ConfigError::DHKEMComputationError))?; // Compute the ML-KEM shared secret let (ct_ml, ss_ml) = &recipient @@ -228,7 +226,7 @@ impl Encapsulate for HybridMultiRecipi .or(Err(ConfigError::MLKEMComputationError))?; // Combine them to obtain the hybrid shared secret - let ss_hybrid = combine(&ss_ecc, ss_ml, ct_ecc.as_bytes(), ct_ml); + let ss_hybrid = combine(&ss_ecc.0, ss_ml, &ct_ecc.to_bytes(), ct_ml); // Wrap the final key let nonce = csprng.gen::<[u8; NONCE_AES_SIZE]>(); @@ -246,19 +244,14 @@ impl Encapsulate for HybridMultiRecipi recipients.push(HybridRecipientEncapsulatedKey { ct_ml: (*ct_ml).into(), + ct_ecc, wrapped_key, tag, nonce, }); } - Ok(( - HybridMultiRecipientEncapsulatedKey { - ct_ecc: ct_ecc.to_bytes(), - recipients, - }, - key, - )) + Ok((HybridMultiRecipientEncapsulatedKey { recipients }, key)) } } From 4abf764ed6ea8107c8456c4220a8a5fac900bf4a Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 5 Aug 2024 16:56:17 +0200 Subject: [PATCH 04/29] Error: ECIES -> DHKEM --- bindings/C/mla.h | 2 +- bindings/C/mla.hpp | 2 +- bindings/C/src/lib.rs | 6 +++--- mla/src/errors.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bindings/C/mla.h b/bindings/C/mla.h index 08e044f3..1a221357 100644 --- a/bindings/C/mla.h +++ b/bindings/C/mla.h @@ -32,7 +32,7 @@ enum MLAStatus { MLA_STATUS_CONFIG_ERROR_ENCRYPTION_KEY_IS_MISSING = 1310723, MLA_STATUS_CONFIG_ERROR_PRIVATE_KEY_NOT_SET = 1310724, MLA_STATUS_CONFIG_ERROR_PRIVATE_KEY_NOT_FOUND = 1310725, - MLA_STATUS_CONFIG_ERROR_ECIES_COMPUTATION_ERROR = 1310726, + MLA_STATUS_CONFIG_ERROR_DHKEM_COMPUTATION_ERROR = 1310726, MLA_STATUS_CONFIG_ERROR_KEY_COMMITMENT_COMPUTATION_ERROR = 1310727, MLA_STATUS_CONFIG_ERROR_KEY_COMMITMENT_CHECKING_ERROR = 1310728, MLA_STATUS_CONFIG_ERROR_NO_RECIPIENTS = 1310729, diff --git a/bindings/C/mla.hpp b/bindings/C/mla.hpp index 499f0a28..363d591c 100644 --- a/bindings/C/mla.hpp +++ b/bindings/C/mla.hpp @@ -33,7 +33,7 @@ enum class MLAStatus : uint64_t { MLA_STATUS_CONFIG_ERROR_ENCRYPTION_KEY_IS_MISSING = 1310723, MLA_STATUS_CONFIG_ERROR_PRIVATE_KEY_NOT_SET = 1310724, MLA_STATUS_CONFIG_ERROR_PRIVATE_KEY_NOT_FOUND = 1310725, - MLA_STATUS_CONFIG_ERROR_ECIES_COMPUTATION_ERROR = 1310726, + MLA_STATUS_CONFIG_ERROR_DHKEM_COMPUTATION_ERROR = 1310726, MLA_STATUS_CONFIG_ERROR_KEY_COMMITMENT_COMPUTATION_ERROR = 1310727, MLA_STATUS_CONFIG_ERROR_KEY_COMMITMENT_CHECKING_ERROR = 1310728, MLA_STATUS_CONFIG_ERROR_NO_RECIPIENTS = 1310729, diff --git a/bindings/C/src/lib.rs b/bindings/C/src/lib.rs index 85227165..144fe3ad 100644 --- a/bindings/C/src/lib.rs +++ b/bindings/C/src/lib.rs @@ -48,7 +48,7 @@ pub enum MLAStatus { ConfigErrorEncryptionKeyIsMissing = 0x140003, ConfigErrorPrivateKeyNotSet = 0x140004, ConfigErrorPrivateKeyNotFound = 0x140005, - ConfigErrorECIESComputationError = 0x140006, + ConfigErrorDHKEMComputationError = 0x140006, ConfigErrorKeyCommitmentComputationError = 0x140007, ConfigErrorKeyCommitmentCheckingError = 0x140008, ConfigErrorNoRecipients = 0x140009, @@ -176,8 +176,8 @@ impl From for MLAStatus { MLAError::ConfigError(ConfigError::PrivateKeyNotFound) => { MLAStatus::ConfigErrorPrivateKeyNotFound } - MLAError::ConfigError(ConfigError::ECIESComputationError) => { - MLAStatus::ConfigErrorECIESComputationError + MLAError::ConfigError(ConfigError::DHKEMComputationError) => { + MLAStatus::ConfigErrorDHKEMComputationError } MLAError::ConfigError(ConfigError::KeyCommitmentComputationError) => { MLAStatus::ConfigErrorKeyCommitmentComputationError diff --git a/mla/src/errors.rs b/mla/src/errors.rs index 09fc91c0..eddbb523 100644 --- a/mla/src/errors.rs +++ b/mla/src/errors.rs @@ -202,7 +202,7 @@ pub enum ConfigError { PrivateKeyNotSet, PrivateKeyNotFound, MLKEMComputationError, - ECIESComputationError, + DHKEMComputationError, KeyCommitmentComputationError, /// The encrypted key commitment does not correspond to the key commitment chain KeyCommitmentCheckingError, From d5e23cb62d3ef0259fc4632364176437c3715b9e Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 5 Aug 2024 17:08:59 +0200 Subject: [PATCH 05/29] Crypto: remove no more used ECC --- mla/src/crypto/ecc.rs | 50 ---------------------------------------- mla/src/crypto/hybrid.rs | 4 +--- mla/src/crypto/mod.rs | 1 - 3 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 mla/src/crypto/ecc.rs diff --git a/mla/src/crypto/ecc.rs b/mla/src/crypto/ecc.rs deleted file mode 100644 index 743248a3..00000000 --- a/mla/src/crypto/ecc.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::crypto::aesgcm::Key; -use crate::errors::Error; -use hkdf::Hkdf; -use sha2::Sha256; -use x25519_dalek::{PublicKey, StaticSecret}; -use zeroize::Zeroize; - -pub const PUBLIC_KEY_SIZE: usize = 32; -/// The ciphertext in DH-KEM / ECIES is a public key -pub(crate) type DHKEMCipherText = [u8; PUBLIC_KEY_SIZE]; -const DERIVE_KEY_INFO: &[u8; 14] = b"KEY DERIVATION"; - -// TODO: consider DH-KEM -// Implementation inspired from XSTREAM/x25519hkdf.rs -// /!\ in XSTREAM/x25519hkdf.rs, the arguments of Hkdf::new seem inverted -pub(crate) fn derive_key(private_key: &StaticSecret, public_key: &PublicKey) -> Result { - let mut shared_secret = private_key.diffie_hellman(public_key); - let hkdf: Hkdf = Hkdf::new(None, shared_secret.as_bytes()); - let mut output = Key::default(); - hkdf.expand(DERIVE_KEY_INFO, &mut output)?; - shared_secret.zeroize(); - Ok(output) -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::{RngCore, SeedableRng}; - use rand_chacha::ChaChaRng; - use x25519_dalek::{PublicKey, StaticSecret}; - - #[test] - fn ecies() { - let mut csprng = ChaChaRng::from_entropy(); - let mut bytes = [0u8; 32]; - csprng.fill_bytes(&mut bytes); - let ephemeral_scalar = StaticSecret::from(bytes); - let ephemeral_public = PublicKey::from(&ephemeral_scalar); - - csprng.fill_bytes(&mut bytes); - let receiver_private = StaticSecret::from(bytes); - let receiver_public = PublicKey::from(&receiver_private); - - let symmetric_key = derive_key(&ephemeral_scalar, &receiver_public).unwrap(); - - let receiver_key = derive_key(&receiver_private, &ephemeral_public).unwrap(); - - assert_eq!(symmetric_key, receiver_key); - } -} diff --git a/mla/src/crypto/hybrid.rs b/mla/src/crypto/hybrid.rs index 45a92101..5ccf2228 100644 --- a/mla/src/crypto/hybrid.rs +++ b/mla/src/crypto/hybrid.rs @@ -1,5 +1,5 @@ use crate::crypto::aesgcm::{ConstantTimeEq, Key, KEY_SIZE, NONCE_AES_SIZE, TAG_LENGTH}; -use crate::crypto::ecc::derive_key as ecc_derive_key; +use crate::crypto::hpke::{dhkem_decap, dhkem_encap, DHKEMCiphertext}; use crate::errors::ConfigError; use crate::layers::encrypt::get_crypto_rng; use hkdf::Hkdf; @@ -13,8 +13,6 @@ use sha2::Sha256; use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret as X25519StaticSecret}; use zeroize::Zeroize; -use super::hpke::{dhkem_decap, dhkem_encap, DHKEMCiphertext}; - /// Common structures for ML-KEM 1024 type MLKEMCiphertext = [u8; 1568]; /// ML-KEM 1024 "private key" diff --git a/mla/src/crypto/mod.rs b/mla/src/crypto/mod.rs index 88a4037e..0a2e4ec4 100644 --- a/mla/src/crypto/mod.rs +++ b/mla/src/crypto/mod.rs @@ -1,5 +1,4 @@ pub mod aesgcm; -pub mod ecc; pub mod hash; mod hpke; pub mod hybrid; From f869512228b578064cd2a39dca44fe5d502ecc70 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 12 Aug 2024 17:23:31 +0200 Subject: [PATCH 06/29] HPKE: add Key scheduling / Encryption context methods & tests --- mla/src/crypto/hpke.rs | 200 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 2 deletions(-) diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs index 0f26759c..83d7d039 100644 --- a/mla/src/crypto/hpke.rs +++ b/mla/src/crypto/hpke.rs @@ -1,10 +1,13 @@ /// Implements RFC 9180 for MLA needs +use hpke::aead::{Aead as HPKEAeadTrait, AesGcm256 as HPKEAesGcm256}; +use hpke::kdf::{labeled_extract, HkdfSha512, Kdf as HpkeKdfTrait, LabeledExpand}; use hpke::{kem::X25519HkdfSha256, Kem as KemTrait}; use hpke::{Deserializable, Serializable}; use rand::{CryptoRng, RngCore}; use serde::{Deserialize, Serialize}; use x25519_dalek::PublicKey as X25519PublicKey; +use crate::crypto::aesgcm::{Key, Nonce}; use crate::errors::Error; use crate::layers::encrypt::get_crypto_rng; @@ -79,11 +82,128 @@ pub(crate) fn dhkem_decap( Ok(X25519HkdfSha256::decap(&wrapped, None, &encapped_key.0)?) } +// ----- Key scheduling / Encryption context ----- +// Based on RFC 9180, extract a key and a base nonce for future AEAD encryption +// +// rust-hpke provides `setup_sender` and `setup_receiver` +// Unfortunately, re-using their code means: +// - implementing the rust-hpke `Kem` traitsuse hpke::kdf::LabeledExpand; for our Hybrid encryption KEM, while we are not yet able to convert a private key to a public one +// - re-implement the AEAD trait for our AesGcm, to be able to repair +// - implementing struct to use suite ID which are not in the RFC (because we are using our own Hybrid KEM) +// +// Regarding the code quantity involved, the choice has been made to rather have it implemented here + +type HpkeKdf = HkdfSha512; +type HpkeAead = HPKEAesGcm256; +/// 5. Hybrid Public Key Encryption - HPKE Modes (§5.1 - Table 1) +const HPKE_MODE_BASE: u8 = 0; + +/// Custom KEM ID, not in the RFC 9180 +/// Hybrid : DHKEM(X25519, HKDF-SHA256) + MLKEM +const HYBRID_KEM_ID: u16 = 0x1020; + +/// `info` to bound the HPKE usage to MLA +const HPKE_INFO: &[u8] = b"MLA Encrypt Layer"; + +/// Return the suite_id for the Hybrid KEM (RFC 9180 §5.1) +/// suite_id = concat( +/// "HPKE", +/// I2OSP(kem_id, 2), +/// I2OSP(kdf_id, 2), +/// I2OSP(aead_id, 2) +/// ) +/// +/// `kem_id` is kept as an argument to allow testing against RFC 9180 test vectors +fn build_suite_id(kem_id: u16) -> [u8; 10] { + // TODO : convert to a const fn + let mut out = [0u8; 10]; + out[0..4].copy_from_slice(b"HPKE"); + out[4..6].copy_from_slice(&kem_id.to_be_bytes()); + out[6..8].copy_from_slice(&HpkeKdf::KDF_ID.to_be_bytes()); + out[8..10].copy_from_slice(&HpkeAead::AEAD_ID.to_be_bytes()); + out +} + +/// Key schedule for the sender (RFC 9180 §5.1), internal version +/// +/// This version is kept for testing purpose (against RFC 9180 test vectors) +/// +/// Parameters are: +/// - `shared_secret`: the shared secret from the Hybrid KEM +/// - mode: set to Base (no PSK nor sender key) +/// - `info`` +/// - psk: no PSK, because the mode used is "Base" +/// - algorithms: +/// - Kem: `kem_id` +/// - Kdf: HKDF-SHA512 +/// - Aead: AES-GCM-256 +fn key_schedule_s_internal( + shared_secret: &[u8], + info: &[u8], + kem_id: u16, +) -> Result<(Key, Nonce), Error> { + let suite_id = build_suite_id(kem_id); + let mut key = Key::default(); + let mut base_nonce = Nonce::default(); + + // No PSK, no Info + let (psk_id_hash, _psk_kdf) = labeled_extract::(&[], &suite_id, b"psk_id_hash", b""); + let (info_hash, _info_kdf) = labeled_extract::(&[], &suite_id, b"info_hash", info); + // Concat HPKE_MODE_BASE and info + let mut key_schedule_context: Vec = vec![]; + key_schedule_context.push(HPKE_MODE_BASE); + key_schedule_context.extend_from_slice(&psk_id_hash); + key_schedule_context.extend_from_slice(&info_hash); + + let (_prk, secret_kdf) = labeled_extract::(shared_secret, &suite_id, b"secret", b""); + secret_kdf.labeled_expand(&suite_id, b"key", &key_schedule_context, &mut key)?; + secret_kdf.labeled_expand( + &suite_id, + b"base_nonce", + &key_schedule_context, + &mut base_nonce, + )?; + + Ok((key, base_nonce)) +} + +/// Key schedule for the sender (RFC 9180 §5.1) +/// +/// Parameters are: +/// - `shared_secret`: the shared secret from the Hybrid KEM +/// - mode: set to Base (no PSK nor sender key) +/// - info: set to "MLA Encrypt Layer" +/// - psk: no PSK, because the mode used is "Base" +/// - algorithms: +/// - Kem: HYBRID_KEM_ID (custom value) +/// - Kdf: HKDF-SHA512 +/// - Aead: AES-GCM-256 +pub(crate) fn key_schedule_s(shared_secret: &[u8]) -> Result<(Key, Nonce), Error> { + key_schedule_s_internal(shared_secret, HPKE_INFO, HYBRID_KEM_ID) +} + +/// Compute the nonce for a given sequence number (RFC 9180 §5.2) +pub(crate) fn compute_nonce(base_nonce: Nonce, seq: u64) -> Nonce { + // RFC 9180 §5.2: seq must not be superior to 1 << (8*Nn) + // As we use AES-256-GCM, Nn = 12 (RFC 9180 §7.3), so u64 is always enough + + // Nonce = nonce ^ 0...seq + let mut nonce = base_nonce; + let seq_be = seq.to_be_bytes(); + for i in 0..seq_be.len() { + let nonce_idx = i + nonce.len() - seq_be.len(); + nonce[nonce_idx] ^= seq_be[i]; + } + nonce +} + #[cfg(test)] mod tests { use std::io; use std::io::{BufReader, Cursor}; + use crate::crypto::aesgcm::AesGcm256; + use super::*; use hex_literal::hex; use hpke::Serializable; @@ -157,7 +277,6 @@ mod tests { // from_bytes / to_bytes let ciphertext = DHKEMCiphertext::from_bytes(&RFC_PKRM).unwrap(); assert_eq!(ciphertext.to_bytes(), RFC_PKRM); - // serialize / deserialize let serialized = bincode::serialize(&ciphertext).unwrap(); let deserialized: DHKEMCiphertext = bincode::deserialize(&serialized).unwrap(); @@ -174,7 +293,6 @@ mod tests { let (privkey_em, pubkey_em) = X25519HkdfSha256::derive_keypair(&RFC_IKME); assert_eq!(&pubkey_em.to_bytes().as_ref(), &RFC_PKEM); assert_eq!(&privkey_em.to_bytes().as_ref(), &RFC_SKEM); - let (privkey_rm, pubkey_rm) = X25519HkdfSha256::derive_keypair(&RFC_IKMR); assert_eq!(&pubkey_rm.to_bytes().as_ref(), &RFC_PKRM); assert_eq!(&privkey_rm.to_bytes().as_ref(), &RFC_SKRM); @@ -194,4 +312,82 @@ mod tests { .unwrap(); assert_eq!(&shared_secret.0.to_vec(), &RFC_SHARED_SECRET); } + + /// RFC 9180 §A.6.1 - DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM + const RFC_A6_INFO: [u8; 20] = hex!("4f6465206f6e2061204772656369616e2055726e"); + const RFC_A6_SHARED_SECRET: [u8; 64] = hex!("776ab421302f6eff7d7cb5cb1adaea0cd50872c71c2d63c30c4f1d5e43653336fef33b103c67e7a98add2d3b66e2fda95b5b2a667aa9dac7e59cc1d46d30e818"); + const RFC_A6_KEM_ID: u16 = 18; + const RFC_A6_KEY: [u8; 32] = + hex!("751e346ce8f0ddb2305c8a2a85c70d5cf559c53093656be636b9406d4d7d1b70"); + const RFC_A6_BASE_NONCE: [u8; 12] = hex!("55ff7a7d739c69f44b25447b"); + + /// RFC 9180 §A.6.1 + /// + /// Use A.6 for HKDF-SHA512 and AES-256-GCM + /// In MLA, we rather use a custom Kem ID (Hybrid KEM), but this method does the main job + #[test] + fn test_key_schedule_s_internal() { + let (key, nonce) = + key_schedule_s_internal(&RFC_A6_SHARED_SECRET, &RFC_A6_INFO, RFC_A6_KEM_ID).unwrap(); + assert_eq!(key, RFC_A6_KEY); + assert_eq!(nonce, RFC_A6_BASE_NONCE); + } + + const RFC_A6_SEQS: [u64; 6] = [0, 1, 2, 4, 255, 256]; + const RFC_A6_NONCE: [[u8; 12]; 6] = [ + hex!("55ff7a7d739c69f44b25447b"), // sequence_number: 0 + hex!("55ff7a7d739c69f44b25447a"), // sequence_number: 1 + hex!("55ff7a7d739c69f44b254479"), // sequence_number: 2 + hex!("55ff7a7d739c69f44b25447f"), // sequence_number: 4 + hex!("55ff7a7d739c69f44b254484"), // sequence_number: 255 + hex!("55ff7a7d739c69f44b25457b"), // sequence_number: 256 + ]; + + const RFC_A6_AAD: [&[u8]; 6] = [ + &hex!("436f756e742d30"), // sequence number: 0 + &hex!("436f756e742d31"), // sequence number: 1 + &hex!("436f756e742d32"), // sequence number: 2 + &hex!("436f756e742d34"), // sequence number: 4 + &hex!("436f756e742d323535"), // sequence number: 255 + &hex!("436f756e742d323536"), // sequence number: 256 + ]; + const RFC_A6_PT: &[u8] = &hex!("4265617574792069732074727574682c20747275746820626561757479"); + const RFC_A6_CT: [&[u8]; 6] = [ + &hex!("170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a"), // sequence number: 0 + &hex!("d9ee248e220ca24ac00bbbe7e221a832e4f7fa64c4fbab3945b6f3af0c5ecd5e16815b328be4954a05fd352256"), // sequence number: 1 + &hex!("142cf1e02d1f58d9285f2af7dcfa44f7c3f2d15c73d460c48c6e0e506a3144bae35284e7e221105b61d24e1c7a"), // sequence number: 2 + &hex!("3bb3a5a07100e5a12805327bf3b152df728b1c1be75a9fd2cb2bf5eac0cca1fb80addb37eb2a32938c7268e3e5"), // sequence number: 4 + &hex!("4f268d0930f8d50b8fd9d0f26657ba25b5cb08b308c92e33382f369c768b558e113ac95a4c70dd60909ad1adc7"), // sequence number: 255 + &hex!("dbbfc44ae037864e75f136e8b4b4123351d480e6619ae0e0ae437f036f2f8f1ef677686323977a1ccbb4b4f16a"), // sequence number: 256 + ]; + + /// RFC 9180 §A.6.1.1 + #[test] + fn test_compute_nonce() { + let base_nonce = RFC_A6_BASE_NONCE; + for i in 0..RFC_A6_SEQS.len() { + let computed_nonce = compute_nonce(base_nonce, RFC_A6_SEQS[i]); + assert_eq!(computed_nonce, RFC_A6_NONCE[i]); + } + } + + /// RFC 9180 §A.6.1.1 + /// + /// As AES and previous values from RFC are already tested, this test is optionnal + /// But it helps to ensure we correctly implements HPKE + #[test] + fn test_rfc_a6_encryption() { + for i in 0..RFC_A6_SEQS.len() { + let mut aes = AesGcm256::new( + &RFC_A6_KEY, + &compute_nonce(RFC_A6_BASE_NONCE, RFC_A6_SEQS[i]), + RFC_A6_AAD[i], + ) + .unwrap(); + let mut buf = Vec::from(RFC_A6_PT); + aes.encrypt(buf.as_mut_slice()); + buf.extend_from_slice(&aes.into_tag()); + assert_eq!(buf, RFC_A6_CT[i]); + } + } } From e427413fe432537de4225d9a51882ab9f317fdaa Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 12 Aug 2024 17:46:23 +0200 Subject: [PATCH 07/29] Encrypt: switch to HPKE compute_nonce --- mla/src/crypto/hpke.rs | 9 +++-- mla/src/crypto/mod.rs | 2 +- mla/src/layers/encrypt.rs | 69 ++++++++++++++++----------------------- 3 files changed, 34 insertions(+), 46 deletions(-) diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs index 83d7d039..be80d842 100644 --- a/mla/src/crypto/hpke.rs +++ b/mla/src/crypto/hpke.rs @@ -183,12 +183,12 @@ pub(crate) fn key_schedule_s(shared_secret: &[u8]) -> Result<(Key, Nonce), Error } /// Compute the nonce for a given sequence number (RFC 9180 §5.2) -pub(crate) fn compute_nonce(base_nonce: Nonce, seq: u64) -> Nonce { +pub(crate) fn compute_nonce(base_nonce: &Nonce, seq: u64) -> Nonce { // RFC 9180 §5.2: seq must not be superior to 1 << (8*Nn) // As we use AES-256-GCM, Nn = 12 (RFC 9180 §7.3), so u64 is always enough // Nonce = nonce ^ 0...seq - let mut nonce = base_nonce; + let mut nonce = *base_nonce; let seq_be = seq.to_be_bytes(); for i in 0..seq_be.len() { let nonce_idx = i + nonce.len() - seq_be.len(); @@ -364,9 +364,8 @@ mod tests { /// RFC 9180 §A.6.1.1 #[test] fn test_compute_nonce() { - let base_nonce = RFC_A6_BASE_NONCE; for i in 0..RFC_A6_SEQS.len() { - let computed_nonce = compute_nonce(base_nonce, RFC_A6_SEQS[i]); + let computed_nonce = compute_nonce(&RFC_A6_BASE_NONCE, RFC_A6_SEQS[i]); assert_eq!(computed_nonce, RFC_A6_NONCE[i]); } } @@ -380,7 +379,7 @@ mod tests { for i in 0..RFC_A6_SEQS.len() { let mut aes = AesGcm256::new( &RFC_A6_KEY, - &compute_nonce(RFC_A6_BASE_NONCE, RFC_A6_SEQS[i]), + &compute_nonce(&RFC_A6_BASE_NONCE, RFC_A6_SEQS[i]), RFC_A6_AAD[i], ) .unwrap(); diff --git a/mla/src/crypto/mod.rs b/mla/src/crypto/mod.rs index 0a2e4ec4..e259a61d 100644 --- a/mla/src/crypto/mod.rs +++ b/mla/src/crypto/mod.rs @@ -1,4 +1,4 @@ pub mod aesgcm; pub mod hash; -mod hpke; +pub mod hpke; pub mod hybrid; diff --git a/mla/src/layers/encrypt.rs b/mla/src/layers/encrypt.rs index cd6a3ae3..0563fada 100644 --- a/mla/src/layers/encrypt.rs +++ b/mla/src/layers/encrypt.rs @@ -1,7 +1,6 @@ -use crate::crypto::aesgcm::{ - AesGcm256, ConstantTimeEq, Key, Nonce, Tag, KEY_COMMITMENT_SIZE, TAG_LENGTH, -}; +use crate::crypto::aesgcm::{AesGcm256, ConstantTimeEq, Key, Tag, KEY_COMMITMENT_SIZE, TAG_LENGTH}; +use crate::crypto::hpke::compute_nonce; use crate::crypto::hybrid::{ HybridMultiRecipientEncapsulatedKey, HybridMultiRecipientsPublicKeys, HybridPrivateKey, HybridPublicKey, @@ -15,6 +14,7 @@ use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write}; use crate::config::{ArchiveReaderConfig, ArchiveWriterConfig}; use crate::errors::ConfigError; +use hpke::HpkeError; use kem::{Decapsulate, Encapsulate}; use rand::{Rng, SeedableRng}; use rand_chacha::{rand_core::CryptoRngCore, ChaChaRng}; @@ -26,30 +26,13 @@ use super::traits::InnerReaderTrait; const CIPHER_BUF_SIZE: u64 = 4096; // This is the size of the nonce taken as input -const NONCE_SIZE: usize = 8; +const NONCE_SIZE: usize = 12; /// Type standing for the unique nonce per-archive type ArchiveNonce = [u8; NONCE_SIZE]; const CHUNK_SIZE: u64 = 128 * 1024; const ASSOCIATED_DATA: &[u8; 0] = b""; -/// Build nonce according to a given state -/// -/// AesGcm expect a 96 bits nonce. -/// The nonce build as: -/// 1. 8 byte nonce, unique per archive -/// 2. 4 byte counter, unique per chunk and incremental -/// -/// Inspired from the construction in TLS or STREAM from "Online -/// Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance" -fn build_nonce(nonce_prefix: ArchiveNonce, current_ctr: u32) -> Nonce { - // This is the Nonce as expected by AesGcm - let mut nonce = Nonce::default(); - nonce[..NONCE_SIZE].copy_from_slice(&nonce_prefix); - nonce[NONCE_SIZE..].copy_from_slice(¤t_ctr.to_be_bytes()); - nonce -} - // ---------- Key commitment ---------- /// Key commitment chain, to be used to ensure the key is actually the expected one @@ -64,7 +47,7 @@ fn build_key_commitment_chain( ) -> Result { let mut key_commitment = [0u8; KEY_COMMITMENT_SIZE]; key_commitment.copy_from_slice(KEY_COMMITMENT_CHAIN); - let mut cipher = AesGcm256::new(key, &build_nonce(*nonce, 0), ASSOCIATED_DATA)?; + let mut cipher = AesGcm256::new(key, &compute_nonce(nonce, 0), ASSOCIATED_DATA)?; cipher.encrypt(&mut key_commitment); let mut tag = [0u8; TAG_LENGTH]; tag.copy_from_slice(&cipher.into_tag()); @@ -80,7 +63,7 @@ fn check_key_commitment( commitment: &KeyCommitmentAndTag, ) -> Result<(), ConfigError> { let mut key_commitment = commitment.key_commitment; - let mut cipher = AesGcm256::new(key, &build_nonce(*nonce, 0), ASSOCIATED_DATA) + let mut cipher = AesGcm256::new(key, &compute_nonce(nonce, 0), ASSOCIATED_DATA) .or(Err(ConfigError::KeyCommitmentCheckingError))?; let tag = cipher.decrypt(&mut key_commitment); if tag.ct_eq(&commitment.tag).unwrap_u8() == 1 { @@ -94,7 +77,7 @@ fn check_key_commitment( /// /// 0: used to encrypt the key commitment chain /// 1-n: used for the actual data -const FIRST_DATA_CHUNK_NUMBER: u32 = 1; +const FIRST_DATA_CHUNK_NUMBER: u64 = 1; // ---------- Config ---------- /// Encrypted Key commitment and associated tag @@ -301,10 +284,10 @@ pub(crate) struct EncryptionLayerWriter<'a, W: 'a + InnerWriterTrait> { cipher: AesGcm256, /// Symmetric encryption Key key: Key, - /// Symmetric encryption nonce prefix, see `build_nonce` + /// Symmetric encryption nonce prefix, see `compute_nonce` nonce_prefix: ArchiveNonce, current_chunk_offset: u64, - current_ctr: u32, + current_ctr: u64, } impl<'a, W: 'a + InnerWriterTrait> EncryptionLayerWriter<'a, W> { @@ -318,7 +301,7 @@ impl<'a, W: 'a + InnerWriterTrait> EncryptionLayerWriter<'a, W> { nonce_prefix: internal_config.nonce, cipher: AesGcm256::new( &internal_config.key, - &build_nonce(internal_config.nonce, FIRST_DATA_CHUNK_NUMBER), + &compute_nonce(&internal_config.nonce, FIRST_DATA_CHUNK_NUMBER), ASSOCIATED_DATA, )?, current_chunk_offset: 0, @@ -328,11 +311,14 @@ impl<'a, W: 'a + InnerWriterTrait> EncryptionLayerWriter<'a, W> { fn renew_cipher(&mut self) -> Result { // Prepare a new cipher - self.current_ctr += 1; + self.current_ctr = self + .current_ctr + .checked_add(1) + .ok_or(HpkeError::MessageLimitReached)?; self.current_chunk_offset = 0; let cipher = AesGcm256::new( &self.key, - &build_nonce(self.nonce_prefix, self.current_ctr), + &compute_nonce(&self.nonce_prefix, self.current_ctr), ASSOCIATED_DATA, )?; let old_cipher = std::mem::replace(&mut self.cipher, cipher); @@ -405,7 +391,7 @@ pub struct EncryptionLayerReader<'a, R: Read + Seek> { chunk_cache: Cursor>, /// Current chunk number in the data. /// Note: the actual chunk number used in the cipher is offseted by FIRST_DATA_CHUNK_NUMBER - current_chunk_number: u32, + current_chunk_number: u64, } impl<'a, R: 'a + Read + Seek> EncryptionLayerReader<'a, R> { @@ -418,7 +404,7 @@ impl<'a, R: 'a + Read + Seek> EncryptionLayerReader<'a, R> { inner, cipher: AesGcm256::new( &key, - &build_nonce(nonce, FIRST_DATA_CHUNK_NUMBER), + &compute_nonce(&nonce, FIRST_DATA_CHUNK_NUMBER), ASSOCIATED_DATA, )?, key, @@ -435,8 +421,8 @@ impl<'a, R: 'a + Read + Seek> EncryptionLayerReader<'a, R> { fn load_in_cache(&mut self) -> Result, Error> { self.cipher = AesGcm256::new( &self.key, - &build_nonce( - self.nonce, + &compute_nonce( + &self.nonce, self.current_chunk_number + FIRST_DATA_CHUNK_NUMBER, ), ASSOCIATED_DATA, @@ -543,7 +529,7 @@ impl<'a, R: 'a + Read + Seek> Seek for EncryptionLayerReader<'a, R> { self.inner.seek(SeekFrom::Start(pos_chunk_start))?; // Load and move into the cache - self.current_chunk_number = chunk_number as u32; + self.current_chunk_number = chunk_number; self.load_in_cache()?; self.chunk_cache.seek(SeekFrom::Start(pos_in_chunk))?; Ok(pos) @@ -595,7 +581,7 @@ pub struct EncryptionLayerFailSafeReader<'a, R: Read> { cipher: AesGcm256, key: Key, nonce: ArchiveNonce, - current_chunk_number: u32, + current_chunk_number: u64, current_chunk_offset: u64, } @@ -609,7 +595,7 @@ impl<'a, R: 'a + Read> EncryptionLayerFailSafeReader<'a, R> { inner, cipher: AesGcm256::new( &key, - &build_nonce(nonce, FIRST_DATA_CHUNK_NUMBER), + &compute_nonce(&nonce, FIRST_DATA_CHUNK_NUMBER), ASSOCIATED_DATA, )?, key, @@ -640,12 +626,15 @@ impl<'a, R: Read> Read for EncryptionLayerFailSafeReader<'a, R> { &mut (&mut self.inner).take(TAG_LENGTH as u64), &mut io::sink(), )?; - self.current_chunk_number += 1; + self.current_chunk_number = self + .current_chunk_number + .checked_add(1) + .ok_or(Error::HPKEError(HpkeError::MessageLimitReached))?; self.current_chunk_offset = 0; self.cipher = AesGcm256::new( &self.key, - &build_nonce( - self.nonce, + &compute_nonce( + &self.nonce, self.current_chunk_number + FIRST_DATA_CHUNK_NUMBER, ), ASSOCIATED_DATA, @@ -853,7 +842,7 @@ mod tests { let key_commitment_and_tag = result.unwrap(); // Decrypt it - let mut cipher = AesGcm256::new(&key, &build_nonce(nonce, 0), ASSOCIATED_DATA).unwrap(); + let mut cipher = AesGcm256::new(&key, &compute_nonce(&nonce, 0), ASSOCIATED_DATA).unwrap(); let mut decrypted_key_commitment = [0u8; KEY_COMMITMENT_SIZE]; decrypted_key_commitment.copy_from_slice(&key_commitment_and_tag.key_commitment); let tag = cipher.decrypt(&mut decrypted_key_commitment); From 8889475b90f1925e66d1db3581660ae57c4a71e2 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Mon, 12 Aug 2024 19:05:14 +0200 Subject: [PATCH 08/29] Encrypt: apply HPKE to HybridKEM output -> key, nonce --- mla/src/crypto/hybrid.rs | 121 +++++++++++++++++++++++--------------- mla/src/layers/encrypt.rs | 69 ++++++++++------------ 2 files changed, 105 insertions(+), 85 deletions(-) diff --git a/mla/src/crypto/hybrid.rs b/mla/src/crypto/hybrid.rs index 5ccf2228..12b90a00 100644 --- a/mla/src/crypto/hybrid.rs +++ b/mla/src/crypto/hybrid.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use serde_big_array::BigArray; use sha2::Sha256; use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret as X25519StaticSecret}; -use zeroize::Zeroize; +use zeroize::{Zeroize, ZeroizeOnDrop}; /// Common structures for ML-KEM 1024 type MLKEMCiphertext = [u8; 1568]; @@ -20,6 +20,28 @@ pub type MLKEMDecapsulationKey = ::DecapsulationKey; /// ML-KEM 1024 "public key" pub type MLKEMEncapsulationKey = ::EncapsulationKey; +type HybridKemSharedSecretArray = [u8; 32]; +type EncryptedSharedSecret = HybridKemSharedSecretArray; + +/// A shared secret, as produced by the Hybrid KEM decapsulation/encapsulation +/// +/// The type is wrapped to ease future changes & traits implementation +#[derive(Zeroize, ZeroizeOnDrop, Debug)] +pub(crate) struct HybridKemSharedSecret(pub(crate) HybridKemSharedSecretArray); + +impl HybridKemSharedSecret { + /// Generate a new HybridKemSharedSecret from a CSPRNG + pub fn from_rng(csprng: &mut R) -> Self { + Self(csprng.gen::()) + } +} + +impl PartialEq for HybridKemSharedSecret { + fn eq(&self, other: &Self) -> bool { + self.0.ct_eq(&other.0).into() + } +} + const HYBRIDKEM_ASSOCIATED_DATA: &[u8; 0] = b""; /// Produce a secret key by combining two KEM-Encaps outputs, using a "Nested Dual-PRF Combiner", proved in [6] (3.3) @@ -86,7 +108,7 @@ fn combine( // Use `_ecc` and `_ml` naming rather than a generic approach (`_1``, `_2`) // to avoid confusion / prone-to-error code -/// Per-recipient hybrid encapsulated key +/// Per-recipient hybrid encapsulated shared secret #[derive(Serialize, Deserialize)] struct HybridRecipientEncapsulatedKey { /// Ciphertext for ML-KEM @@ -94,11 +116,11 @@ struct HybridRecipientEncapsulatedKey { ct_ml: MLKEMCiphertext, /// Ciphertext for DH-KEM (actually an ECC ephemeral public key) ct_ecc: DHKEMCiphertext, - /// Wrapped (encrypted) version of the main key - /// - Algorithm: AES-GCM 256 - /// - Key: hybrid shared secret + /// Wrapped (encrypted) version of the main shared secret + /// - Algorithm: AES-256-GCM + /// - Key: per-recipient hybrid shared secret /// - Nonce: per-recipient - wrapped_key: [u8; KEY_SIZE], + wrapped_ss: EncryptedSharedSecret, /// Associated tag tag: [u8; TAG_LENGTH], /// Associated nonce @@ -141,13 +163,13 @@ impl Zeroize for HybridPrivateKey { } } -impl Decapsulate for HybridPrivateKey { +impl Decapsulate for HybridPrivateKey { type Error = ConfigError; fn decapsulate( &self, encapsulated_key: &HybridMultiRecipientEncapsulatedKey, - ) -> Result { + ) -> Result { // For each possible recipient, compute the candidate hybrid shared secret for recipient in &encapsulated_key.recipients { let ss_ecc = dhkem_decap(&recipient.ct_ecc, &self.private_key_ecc) @@ -157,29 +179,29 @@ impl Decapsulate for HybridPrivateKey .decapsulate(&recipient.ct_ml.into()) .or(Err(ConfigError::MLKEMComputationError))?; - let shared_secret = combine( + let ss_recipient = combine( &ss_ecc.0, &ss_ml, &recipient.ct_ecc.to_bytes(), &recipient.ct_ml, ); - // Unwrap the candidate key and check it using AES-GCM tag validation + // Unwrap the candidate shared secret and check it using AES-GCM tag validation let mut cipher = crate::crypto::aesgcm::AesGcm256::new( - &shared_secret, + &ss_recipient, &recipient.nonce, HYBRIDKEM_ASSOCIATED_DATA, ) .or(Err(ConfigError::KeyWrappingComputationError))?; - let mut decrypted_key = Key::default(); - decrypted_key.copy_from_slice(&recipient.wrapped_key); - let tag = cipher.decrypt(&mut decrypted_key); + let mut decrypted_ss = HybridKemSharedSecretArray::default(); + decrypted_ss.copy_from_slice(&recipient.wrapped_ss); + let tag = cipher.decrypt(&mut decrypted_ss); if tag.ct_eq(&recipient.tag).unwrap_u8() == 1 { - return Ok(decrypted_key); + return Ok(HybridKemSharedSecret(decrypted_ss)); } } - // No candidate key found + // No candidate found Err(ConfigError::PrivateKeyNotFound) } } @@ -201,15 +223,17 @@ pub(crate) struct HybridMultiRecipientsPublicKeys { pub(crate) keys: Vec, } -impl Encapsulate for HybridMultiRecipientsPublicKeys { +impl Encapsulate + for HybridMultiRecipientsPublicKeys +{ type Error = ConfigError; fn encapsulate( &self, csprng: &mut impl CryptoRngCore, - ) -> Result<(HybridMultiRecipientEncapsulatedKey, Key), Self::Error> { - // Generate the final Key -- the one each recipient will finally retrieve - let key = csprng.gen::(); + ) -> Result<(HybridMultiRecipientEncapsulatedKey, HybridKemSharedSecret), Self::Error> { + // Generate the final shared secret -- the one each recipient will finally retrieve + let final_ss_hybrid = HybridKemSharedSecret::from_rng(csprng); let mut recipients = Vec::new(); for recipient in &self.keys { @@ -223,33 +247,36 @@ impl Encapsulate for HybridMultiRecipi .encapsulate(csprng) .or(Err(ConfigError::MLKEMComputationError))?; - // Combine them to obtain the hybrid shared secret - let ss_hybrid = combine(&ss_ecc.0, ss_ml, &ct_ecc.to_bytes(), ct_ml); + // Combine them to obtain the recipient's hybrid key + let ss_recipient = combine(&ss_ecc.0, ss_ml, &ct_ecc.to_bytes(), ct_ml); - // Wrap the final key + // Wrap the final shared secret let nonce = csprng.gen::<[u8; NONCE_AES_SIZE]>(); let mut cipher = crate::crypto::aesgcm::AesGcm256::new( - &ss_hybrid, + &ss_recipient, &nonce, HYBRIDKEM_ASSOCIATED_DATA, ) .or(Err(ConfigError::KeyWrappingComputationError))?; - let mut wrapped_key = Key::default(); - wrapped_key.copy_from_slice(&key); - cipher.encrypt(&mut wrapped_key); + let mut wrapped_ss = EncryptedSharedSecret::default(); + wrapped_ss.copy_from_slice(&final_ss_hybrid.0); + cipher.encrypt(&mut wrapped_ss); let mut tag = [0u8; TAG_LENGTH]; tag.copy_from_slice(&cipher.into_tag()); recipients.push(HybridRecipientEncapsulatedKey { ct_ml: (*ct_ml).into(), ct_ecc, - wrapped_key, + wrapped_ss, tag, nonce, }); } - Ok((HybridMultiRecipientEncapsulatedKey { recipients }, key)) + Ok(( + HybridMultiRecipientEncapsulatedKey { recipients }, + final_ss_hybrid, + )) } } @@ -374,7 +401,7 @@ mod tests { assert_eq!(&computed_result, &expected_result); } - /// Test the encapsulation and decapsulation of an hybrid key + /// Test the encapsulation and decapsulation of an hybrid shared secret #[test] fn test_encapsulate_decapsulate() { let mut csprng = ChaChaRng::from_entropy(); @@ -399,20 +426,20 @@ mod tests { keys: vec![hybrid_public_key], }; - // Encapsulate a key - let (encapsulated_keys, key_encap) = hybrid_multi_recipient_keys + // Encapsulate a shared secret + let (encapsulated_keys, ss_hybrid_encap) = hybrid_multi_recipient_keys .encapsulate(&mut csprng) .unwrap(); - // Decapsulate the key - let key_decap = hybrid_private_key.decapsulate(&encapsulated_keys).unwrap(); + // Decapsulate the shared secret + let ss_hybrid_decap = hybrid_private_key.decapsulate(&encapsulated_keys).unwrap(); - // Ensure both key match - assert_eq!(key_encap, key_decap); + // Ensure both secret match + assert_eq!(ss_hybrid_encap, ss_hybrid_decap); } const NB_RECIPIENT: u32 = 10; - /// Test the encapsulation and decapsulation of an hybrid key for several recipients + /// Test the encapsulation and decapsulation of an hybrid shared secret for several recipients #[test] fn test_encapsulate_decapsulate_multi() { let mut csprng = ChaChaRng::from_entropy(); @@ -442,21 +469,21 @@ mod tests { hybrid_multi_recipient_private_keys.push(hybrid_private_key); } - // Encapsulate a key - let (encapsulated_keys, key_encap) = hybrid_multi_recipient_public_keys + // Encapsulate a shared secret + let (encapsulated_keys, ss_hybrid_encap) = hybrid_multi_recipient_public_keys .encapsulate(&mut csprng) .unwrap(); - // Decapsulate the key for each recipient + // Decapsulate the shared secret for each recipient for private_key in &hybrid_multi_recipient_private_keys { - let key_decap = private_key.decapsulate(&encapsulated_keys).unwrap(); + let ss_hybrid_decap = private_key.decapsulate(&encapsulated_keys).unwrap(); - // Check the key is the expected one - assert_eq!(key_encap, key_decap); + // Check the shared secret is the expected one + assert_eq!(ss_hybrid_encap, ss_hybrid_decap); } } - /// Test cryprographic materials (including the encapsulated key) for entropy + /// Test cryptographic materials (including the encapsulated shared secret) for entropy #[test] fn test_encapsulated_key_entropy() { let mut csprng = ChaChaRng::from_entropy(); @@ -474,7 +501,7 @@ mod tests { }; // Encapsulate a key - let (encapsulated_keys, key_encap) = hybrid_multi_recipient_keys + let (encapsulated_keys, ss_hybrid) = hybrid_multi_recipient_keys .encapsulate(&mut csprng) .unwrap(); let recipient = &encapsulated_keys.recipients[0]; @@ -482,10 +509,10 @@ mod tests { // Ensure materials have enough entropy // This is a naive check, using compression, to avoid naive bugs let materials: Vec<&[u8]> = vec![ - &key_encap, + &ss_hybrid.0, &recipient.nonce, &recipient.tag, - &recipient.wrapped_key, + &recipient.wrapped_ss, ]; for material in materials { let mut compressed = brotli::CompressorReader::new(material, 0, 0, 0); diff --git a/mla/src/layers/encrypt.rs b/mla/src/layers/encrypt.rs index 0563fada..6e00d898 100644 --- a/mla/src/layers/encrypt.rs +++ b/mla/src/layers/encrypt.rs @@ -1,9 +1,11 @@ -use crate::crypto::aesgcm::{AesGcm256, ConstantTimeEq, Key, Tag, KEY_COMMITMENT_SIZE, TAG_LENGTH}; +use crate::crypto::aesgcm::{ + AesGcm256, ConstantTimeEq, Key, Nonce, Tag, KEY_COMMITMENT_SIZE, TAG_LENGTH, +}; -use crate::crypto::hpke::compute_nonce; +use crate::crypto::hpke::{compute_nonce, key_schedule_s}; use crate::crypto::hybrid::{ - HybridMultiRecipientEncapsulatedKey, HybridMultiRecipientsPublicKeys, HybridPrivateKey, - HybridPublicKey, + HybridKemSharedSecret, HybridMultiRecipientEncapsulatedKey, HybridMultiRecipientsPublicKeys, + HybridPrivateKey, HybridPublicKey, }; use crate::layers::traits::{ InnerWriterTrait, InnerWriterType, LayerFailSafeReader, LayerReader, LayerWriter, @@ -16,7 +18,7 @@ use crate::config::{ArchiveReaderConfig, ArchiveWriterConfig}; use crate::errors::ConfigError; use hpke::HpkeError; use kem::{Decapsulate, Encapsulate}; -use rand::{Rng, SeedableRng}; +use rand::SeedableRng; use rand_chacha::{rand_core::CryptoRngCore, ChaChaRng}; use serde::{Deserialize, Deserializer, Serialize}; @@ -25,10 +27,6 @@ use zeroize::Zeroize; use super::traits::InnerReaderTrait; const CIPHER_BUF_SIZE: u64 = 4096; -// This is the size of the nonce taken as input -const NONCE_SIZE: usize = 12; -/// Type standing for the unique nonce per-archive -type ArchiveNonce = [u8; NONCE_SIZE]; const CHUNK_SIZE: u64 = 128 * 1024; const ASSOCIATED_DATA: &[u8; 0] = b""; @@ -41,10 +39,7 @@ const KEY_COMMITMENT_CHAIN: &[u8; KEY_COMMITMENT_SIZE] = b"-KEY COMMITMENT--KEY COMMITMENT--KEY COMMITMENT--KEY COMMITMENT-"; /// Encrypt the hardcoded `KEY_COMMITMENT_CHAIN` with the given key and nonce -fn build_key_commitment_chain( - key: &Key, - nonce: &ArchiveNonce, -) -> Result { +fn build_key_commitment_chain(key: &Key, nonce: &Nonce) -> Result { let mut key_commitment = [0u8; KEY_COMMITMENT_SIZE]; key_commitment.copy_from_slice(KEY_COMMITMENT_CHAIN); let mut cipher = AesGcm256::new(key, &compute_nonce(nonce, 0), ASSOCIATED_DATA)?; @@ -59,7 +54,7 @@ fn build_key_commitment_chain( fn check_key_commitment( key: &Key, - nonce: &ArchiveNonce, + nonce: &Nonce, commitment: &KeyCommitmentAndTag, ) -> Result<(), ConfigError> { let mut key_commitment = commitment.key_commitment; @@ -150,15 +145,14 @@ pub(crate) fn get_crypto_rng() -> impl CryptoRngCore { /// Part of this data must be kept secret and drop as soon as possible pub(crate) struct InternalEncryptionConfig { pub(crate) key: Key, - pub(crate) nonce: ArchiveNonce, + pub(crate) nonce: Nonce, } impl InternalEncryptionConfig { - fn from(key: Key) -> Self { - let mut csprng = get_crypto_rng(); - let nonce = csprng.gen::(); + fn from(shared_secret: HybridKemSharedSecret) -> Result { + let (key, nonce) = key_schedule_s(&shared_secret.0)?; - Self { key, nonce } + Ok(Self { key, nonce }) } } @@ -167,8 +161,6 @@ impl InternalEncryptionConfig { pub struct EncryptionPersistentConfig { /// Key-wrapping for each recipients pub multi_recipient: HybridMultiRecipientEncapsulatedKey, - /// Nonce for the archive AES-GCM - nonce: ArchiveNonce, /// Encrypted version of the hardcoded `KEY_COMMITMENT_CHAIN` key_commitment: KeyCommitmentAndTag, } @@ -199,10 +191,11 @@ impl EncryptionConfig { &self, ) -> Result<(EncryptionPersistentConfig, InternalEncryptionConfig), ConfigError> { // Generate then encapsulate the main key for each recipients - let (multi_recipient, key) = self.public_keys.encapsulate(&mut get_crypto_rng())?; + let (multi_recipient, ss_hybrid) = self.public_keys.encapsulate(&mut get_crypto_rng())?; // Generate the main encrypt layer nonce and keep the main key for internal use - let cryptographic_material = InternalEncryptionConfig::from(key); + let cryptographic_material = InternalEncryptionConfig::from(ss_hybrid) + .or(Err(ConfigError::KeyWrappingComputationError))?; // Add a key commitment let key_commitment = @@ -210,11 +203,9 @@ impl EncryptionConfig { .or(Err(ConfigError::KeyCommitmentComputationError))?; // Create the persistent version, to be exported - let nonce = cryptographic_material.nonce; Ok(( EncryptionPersistentConfig { multi_recipient, - nonce, key_commitment, }, cryptographic_material, @@ -236,7 +227,7 @@ pub struct EncryptionReaderConfig { private_keys: Vec, /// Symmetric encryption key and nonce, if decrypted successfully from header // TODO: split in two, like InternalEncryptionConfig - encrypt_parameters: Option<(Key, ArchiveNonce)>, + encrypt_parameters: Option<(Key, Nonce)>, } impl EncryptionReaderConfig { @@ -249,8 +240,10 @@ impl EncryptionReaderConfig { return Err(ConfigError::PrivateKeyNotSet); } for private_key in &self.private_keys { - if let Ok(key) = private_key.decapsulate(&config.multi_recipient) { - self.encrypt_parameters = Some((key, config.nonce)); + if let Ok(ss_hybrid) = private_key.decapsulate(&config.multi_recipient) { + let (key, nonce) = key_schedule_s(&ss_hybrid.0) + .or(Err(ConfigError::KeyWrappingComputationError))?; + self.encrypt_parameters = Some((key, nonce)); break; }; } @@ -272,7 +265,7 @@ impl ArchiveReaderConfig { } /// Retrieve key and nonce used for encryption - pub fn get_encrypt_parameters(&self) -> Option<(Key, ArchiveNonce)> { + pub fn get_encrypt_parameters(&self) -> Option<(Key, Nonce)> { self.encrypt.encrypt_parameters } } @@ -285,7 +278,7 @@ pub(crate) struct EncryptionLayerWriter<'a, W: 'a + InnerWriterTrait> { /// Symmetric encryption Key key: Key, /// Symmetric encryption nonce prefix, see `compute_nonce` - nonce_prefix: ArchiveNonce, + base_nonce: Nonce, current_chunk_offset: u64, current_ctr: u64, } @@ -298,7 +291,7 @@ impl<'a, W: 'a + InnerWriterTrait> EncryptionLayerWriter<'a, W> { Ok(Self { inner, key: internal_config.key, - nonce_prefix: internal_config.nonce, + base_nonce: internal_config.nonce, cipher: AesGcm256::new( &internal_config.key, &compute_nonce(&internal_config.nonce, FIRST_DATA_CHUNK_NUMBER), @@ -318,7 +311,7 @@ impl<'a, W: 'a + InnerWriterTrait> EncryptionLayerWriter<'a, W> { self.current_chunk_offset = 0; let cipher = AesGcm256::new( &self.key, - &compute_nonce(&self.nonce_prefix, self.current_ctr), + &compute_nonce(&self.base_nonce, self.current_ctr), ASSOCIATED_DATA, )?; let old_cipher = std::mem::replace(&mut self.cipher, cipher); @@ -387,7 +380,7 @@ pub struct EncryptionLayerReader<'a, R: Read + Seek> { inner: Box>, cipher: AesGcm256, key: Key, - nonce: ArchiveNonce, + nonce: Nonce, chunk_cache: Cursor>, /// Current chunk number in the data. /// Note: the actual chunk number used in the cipher is offseted by FIRST_DATA_CHUNK_NUMBER @@ -580,7 +573,7 @@ pub struct EncryptionLayerFailSafeReader<'a, R: Read> { inner: Box>, cipher: AesGcm256, key: Key, - nonce: ArchiveNonce, + nonce: Nonce, current_chunk_number: u64, current_chunk_offset: u64, } @@ -663,12 +656,12 @@ mod tests { use rand::SeedableRng; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; - use crate::crypto::aesgcm::KEY_SIZE; + use crate::crypto::aesgcm::{KEY_SIZE, NONCE_AES_SIZE}; use crate::layers::raw::{RawLayerFailSafeReader, RawLayerReader, RawLayerWriter}; static FAKE_FILE: [u8; 26] = *b"abcdefghijklmnopqrstuvwxyz"; static KEY: Key = [2u8; KEY_SIZE]; - static NONCE: ArchiveNonce = [3u8; NONCE_SIZE]; + static NONCE: Nonce = [3u8; NONCE_AES_SIZE]; fn encrypt_write(file: Vec) -> Vec { // Instantiate a EncryptionLayerWriter and fill it with FAKE_FILE @@ -836,7 +829,7 @@ mod tests { fn build_key_commitment_chain_test() { // Build the encrypted key commitment chain let key: Key = [1u8; KEY_SIZE]; - let nonce: ArchiveNonce = [2u8; NONCE_SIZE]; + let nonce: Nonce = [2u8; NONCE_AES_SIZE]; let result = build_key_commitment_chain(&key, &nonce); assert!(result.is_ok()); let key_commitment_and_tag = result.unwrap(); @@ -854,7 +847,7 @@ mod tests { fn check_key_commitment_test() { // Build the encrypted key commitment chain let key: Key = [1u8; KEY_SIZE]; - let nonce: ArchiveNonce = [2u8; NONCE_SIZE]; + let nonce: Nonce = [2u8; NONCE_AES_SIZE]; let result = build_key_commitment_chain(&key, &nonce); assert!(result.is_ok()); let key_commitment_and_tag = result.unwrap(); From 04db37ade34c7c9cec788fb00d044d9e31debb77 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Tue, 13 Aug 2024 10:26:38 +0200 Subject: [PATCH 09/29] Encrypt: Zeroize on drop InternalEncryptionConfig --- mla/src/layers/encrypt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mla/src/layers/encrypt.rs b/mla/src/layers/encrypt.rs index 6e00d898..c4b7197d 100644 --- a/mla/src/layers/encrypt.rs +++ b/mla/src/layers/encrypt.rs @@ -22,7 +22,7 @@ use rand::SeedableRng; use rand_chacha::{rand_core::CryptoRngCore, ChaChaRng}; use serde::{Deserialize, Deserializer, Serialize}; -use zeroize::Zeroize; +use zeroize::{Zeroize, ZeroizeOnDrop}; use super::traits::InnerReaderTrait; @@ -140,7 +140,7 @@ pub(crate) fn get_crypto_rng() -> impl CryptoRngCore { ChaChaRng::from_entropy() } -#[derive(Zeroize)] +#[derive(Zeroize, ZeroizeOnDrop)] /// Cryptographic material used for encryption in the Encrypt layer /// Part of this data must be kept secret and drop as soon as possible pub(crate) struct InternalEncryptionConfig { From 461d072b87f8ebdcb8e8b8892a480717c35039b8 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Tue, 13 Aug 2024 10:45:01 +0200 Subject: [PATCH 10/29] Encrypt: use HPKE to wrap/unwrap the intermediate recipient's shared secret --- mla/src/crypto/hpke.rs | 9 +++------ mla/src/crypto/hybrid.rs | 31 +++++++++++++++---------------- mla/src/layers/encrypt.rs | 8 ++++++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs index be80d842..21dc9c7c 100644 --- a/mla/src/crypto/hpke.rs +++ b/mla/src/crypto/hpke.rs @@ -102,9 +102,6 @@ const HPKE_MODE_BASE: u8 = 0; /// Hybrid : DHKEM(X25519, HKDF-SHA256) + MLKEM const HYBRID_KEM_ID: u16 = 0x1020; -/// `info` to bound the HPKE usage to MLA -const HPKE_INFO: &[u8] = b"MLA Encrypt Layer"; - /// Return the suite_id for the Hybrid KEM (RFC 9180 §5.1) /// suite_id = concat( /// "HPKE", @@ -131,7 +128,7 @@ fn build_suite_id(kem_id: u16) -> [u8; 10] { /// Parameters are: /// - `shared_secret`: the shared secret from the Hybrid KEM /// - mode: set to Base (no PSK nor sender key) -/// - `info`` +/// - `info`: the info to use in HPKE /// - psk: no PSK, because the mode used is "Base" /// - algorithms: /// - Kem: `kem_id` @@ -178,8 +175,8 @@ fn key_schedule_s_internal( /// - Kem: HYBRID_KEM_ID (custom value) /// - Kdf: HKDF-SHA512 /// - Aead: AES-GCM-256 -pub(crate) fn key_schedule_s(shared_secret: &[u8]) -> Result<(Key, Nonce), Error> { - key_schedule_s_internal(shared_secret, HPKE_INFO, HYBRID_KEM_ID) +pub(crate) fn key_schedule_s(shared_secret: &[u8], info: &[u8]) -> Result<(Key, Nonce), Error> { + key_schedule_s_internal(shared_secret, info, HYBRID_KEM_ID) } /// Compute the nonce for a given sequence number (RFC 9180 §5.2) diff --git a/mla/src/crypto/hybrid.rs b/mla/src/crypto/hybrid.rs index 12b90a00..6a2e9d37 100644 --- a/mla/src/crypto/hybrid.rs +++ b/mla/src/crypto/hybrid.rs @@ -1,5 +1,5 @@ -use crate::crypto::aesgcm::{ConstantTimeEq, Key, KEY_SIZE, NONCE_AES_SIZE, TAG_LENGTH}; -use crate::crypto::hpke::{dhkem_decap, dhkem_encap, DHKEMCiphertext}; +use crate::crypto::aesgcm::{ConstantTimeEq, Key, KEY_SIZE, TAG_LENGTH}; +use crate::crypto::hpke::{dhkem_decap, dhkem_encap, key_schedule_s, DHKEMCiphertext}; use crate::errors::ConfigError; use crate::layers::encrypt::get_crypto_rng; use hkdf::Hkdf; @@ -13,6 +13,9 @@ use sha2::Sha256; use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret as X25519StaticSecret}; use zeroize::{Zeroize, ZeroizeOnDrop}; +/// `info` to bound the HPKE usage to the MLA Recipient derivation +const HPKE_INFO_RECIPIENT: &[u8] = b"MLA Recipient"; + /// Common structures for ML-KEM 1024 type MLKEMCiphertext = [u8; 1568]; /// ML-KEM 1024 "private key" @@ -123,8 +126,6 @@ struct HybridRecipientEncapsulatedKey { wrapped_ss: EncryptedSharedSecret, /// Associated tag tag: [u8; TAG_LENGTH], - /// Associated nonce - nonce: [u8; NONCE_AES_SIZE], } /// Key encapsulated for multiple recipient with hybrid cryptography @@ -186,10 +187,13 @@ impl Decapsulate for &recipient.ct_ml, ); + let (unwrap_key, unwrap_nonce) = key_schedule_s(&ss_recipient, HPKE_INFO_RECIPIENT) + .or(Err(ConfigError::KeyWrappingComputationError))?; + // Unwrap the candidate shared secret and check it using AES-GCM tag validation let mut cipher = crate::crypto::aesgcm::AesGcm256::new( - &ss_recipient, - &recipient.nonce, + &unwrap_key, + &unwrap_nonce, HYBRIDKEM_ASSOCIATED_DATA, ) .or(Err(ConfigError::KeyWrappingComputationError))?; @@ -251,10 +255,11 @@ impl Encapsulate let ss_recipient = combine(&ss_ecc.0, ss_ml, &ct_ecc.to_bytes(), ct_ml); // Wrap the final shared secret - let nonce = csprng.gen::<[u8; NONCE_AES_SIZE]>(); + let (wrap_key, wrap_nonce) = key_schedule_s(&ss_recipient, HPKE_INFO_RECIPIENT) + .or(Err(ConfigError::KeyWrappingComputationError))?; let mut cipher = crate::crypto::aesgcm::AesGcm256::new( - &ss_recipient, - &nonce, + &wrap_key, + &wrap_nonce, HYBRIDKEM_ASSOCIATED_DATA, ) .or(Err(ConfigError::KeyWrappingComputationError))?; @@ -269,7 +274,6 @@ impl Encapsulate ct_ecc, wrapped_ss, tag, - nonce, }); } @@ -508,12 +512,7 @@ mod tests { // Ensure materials have enough entropy // This is a naive check, using compression, to avoid naive bugs - let materials: Vec<&[u8]> = vec![ - &ss_hybrid.0, - &recipient.nonce, - &recipient.tag, - &recipient.wrapped_ss, - ]; + let materials: Vec<&[u8]> = vec![&ss_hybrid.0, &recipient.tag, &recipient.wrapped_ss]; for material in materials { let mut compressed = brotli::CompressorReader::new(material, 0, 0, 0); let mut compressed_data = Vec::new(); diff --git a/mla/src/layers/encrypt.rs b/mla/src/layers/encrypt.rs index c4b7197d..d288e629 100644 --- a/mla/src/layers/encrypt.rs +++ b/mla/src/layers/encrypt.rs @@ -75,6 +75,10 @@ fn check_key_commitment( const FIRST_DATA_CHUNK_NUMBER: u64 = 1; // ---------- Config ---------- + +/// `info` to bound the HPKE usage to the MLA Encrypt Layer +const HPKE_INFO_LAYER: &[u8] = b"MLA Encrypt Layer"; + /// Encrypted Key commitment and associated tag struct KeyCommitmentAndTag { key_commitment: [u8; KEY_COMMITMENT_SIZE], @@ -150,7 +154,7 @@ pub(crate) struct InternalEncryptionConfig { impl InternalEncryptionConfig { fn from(shared_secret: HybridKemSharedSecret) -> Result { - let (key, nonce) = key_schedule_s(&shared_secret.0)?; + let (key, nonce) = key_schedule_s(&shared_secret.0, HPKE_INFO_LAYER)?; Ok(Self { key, nonce }) } @@ -241,7 +245,7 @@ impl EncryptionReaderConfig { } for private_key in &self.private_keys { if let Ok(ss_hybrid) = private_key.decapsulate(&config.multi_recipient) { - let (key, nonce) = key_schedule_s(&ss_hybrid.0) + let (key, nonce) = key_schedule_s(&ss_hybrid.0, HPKE_INFO_LAYER) .or(Err(ConfigError::KeyWrappingComputationError))?; self.encrypt_parameters = Some((key, nonce)); break; From f2aec20987a8af24ecc895704275adb73a214fb7 Mon Sep 17 00:00:00 2001 From: Camille Mougey Date: Tue, 13 Aug 2024 11:05:17 +0200 Subject: [PATCH 11/29] HPKE: two distinct KEM are actually used, one per recipient, one for the whole Hybrid KEM --- mla/src/crypto/hpke.rs | 42 ++++++++++++++++++++------------------- mla/src/crypto/hybrid.rs | 14 ++++++++----- mla/src/layers/encrypt.rs | 6 +++--- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs index 21dc9c7c..ea9f095f 100644 --- a/mla/src/crypto/hpke.rs +++ b/mla/src/crypto/hpke.rs @@ -99,8 +99,11 @@ type HpkeAead = HPKEAesGcm256; const HPKE_MODE_BASE: u8 = 0; /// Custom KEM ID, not in the RFC 9180 -/// Hybrid : DHKEM(X25519, HKDF-SHA256) + MLKEM +/// Hybrid : DHKEM(X25519, HKDF-SHA256) + MLKEM, wrapping a shared secret to support multi-recipient const HYBRID_KEM_ID: u16 = 0x1020; +/// Custom KEM ID, not in the RFC 9180 +/// Hybrid Recipient : DHKEM(X25519, HKDF-SHA256) + MLKEM, used internally in the Hybrid KEM to wrap the per-recipient shared secret +const HYBRID_KEM_RECIPIENT_ID: u16 = 0x1120; /// Return the suite_id for the Hybrid KEM (RFC 9180 §5.1) /// suite_id = concat( @@ -121,9 +124,7 @@ fn build_suite_id(kem_id: u16) -> [u8; 10] { out } -/// Key schedule for the sender (RFC 9180 §5.1), internal version -/// -/// This version is kept for testing purpose (against RFC 9180 test vectors) +/// Key schedule (RFC 9180 §5.1), mode Base /// /// Parameters are: /// - `shared_secret`: the shared secret from the Hybrid KEM @@ -134,7 +135,7 @@ fn build_suite_id(kem_id: u16) -> [u8; 10] { /// - Kem: `kem_id` /// - Kdf: HKDF-SHA512 /// - Aead: AES-GCM-256 -fn key_schedule_s_internal( +fn key_schedule_base( shared_secret: &[u8], info: &[u8], kem_id: u16, @@ -164,19 +165,20 @@ fn key_schedule_s_internal( Ok((key, base_nonce)) } -/// Key schedule for the sender (RFC 9180 §5.1) -/// -/// Parameters are: -/// - `shared_secret`: the shared secret from the Hybrid KEM -/// - mode: set to Base (no PSK nor sender key) -/// - info: set to "MLA Encrypt Layer" -/// - psk: no PSK, because the mode used is "Base" -/// - algorithms: -/// - Kem: HYBRID_KEM_ID (custom value) -/// - Kdf: HKDF-SHA512 -/// - Aead: AES-GCM-256 -pub(crate) fn key_schedule_s(shared_secret: &[u8], info: &[u8]) -> Result<(Key, Nonce), Error> { - key_schedule_s_internal(shared_secret, info, HYBRID_KEM_ID) +/// Key schedule (RFC 9180 §5.1), mode Base, for the custom multi-recipient Hybrid KEM +pub(crate) fn key_schedule_base_hybrid_kem( + shared_secret: &[u8], + info: &[u8], +) -> Result<(Key, Nonce), Error> { + key_schedule_base(shared_secret, info, HYBRID_KEM_ID) +} + +/// Key schedule (RFC 9180 §5.1), mode Base, for the custom per-recipient Hybrid KEM +pub(crate) fn key_schedule_base_hybrid_kem_recipient( + shared_secret: &[u8], + info: &[u8], +) -> Result<(Key, Nonce), Error> { + key_schedule_base(shared_secret, info, HYBRID_KEM_RECIPIENT_ID) } /// Compute the nonce for a given sequence number (RFC 9180 §5.2) @@ -323,9 +325,9 @@ mod tests { /// Use A.6 for HKDF-SHA512 and AES-256-GCM /// In MLA, we rather use a custom Kem ID (Hybrid KEM), but this method does the main job #[test] - fn test_key_schedule_s_internal() { + fn test_key_schedule_base() { let (key, nonce) = - key_schedule_s_internal(&RFC_A6_SHARED_SECRET, &RFC_A6_INFO, RFC_A6_KEM_ID).unwrap(); + key_schedule_base(&RFC_A6_SHARED_SECRET, &RFC_A6_INFO, RFC_A6_KEM_ID).unwrap(); assert_eq!(key, RFC_A6_KEY); assert_eq!(nonce, RFC_A6_BASE_NONCE); } diff --git a/mla/src/crypto/hybrid.rs b/mla/src/crypto/hybrid.rs index 6a2e9d37..7815a5a9 100644 --- a/mla/src/crypto/hybrid.rs +++ b/mla/src/crypto/hybrid.rs @@ -1,5 +1,7 @@ use crate::crypto::aesgcm::{ConstantTimeEq, Key, KEY_SIZE, TAG_LENGTH}; -use crate::crypto::hpke::{dhkem_decap, dhkem_encap, key_schedule_s, DHKEMCiphertext}; +use crate::crypto::hpke::{ + dhkem_decap, dhkem_encap, key_schedule_base_hybrid_kem_recipient, DHKEMCiphertext, +}; use crate::errors::ConfigError; use crate::layers::encrypt::get_crypto_rng; use hkdf::Hkdf; @@ -187,8 +189,9 @@ impl Decapsulate for &recipient.ct_ml, ); - let (unwrap_key, unwrap_nonce) = key_schedule_s(&ss_recipient, HPKE_INFO_RECIPIENT) - .or(Err(ConfigError::KeyWrappingComputationError))?; + let (unwrap_key, unwrap_nonce) = + key_schedule_base_hybrid_kem_recipient(&ss_recipient, HPKE_INFO_RECIPIENT) + .or(Err(ConfigError::KeyWrappingComputationError))?; // Unwrap the candidate shared secret and check it using AES-GCM tag validation let mut cipher = crate::crypto::aesgcm::AesGcm256::new( @@ -255,8 +258,9 @@ impl Encapsulate let ss_recipient = combine(&ss_ecc.0, ss_ml, &ct_ecc.to_bytes(), ct_ml); // Wrap the final shared secret - let (wrap_key, wrap_nonce) = key_schedule_s(&ss_recipient, HPKE_INFO_RECIPIENT) - .or(Err(ConfigError::KeyWrappingComputationError))?; + let (wrap_key, wrap_nonce) = + key_schedule_base_hybrid_kem_recipient(&ss_recipient, HPKE_INFO_RECIPIENT) + .or(Err(ConfigError::KeyWrappingComputationError))?; let mut cipher = crate::crypto::aesgcm::AesGcm256::new( &wrap_key, &wrap_nonce, diff --git a/mla/src/layers/encrypt.rs b/mla/src/layers/encrypt.rs index d288e629..b69f78b2 100644 --- a/mla/src/layers/encrypt.rs +++ b/mla/src/layers/encrypt.rs @@ -2,7 +2,7 @@ use crate::crypto::aesgcm::{ AesGcm256, ConstantTimeEq, Key, Nonce, Tag, KEY_COMMITMENT_SIZE, TAG_LENGTH, }; -use crate::crypto::hpke::{compute_nonce, key_schedule_s}; +use crate::crypto::hpke::{compute_nonce, key_schedule_base_hybrid_kem}; use crate::crypto::hybrid::{ HybridKemSharedSecret, HybridMultiRecipientEncapsulatedKey, HybridMultiRecipientsPublicKeys, HybridPrivateKey, HybridPublicKey, @@ -154,7 +154,7 @@ pub(crate) struct InternalEncryptionConfig { impl InternalEncryptionConfig { fn from(shared_secret: HybridKemSharedSecret) -> Result { - let (key, nonce) = key_schedule_s(&shared_secret.0, HPKE_INFO_LAYER)?; + let (key, nonce) = key_schedule_base_hybrid_kem(&shared_secret.0, HPKE_INFO_LAYER)?; Ok(Self { key, nonce }) } @@ -245,7 +245,7 @@ impl EncryptionReaderConfig { } for private_key in &self.private_keys { if let Ok(ss_hybrid) = private_key.decapsulate(&config.multi_recipient) { - let (key, nonce) = key_schedule_s(&ss_hybrid.0, HPKE_INFO_LAYER) + let (key, nonce) = key_schedule_base_hybrid_kem(&ss_hybrid.0, HPKE_INFO_LAYER) .or(Err(ConfigError::KeyWrappingComputationError))?; self.encrypt_parameters = Some((key, nonce)); break; From 4d519f9ea28a85d35552665d4707fcb4cd51af92 Mon Sep 17 00:00:00 2001 From: commial Date: Thu, 15 Aug 2024 22:47:11 +0200 Subject: [PATCH 12/29] HPKE: apply @extiop suggestion --- mla/src/crypto/hpke.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mla/src/crypto/hpke.rs b/mla/src/crypto/hpke.rs index ea9f095f..efd7c053 100644 --- a/mla/src/crypto/hpke.rs +++ b/mla/src/crypto/hpke.rs @@ -87,7 +87,7 @@ pub(crate) fn dhkem_decap( // // rust-hpke provides `setup_sender` and `setup_receiver` // Unfortunately, re-using their code means: -// - implementing the rust-hpke `Kem` traitsuse hpke::kdf::LabeledExpand; for our Hybrid encryption KEM, while we are not yet able to convert a private key to a public one +// - implementing the rust-hpke `Kem` trait for our Hybrid encryption KEM, while we are not yet able to convert a private key to a public one // - re-implement the AEAD trait for our AesGcm, to be able to repair // - implementing struct to use suite ID which are not in the RFC (because we are using our own Hybrid KEM) // From f69bfe189950f47257ac6e2b7c7f45036eb193e0 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Sun, 3 Nov 2024 16:46:02 +0100 Subject: [PATCH 13/29] clippy fix + gitignore update --- .gitignore | 3 +++ mla/src/layers/compress.rs | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 13969af1..8525d342 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ debug/ debugs/ target/ + +# cargo lock file +Cargo.lock \ No newline at end of file diff --git a/mla/src/layers/compress.rs b/mla/src/layers/compress.rs index efd7cea4..09b0eb9f 100644 --- a/mla/src/layers/compress.rs +++ b/mla/src/layers/compress.rs @@ -478,9 +478,8 @@ impl WriterWithCount { impl Write for WriterWithCount { fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf).map(|i| { + self.inner.write(buf).inspect(|&i| { self.pos += i as u32; - i }) } From b84c5ab8fa1dc5ef08c574091fe6bcceaa785139 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Sun, 3 Nov 2024 16:57:50 +0100 Subject: [PATCH 14/29] Bump actions/download-artifact from 1 to 4.1.8 --- .github/workflows/mla_release.yml | 10 +++++----- .github/workflows/mlar_release.yml | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/mla_release.yml b/.github/workflows/mla_release.yml index e02087c1..4cd17958 100644 --- a/.github/workflows/mla_release.yml +++ b/.github/workflows/mla_release.yml @@ -116,27 +116,27 @@ jobs: draft: true - name: Download linux-x86_64 artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mla-linux-x86_64 - name: Download windows-i686 artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mla-windows-i686 - name: Download windows-x86_64 artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mla-windows-x86_64 - name: Download windows-i686-debug artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mla-windows-i686-debug - name: Download windows-x86_64-debug artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mla-windows-x86_64-debug diff --git a/.github/workflows/mlar_release.yml b/.github/workflows/mlar_release.yml index 568221f9..f8d1a4f6 100644 --- a/.github/workflows/mlar_release.yml +++ b/.github/workflows/mlar_release.yml @@ -82,17 +82,17 @@ jobs: draft: true - name: Download Linux artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mlar-linux - name: Download Windows artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mlar-windows - name: Download MacOS artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v4.1.8 with: name: mlar-macos From f3a74f4aaafa3957cab7ad78ee83bb8dfb630e9d Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Sun, 3 Nov 2024 17:05:33 +0100 Subject: [PATCH 15/29] Bump actions/upload-artifact and actions/upload-release-asset too --- .github/workflows/mla_release.yml | 16 ++++++++-------- .github/workflows/mlar_release.yml | 8 ++++---- .github/workflows/py-bindings.yml | 6 +++--- .github/workflows/test.yml | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/mla_release.yml b/.github/workflows/mla_release.yml index 4cd17958..7e3fce83 100644 --- a/.github/workflows/mla_release.yml +++ b/.github/workflows/mla_release.yml @@ -80,7 +80,7 @@ jobs: command: build args: ${{ matrix.cargo_arg }} --manifest-path=bindings/C/Cargo.toml --target=${{ matrix.target }} - name: Upload resulting 'mla' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4.4.3 with: name: mla-${{ matrix.build }} path: ${{ matrix.path }} @@ -141,7 +141,7 @@ jobs: name: mla-windows-x86_64-debug - name: Release Linux artifact - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -158,7 +158,7 @@ jobs: zip --junk-paths windows-x86_64-debug mla-windows-x86_64-debug/mla.dll mla-windows-x86_64-debug/mla.lib mla-windows-x86_64-debug/mla.dll.lib mla-windows-x86_64-debug/mla.pdb - name: Release windows-i686 - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -168,7 +168,7 @@ jobs: asset_name: libmla-windows-i686-${{ steps.get_version.outputs.VERSION }}.zip - name: Release windows-x86_64 - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -178,7 +178,7 @@ jobs: asset_name: libmla-windows-x86_64-${{ steps.get_version.outputs.VERSION }}.zip - name: Release windows-i686-debug - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -188,7 +188,7 @@ jobs: asset_name: libmla-windows-i686-debug-${{ steps.get_version.outputs.VERSION }}.zip - name: Release windows-x86_64-debug - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -199,7 +199,7 @@ jobs: - uses: actions/checkout@v2 - name: Release C Header file - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -209,7 +209,7 @@ jobs: asset_name: mla.h - name: Release CPP Header file - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/mlar_release.yml b/.github/workflows/mlar_release.yml index f8d1a4f6..2670c279 100644 --- a/.github/workflows/mlar_release.yml +++ b/.github/workflows/mlar_release.yml @@ -46,7 +46,7 @@ jobs: if: matrix.build == 'linux' run: strip ./target/${{ matrix.target }}/release/mlar${{ matrix.extension }} - name: Upload resulting 'mlar' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4.4.3 with: name: mlar-${{ matrix.build }} path: ./target/${{ matrix.target }}/release/mlar${{ matrix.extension }} @@ -97,7 +97,7 @@ jobs: name: mlar-macos - name: Release Linux artifact - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -107,7 +107,7 @@ jobs: asset_name: mlar-linux-static-${{ steps.get_version.outputs.VERSION }} - name: Release Windows artifact - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -117,7 +117,7 @@ jobs: asset_name: mlar-windows-${{ steps.get_version.outputs.VERSION }}.exe - name: Release MacOS artifact - uses: actions/upload-release-asset@v1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/py-bindings.yml b/.github/workflows/py-bindings.yml index f9afbc7b..54c50e3c 100644 --- a/.github/workflows/py-bindings.yml +++ b/.github/workflows/py-bindings.yml @@ -32,7 +32,7 @@ jobs: sccache: 'true' manylinux: auto - name: Upload wheels - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.3 with: name: wheels path: dist @@ -63,7 +63,7 @@ jobs: args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' - name: Upload wheels - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.3 with: name: wheels path: dist @@ -92,7 +92,7 @@ jobs: args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' - name: Upload wheels - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4.4.3 with: name: wheels path: dist diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e7e7414..9ec8f1b1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - name: Run tests run: cargo test --all --exclude mla-fuzz-afl --release --verbose - name: Upload resulting 'mlar' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v4.4.3 with: name: ${{ matrix.build }} path: ./target/release/mlar${{ matrix.extension }} From 0bf543fd69a7eda85840f4adda1c1990c4c6718f Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Sun, 3 Nov 2024 17:40:56 +0100 Subject: [PATCH 16/29] Fix cbindgen for C++ header change --- bindings/C/mla.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/C/mla.hpp b/bindings/C/mla.hpp index 363d591c..473c1ca1 100644 --- a/bindings/C/mla.hpp +++ b/bindings/C/mla.hpp @@ -178,4 +178,4 @@ MLAStatus mla_roarchive_extract(MLAConfigHandle *config, /// Get info on an existing MLA archive MLAStatus mla_roarchive_info(MlaReadCallback read_callback, void *context, ArchiveInfo *info_out); -} // extern "C" +} // extern "C" From 5685e151103e1001909fc2e71bf3ded26d9ca477 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 01:09:50 +0100 Subject: [PATCH 17/29] Fix dry run for mlakey-parser --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ec8f1b1..12ffc3c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -186,8 +186,8 @@ jobs: - uses: actions-rs/toolchain@v1 with: toolchain: stable - - name: Dry-run publish curve25519-parser - run: cd curve25519-parser && cargo publish --dry-run + - name: Dry-run publish mlakey-parser + run: cd mlakey-parser && cargo publish --dry-run - name: Dry-run publish mla run: cd mla && cargo publish --dry-run - name: Dry-run publish mlar From 71a3d6575996562912870a682f9fcec7f398bce6 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 02:03:12 +0100 Subject: [PATCH 18/29] curve25519_parser -> mlakey_parser --- bindings/python/Cargo.toml | 2 +- bindings/python/src/lib.rs | 2 +- mla/Cargo.toml | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index a21f1908..50276190 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -22,4 +22,4 @@ crate-type = ["cdylib"] pyo3 = "0" mla = { version = "1", features = ["send"], path = "../../mla"} x25519-dalek = "2" -curve25519-parser = { path = "../../curve25519-parser", version = "0.4" } +mlakey-parser = { path = "../../mlakey-parser", version = "0.1" } diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 1b3525c1..b7d413ce 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -5,7 +5,7 @@ use std::{ io::{self, Read}, }; -use curve25519_parser::{parse_openssl_25519_privkey, parse_openssl_25519_pubkey}; +use mlakey_parser::{parse_openssl_25519_privkey, parse_openssl_25519_pubkey}; use mla::{ config::{ArchiveReaderConfig, ArchiveWriterConfig}, ArchiveReader, ArchiveWriter, Layers, diff --git a/mla/Cargo.toml b/mla/Cargo.toml index 05287696..86f0e708 100644 --- a/mla/Cargo.toml +++ b/mla/Cargo.toml @@ -14,7 +14,7 @@ readme = "../README.md" [dependencies] rand = { version = "0.8", default-features = false, features = ["getrandom", "std"]} rand_chacha = { version = "0.3", default-features = false} -brotli = { version = "6.0", default-features = false, features = ["std"]} +brotli = { version = "7.0", default-features = false, features = ["std"]} bitflags = { version = "2.1", default-features = false, features = ["serde"]} byteorder = { version = "1.3", default-features = false, features = ["std"] } serde = { version = "1", default-features = false, features = ["derive"] } @@ -43,7 +43,8 @@ hex-literal = { version = "0.4", default-features = false} aes-gcm = { version = "0.10", default-features = false, features = ["aes"]} aead = { version = "0.5", default-features = false, features = ["alloc"]} criterion = { version = "0.5", default-features = false} -curve25519-parser = { version = "0.4", default-features = false } +# mlakey-parser = { version = "0.4", default-features = false } +mlakey-parser = { path = "../mlakey-parser", version = "0.1", default-features = false } hex = { version = "0.4", default-features = false, features = ["alloc"]} static_assertions = { version = "1", default-features = false } From 5c30e0fbcc75faa3653ee30a33ed9e2f3d04be4f Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 04:24:35 +0100 Subject: [PATCH 19/29] WIP update python bindings for pyO3 breaking changes --- bindings/python/Cargo.toml | 2 +- bindings/python/src/lib.rs | 89 ++++++++++++++++++------------- bindings/python/tests/test_mla.py | 8 +-- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index 50276190..7043b207 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -19,7 +19,7 @@ name = "pymla" crate-type = ["cdylib"] [dependencies] -pyo3 = "0" +pyo3 = {version = "0", features = ["gil-refs"]} mla = { version = "1", features = ["send"], path = "../../mla"} x25519-dalek = "2" mlakey-parser = { path = "../../mlakey-parser", version = "0.1" } diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index b7d413ce..0d015823 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -40,9 +40,9 @@ create_exception!( ); create_exception!( mla, - InvalidECCKeyFormat, + InvalidKeyFormat, MLAError, - "Supplied ECC key is not in the expected format" + "Supplied MLA key is not in the expected format" ); create_exception!(mla, WrongBlockSubFileType, MLAError, "Wrong BlockSubFile magic has been encountered. Is the deserializion tarting at the beginning of a block?"); create_exception!( @@ -131,6 +131,12 @@ create_exception!( MLAError, "Unable to expand while using the HKDF" ); +create_exception!( + mla, + HPKEError, + MLAError, + "Error during HPKE computation" +); // Convert potentials errors to the wrapped type @@ -168,7 +174,7 @@ impl From for PyErr { mla::errors::Error::AssertionError(msg) => PyErr::new::(msg), mla::errors::Error::WrongMagic => PyErr::new::("Wrong magic, must be \"MLA\""), mla::errors::Error::UnsupportedVersion => PyErr::new::("Unsupported version, must be 1"), - mla::errors::Error::InvalidECCKeyFormat => PyErr::new::("Supplied ECC key is not in the expected format"), + mla::errors::Error::InvalidKeyFormat => PyErr::new::("Supplied MLA key is not in the expected format"), mla::errors::Error::WrongBlockSubFileType => PyErr::new::("Wrong BlockSubFile magic has been encountered. Is the deserializion tarting at the beginning of a block?"), mla::errors::Error::UTF8ConversionError(err) => PyErr::new::(err), mla::errors::Error::FilenameTooLong => PyErr::new::("Filenames have a limited size `FILENAME_MAX_SIZE`"), @@ -186,6 +192,7 @@ impl From for PyErr { mla::errors::Error::DuplicateFilename => PyErr::new::("Filename already used"), mla::errors::Error::AuthenticatedDecryptionWrongTag => PyErr::new::("Wrong tag while decrypting authenticated data"), mla::errors::Error::HKDFInvalidKeyLength => PyErr::new::("Unable to expand while using the HKDF"), + mla::errors::Error::HPKEError(msg) => PyErr::new::(msg), } }, WrappedError::WrappedPy(inner_err) => inner_err @@ -255,12 +262,12 @@ impl PublicKeys { file.read_to_end(&mut buf)?; keys.push( parse_openssl_25519_pubkey(&buf) - .map_err(|_| mla::errors::Error::InvalidECCKeyFormat)?, + .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else if let Ok(data) = element.downcast::() { keys.push( parse_openssl_25519_pubkey(data.as_bytes()) - .map_err(|_| mla::errors::Error::InvalidECCKeyFormat)?, + .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else { return Err( @@ -319,12 +326,12 @@ impl PrivateKeys { file.read_to_end(&mut buf)?; keys.push( parse_openssl_25519_privkey(&buf) - .map_err(|_| mla::errors::Error::InvalidECCKeyFormat)?, + .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else if let Ok(data) = element.downcast::() { keys.push( parse_openssl_25519_privkey(data.as_bytes()) - .map_err(|_| mla::errors::Error::InvalidECCKeyFormat)?, + .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else { return Err( @@ -639,7 +646,7 @@ macro_rules! check_mode { impl MLAFile { #[new] #[pyo3(signature = (path, mode="r", config=None))] - fn new(path: &str, mode: &str, config: Option<&PyAny>) -> Result { + fn new(path: &str, mode: &str, config: Option<&Bound<'_, PyAny>>) -> Result { match mode { "r" => { let rconfig = match config { @@ -807,11 +814,13 @@ impl MLAFile { slf } + // cf. https://pyo3.rs/v0.22.5/function/signature + #[pyo3(signature = (exc_type=None, _exc_value=None, _traceback=None))] fn __exit__( &mut self, - exc_type: Option<&PyAny>, - _exc_value: Option<&PyAny>, - _traceback: Option<&PyAny>, + exc_type: Option<&Bound<'_, PyAny>>, + _exc_value: Option<&Bound<'_, PyAny>>, + _traceback: Option<&Bound<'_, PyAny>>, ) -> Result { if exc_type.is_some() { // An exception occured, let it be raised again @@ -834,7 +843,7 @@ impl MLAFile { // Purpose: only one import #[classattr] fn _buffered_type(py: Python) -> Result<&PyType, WrappedError> { - Ok(py.import("io")?.getattr("BufferedIOBase")?.extract()?) + Ok(py.import_bound("io")?.getattr("BufferedIOBase")?.extract()?) } /// Write an archive file to @dest, which can be: @@ -857,7 +866,7 @@ impl MLAFile { &mut self, py: Python, key: &str, - dest: &PyAny, + dest: &Bound, chunk_size: usize, ) -> Result<(), WrappedError> { let reader = check_mode!(mut self, Read); @@ -871,7 +880,7 @@ impl MLAFile { // `/path/to/dest` let mut output = std::fs::File::create(dest.to_string())?; io::copy(&mut archive_file.unwrap().data, &mut output)?; - } else if dest.is_instance(py.get_type::().getattr("_buffered_type")?)? { + } else if dest.is_instance(&py.get_type_bound::().getattr("_buffered_type")?)? { // isinstance(dest, io.BufferedIOBase) // offer `.write` (`.close` must be called from the caller) @@ -912,7 +921,7 @@ impl MLAFile { &mut self, py: Python, key: &str, - src: &PyAny, + src: &Bound, chunk_size: usize, ) -> Result<(), WrappedError> { let writer = check_mode!(mut self, Write); @@ -922,7 +931,7 @@ impl MLAFile { // `/path/to/src` let mut input = std::fs::File::open(src.to_string())?; writer.add_file(key, input.metadata()?.len(), &mut input)?; - } else if src.is_instance(py.get_type::().getattr("_buffered_type")?)? { + } else if src.is_instance(&py.get_type_bound::().getattr("_buffered_type")?)? { // isinstance(src, io.BufferedIOBase) // offer `.read` (`.close` must be called from the caller) @@ -953,7 +962,7 @@ impl MLAFile { /// Instanciate the Python module #[pymodule] #[pyo3(name = "mla")] -fn pymla(py: Python, m: &PyModule) -> PyResult<()> { +fn pymla(py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { // Classes m.add_class::()?; m.add_class::()?; @@ -963,41 +972,45 @@ fn pymla(py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; // Exceptions - m.add("MLAError", py.get_type::())?; - m.add("WrongMagic", py.get_type::())?; - m.add("UnsupportedVersion", py.get_type::())?; - m.add("InvalidECCKeyFormat", py.get_type::())?; + m.add("MLAError", py.get_type_bound::())?; + m.add("WrongMagic", py.get_type_bound::())?; + m.add("UnsupportedVersion", py.get_type_bound::())?; + m.add("InvalidKeyFormat", py.get_type_bound::())?; m.add( "WrongBlockSubFileType", - py.get_type::(), + py.get_type_bound::(), )?; - m.add("UTF8ConversionError", py.get_type::())?; - m.add("FilenameTooLong", py.get_type::())?; + m.add("UTF8ConversionError", py.get_type_bound::())?; + m.add("FilenameTooLong", py.get_type_bound::())?; m.add( "WrongArchiveWriterState", - py.get_type::(), + py.get_type_bound::(), )?; - m.add("WrongReaderState", py.get_type::())?; - m.add("WrongWriterState", py.get_type::())?; - m.add("RandError", py.get_type::())?; - m.add("PrivateKeyNeeded", py.get_type::())?; + m.add("WrongReaderState", py.get_type_bound::())?; + m.add("WrongWriterState", py.get_type_bound::())?; + m.add("RandError", py.get_type_bound::())?; + m.add("PrivateKeyNeeded", py.get_type_bound::())?; m.add( "DeserializationError", - py.get_type::(), + py.get_type_bound::(), )?; - m.add("SerializationError", py.get_type::())?; - m.add("MissingMetadata", py.get_type::())?; - m.add("BadAPIArgument", py.get_type::())?; - m.add("EndOfStream", py.get_type::())?; - m.add("ConfigError", py.get_type::())?; - m.add("DuplicateFilename", py.get_type::())?; + m.add("SerializationError", py.get_type_bound::())?; + m.add("MissingMetadata", py.get_type_bound::())?; + m.add("BadAPIArgument", py.get_type_bound::())?; + m.add("EndOfStream", py.get_type_bound::())?; + m.add("ConfigError", py.get_type_bound::())?; + m.add("DuplicateFilename", py.get_type_bound::())?; m.add( "AuthenticatedDecryptionWrongTag", - py.get_type::(), + py.get_type_bound::(), )?; m.add( "HKDFInvalidKeyLength", - py.get_type::(), + py.get_type_bound::(), + )?; + m.add( + "HPKEError", + py.get_type_bound::(), )?; // Add constants diff --git a/bindings/python/tests/test_mla.py b/bindings/python/tests/test_mla.py index 80cd530d..7b875015 100644 --- a/bindings/python/tests/test_mla.py +++ b/bindings/python/tests/test_mla.py @@ -266,7 +266,7 @@ def test_writer_config_compression(): def test_public_keys(): "Test the PublicKeys object" # Bad parsing - with pytest.raises(mla.InvalidECCKeyFormat): + with pytest.raises(mla.InvalidKeyFormat): mla.PublicKeys(b"NOT A KEY") with pytest.raises(FileNotFoundError): @@ -304,10 +304,10 @@ def test_public_keys(): def test_private_keys(): "Test the PrivateKeys object" # Bad parsing - with pytest.raises(mla.InvalidECCKeyFormat): + with pytest.raises(mla.InvalidKeyFormat): mla.PrivateKeys(b"NOT A KEY") - with pytest.raises(mla.InvalidECCKeyFormat): + with pytest.raises(mla.InvalidKeyFormat): # This is a public key, not a private one mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem")) @@ -348,7 +348,7 @@ def test_writer_config_public_keys(): # Test API call config = mla.WriterConfig() - with pytest.raises(mla.InvalidECCKeyFormat): + with pytest.raises(mla.InvalidKeyFormat): config.set_public_keys(mla.PublicKeys(b"NOT A KEY")) # Test shortcut on object build From 20ae2ba2d70255e1fb3339fe29c9994b3e64eed5 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:18:06 +0100 Subject: [PATCH 20/29] Added MLKEM keys handle in python bindings --- bindings/python/Cargo.toml | 1 + bindings/python/src/lib.rs | 44 +++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index 7043b207..e7defe50 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -21,5 +21,6 @@ crate-type = ["cdylib"] [dependencies] pyo3 = {version = "0", features = ["gil-refs"]} mla = { version = "1", features = ["send"], path = "../../mla"} +ml-kem = "0.1.1" x25519-dalek = "2" mlakey-parser = { path = "../../mlakey-parser", version = "0.1" } diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 0d015823..fb83b292 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -5,11 +5,13 @@ use std::{ io::{self, Read}, }; -use mlakey_parser::{parse_openssl_25519_privkey, parse_openssl_25519_pubkey}; +use mlakey_parser::{parse_mlakey_privkey, parse_mlakey_pubkey}; use mla::{ + crypto::hybrid::{HybridPrivateKey, HybridPublicKey}, config::{ArchiveReaderConfig, ArchiveWriterConfig}, ArchiveReader, ArchiveWriter, Layers, }; +use ml_kem::EncodedSizeUser; use pyo3::{ create_exception, exceptions::{PyKeyError, PyRuntimeError, PyTypeError}, @@ -226,7 +228,7 @@ impl FileMetadata { // -------- mla.PublicKeys -------- -/// Represents multiple ECC Public Keys +/// Represents multiple ECC and MLKEM Public Keys /// /// Instanciate with path (as string) or data (as bytes) /// PEM and DER format are supported @@ -242,7 +244,7 @@ impl FileMetadata { #[derive(Clone)] #[pyclass] struct PublicKeys { - keys: Vec, + keys: Vec } #[pymethods] @@ -257,16 +259,16 @@ impl PublicKeys { // "/path/to/public.pem" if let Ok(path) = element.downcast::() { let mut file = File::open(path.to_string())?; - // Load the the ECC key in-memory and parse it + // Load the the ECC and MLKEM keys in-memory and parse it let mut buf = Vec::new(); file.read_to_end(&mut buf)?; keys.push( - parse_openssl_25519_pubkey(&buf) + parse_mlakey_pubkey(&buf) .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else if let Ok(data) = element.downcast::() { keys.push( - parse_openssl_25519_pubkey(data.as_bytes()) + parse_mlakey_pubkey(data.as_bytes()) .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else { @@ -283,14 +285,20 @@ impl PublicKeys { fn keys(&self) -> Vec> { self.keys .iter() - .map(|pubkey| Cow::Owned(Vec::from(pubkey.to_bytes()))) + .map(|pubkey| { + let mut result = Vec::new(); + result.extend(pubkey.public_key_ecc.to_bytes()); + result.extend(pubkey.public_key_ml.as_bytes()); + Cow::Owned(result) + }) + //Cow::Owned(Vec::from(pubkey.public_key_ecc.to_bytes()))) .collect() } } // -------- mla.PrivateKeys -------- -/// Represents multiple ECC Private Keys +/// Represents multiple ECC and MLKEM Private Keys /// /// Instanciate with path (as string) or data (as bytes) /// PEM and DER format are supported @@ -306,7 +314,7 @@ impl PublicKeys { #[derive(Clone)] #[pyclass] struct PrivateKeys { - keys: Vec, + keys: Vec, } #[pymethods] @@ -314,23 +322,24 @@ impl PrivateKeys { #[new] #[pyo3(signature = (*args))] fn new(args: &PyTuple) -> Result { - let mut keys = Vec::new(); + let mut keys: Vec = Vec::new(); for element in args { // String argument: this is a path // "/path/to/public.pem" if let Ok(path) = element.downcast::() { let mut file = File::open(path.to_string())?; - // Load the the ECC key in-memory and parse it + // Load the the ECC and MLKEM keys in-memory and parse it let mut buf = Vec::new(); file.read_to_end(&mut buf)?; + keys.push( - parse_openssl_25519_privkey(&buf) + parse_mlakey_privkey(&buf) .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, - ); + ); } else if let Ok(data) = element.downcast::() { keys.push( - parse_openssl_25519_privkey(data.as_bytes()) + parse_mlakey_privkey(&data.as_bytes()) .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else { @@ -348,7 +357,12 @@ impl PrivateKeys { fn keys(&self) -> Vec> { self.keys .iter() - .map(|privkey| Cow::Owned(Vec::from(privkey.to_bytes()))) + .map(|privkey| { + let mut result = Vec::new(); + result.extend(privkey.private_key_ecc.to_bytes()); + result.extend(privkey.private_key_ml.as_bytes()); + Cow::Owned(result) + }) .collect() } } From fe6ada23c9d3878b4cc3432cd94d819b7a24b3cc Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:36:52 +0100 Subject: [PATCH 21/29] More fixes for python bindings --- bindings/python/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index fb83b292..085fc01e 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -194,7 +194,7 @@ impl From for PyErr { mla::errors::Error::DuplicateFilename => PyErr::new::("Filename already used"), mla::errors::Error::AuthenticatedDecryptionWrongTag => PyErr::new::("Wrong tag while decrypting authenticated data"), mla::errors::Error::HKDFInvalidKeyLength => PyErr::new::("Unable to expand while using the HKDF"), - mla::errors::Error::HPKEError(msg) => PyErr::new::(msg), + mla::errors::Error::HPKEError(msg) => PyErr::new::(format!("{:}", msg)), } }, WrappedError::WrappedPy(inner_err) => inner_err @@ -339,7 +339,7 @@ impl PrivateKeys { ); } else if let Ok(data) = element.downcast::() { keys.push( - parse_mlakey_privkey(&data.as_bytes()) + parse_mlakey_privkey(data.as_bytes()) .map_err(|_| mla::errors::Error::InvalidKeyFormat)?, ); } else { From 69224ab4c52f4f354325fee536e56bc2609c2a81 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:48:31 +0100 Subject: [PATCH 22/29] rollback actions/upload-artifact cf. issues#478 --- .github/workflows/mla_release.yml | 2 +- .github/workflows/mlar_release.yml | 2 +- .github/workflows/py-bindings.yml | 6 +++--- .github/workflows/test.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/mla_release.yml b/.github/workflows/mla_release.yml index 7e3fce83..572e69ed 100644 --- a/.github/workflows/mla_release.yml +++ b/.github/workflows/mla_release.yml @@ -80,7 +80,7 @@ jobs: command: build args: ${{ matrix.cargo_arg }} --manifest-path=bindings/C/Cargo.toml --target=${{ matrix.target }} - name: Upload resulting 'mla' - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v3.2.1 with: name: mla-${{ matrix.build }} path: ${{ matrix.path }} diff --git a/.github/workflows/mlar_release.yml b/.github/workflows/mlar_release.yml index 2670c279..2f41b4c0 100644 --- a/.github/workflows/mlar_release.yml +++ b/.github/workflows/mlar_release.yml @@ -46,7 +46,7 @@ jobs: if: matrix.build == 'linux' run: strip ./target/${{ matrix.target }}/release/mlar${{ matrix.extension }} - name: Upload resulting 'mlar' - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v3.2.1 with: name: mlar-${{ matrix.build }} path: ./target/${{ matrix.target }}/release/mlar${{ matrix.extension }} diff --git a/.github/workflows/py-bindings.yml b/.github/workflows/py-bindings.yml index 54c50e3c..abae4f0e 100644 --- a/.github/workflows/py-bindings.yml +++ b/.github/workflows/py-bindings.yml @@ -32,7 +32,7 @@ jobs: sccache: 'true' manylinux: auto - name: Upload wheels - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v3.2.1 with: name: wheels path: dist @@ -63,7 +63,7 @@ jobs: args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' - name: Upload wheels - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v3.2.1 with: name: wheels path: dist @@ -92,7 +92,7 @@ jobs: args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' - name: Upload wheels - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v3.2.1 with: name: wheels path: dist diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12ffc3c4..2eb3f59b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - name: Run tests run: cargo test --all --exclude mla-fuzz-afl --release --verbose - name: Upload resulting 'mlar' - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v3.2.1 with: name: ${{ matrix.build }} path: ./target/release/mlar${{ matrix.extension }} From 27f6847844632b22bc407af91f378283e0ec27c8 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:09:14 +0100 Subject: [PATCH 23/29] MLKEM adds also for pytests --- bindings/python/tests/test_mla.py | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/bindings/python/tests/test_mla.py b/bindings/python/tests/test_mla.py index 7b875015..bfe5c039 100644 --- a/bindings/python/tests/test_mla.py +++ b/bindings/python/tests/test_mla.py @@ -273,22 +273,22 @@ def test_public_keys(): mla.PublicKeys("/tmp/does_not_exists") # Open a PEM key, through path - pkeys_pem = mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem")) + pkeys_pem = mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem")) assert len(pkeys_pem.keys) == 1 # Open a DER key, through path - pkeys_der = mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.der")) + pkeys_der = mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_mlakey_pub.der")) assert len(pkeys_pem.keys) == 1 # Keys must be the same assert pkeys_pem.keys == pkeys_der.keys # Open a PEM key, through data - pkeys_pem = mla.PublicKeys(open(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem"), "rb").read()) + pkeys_pem = mla.PublicKeys(open(os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem"), "rb").read()) assert len(pkeys_pem.keys) == 1 # Open a DER key, through data - pkeys_pem = mla.PublicKeys(open(os.path.join(SAMPLE_PATH, "test_ed25519_pub.der"), "rb").read()) + pkeys_pem = mla.PublicKeys(open(os.path.join(SAMPLE_PATH, "test_mlakey_pub.der"), "rb").read()) assert len(pkeys_pem.keys) == 1 # Keys must be the same @@ -296,8 +296,8 @@ def test_public_keys(): # Open several keys, using both path and data pkeys = mla.PublicKeys( - os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem"), - open(os.path.join(SAMPLE_PATH, "test_x25519_2_pub.pem"), "rb").read() + os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem"), + open(os.path.join(SAMPLE_PATH, "test_mlakey_2_pub.pem"), "rb").read() ) assert len(pkeys.keys) == 2 @@ -309,28 +309,28 @@ def test_private_keys(): with pytest.raises(mla.InvalidKeyFormat): # This is a public key, not a private one - mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem")) + mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem")) with pytest.raises(FileNotFoundError): mla.PrivateKeys("/tmp/does_not_exists") # Open a PEM key, through path - pkeys_pem = mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519.pem")) + pkeys_pem = mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey.pem")) assert len(pkeys_pem.keys) == 1 # Open a DER key, through path - pkeys_der = mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519.der")) + pkeys_der = mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey.der")) assert len(pkeys_pem.keys) == 1 # Keys must be the same assert pkeys_pem.keys == pkeys_der.keys # Open a PEM key, through data - pkeys_pem = mla.PrivateKeys(open(os.path.join(SAMPLE_PATH, "test_ed25519.pem"), "rb").read()) + pkeys_pem = mla.PrivateKeys(open(os.path.join(SAMPLE_PATH, "test_mlakey.pem"), "rb").read()) assert len(pkeys_pem.keys) == 1 # Open a DER key, through data - pkeys_pem = mla.PrivateKeys(open(os.path.join(SAMPLE_PATH, "test_ed25519.der"), "rb").read()) + pkeys_pem = mla.PrivateKeys(open(os.path.join(SAMPLE_PATH, "test_mlakey.der"), "rb").read()) assert len(pkeys_pem.keys) == 1 # Keys must be the same @@ -338,8 +338,8 @@ def test_private_keys(): # Open several keys, using both path and data pkeys = mla.PrivateKeys( - os.path.join(SAMPLE_PATH, "test_ed25519.pem"), - open(os.path.join(SAMPLE_PATH, "test_x25519_2.pem"), "rb").read() + os.path.join(SAMPLE_PATH, "test_mlakey.pem"), + open(os.path.join(SAMPLE_PATH, "test_mlakey_2.pem"), "rb").read() ) assert len(pkeys.keys) == 2 @@ -353,15 +353,15 @@ def test_writer_config_public_keys(): # Test shortcut on object build config = mla.WriterConfig( - public_keys=mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem")) + public_keys=mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem")) ) # Test the getter assert len(config.public_keys.keys) == 1 # Chaining out = config.set_public_keys(mla.PublicKeys( - os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem"), - open(os.path.join(SAMPLE_PATH, "test_x25519_2_pub.pem"), "rb").read() + os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem"), + open(os.path.join(SAMPLE_PATH, "test_mlakey_2_pub.pem"), "rb").read() )) assert out is config assert len(config.public_keys.keys) == 2 @@ -385,17 +385,17 @@ def test_reader_config_api(): assert config.private_keys is None config.set_private_keys( - mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519.pem")) + mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey.pem")) ) assert len(config.private_keys.keys) == 1 - config = mla.ReaderConfig(private_keys=mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519.pem"))) + config = mla.ReaderConfig(private_keys=mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey.pem"))) assert len(config.private_keys.keys) == 1 # Chaining config = mla.ReaderConfig() out = config.set_private_keys( - mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519.pem")), + mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey.pem")), ) assert out is config @@ -404,7 +404,7 @@ def test_write_then_read_encrypted(): # Create the archive path = tempfile.mkstemp(suffix=".mla")[1] with MLAFile(path, "w", config=mla.WriterConfig( - public_keys=mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem")), + public_keys=mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem")), layers=mla.LAYER_ENCRYPT )) as archive: for name, data in FILES.items(): @@ -412,7 +412,7 @@ def test_write_then_read_encrypted(): # Read the archive with MLAFile(path, config=mla.ReaderConfig( - private_keys=mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_ed25519.pem")) + private_keys=mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey.pem")) )) as archive: assert sorted(archive.keys()) == sorted(list(FILES.keys())) for name, data in FILES.items(): @@ -423,7 +423,7 @@ def test_read_encrypted_archive_bad_key(): # Create the archive path = tempfile.mkstemp(suffix=".mla")[1] with MLAFile(path, "w", config=mla.WriterConfig( - public_keys=mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_ed25519_pub.pem")), + public_keys=mla.PublicKeys(os.path.join(SAMPLE_PATH, "test_mlakey_pub.pem")), layers=mla.LAYER_ENCRYPT )) as archive: for name, data in FILES.items(): @@ -437,7 +437,7 @@ def test_read_encrypted_archive_bad_key(): # Try to read with an incorrect key (mla.ConfigError: PrivateKeyNotFound) with pytest.raises(mla.ConfigError): with MLAFile(path, config=mla.ReaderConfig( - private_keys=mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_x25519_2.pem")) + private_keys=mla.PrivateKeys(os.path.join(SAMPLE_PATH, "test_mlakey_2.pem")) )) as archive: pass From 90a8990b7340199105a6548b5545b62640bd7a40 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Thu, 7 Nov 2024 01:22:12 +0100 Subject: [PATCH 24/29] Update py-bindings workflow --- .github/workflows/py-bindings.yml | 171 +++++++++++++++++++++++++----- 1 file changed, 143 insertions(+), 28 deletions(-) diff --git a/.github/workflows/py-bindings.yml b/.github/workflows/py-bindings.yml index abae4f0e..ab6be97a 100644 --- a/.github/workflows/py-bindings.yml +++ b/.github/workflows/py-bindings.yml @@ -15,93 +15,208 @@ permissions: jobs: linux: - runs-on: ubuntu-latest + runs-on: ${{ matrix.platform.runner }} strategy: matrix: - target: [x86_64, x86, aarch64, armv7, s390x, ppc64le] + platform: + - runner: ubuntu-latest + target: x86_64 + - runner: ubuntu-latest + target: x86 + - runner: ubuntu-latest + target: aarch64 + - runner: ubuntu-latest + target: armv7 + - runner: ubuntu-latest + target: s390x + - runner: ubuntu-latest + target: ppc64le steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - name: Build wheels uses: PyO3/maturin-action@v1 with: - target: ${{ matrix.target }} + target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' manylinux: auto - name: Upload wheels - uses: actions/upload-artifact@v3.2.1 + uses: actions/upload-artifact@v4 with: - name: wheels + name: wheels-linux-${{ matrix.platform.target }} path: dist - name: pytest - if: ${{ startsWith(matrix.target, 'x86_64') }} + if: ${{ startsWith(matrix.platform.target, 'x86_64') }} shell: bash run: | set -e + python3 -m venv .venv + source .venv/bin/activate pip install mla-archive --find-links dist --force-reinstall pip install pytest cd bindings/python && pytest + - name: pytest + if: ${{ !startsWith(matrix.platform.target, 'x86') && matrix.platform.target != 'ppc64' }} + uses: uraimo/run-on-arch-action@v2 + with: + arch: ${{ matrix.platform.target }} + distro: ubuntu22.04 + githubToken: ${{ github.token }} + install: | + apt-get update + apt-get install -y --no-install-recommends python3 python3-pip + pip3 install -U pip pytest + run: | + set -e + pip3 install mla-archive --find-links dist --force-reinstall + cd bindings/python && pytest + + musllinux: + runs-on: ${{ matrix.platform.runner }} + strategy: + matrix: + platform: + - runner: ubuntu-latest + target: x86_64 + - runner: ubuntu-latest + target: x86 + - runner: ubuntu-latest + target: aarch64 + - runner: ubuntu-latest + target: armv7 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.platform.target }} + args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml + sccache: 'true' + manylinux: musllinux_1_2 + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-musllinux-${{ matrix.platform.target }} + path: dist + - name: pytest + if: ${{ startsWith(matrix.platform.target, 'x86_64') }} + uses: addnab/docker-run-action@v3 + with: + image: alpine:latest + options: -v ${{ github.workspace }}:/io -w /io + run: | + set -e + apk add py3-pip py3-virtualenv + python3 -m virtualenv .venv + source .venv/bin/activate + pip install mla-archive --no-index --find-links dist --force-reinstall + pip install pytest + cd bindings/python && pytest + - name: pytest + if: ${{ !startsWith(matrix.platform.target, 'x86') }} + uses: uraimo/run-on-arch-action@v2 + with: + arch: ${{ matrix.platform.target }} + distro: alpine_latest + githubToken: ${{ github.token }} + install: | + apk add py3-virtualenv + run: | + set -e + python3 -m virtualenv .venv + source .venv/bin/activate + pip install pytest + pip install mla-archive --find-links dist --force-reinstall + cd bindings/python && pytest windows: - runs-on: windows-latest + runs-on: ${{ matrix.platform.runner }} strategy: matrix: - target: [x64, x86] + platform: + - runner: windows-latest + target: x64 + - runner: windows-latest + target: x86 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.10' - architecture: ${{ matrix.target }} + python-version: '3.11' + architecture: ${{ matrix.platform.target }} - name: Build wheels uses: PyO3/maturin-action@v1 with: - target: ${{ matrix.target }} + target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' - name: Upload wheels - uses: actions/upload-artifact@v3.2.1 + uses: actions/upload-artifact@v4 with: - name: wheels + name: wheels-windows-${{ matrix.platform.target }} path: dist - name: pytest + if: ${{ !startsWith(matrix.platform.target, 'aarch64') }} shell: bash run: | set -e + python3 -m venv .venv + source .venv/Scripts/activate pip install mla-archive --find-links dist --force-reinstall pip install pytest cd bindings/python && pytest macos: - runs-on: macos-latest + runs-on: ${{ matrix.platform.runner }} strategy: matrix: - target: [x86_64, aarch64] + platform: + - runner: macos-12 + target: x86_64 + - runner: macos-14 + target: aarch64 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' - name: Build wheels uses: PyO3/maturin-action@v1 with: - target: ${{ matrix.target }} + target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml sccache: 'true' - name: Upload wheels - uses: actions/upload-artifact@v3.2.1 + uses: actions/upload-artifact@v4 with: - name: wheels + name: wheels-macos-${{ matrix.platform.target }} path: dist - name: pytest - if: ${{ !startsWith(matrix.target, 'aarch64') }} - shell: bash run: | set -e + python3 -m venv .venv + source .venv/bin/activate pip install mla-archive --find-links dist --force-reinstall pip install pytest cd bindings/python && pytest + sdist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist --manifest-path bindings/python/Cargo.toml + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: wheels-sdist + path: dist From 917b0330e1facb699e512c253df8f94409b4869d Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Thu, 7 Nov 2024 02:04:04 +0100 Subject: [PATCH 25/29] Actual fix for Py-bindings workflow --- .github/workflows/py-bindings.yml | 83 ++----------------------------- .gitignore | 5 +- bindings/python/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 81 deletions(-) diff --git a/.github/workflows/py-bindings.yml b/.github/workflows/py-bindings.yml index ab6be97a..57b9c785 100644 --- a/.github/workflows/py-bindings.yml +++ b/.github/workflows/py-bindings.yml @@ -2,12 +2,13 @@ # # maturin generate-ci github --pytest -m bindings/python/Cargo.toml # -# Using maturin v1.4.0 +# Using maturin v1.7.1 name: Py-bindings on: push: - branches: [ master ] + branches: + - master pull_request: permissions: @@ -58,82 +59,6 @@ jobs: pip install mla-archive --find-links dist --force-reinstall pip install pytest cd bindings/python && pytest - - name: pytest - if: ${{ !startsWith(matrix.platform.target, 'x86') && matrix.platform.target != 'ppc64' }} - uses: uraimo/run-on-arch-action@v2 - with: - arch: ${{ matrix.platform.target }} - distro: ubuntu22.04 - githubToken: ${{ github.token }} - install: | - apt-get update - apt-get install -y --no-install-recommends python3 python3-pip - pip3 install -U pip pytest - run: | - set -e - pip3 install mla-archive --find-links dist --force-reinstall - cd bindings/python && pytest - - musllinux: - runs-on: ${{ matrix.platform.runner }} - strategy: - matrix: - platform: - - runner: ubuntu-latest - target: x86_64 - - runner: ubuntu-latest - target: x86 - - runner: ubuntu-latest - target: aarch64 - - runner: ubuntu-latest - target: armv7 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.platform.target }} - args: --release --out dist --find-interpreter --manifest-path bindings/python/Cargo.toml - sccache: 'true' - manylinux: musllinux_1_2 - - name: Upload wheels - uses: actions/upload-artifact@v4 - with: - name: wheels-musllinux-${{ matrix.platform.target }} - path: dist - - name: pytest - if: ${{ startsWith(matrix.platform.target, 'x86_64') }} - uses: addnab/docker-run-action@v3 - with: - image: alpine:latest - options: -v ${{ github.workspace }}:/io -w /io - run: | - set -e - apk add py3-pip py3-virtualenv - python3 -m virtualenv .venv - source .venv/bin/activate - pip install mla-archive --no-index --find-links dist --force-reinstall - pip install pytest - cd bindings/python && pytest - - name: pytest - if: ${{ !startsWith(matrix.platform.target, 'x86') }} - uses: uraimo/run-on-arch-action@v2 - with: - arch: ${{ matrix.platform.target }} - distro: alpine_latest - githubToken: ${{ github.token }} - install: | - apk add py3-virtualenv - run: | - set -e - python3 -m virtualenv .venv - source .venv/bin/activate - pip install pytest - pip install mla-archive --find-links dist --force-reinstall - cd bindings/python && pytest windows: runs-on: ${{ matrix.platform.runner }} @@ -219,4 +144,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: wheels-sdist - path: dist + path: dist \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8525d342..a8facbe6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ debugs/ target/ # cargo lock file -Cargo.lock \ No newline at end of file +Cargo.lock + +# pycache for Py-bindings +__pycache__/ \ No newline at end of file diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index e7defe50..d7b08991 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pymla" -version = "0.1.0" +version = "0.2.0" edition = "2021" authors = ["Camille Mougey "] license = "LGPL-3.0-only" From 1f69ccaf8b4b28d62cc730df206d20480cf01420 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:06:12 +0100 Subject: [PATCH 26/29] Improvement in upload-artifact --- .github/workflows/mla_release.yml | 2 +- .github/workflows/mlar_release.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mla_release.yml b/.github/workflows/mla_release.yml index 572e69ed..d90b22c1 100644 --- a/.github/workflows/mla_release.yml +++ b/.github/workflows/mla_release.yml @@ -80,7 +80,7 @@ jobs: command: build args: ${{ matrix.cargo_arg }} --manifest-path=bindings/C/Cargo.toml --target=${{ matrix.target }} - name: Upload resulting 'mla' - uses: actions/upload-artifact@v3.2.1 + uses: actions/upload-artifact@v4 with: name: mla-${{ matrix.build }} path: ${{ matrix.path }} diff --git a/.github/workflows/mlar_release.yml b/.github/workflows/mlar_release.yml index 2f41b4c0..7493fb6e 100644 --- a/.github/workflows/mlar_release.yml +++ b/.github/workflows/mlar_release.yml @@ -46,7 +46,7 @@ jobs: if: matrix.build == 'linux' run: strip ./target/${{ matrix.target }}/release/mlar${{ matrix.extension }} - name: Upload resulting 'mlar' - uses: actions/upload-artifact@v3.2.1 + uses: actions/upload-artifact@v4 with: name: mlar-${{ matrix.build }} path: ./target/${{ matrix.target }}/release/mlar${{ matrix.extension }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2eb3f59b..a9ab57b8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,7 +42,7 @@ jobs: - name: Run tests run: cargo test --all --exclude mla-fuzz-afl --release --verbose - name: Upload resulting 'mlar' - uses: actions/upload-artifact@v3.2.1 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.build }} path: ./target/release/mlar${{ matrix.extension }} From ce660ff93ca9c443ee9060e1db7d47bd2a2bdcf4 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Thu, 7 Nov 2024 23:52:28 +0100 Subject: [PATCH 27/29] Fix C/C++ Linux bindings --- bindings/C/tests/linux-gcc-g++/info.c | 2 +- bindings/C/tests/linux-gcc-g++/open.c | 2 +- samples/archive_v2.mla | Bin 0 -> 27906 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 samples/archive_v2.mla diff --git a/bindings/C/tests/linux-gcc-g++/info.c b/bindings/C/tests/linux-gcc-g++/info.c index c1b7e6e6..50891cf1 100644 --- a/bindings/C/tests/linux-gcc-g++/info.c +++ b/bindings/C/tests/linux-gcc-g++/info.c @@ -24,7 +24,7 @@ int main() MLAStatus status; ArchiveInfo archive_info; - FILE *f = fopen("../../../../samples/archive_v1.mla", "r"); + FILE *f = fopen("../../../../samples/archive_v2.mla", "r"); if (!f) { fprintf(stderr, " [!] Cannot open file: %d\n", errno); diff --git a/bindings/C/tests/linux-gcc-g++/open.c b/bindings/C/tests/linux-gcc-g++/open.c index c71d0582..0ecc7526 100644 --- a/bindings/C/tests/linux-gcc-g++/open.c +++ b/bindings/C/tests/linux-gcc-g++/open.c @@ -122,7 +122,7 @@ int main() return (int)status; } - FILE *f = fopen("../../../../samples/archive_v1.mla", "r"); + FILE *f = fopen("../../../../samples/archive_v2.mla", "r"); if (!f) { fprintf(stderr, " [!] Cannot open file: %d\n", errno); diff --git a/samples/archive_v2.mla b/samples/archive_v2.mla new file mode 100644 index 0000000000000000000000000000000000000000..9bf88d17f4afdca8073083774430eae77afe2bdc GIT binary patch literal 27906 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 x==mf0 Date: Thu, 7 Nov 2024 23:59:21 +0100 Subject: [PATCH 28/29] Fix C/C++ Windows bindings --- bindings/C/tests/windows-msvc/src/read.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/C/tests/windows-msvc/src/read.c b/bindings/C/tests/windows-msvc/src/read.c index d46418ec..13811b9f 100644 --- a/bindings/C/tests/windows-msvc/src/read.c +++ b/bindings/C/tests/windows-msvc/src/read.c @@ -90,7 +90,7 @@ int test_reader_info() MLAStatus status; ArchiveInfo archive_info; - HANDLE hFile = CreateFile(TEXT("../../../../samples/archive_v1.mla"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFile = CreateFile(TEXT("../../../../samples/archive_v2.mla"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { fprintf(stderr, " [!] Cannot open file: %d\n", GetLastError()); @@ -166,7 +166,7 @@ int test_reader_extract() } FILE *f; - if (fopen_s(&f, "../../../../samples/archive_v1.mla", "r")) + if (fopen_s(&f, "../../../../samples/archive_v2.mla", "r")) { fprintf(stderr, " [!] Cannot open file: %d\n", errno); return 1; From 4eaf98ddcbfec6cae24823a736e6208b22fc6286 Mon Sep 17 00:00:00 2001 From: extiop <29679238+extiop@users.noreply.github.com> Date: Mon, 11 Nov 2024 02:40:00 +0100 Subject: [PATCH 29/29] Add all Cargo.lock --- .gitignore | 5 +- Cargo.lock | 1652 ++++++++++++++++++++++++++++++++++++ bindings/python/Cargo.lock | 913 ++++++++++++++++++++ 3 files changed, 2566 insertions(+), 4 deletions(-) create mode 100644 Cargo.lock create mode 100644 bindings/python/Cargo.lock diff --git a/.gitignore b/.gitignore index a8facbe6..48855308 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,5 @@ debug/ debugs/ target/ -# cargo lock file -Cargo.lock - -# pycache for Py-bindings +# pycache for py-bindings __pycache__/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..409bbc27 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1652 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "afl" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bb240a3b9ff18002142c1a736e98046461d51a694d687c3e7329b456ab0fe4" +dependencies = [ + "home", + "libc", + "rustc_version", + "xdg", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "assert_cmd" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "assert_fs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efdb1fdb47602827a342857666feb372712cbc64b414172bd6b167a02927674" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates", + "predicates-core", + "predicates-tree", + "tempfile", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hpke" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4917627a14198c3603282c5158b815ad5534795451d3c074b53cf3cee0960b11" +dependencies = [ + "aead", + "aes-gcm", + "chacha20poly1305", + "digest", + "generic-array", + "hkdf", + "hmac", + "rand_core", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "hybrid-array" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a9a965bb102c1c891fb017c09a05c965186b1265a207640f323ddd009f9deb" +dependencies = [ + "typenum", +] + +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kem" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8645470337db67b01a7f966decf7d0bafedbae74147d33e641c67a91df239f" +dependencies = [ + "rand_core", + "zeroize", +] + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "ml-kem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "860a7986a65e8b6c965082dfeba382302ddad057c21e0830373463e612e67217" +dependencies = [ + "hybrid-array", + "kem", + "rand_core", + "sha3", +] + +[[package]] +name = "mla" +version = "1.4.0" +dependencies = [ + "aead", + "aes", + "aes-gcm", + "bincode", + "bitflags", + "brotli", + "byteorder", + "criterion", + "ctr", + "digest", + "generic-array", + "ghash", + "hex", + "hex-literal", + "hkdf", + "hpke", + "kem", + "ml-kem", + "mlakey-parser", + "rand", + "rand_chacha", + "serde", + "serde-big-array", + "sha2", + "static_assertions", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "mla-bindings-c" +version = "1.0.0" +dependencies = [ + "mla", + "mlakey-parser", +] + +[[package]] +name = "mla-fuzz-afl" +version = "0.1.0" +dependencies = [ + "afl", + "bincode", + "mla", + "mlakey-parser", + "serde", +] + +[[package]] +name = "mlakey-parser" +version = "0.1.0" +dependencies = [ + "curve25519-dalek", + "der-parser", + "kem", + "ml-kem", + "mla", + "pem", + "rand", + "rand_chacha", + "rand_core", + "sha2", + "x25519-dalek", +] + +[[package]] +name = "mlar" +version = "1.3.0" +dependencies = [ + "assert_cmd", + "assert_fs", + "clap", + "glob", + "hex", + "hkdf", + "humansize", + "lru", + "ml-kem", + "mla", + "mlakey-parser", + "permutate", + "rand", + "rand_chacha", + "sha2", + "tar", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64", +] + +[[package]] +name = "permutate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b7d5b19a715ffab38693a9dd44b067fdfa2b18eef65bd93562dfe507022fae" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "difflib", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tar" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "terminal_size" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +dependencies = [ + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/bindings/python/Cargo.lock b/bindings/python/Cargo.lock new file mode 100644 index 00000000..57b5cb42 --- /dev/null +++ b/bindings/python/Cargo.lock @@ -0,0 +1,913 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "hpke" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4917627a14198c3603282c5158b815ad5534795451d3c074b53cf3cee0960b11" +dependencies = [ + "aead", + "aes-gcm", + "chacha20poly1305", + "digest", + "generic-array", + "hkdf", + "hmac", + "rand_core", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "hybrid-array" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a9a965bb102c1c891fb017c09a05c965186b1265a207640f323ddd009f9deb" +dependencies = [ + "typenum", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kem" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8645470337db67b01a7f966decf7d0bafedbae74147d33e641c67a91df239f" +dependencies = [ + "rand_core", + "zeroize", +] + +[[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "ml-kem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "860a7986a65e8b6c965082dfeba382302ddad057c21e0830373463e612e67217" +dependencies = [ + "hybrid-array", + "kem", + "rand_core", + "sha3", +] + +[[package]] +name = "mla" +version = "1.4.0" +dependencies = [ + "aes", + "bincode", + "bitflags", + "brotli", + "byteorder", + "ctr", + "digest", + "generic-array", + "ghash", + "hkdf", + "hpke", + "kem", + "ml-kem", + "rand", + "rand_chacha", + "serde", + "serde-big-array", + "sha2", + "subtle", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "mlakey-parser" +version = "0.1.0" +dependencies = [ + "curve25519-dalek", + "der-parser", + "ml-kem", + "mla", + "pem", + "rand_core", + "sha2", + "x25519-dalek", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pymla" +version = "0.2.0" +dependencies = [ + "ml-kem", + "mla", + "mlakey-parser", + "pyo3", + "x25519-dalek", +] + +[[package]] +name = "pyo3" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.214" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +]