diff --git a/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md b/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md index 3e236997df3..f5e3a920a31 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md +++ b/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md @@ -6,11 +6,20 @@ Breaking changes: - Curve25519Key::generate() now takes ownership of the RNG instead of borrowing it; the key holds the RNG for its lifetime +- Ed25519 and Ed448 no longer implement the signature crate's Keypair trait; + use the new SigningKey types instead New features: - Add Curve25519Key::generate_shared_rng() to generate a key from an RNG shared between keys via Rc (requires the alloc feature) +- Add ed25519::SigningKey and ed448::SigningKey, which always carry a public + key and so implement Keypair::verifying_key() without it being able to fail + +Fixes and improvements: + +- Fix a panic in Keypair::verifying_key() for Ed25519 and Ed448 keys with no + public key, such as after new() or import_private_only() ## v2.2.0 diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/ed25519.rs b/wrapper/rust/wolfssl-wolfcrypt/src/ed25519.rs index 33fa196d1de..4a934e79c8d 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/ed25519.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/ed25519.rs @@ -1504,13 +1504,17 @@ impl Drop for Ed25519 { /// RustCrypto `signature` crate trait implementations. /// -/// Provides a fixed-size [`Signature`] and a [`VerifyingKey`] type so that -/// [`Ed25519`] can be used wherever the `signature` crate's -/// [`signature::SignerMut`], [`signature::Keypair`], and +/// Provides a fixed-size [`Signature`], a [`VerifyingKey`] and a +/// [`SigningKey`] type so that Ed25519 keys can be used wherever the +/// `signature` crate's [`signature::SignerMut`], [`signature::Keypair`], and /// [`signature::Verifier`] traits are accepted. #[cfg(feature = "signature")] mod signature_impl { use super::Ed25519; + #[cfg(all(ed25519_sign, ed25519_export))] + use zeroize::Zeroize; + #[cfg(all(ed25519_make_key, ed25519_sign, ed25519_export, random))] + use crate::random::RNG; use signature::Error; /// Ed25519 signature in its standard 64-byte encoded form. @@ -1587,13 +1591,191 @@ mod signature_impl { } } + /// Ed25519 signing (private) key that is guaranteed to carry a public key. + /// + /// An [`Ed25519`] on its own may hold no public key: `Ed25519::new()` + /// leaves the key empty and `Ed25519::import_private_only()` loads only + /// the private scalar. [`signature::Keypair::verifying_key()`] cannot + /// fail, so it is implemented for this type rather than for [`Ed25519`]. + /// Every constructor here derives or validates the public key and caches + /// it, which makes handing out a [`VerifyingKey`] infallible. + #[cfg(all(ed25519_sign, ed25519_export))] + pub struct SigningKey { + inner: Ed25519, + public: [u8; Ed25519::PUB_KEY_SIZE], + } + #[cfg(all(ed25519_sign, ed25519_export))] - impl signature::Keypair for Ed25519 { + impl SigningKey { + /// Generate a new Ed25519 signing key. + /// + /// # Parameters + /// + /// * `rng`: Random number generator to use. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed25519_make_key, ed25519_sign, ed25519_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed25519::SigningKey; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let sk = SigningKey::generate(&rng).expect("Error with generate()"); + /// } + /// ``` + #[cfg(all(ed25519_make_key, random))] + pub fn generate(rng: &RNG) -> Result { + Self::from_key(Ed25519::generate(rng)?) + } + + /// Create a signing key from a private key, deriving its public key. + /// + /// # Parameters + /// + /// * `private`: Input buffer containing the private key. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed25519_make_key, ed25519_import, ed25519_export, ed25519_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed25519::{Ed25519, SigningKey}; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let ed = Ed25519::generate(&rng).expect("Error with generate()"); + /// let mut private = [0u8; Ed25519::KEY_SIZE]; + /// ed.export_private_only(&mut private).expect("Error with export_private_only()"); + /// let sk = SigningKey::from_private_only(&private).expect("Error with from_private_only()"); + /// } + /// ``` + #[cfg(all(ed25519_import, ed25519_make_key))] + pub fn from_private_only(private: &[u8; Ed25519::KEY_SIZE]) -> Result { + let mut key = Ed25519::new()?; + key.import_private_only(private)?; + let mut public = [0u8; Ed25519::PUB_KEY_SIZE]; + key.make_public(&mut public)?; + Ok(Self { inner: key, public }) + } + + /// Create a signing key from a private key and its public key. + /// + /// The public key is untrusted and is checked against the private key. + /// + /// # Parameters + /// + /// * `private`: Input buffer containing the private key. + /// * `public`: Input buffer containing the public key. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed25519_make_key, ed25519_import, ed25519_export, ed25519_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed25519::{Ed25519, SigningKey}; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let ed = Ed25519::generate(&rng).expect("Error with generate()"); + /// let mut private = [0u8; Ed25519::KEY_SIZE]; + /// let mut public = [0u8; Ed25519::PUB_KEY_SIZE]; + /// ed.export_private_only(&mut private).expect("Error with export_private_only()"); + /// ed.export_public(&mut public).expect("Error with export_public()"); + /// let sk = SigningKey::from_keypair(&private, &public).expect("Error with from_keypair()"); + /// } + /// ``` + #[cfg(ed25519_import)] + pub fn from_keypair(private: &[u8; Ed25519::KEY_SIZE], + public: &[u8; Ed25519::PUB_KEY_SIZE]) -> Result + { + let mut key = Ed25519::new()?; + key.import_private_key(private, Some(public))?; + Ok(Self { inner: key, public: *public }) + } + + /// Create a signing key from an existing [`Ed25519`] key. + /// + /// Both key components must be present. Fails with the wolfSSL error + /// code `PUBLIC_KEY_E` when `key` holds no public key, for instance + /// after `Ed25519::new()` or `Ed25519::import_private_only()`, and with + /// `BAD_FUNC_ARG` when it holds no private key, for instance after + /// `Ed25519::import_public()`. + /// + /// # Parameters + /// + /// * `key`: The Ed25519 key to wrap. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed25519_make_key, ed25519_export, ed25519_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed25519::{Ed25519, SigningKey}; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let ed = Ed25519::generate(&rng).expect("Error with generate()"); + /// let sk = SigningKey::from_key(ed).expect("Error with from_key()"); + /// } + /// ``` + pub fn from_key(key: Ed25519) -> Result { + let mut public = [0u8; Ed25519::PUB_KEY_SIZE]; + key.export_public(&mut public)?; + /* A key carrying only a public component would build a SigningKey + * that cannot sign, so require the private component too. + * Exporting it is the only way to ask wolfCrypt whether it is + * there; the copy is wiped again right away. */ + let mut private = [0u8; Ed25519::KEY_SIZE]; + let ret = key.export_private_only(&mut private); + private.zeroize(); + ret?; + Ok(Self { inner: key, public }) + } + + /// Borrow the wrapped [`Ed25519`] key for operations that are not + /// covered by the signature traits. + pub fn as_key(&self) -> &Ed25519 { + &self.inner + } + + /// Consume the signing key and return the wrapped [`Ed25519`] key. + pub fn into_key(self) -> Ed25519 { + self.inner + } + } + + #[cfg(all(ed25519_sign, ed25519_export))] + impl signature::Keypair for SigningKey { type VerifyingKey = VerifyingKey; fn verifying_key(&self) -> Self::VerifyingKey { - let mut pub_key = [0u8; Ed25519::PUB_KEY_SIZE]; - self.export_public(&mut pub_key).expect("ed25519 export_public failed"); - VerifyingKey(pub_key) + VerifyingKey(self.public) + } + } + + #[cfg(all(ed25519_sign, ed25519_export))] + impl signature::SignerMut for SigningKey { + fn try_sign(&mut self, msg: &[u8]) -> Result { + let mut sig = [0u8; Ed25519::SIG_SIZE]; + self.inner.sign_msg(msg, &mut sig).map_err(|_| Error::new())?; + Ok(Signature(sig)) } } @@ -1621,3 +1803,5 @@ mod signature_impl { #[cfg(feature = "signature")] pub use signature_impl::{Signature, VerifyingKey}; +#[cfg(all(feature = "signature", ed25519_sign, ed25519_export))] +pub use signature_impl::SigningKey; diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/ed448.rs b/wrapper/rust/wolfssl-wolfcrypt/src/ed448.rs index 4018f929b3f..62bb0fcde0e 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/ed448.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/ed448.rs @@ -1423,9 +1423,9 @@ impl Drop for Ed448 { /// RustCrypto `signature` crate trait implementations. /// -/// Provides a fixed-size [`Signature`] and a [`VerifyingKey`] type so that -/// [`Ed448`] can be used wherever the `signature` crate's -/// [`signature::SignerMut`], [`signature::Keypair`], and +/// Provides a fixed-size [`Signature`], a [`VerifyingKey`] and a +/// [`SigningKey`] type so that Ed448 keys can be used wherever the +/// `signature` crate's [`signature::SignerMut`], [`signature::Keypair`], and /// [`signature::Verifier`] traits are accepted. /// /// These impls use the plain Ed448 (pure) signature variant with no context; @@ -1434,6 +1434,10 @@ impl Drop for Ed448 { #[cfg(feature = "signature")] mod signature_impl { use super::Ed448; + #[cfg(all(ed448_sign, ed448_export))] + use zeroize::Zeroize; + #[cfg(all(ed448_sign, ed448_export, random))] + use crate::random::RNG; use signature::Error; /// Ed448 signature in its standard 114-byte encoded form. @@ -1510,13 +1514,191 @@ mod signature_impl { } } + /// Ed448 signing (private) key that is guaranteed to carry a public key. + /// + /// An [`Ed448`] on its own may hold no public key: `Ed448::new()` leaves + /// the key empty and `Ed448::import_private_only()` loads only the + /// private scalar. [`signature::Keypair::verifying_key()`] cannot fail, + /// so it is implemented for this type rather than for [`Ed448`]. Every + /// constructor here derives or validates the public key and caches it, + /// which makes handing out a [`VerifyingKey`] infallible. + #[cfg(all(ed448_sign, ed448_export))] + pub struct SigningKey { + inner: Ed448, + public: [u8; Ed448::PUB_KEY_SIZE], + } + #[cfg(all(ed448_sign, ed448_export))] - impl signature::Keypair for Ed448 { + impl SigningKey { + /// Generate a new Ed448 signing key. + /// + /// # Parameters + /// + /// * `rng`: Random number generator to use. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed448_sign, ed448_export, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed448::SigningKey; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let sk = SigningKey::generate(&rng).expect("Error with generate()"); + /// } + /// ``` + #[cfg(random)] + pub fn generate(rng: &RNG) -> Result { + Self::from_key(Ed448::generate(rng)?) + } + + /// Create a signing key from a private key, deriving its public key. + /// + /// # Parameters + /// + /// * `private`: Input buffer containing the private key. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed448_import, ed448_export, ed448_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed448::{Ed448, SigningKey}; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let ed = Ed448::generate(&rng).expect("Error with generate()"); + /// let mut private = [0u8; Ed448::KEY_SIZE]; + /// ed.export_private_only(&mut private).expect("Error with export_private_only()"); + /// let sk = SigningKey::from_private_only(&private).expect("Error with from_private_only()"); + /// } + /// ``` + #[cfg(ed448_import)] + pub fn from_private_only(private: &[u8; Ed448::KEY_SIZE]) -> Result { + let mut key = Ed448::new()?; + key.import_private_only(private)?; + let mut public = [0u8; Ed448::PUB_KEY_SIZE]; + key.make_public(&mut public)?; + Ok(Self { inner: key, public }) + } + + /// Create a signing key from a private key and its public key. + /// + /// The public key is untrusted and is checked against the private key. + /// + /// # Parameters + /// + /// * `private`: Input buffer containing the private key. + /// * `public`: Input buffer containing the public key. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed448_import, ed448_export, ed448_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed448::{Ed448, SigningKey}; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let ed = Ed448::generate(&rng).expect("Error with generate()"); + /// let mut private = [0u8; Ed448::KEY_SIZE]; + /// let mut public = [0u8; Ed448::PUB_KEY_SIZE]; + /// ed.export_private_only(&mut private).expect("Error with export_private_only()"); + /// ed.export_public(&mut public).expect("Error with export_public()"); + /// let sk = SigningKey::from_keypair(&private, &public).expect("Error with from_keypair()"); + /// } + /// ``` + #[cfg(ed448_import)] + pub fn from_keypair(private: &[u8; Ed448::KEY_SIZE], + public: &[u8; Ed448::PUB_KEY_SIZE]) -> Result + { + let mut key = Ed448::new()?; + key.import_private_key(private, Some(public))?; + Ok(Self { inner: key, public: *public }) + } + + /// Create a signing key from an existing [`Ed448`] key. + /// + /// Both key components must be present. Fails with the wolfSSL error + /// code `PUBLIC_KEY_E` when `key` holds no public key, for instance + /// after `Ed448::new()` or `Ed448::import_private_only()`, and with + /// `BAD_FUNC_ARG` when it holds no private key, for instance after + /// `Ed448::import_public()`. + /// + /// # Parameters + /// + /// * `key`: The Ed448 key to wrap. + /// + /// # Returns + /// + /// Returns either Ok(signing_key) containing the SigningKey struct + /// instance or Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// #[cfg(all(feature = "signature", ed448_export, ed448_sign, random))] + /// { + /// use wolfssl_wolfcrypt::random::RNG; + /// use wolfssl_wolfcrypt::ed448::{Ed448, SigningKey}; + /// let rng = RNG::new().expect("Error creating RNG"); + /// let ed = Ed448::generate(&rng).expect("Error with generate()"); + /// let sk = SigningKey::from_key(ed).expect("Error with from_key()"); + /// } + /// ``` + pub fn from_key(key: Ed448) -> Result { + let mut public = [0u8; Ed448::PUB_KEY_SIZE]; + key.export_public(&mut public)?; + /* A key carrying only a public component would build a SigningKey + * that cannot sign, so require the private component too. + * Exporting it is the only way to ask wolfCrypt whether it is + * there; the copy is wiped again right away. */ + let mut private = [0u8; Ed448::KEY_SIZE]; + let ret = key.export_private_only(&mut private); + private.zeroize(); + ret?; + Ok(Self { inner: key, public }) + } + + /// Borrow the wrapped [`Ed448`] key for operations that are not + /// covered by the signature traits. + pub fn as_key(&self) -> &Ed448 { + &self.inner + } + + /// Consume the signing key and return the wrapped [`Ed448`] key. + pub fn into_key(self) -> Ed448 { + self.inner + } + } + + #[cfg(all(ed448_sign, ed448_export))] + impl signature::Keypair for SigningKey { type VerifyingKey = VerifyingKey; fn verifying_key(&self) -> Self::VerifyingKey { - let mut pub_key = [0u8; Ed448::PUB_KEY_SIZE]; - self.export_public(&mut pub_key).expect("ed448 export_public failed"); - VerifyingKey(pub_key) + VerifyingKey(self.public) + } + } + + #[cfg(all(ed448_sign, ed448_export))] + impl signature::SignerMut for SigningKey { + fn try_sign(&mut self, msg: &[u8]) -> Result { + let mut sig = [0u8; Ed448::SIG_SIZE]; + self.inner.sign_msg(msg, None, &mut sig).map_err(|_| Error::new())?; + Ok(Signature(sig)) } } @@ -1544,3 +1726,5 @@ mod signature_impl { #[cfg(feature = "signature")] pub use signature_impl::{Signature, VerifyingKey}; +#[cfg(all(feature = "signature", ed448_sign, ed448_export))] +pub use signature_impl::SigningKey; diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed25519.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed25519.rs index 8eb9b782547..91d026ab5b8 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed25519.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed25519.rs @@ -367,11 +367,11 @@ fn test_signature_traits() { common::setup(); - let mut rng = RNG::new().expect("Error creating RNG"); - let mut ed = Ed25519::generate(&mut rng).expect("Error with generate()"); + let rng = RNG::new().expect("Error creating RNG"); + let mut sk = SigningKey::generate(&rng).expect("Error with SigningKey::generate()"); let message = b"message to sign via RustCrypto signature trait"; - let sig: Signature = ed.sign(message); + let sig: Signature = sk.sign(message); // Round-trip the signature bytes through the SignatureEncoding machinery. let bytes = sig.to_bytes(); @@ -383,7 +383,7 @@ fn test_signature_traits() { assert!(Signature::try_from(&bytes[..bytes.len() - 1]).is_err()); // VerifyingKey obtained via the Keypair trait verifies this signature. - let vk: VerifyingKey = ed.verifying_key(); + let vk: VerifyingKey = sk.verifying_key(); vk.verify(message, &sig).expect("Verifier::verify failed"); // A tampered message must fail verification. @@ -397,6 +397,55 @@ fn test_signature_traits() { assert_eq!(vk, vk2); } +#[test] +#[cfg(all(feature = "signature", ed25519_make_key, ed25519_import, ed25519_export, ed25519_sign, ed25519_verify, random))] +fn test_signing_key_from_incomplete_key() { + use signature::{Keypair, SignerMut, Verifier}; + + common::setup(); + + let rng = RNG::new().expect("Error creating RNG"); + let ed = Ed25519::generate(&rng).expect("Error with generate()"); + let mut private = [0u8; Ed25519::KEY_SIZE]; + let mut public = [0u8; Ed25519::PUB_KEY_SIZE]; + ed.export_private_only(&mut private).expect("Error with export_private_only()"); + ed.export_public(&mut public).expect("Error with export_public()"); + + // A key with no public key at all cannot become a SigningKey. + let empty = Ed25519::new().expect("Error with new()"); + assert!(SigningKey::from_key(empty).is_err()); + + // Nor can one holding only the private scalar. + let mut private_only = Ed25519::new().expect("Error with new()"); + private_only.import_private_only(&private).expect("Error with import_private_only()"); + assert!(SigningKey::from_key(private_only).is_err()); + + // Nor can a public-only key, which could never sign. + let mut public_only = Ed25519::new().expect("Error with new()"); + public_only.import_public(&public).expect("Error with import_public()"); + assert!(SigningKey::from_key(public_only).is_err()); + + // from_private_only() derives the public key instead of failing. + let mut sk = SigningKey::from_private_only(&private) + .expect("Error with SigningKey::from_private_only()"); + assert_eq!(sk.verifying_key().to_bytes(), public); + + // As does importing the pair, and both agree on signatures. + let mut sk2 = SigningKey::from_keypair(&private, &public) + .expect("Error with SigningKey::from_keypair()"); + assert_eq!(sk2.verifying_key(), sk.verifying_key()); + + let message = b"message signed by a derived signing key"; + let sig: Signature = sk.sign(message); + assert_eq!(sig, sk2.sign(message)); + sk.verifying_key().verify(message, &sig).expect("Verifier::verify failed"); + + // A public key that does not match the private key is rejected. + let mut wrong_public = public; + wrong_public[0] ^= 0x01; + assert!(SigningKey::from_keypair(&private, &wrong_public).is_err()); +} + #[test] #[cfg(all(ed25519_make_key, random))] fn test_sizes() { diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed448.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed448.rs index f07dde1da45..5339191b2e7 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed448.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_ed448.rs @@ -353,11 +353,11 @@ fn test_signature_traits() { common::setup(); - let mut rng = RNG::new().expect("Error creating RNG"); - let mut ed = Ed448::generate(&mut rng).expect("Error with generate()"); + let rng = RNG::new().expect("Error creating RNG"); + let mut sk = SigningKey::generate(&rng).expect("Error with SigningKey::generate()"); let message = b"message to sign via RustCrypto signature trait"; - let sig: Signature = ed.sign(message); + let sig: Signature = sk.sign(message); // Round-trip the signature bytes through the SignatureEncoding machinery. let bytes = sig.to_bytes(); @@ -369,7 +369,7 @@ fn test_signature_traits() { assert!(Signature::try_from(&bytes[..bytes.len() - 1]).is_err()); // VerifyingKey obtained via the Keypair trait verifies this signature. - let vk: VerifyingKey = ed.verifying_key(); + let vk: VerifyingKey = sk.verifying_key(); vk.verify(message, &sig).expect("Verifier::verify failed"); // A tampered message must fail verification. @@ -383,6 +383,55 @@ fn test_signature_traits() { assert_eq!(vk, vk2); } +#[test] +#[cfg(all(feature = "signature", ed448_import, ed448_export, ed448_sign, ed448_verify, random))] +fn test_signing_key_from_incomplete_key() { + use signature::{Keypair, SignerMut, Verifier}; + + common::setup(); + + let rng = RNG::new().expect("Error creating RNG"); + let ed = Ed448::generate(&rng).expect("Error with generate()"); + let mut private = [0u8; Ed448::KEY_SIZE]; + let mut public = [0u8; Ed448::PUB_KEY_SIZE]; + ed.export_private_only(&mut private).expect("Error with export_private_only()"); + ed.export_public(&mut public).expect("Error with export_public()"); + + // A key with no public key at all cannot become a SigningKey. + let empty = Ed448::new().expect("Error with new()"); + assert!(SigningKey::from_key(empty).is_err()); + + // Nor can one holding only the private scalar. + let mut private_only = Ed448::new().expect("Error with new()"); + private_only.import_private_only(&private).expect("Error with import_private_only()"); + assert!(SigningKey::from_key(private_only).is_err()); + + // Nor can a public-only key, which could never sign. + let mut public_only = Ed448::new().expect("Error with new()"); + public_only.import_public(&public).expect("Error with import_public()"); + assert!(SigningKey::from_key(public_only).is_err()); + + // from_private_only() derives the public key instead of failing. + let mut sk = SigningKey::from_private_only(&private) + .expect("Error with SigningKey::from_private_only()"); + assert_eq!(sk.verifying_key().to_bytes(), public); + + // As does importing the pair, and both agree on signatures. + let mut sk2 = SigningKey::from_keypair(&private, &public) + .expect("Error with SigningKey::from_keypair()"); + assert_eq!(sk2.verifying_key(), sk.verifying_key()); + + let message = b"message signed by a derived signing key"; + let sig: Signature = sk.sign(message); + assert_eq!(sig, sk2.sign(message)); + sk.verifying_key().verify(message, &sig).expect("Verifier::verify failed"); + + // A public key that does not match the private key is rejected. + let mut wrong_public = public; + wrong_public[0] ^= 0x01; + assert!(SigningKey::from_keypair(&private, &wrong_public).is_err()); +} + #[test] #[cfg(random)] fn test_sizes() {