// Copyright 2023 The BoringSSL Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! 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 //! . //! //! ``` //! 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::{ cbb_to_buffer, parse_with_cbs, scoped, with_output_array, Buffer, FfiMutSlice, FfiSlice, InvalidSignatureError, }; /// The length in bytes of an Ed25519 public key. 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_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_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_LEN: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize; /// An Ed25519 private key. pub struct PrivateKey([u8; KEYPAIR_LEN]); /// An Ed25519 public key used to verify a signature + message. pub struct PublicKey([u8; PUBLIC_KEY_LEN]); /// 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_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_ffi_ptr(), private_key.as_mut_ffi_ptr()) } PrivateKey(private_key) } /// 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_LEN] .try_into() .expect("A slice of length SEED_LEN will always fit into an array of length SEED_LEN") } /// Derives a key-pair from `seed`, which is the 32-byte private key representation defined /// in RFC 8032. 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_ffi_ptr(), private_key.as_mut_ffi_ptr(), seed.as_ffi_ptr(), ) } PrivateKey(private_key) } /// Signs the given message and returns the signature. pub fn sign(&self, msg: &[u8]) -> Signature { let mut sig_bytes = [0u8; SIGNATURE_LEN]; // 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_ffi_ptr(), msg.as_ffi_ptr(), msg.len(), self.0.as_ffi_ptr(), ) }; assert_eq!(result, 1, "allocation failure in bssl_sys::ED25519_sign"); sig_bytes } /// 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_LEN..] .try_into() .expect("The slice is always the correct size for a public key"), ) } } impl PublicKey { /// Builds the public key from an array of bytes. pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LEN]) -> Self { PublicKey(*bytes) } /// Returns the bytes of the public key. pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] { &self.0 } /// Parse a public key in SubjectPublicKeyInfo format. pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option { let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs( spki, // Safety: `pkey` is a non-null result from `EVP_parse_public_key` here. |pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) }, // Safety: cbs is valid per `parse_with_cbs`. |cbs| unsafe { bssl_sys::EVP_parse_public_key(cbs) }, )?); let mut out_len = 0; // When the out buffer is null, `out_len` is set to the size of the raw public key. // Safety: the arguments are valid. let result = unsafe { bssl_sys::EVP_PKEY_get_raw_public_key( pkey.as_ffi_ptr(), core::ptr::null_mut(), &mut out_len, ) }; if result != 1 { return None; } if out_len != PUBLIC_KEY_LEN { return None; } // When the out buffer is not null, the raw public key is written into it. // Safety: the arguments are valid. let raw_pkey: [u8; PUBLIC_KEY_LEN] = unsafe { with_output_array(|out, _| { assert_eq!( 1, bssl_sys::EVP_PKEY_get_raw_public_key(pkey.as_ffi_ptr(), out, &mut out_len) ); }) }; Some(PublicKey(raw_pkey)) } /// Serialize this key in SubjectPublicKeyInfo format. pub fn to_der_subject_public_key_info(&self) -> Buffer { // Safety: this only copies from the `self.0` buffer. let mut pkey = scoped::EvpPkey::from_ptr(unsafe { bssl_sys::EVP_PKEY_new_raw_public_key( bssl_sys::EVP_PKEY_ED25519, /*unused=*/ core::ptr::null_mut(), self.0.as_ffi_ptr(), PUBLIC_KEY_LEN, ) }); assert!(!pkey.as_ffi_ptr().is_null()); cbb_to_buffer(PUBLIC_KEY_LEN + 32, |cbb| unsafe { // The arguments are valid so this will only fail if out of memory, // which this crate doesn't handle. assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, pkey.as_ffi_ptr())); }) } /// 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( msg.as_ffi_ptr(), msg.len(), signature.as_ffi_ptr(), self.0.as_ffi_ptr(), ) }; if ret == 1 { Ok(()) } else { Err(InvalidSignatureError) } } } #[cfg(test)] mod test { use super::*; use crate::test_helpers; #[test] 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::from_seed(&seed); assert_eq!(private_key.0, new_private_key.0); } #[test] fn der_subject_public_key_info() { let priv_key = PrivateKey::generate(); let msg = [0u8; 0]; let sig = priv_key.sign(&msg); let pub_key = priv_key.to_public(); assert!(pub_key.verify(&msg, &sig).is_ok()); let pub_key_der = pub_key.to_der_subject_public_key_info(); let pub_key_from_der = PublicKey::from_der_subject_public_key_info(pub_key_der.as_ref()).unwrap(); assert_eq!(pub_key.as_bytes(), pub_key_from_der.as_bytes()); assert!(pub_key_from_der.verify(&msg, &sig).is_ok()); assert!(PublicKey::from_der_subject_public_key_info( &pub_key_from_der.as_bytes()[0..PUBLIC_KEY_LEN / 2] ) .is_none()); assert!(PublicKey::from_der_subject_public_key_info(b"").is_none()); } #[test] 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 seed = test_helpers::decode_hex( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", ); let msg = [0u8; 0]; let sig_expected = test_helpers::decode_hex("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"); let kp = PrivateKey::from_seed(&seed); let sig = kp.sign(&msg); assert_eq!(sig_expected, sig); 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] fn ed25519_sign_and_verify() { // Test Case 15 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1 let pk = test_helpers::decode_hex( "cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291", ); let sk = test_helpers::decode_hex( "9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738", ); let msg: [u8; 14] = test_helpers::decode_hex("55c7fa434f5ed8cdec2b7aeac173"); let sig_expected = test_helpers::decode_hex("6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf06"); let kp = PrivateKey::from_seed(&sk); let sig = kp.sign(&msg); assert_eq!(sig_expected, sig); 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()); } }