diff options
author | Adam Langley <agl@chromium.org> | 2024-01-07 18:05:24 -0800 |
---|---|---|
committer | Adam Langley <agl@google.com> | 2024-01-19 18:24:55 +0000 |
commit | 9333b29d08451a85d69cbf86d26950c8a96a532c (patch) | |
tree | 3891dc78ed099b2d4c52b4622d6b248038f9ef19 /rust | |
parent | 169128d47a7d558477bcb4ffbc565450cd449914 (diff) | |
download | boringssl-9333b29d08451a85d69cbf86d26950c8a96a532c.zip boringssl-9333b29d08451a85d69cbf86d26950c8a96a532c.tar.gz boringssl-9333b29d08451a85d69cbf86d26950c8a96a532c.tar.bz2 |
Reworking bssl_crypto: Ed25519
Change-Id: Ie59326b9c987319dfbe4909955e2370384a7532f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65177
Reviewed-by: Bob Beck <bbe@google.com>
Diffstat (limited to 'rust')
-rw-r--r-- | rust/bssl-crypto/src/ecdsa.rs | 10 | ||||
-rw-r--r-- | rust/bssl-crypto/src/ed25519.rs | 168 | ||||
-rw-r--r-- | rust/bssl-crypto/src/lib.rs | 1 |
3 files changed, 90 insertions, 89 deletions
diff --git a/rust/bssl-crypto/src/ecdsa.rs b/rust/bssl-crypto/src/ecdsa.rs index ae65200..c0b5fee 100644 --- a/rust/bssl-crypto/src/ecdsa.rs +++ b/rust/bssl-crypto/src/ecdsa.rs @@ -34,7 +34,7 @@ //! assert!(public_key.verify(signed_message, sig.as_slice()).is_ok()); //! ``` -use crate::{ec, sealed, with_output_vec, Buffer, FfiSlice}; +use crate::{ec, sealed, with_output_vec, Buffer, FfiSlice, InvalidSignatureError}; use alloc::vec::Vec; use core::marker::PhantomData; @@ -44,10 +44,6 @@ pub struct PublicKey<C: ec::Curve> { marker: PhantomData<C>, } -/// InvalidSignature is a [`Result`] error type when a signature fails to verify. -#[derive(Debug)] -pub struct InvalidSignature; - impl<C: ec::Curve> PublicKey<C> { /// Parse a public key in uncompressed X9.62 format. (This is the common /// format for elliptic curve points beginning with an 0x04 byte.) @@ -83,7 +79,7 @@ impl<C: ec::Curve> PublicKey<C> { /// with this public key. SHA-256 will be used to produce the digest if the /// curve of this public key is P-256. SHA-384 will be used to produce the /// digest if the curve of this public key is P-384. - pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignature> { + pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> { let digest = C::hash(signed_msg); let result = self.point.with_point_as_ec_key(|ec_key| unsafe { // Safety: `ec_key` is valid per `with_point_as_ec_key`. @@ -99,7 +95,7 @@ impl<C: ec::Curve> PublicKey<C> { if result == 1 { Ok(()) } else { - Err(InvalidSignature) + Err(InvalidSignatureError) } } } diff --git a/rust/bssl-crypto/src/ed25519.rs b/rust/bssl-crypto/src/ed25519.rs index f4ab5be..5ac5291 100644 --- a/rust/bssl-crypto/src/ed25519.rs +++ b/rust/bssl-crypto/src/ed25519.rs @@ -13,108 +13,126 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -use crate::CSlice; +//! Ed25519, a signature scheme. +//! +//! Ed25519 builds a signature scheme over a curve that is isogenous to +//! curve25519. This module provides the "pure" signature scheme described in +//! <https://datatracker.ietf.org/doc/html/rfc8032>. +//! +//! ``` +//! use bssl_crypto::ed25519; +//! +//! let key = ed25519::PrivateKey::generate(); +//! // Publish your public key. +//! let public_key_bytes = *key.to_public().as_bytes(); +//! +//! // Sign and publish some message. +//! let signed_message = b"hello world"; +//! let mut sig = key.sign(signed_message); +//! +//! // Anyone with the public key can verify it. +//! let public_key = ed25519::PublicKey::from_bytes(&public_key_bytes); +//! assert!(public_key.verify(signed_message, &sig).is_ok()); +//! ``` + +use crate::{FfiMutSlice, FfiSlice, InvalidSignatureError}; /// The length in bytes of an Ed25519 public key. -pub const PUBLIC_KEY_LENGTH: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize; +pub const PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize; -/// The length in bytes of an Ed25519 seed which is the 32-byte private key representation defined -/// in RFC 8032. -pub const SEED_LENGTH: usize = +/// The length in bytes of an Ed25519 seed which is the 32-byte private key +/// representation defined in RFC 8032. +pub const SEED_LEN: usize = (bssl_sys::ED25519_PRIVATE_KEY_LEN - bssl_sys::ED25519_PUBLIC_KEY_LEN) as usize; /// The length in bytes of an Ed25519 signature. -pub const SIGNATURE_LENGTH: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize; +pub const SIGNATURE_LEN: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize; // The length in bytes of an Ed25519 keypair. In BoringSSL, the private key is suffixed with the // public key, so the keypair length is the same as the private key length. -const KEYPAIR_LENGTH: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize; +const KEYPAIR_LEN: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize; /// An Ed25519 private key. -pub struct PrivateKey([u8; KEYPAIR_LENGTH]); - -/// An Ed25519 signature created by signing a message with a private key. -pub struct Signature([u8; SIGNATURE_LENGTH]); +pub struct PrivateKey([u8; KEYPAIR_LEN]); /// An Ed25519 public key used to verify a signature + message. -pub struct PublicKey([u8; PUBLIC_KEY_LENGTH]); +pub struct PublicKey([u8; PUBLIC_KEY_LEN]); -/// Error returned if the verification on the signature + message fails. -#[derive(Debug)] -pub struct SignatureError; +/// An Ed25519 signature created by signing a message with a private key. +pub type Signature = [u8; SIGNATURE_LEN]; impl PrivateKey { /// Generates a new Ed25519 keypair. pub fn generate() -> Self { - let mut public_key = [0u8; PUBLIC_KEY_LENGTH]; - let mut private_key = [0u8; KEYPAIR_LENGTH]; + let mut public_key = [0u8; PUBLIC_KEY_LEN]; + let mut private_key = [0u8; KEYPAIR_LEN]; // Safety: // - Public key and private key are the correct length. - unsafe { bssl_sys::ED25519_keypair(public_key.as_mut_ptr(), private_key.as_mut_ptr()) } + unsafe { + bssl_sys::ED25519_keypair(public_key.as_mut_ffi_ptr(), private_key.as_mut_ffi_ptr()) + } PrivateKey(private_key) } - /// Converts the key-pair to an array of bytes consisting of the bytes of the private key - /// followed by the bytes of the public key. - pub fn to_seed(&self) -> [u8; SEED_LENGTH] { + /// Returns the "seed" of this private key, as defined in RFC 8032. + pub fn to_seed(&self) -> [u8; SEED_LEN] { // This code will never panic because a length 32 slice will always fit into a // size 32 byte array. The private key is the first 32 bytes of the keypair. #[allow(clippy::expect_used)] - self.0[..SEED_LENGTH].try_into().expect( - "A slice of length SEED_LENGTH will always fit into an array of length SEED_LENGTH", - ) + self.0[..SEED_LEN] + .try_into() + .expect("A slice of length SEED_LEN will always fit into an array of length SEED_LEN") } - /// Builds this key-pair from `seed`, which is the 32-byte private key representation defined + /// Derives a key-pair from `seed`, which is the 32-byte private key representation defined /// in RFC 8032. - pub fn new_from_seed(seed: &[u8; SEED_LENGTH]) -> Self { - let mut public_key = [0u8; PUBLIC_KEY_LENGTH]; - let mut private_key = [0u8; KEYPAIR_LENGTH]; + pub fn from_seed(seed: &[u8; SEED_LEN]) -> Self { + let mut public_key = [0u8; PUBLIC_KEY_LEN]; + let mut private_key = [0u8; KEYPAIR_LEN]; // Safety: // - Public key, private key, and seed are the correct lengths. unsafe { bssl_sys::ED25519_keypair_from_seed( - public_key.as_mut_ptr(), - private_key.as_mut_ptr(), - seed.as_ptr(), + public_key.as_mut_ffi_ptr(), + private_key.as_mut_ffi_ptr(), + seed.as_ffi_ptr(), ) } PrivateKey(private_key) } - /// Signs the given message and returns a digital signature. + /// Signs the given message and returns the signature. pub fn sign(&self, msg: &[u8]) -> Signature { - let mut sig_bytes = [0u8; SIGNATURE_LENGTH]; + let mut sig_bytes = [0u8; SIGNATURE_LEN]; - let msg_ffi = CSlice(msg); // Safety: // - On allocation failure we panic. // - Signature and private keys are always the correct length. let result = unsafe { bssl_sys::ED25519_sign( - sig_bytes.as_mut_ptr(), - msg_ffi.as_ptr(), - msg_ffi.len(), - self.0.as_ptr(), + sig_bytes.as_mut_ffi_ptr(), + msg.as_ffi_ptr(), + msg.len(), + self.0.as_ffi_ptr(), ) }; assert_eq!(result, 1, "allocation failure in bssl_sys::ED25519_sign"); - Signature(sig_bytes) + sig_bytes } - /// Returns the PublicKey of the KeyPair. - pub fn public(&self) -> PublicKey { - let keypair_bytes = self.0; + /// Returns the [`PublicKey`] corresponding to this private key. + pub fn to_public(&self) -> PublicKey { + let keypair_bytes = &self.0; // This code will never panic because a length 32 slice will always fit into a // size 32 byte array. The public key is the last 32 bytes of the keypair. #[allow(clippy::expect_used)] PublicKey( - keypair_bytes[PUBLIC_KEY_LENGTH..] + keypair_bytes[PUBLIC_KEY_LEN..] .try_into() .expect("The slice is always the correct size for a public key"), ) @@ -123,78 +141,66 @@ impl PrivateKey { impl PublicKey { /// Builds the public key from an array of bytes. - pub fn from_bytes(bytes: [u8; PUBLIC_KEY_LENGTH]) -> Self { - PublicKey(bytes) + pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LEN]) -> Self { + PublicKey(*bytes) } /// Returns the bytes of the public key. - pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] { - self.0 + pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] { + &self.0 } - /// Succeeds if the signature is a valid signature created by this keypair, otherwise returns an Error. - pub fn verify(&self, message: &[u8], signature: Signature) -> Result<(), SignatureError> { - let message_cslice = CSlice::from(message); + /// Verifies that `signature` is a valid signature, by this key, of `msg`. + pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), InvalidSignatureError> { let ret = unsafe { + // Safety: `self.0` is the correct length and other buffers are valid. bssl_sys::ED25519_verify( - message_cslice.as_ptr(), - message_cslice.len(), - signature.0.as_ptr(), - self.0.as_ptr(), + msg.as_ffi_ptr(), + msg.len(), + signature.as_ffi_ptr(), + self.0.as_ffi_ptr(), ) }; if ret == 1 { Ok(()) } else { - Err(SignatureError) + Err(InvalidSignatureError) } } } -impl Signature { - /// Creates a signature from a byte array. - pub fn from_bytes(bytes: [u8; SIGNATURE_LENGTH]) -> Self { - Self(bytes) - } - - /// Returns the bytes of the signature. - pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { - self.0 - } -} - #[cfg(test)] mod test { use super::*; use crate::test_helpers; #[test] - fn ed25519_kp_gen_roundtrip() { + fn gen_roundtrip() { let private_key = PrivateKey::generate(); assert_ne!([0u8; 64], private_key.0); let seed = private_key.to_seed(); - let new_private_key = PrivateKey::new_from_seed(&seed); + let new_private_key = PrivateKey::from_seed(&seed); assert_eq!(private_key.0, new_private_key.0); } #[test] - fn ed25519_empty_msg() { + fn empty_msg() { // Test Case 1 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1 let pk = test_helpers::decode_hex( "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", ); - let sk = test_helpers::decode_hex( + let seed = test_helpers::decode_hex( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", ); let msg = [0u8; 0]; let sig_expected = test_helpers::decode_hex("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"); - let kp = PrivateKey::new_from_seed(&sk); + let kp = PrivateKey::from_seed(&seed); let sig = kp.sign(&msg); - assert_eq!(sig_expected, sig.0); + assert_eq!(sig_expected, sig); - let pub_key = PublicKey::from_bytes(pk); - assert_eq!(pub_key.to_bytes(), kp.public().to_bytes()); - assert!(pub_key.verify(&msg, sig).is_ok()); + let pub_key = PublicKey::from_bytes(&pk); + assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes()); + assert!(pub_key.verify(&msg, &sig).is_ok()); } #[test] @@ -208,13 +214,13 @@ mod test { ); let msg: [u8; 14] = test_helpers::decode_hex("55c7fa434f5ed8cdec2b7aeac173"); let sig_expected = test_helpers::decode_hex("6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf06"); - let kp = PrivateKey::new_from_seed(&sk); + let kp = PrivateKey::from_seed(&sk); let sig = kp.sign(&msg); - assert_eq!(sig_expected, sig.0); + assert_eq!(sig_expected, sig); - let pub_key = PublicKey::from_bytes(pk); - assert_eq!(pub_key.to_bytes(), kp.public().to_bytes()); - assert!(pub_key.verify(&msg, sig).is_ok()); + let pub_key = PublicKey::from_bytes(&pk); + assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes()); + assert!(pub_key.verify(&msg, &sig).is_ok()); } } diff --git a/rust/bssl-crypto/src/lib.rs b/rust/bssl-crypto/src/lib.rs index 4263be5..babb044 100644 --- a/rust/bssl-crypto/src/lib.rs +++ b/rust/bssl-crypto/src/lib.rs @@ -43,7 +43,6 @@ pub mod cipher; pub mod digest; -/// Ed25519, a signature scheme. pub mod ed25519; pub mod hkdf; |