From 7f016202ca4b8854c81ec7b319756e4e35f78b54 Mon Sep 17 00:00:00 2001 From: Mathieu Amiot Date: Tue, 9 Apr 2024 18:04:55 +0200 Subject: [PATCH] feat: Full p521 support --- basic_credential/Cargo.toml | 1 + basic_credential/src/lib.rs | 31 +++- .../group/public_group/diff/compute_path.rs | 1 + .../group/tests/test_proposal_validation.rs | 29 +--- openmls/src/test_utils/mod.rs | 4 + .../kats/kat_message_protection.rs | 146 ++++++++---------- .../treesync/node/leaf_node/capabilities.rs | 1 + openmls_rust_crypto/Cargo.toml | 14 +- openmls_rust_crypto/src/provider.rs | 83 +++++++++- traits/Cargo.toml | 3 +- traits/src/signatures.rs | 11 +- x509_credential/Cargo.toml | 2 +- x509_credential/src/lib.rs | 43 +++--- 13 files changed, 228 insertions(+), 141 deletions(-) diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index 572f3bf62f..02b2c6510e 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -19,6 +19,7 @@ serde = "1.0" ed25519-dalek = { version = "2.0.0-rc.3", features = ["rand_core"] } p256 = "0.13" p384 = "0.13" +p521 = "0.13" secrecy = { version = "0.8", features = ["serde"] } rand_core = "0.6" getrandom = { version = "0.2", features = ["js"] } diff --git a/basic_credential/src/lib.rs b/basic_credential/src/lib.rs index 9ea795a840..0a40088d2c 100644 --- a/basic_credential/src/lib.rs +++ b/basic_credential/src/lib.rs @@ -121,6 +121,14 @@ impl SignatureKeyPair { let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); (sk.to_bytes().to_vec().into(), pk) } + SignatureScheme::ECDSA_SECP521R1_SHA512 => { + let sk = p521::ecdsa::SigningKey::random(csprng); + let pk = p521::ecdsa::VerifyingKey::from(&sk) + .to_encoded_point(false) + .to_bytes() + .into(); + (sk.to_bytes().to_vec().into(), pk) + } SignatureScheme::ED25519 => { let sk = ed25519_dalek::SigningKey::generate(csprng); let pk = sk.verifying_key(); @@ -159,7 +167,7 @@ impl SignatureKeyPair { &private[..ed25519_dalek::SECRET_KEY_LENGTH], ) .map_err(|_| CryptoError::InvalidKey)?; - let pk = ed25519_dalek::VerifyingKey::try_from(&public[..]) + let pk = ed25519_dalek::VerifyingKey::try_from(public.as_slice()) .map_err(|_| CryptoError::InvalidKey)?; if sk.verifying_key() != pk { @@ -167,23 +175,35 @@ impl SignatureKeyPair { } } SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let sk = p256::ecdsa::SigningKey::try_from(&private[..]) + let sk = p256::ecdsa::SigningKey::from_slice(&private) .map_err(|_| CryptoError::InvalidKey)?; - let pk = p256::ecdsa::VerifyingKey::try_from(&public[..]) + let pk = p256::ecdsa::VerifyingKey::from_sec1_bytes(&public) .map_err(|_| CryptoError::InvalidKey)?; if sk.verifying_key() != &pk { return Err(CryptoError::MismatchKeypair); } } SignatureScheme::ECDSA_SECP384R1_SHA384 => { - let sk = p384::ecdsa::SigningKey::try_from(&private[..]) + let sk = p384::ecdsa::SigningKey::from_slice(&private) .map_err(|_| CryptoError::InvalidKey)?; - let pk = p384::ecdsa::VerifyingKey::try_from(&public[..]) + + let pk = p384::ecdsa::VerifyingKey::from_sec1_bytes(&public) .map_err(|_| CryptoError::InvalidKey)?; + dbg!(&pk); if sk.verifying_key() != &pk { return Err(CryptoError::MismatchKeypair); } } + SignatureScheme::ECDSA_SECP521R1_SHA512 => { + let sk = p521::ecdsa::SigningKey::from_slice(&private[..]) + .map_err(|_| CryptoError::InvalidKey)?; + let pk = p521::ecdsa::VerifyingKey::from_sec1_bytes(&public[..]) + .map_err(|_| CryptoError::InvalidKey)?; + let sk_pk = p521::ecdsa::VerifyingKey::from(&sk); + if sk_pk.to_encoded_point(false) != pk.to_encoded_point(false) { + return Err(CryptoError::MismatchKeypair); + } + } _ => {} }; @@ -238,6 +258,7 @@ pub mod tests { SignatureScheme::ED25519, SignatureScheme::ECDSA_SECP256R1_SHA256, SignatureScheme::ECDSA_SECP384R1_SHA384, + SignatureScheme::ECDSA_SECP521R1_SHA512, ]; for scheme in schemes { let kp = SignatureKeyPair::new(scheme, &mut rand::thread_rng()).unwrap(); diff --git a/openmls/src/group/public_group/diff/compute_path.rs b/openmls/src/group/public_group/diff/compute_path.rs index 35f71ba33d..f8ba6be6b8 100644 --- a/openmls/src/group/public_group/diff/compute_path.rs +++ b/openmls/src/group/public_group/diff/compute_path.rs @@ -74,6 +74,7 @@ impl<'a> PublicGroupDiff<'a> { Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, + Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521, Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, ]), Some(&[]), diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests/test_proposal_validation.rs index 6da1b28147..1802ad8ca4 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests/test_proposal_validation.rs @@ -1455,28 +1455,13 @@ async fn test_valsem107(ciphersuite: Ciphersuite, backend: &impl OpenMlsCryptoPr // expected. let bob_leaf_index = bob_group.own_leaf_index(); - let ref_propose = { - // We first go the manual route - let (ref_propose1, _) = alice_group - .propose_remove_member( - backend, - &alice_credential_with_key_and_signer.signer, - bob_leaf_index, - ) - .unwrap(); - - let (ref_propose2, _) = alice_group - .propose_remove_member( - backend, - &alice_credential_with_key_and_signer.signer, - bob_leaf_index, - ) - .unwrap(); - - assert_eq!(ref_propose1, ref_propose2); - - ref_propose1 - }; + let (ref_propose, _) = alice_group + .propose_remove_member( + backend, + &alice_credential_with_key_and_signer.signer, + bob_leaf_index, + ) + .unwrap(); // While this shouldn't fail, it should produce a valid commit, i.e. one // that contains only one remove proposal. diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index d8507a2bd0..cf86106fff 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -229,6 +229,9 @@ pub async fn backends(backend: &impl OpenMlsCryptoProvider) {} case::MLS_256_DHKEMP384_AES256GCM_SHA384_P384( Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 ), + case::MLS_256_DHKEMP521_AES256GCM_SHA512_P521( + Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 + ), case::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519( Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 ), @@ -247,6 +250,7 @@ pub async fn ciphersuites(ciphersuite: Ciphersuite) {} case::rust_crypto_MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_128_DHKEMP256_AES128GCM_SHA256_P256(Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_256_DHKEMP384_AES256GCM_SHA384_P384(Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, &OpenMlsRustCrypto::default()), + case::rust_crypto_MLS_256_DHKEMP521_AES256GCM_SHA512_P521(Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, &OpenMlsRustCrypto::default()), case::rust_crypto_MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519(Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, &OpenMlsRustCrypto::default()), ) diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index 0fb65fe623..34b26a0569 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -82,28 +82,44 @@ use crate::{ pub struct MessageProtectionTest { cipher_suite: u16, - group_id: String, + #[serde(with = "hex::serde")] + group_id: Vec, epoch: u64, - tree_hash: String, - confirmed_transcript_hash: String, - - signature_priv: String, - signature_pub: String, - - encryption_secret: String, - sender_data_secret: String, - membership_key: String, - - proposal: String, - proposal_pub: String, - proposal_priv: String, - - commit: String, - commit_pub: String, - commit_priv: String, - - application: String, - application_priv: String, + #[serde(with = "hex::serde")] + tree_hash: Vec, + #[serde(with = "hex::serde")] + confirmed_transcript_hash: Vec, + + #[serde(with = "hex::serde")] + signature_priv: Vec, + #[serde(with = "hex::serde")] + signature_pub: Vec, + + #[serde(with = "hex::serde")] + encryption_secret: Vec, + #[serde(with = "hex::serde")] + sender_data_secret: Vec, + #[serde(with = "hex::serde")] + membership_key: Vec, + + #[serde(with = "hex::serde")] + proposal: Vec, + #[serde(with = "hex::serde")] + proposal_pub: Vec, + #[serde(with = "hex::serde")] + proposal_priv: Vec, + + #[serde(with = "hex::serde")] + commit: Vec, + #[serde(with = "hex::serde")] + commit_pub: Vec, + #[serde(with = "hex::serde")] + commit_priv: Vec, + + #[serde(with = "hex::serde")] + application: Vec, + #[serde(with = "hex::serde")] + application_priv: Vec, } async fn generate_credential( @@ -200,36 +216,24 @@ pub async fn run_test_vector( .supported_ciphersuites() .contains(&ciphersuite) { - log::debug!("Skipping unsupported ciphersuite {:?}", ciphersuite); + log::warn!("Skipping unsupported ciphersuite {:?}", ciphersuite); return Ok(()); } - log::debug!("Testing tv with ciphersuite {:?}", ciphersuite); + log::info!("Testing tv with ciphersuite {:?}", ciphersuite); let group_context = GroupContext::new( ciphersuite, - GroupId::from_slice(&hex_to_bytes(&test.group_id)), + GroupId::from_slice(&test.group_id), test.epoch, - hex_to_bytes(&test.tree_hash), - hex_to_bytes(&test.confirmed_transcript_hash), + test.tree_hash.clone(), + test.confirmed_transcript_hash.clone(), Extensions::empty(), ); let sender_index = LeafNodeIndex::new(1); // Set up the group, unfortunately we can't do without. - let signature_private_key = match ciphersuite { - Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { - let mut private = hex_to_bytes(&test.signature_priv); - private.append(&mut hex_to_bytes(&test.signature_pub)); - private - } - Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 - | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => { - hex_to_bytes(&test.signature_priv) - } - _ => unimplemented!(), - }; + let signature_private_key = test.signature_priv.clone(); let random_own_signature_key = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap(), @@ -251,28 +255,16 @@ pub async fn run_test_vector( ) -> CoreGroup { let group_context = GroupContext::new( ciphersuite, - GroupId::from_slice(&hex_to_bytes(&test.group_id)), + GroupId::from_slice(&test.group_id), test.epoch, - hex_to_bytes(&test.tree_hash), - hex_to_bytes(&test.confirmed_transcript_hash), + test.tree_hash.clone(), + test.confirmed_transcript_hash.clone(), Extensions::empty(), ); // Set up the group, unfortunately we can't do without. let credential = Credential::new_basic(b"This is not needed".to_vec()); - let signature_private_key = match ciphersuite { - Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 - | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => { - let mut private = hex_to_bytes(&test.signature_priv); - private.append(&mut hex_to_bytes(&test.signature_pub)); - private - } - Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 - | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => { - hex_to_bytes(&test.signature_priv) - } - _ => unimplemented!(), - }; + let random_own_signature_key = SignatureKeyPair::new( ciphersuite.signature_algorithm(), &mut *backend.rand().borrow_rand().unwrap(), @@ -281,7 +273,7 @@ pub async fn run_test_vector( let random_own_signature_key = random_own_signature_key.public(); let signer = SignatureKeyPair::from_raw( ciphersuite.signature_algorithm(), - signature_private_key, + test.signature_priv.clone(), random_own_signature_key.to_vec(), ); @@ -309,7 +301,7 @@ pub async fn run_test_vector( ciphersuite, CredentialWithKey { credential, - signature_key: hex_to_bytes(&test.signature_pub).into(), + signature_key: test.signature_pub.clone().into(), }, ) .await; @@ -347,7 +339,7 @@ pub async fn run_test_vector( // Inject the test values into the group let encryption_secret = EncryptionSecret::from_slice( - &hex_to_bytes(&test.encryption_secret), + &&test.encryption_secret, group_context.protocol_version(), ciphersuite, ); @@ -372,12 +364,12 @@ pub async fn run_test_vector( } message_secrets.set_serialized_context(serialized_group_context); *message_secrets.sender_data_secret_mut() = SenderDataSecret::from_slice( - &hex_to_bytes(&test.sender_data_secret), + &&test.sender_data_secret, ProtocolVersion::Mls10, ciphersuite, ); message_secrets.set_membership_key(Secret::from_slice( - &hex_to_bytes(&test.membership_key), + &&test.membership_key, ProtocolVersion::Mls10, ciphersuite, )); @@ -387,11 +379,9 @@ pub async fn run_test_vector( // Proposal { - let proposal = ProposalIn::tls_deserialize_exact(hex_to_bytes(&test.proposal)).unwrap(); - let proposal_pub = - MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.proposal_pub)).unwrap(); - let proposal_priv = - MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.proposal_priv)).unwrap(); + let proposal = ProposalIn::tls_deserialize_exact(&test.proposal).unwrap(); + let proposal_pub = MlsMessageIn::tls_deserialize_exact(&test.proposal_pub).unwrap(); + let proposal_priv = MlsMessageIn::tls_deserialize_exact(&test.proposal_priv).unwrap(); async fn test_proposal_pub( mut group: CoreGroup, @@ -533,11 +523,9 @@ pub async fn run_test_vector( // Commit { - let commit = CommitIn::tls_deserialize_exact(hex_to_bytes(&test.commit)).unwrap(); - let commit_pub = - MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.commit_pub)).unwrap(); - let commit_priv = - MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.commit_priv)).unwrap(); + let commit = CommitIn::tls_deserialize_exact(&test.commit).unwrap(); + let commit_pub = MlsMessageIn::tls_deserialize_exact(&test.commit_pub).unwrap(); + let commit_priv = MlsMessageIn::tls_deserialize_exact(&test.commit_priv).unwrap(); async fn test_commit_pub( mut group: CoreGroup, @@ -704,9 +692,8 @@ pub async fn run_test_vector( // Application { - let application = hex_to_bytes(&test.application); - let application_priv = - MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.application_priv)).unwrap(); + let application = &test.application; + let application_priv = MlsMessageIn::tls_deserialize_exact(&test.application_priv).unwrap(); async fn test_application_priv( mut group: CoreGroup, @@ -762,7 +749,7 @@ pub async fn run_test_vector( .await; } - log::trace!("Finished test verification"); + log::info!("Finished test verification"); Ok(()) } @@ -770,15 +757,12 @@ pub async fn run_test_vector( #[apply(backends)] async fn read_test_vectors_mp(backend: &impl OpenMlsCryptoProvider) { let _ = pretty_env_logger::try_init(); - log::debug!("Reading test vectors ..."); + log::info!("Reading test vectors ..."); let tests: Vec = read("test_vectors/message-protection.json"); - for test_vector in tests { - match run_test_vector(test_vector, backend).await { - Ok(_) => {} - Err(e) => panic!("Error while checking message protection test vector.\n{e:?}"), - } + for test_vector in tests.into_iter() { + run_test_vector(test_vector, backend).await.unwrap(); } - log::trace!("Finished test vector verification"); + log::info!("Finished test vector verification"); } diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index 73970981a1..2a4994c224 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -202,6 +202,7 @@ pub(super) fn default_ciphersuites() -> Vec { Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, + Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521, Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, ] diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index ea42ea3f5f..f7b5dc7058 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -15,23 +15,23 @@ openmls_traits = { version = "0.2.0", path = "../traits" } openmls_memory_keystore = { version = "0.2.0", path = "../memory_keystore" } # Rust Crypto dependencies sha2 = { version = "0.10" } -aes-gcm = { version = "0.9" } -chacha20poly1305 = { version = "0.9" } +aes-gcm = { version = "0.10" } +chacha20poly1305 = { version = "0.10" } hmac = { version = "0.12" } -ed25519-dalek = { version = "2.0.0-rc.3", features = ["rand_core"] } +ed25519-dalek = { version = "2.1", features = ["rand_core"] } p256 = { version = "0.13" } p384 = { version = "0.13" } -p521 = "0.13" +p521 = { version = "0.13" } hkdf = { version = "0.12" } rand_core = "0.6" rand_chacha = { version = "0.3" } tls_codec = { workspace = true } -zeroize = { version = "1.6", features = ["derive"] } +zeroize = { version = "1.7", features = ["derive"] } signature = "2.1" thiserror = "1.0" generic-array = "0.14" [dependencies.hpke] git = "https://github.com/wireapp/rust-hpke.git" -branch = "wire/unstable-pq-xyber" -features = ["x25519", "p256", "p384", "xyber768d00", "serde_impls"] +branch = "wire/unstable-pq-xyber-p521" +features = ["x25519", "p256", "p384", "p521", "xyber768d00"] diff --git a/openmls_rust_crypto/src/provider.rs b/openmls_rust_crypto/src/provider.rs index 42f2c9aef8..de0b2d45cc 100644 --- a/openmls_rust_crypto/src/provider.rs +++ b/openmls_rust_crypto/src/provider.rs @@ -3,7 +3,7 @@ use std::sync::RwLock; use aes_gcm::{ aead::{Aead, Payload}, - Aes128Gcm, Aes256Gcm, NewAead, + Aes128Gcm, Aes256Gcm, KeyInit, }; use chacha20poly1305::ChaCha20Poly1305; use hkdf::Hkdf; @@ -82,6 +82,13 @@ impl RustCrypto { } } +#[inline] +fn normalize_p521_secret_key(sk: &[u8]) -> zeroize::Zeroizing<[u8; 66]> { + let mut sk_buf = zeroize::Zeroizing::new([0u8; 66]); + sk_buf[66 - sk.len()..].copy_from_slice(sk); + sk_buf +} + impl OpenMlsCrypto for RustCrypto { fn supports(&self, ciphersuite: Ciphersuite) -> Result<(), CryptoError> { match ciphersuite { @@ -89,6 +96,7 @@ impl OpenMlsCrypto for RustCrypto { | Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 | Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 | Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 + | Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 | Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519 => Ok(()), _ => Err(CryptoError::UnsupportedCiphersuite), } @@ -100,6 +108,7 @@ impl OpenMlsCrypto for RustCrypto { Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519, Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256, Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384, + Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521, Ciphersuite::MLS_128_X25519KYBER768DRAFT00_AES128GCM_SHA256_Ed25519, ] } @@ -261,6 +270,14 @@ impl OpenMlsCrypto for RustCrypto { let pk = sk.verifying_key().to_encoded_point(false).to_bytes().into(); Ok((sk.to_bytes().to_vec(), pk)) } + SignatureScheme::ECDSA_SECP521R1_SHA512 => { + let sk = p521::ecdsa::SigningKey::random(&mut *rng); + let pk = p521::ecdsa::VerifyingKey::from(&sk) + .to_encoded_point(false) + .to_bytes() + .into(); + Ok((sk.to_bytes().to_vec(), pk)) + } SignatureScheme::ED25519 => { let mut rng = self .rng @@ -320,6 +337,16 @@ impl OpenMlsCrypto for RustCrypto { k.verify(data, &signature) .map_err(|_| CryptoError::InvalidSignature) } + SignatureScheme::ECDSA_SECP521R1_SHA512 => { + let k = p521::ecdsa::VerifyingKey::from_sec1_bytes(pk) + .map_err(|_| CryptoError::CryptoLibraryError)?; + + let signature = p521::ecdsa::Signature::from_der(signature) + .map_err(|_| CryptoError::InvalidSignature)?; + + k.verify(data, &signature) + .map_err(|_| CryptoError::InvalidSignature) + } SignatureScheme::ED25519 => { let k = ed25519_dalek::VerifyingKey::try_from(pk) .map_err(|_| CryptoError::CryptoLibraryError)?; @@ -362,6 +389,15 @@ impl OpenMlsCrypto for RustCrypto { .map_err(|_| CryptoError::CryptoLibraryError)?; Ok(signature.to_bytes().into()) } + SignatureScheme::ECDSA_SECP521R1_SHA512 => { + let k = p521::ecdsa::SigningKey::from_slice(&*normalize_p521_secret_key(key)) + .map_err(|_| CryptoError::CryptoLibraryError)?; + let signature: p521::ecdsa::DerSignature = k + .try_sign(data) + .map_err(|_| CryptoError::CryptoLibraryError)? + .to_der(); + Ok(signature.to_bytes().into()) + } SignatureScheme::ED25519 => { let k = match key.len() { // Compat layer for legacy keypairs [seed, pk] @@ -438,6 +474,15 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(pk_r, info, aad, ptxt, &mut *rng), + HpkeConfig( + HpkeKemType::DhKemP521, + HpkeKdfType::HkdfSha512, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_seal::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha512, + hpke::kem::DhP521HkdfSha512, + >(pk_r, info, aad, ptxt, &mut *rng), HpkeConfig( HpkeKemType::X25519Kyber768Draft00, HpkeKdfType::HkdfSha256, @@ -520,6 +565,21 @@ impl OpenMlsCrypto for RustCrypto { aad, input.ciphertext.as_slice(), )?, + HpkeConfig( + HpkeKemType::DhKemP521, + HpkeKdfType::HkdfSha512, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_open::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha512, + hpke::kem::DhP521HkdfSha512, + >( + sk_r, + input.kem_output.as_slice(), + info, + aad, + input.ciphertext.as_slice(), + )?, HpkeConfig( HpkeKemType::X25519Kyber768Draft00, HpkeKdfType::HkdfSha256, @@ -591,6 +651,15 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, + HpkeConfig( + HpkeKemType::DhKemP521, + HpkeKdfType::HkdfSha512, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_export_tx::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha512, + hpke::kem::DhP521HkdfSha512, + >(pk_r, info, exporter_context, exporter_length, &mut *rng)?, HpkeConfig( HpkeKemType::X25519Kyber768Draft00, HpkeKdfType::HkdfSha256, @@ -654,6 +723,15 @@ impl OpenMlsCrypto for RustCrypto { hpke::kdf::HkdfSha384, hpke::kem::DhP384HkdfSha384, >(enc, sk_r, info, exporter_context, exporter_length)?, + HpkeConfig( + HpkeKemType::DhKemP521, + HpkeKdfType::HkdfSha512, + HpkeAeadType::AesGcm256, + ) => hpke_core::hpke_export_rx::< + hpke::aead::AesGcm256, + hpke::kdf::HkdfSha512, + hpke::kem::DhP521HkdfSha512, + >(enc, sk_r, info, exporter_context, exporter_length)?, HpkeConfig( HpkeKemType::X25519Kyber768Draft00, HpkeKdfType::HkdfSha256, @@ -683,6 +761,9 @@ impl OpenMlsCrypto for RustCrypto { HpkeKemType::DhKemP384 => { hpke_core::hpke_derive_keypair::(ikm) } + HpkeKemType::DhKemP521 => { + hpke_core::hpke_derive_keypair::(ikm) + } HpkeKemType::DhKem25519 => { hpke_core::hpke_derive_keypair::(ikm) } diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 40ae55ea35..487d40229d 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -22,8 +22,9 @@ rand_core = "0.6" tls_codec = { workspace = true } async-trait = { workspace = true } # for the default signer -ed25519-dalek = { version = "2.0.0-rc.3", features = ["rand_core"] } +ed25519-dalek = { version = "2.1", features = ["rand_core"] } p256 = "0.13" p384 = "0.13" +p521 = "0.13" zeroize = "1.6" signature = "2.1" diff --git a/traits/src/signatures.rs b/traits/src/signatures.rs index df5c591b3f..f445ad9b77 100644 --- a/traits/src/signatures.rs +++ b/traits/src/signatures.rs @@ -27,19 +27,26 @@ impl Signer for T { use signature::Signer; match self.signature_scheme() { SignatureScheme::ECDSA_SECP256R1_SHA256 => { - let k = p256::ecdsa::SigningKey::from_bytes(self.private_key().into()) + let k = p256::ecdsa::SigningKey::from_slice(self.private_key()) .map_err(|_| Error::SigningError)?; let signature: p256::ecdsa::Signature = k.try_sign(payload).map_err(|_| Error::SigningError)?; Ok(signature.to_der().to_bytes().into()) } SignatureScheme::ECDSA_SECP384R1_SHA384 => { - let k = p384::ecdsa::SigningKey::from_bytes(self.private_key().into()) + let k = p384::ecdsa::SigningKey::from_slice(self.private_key()) .map_err(|_| Error::SigningError)?; let signature: p384::ecdsa::Signature = k.try_sign(payload).map_err(|_| Error::SigningError)?; Ok(signature.to_der().to_bytes().into()) } + SignatureScheme::ECDSA_SECP521R1_SHA512 => { + let k = p521::ecdsa::SigningKey::from_slice(self.private_key()) + .map_err(|_| Error::SigningError)?; + let signature: p521::ecdsa::Signature = + k.try_sign(payload).map_err(|_| Error::SigningError)?; + Ok(signature.to_der().to_bytes().into()) + } SignatureScheme::ED25519 => { let k = match self.private_key().len() { // Compat layer for legacy keypairs [seed, pk] diff --git a/x509_credential/Cargo.toml b/x509_credential/Cargo.toml index 156aba7c41..a3a2baa68c 100644 --- a/x509_credential/Cargo.toml +++ b/x509_credential/Cargo.toml @@ -20,4 +20,4 @@ percent-encoding = "2.3" # Rust Crypto secrecy = { version = "0.8", features = ["serde"] } x509-cert = "0.2" -oid-registry = "0.6" +const-oid = "0.9" diff --git a/x509_credential/src/lib.rs b/x509_credential/src/lib.rs index 194030c404..ee03653415 100644 --- a/x509_credential/src/lib.rs +++ b/x509_credential/src/lib.rs @@ -141,26 +141,29 @@ impl X509Ext for Certificate { } fn signature_scheme(&self) -> Result { + use const_oid::db::{ + rfc5912::{ID_EC_PUBLIC_KEY, SECP_256_R_1, SECP_384_R_1, SECP_521_R_1}, + rfc8410::{ID_ED_25519, ID_ED_448}, + }; let alg = self.tbs_certificate.subject_public_key_info.algorithm.oid; - let alg = oid_registry::Oid::new(std::borrow::Cow::Borrowed(alg.as_bytes())); - - let scheme = if alg == oid_registry::OID_SIG_ED25519 { - SignatureScheme::ED25519 - } else if alg == oid_registry::OID_SIG_ED448 { - SignatureScheme::ED448 - } else if alg == oid_registry::OID_SIG_ECDSA_WITH_SHA256 - || alg == oid_registry::OID_KEY_TYPE_EC_PUBLIC_KEY - // ☝️ - // this is a mess but we will soon move to a x509 crate doing this for us - { - SignatureScheme::ECDSA_SECP256R1_SHA256 - } else if alg == oid_registry::OID_SIG_ECDSA_WITH_SHA384 { - SignatureScheme::ECDSA_SECP384R1_SHA384 - } else if alg == oid_registry::OID_SIG_ECDSA_WITH_SHA512 { - SignatureScheme::ECDSA_SECP521R1_SHA512 - } else { - return Err(CryptoError::UnsupportedSignatureScheme); + let params = self + .tbs_certificate + .subject_public_key_info + .algorithm + .parameters + .as_ref() + .map(|param| x509_cert::spki::ObjectIdentifier::from_bytes(param.value()).ok()) + .flatten(); + + let scheme = match (alg, params) { + (ID_ED_25519, None) => SignatureScheme::ED25519, + (ID_ED_448, None) => SignatureScheme::ED448, + (ID_EC_PUBLIC_KEY, Some(SECP_256_R_1)) => SignatureScheme::ECDSA_SECP256R1_SHA256, + (ID_EC_PUBLIC_KEY, Some(SECP_384_R_1)) => SignatureScheme::ECDSA_SECP384R1_SHA384, + (ID_EC_PUBLIC_KEY, Some(SECP_521_R_1)) => SignatureScheme::ECDSA_SECP521R1_SHA512, + _ => return Err(CryptoError::UnsupportedSignatureScheme), }; + Ok(scheme) } @@ -194,9 +197,7 @@ impl X509Ext for Certificate { .ok_or(CryptoError::InvalidCertificate)?; let san = extensions .iter() - .find(|e| { - e.extn_id.as_bytes() == oid_registry::OID_X509_EXT_SUBJECT_ALT_NAME.as_bytes() - }) + .find(|e| e.extn_id == const_oid::db::rfc5280::ID_CE_SUBJECT_ALT_NAME) .and_then(|e| { x509_cert::ext::pkix::SubjectAltName::from_der(e.extn_value.as_bytes()).ok() })