From dd623a524ec38221941681db463600f007899dd1 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 27 Jun 2023 09:38:17 -0400 Subject: [PATCH 01/24] Support to_pkcs8 --- aws-lc-rs/src/ec/key_pair.rs | 39 ++++++++++++++++++++++ aws-lc-rs/src/ed25519.rs | 64 ++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index afb60f6e16c..09aae88521e 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -101,6 +101,15 @@ impl EcdsaKeyPair { evp_pkey.marshall_private_key(Version::V1) } + /// Serializes this `EcdsaKeyPair` into a PKCS#8 v1 document. + /// + /// # Errors + /// `error::Unspecified` on internal error. + /// + pub fn to_pkcs8(&self) -> Result { + unsafe { self.evp_pkey.marshall_private_key(Version::V1) } + } + /// Constructs an ECDSA key pair from the private key and public key bytes /// /// The private key must encoded as a big-endian fixed-length integer. For @@ -224,3 +233,33 @@ fn compute_ecdsa_signature<'a>( Ok(&mut signature[0..out_sig_len]) } + +#[cfg(test)] +mod tests { + use crate::rand::SystemRandom; + use crate::signature::*; + + #[test] + fn test_to_pkcs8() { + for signing_alg in [ + &ECDSA_P521_SHA3_512_ASN1_SIGNING, + &ECDSA_P521_SHA3_512_FIXED_SIGNING, + &ECDSA_P521_SHA512_ASN1_SIGNING, + &ECDSA_P521_SHA512_FIXED_SIGNING, + &ECDSA_P384_SHA3_384_ASN1_SIGNING, + &ECDSA_P384_SHA3_384_FIXED_SIGNING, + &ECDSA_P384_SHA384_ASN1_SIGNING, + &ECDSA_P384_SHA384_FIXED_SIGNING, + &ECDSA_P256_SHA256_ASN1_SIGNING, + &ECDSA_P256_SHA256_FIXED_SIGNING, + ] { + let rnd = SystemRandom::new(); + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); + let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); + } + } +} diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index e58049ac381..0f44c936ded 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -13,7 +13,7 @@ use crate::{constant_time, sealed, test}; use aws_lc::{ ED25519_keypair_from_seed, ED25519_sign, ED25519_verify, EVP_PKEY_CTX_new_id, EVP_PKEY_get_raw_private_key, EVP_PKEY_get_raw_public_key, EVP_PKEY_keygen, - EVP_PKEY_keygen_init, EVP_PKEY, EVP_PKEY_ED25519, + EVP_PKEY_keygen_init, EVP_PKEY_new_raw_private_key, EVP_PKEY, EVP_PKEY_ED25519, }; use std::fmt; use std::fmt::{Debug, Formatter}; @@ -163,6 +163,24 @@ impl Ed25519KeyPair { evp_pkey.marshall_private_key(Version::V2) } + /// Serializes this `Ed25519KeyPair` into a PKCS#8 v2 document. + /// + /// # Errors + /// `error::Unspecified` on internal error. + /// + pub fn to_pkcs8(&self) -> Result { + unsafe { + let evp_pkey: LcPtr = LcPtr::new(EVP_PKEY_new_raw_private_key( + EVP_PKEY_ED25519, + null_mut(), + self.private_key.as_ptr(), + ED25519_PRIVATE_KEY_SEED_LEN, + ))?; + + evp_pkey.marshall_private_key(Version::V2) + } + } + /// Generates a `Ed25519KeyPair` using the `rng` provided, then serializes that key as a /// PKCS#8 document. /// @@ -182,6 +200,24 @@ impl Ed25519KeyPair { evp_pkey.marshall_private_key(Version::V1) } + /// Serializes this `Ed25519KeyPair` into a PKCS#8 v2 document. + /// + /// # Errors + /// `error::Unspecified` on internal error. + /// + pub fn to_pkcs8v1(&self) -> Result { + unsafe { + let evp_pkey: LcPtr = LcPtr::new(EVP_PKEY_new_raw_private_key( + EVP_PKEY_ED25519, + null_mut(), + self.private_key.as_ptr(), + ED25519_PRIVATE_KEY_SEED_LEN, + ))?; + + evp_pkey.marshall_private_key(Version::V1) + } + } + /// Constructs an Ed25519 key pair from the private key seed `seed` and its /// public key `public_key`. /// @@ -330,12 +366,14 @@ impl Ed25519KeyPair { #[cfg(test)] mod tests { + use crate::ed25519::Ed25519KeyPair; + use crate::rand::SystemRandom; use crate::test; #[test] fn test_generate_pkcs8() { - let rng = crate::rand::SystemRandom::new(); + let rng = SystemRandom::new(); let document = Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); let _: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap(); let _: Ed25519KeyPair = @@ -390,4 +428,26 @@ mod tests { ); } } + + #[test] + fn test_to_pkcs8() { + let rnd = SystemRandom::new(); + let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); + } + + #[test] + fn test_to_pkcs8v1() { + let rnd = SystemRandom::new(); + let key_pair_doc = Ed25519KeyPair::generate_pkcs8v1(&rnd).unwrap(); + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8v1().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); + } } From 8c710eaa338ceac370f74e7312ee1f8f6b8ebfe4 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Tue, 27 Jun 2023 14:24:04 -0400 Subject: [PATCH 02/24] Access for EC keypair key bytes --- aws-lc-rs/src/ec.rs | 89 +++++++++++-- aws-lc-rs/src/ec/key_pair.rs | 18 ++- aws-lc-rs/src/ed25519.rs | 70 +++++----- .../data/ecdsa_test_public_key_p256_debug.txt | 2 +- aws-lc-rs/tests/ecdsa_tests.rs | 121 ++++++++++++------ aws-lc-rs/tests/ed25519_tests.rs | 40 ++++++ 6 files changed, 258 insertions(+), 82 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 97f8ea9b2ca..ddeb7b30b45 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -17,13 +17,14 @@ use aws_lc::EC_KEY_check_fips; #[cfg(not(feature = "fips"))] use aws_lc::EC_KEY_check_key; use aws_lc::{ - point_conversion_form_t, ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, - ECDSA_SIG_new, ECDSA_SIG_set0, ECDSA_SIG_to_bytes, EC_GROUP_get_curve_name, - EC_GROUP_new_by_curve_name, EC_KEY_get0_group, EC_KEY_get0_public_key, EC_KEY_new, - EC_KEY_set_group, EC_KEY_set_private_key, EC_KEY_set_public_key, EC_POINT_new, - EC_POINT_oct2point, EC_POINT_point2oct, EVP_DigestVerify, EVP_DigestVerifyInit, - EVP_PKEY_CTX_new_id, EVP_PKEY_CTX_set_ec_paramgen_curve_nid, EVP_PKEY_assign_EC_KEY, - EVP_PKEY_get0_EC_KEY, EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, + point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, CONF_modules_finish, + ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0, + ECDSA_SIG_to_bytes, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_get0_group, + EC_KEY_get0_private_key, EC_KEY_get0_public_key, EC_KEY_new, EC_KEY_set_group, + EC_KEY_set_private_key, EC_KEY_set_public_key, EC_POINT_new, EC_POINT_oct2point, + EC_POINT_point2oct, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_CTX_new_id, + EVP_PKEY_CTX_set_ec_paramgen_curve_nid, EVP_PKEY_assign_EC_KEY, EVP_PKEY_get0_EC_KEY, + EVP_PKEY_get_raw_private_key, EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, NID_X9_62_prime256v1, NID_secp256k1, NID_secp384r1, NID_secp521r1, BIGNUM, ECDSA_SIG, EC_GROUP, EC_POINT, EVP_PKEY, EVP_PKEY_EC, }; @@ -41,13 +42,16 @@ use std::ptr::null_mut; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; +use zeroize::Zeroize; pub(crate) mod key_pair; const ELEM_MAX_BITS: usize = 521; -pub const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8; +pub(crate) const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8; -pub const SCALAR_MAX_BYTES: usize = ELEM_MAX_BYTES; +pub(crate) const SCALAR_MAX_BYTES: usize = ELEM_MAX_BYTES; + +pub(crate) const PRIVATE_KEY_MAX_LEN: usize = 1 + ELEM_MAX_BYTES; /// The maximum length, in bytes, of an encoded public key. pub(crate) const PUBLIC_KEY_MAX_LEN: usize = 1 + (2 * ELEM_MAX_BYTES); @@ -119,7 +123,10 @@ pub struct PublicKey(Box<[u8]>); impl Debug for PublicKey { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str(&format!("PublicKey(\"{}\")", test::to_hex(self.0.as_ref()))) + f.write_str(&format!( + "EcdsaPublicKey(\"{}\")", + test::to_hex(self.0.as_ref()) + )) } } @@ -139,6 +146,37 @@ impl AsRef<[u8]> for PublicKey { unsafe impl Send for PublicKey {} unsafe impl Sync for PublicKey {} +#[derive(Clone)] +pub struct PrivateKey(Box<[u8]>); + +impl Drop for PrivateKey { + fn drop(&mut self) { + self.0.zeroize(); + } +} + +impl Debug for PrivateKey { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("EcdsaPrivateKey()") + } +} + +impl PrivateKey { + fn new(box_bytes: Box<[u8]>) -> Self { + PrivateKey(box_bytes) + } +} + +impl AsRef<[u8]> for PrivateKey { + #[inline] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +unsafe impl Send for PrivateKey {} +unsafe impl Sync for PrivateKey {} + impl VerificationAlgorithm for EcdsaVerificationAlgorithm { #[inline] #[cfg(feature = "ring-sig-verify")] @@ -265,6 +303,37 @@ unsafe fn validate_evp_key( Ok(()) } +pub(crate) unsafe fn marshal_private_key_to_buffer( + alg_id: &'static AlgorithmID, + evp_key: &ConstPointer, +) -> Result, Unspecified> { + let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_key))?; + let private_bn = ConstPointer::new(EC_KEY_get0_private_key(*ec_key))?; + let private_size: usize = ecdsa_fixed_number_byte_size(alg_id); + { + let size: usize = BN_num_bytes(*private_bn).try_into()?; + debug_assert!(size <= private_size); + } + + let mut buffer = vec![0u8; SCALAR_MAX_BYTES]; + if 1 != BN_bn2bin_padded(buffer.as_mut_ptr(), private_size, *private_bn) { + return Err(Unspecified); + } + buffer.truncate(private_size); + + Ok(buffer) +} + +pub(crate) fn marshal_private_key( + alg_id: &'static AlgorithmID, + evp_key: &ConstPointer, +) -> Result { + unsafe { + let priv_key_bytes = marshal_private_key_to_buffer(alg_id, evp_key)?; + Ok(PrivateKey::new(priv_key_bytes.into())) + } +} + pub(crate) unsafe fn marshal_public_key_to_buffer( buffer: &mut [u8; PUBLIC_KEY_MAX_LEN], evp_pkey: &ConstPointer, diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 09aae88521e..6ffb7ce4330 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -5,7 +5,8 @@ use crate::digest::digest_ctx::DigestContext; use crate::ec::{ - evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey, + evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PrivateKey, + PublicKey, }; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; @@ -27,6 +28,7 @@ pub struct EcdsaKeyPair { algorithm: &'static EcdsaSigningAlgorithm, evp_pkey: LcPtr, pubkey: PublicKey, + privkey: PrivateKey, } impl Debug for EcdsaKeyPair { @@ -55,11 +57,12 @@ impl EcdsaKeyPair { evp_pkey: LcPtr, ) -> Result { let pubkey = ec::marshal_public_key(&evp_pkey.as_const())?; - + let privkey = ec::marshal_private_key(algorithm.id, &evp_pkey.as_const())?; Ok(Self { algorithm, evp_pkey, pubkey, + privkey, }) } @@ -147,6 +150,17 @@ impl EcdsaKeyPair { } } + /// Exposes the private key encoded as a big-endian fixed-length integer. + /// + /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. + /// + /// # Errors + /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. + #[must_use] + pub fn private_key(&self) -> &PrivateKey { + &self.privkey + } + /// Returns the signature of the message using a random nonce. /// /// # *ring* Compatibility diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 0f44c936ded..8e68e64f1c8 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -22,6 +22,7 @@ use std::ptr::null_mut; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; +use zeroize::Zeroize; /// The length of an Ed25519 public key. pub const ED25519_PUBLIC_KEY_LEN: usize = aws_lc::ED25519_PUBLIC_KEY_LEN as usize; @@ -90,22 +91,43 @@ impl Debug for Ed25519KeyPair { } } +impl Drop for Ed25519KeyPair { + fn drop(&mut self) { + self.private_key.zeroize(); + } +} + #[derive(Clone)] #[allow(clippy::module_name_repetitions)] -pub struct PublicKey { - public_key: [u8; ED25519_PUBLIC_KEY_LEN], +pub struct Seed<'a>(&'a Ed25519KeyPair); + +impl AsRef<[u8]> for Seed<'_> { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN] + } } +impl Debug for Seed<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("Ed25519Seed()") + } +} + +#[derive(Clone)] +#[allow(clippy::module_name_repetitions)] +pub struct PublicKey([u8; ED25519_PUBLIC_KEY_LEN]); + impl AsRef<[u8]> for PublicKey { #[inline] fn as_ref(&self) -> &[u8] { - &self.public_key + &self.0 } } impl Debug for PublicKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str(&format!("PublicKey(\"{}\")", test::to_hex(self.public_key))) + f.write_str(&format!("PublicKey(\"{}\")", test::to_hex(self.0))) } } @@ -173,7 +195,7 @@ impl Ed25519KeyPair { let evp_pkey: LcPtr = LcPtr::new(EVP_PKEY_new_raw_private_key( EVP_PKEY_ED25519, null_mut(), - self.private_key.as_ptr(), + self.private_key.as_ref().as_ptr(), ED25519_PRIVATE_KEY_SEED_LEN, ))?; @@ -210,7 +232,7 @@ impl Ed25519KeyPair { let evp_pkey: LcPtr = LcPtr::new(EVP_PKEY_new_raw_private_key( EVP_PKEY_ED25519, null_mut(), - self.private_key.as_ptr(), + self.private_key.as_ref().as_ptr(), ED25519_PRIVATE_KEY_SEED_LEN, ))?; @@ -252,9 +274,7 @@ impl Ed25519KeyPair { Ok(Self { private_key, - public_key: PublicKey { - public_key: derived_public_key, - }, + public_key: PublicKey(derived_public_key), }) } } @@ -319,7 +339,7 @@ impl Ed25519KeyPair { let key_pair = Self { private_key, - public_key: PublicKey { public_key }, + public_key: PublicKey(public_key), }; Ok(key_pair) @@ -362,6 +382,14 @@ impl Ed25519KeyPair { ED25519_SIGNATURE_LEN })) } + + /// Provides the private key "seed" for this Ed25519 key pair. + /// + /// For serialization of the key pair, `Ed25519KeyPair::to_pkcs8()` is preferred. + #[must_use] + pub fn seed(&self) -> Seed { + Seed(self) + } } #[cfg(test)] @@ -428,26 +456,4 @@ mod tests { ); } } - - #[test] - fn test_to_pkcs8() { - let rnd = SystemRandom::new(); - let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); - let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); - - let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); - // Verify that the exported bytes match the original generated bytes - assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); - } - - #[test] - fn test_to_pkcs8v1() { - let rnd = SystemRandom::new(); - let key_pair_doc = Ed25519KeyPair::generate_pkcs8v1(&rnd).unwrap(); - let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); - - let key_pair_export_doc = key_pair.to_pkcs8v1().unwrap(); - // Verify that the exported bytes match the original generated bytes - assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); - } } diff --git a/aws-lc-rs/tests/data/ecdsa_test_public_key_p256_debug.txt b/aws-lc-rs/tests/data/ecdsa_test_public_key_p256_debug.txt index b590fc84904..0be46ece4cf 100644 --- a/aws-lc-rs/tests/data/ecdsa_test_public_key_p256_debug.txt +++ b/aws-lc-rs/tests/data/ecdsa_test_public_key_p256_debug.txt @@ -1 +1 @@ -PublicKey("04fc116698a3e3236550c4c9efa9bd4d0619602a65d2930e9150ab33e84dbc83f8a6a6b9933f35ab59245e5b5a7af5dca76b33cbe7aeee5981b3ca350bebf52ecd") \ No newline at end of file +EcdsaPublicKey("04fc116698a3e3236550c4c9efa9bd4d0619602a65d2930e9150ab33e84dbc83f8a6a6b9933f35ab59245e5b5a7af5dca76b33cbe7aeee5981b3ca350bebf52ecd") \ No newline at end of file diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index 50d6aa0cdba..e5287f4e5b8 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -4,22 +4,22 @@ // SPDX-License-Identifier: Apache-2.0 OR ISC use aws_lc_rs::{ - rand, - signature::{self, KeyPair}, + rand::SystemRandom, + signature::{self, EcdsaKeyPair, KeyPair, Signature, UnparsedPublicKey}, test, test_file, }; use mirai_annotations::unrecoverable; #[test] fn ecdsa_traits() { - test::compile_time_assert_send::(); - test::compile_time_assert_sync::(); - test::compile_time_assert_send::(); - test::compile_time_assert_sync::(); - test::compile_time_assert_send::>(); - test::compile_time_assert_sync::>(); - test::compile_time_assert_send::>>(); - test::compile_time_assert_sync::>>(); + test::compile_time_assert_send::(); + test::compile_time_assert_sync::(); + test::compile_time_assert_send::(); + test::compile_time_assert_sync::(); + test::compile_time_assert_send::>(); + test::compile_time_assert_sync::>(); + test::compile_time_assert_send::>>(); + test::compile_time_assert_sync::>>(); } #[test] @@ -68,10 +68,7 @@ fn ecdsa_from_pkcs8_test() { let error = test_case.consume_optional_string("Error"); - match ( - signature::EcdsaKeyPair::from_pkcs8(this_fixed, &input), - error.clone(), - ) { + match (EcdsaKeyPair::from_pkcs8(this_fixed, &input), error.clone()) { (Ok(_), None) => (), (Err(e), None) => panic!( "Failed with error \"{}\", but expected to succeed. Input: {}", @@ -91,10 +88,7 @@ fn ecdsa_from_pkcs8_test() { ), }; - match ( - signature::EcdsaKeyPair::from_pkcs8(this_asn1, &input), - error, - ) { + match (EcdsaKeyPair::from_pkcs8(this_asn1, &input), error) { (Ok(_), None) => (), (Err(e), None) => { unrecoverable!("Failed with error \"{}\", but expected to succeed", e); @@ -104,12 +98,12 @@ fn ecdsa_from_pkcs8_test() { }; assert!( - signature::EcdsaKeyPair::from_pkcs8(other_fixed, &input).is_err(), + EcdsaKeyPair::from_pkcs8(other_fixed, &input).is_err(), "Input: {}", test::to_hex(&input) ); assert!( - signature::EcdsaKeyPair::from_pkcs8(other_asn1, &input).is_err(), + EcdsaKeyPair::from_pkcs8(other_asn1, &input).is_err(), "Input: {}", test::to_hex(&input) ); @@ -122,7 +116,7 @@ fn ecdsa_from_pkcs8_test() { // Verify that, at least, we generate PKCS#8 documents that we can read. #[test] fn ecdsa_generate_pkcs8_test() { - let rng = rand::SystemRandom::new(); + let rng = SystemRandom::new(); for alg in &[ &signature::ECDSA_P256_SHA256_ASN1_SIGNING, @@ -140,7 +134,7 @@ fn ecdsa_generate_pkcs8_test() { &signature::ECDSA_P256K1_SHA3_256_ASN1_SIGNING, &signature::ECDSA_P256K1_SHA3_256_FIXED_SIGNING, ] { - let pkcs8 = signature::EcdsaKeyPair::generate_pkcs8(alg, &rng).unwrap(); + let pkcs8 = EcdsaKeyPair::generate_pkcs8(alg, &rng).unwrap(); println!(); for b in pkcs8.as_ref() { print!("{:02x}", *b); @@ -148,7 +142,7 @@ fn ecdsa_generate_pkcs8_test() { println!(); println!(); - signature::EcdsaKeyPair::from_pkcs8(alg, pkcs8.as_ref()).unwrap(); + EcdsaKeyPair::from_pkcs8(alg, pkcs8.as_ref()).unwrap(); } } @@ -188,7 +182,7 @@ fn test_signature_ecdsa_verify_asn1(data_file: test::File) { } }; - let actual_result = signature::UnparsedPublicKey::new(alg, &public_key).verify(&msg, &sig); + let actual_result = UnparsedPublicKey::new(alg, &public_key).verify(&msg, &sig); assert_eq!(actual_result.is_ok(), is_valid); Ok(()) @@ -232,7 +226,7 @@ fn test_signature_ecdsa_verify_fixed(data_file: test::File) { let is_valid = expected_result == "P (0 )"; - let actual_result = signature::UnparsedPublicKey::new(alg, &public_key).verify(&msg, &sig); + let actual_result = UnparsedPublicKey::new(alg, &public_key).verify(&msg, &sig); assert_eq!(actual_result.is_ok(), is_valid); Ok(()) @@ -245,11 +239,8 @@ fn ecdsa_test_public_key_coverage() { const PUBLIC_KEY: &[u8] = include_bytes!("data/ecdsa_test_public_key_p256.der"); const PUBLIC_KEY_DEBUG: &str = include_str!("data/ecdsa_test_public_key_p256_debug.txt"); - let key_pair = signature::EcdsaKeyPair::from_pkcs8( - &signature::ECDSA_P256_SHA256_FIXED_SIGNING, - PRIVATE_KEY, - ) - .unwrap(); + let key_pair = + EcdsaKeyPair::from_pkcs8(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, PRIVATE_KEY).unwrap(); // Test `AsRef<[u8]>` assert_eq!(key_pair.public_key().as_ref(), PUBLIC_KEY); @@ -260,7 +251,7 @@ fn ecdsa_test_public_key_coverage() { // Test `Copy`. #[allow(let_underscore_drop)] - let _: ::PublicKey = *key_pair.public_key(); + let _: ::PublicKey = *key_pair.public_key(); // Test `Debug`. assert_eq!(PUBLIC_KEY_DEBUG, format!("{:?}", key_pair.public_key())); @@ -287,7 +278,7 @@ fn signature_ecdsa_sign_fixed_sign_and_verify_sha3_test() { // different each time. Because of that, here we simply verify that the // signature verifies correctly. fn test_signature_ecdsa_sign_fixed_sign_and_verify(data_file: test::File) { - let rng = rand::SystemRandom::new(); + let rng = SystemRandom::new(); test::run(data_file, |section, test_case| { assert_eq!(section, ""); @@ -338,11 +329,11 @@ fn test_signature_ecdsa_sign_fixed_sign_and_verify(data_file: test::File) { }; let private_key = - signature::EcdsaKeyPair::from_private_key_and_public_key(signing_alg, &d, &q).unwrap(); + EcdsaKeyPair::from_private_key_and_public_key(signing_alg, &d, &q).unwrap(); let signature = private_key.sign(&rng, &msg).unwrap(); - let public_key = signature::UnparsedPublicKey::new(verification_alg, q); + let public_key = UnparsedPublicKey::new(verification_alg, q); let vfy_result = public_key.verify(&msg, signature.as_ref()); assert!(vfy_result.is_ok()); @@ -365,7 +356,7 @@ fn signature_ecdsa_sign_asn1_sha3_test() { // different each time. Because of that, here we simply verify that the // signature verifies correctly. fn test_signature_ecdsa_sign_asn1(data_file: test::File) { - let rng = rand::SystemRandom::new(); + let rng = SystemRandom::new(); test::run(data_file, |section, test_case| { assert_eq!(section, ""); @@ -416,13 +407,69 @@ fn test_signature_ecdsa_sign_asn1(data_file: test::File) { }; let private_key = - signature::EcdsaKeyPair::from_private_key_and_public_key(signing_alg, &d, &q).unwrap(); + EcdsaKeyPair::from_private_key_and_public_key(signing_alg, &d, &q).unwrap(); let signature = private_key.sign(&rng, &msg).unwrap(); - let public_key = signature::UnparsedPublicKey::new(verification_alg, q); + let public_key = UnparsedPublicKey::new(verification_alg, q); assert_eq!(public_key.verify(&msg, signature.as_ref()), Ok(())); Ok(()) }); } + +#[test] +fn test_to_pkcs8() { + for signing_alg in [ + &signature::ECDSA_P521_SHA3_512_ASN1_SIGNING, + &signature::ECDSA_P521_SHA3_512_FIXED_SIGNING, + &signature::ECDSA_P521_SHA512_ASN1_SIGNING, + &signature::ECDSA_P521_SHA512_FIXED_SIGNING, + &signature::ECDSA_P384_SHA3_384_ASN1_SIGNING, + &signature::ECDSA_P384_SHA3_384_FIXED_SIGNING, + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + &signature::ECDSA_P384_SHA384_FIXED_SIGNING, + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + &signature::ECDSA_P256_SHA256_FIXED_SIGNING, + ] { + let rnd = SystemRandom::new(); + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); + let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); + } +} + +#[test] +fn test_private_key() { + for signing_alg in [ + &signature::ECDSA_P521_SHA3_512_ASN1_SIGNING, + &signature::ECDSA_P521_SHA3_512_FIXED_SIGNING, + &signature::ECDSA_P521_SHA512_ASN1_SIGNING, + &signature::ECDSA_P521_SHA512_FIXED_SIGNING, + &signature::ECDSA_P384_SHA3_384_ASN1_SIGNING, + &signature::ECDSA_P384_SHA3_384_FIXED_SIGNING, + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + &signature::ECDSA_P384_SHA384_FIXED_SIGNING, + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + &signature::ECDSA_P256_SHA256_FIXED_SIGNING, + ] { + let rnd = SystemRandom::new(); + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); + let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); + + let private_key = key_pair.private_key(); + let public_key = key_pair.public_key(); + + let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( + signing_alg, + private_key.as_ref(), + public_key.as_ref(), + ) + .unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); + assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); + } +} diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index 482d90ab8dd..af5b9fbea81 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -3,6 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +use aws_lc_rs::rand::SystemRandom; use aws_lc_rs::{ error, signature::{self, Ed25519KeyPair, KeyPair}, @@ -191,3 +192,42 @@ fn ed25519_test_public_key_coverage() { format!("{key_pair:?}") ); } + +#[test] +fn test_to_pkcs8() { + let rnd = SystemRandom::new(); + let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); +} + +#[test] +fn test_to_pkcs8v1() { + let rnd = SystemRandom::new(); + let key_pair_doc = Ed25519KeyPair::generate_pkcs8v1(&rnd).unwrap(); + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8v1().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); +} + +#[test] +fn test_seed() { + let rnd = SystemRandom::new(); + let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); + + let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); + let seed = key_pair.seed(); + + let pub_key = key_pair.public_key(); + + let key_pair_copy = + Ed25519KeyPair::from_seed_and_public_key(seed.as_ref(), pub_key.as_ref()).unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); + + assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); +} From 247dca52c90b59aabc4dbd06eb8d86b59b9f2f31 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Wed, 28 Jun 2023 12:17:55 -0400 Subject: [PATCH 03/24] Lifetime parameter for PrivateKey/Seed --- aws-lc-rs/src/ec.rs | 62 +++++--------------------------- aws-lc-rs/src/ec/key_pair.rs | 56 ++++++++++++++++++++++++----- aws-lc-rs/src/ed25519.rs | 8 +++-- aws-lc-rs/tests/ecdsa_tests.rs | 2 +- aws-lc-rs/tests/ed25519_tests.rs | 2 +- 5 files changed, 62 insertions(+), 68 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index ddeb7b30b45..ebaa05a321a 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -17,16 +17,15 @@ use aws_lc::EC_KEY_check_fips; #[cfg(not(feature = "fips"))] use aws_lc::EC_KEY_check_key; use aws_lc::{ - point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, CONF_modules_finish, - ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0, - ECDSA_SIG_to_bytes, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_get0_group, + point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, ECDSA_SIG_from_bytes, + ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0, ECDSA_SIG_to_bytes, + EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_get0_group, EC_KEY_get0_private_key, EC_KEY_get0_public_key, EC_KEY_new, EC_KEY_set_group, EC_KEY_set_private_key, EC_KEY_set_public_key, EC_POINT_new, EC_POINT_oct2point, EC_POINT_point2oct, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_CTX_new_id, EVP_PKEY_CTX_set_ec_paramgen_curve_nid, EVP_PKEY_assign_EC_KEY, EVP_PKEY_get0_EC_KEY, - EVP_PKEY_get_raw_private_key, EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, - NID_X9_62_prime256v1, NID_secp256k1, NID_secp384r1, NID_secp521r1, BIGNUM, ECDSA_SIG, EC_GROUP, - EC_POINT, EVP_PKEY, EVP_PKEY_EC, + EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, NID_X9_62_prime256v1, NID_secp256k1, + NID_secp384r1, NID_secp521r1, BIGNUM, ECDSA_SIG, EC_GROUP, EC_POINT, EVP_PKEY, EVP_PKEY_EC, }; #[cfg(test)] @@ -42,7 +41,6 @@ use std::ptr::null_mut; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; -use zeroize::Zeroize; pub(crate) mod key_pair; @@ -51,8 +49,6 @@ pub(crate) const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8; pub(crate) const SCALAR_MAX_BYTES: usize = ELEM_MAX_BYTES; -pub(crate) const PRIVATE_KEY_MAX_LEN: usize = 1 + ELEM_MAX_BYTES; - /// The maximum length, in bytes, of an encoded public key. pub(crate) const PUBLIC_KEY_MAX_LEN: usize = 1 + (2 * ELEM_MAX_BYTES); @@ -146,37 +142,6 @@ impl AsRef<[u8]> for PublicKey { unsafe impl Send for PublicKey {} unsafe impl Sync for PublicKey {} -#[derive(Clone)] -pub struct PrivateKey(Box<[u8]>); - -impl Drop for PrivateKey { - fn drop(&mut self) { - self.0.zeroize(); - } -} - -impl Debug for PrivateKey { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("EcdsaPrivateKey()") - } -} - -impl PrivateKey { - fn new(box_bytes: Box<[u8]>) -> Self { - PrivateKey(box_bytes) - } -} - -impl AsRef<[u8]> for PrivateKey { - #[inline] - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -unsafe impl Send for PrivateKey {} -unsafe impl Sync for PrivateKey {} - impl VerificationAlgorithm for EcdsaVerificationAlgorithm { #[inline] #[cfg(feature = "ring-sig-verify")] @@ -305,8 +270,9 @@ unsafe fn validate_evp_key( pub(crate) unsafe fn marshal_private_key_to_buffer( alg_id: &'static AlgorithmID, + buffer: &mut [u8; SCALAR_MAX_BYTES], evp_key: &ConstPointer, -) -> Result, Unspecified> { +) -> Result { let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_key))?; let private_bn = ConstPointer::new(EC_KEY_get0_private_key(*ec_key))?; let private_size: usize = ecdsa_fixed_number_byte_size(alg_id); @@ -315,23 +281,11 @@ pub(crate) unsafe fn marshal_private_key_to_buffer( debug_assert!(size <= private_size); } - let mut buffer = vec![0u8; SCALAR_MAX_BYTES]; if 1 != BN_bn2bin_padded(buffer.as_mut_ptr(), private_size, *private_bn) { return Err(Unspecified); } - buffer.truncate(private_size); - Ok(buffer) -} - -pub(crate) fn marshal_private_key( - alg_id: &'static AlgorithmID, - evp_key: &ConstPointer, -) -> Result { - unsafe { - let priv_key_bytes = marshal_private_key_to_buffer(alg_id, evp_key)?; - Ok(PrivateKey::new(priv_key_bytes.into())) - } + Ok(private_size) } pub(crate) unsafe fn marshal_public_key_to_buffer( diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 6ffb7ce4330..9527cf5b09a 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -5,8 +5,8 @@ use crate::digest::digest_ctx::DigestContext; use crate::ec::{ - evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PrivateKey, - PublicKey, + evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey, + SCALAR_MAX_BYTES, }; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; @@ -21,6 +21,7 @@ use std::mem::MaybeUninit; use std::ptr::{null, null_mut}; use std::fmt::{Debug, Formatter}; +use zeroize::Zeroize; /// An ECDSA key pair, used for signing. #[allow(clippy::module_name_repetitions)] @@ -28,7 +29,6 @@ pub struct EcdsaKeyPair { algorithm: &'static EcdsaSigningAlgorithm, evp_pkey: LcPtr, pubkey: PublicKey, - privkey: PrivateKey, } impl Debug for EcdsaKeyPair { @@ -57,12 +57,10 @@ impl EcdsaKeyPair { evp_pkey: LcPtr, ) -> Result { let pubkey = ec::marshal_public_key(&evp_pkey.as_const())?; - let privkey = ec::marshal_private_key(algorithm.id, &evp_pkey.as_const())?; Ok(Self { algorithm, evp_pkey, pubkey, - privkey, }) } @@ -110,7 +108,7 @@ impl EcdsaKeyPair { /// `error::Unspecified` on internal error. /// pub fn to_pkcs8(&self) -> Result { - unsafe { self.evp_pkey.marshall_private_key(Version::V1) } + self.evp_pkey.marshall_private_key(Version::V1) } /// Constructs an ECDSA key pair from the private key and public key bytes @@ -156,9 +154,18 @@ impl EcdsaKeyPair { /// /// # Errors /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. - #[must_use] - pub fn private_key(&self) -> &PrivateKey { - &self.privkey + pub fn private_key(&self) -> Result { + unsafe { + let mut priv_key_bytes = [0u8; SCALAR_MAX_BYTES]; + + let key_len = ec::marshal_private_key_to_buffer( + self.algorithm.id, + &mut priv_key_bytes, + &self.evp_pkey.as_const(), + )?; + + Ok(PrivateKey::new(self, priv_key_bytes[0..key_len].into())) + } } /// Returns the signature of the message using a random nonce. @@ -277,3 +284,34 @@ mod tests { } } } + +#[derive(Clone)] +pub struct PrivateKey<'a>(&'a EcdsaKeyPair, Box<[u8]>); + +impl Drop for PrivateKey<'_> { + fn drop(&mut self) { + self.1.zeroize(); + } +} + +impl Debug for PrivateKey<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("EcdsaPrivateKey()") + } +} + +impl<'a> PrivateKey<'a> { + fn new(key_pair: &'a EcdsaKeyPair, box_bytes: Box<[u8]>) -> Self { + PrivateKey(key_pair, box_bytes) + } +} + +impl AsRef<[u8]> for PrivateKey<'_> { + #[inline] + fn as_ref(&self) -> &[u8] { + self.1.as_ref() + } +} + +unsafe impl Send for PrivateKey<'_> {} +unsafe impl Sync for PrivateKey<'_> {} diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 8e68e64f1c8..3d51598b10b 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -386,9 +386,11 @@ impl Ed25519KeyPair { /// Provides the private key "seed" for this Ed25519 key pair. /// /// For serialization of the key pair, `Ed25519KeyPair::to_pkcs8()` is preferred. - #[must_use] - pub fn seed(&self) -> Seed { - Seed(self) + /// + /// # Errors + /// Currently the function cannot fail, but it might in future implementations. + pub fn seed(&self) -> Result { + Ok(Seed(self)) } } diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index e5287f4e5b8..80afa77930a 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -460,7 +460,7 @@ fn test_private_key() { let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - let private_key = key_pair.private_key(); + let private_key = key_pair.private_key().unwrap(); let public_key = key_pair.public_key(); let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index af5b9fbea81..74201c1cf7f 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -221,7 +221,7 @@ fn test_seed() { let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); - let seed = key_pair.seed(); + let seed = key_pair.seed().unwrap(); let pub_key = key_pair.public_key(); From 84c3bf4e140b5120529e7b91cf087f7326478801 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 25 Sep 2023 14:07:54 +0000 Subject: [PATCH 04/24] Expose private/public key ec types --- aws-lc-rs/src/ec.rs | 1 + aws-lc-rs/src/ec/key_pair.rs | 87 +++++++++++++++++++++------------- aws-lc-rs/src/signature.rs | 6 ++- aws-lc-rs/tests/ecdsa_tests.rs | 4 +- 4 files changed, 62 insertions(+), 36 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index ebaa05a321a..a1d1662da16 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -114,6 +114,7 @@ impl AlgorithmID { } } +/// Elliptic curve public key. #[derive(Clone)] pub struct PublicKey(Box<[u8]>); diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 9527cf5b09a..508957fa0fa 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -64,6 +64,20 @@ impl EcdsaKeyPair { }) } + /// Generates a new key pair. + /// + /// # Errors + /// `error::Unspecified` on internal error. + /// + pub fn generate(alg: &'static EcdsaSigningAlgorithm) -> Result { + unsafe { + let evp_pkey = ec::evp_key_generate(alg.0.id.nid())?; + validate_evp_key(&evp_pkey.as_const(), alg.id.nid())?; + + Ok(Self::new(alg, evp_pkey)?) + } + } + /// Constructs an ECDSA key pair by parsing an unencrypted PKCS#8 v1 /// id-ecPublicKey `ECPrivateKey` key. /// @@ -111,6 +125,15 @@ impl EcdsaKeyPair { self.evp_pkey.marshall_private_key(Version::V1) } + /// Serializes this `EcdsaKeyPair` into a PKCS#8 v1 document. + /// + /// # Errors + /// `error::Unspecified` on internal error. + /// + pub fn to_pkcs8v1(&self) -> Result { + self.evp_pkey.marshall_private_key(Version::V1) + } + /// Constructs an ECDSA key pair from the private key and public key bytes /// /// The private key must encoded as a big-endian fixed-length integer. For @@ -154,7 +177,7 @@ impl EcdsaKeyPair { /// /// # Errors /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. - pub fn private_key(&self) -> Result { + pub fn private_key(&self) -> Result, Unspecified> { unsafe { let mut priv_key_bytes = [0u8; SCALAR_MAX_BYTES]; @@ -255,37 +278,7 @@ fn compute_ecdsa_signature<'a>( Ok(&mut signature[0..out_sig_len]) } -#[cfg(test)] -mod tests { - use crate::rand::SystemRandom; - use crate::signature::*; - - #[test] - fn test_to_pkcs8() { - for signing_alg in [ - &ECDSA_P521_SHA3_512_ASN1_SIGNING, - &ECDSA_P521_SHA3_512_FIXED_SIGNING, - &ECDSA_P521_SHA512_ASN1_SIGNING, - &ECDSA_P521_SHA512_FIXED_SIGNING, - &ECDSA_P384_SHA3_384_ASN1_SIGNING, - &ECDSA_P384_SHA3_384_FIXED_SIGNING, - &ECDSA_P384_SHA384_ASN1_SIGNING, - &ECDSA_P384_SHA384_FIXED_SIGNING, - &ECDSA_P256_SHA256_ASN1_SIGNING, - &ECDSA_P256_SHA256_FIXED_SIGNING, - ] { - let rnd = SystemRandom::new(); - let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); - let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - - let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); - // Verify that the exported bytes match the original generated bytes - assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); - } - } -} - -#[derive(Clone)] +/// Elliptic curve private key. pub struct PrivateKey<'a>(&'a EcdsaKeyPair, Box<[u8]>); impl Drop for PrivateKey<'_> { @@ -315,3 +308,33 @@ impl AsRef<[u8]> for PrivateKey<'_> { unsafe impl Send for PrivateKey<'_> {} unsafe impl Sync for PrivateKey<'_> {} + +#[cfg(test)] +mod tests { + use crate::rand::SystemRandom; + use crate::signature::*; + + #[test] + fn test_to_pkcs8() { + for signing_alg in [ + &ECDSA_P521_SHA3_512_ASN1_SIGNING, + &ECDSA_P521_SHA3_512_FIXED_SIGNING, + &ECDSA_P521_SHA512_ASN1_SIGNING, + &ECDSA_P521_SHA512_FIXED_SIGNING, + &ECDSA_P384_SHA3_384_ASN1_SIGNING, + &ECDSA_P384_SHA3_384_FIXED_SIGNING, + &ECDSA_P384_SHA384_ASN1_SIGNING, + &ECDSA_P384_SHA384_FIXED_SIGNING, + &ECDSA_P256_SHA256_ASN1_SIGNING, + &ECDSA_P256_SHA256_FIXED_SIGNING, + ] { + let rnd = SystemRandom::new(); + let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); + let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); + + let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + // Verify that the exported bytes match the original generated bytes + assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); + } + } +} diff --git a/aws-lc-rs/src/signature.rs b/aws-lc-rs/src/signature.rs index 0276789ed3b..a709570966f 100644 --- a/aws-lc-rs/src/signature.rs +++ b/aws-lc-rs/src/signature.rs @@ -250,9 +250,11 @@ use std::fmt::{Debug, Formatter}; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; -pub use crate::ec::key_pair::EcdsaKeyPair; +pub use crate::ec::key_pair::{EcdsaKeyPair, PrivateKey as EcdsaPrivateKey}; use crate::ec::EcdsaSignatureFormat; -pub use crate::ec::{EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm}; +pub use crate::ec::{ + EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, +}; pub use crate::ed25519::{Ed25519KeyPair, EdDSAParameters, ED25519_PUBLIC_KEY_LEN}; /// The longest signature is an ASN.1 P-384 signature where *r* and *s* are of diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index 80afa77930a..ba3255d5508 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -436,7 +436,7 @@ fn test_to_pkcs8() { let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + let key_pair_export_doc = key_pair.to_pkcs8v1().unwrap(); // Verify that the exported bytes match the original generated bytes assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); } @@ -469,7 +469,7 @@ fn test_private_key() { public_key.as_ref(), ) .unwrap(); - let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); } } From 406b6582c5e8078e907ba8227d4f339434fabd5d Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 25 Sep 2023 14:50:01 +0000 Subject: [PATCH 05/24] Expose PrivateKey::to_der --- aws-lc-rs/src/buffer.rs | 37 +++++++++++ aws-lc-rs/src/ec/key_pair.rs | 117 +++++++++++++-------------------- aws-lc-rs/src/lib.rs | 1 + aws-lc-rs/tests/ecdsa_tests.rs | 2 +- 4 files changed, 83 insertions(+), 74 deletions(-) create mode 100644 aws-lc-rs/src/buffer.rs diff --git a/aws-lc-rs/src/buffer.rs b/aws-lc-rs/src/buffer.rs new file mode 100644 index 00000000000..e907ba26ee1 --- /dev/null +++ b/aws-lc-rs/src/buffer.rs @@ -0,0 +1,37 @@ +//! This module exposes a buffer type used in crate APIs returning private keys and other "private" +//! contents. + +use std::fmt; + +use zeroize::Zeroize; + +/// This is a buffer type for private contents (e.g., private key bytes) which is zeroed on drop. +#[allow(clippy::module_name_repetitions)] +pub struct PrivateBuffer(Box<[u8]>); + +impl PrivateBuffer { + pub(crate) fn new(slice: &mut [u8]) -> PrivateBuffer { + let ret = PrivateBuffer(slice.to_vec().into_boxed_slice()); + slice.zeroize(); + ret + } +} + +impl Drop for PrivateBuffer { + fn drop(&mut self) { + self.0.zeroize(); + } +} + +impl fmt::Debug for PrivateBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("PrivateBuffer()") + } +} + +impl AsRef<[u8]> for PrivateBuffer { + #[inline] + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 508957fa0fa..b48fe1bf18f 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -3,6 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +use crate::buffer::PrivateBuffer; use crate::digest::digest_ctx::DigestContext; use crate::ec::{ evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey, @@ -11,17 +12,16 @@ use crate::ec::{ use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; use crate::pkcs8::{Document, Version}; -use crate::ptr::{DetachableLcPtr, LcPtr}; +use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr, Pointer}; use crate::rand::SecureRandom; use crate::signature::{KeyPair, Signature}; use crate::{digest, ec}; -use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY}; +use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY}; use std::fmt; use std::mem::MaybeUninit; use std::ptr::{null, null_mut}; use std::fmt::{Debug, Formatter}; -use zeroize::Zeroize; /// An ECDSA key pair, used for signing. #[allow(clippy::module_name_repetitions)] @@ -171,24 +171,10 @@ impl EcdsaKeyPair { } } - /// Exposes the private key encoded as a big-endian fixed-length integer. - /// - /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. - /// - /// # Errors - /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. - pub fn private_key(&self) -> Result, Unspecified> { - unsafe { - let mut priv_key_bytes = [0u8; SCALAR_MAX_BYTES]; - - let key_len = ec::marshal_private_key_to_buffer( - self.algorithm.id, - &mut priv_key_bytes, - &self.evp_pkey.as_const(), - )?; - - Ok(PrivateKey::new(self, priv_key_bytes[0..key_len].into())) - } + /// Access functions related to the private key. + #[must_use] + pub fn private_key(&self) -> PrivateKey<'_> { + PrivateKey(self) } /// Returns the signature of the message using a random nonce. @@ -279,62 +265,47 @@ fn compute_ecdsa_signature<'a>( } /// Elliptic curve private key. -pub struct PrivateKey<'a>(&'a EcdsaKeyPair, Box<[u8]>); - -impl Drop for PrivateKey<'_> { - fn drop(&mut self) { - self.1.zeroize(); - } -} +#[derive(Debug)] +pub struct PrivateKey<'a>(&'a EcdsaKeyPair); -impl Debug for PrivateKey<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("EcdsaPrivateKey()") - } -} - -impl<'a> PrivateKey<'a> { - fn new(key_pair: &'a EcdsaKeyPair, box_bytes: Box<[u8]>) -> Self { - PrivateKey(key_pair, box_bytes) - } -} +impl PrivateKey<'_> { + /// Exposes the private key encoded as a big-endian fixed-length integer. + /// + /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. + /// + /// # Errors + /// `error::Unspecified` if serialization failed. + pub fn to_integer(&self) -> Result { + unsafe { + let mut priv_key_bytes = [0u8; SCALAR_MAX_BYTES]; -impl AsRef<[u8]> for PrivateKey<'_> { - #[inline] - fn as_ref(&self) -> &[u8] { - self.1.as_ref() + let key_len = ec::marshal_private_key_to_buffer( + self.0.algorithm.id, + &mut priv_key_bytes, + &self.0.evp_pkey.as_const(), + )?; + Ok(PrivateBuffer::new(&mut priv_key_bytes[..key_len])) + } } -} -unsafe impl Send for PrivateKey<'_> {} -unsafe impl Sync for PrivateKey<'_> {} - -#[cfg(test)] -mod tests { - use crate::rand::SystemRandom; - use crate::signature::*; - - #[test] - fn test_to_pkcs8() { - for signing_alg in [ - &ECDSA_P521_SHA3_512_ASN1_SIGNING, - &ECDSA_P521_SHA3_512_FIXED_SIGNING, - &ECDSA_P521_SHA512_ASN1_SIGNING, - &ECDSA_P521_SHA512_FIXED_SIGNING, - &ECDSA_P384_SHA3_384_ASN1_SIGNING, - &ECDSA_P384_SHA3_384_FIXED_SIGNING, - &ECDSA_P384_SHA384_ASN1_SIGNING, - &ECDSA_P384_SHA384_FIXED_SIGNING, - &ECDSA_P256_SHA256_ASN1_SIGNING, - &ECDSA_P256_SHA256_FIXED_SIGNING, - ] { - let rnd = SystemRandom::new(); - let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); - let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - - let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); - // Verify that the exported bytes match the original generated bytes - assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); + /// Encode the private key via DER into bytes. + /// + /// # Errors + /// `error::Unspecified` if serialization failed. + pub fn to_der(&self) -> Result { + unsafe { + let mut outp = std::ptr::null_mut::(); + let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(self.0.evp_pkey.as_const_ptr()))?; + let length = aws_lc::i2d_ECPrivateKey(*ec_key, &mut outp); + if length < 0 { + return Err(Unspecified); + } + let outp = LcPtr::new(outp)?; + #[allow(clippy::cast_sign_loss)] + Ok(PrivateBuffer::new(std::slice::from_raw_parts_mut( + *outp, + length as usize, + ))) } } } diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index bf4609aeb76..84dde4ba147 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -103,6 +103,7 @@ extern crate core; pub mod aead; pub mod agreement; +pub mod buffer; pub mod constant_time; pub mod digest; pub mod error; diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index ba3255d5508..b218a495231 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -460,7 +460,7 @@ fn test_private_key() { let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - let private_key = key_pair.private_key().unwrap(); + let private_key = key_pair.private_key().to_integer().unwrap(); let public_key = key_pair.public_key(); let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( From de85b90d5d2de15bef7a84636b7dbea10846b254 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 25 Sep 2023 17:26:56 +0000 Subject: [PATCH 06/24] Add PublicKey::as_der --- aws-lc-rs/src/ec.rs | 49 ++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index a1d1662da16..b295118ea8d 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -116,27 +116,32 @@ impl AlgorithmID { /// Elliptic curve public key. #[derive(Clone)] -pub struct PublicKey(Box<[u8]>); +pub struct PublicKey { + octets: Box<[u8]>, + der: Box<[u8]>, +} + +impl PublicKey { + /// Provides the public key as a DER-encoded `SubjectPublicKeyInfo` structure. + #[must_use] + pub fn as_der(&self) -> &[u8] { + &self.der + } +} impl Debug for PublicKey { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { f.write_str(&format!( "EcdsaPublicKey(\"{}\")", - test::to_hex(self.0.as_ref()) + test::to_hex(self.octets.as_ref()) )) } } -impl PublicKey { - fn new(pubkey_box: Box<[u8]>) -> Self { - PublicKey(pubkey_box) - } -} - impl AsRef<[u8]> for PublicKey { #[inline] fn as_ref(&self) -> &[u8] { - self.0.as_ref() + self.octets.as_ref() } } @@ -307,13 +312,29 @@ pub(crate) unsafe fn marshal_public_key_to_buffer( } pub(crate) fn marshal_public_key( - evp_pkey: &ConstPointer, + evp_key: &ConstPointer, ) -> Result { + let mut pub_key_bytes = [0u8; PUBLIC_KEY_MAX_LEN]; unsafe { - let mut pub_key_bytes = [0u8; PUBLIC_KEY_MAX_LEN]; - let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_pkey)?; - let pub_key = Vec::from(&pub_key_bytes[0..key_len]); - Ok(PublicKey::new(pub_key.into_boxed_slice())) + let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_key)?; + + let der = { + let mut buffer = std::ptr::null_mut::(); + let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_key))?; + let len = aws_lc::i2d_EC_PUBKEY(*ec_key, &mut buffer); + if len < 0 { + return Err(Unspecified); + } + let buffer = LcPtr::new(buffer)?; + std::slice::from_raw_parts(*buffer, len.try_into()?) + .to_vec() + .into_boxed_slice() + }; + + Ok(PublicKey { + octets: pub_key_bytes[0..key_len].into(), + der, + }) } } From 902ba3f40a46741b5f30c712a41bfd5ab4ed0542 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Mon, 25 Sep 2023 18:45:40 +0000 Subject: [PATCH 07/24] Add EcdsaKeyPair::from_private_key_der --- aws-lc-rs/src/ec/key_pair.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index b48fe1bf18f..66acbf87675 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -171,6 +171,38 @@ impl EcdsaKeyPair { } } + /// Deserialize a DER private key and produce an ECDSA key. + /// + /// This function will attempt to automatically detect the underlying key format, and + /// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific + /// formats. + /// + /// # Errors + /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. + pub fn from_private_key_der( + alg: &'static EcdsaSigningAlgorithm, + private_key: &[u8], + ) -> Result { + unsafe { + let mut out = std::ptr::null_mut(); + if aws_lc::d2i_AutoPrivateKey( + &mut out, + &mut private_key.as_ptr(), + private_key.len().try_into().unwrap(), + ) + .is_null() + { + // FIXME: unclear which error or if we can get more detail + return Err(KeyRejected::unexpected_error()); + } + let evp_pkey = LcPtr::new(out)?; + let ec_key = evp_pkey.get_ec_key()?; + validate_ec_key(&ec_key.as_const(), alg.id.nid())?; + + Ok(Self::new(alg, ec_key)?) + } + } + /// Access functions related to the private key. #[must_use] pub fn private_key(&self) -> PrivateKey<'_> { From 6edfd325f70cc9e3db11ad972940d8f3de9ab0a2 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 12 Oct 2023 15:57:38 +0000 Subject: [PATCH 08/24] Fix version in comment --- aws-lc-rs/src/ed25519.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 3d51598b10b..505f0c99a01 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -222,7 +222,7 @@ impl Ed25519KeyPair { evp_pkey.marshall_private_key(Version::V1) } - /// Serializes this `Ed25519KeyPair` into a PKCS#8 v2 document. + /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document. /// /// # Errors /// `error::Unspecified` on internal error. From d881f0855a26d70e7002fa626afe58e4f4b1b514 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 13 Oct 2023 14:43:15 +0000 Subject: [PATCH 09/24] Refactor buffer to include per-type variants Also adds test coverage for EcdsaKeyPair::from_private_key_der and EcdsaPrivateKey::to_der (round trip test). --- aws-lc-rs/src/buffer.rs | 45 +++++++++++++++++++--------- aws-lc-rs/src/ec.rs | 22 +++++++++----- aws-lc-rs/src/ec/key_pair.rs | 54 +++++++++++++++++++--------------- aws-lc-rs/src/signature.rs | 6 ++-- aws-lc-rs/tests/ecdsa_tests.rs | 10 ++++++- 5 files changed, 89 insertions(+), 48 deletions(-) diff --git a/aws-lc-rs/src/buffer.rs b/aws-lc-rs/src/buffer.rs index e907ba26ee1..dd9a934e13c 100644 --- a/aws-lc-rs/src/buffer.rs +++ b/aws-lc-rs/src/buffer.rs @@ -1,35 +1,52 @@ //! This module exposes a buffer type used in crate APIs returning private keys and other "private" //! contents. +#![allow(clippy::module_name_repetitions)] + +use std::borrow::Cow; use std::fmt; +use std::marker::PhantomData; use zeroize::Zeroize; -/// This is a buffer type for private contents (e.g., private key bytes) which is zeroed on drop. -#[allow(clippy::module_name_repetitions)] -pub struct PrivateBuffer(Box<[u8]>); +/// This is a buffer type for some data exposed by various APIs in this crate. +/// +/// `T` acts as a discriminant between different kinds of data. +/// +/// The buffer will be zeroed on drop if it is owned. +pub struct Buffer<'a, T>(Cow<'a, [u8]>, PhantomData); -impl PrivateBuffer { - pub(crate) fn new(slice: &mut [u8]) -> PrivateBuffer { - let ret = PrivateBuffer(slice.to_vec().into_boxed_slice()); - slice.zeroize(); - ret +impl<'a, T> Drop for Buffer<'a, T> { + fn drop(&mut self) { + if let Cow::Owned(b) = &mut self.0 { + b.zeroize(); + } } } -impl Drop for PrivateBuffer { - fn drop(&mut self) { - self.0.zeroize(); +impl<'a, T> Buffer<'a, T> { + pub(crate) fn new(owned: Vec) -> Buffer<'a, T> { + Buffer(Cow::Owned(owned), PhantomData) + } + + pub(crate) fn take_from_slice(slice: &mut [u8]) -> Buffer<'a, T> { + let owned = slice.to_vec(); + slice.zeroize(); + Buffer(Cow::Owned(owned), PhantomData) + } + + pub(crate) fn public_from_slice(slice: &[u8]) -> Buffer<'_, T> { + Buffer(Cow::Borrowed(slice), PhantomData) } } -impl fmt::Debug for PrivateBuffer { +impl fmt::Debug for Buffer<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.write_str("PrivateBuffer()") + f.write_str("Buffer(...)") } } -impl AsRef<[u8]> for PrivateBuffer { +impl AsRef<[u8]> for Buffer<'_, T> { #[inline] fn as_ref(&self) -> &[u8] { self.0.as_ref() diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index b295118ea8d..f142a694e67 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -3,6 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +use crate::buffer::Buffer; use crate::digest::digest_ctx::DigestContext; use crate::error::{KeyRejected, Unspecified}; use core::fmt; @@ -121,11 +122,17 @@ pub struct PublicKey { der: Box<[u8]>, } +/// An elliptic curve public key as a DER-encoded `SubjectPublicKeyInfo` structure +#[allow(clippy::module_name_repetitions)] +pub struct EcPublicKeyDer { + _priv: (), +} + impl PublicKey { /// Provides the public key as a DER-encoded `SubjectPublicKeyInfo` structure. #[must_use] - pub fn as_der(&self) -> &[u8] { - &self.der + pub fn as_der(&self) -> Buffer<'_, EcPublicKeyDer> { + Buffer::public_from_slice(&self.der) } } @@ -276,10 +283,9 @@ unsafe fn validate_evp_key( pub(crate) unsafe fn marshal_private_key_to_buffer( alg_id: &'static AlgorithmID, - buffer: &mut [u8; SCALAR_MAX_BYTES], - evp_key: &ConstPointer, -) -> Result { - let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_key))?; + evp_pkey: &ConstPointer, +) -> Result, Unspecified> { + let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_pkey))?; let private_bn = ConstPointer::new(EC_KEY_get0_private_key(*ec_key))?; let private_size: usize = ecdsa_fixed_number_byte_size(alg_id); { @@ -287,11 +293,13 @@ pub(crate) unsafe fn marshal_private_key_to_buffer( debug_assert!(size <= private_size); } + let mut buffer = vec![0u8; SCALAR_MAX_BYTES]; if 1 != BN_bn2bin_padded(buffer.as_mut_ptr(), private_size, *private_bn) { return Err(Unspecified); } + buffer.truncate(private_size); - Ok(private_size) + Ok(buffer) } pub(crate) unsafe fn marshal_public_key_to_buffer( diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 66acbf87675..006f6fe9adc 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -3,16 +3,15 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use crate::buffer::PrivateBuffer; +use crate::buffer::Buffer; use crate::digest::digest_ctx::DigestContext; use crate::ec::{ evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey, - SCALAR_MAX_BYTES, }; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; use crate::pkcs8::{Document, Version}; -use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr, Pointer}; +use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr}; use crate::rand::SecureRandom; use crate::signature::{KeyPair, Signature}; use crate::{digest, ec}; @@ -174,11 +173,13 @@ impl EcdsaKeyPair { /// Deserialize a DER private key and produce an ECDSA key. /// /// This function will attempt to automatically detect the underlying key format, and - /// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific + /// supports the unencrypted PKCS#8 `PrivateKeyInfo` structures as well as key type specific /// formats. /// /// # Errors /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. + /// + /// # Panics pub fn from_private_key_der( alg: &'static EcdsaSigningAlgorithm, private_key: &[u8], @@ -188,7 +189,10 @@ impl EcdsaKeyPair { if aws_lc::d2i_AutoPrivateKey( &mut out, &mut private_key.as_ptr(), - private_key.len().try_into().unwrap(), + private_key + .len() + .try_into() + .map_err(|_| KeyRejected::too_large())?, ) .is_null() { @@ -196,10 +200,9 @@ impl EcdsaKeyPair { return Err(KeyRejected::unexpected_error()); } let evp_pkey = LcPtr::new(out)?; - let ec_key = evp_pkey.get_ec_key()?; - validate_ec_key(&ec_key.as_const(), alg.id.nid())?; + validate_evp_key(&evp_pkey.as_const(), alg.id.nid())?; - Ok(Self::new(alg, ec_key)?) + Ok(Self::new(alg, evp_pkey)?) } } @@ -300,6 +303,16 @@ fn compute_ecdsa_signature<'a>( #[derive(Debug)] pub struct PrivateKey<'a>(&'a EcdsaKeyPair); +/// Elliptic curve private key data encoded as a big-endian fixed-length integer. +pub struct EcPrivateKeyBuffer { + _priv: (), +} + +/// Elliptic curve private key data encoded as DER. +pub struct EcPrivateKeyDer { + _priv: (), +} + impl PrivateKey<'_> { /// Exposes the private key encoded as a big-endian fixed-length integer. /// @@ -307,16 +320,13 @@ impl PrivateKey<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_integer(&self) -> Result { + pub fn to_buffer(&self) -> Result, Unspecified> { unsafe { - let mut priv_key_bytes = [0u8; SCALAR_MAX_BYTES]; - - let key_len = ec::marshal_private_key_to_buffer( + let buffer = ec::marshal_private_key_to_buffer( self.0.algorithm.id, - &mut priv_key_bytes, &self.0.evp_pkey.as_const(), )?; - Ok(PrivateBuffer::new(&mut priv_key_bytes[..key_len])) + Ok(Buffer::::new(buffer)) } } @@ -324,19 +334,15 @@ impl PrivateKey<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_der(&self) -> Result { + pub fn to_der(&self) -> Result, Unspecified> { unsafe { let mut outp = std::ptr::null_mut::(); - let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(self.0.evp_pkey.as_const_ptr()))?; - let length = aws_lc::i2d_ECPrivateKey(*ec_key, &mut outp); - if length < 0 { - return Err(Unspecified); - } + let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(*self.0.evp_pkey))?; + let length = usize::try_from(aws_lc::i2d_ECPrivateKey(*ec_key, &mut outp)) + .map_err(|_| Unspecified)?; let outp = LcPtr::new(outp)?; - #[allow(clippy::cast_sign_loss)] - Ok(PrivateBuffer::new(std::slice::from_raw_parts_mut( - *outp, - length as usize, + Ok(Buffer::take_from_slice(std::slice::from_raw_parts_mut( + *outp, length, ))) } } diff --git a/aws-lc-rs/src/signature.rs b/aws-lc-rs/src/signature.rs index a709570966f..af829c76039 100644 --- a/aws-lc-rs/src/signature.rs +++ b/aws-lc-rs/src/signature.rs @@ -250,10 +250,12 @@ use std::fmt::{Debug, Formatter}; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; -pub use crate::ec::key_pair::{EcdsaKeyPair, PrivateKey as EcdsaPrivateKey}; +pub use crate::ec::key_pair::{ + EcPrivateKeyBuffer, EcPrivateKeyDer, EcdsaKeyPair, PrivateKey as EcdsaPrivateKey, +}; use crate::ec::EcdsaSignatureFormat; pub use crate::ec::{ - EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, + EcPublicKeyDer, EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, }; pub use crate::ed25519::{Ed25519KeyPair, EdDSAParameters, ED25519_PUBLIC_KEY_LEN}; diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index b218a495231..5a2a631d766 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -460,7 +460,7 @@ fn test_private_key() { let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - let private_key = key_pair.private_key().to_integer().unwrap(); + let private_key = key_pair.private_key().to_buffer().unwrap(); let public_key = key_pair.public_key(); let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( @@ -471,5 +471,13 @@ fn test_private_key() { .unwrap(); let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); + + let key_pair_copy = EcdsaKeyPair::from_private_key_der( + signing_alg, + key_pair.private_key().to_der().unwrap().as_ref(), + ) + .unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); + assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); } } From 63b0031ac7017a6c828d0ff5d794e965b0f6b80c Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 13 Oct 2023 15:12:11 +0000 Subject: [PATCH 10/24] Clearer Ed25519KeyPair APIs --- aws-lc-rs/src/ed25519.rs | 2 +- aws-lc-rs/tests/ed25519_tests.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 505f0c99a01..95e134c0ee1 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -190,7 +190,7 @@ impl Ed25519KeyPair { /// # Errors /// `error::Unspecified` on internal error. /// - pub fn to_pkcs8(&self) -> Result { + pub fn to_pkcs8v2(&self) -> Result { unsafe { let evp_pkey: LcPtr = LcPtr::new(EVP_PKEY_new_raw_private_key( EVP_PKEY_ED25519, diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index 74201c1cf7f..84a0a3abdaf 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -199,7 +199,7 @@ fn test_to_pkcs8() { let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); - let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); + let key_pair_export_doc = key_pair.to_pkcs8v2().unwrap(); // Verify that the exported bytes match the original generated bytes assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); } @@ -227,7 +227,7 @@ fn test_seed() { let key_pair_copy = Ed25519KeyPair::from_seed_and_public_key(seed.as_ref(), pub_key.as_ref()).unwrap(); - let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8v2().unwrap(); assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); } From b70efd870a7ccc108cebe1c767b9683ccbb8130f Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:34:00 -0400 Subject: [PATCH 11/24] Use `d2i_PrivateKey` instead of `d2i_AutoPrivateKey` --- aws-lc-rs/src/ec/key_pair.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 006f6fe9adc..9bde7b6bef1 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -15,7 +15,7 @@ use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr}; use crate::rand::SecureRandom; use crate::signature::{KeyPair, Signature}; use crate::{digest, ec}; -use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY}; +use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY, EVP_PKEY_EC}; use std::fmt; use std::mem::MaybeUninit; use std::ptr::{null, null_mut}; @@ -186,7 +186,8 @@ impl EcdsaKeyPair { ) -> Result { unsafe { let mut out = std::ptr::null_mut(); - if aws_lc::d2i_AutoPrivateKey( + if aws_lc::d2i_PrivateKey( + EVP_PKEY_EC, &mut out, &mut private_key.as_ptr(), private_key From 3aea14a7f293d71a41da760c2671d635d280c123 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:28:29 -0400 Subject: [PATCH 12/24] Add copyright for buffer.rs --- aws-lc-rs/src/buffer.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws-lc-rs/src/buffer.rs b/aws-lc-rs/src/buffer.rs index dd9a934e13c..9448a7a7000 100644 --- a/aws-lc-rs/src/buffer.rs +++ b/aws-lc-rs/src/buffer.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + //! This module exposes a buffer type used in crate APIs returning private keys and other "private" //! contents. From 1aa0684b88784ec409cd5f94049b167481f8dd41 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:23:33 -0400 Subject: [PATCH 13/24] API cleanup --- aws-lc-rs/src/ec/key_pair.rs | 12 ++---------- aws-lc-rs/src/ed25519.rs | 2 +- aws-lc-rs/tests/ed25519_tests.rs | 4 ++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 9bde7b6bef1..511f29fbc87 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -56,6 +56,7 @@ impl EcdsaKeyPair { evp_pkey: LcPtr, ) -> Result { let pubkey = ec::marshal_public_key(&evp_pkey.as_const())?; + Ok(Self { algorithm, evp_pkey, @@ -70,7 +71,7 @@ impl EcdsaKeyPair { /// pub fn generate(alg: &'static EcdsaSigningAlgorithm) -> Result { unsafe { - let evp_pkey = ec::evp_key_generate(alg.0.id.nid())?; + let evp_pkey = evp_key_generate(alg.0.id.nid())?; validate_evp_key(&evp_pkey.as_const(), alg.id.nid())?; Ok(Self::new(alg, evp_pkey)?) @@ -115,15 +116,6 @@ impl EcdsaKeyPair { evp_pkey.marshall_private_key(Version::V1) } - /// Serializes this `EcdsaKeyPair` into a PKCS#8 v1 document. - /// - /// # Errors - /// `error::Unspecified` on internal error. - /// - pub fn to_pkcs8(&self) -> Result { - self.evp_pkey.marshall_private_key(Version::V1) - } - /// Serializes this `EcdsaKeyPair` into a PKCS#8 v1 document. /// /// # Errors diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 95e134c0ee1..505f0c99a01 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -190,7 +190,7 @@ impl Ed25519KeyPair { /// # Errors /// `error::Unspecified` on internal error. /// - pub fn to_pkcs8v2(&self) -> Result { + pub fn to_pkcs8(&self) -> Result { unsafe { let evp_pkey: LcPtr = LcPtr::new(EVP_PKEY_new_raw_private_key( EVP_PKEY_ED25519, diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index 84a0a3abdaf..74201c1cf7f 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -199,7 +199,7 @@ fn test_to_pkcs8() { let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); - let key_pair_export_doc = key_pair.to_pkcs8v2().unwrap(); + let key_pair_export_doc = key_pair.to_pkcs8().unwrap(); // Verify that the exported bytes match the original generated bytes assert_eq!(key_pair_doc.as_ref(), key_pair_export_doc.as_ref()); } @@ -227,7 +227,7 @@ fn test_seed() { let key_pair_copy = Ed25519KeyPair::from_seed_and_public_key(seed.as_ref(), pub_key.as_ref()).unwrap(); - let key_pair_copy_doc = key_pair_copy.to_pkcs8v2().unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); } From 78a2f40b786095075ba3820b71a4db987be672db Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:22:02 -0400 Subject: [PATCH 14/24] Expose Ed25519Seed --- aws-lc-rs/src/ed25519.rs | 24 +++++++++++++++++++----- aws-lc-rs/src/signature.rs | 4 +++- aws-lc-rs/tests/ed25519_tests.rs | 3 ++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 505f0c99a01..3fec3c45639 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -20,6 +20,7 @@ use std::fmt::{Debug, Formatter}; use std::mem::MaybeUninit; use std::ptr::null_mut; +use crate::buffer::Buffer; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; use zeroize::Zeroize; @@ -99,12 +100,25 @@ impl Drop for Ed25519KeyPair { #[derive(Clone)] #[allow(clippy::module_name_repetitions)] +/// The seed value for the `EdDSA` signature scheme using Curve25519 pub struct Seed<'a>(&'a Ed25519KeyPair); -impl AsRef<[u8]> for Seed<'_> { - #[inline] - fn as_ref(&self) -> &[u8] { - &self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN] +/// Elliptic curve private key data encoded as a big-endian fixed-length integer. +#[allow(clippy::module_name_repetitions)] +pub struct Ed25519SeedBuffer { + _priv: (), +} + +impl Seed<'_> { + /// Exposes the seed encoded as a big-endian fixed-length integer. + /// + /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. + /// + /// # Errors + /// `error::Unspecified` if serialization failed. + pub fn to_buffer(&self) -> Result, Unspecified> { + let buffer = Vec::from(&self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN]); + Ok(Buffer::::new(buffer)) } } @@ -383,7 +397,7 @@ impl Ed25519KeyPair { })) } - /// Provides the private key "seed" for this Ed25519 key pair. + /// Provides the private key "seed" for this `Ed25519` key pair. /// /// For serialization of the key pair, `Ed25519KeyPair::to_pkcs8()` is preferred. /// diff --git a/aws-lc-rs/src/signature.rs b/aws-lc-rs/src/signature.rs index af829c76039..5424588a3b7 100644 --- a/aws-lc-rs/src/signature.rs +++ b/aws-lc-rs/src/signature.rs @@ -257,7 +257,9 @@ use crate::ec::EcdsaSignatureFormat; pub use crate::ec::{ EcPublicKeyDer, EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, }; -pub use crate::ed25519::{Ed25519KeyPair, EdDSAParameters, ED25519_PUBLIC_KEY_LEN}; +pub use crate::ed25519::{ + Ed25519KeyPair, EdDSAParameters, Seed as Ed25519Seed, ED25519_PUBLIC_KEY_LEN, +}; /// The longest signature is an ASN.1 P-384 signature where *r* and *s* are of /// maximum length with the leading high bit set on each. Then each component diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index 74201c1cf7f..3cbe2644398 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -222,11 +222,12 @@ fn test_seed() { let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); let seed = key_pair.seed().unwrap(); + let seed_buffer = seed.to_buffer().unwrap(); let pub_key = key_pair.public_key(); let key_pair_copy = - Ed25519KeyPair::from_seed_and_public_key(seed.as_ref(), pub_key.as_ref()).unwrap(); + Ed25519KeyPair::from_seed_and_public_key(seed_buffer.as_ref(), pub_key.as_ref()).unwrap(); let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); From 0b72c89caa22d2b56d1b70fda59b52a9f82066b0 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:26:02 -0400 Subject: [PATCH 15/24] Move Ed25519 private key into Box --- aws-lc-rs/src/ed25519.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 3fec3c45639..257191a63d7 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -79,7 +79,7 @@ impl VerificationAlgorithm for EdDSAParameters { /// An Ed25519 key pair, for signing. #[allow(clippy::module_name_repetitions)] pub struct Ed25519KeyPair { - private_key: [u8; ED25519_PRIVATE_KEY_LEN], + private_key: Box<[u8; ED25519_PRIVATE_KEY_LEN]>, public_key: PublicKey, } @@ -281,15 +281,17 @@ impl Ed25519KeyPair { seed.as_ptr(), ); let derived_public_key = derived_public_key.assume_init(); - let private_key = private_key.assume_init(); + let mut private_key = private_key.assume_init(); constant_time::verify_slices_are_equal(public_key, &derived_public_key) .map_err(|_| KeyRejected::inconsistent_components())?; - Ok(Self { - private_key, + let key_pair = Self { + private_key: Box::new(private_key), public_key: PublicKey(derived_public_key), - }) + }; + private_key.zeroize(); + Ok(key_pair) } } @@ -352,9 +354,10 @@ impl Ed25519KeyPair { private_key[ED25519_PRIVATE_KEY_SEED_LEN..].copy_from_slice(&public_key); let key_pair = Self { - private_key, + private_key: Box::new(private_key), public_key: PublicKey(public_key), }; + private_key.zeroize(); Ok(key_pair) } From 0f3ead82d310ba5a6151d523adeba1d5bd40a27d Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Wed, 25 Oct 2023 10:18:44 -0400 Subject: [PATCH 16/24] Improve test coverage --- aws-lc-rs/src/buffer.rs | 26 ++++++++++++++++++++++++++ aws-lc-rs/src/ec/key_pair.rs | 5 ++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/aws-lc-rs/src/buffer.rs b/aws-lc-rs/src/buffer.rs index 9448a7a7000..4e02f38ff4e 100644 --- a/aws-lc-rs/src/buffer.rs +++ b/aws-lc-rs/src/buffer.rs @@ -55,3 +55,29 @@ impl AsRef<[u8]> for Buffer<'_, T> { self.0.as_ref() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let buffer: Buffer = Buffer::new(vec![1, 2, 3]); + assert_eq!(buffer.as_ref(), &[1, 2, 3]); + } + + #[test] + fn test_take_from_slice() { + let mut slice = [1, 2, 3]; + let buffer: Buffer = Buffer::take_from_slice(&mut slice); + assert_eq!(buffer.as_ref(), &[1, 2, 3]); + assert_eq!(slice, [0, 0, 0]); + } + + #[test] + fn test_public_from_slice() { + let slice = [1, 2, 3]; + let buffer: Buffer = Buffer::public_from_slice(&slice); + assert_eq!(buffer.as_ref(), &[1, 2, 3]); + } +} diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 511f29fbc87..130f7990f34 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -72,7 +72,6 @@ impl EcdsaKeyPair { pub fn generate(alg: &'static EcdsaSigningAlgorithm) -> Result { unsafe { let evp_pkey = evp_key_generate(alg.0.id.nid())?; - validate_evp_key(&evp_pkey.as_const(), alg.id.nid())?; Ok(Self::new(alg, evp_pkey)?) } @@ -111,9 +110,9 @@ impl EcdsaKeyPair { alg: &'static EcdsaSigningAlgorithm, _rng: &dyn SecureRandom, ) -> Result { - let evp_pkey = evp_key_generate(alg.0.id.nid())?; + let key_pair = Self::generate(alg)?; - evp_pkey.marshall_private_key(Version::V1) + key_pair.to_pkcs8v1() } /// Serializes this `EcdsaKeyPair` into a PKCS#8 v1 document. From 50edaf2d40782ea5fdae17f67b4de8d642212458 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:02:50 -0400 Subject: [PATCH 17/24] More increase test coverage --- aws-lc-rs/src/ec.rs | 28 ++++++++++++++++++++++++---- aws-lc-rs/src/ec/key_pair.rs | 7 ++++++- aws-lc-rs/src/ed25519.rs | 14 ++++++++++---- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index f142a694e67..482338ac5dc 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -578,7 +578,7 @@ unsafe fn ecdsa_sig_from_fixed( #[cfg(test)] mod tests { use crate::ec::key_pair::EcdsaKeyPair; - use crate::signature::ECDSA_P256_SHA256_FIXED_SIGNING; + use crate::signature::{KeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; use crate::test::from_dirty_hex; use crate::{signature, test}; @@ -592,7 +592,26 @@ mod tests { ); let result = EcdsaKeyPair::from_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &input); - result.unwrap(); + assert!(result.is_ok()); + let key_pair = result.unwrap(); + assert_eq!("EcdsaKeyPair { public_key: EcdsaPublicKey(\"04cf0d13a3a7577231ea1b66cf4021cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9b0437af3f7af6e724\") }", + format!("{key_pair:?}")); + assert_eq!( + "EcdsaPrivateKey(ECDSA_P256)", + format!("{:?}", key_pair.private_key()) + ); + let pub_key = key_pair.public_key(); + let der_pub_key = pub_key.as_der(); + + assert_eq!( + from_dirty_hex( + r"3059301306072a8648ce3d020106082a8648ce3d03010703420004cf0d13a3a7577231ea1b66cf402 + 1cd54f21f4ac4f5f2fdd28e05bc7d2bd099d1374cd08d2ef654d6f04498db462f73e0282058dd661a4c9 + b0437af3f7af6e724", + ) + .as_slice(), + der_pub_key.as_ref() + ); } #[test] @@ -616,8 +635,9 @@ mod tests { r"30440220341f6779b75e98bb42e01095dd48356cbf9002dc704ac8bd2a8240b8 8d3796c60220555843b1b4e264fe6ffe6e2b705a376c05c09404303ffe5d2711f3e3b3a010a1", ); - let actual_result = - signature::UnparsedPublicKey::new(alg, &public_key).verify(msg.as_bytes(), &sig); + let unparsed_pub_key = signature::UnparsedPublicKey::new(alg, &public_key); + + let actual_result = unparsed_pub_key.verify(msg.as_bytes(), &sig); assert!(actual_result.is_ok(), "Key: {}", test::to_hex(public_key)); } } diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 130f7990f34..5bc2e3d547c 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -292,9 +292,14 @@ fn compute_ecdsa_signature<'a>( } /// Elliptic curve private key. -#[derive(Debug)] pub struct PrivateKey<'a>(&'a EcdsaKeyPair); +impl Debug for PrivateKey<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(&format!("EcdsaPrivateKey({:?})", self.0.algorithm.id)) + } +} + /// Elliptic curve private key data encoded as a big-endian fixed-length integer. pub struct EcPrivateKeyBuffer { _priv: (), diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 257191a63d7..371234c709a 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -422,14 +422,20 @@ mod tests { fn test_generate_pkcs8() { let rng = SystemRandom::new(); let document = Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); - let _: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap(); - let _: Ed25519KeyPair = + let kp1: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap(); + let kp2: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8_maybe_unchecked(document.as_ref()).unwrap(); + assert_eq!(kp1.private_key.as_slice(), kp2.private_key.as_slice()); + assert_eq!(kp1.public_key.as_ref(), kp2.public_key.as_ref()); let document = Ed25519KeyPair::generate_pkcs8v1(&rng).unwrap(); - let _: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap(); - let _: Ed25519KeyPair = + let kp1: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap(); + let kp2: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8_maybe_unchecked(document.as_ref()).unwrap(); + assert_eq!(kp1.private_key.as_slice(), kp2.private_key.as_slice()); + assert_eq!(kp1.public_key.as_ref(), kp2.public_key.as_ref()); + let seed = kp1.seed().unwrap(); + assert_eq!("Ed25519Seed()", format!("{seed:?}")); } #[test] From 1c1cd8ca01a33cd5efbc7c9bdb80f5f1a460a048 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Fri, 27 Oct 2023 11:45:50 -0400 Subject: [PATCH 18/24] Update per PR comments --- aws-lc-rs/src/ec.rs | 17 ++++++++------ aws-lc-rs/src/ec/key_pair.rs | 29 +++++++++++++----------- aws-lc-rs/src/signature.rs | 12 ++++++---- aws-lc-rs/tests/ecdsa_tests.rs | 41 ++++++++++++++++++---------------- 4 files changed, 56 insertions(+), 43 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 482338ac5dc..10f1d6e40c1 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -122,16 +122,16 @@ pub struct PublicKey { der: Box<[u8]>, } -/// An elliptic curve public key as a DER-encoded `SubjectPublicKeyInfo` structure +/// An elliptic curve public key as a DER-encoded (X509) `SubjectPublicKeyInfo` structure #[allow(clippy::module_name_repetitions)] -pub struct EcPublicKeyDer { +pub struct EcPublicKeyX509Der { _priv: (), } impl PublicKey { - /// Provides the public key as a DER-encoded `SubjectPublicKeyInfo` structure. + /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure. #[must_use] - pub fn as_der(&self) -> Buffer<'_, EcPublicKeyDer> { + pub fn as_der(&self) -> Buffer<'_, EcPublicKeyX509Der> { Buffer::public_from_slice(&self.der) } } @@ -147,6 +147,9 @@ impl Debug for PublicKey { impl AsRef<[u8]> for PublicKey { #[inline] + /// Serializes the public key in an uncompressed form (X9.62) using the + /// Octet-String-to-Elliptic-Curve-Point algorithm in + /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]. fn as_ref(&self) -> &[u8] { self.octets.as_ref() } @@ -320,15 +323,15 @@ pub(crate) unsafe fn marshal_public_key_to_buffer( } pub(crate) fn marshal_public_key( - evp_key: &ConstPointer, + evp_pkey: &ConstPointer, ) -> Result { let mut pub_key_bytes = [0u8; PUBLIC_KEY_MAX_LEN]; unsafe { - let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_key)?; + let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_pkey)?; let der = { let mut buffer = std::ptr::null_mut::(); - let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_key))?; + let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_pkey))?; let len = aws_lc::i2d_EC_PUBKEY(*ec_key, &mut buffer); if len < 0 { return Err(Unspecified); diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index 5bc2e3d547c..ca551a312fa 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -13,7 +13,7 @@ use crate::fips::indicator_check; use crate::pkcs8::{Document, Version}; use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr}; use crate::rand::SecureRandom; -use crate::signature::{KeyPair, Signature}; +use crate::signature::{EcdsaPublicKey, KeyPair, Signature}; use crate::{digest, ec}; use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY, EVP_PKEY_EC}; use std::fmt; @@ -44,7 +44,8 @@ impl KeyPair for EcdsaKeyPair { type PublicKey = PublicKey; #[inline] - fn public_key(&self) -> &Self::PublicKey { + /// Provides the public key. + fn public_key(&self) -> &EcdsaPublicKey { &self.pubkey } } @@ -161,11 +162,13 @@ impl EcdsaKeyPair { } } - /// Deserialize a DER private key and produce an ECDSA key. + /// Deserializes a DER-encoded private key structure to produce a `EcdsaKeyPair`. /// - /// This function will attempt to automatically detect the underlying key format, and - /// supports the unencrypted PKCS#8 `PrivateKeyInfo` structures as well as key type specific - /// formats. + /// This function is typically used to deserialize RFC 5915 encoded private keys, but it will + /// attempt to automatically detect other key formats. This function supports unencrypted + /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats. + /// + /// See `EcdsaPrivateKey::to_der`. /// /// # Errors /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. @@ -301,12 +304,12 @@ impl Debug for PrivateKey<'_> { } /// Elliptic curve private key data encoded as a big-endian fixed-length integer. -pub struct EcPrivateKeyBuffer { +pub struct EcPrivateKeyBin { _priv: (), } -/// Elliptic curve private key data encoded as DER. -pub struct EcPrivateKeyDer { +/// Elliptic curve private key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. +pub struct EcPrivateKeyRfc5915Der { _priv: (), } @@ -317,21 +320,21 @@ impl PrivateKey<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_buffer(&self) -> Result, Unspecified> { + pub fn to_bin(&self) -> Result, Unspecified> { unsafe { let buffer = ec::marshal_private_key_to_buffer( self.0.algorithm.id, &self.0.evp_pkey.as_const(), )?; - Ok(Buffer::::new(buffer)) + Ok(Buffer::::new(buffer)) } } - /// Encode the private key via DER into bytes. + /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_der(&self) -> Result, Unspecified> { + pub fn to_der(&self) -> Result, Unspecified> { unsafe { let mut outp = std::ptr::null_mut::(); let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(*self.0.evp_pkey))?; diff --git a/aws-lc-rs/src/signature.rs b/aws-lc-rs/src/signature.rs index 5424588a3b7..6f754f0c30e 100644 --- a/aws-lc-rs/src/signature.rs +++ b/aws-lc-rs/src/signature.rs @@ -250,17 +250,21 @@ use std::fmt::{Debug, Formatter}; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; -pub use crate::ec::key_pair::{ - EcPrivateKeyBuffer, EcPrivateKeyDer, EcdsaKeyPair, PrivateKey as EcdsaPrivateKey, -}; +pub use crate::ec::key_pair::{EcdsaKeyPair, PrivateKey as EcdsaPrivateKey}; use crate::ec::EcdsaSignatureFormat; pub use crate::ec::{ - EcPublicKeyDer, EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, + EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, }; pub use crate::ed25519::{ Ed25519KeyPair, EdDSAParameters, Seed as Ed25519Seed, ED25519_PUBLIC_KEY_LEN, }; +/// A module containing types that indicate the encoding of a `buffer::Buffer` +pub mod encoding { + pub use crate::ec::key_pair::{EcPrivateKeyBin, EcPrivateKeyRfc5915Der}; + pub use crate::ec::EcPublicKeyX509Der; +} + /// The longest signature is an ASN.1 P-384 signature where *r* and *s* are of /// maximum length with the leading high bit set on each. Then each component /// will have a tag, a one-byte length, and a one-byte “I'm not negative” diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index 5a2a631d766..a99eded1fc0 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -460,24 +460,27 @@ fn test_private_key() { let key_pair_doc = EcdsaKeyPair::generate_pkcs8(signing_alg, &rnd).unwrap(); let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); - let private_key = key_pair.private_key().to_buffer().unwrap(); - let public_key = key_pair.public_key(); - - let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( - signing_alg, - private_key.as_ref(), - public_key.as_ref(), - ) - .unwrap(); - let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); - assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); - - let key_pair_copy = EcdsaKeyPair::from_private_key_der( - signing_alg, - key_pair.private_key().to_der().unwrap().as_ref(), - ) - .unwrap(); - let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); - assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); + { + let private_key = key_pair.private_key().to_bin().unwrap(); + let public_key = key_pair.public_key(); + let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( + signing_alg, + private_key.as_ref(), + public_key.as_ref(), + ) + .unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); + assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); + } + { + let private_key_der = key_pair.private_key().to_der().unwrap(); + assert_eq!("Buffer(...)", format!("{private_key_der:?}")); + assert!(EcdsaKeyPair::from_pkcs8(signing_alg, private_key_der.as_ref()).is_err()); + + let key_pair_copy = + EcdsaKeyPair::from_private_key_der(signing_alg, private_key_der.as_ref()).unwrap(); + let key_pair_copy_doc = key_pair_copy.to_pkcs8v1().unwrap(); + assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); + } } } From d76698daf4795b42b719d54e711a7be27e4e2ac3 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Wed, 29 Nov 2023 09:12:44 -0500 Subject: [PATCH 19/24] Buffer type aliases; Generate DER only when requested --- aws-lc-rs/src/buffer.rs | 2 ++ aws-lc-rs/src/ec.rs | 50 +++++++++++++++++++++--------------- aws-lc-rs/src/ec/key_pair.rs | 18 +++++++------ aws-lc-rs/src/ed25519.rs | 10 +++++--- 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/aws-lc-rs/src/buffer.rs b/aws-lc-rs/src/buffer.rs index 4e02f38ff4e..67c8e9bb1fd 100644 --- a/aws-lc-rs/src/buffer.rs +++ b/aws-lc-rs/src/buffer.rs @@ -38,6 +38,8 @@ impl<'a, T> Buffer<'a, T> { Buffer(Cow::Owned(owned), PhantomData) } + // TODO: remove this "allow" once this is used. + #[allow(dead_code)] pub(crate) fn public_from_slice(slice: &[u8]) -> Buffer<'_, T> { Buffer(Cow::Borrowed(slice), PhantomData) } diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 10f1d6e40c1..87156724039 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -118,21 +118,41 @@ impl AlgorithmID { /// Elliptic curve public key. #[derive(Clone)] pub struct PublicKey { + algorithm: &'static EcdsaSigningAlgorithm, octets: Box<[u8]>, - der: Box<[u8]>, } -/// An elliptic curve public key as a DER-encoded (X509) `SubjectPublicKeyInfo` structure #[allow(clippy::module_name_repetitions)] -pub struct EcPublicKeyX509Der { +pub struct EcPublicKeyX509DerType { _priv: (), } +/// An elliptic curve public key as a DER-encoded (X509) `SubjectPublicKeyInfo` structure +#[allow(clippy::module_name_repetitions)] +pub type EcPublicKeyX509Der<'a> = Buffer<'a, EcPublicKeyX509DerType>; impl PublicKey { /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure. - #[must_use] - pub fn as_der(&self) -> Buffer<'_, EcPublicKeyX509Der> { - Buffer::public_from_slice(&self.der) + /// # Errors + /// Returns an error if the underlying implementation is unable to marshal the point. + pub fn as_der(&self) -> Result, Unspecified> { + let ec_group = unsafe { LcPtr::new(EC_GROUP_new_by_curve_name(self.algorithm.id.nid()))? }; + let ec_point = unsafe { ec_point_from_bytes(&ec_group, self.as_ref())? }; + let ec_key = unsafe { LcPtr::new(EC_KEY_new())? }; + if 1 != unsafe { EC_KEY_set_group(*ec_key, *ec_group) } { + return Err(Unspecified); + } + if 1 != unsafe { EC_KEY_set_public_key(*ec_key, *ec_point) } { + return Err(Unspecified); + } + let mut buffer = null_mut::(); + let len = unsafe { aws_lc::i2d_EC_PUBKEY(*ec_key, &mut buffer) }; + if len < 0 || buffer.is_null() { + return Err(Unspecified); + } + let buffer = LcPtr::new(buffer)?; + let mut der = unsafe { std::slice::from_raw_parts(*buffer, len.try_into()?) }.to_owned(); + + Ok(Buffer::take_from_slice(&mut der)) } } @@ -324,27 +344,15 @@ pub(crate) unsafe fn marshal_public_key_to_buffer( pub(crate) fn marshal_public_key( evp_pkey: &ConstPointer, + algorithm: &'static EcdsaSigningAlgorithm, ) -> Result { let mut pub_key_bytes = [0u8; PUBLIC_KEY_MAX_LEN]; unsafe { let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_pkey)?; - let der = { - let mut buffer = std::ptr::null_mut::(); - let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(**evp_pkey))?; - let len = aws_lc::i2d_EC_PUBKEY(*ec_key, &mut buffer); - if len < 0 { - return Err(Unspecified); - } - let buffer = LcPtr::new(buffer)?; - std::slice::from_raw_parts(*buffer, len.try_into()?) - .to_vec() - .into_boxed_slice() - }; - Ok(PublicKey { + algorithm, octets: pub_key_bytes[0..key_len].into(), - der, }) } } @@ -604,7 +612,7 @@ mod tests { format!("{:?}", key_pair.private_key()) ); let pub_key = key_pair.public_key(); - let der_pub_key = pub_key.as_der(); + let der_pub_key = pub_key.as_der().unwrap(); assert_eq!( from_dirty_hex( diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index ca551a312fa..fa8ffbb3843 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -56,7 +56,7 @@ impl EcdsaKeyPair { algorithm: &'static EcdsaSigningAlgorithm, evp_pkey: LcPtr, ) -> Result { - let pubkey = ec::marshal_public_key(&evp_pkey.as_const())?; + let pubkey = ec::marshal_public_key(&evp_pkey.as_const(), algorithm)?; Ok(Self { algorithm, @@ -303,15 +303,17 @@ impl Debug for PrivateKey<'_> { } } -/// Elliptic curve private key data encoded as a big-endian fixed-length integer. -pub struct EcPrivateKeyBin { +pub struct EcPrivateKeyBinType { _priv: (), } +/// Elliptic curve private key data encoded as a big-endian fixed-length integer. +pub type EcPrivateKeyBin<'a> = Buffer<'a, EcPrivateKeyBinType>; -/// Elliptic curve private key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. -pub struct EcPrivateKeyRfc5915Der { +pub struct EcPrivateKeyRfc5915DerType { _priv: (), } +/// Elliptic curve private key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. +pub type EcPrivateKeyRfc5915Der<'a> = Buffer<'a, EcPrivateKeyRfc5915DerType>; impl PrivateKey<'_> { /// Exposes the private key encoded as a big-endian fixed-length integer. @@ -320,13 +322,13 @@ impl PrivateKey<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_bin(&self) -> Result, Unspecified> { + pub fn to_bin(&self) -> Result, Unspecified> { unsafe { let buffer = ec::marshal_private_key_to_buffer( self.0.algorithm.id, &self.0.evp_pkey.as_const(), )?; - Ok(Buffer::::new(buffer)) + Ok(EcPrivateKeyBin::new(buffer)) } } @@ -334,7 +336,7 @@ impl PrivateKey<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_der(&self) -> Result, Unspecified> { + pub fn to_der(&self) -> Result, Unspecified> { unsafe { let mut outp = std::ptr::null_mut::(); let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(*self.0.evp_pkey))?; diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 371234c709a..825ee03139a 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -103,11 +103,13 @@ impl Drop for Ed25519KeyPair { /// The seed value for the `EdDSA` signature scheme using Curve25519 pub struct Seed<'a>(&'a Ed25519KeyPair); -/// Elliptic curve private key data encoded as a big-endian fixed-length integer. #[allow(clippy::module_name_repetitions)] -pub struct Ed25519SeedBuffer { +pub struct Ed25519SeedBufferType { _priv: (), } +/// Elliptic curve private key data encoded as a big-endian fixed-length integer. +#[allow(clippy::module_name_repetitions)] +pub type Ed25519SeedBuffer<'a> = Buffer<'a, Ed25519SeedBufferType>; impl Seed<'_> { /// Exposes the seed encoded as a big-endian fixed-length integer. @@ -116,9 +118,9 @@ impl Seed<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_buffer(&self) -> Result, Unspecified> { + pub fn to_buffer(&self) -> Result { let buffer = Vec::from(&self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN]); - Ok(Buffer::::new(buffer)) + Ok(Ed25519SeedBuffer::new(buffer)) } } From f56c92b2b6fd42b0dc5e089f4b8a9ee9a4cb2ab6 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:50:50 -0500 Subject: [PATCH 20/24] Private buffer mod --- aws-lc-rs/src/ed25519.rs | 6 +++--- aws-lc-rs/src/lib.rs | 2 +- aws-lc-rs/src/signature.rs | 6 ------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 825ee03139a..db5eff34821 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -109,7 +109,7 @@ pub struct Ed25519SeedBufferType { } /// Elliptic curve private key data encoded as a big-endian fixed-length integer. #[allow(clippy::module_name_repetitions)] -pub type Ed25519SeedBuffer<'a> = Buffer<'a, Ed25519SeedBufferType>; +pub type Ed25519SeedBin<'a> = Buffer<'a, Ed25519SeedBufferType>; impl Seed<'_> { /// Exposes the seed encoded as a big-endian fixed-length integer. @@ -118,9 +118,9 @@ impl Seed<'_> { /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_buffer(&self) -> Result { + pub fn to_buffer(&self) -> Result { let buffer = Vec::from(&self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN]); - Ok(Ed25519SeedBuffer::new(buffer)) + Ok(Ed25519SeedBin::new(buffer)) } } diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index 84dde4ba147..5e8317fe508 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -103,7 +103,7 @@ extern crate core; pub mod aead; pub mod agreement; -pub mod buffer; +mod buffer; pub mod constant_time; pub mod digest; pub mod error; diff --git a/aws-lc-rs/src/signature.rs b/aws-lc-rs/src/signature.rs index 6f754f0c30e..0cfa3b08dc3 100644 --- a/aws-lc-rs/src/signature.rs +++ b/aws-lc-rs/src/signature.rs @@ -259,12 +259,6 @@ pub use crate::ed25519::{ Ed25519KeyPair, EdDSAParameters, Seed as Ed25519Seed, ED25519_PUBLIC_KEY_LEN, }; -/// A module containing types that indicate the encoding of a `buffer::Buffer` -pub mod encoding { - pub use crate::ec::key_pair::{EcPrivateKeyBin, EcPrivateKeyRfc5915Der}; - pub use crate::ec::EcPublicKeyX509Der; -} - /// The longest signature is an ASN.1 P-384 signature where *r* and *s* are of /// maximum length with the leading high bit set on each. Then each component /// will have a tag, a one-byte length, and a one-byte “I'm not negative” From 1b4c9d1b9f1c0c24d552f6e4677fefc0ce011d36 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:46:52 -0500 Subject: [PATCH 21/24] Traits: AsDer and AsBin --- aws-lc-rs/src/ec.rs | 51 ++++++++++++++++---------------- aws-lc-rs/src/ec/key_pair.rs | 30 +++++++++++-------- aws-lc-rs/src/ed25519.rs | 35 ++++++++++++---------- aws-lc-rs/src/lib.rs | 20 +++++++++++++ aws-lc-rs/src/signature.rs | 19 +++++++----- aws-lc-rs/tests/ecdsa_tests.rs | 7 +++-- aws-lc-rs/tests/ed25519_tests.rs | 4 ++- 7 files changed, 101 insertions(+), 65 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 87156724039..3456c550842 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -3,20 +3,24 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use crate::buffer::Buffer; -use crate::digest::digest_ctx::DigestContext; -use crate::error::{KeyRejected, Unspecified}; use core::fmt; +use std::fmt::{Debug, Formatter}; +use std::mem::MaybeUninit; +use std::ops::Deref; +use std::os::raw::{c_int, c_uint}; +#[cfg(test)] +use std::ptr::null; +use std::ptr::null_mut; -use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr, Pointer}; +#[cfg(feature = "ring-sig-verify")] +use untrusted::Input; -use crate::fips::indicator_check; -use crate::signature::{Signature, VerificationAlgorithm}; -use crate::{digest, sealed, test}; #[cfg(feature = "fips")] use aws_lc::EC_KEY_check_fips; #[cfg(not(feature = "fips"))] use aws_lc::EC_KEY_check_key; +#[cfg(test)] +use aws_lc::EC_POINT_mul; use aws_lc::{ point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0, ECDSA_SIG_to_bytes, @@ -29,19 +33,14 @@ use aws_lc::{ NID_secp384r1, NID_secp521r1, BIGNUM, ECDSA_SIG, EC_GROUP, EC_POINT, EVP_PKEY, EVP_PKEY_EC, }; -#[cfg(test)] -use aws_lc::EC_POINT_mul; - -use std::fmt::{Debug, Formatter}; -use std::mem::MaybeUninit; -use std::ops::Deref; -use std::os::raw::{c_int, c_uint}; -#[cfg(test)] -use std::ptr::null; -use std::ptr::null_mut; - -#[cfg(feature = "ring-sig-verify")] -use untrusted::Input; +use crate::buffer::Buffer; +use crate::digest::digest_ctx::DigestContext; +use crate::error::{KeyRejected, Unspecified}; +use crate::fips::indicator_check; +use crate::fmt::AsDer; +use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr, Pointer}; +use crate::signature::{Signature, VerificationAlgorithm}; +use crate::{digest, sealed, test}; pub(crate) mod key_pair; @@ -128,13 +127,13 @@ pub struct EcPublicKeyX509DerType { } /// An elliptic curve public key as a DER-encoded (X509) `SubjectPublicKeyInfo` structure #[allow(clippy::module_name_repetitions)] -pub type EcPublicKeyX509Der<'a> = Buffer<'a, EcPublicKeyX509DerType>; +pub type EcPublicKeyX509Der = Buffer<'static, EcPublicKeyX509DerType>; -impl PublicKey { +impl AsDer for PublicKey { /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure. /// # Errors /// Returns an error if the underlying implementation is unable to marshal the point. - pub fn as_der(&self) -> Result, Unspecified> { + fn as_der(&self) -> Result { let ec_group = unsafe { LcPtr::new(EC_GROUP_new_by_curve_name(self.algorithm.id.nid()))? }; let ec_point = unsafe { ec_point_from_bytes(&ec_group, self.as_ref())? }; let ec_key = unsafe { LcPtr::new(EC_KEY_new())? }; @@ -588,7 +587,9 @@ unsafe fn ecdsa_sig_from_fixed( #[cfg(test)] mod tests { - use crate::ec::key_pair::EcdsaKeyPair; + use crate::fmt::AsDer; + use crate::signature::EcPublicKeyX509Der; + use crate::signature::EcdsaKeyPair; use crate::signature::{KeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; use crate::test::from_dirty_hex; use crate::{signature, test}; @@ -612,7 +613,7 @@ mod tests { format!("{:?}", key_pair.private_key()) ); let pub_key = key_pair.public_key(); - let der_pub_key = pub_key.as_der().unwrap(); + let der_pub_key: EcPublicKeyX509Der = pub_key.as_der().unwrap(); assert_eq!( from_dirty_hex( diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index fa8ffbb3843..fb6d76a12c1 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -3,6 +3,13 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +use std::fmt; +use std::fmt::{Debug, Formatter}; +use std::mem::MaybeUninit; +use std::ptr::{null, null_mut}; + +use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY, EVP_PKEY_EC}; + use crate::buffer::Buffer; use crate::digest::digest_ctx::DigestContext; use crate::ec::{ @@ -10,17 +17,12 @@ use crate::ec::{ }; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; +use crate::fmt::{AsBin, AsDer}; use crate::pkcs8::{Document, Version}; use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr}; use crate::rand::SecureRandom; use crate::signature::{EcdsaPublicKey, KeyPair, Signature}; use crate::{digest, ec}; -use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY, EVP_PKEY_EC}; -use std::fmt; -use std::mem::MaybeUninit; -use std::ptr::{null, null_mut}; - -use std::fmt::{Debug, Formatter}; /// An ECDSA key pair, used for signing. #[allow(clippy::module_name_repetitions)] @@ -168,7 +170,7 @@ impl EcdsaKeyPair { /// attempt to automatically detect other key formats. This function supports unencrypted /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats. /// - /// See `EcdsaPrivateKey::to_der`. + /// See `EcdsaPrivateKey::as_der`. /// /// # Errors /// `error::KeyRejected` if parsing failed or key otherwise unacceptable. @@ -307,22 +309,22 @@ pub struct EcPrivateKeyBinType { _priv: (), } /// Elliptic curve private key data encoded as a big-endian fixed-length integer. -pub type EcPrivateKeyBin<'a> = Buffer<'a, EcPrivateKeyBinType>; +pub type EcPrivateKeyBin = Buffer<'static, EcPrivateKeyBinType>; pub struct EcPrivateKeyRfc5915DerType { _priv: (), } /// Elliptic curve private key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. -pub type EcPrivateKeyRfc5915Der<'a> = Buffer<'a, EcPrivateKeyRfc5915DerType>; +pub type EcPrivateKeyRfc5915Der = Buffer<'static, EcPrivateKeyRfc5915DerType>; -impl PrivateKey<'_> { +impl AsBin for PrivateKey<'_> { /// Exposes the private key encoded as a big-endian fixed-length integer. /// /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_bin(&self) -> Result, Unspecified> { + fn as_bin(&self) -> Result { unsafe { let buffer = ec::marshal_private_key_to_buffer( self.0.algorithm.id, @@ -331,14 +333,16 @@ impl PrivateKey<'_> { Ok(EcPrivateKeyBin::new(buffer)) } } +} +impl AsDer for PrivateKey<'_> { /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_der(&self) -> Result, Unspecified> { + fn as_der(&self) -> Result { unsafe { - let mut outp = std::ptr::null_mut::(); + let mut outp = null_mut::(); let ec_key = ConstPointer::new(EVP_PKEY_get0_EC_KEY(*self.0.evp_pkey))?; let length = usize::try_from(aws_lc::i2d_ECPrivateKey(*ec_key, &mut outp)) .map_err(|_| Unspecified)?; diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index db5eff34821..7bbb0ec7c65 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -3,28 +3,31 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use crate::error::{KeyRejected, Unspecified}; -use crate::fips::indicator_check; -use crate::pkcs8::{Document, Version}; -use crate::ptr::LcPtr; -use crate::rand::SecureRandom; -use crate::signature::{KeyPair, Signature, VerificationAlgorithm}; -use crate::{constant_time, sealed, test}; -use aws_lc::{ - ED25519_keypair_from_seed, ED25519_sign, ED25519_verify, EVP_PKEY_CTX_new_id, - EVP_PKEY_get_raw_private_key, EVP_PKEY_get_raw_public_key, EVP_PKEY_keygen, - EVP_PKEY_keygen_init, EVP_PKEY_new_raw_private_key, EVP_PKEY, EVP_PKEY_ED25519, -}; use std::fmt; use std::fmt::{Debug, Formatter}; use std::mem::MaybeUninit; use std::ptr::null_mut; -use crate::buffer::Buffer; #[cfg(feature = "ring-sig-verify")] use untrusted::Input; use zeroize::Zeroize; +use aws_lc::{ + ED25519_keypair_from_seed, ED25519_sign, ED25519_verify, EVP_PKEY_CTX_new_id, + EVP_PKEY_get_raw_private_key, EVP_PKEY_get_raw_public_key, EVP_PKEY_keygen, + EVP_PKEY_keygen_init, EVP_PKEY_new_raw_private_key, EVP_PKEY, EVP_PKEY_ED25519, +}; + +use crate::buffer::Buffer; +use crate::error::{KeyRejected, Unspecified}; +use crate::fips::indicator_check; +use crate::fmt::AsBin; +use crate::pkcs8::{Document, Version}; +use crate::ptr::LcPtr; +use crate::rand::SecureRandom; +use crate::signature::{KeyPair, Signature, VerificationAlgorithm}; +use crate::{constant_time, sealed, test}; + /// The length of an Ed25519 public key. pub const ED25519_PUBLIC_KEY_LEN: usize = aws_lc::ED25519_PUBLIC_KEY_LEN as usize; pub(crate) const ED25519_PRIVATE_KEY_LEN: usize = aws_lc::ED25519_PRIVATE_KEY_LEN as usize; @@ -109,16 +112,16 @@ pub struct Ed25519SeedBufferType { } /// Elliptic curve private key data encoded as a big-endian fixed-length integer. #[allow(clippy::module_name_repetitions)] -pub type Ed25519SeedBin<'a> = Buffer<'a, Ed25519SeedBufferType>; +pub type Ed25519SeedBin = Buffer<'static, Ed25519SeedBufferType>; -impl Seed<'_> { +impl AsBin for Seed<'_> { /// Exposes the seed encoded as a big-endian fixed-length integer. /// /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. /// /// # Errors /// `error::Unspecified` if serialization failed. - pub fn to_buffer(&self) -> Result { + fn as_bin(&self) -> Result { let buffer = Vec::from(&self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN]); Ok(Ed25519SeedBin::new(buffer)) } diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index 5e8317fe508..bb7d4aa2a7c 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -201,6 +201,26 @@ mod sealed { // ``` pub trait Sealed {} } +/// Serialization formats +pub mod fmt { + /// Trait for structs that can be serialized into a DER format. + pub trait AsDer { + /// Serializes into a DER format. + /// + /// # Errors + /// Returns Unspecified if serialization fails. + fn as_der(&self) -> Result; + } + + /// Trait for structs that can be serialized into a binary format + pub trait AsBin { + /// Serializes into a binary format. + /// + /// # Errors + /// Returns Unspecified if serialization fails. + fn as_bin(&self) -> Result; + } +} #[cfg(test)] mod tests { diff --git a/aws-lc-rs/src/signature.rs b/aws-lc-rs/src/signature.rs index 0cfa3b08dc3..6e9950cfb5c 100644 --- a/aws-lc-rs/src/signature.rs +++ b/aws-lc-rs/src/signature.rs @@ -239,25 +239,27 @@ //! sign_and_verify_rsa(&private_key_path, &public_key_path).unwrap() //! } //! ``` -use crate::rsa; +use std::fmt::{Debug, Formatter}; + +#[cfg(feature = "ring-sig-verify")] +use untrusted::Input; + use rsa::{RSASigningAlgorithmId, RSAVerificationAlgorithmId, RsaSignatureEncoding}; pub use rsa::{ RsaEncoding, RsaKeyPair, RsaParameters, RsaPublicKeyComponents, RsaSubjectPublicKey, }; -use crate::{digest, ec, error, sealed, test}; -use std::fmt::{Debug, Formatter}; -#[cfg(feature = "ring-sig-verify")] -use untrusted::Input; - pub use crate::ec::key_pair::{EcdsaKeyPair, PrivateKey as EcdsaPrivateKey}; use crate::ec::EcdsaSignatureFormat; pub use crate::ec::{ + key_pair::EcPrivateKeyBin, key_pair::EcPrivateKeyRfc5915Der, EcPublicKeyX509Der, EcdsaSigningAlgorithm, EcdsaVerificationAlgorithm, PublicKey as EcdsaPublicKey, }; pub use crate::ed25519::{ - Ed25519KeyPair, EdDSAParameters, Seed as Ed25519Seed, ED25519_PUBLIC_KEY_LEN, + Ed25519KeyPair, Ed25519SeedBin, EdDSAParameters, Seed as Ed25519Seed, ED25519_PUBLIC_KEY_LEN, }; +use crate::rsa; +use crate::{digest, ec, error, sealed, test}; /// The longest signature is an ASN.1 P-384 signature where *r* and *s* are of /// maximum length with the leading high bit set on each. Then each component @@ -712,9 +714,10 @@ pub static ED25519: EdDSAParameters = EdDSAParameters {}; #[cfg(test)] mod tests { + use regex::Regex; + use crate::rand::{generate, SystemRandom}; use crate::signature::{UnparsedPublicKey, ED25519}; - use regex::Regex; #[cfg(feature = "fips")] mod fips; diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index a99eded1fc0..77875046099 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -3,7 +3,10 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +use aws_lc_rs::fmt::AsBin; +use aws_lc_rs::signature::EcPrivateKeyRfc5915Der; use aws_lc_rs::{ + fmt::AsDer, rand::SystemRandom, signature::{self, EcdsaKeyPair, KeyPair, Signature, UnparsedPublicKey}, test, test_file, @@ -461,7 +464,7 @@ fn test_private_key() { let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); { - let private_key = key_pair.private_key().to_bin().unwrap(); + let private_key = key_pair.private_key().as_bin().unwrap(); let public_key = key_pair.public_key(); let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( signing_alg, @@ -473,7 +476,7 @@ fn test_private_key() { assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); } { - let private_key_der = key_pair.private_key().to_der().unwrap(); + let private_key_der: EcPrivateKeyRfc5915Der = key_pair.private_key().as_der().unwrap(); assert_eq!("Buffer(...)", format!("{private_key_der:?}")); assert!(EcdsaKeyPair::from_pkcs8(signing_alg, private_key_der.as_ref()).is_err()); diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index 3cbe2644398..cd55401a28f 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -3,7 +3,9 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC +use aws_lc_rs::fmt::AsBin; use aws_lc_rs::rand::SystemRandom; +use aws_lc_rs::signature::Ed25519SeedBin; use aws_lc_rs::{ error, signature::{self, Ed25519KeyPair, KeyPair}, @@ -222,7 +224,7 @@ fn test_seed() { let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); let seed = key_pair.seed().unwrap(); - let seed_buffer = seed.to_buffer().unwrap(); + let seed_buffer: Ed25519SeedBin = seed.as_bin().unwrap(); let pub_key = key_pair.public_key(); From 2bf0c103bfbf1c504df834176843f63b17ef12aa Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 4 Dec 2023 15:53:17 -0500 Subject: [PATCH 22/24] Cleanup; AsBigEndian --- aws-lc-rs/src/ec.rs | 7 +++---- aws-lc-rs/src/ec/key_pair.rs | 6 +++--- aws-lc-rs/src/ed25519.rs | 6 +++--- aws-lc-rs/src/lib.rs | 10 +++++----- aws-lc-rs/tests/ecdsa_tests.rs | 6 +++--- aws-lc-rs/tests/ed25519_tests.rs | 4 ++-- 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 3456c550842..6ab77460f24 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -35,9 +35,9 @@ use aws_lc::{ use crate::buffer::Buffer; use crate::digest::digest_ctx::DigestContext; +use crate::encoding::AsDer; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; -use crate::fmt::AsDer; use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr, Pointer}; use crate::signature::{Signature, VerificationAlgorithm}; use crate::{digest, sealed, test}; @@ -315,11 +315,10 @@ pub(crate) unsafe fn marshal_private_key_to_buffer( debug_assert!(size <= private_size); } - let mut buffer = vec![0u8; SCALAR_MAX_BYTES]; + let mut buffer = vec![0u8; private_size]; if 1 != BN_bn2bin_padded(buffer.as_mut_ptr(), private_size, *private_bn) { return Err(Unspecified); } - buffer.truncate(private_size); Ok(buffer) } @@ -587,7 +586,7 @@ unsafe fn ecdsa_sig_from_fixed( #[cfg(test)] mod tests { - use crate::fmt::AsDer; + use crate::encoding::AsDer; use crate::signature::EcPublicKeyX509Der; use crate::signature::EcdsaKeyPair; use crate::signature::{KeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index fb6d76a12c1..a9cbfb6a166 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -15,9 +15,9 @@ use crate::digest::digest_ctx::DigestContext; use crate::ec::{ evp_key_generate, validate_evp_key, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey, }; +use crate::encoding::{AsBigEndian, AsDer}; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; -use crate::fmt::{AsBin, AsDer}; use crate::pkcs8::{Document, Version}; use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr}; use crate::rand::SecureRandom; @@ -317,14 +317,14 @@ pub struct EcPrivateKeyRfc5915DerType { /// Elliptic curve private key as a DER-encoded `ECPrivateKey` (RFC 5915) structure. pub type EcPrivateKeyRfc5915Der = Buffer<'static, EcPrivateKeyRfc5915DerType>; -impl AsBin for PrivateKey<'_> { +impl AsBigEndian for PrivateKey<'_> { /// Exposes the private key encoded as a big-endian fixed-length integer. /// /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. /// /// # Errors /// `error::Unspecified` if serialization failed. - fn as_bin(&self) -> Result { + fn as_be_bytes(&self) -> Result { unsafe { let buffer = ec::marshal_private_key_to_buffer( self.0.algorithm.id, diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 7bbb0ec7c65..d4bb089d45a 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -19,9 +19,9 @@ use aws_lc::{ }; use crate::buffer::Buffer; +use crate::encoding::AsBigEndian; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; -use crate::fmt::AsBin; use crate::pkcs8::{Document, Version}; use crate::ptr::LcPtr; use crate::rand::SecureRandom; @@ -114,14 +114,14 @@ pub struct Ed25519SeedBufferType { #[allow(clippy::module_name_repetitions)] pub type Ed25519SeedBin = Buffer<'static, Ed25519SeedBufferType>; -impl AsBin for Seed<'_> { +impl AsBigEndian for Seed<'_> { /// Exposes the seed encoded as a big-endian fixed-length integer. /// /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred. /// /// # Errors /// `error::Unspecified` if serialization failed. - fn as_bin(&self) -> Result { + fn as_be_bytes(&self) -> Result { let buffer = Vec::from(&self.0.private_key[..ED25519_PRIVATE_KEY_SEED_LEN]); Ok(Ed25519SeedBin::new(buffer)) } diff --git a/aws-lc-rs/src/lib.rs b/aws-lc-rs/src/lib.rs index bb7d4aa2a7c..c3b55963187 100644 --- a/aws-lc-rs/src/lib.rs +++ b/aws-lc-rs/src/lib.rs @@ -202,7 +202,7 @@ mod sealed { pub trait Sealed {} } /// Serialization formats -pub mod fmt { +pub mod encoding { /// Trait for structs that can be serialized into a DER format. pub trait AsDer { /// Serializes into a DER format. @@ -212,13 +212,13 @@ pub mod fmt { fn as_der(&self) -> Result; } - /// Trait for structs that can be serialized into a binary format - pub trait AsBin { - /// Serializes into a binary format. + /// Trait for values that can be serialized into a big-endian format + pub trait AsBigEndian { + /// Serializes into a big-endian format. /// /// # Errors /// Returns Unspecified if serialization fails. - fn as_bin(&self) -> Result; + fn as_be_bytes(&self) -> Result; } } diff --git a/aws-lc-rs/tests/ecdsa_tests.rs b/aws-lc-rs/tests/ecdsa_tests.rs index 77875046099..48939d39f63 100644 --- a/aws-lc-rs/tests/ecdsa_tests.rs +++ b/aws-lc-rs/tests/ecdsa_tests.rs @@ -3,10 +3,10 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use aws_lc_rs::fmt::AsBin; +use aws_lc_rs::encoding::AsBigEndian; use aws_lc_rs::signature::EcPrivateKeyRfc5915Der; use aws_lc_rs::{ - fmt::AsDer, + encoding::AsDer, rand::SystemRandom, signature::{self, EcdsaKeyPair, KeyPair, Signature, UnparsedPublicKey}, test, test_file, @@ -464,7 +464,7 @@ fn test_private_key() { let key_pair = EcdsaKeyPair::from_pkcs8(signing_alg, key_pair_doc.as_ref()).unwrap(); { - let private_key = key_pair.private_key().as_bin().unwrap(); + let private_key = key_pair.private_key().as_be_bytes().unwrap(); let public_key = key_pair.public_key(); let key_pair_copy = EcdsaKeyPair::from_private_key_and_public_key( signing_alg, diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index cd55401a28f..50c1c820851 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -3,7 +3,7 @@ // Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 OR ISC -use aws_lc_rs::fmt::AsBin; +use aws_lc_rs::encoding::AsBigEndian; use aws_lc_rs::rand::SystemRandom; use aws_lc_rs::signature::Ed25519SeedBin; use aws_lc_rs::{ @@ -224,7 +224,7 @@ fn test_seed() { let key_pair = Ed25519KeyPair::from_pkcs8(key_pair_doc.as_ref()).unwrap(); let seed = key_pair.seed().unwrap(); - let seed_buffer: Ed25519SeedBin = seed.as_bin().unwrap(); + let seed_buffer: Ed25519SeedBin = seed.as_be_bytes().unwrap(); let pub_key = key_pair.public_key(); From 16ca95873ef8998a675a64d7afcce0df288201b9 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:57:35 -0500 Subject: [PATCH 23/24] Make clippy proud --- aws-lc-rs/src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-lc-rs/src/test.rs b/aws-lc-rs/src/test.rs index e724abd1050..1e4708d891c 100644 --- a/aws-lc-rs/src/test.rs +++ b/aws-lc-rs/src/test.rs @@ -422,7 +422,7 @@ fn parse_test_case( } // A blank line ends a test case if the test case isn't empty. - Some(line) if line.is_empty() => { + Some("") => { if !is_first_line { return Some(TestCase { attributes }); } From 712f0f21a431f41e4bb78558c6c4c4eab30035ca Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:49:02 -0500 Subject: [PATCH 24/24] Remove redundant copying --- aws-lc-rs/src/ec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index 6ab77460f24..adb1996c1b6 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -149,9 +149,9 @@ impl AsDer for PublicKey { return Err(Unspecified); } let buffer = LcPtr::new(buffer)?; - let mut der = unsafe { std::slice::from_raw_parts(*buffer, len.try_into()?) }.to_owned(); + let der = unsafe { std::slice::from_raw_parts(*buffer, len.try_into()?) }.to_owned(); - Ok(Buffer::take_from_slice(&mut der)) + Ok(Buffer::new(der)) } }