Add X25519 bindings for bssl-crypto
Bug: 285222831 Change-Id: I35219ac312fd97e7a51af8156c73fa7eb38c17c2 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60268 Commit-Queue: Bob Beck <bbe@google.com> Reviewed-by: Bob Beck <bbe@google.com>
This commit is contained in:
parent
37be47b0cc
commit
f7629e189a
@ -50,6 +50,9 @@ pub mod hmac;
|
||||
/// Random number generation.
|
||||
pub mod rand;
|
||||
|
||||
/// X25519 elliptic curve operations.
|
||||
pub mod x25519;
|
||||
|
||||
/// Memory-manipulation operations.
|
||||
pub mod mem;
|
||||
|
||||
|
213
rust/bssl-crypto/src/x25519.rs
Normal file
213
rust/bssl-crypto/src/x25519.rs
Normal file
@ -0,0 +1,213 @@
|
||||
/* Copyright (c) 2023, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
//! 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.
|
||||
|
||||
/// Number of bytes in a private key in X25519
|
||||
pub const PRIVATE_KEY_LEN: usize = bssl_sys::X25519_PRIVATE_KEY_LEN as usize;
|
||||
/// Number of bytes in a public key in X25519
|
||||
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;
|
||||
|
||||
/// A struct containing a X25519 key pair.
|
||||
pub struct PrivateKey {
|
||||
private_key: [u8; PRIVATE_KEY_LEN],
|
||||
public_key: [u8; PUBLIC_KEY_LEN],
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new key pair for use in a Diffie-Hellman key exchange.
|
||||
pub fn generate() -> Self {
|
||||
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 {
|
||||
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
|
||||
(
|
||||
public_key_uninit.assume_init(),
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn x25519_test_diffie_hellman() {
|
||||
// wycheproof/testvectors/x25519_test.json tcId 1
|
||||
let public_key_bytes: [u8; 32] =
|
||||
decode_hex("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829");
|
||||
let private_key =
|
||||
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());
|
||||
}
|
||||
|
||||
#[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());
|
||||
}
|
||||
|
||||
#[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() {
|
||||
// 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)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user