@ -13,11 +13,40 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
* /
//! Diffie-Hellman over curve25519.
//!
//! X25519 is the Diffie-Hellman primitive built from curve25519. It is sometimes referred to as
//! “curve25519”, but “X25519” is a more precise name. See http://cr.yp.to/ecdh.html and
//! https://tools.ietf.org/html/rfc7748.
use alloc ::borrow ::ToOwned ;
//! “curve25519”, but “X25519” is a more precise name. See <http://cr.yp.to/ecdh.html> and
//! <https://tools.ietf.org/html/rfc7748>.
//!
//! ```
//! use bssl_crypto::x25519;
//!
//! // Alice generates her key pair.
//! let (alice_public_key, alice_private_key) = x25519::PrivateKey::generate();
//! // Bob generates his key pair.
//! let (bob_public_key, bob_private_key) = x25519::PrivateKey::generate();
//!
//! // If Alice obtains Bob's public key somehow, she can compute their
//! // shared key:
//! let shared_key = alice_private_key.compute_shared_key(&bob_public_key);
//!
//! // Alice can then derive a key (e.g. by using HKDF), which should include
//! // at least the two public keys. Then shen can send a message to Bob
//! // including her public key and an AEAD-protected blob. Bob can compute the
//! // same shared key given Alice's public key:
//! let shared_key2 = bob_private_key.compute_shared_key(&alice_public_key);
//! assert_eq!(shared_key, shared_key2);
//!
//! // This is an _unauthenticated_ exchange which is vulnerable to an
//! // active attacker. See, for example,
//! // http://www.noiseprotocol.org/noise.html for an example of building
//! // real protocols from a Diffie-Hellman primitive.
//! ```
use crate ::with_output_array ;
use crate ::with_output_array_fallible ;
use crate ::FfiSlice ;
/// Number of bytes in a private key in X25519
pub const PRIVATE_KEY_LEN : usize = bssl_sys ::X25519_PRIVATE_KEY_LEN as usize ;
@ -26,190 +55,99 @@ pub const PUBLIC_KEY_LEN: usize = bssl_sys::X25519_PUBLIC_VALUE_LEN as usize;
/// Number of bytes in a shared secret derived with X25519
pub const SHARED_KEY_LEN : usize = bssl_sys ::X25519_SHARED_KEY_LEN as usize ;
/// Error while performing a X25519 Diffie-Hellman key exchange.
#[ derive(Debug) ]
pub struct DiffieHellmanError ;
/// X25519 public keys are simply 32-byte strings.
pub type PublicKey = [ u8 ; PUBLIC_KEY_LEN ] ;
/// An X25519 private key (a 32-byte string).
pub struct PrivateKey ( pub [ u8 ; PRIVATE_KEY_LEN ] ) ;
/// A struct containing a X25519 key pair.
pub struct PrivateKey {
private_key : [ u8 ; PRIVATE_KEY_LEN ] ,
public_key : [ u8 ; PUBLIC_KEY_LEN ] ,
impl AsRef < [ u8 ] > for PrivateKey {
fn as_ref ( & self ) -> & [ u8 ] {
& self . 0
}
}
impl PrivateKey {
/// Derives a shared secrect from this private key and the given public key.
pub fn diffie_hellman (
& self ,
other_public_key : & PublicKey ,
) -> Result < SharedSecret , DiffieHellmanError > {
let mut shared_key_uninit = core ::mem ::MaybeUninit ::< [ u8 ; SHARED_KEY_LEN ] > ::uninit ( ) ;
// Safety:
// - private_key and other_public_key are Rust 32-byte arrays
// - shared_key_uninit is just initialized above to a 32 byte array
let result = unsafe {
bssl_sys ::X25519 (
shared_key_uninit . as_mut_ptr ( ) as * mut u8 ,
self . private_key . as_ptr ( ) ,
other_public_key . 0. as_ptr ( ) ,
)
} ;
if result = = 1 {
// Safety:
// - `shared_key_uninit` is initialized by `X25519` above, and we checked that it
// succeeded
let shared_key = unsafe { shared_key_uninit . assume_init ( ) } ;
Ok ( crate ::ecdh ::SharedSecret ( shared_key ) )
} else {
Err ( DiffieHellmanError )
/// Derive the shared key between this private key and a peer's public key.
/// Don't use the shared key directly, rather use a KDF and also include
/// the two public values as inputs.
///
/// Will fail and produce `None` if the peer's public key is a point of
/// small order. It is safe to react to this in non-constant time.
pub fn compute_shared_key ( & self , other_public_key : & PublicKey ) -> Option < [ u8 ; SHARED_KEY_LEN ] > {
// Safety: `X25519` indeed writes `SHARED_KEY_LEN` bytes.
unsafe {
with_output_array_fallible ( | out , _ | {
bssl_sys ::X25519 ( out , self . 0. as_ffi_ptr ( ) , other_public_key . as_ffi_ptr ( ) )
} )
}
}
/// Generate a new key pair for use in a Diffie-Hellman key exchange .
pub fn generate ( ) -> Self {
/// Generate a new key pair.
pub fn generate ( ) -> ( PublicKey , PrivateKey ) {
let mut public_key_uninit = core ::mem ::MaybeUninit ::< [ u8 ; PUBLIC_KEY_LEN ] > ::uninit ( ) ;
let mut private_key_uninit = core ::mem ::MaybeUninit ::< [ u8 ; PRIVATE_KEY_LEN ] > ::uninit ( ) ;
// Safety:
// - private_key_uninit and public_key_uninit are allocated to 32-bytes
let ( public_key , private_key ) = unsafe {
// - private_key_uninit and public_key_uninit are the correct length.
unsafe {
bssl_sys ::X25519_keypair (
public_key_uninit . as_mut_ptr ( ) as * mut u8 ,
private_key_uninit . as_mut_ptr ( ) as * mut u8 ,
) ;
// Safety: Initialized by `X25519_keypair` above
// Safety: Initialized by `X25519_keypair` just above.
(
public_key_uninit . assume_init ( ) ,
private_key_uninit . assume_init ( ) ,
PrivateKey ( private_key_uninit . assume_init ( ) ) ,
)
} ;
Self {
private_key ,
public_key ,
}
}
/// Tries to convert the given bytes into a private key.
pub fn from_private_bytes ( private_key_bytes : & [ u8 ; PRIVATE_KEY_LEN ] ) -> Self {
let mut public_key_uninit = core ::mem ::MaybeUninit ::< [ u8 ; PUBLIC_KEY_LEN ] > ::uninit ( ) ;
let private_key : [ u8 ; PRIVATE_KEY_LEN ] = private_key_bytes . to_owned ( ) ;
// Safety:
// - private_key and public_key are Rust 32-byte arrays
let public_key = unsafe {
bssl_sys ::X25519_public_from_private (
public_key_uninit . as_mut_ptr ( ) as * mut _ ,
private_key . as_ptr ( ) ,
) ;
public_key_uninit . assume_init ( )
} ;
Self {
private_key ,
public_key ,
/// Compute the public key corresponding to this private key.
pub fn to_public ( & self ) -> PublicKey {
// Safety: `X25519_public_from_private` indeed fills an entire [`PublicKey`].
unsafe {
with_output_array ( | out , _ | {
bssl_sys ::X25519_public_from_private ( out , self . 0. as_ffi_ptr ( ) ) ;
} )
}
}
}
impl < ' a > From < & ' a PrivateKey > for PublicKey {
fn from ( value : & ' a PrivateKey ) -> Self {
Self ( value . public_key )
}
}
/// A public key for X25519 elliptic curve.
#[ derive(Debug, PartialEq, Eq) ]
pub struct PublicKey ( [ u8 ; PUBLIC_KEY_LEN ] ) ;
impl PublicKey {
/// Converts this public key to its byte representation.
pub fn to_bytes ( & self ) -> [ u8 ; PUBLIC_KEY_LEN ] {
self . 0
}
/// Returns a reference to the byte representation of this public key.
pub fn as_bytes ( & self ) -> & [ u8 ; PUBLIC_KEY_LEN ] {
& self . 0
}
}
impl From < & [ u8 ; 32 ] > for PublicKey {
fn from ( value : & [ u8 ; 32 ] ) -> Self {
Self ( * value )
}
}
/// Shared secret derived from a Diffie-Hellman key exchange. Don't use the shared key directly,
/// rather use a KDF and also include the two public values as inputs.
type SharedSecret = crate ::ecdh ::SharedSecret < SHARED_KEY_LEN > ;
#[ cfg(test) ]
#[ allow(clippy::unwrap_used) ]
mod tests {
use crate ::{
test_helpers ::decode_hex ,
x25519 ::{ PrivateKey , PublicKey } ,
} ;
use crate ::{ test_helpers ::decode_hex , x25519 ::PrivateKey } ;
#[ test ]
fn x25519_test_diffie_hellman ( ) {
fn known_vector ( ) {
// wycheproof/testvectors/x25519_test.json tcId 1
let public_key_bytes : [ u8 ; 32 ] =
let public_key : [ u8 ; 32 ] =
decode_hex ( "504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829" ) ;
let private_key =
decode_hex ( "c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475" ) ;
let private_key = PrivateKey ( decode_hex (
"c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475" ,
) ) ;
let expected_shared_secret : [ u8 ; 32 ] =
decode_hex ( "436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320" ) ;
let public_key = PublicKey ::from ( & public_key_bytes ) ;
let private_key = PrivateKey ::from_private_bytes ( & private_key ) ;
let shared_secret = private_key . diffie_hellman ( & public_key ) . unwrap ( ) ;
assert_eq! ( expected_shared_secret , shared_secret . to_bytes ( ) ) ;
}
#[ test ]
fn x25519_generate_diffie_hellman_matches ( ) {
let private_key_1 = PrivateKey ::generate ( ) ;
let private_key_2 = PrivateKey ::generate ( ) ;
let public_key_1 = PublicKey ::from ( & private_key_1 ) ;
let public_key_2 = PublicKey ::from ( & private_key_2 ) ;
let diffie_hellman_1 = private_key_1 . diffie_hellman ( & public_key_2 ) . unwrap ( ) ;
let diffie_hellman_2 = private_key_2 . diffie_hellman ( & public_key_1 ) . unwrap ( ) ;
assert_eq! ( diffie_hellman_1 . to_bytes ( ) , diffie_hellman_2 . to_bytes ( ) ) ;
let shared_secret = private_key . compute_shared_key ( & public_key ) . unwrap ( ) ;
assert_eq! ( expected_shared_secret , shared_secret ) ;
}
#[ test ]
fn x25519_test_diffie_hellman_zero_public_key ( ) {
// wycheproof/testvectors/x25519_test.json tcId 32
let public_key_bytes =
decode_hex ( "0000000000000000000000000000000000000000000000000000000000000000" ) ;
let private_key =
decode_hex ( "88227494038f2bb811d47805bcdf04a2ac585ada7f2f23389bfd4658f9ddd45e" ) ;
let public_key = PublicKey ::from ( & public_key_bytes ) ;
let private_key = PrivateKey ::from_private_bytes ( & private_key ) ;
let shared_secret = private_key . diffie_hellman ( & public_key ) ;
assert! ( shared_secret . is_err ( ) ) ;
fn all_zero_public_key ( ) {
assert! ( PrivateKey ::generate ( )
. 1
. compute_shared_key ( & [ 0 u8 ; 32 ] )
. is_none ( ) ) ;
}
#[ test ]
fn x25519_public_key_byte_conversion ( ) {
let public_key_bytes =
decode_hex ( "504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829" ) ;
let public_key = PublicKey ::from ( & public_key_bytes ) ;
assert_eq! ( public_key_bytes , public_key . to_bytes ( ) ) ;
}
#[ test ]
fn x25519_test_public_key_from_private_key ( ) {
fn to_public ( ) {
// Taken from https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1
let public_key_bytes =
decode_hex ( "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a" ) ;
let private_key_bytes =
decode_hex ( "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a" ) ;
let private_key = PrivateKey ::from_private_bytes ( & private_key_bytes ) ;
assert_eq! (
PublicKey ::from ( & public_key_bytes ) ,
PublicKey ::from ( & private_key )
) ;
let private_key = PrivateKey ( decode_hex (
"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a" ,
) ) ;
assert_eq! ( public_key_bytes , private_key . to_public ( ) ) ;
}
}