Repository: aws/aws-lc-rs (language: rust)

Issue:
Construct `Ed25519KeyPair` from just a raw private key seed
### Problem:

`Ed25519KeyPair` cannot be constructed from just a raw private key seed. The only option is to encode the key to PKCS#8 and call `from_pkcs8`. There's also `from_seed_and_public_key`, but it requires the caller to already know the public key ahead of time, which in our use case is not.

### Solution:

Implement `from_seed_unchecked`, like `ring` does.

### Requirements / Acceptance Criteria:

What must a solution address in order to solve the problem? How do we know the solution is complete?

* **RFC links:** Links to relevant RFC(s)
* **Related Issues:** Link any relevant issues
* **Will the Usage Guide or other documentation need to be updated?**
* **Testing:** How will this change be tested? Call out new integration tests, functional tests, or particularly
  interesting/important unit tests.


### Out of scope:

Is there anything the solution will intentionally NOT address?



Discussion / hints:

Fix patch:
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<Self, KeyRejected> {
+        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<Self, KeyRejected> {
         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,


Test patch:
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());
 }