From 44fa00dcdd385cef8e74edbeeb0890289dfd59a3 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Tue, 21 Jan 2025 19:28:02 +0100 Subject: [PATCH] Implement `from_seed_unchecked` for `Ed25519KeyPair` (#663) * Implement `from_seed_unchecked` for `Ed25519KeyPair` * Minor doc/test changes --------- Co-authored-by: Justin Smith --- aws-lc-rs/src/ed25519.rs | 23 ++++++++++++++++++++--- aws-lc-rs/tests/ed25519_tests.rs | 15 +++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/aws-lc-rs/src/ed25519.rs b/aws-lc-rs/src/ed25519.rs index 0460a22cb69..ae573eec982 100644 --- a/aws-lc-rs/src/ed25519.rs +++ b/aws-lc-rs/src/ed25519.rs @@ -351,6 +351,26 @@ impl Ed25519KeyPair { /// # Errors /// `error::KeyRejected` if parse error, or if key is otherwise unacceptable. pub fn from_seed_and_public_key(seed: &[u8], public_key: &[u8]) -> Result { + let this = Self::from_seed_unchecked(seed)?; + + constant_time::verify_slices_are_equal(public_key, &this.public_key.public_key_bytes) + .map_err(|_| KeyRejected::inconsistent_components())?; + Ok(this) + } + + /// Constructs an Ed25519 key pair from the private key seed `seed`. + /// + /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. If the public key is + /// available, prefer to use `Ed25519KeyPair::from_seed_and_public_key()` as it will verify + /// the validity of the key pair. + /// + /// CAUTION: Both an Ed25519 seed and its public key are 32-bytes. If the bytes of a public key + /// are provided this function will create an (effectively) invalid `Ed25519KeyPair`. This + /// problem is undetectable by the API. + /// + /// # Errors + /// `error::KeyRejected` if parse error, or if key is otherwise unacceptable. + pub fn from_seed_unchecked(seed: &[u8]) -> Result { if seed.len() < ED25519_SEED_LEN { return Err(KeyRejected::inconsistent_components()); } @@ -372,9 +392,6 @@ impl Ed25519KeyPair { } debug_assert_eq!(derived_public_key.len(), out_len); - constant_time::verify_slices_are_equal(public_key, &derived_public_key) - .map_err(|_| KeyRejected::inconsistent_components())?; - Ok(Self { public_key: PublicKey { public_key_bytes: derived_public_key, diff --git a/aws-lc-rs/tests/ed25519_tests.rs b/aws-lc-rs/tests/ed25519_tests.rs index 0dcda20ac42..444607af22e 100644 --- a/aws-lc-rs/tests/ed25519_tests.rs +++ b/aws-lc-rs/tests/ed25519_tests.rs @@ -34,6 +34,10 @@ fn test_signature_ed25519() { let expected_sig = test_case.consume_bytes("SIG"); + let key_pair = Ed25519KeyPair::from_seed_unchecked(&seed).unwrap(); + let actual_sig = key_pair.sign(&msg); + assert_eq!(&expected_sig[..], actual_sig.as_ref()); + let key_pair = Ed25519KeyPair::from_seed_and_public_key(&seed, &public_key).unwrap(); let actual_sig = key_pair.sign(&msg); assert_eq!(&expected_sig[..], actual_sig.as_ref()); @@ -99,6 +103,12 @@ fn test_ed25519_from_seed_and_public_key_misuse() { // Swapped public and private key. assert!(Ed25519KeyPair::from_seed_and_public_key(PUBLIC_KEY, PRIVATE_KEY).is_err()); + + // From a private seed + assert!(Ed25519KeyPair::from_seed_unchecked(PRIVATE_KEY).is_ok()); + + // From a truncated private seed + assert!(Ed25519KeyPair::from_seed_unchecked(PRIVATE_KEY).is_ok()); } #[test] @@ -232,4 +242,9 @@ fn test_seed() { let key_pair_copy_doc = key_pair_copy.to_pkcs8().unwrap(); assert_eq!(key_pair_doc.as_ref(), key_pair_copy_doc.as_ref()); + + let key_pair_seed_copy = Ed25519KeyPair::from_seed_unchecked(seed_buffer.as_ref()).unwrap(); + let key_pair_seed_copy_doc = key_pair_seed_copy.to_pkcs8().unwrap(); + + assert_eq!(key_pair_doc.as_ref(), key_pair_seed_copy_doc.as_ref()); }