blob: abdb21cbc8dfcecdc50e82a34c8f2bad030bcebb [file] [log] [blame]
/* Copyright 2024 The BoringSSL Authors
*
* 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. */
//! ML-DSA
//!
//! ML-DSA is a post-quantum key signature scheme, specified in
//! [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final).
//!
//! ```
//! use bssl_crypto::mldsa;
//!
//! // Generate a key pair.
//! let (serialized_public_key, private_key, _private_seed) = mldsa::PrivateKey65::generate();
//!
//! // Send `serialized_public_key` to the verifier. The verifier parses it:
//! let public_key = mldsa::PublicKey65::parse(&serialized_public_key).unwrap();
//!
//! // The signer signs a message.
//! let message = &[0u8, 1, 2, 3];
//! let signature = private_key.sign(message);
//!
//! // Send `message` and `signature` to the verifier. The verifier checks the signature.
//! assert!(public_key.verify(message, &signature).is_ok());
//! ```
use crate::{
as_cbs, cbb_to_vec, initialized_boxed_struct, initialized_boxed_struct_fallible,
with_output_vec, with_output_vec_fallible, FfiSlice, InvalidSignatureError,
};
use alloc::{boxed::Box, vec::Vec};
use core::mem::MaybeUninit;
/// An ML-DSA-65 private key.
pub struct PrivateKey65(Box<bssl_sys::MLDSA65_private_key>);
/// An ML-DSA-65 public key.
pub struct PublicKey65(Box<bssl_sys::MLDSA65_public_key>);
/// The number of bytes in an encoded ML-DSA-65 public key.
pub const PUBLIC_KEY_BYTES_65: usize = bssl_sys::MLDSA65_PUBLIC_KEY_BYTES as usize;
/// The number of bytes in an encoded ML-DSA-65 signature.
pub const SIGNATURE_BYTES_65: usize = bssl_sys::MLDSA65_SIGNATURE_BYTES as usize;
/// The number of bytes in an ML-DSA seed value.
pub const SEED_BYTES: usize = bssl_sys::MLDSA_SEED_BYTES as usize;
impl PrivateKey65 {
/// Generates a random public/private key pair returning a serialized public
/// key, a private key, and a private seed value that can be used to
/// regenerate the same private key in the future.
pub fn generate() -> (Vec<u8>, Self, [u8; SEED_BYTES]) {
let mut public_key_bytes = Box::new_uninit_slice(PUBLIC_KEY_BYTES_65);
let mut seed = MaybeUninit::<[u8; SEED_BYTES]>::uninit();
let private_key = unsafe {
// Safety: the buffers are the sizes that the FFI code requires.
initialized_boxed_struct(|priv_key| {
let ok = bssl_sys::MLDSA65_generate_key(
public_key_bytes.as_mut_ptr() as *mut u8,
seed.as_mut_ptr() as *mut u8,
priv_key,
);
// This function can only fail if out of memory, which is not a
// case that this crate handles.
assert_eq!(ok, 1);
})
};
unsafe {
(
// Safety: the buffers are always fully initialized by
// `MLDSA65_generate_key`.
public_key_bytes.assume_init().into(),
Self(private_key),
seed.assume_init(),
)
}
}
/// Regenerates a private key from a seed value.
pub fn from_seed(seed: &[u8; SEED_BYTES]) -> Self {
Self(unsafe {
// Safety: `priv_key` is the correct size via the type system and
// is always fully written.
initialized_boxed_struct(|priv_key| {
let ok = bssl_sys::MLDSA65_private_key_from_seed(
priv_key,
seed.as_ffi_ptr(),
seed.len(),
);
// Since the seed value has the correct length, this function can
// never fail.
assert_eq!(ok, 1);
})
})
}
/// Derives the public key corresponding to this private key.
pub fn to_public_key(&self) -> PublicKey65 {
PublicKey65(unsafe {
// Safety: `pub_key` is the correct size via the type system and
// is always fully written.
initialized_boxed_struct(|pub_key| {
bssl_sys::MLDSA65_public_from_private(pub_key, &*self.0);
})
})
}
/// Signs a message using this private key.
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
unsafe {
// Safety: `signature` is the correct size via the type system and
// is always fully written.
with_output_vec(SIGNATURE_BYTES_65, |signature| {
let ok = bssl_sys::MLDSA65_sign(
signature,
&*self.0,
msg.as_ffi_ptr(),
msg.len(),
core::ptr::null(),
0,
);
// This function can only fail if out of memory, which is not a
// case that this crate handles.
assert_eq!(ok, 1);
SIGNATURE_BYTES_65
})
}
}
/// Signs a message using this private key and the given context.
///
/// This function returns None if `context` is longer than 255 bytes.
pub fn sign_with_context(&self, msg: &[u8], context: &[u8]) -> Option<Vec<u8>> {
unsafe {
// Safety: `signature` is the correct size via the type system and
// is always fully written.
with_output_vec_fallible(SIGNATURE_BYTES_65, |signature| {
if bssl_sys::MLDSA65_sign(
signature,
&*self.0,
msg.as_ffi_ptr(),
msg.len(),
context.as_ffi_ptr(),
context.len(),
) == 1
{
Some(SIGNATURE_BYTES_65)
} else {
None
}
})
}
}
}
impl PublicKey65 {
/// Parses a public key from a byte slice.
pub fn parse(encoded: &[u8]) -> Option<Self> {
let mut cbs = as_cbs(encoded);
unsafe {
// Safety: `pub_key` is the correct size via the type system and
// is fully written if this function returns 1.
initialized_boxed_struct_fallible(|pub_key| {
bssl_sys::MLDSA65_parse_public_key(pub_key, &mut cbs) == 1 && cbs.len == 0
})
}
.map(Self)
}
/// Return the serialization of this public key.
pub fn to_bytes(&self) -> Vec<u8> {
unsafe {
cbb_to_vec(PUBLIC_KEY_BYTES_65, |buf| {
let ok = bssl_sys::MLDSA65_marshal_public_key(buf, &*self.0);
// `MLKEM768_marshal_public_key` only fails if it cannot
// allocate memory, but `cbb_to_vec` allocates a fixed
// amount of memory.
assert_eq!(ok, 1);
})
}
}
/// Verifies a signature for a given message using this public key.
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
unsafe {
let ok = bssl_sys::MLDSA65_verify(
&*self.0,
signature.as_ffi_ptr(),
signature.len(),
msg.as_ffi_ptr(),
msg.len(),
core::ptr::null(),
0,
);
if ok == 1 {
Ok(())
} else {
Err(InvalidSignatureError)
}
}
}
/// Verifies a signature for a given message using this public key and the given context.
pub fn verify_with_context(
&self,
msg: &[u8],
signature: &[u8],
context: &[u8],
) -> Result<(), InvalidSignatureError> {
unsafe {
let ok = bssl_sys::MLDSA65_verify(
&*self.0,
signature.as_ffi_ptr(),
signature.len(),
msg.as_ffi_ptr(),
msg.len(),
context.as_ffi_ptr(),
context.len(),
);
if ok == 1 {
Ok(())
} else {
Err(InvalidSignatureError)
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn basic() {
let (serialized_public_key, private_key, private_seed) = PrivateKey65::generate();
let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
let message = &[0u8, 1, 2, 3];
let signature = private_key.sign(message);
let private_key2 = PrivateKey65::from_seed(&private_seed);
let mut signature2 = private_key2.sign(message);
assert!(public_key.verify(message, &signature).is_ok());
assert!(public_key.verify(message, &signature2).is_ok());
signature2[5] ^= 1;
assert!(public_key.verify(message, &signature2).is_err());
let context = b"context";
let signature3 = private_key.sign_with_context(message, context).unwrap();
assert!(public_key.verify(message, &signature3).is_err());
assert!(public_key
.verify_with_context(message, &signature3, context)
.is_ok());
}
#[test]
fn marshal_public_key() {
let (serialized_public_key, private_key, _) = PrivateKey65::generate();
let public_key = PublicKey65::parse(&serialized_public_key).unwrap();
assert_eq!(serialized_public_key, public_key.to_bytes());
assert_eq!(
serialized_public_key,
private_key.to_public_key().to_bytes()
);
}
}