blob: d191cce9a3978f8180e181351725f91162d8a5d7 [file] [log] [blame] [edit]
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
pub(crate) mod key_pair;
pub(crate) mod signature;
use crate::aws_lc::{EVP_PKEY, EVP_PKEY_PQDSA, NID_MLDSA44, NID_MLDSA65, NID_MLDSA87};
use crate::error::{KeyRejected, Unspecified};
use crate::ptr::LcPtr;
use core::ffi::c_int;
#[derive(Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub(crate) enum AlgorithmID {
ML_DSA_44,
ML_DSA_65,
ML_DSA_87,
}
impl AlgorithmID {
#[allow(dead_code)]
pub(crate) const fn from_nid(nid: c_int) -> Result<Self, Unspecified> {
match nid {
NID_MLDSA44 => Ok(Self::ML_DSA_44),
NID_MLDSA65 => Ok(Self::ML_DSA_65),
NID_MLDSA87 => Ok(Self::ML_DSA_87),
_ => Err(Unspecified),
}
}
pub(crate) const fn nid(&self) -> c_int {
match self {
Self::ML_DSA_44 => NID_MLDSA44,
Self::ML_DSA_65 => NID_MLDSA65,
Self::ML_DSA_87 => NID_MLDSA87,
}
}
#[allow(dead_code)]
pub(crate) const fn priv_key_size_bytes(&self) -> usize {
match self {
Self::ML_DSA_44 => 2560,
Self::ML_DSA_65 => 4032,
Self::ML_DSA_87 => 4896,
}
}
pub(crate) const fn pub_key_size_bytes(&self) -> usize {
match self {
Self::ML_DSA_44 => 1312,
Self::ML_DSA_65 => 1952,
Self::ML_DSA_87 => 2592,
}
}
pub(crate) const fn signature_size_bytes(&self) -> usize {
match self {
Self::ML_DSA_44 => 2420,
Self::ML_DSA_65 => 3309,
Self::ML_DSA_87 => 4627,
}
}
}
pub(crate) fn validate_pqdsa_evp_key(
evp_pkey: &LcPtr<EVP_PKEY>,
id: &'static AlgorithmID,
) -> Result<(), KeyRejected> {
if evp_pkey.as_const().key_size_bytes() == id.pub_key_size_bytes() {
Ok(())
} else {
Err(KeyRejected::unspecified())
}
}
pub(crate) fn parse_pqdsa_public_key(
key_bytes: &[u8],
id: &'static AlgorithmID,
) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(key_bytes, EVP_PKEY_PQDSA)
.or(LcPtr::<EVP_PKEY>::parse_raw_public_key(
key_bytes,
EVP_PKEY_PQDSA,
))
.and_then(|key| validate_pqdsa_evp_key(&key, id).map(|()| key))
}
#[cfg(test)]
mod tests {
use crate::aws_lc::{
EVP_PKEY_cmp, EVP_PKEY, EVP_PKEY_PQDSA, NID_MLDSA44, NID_MLDSA65, NID_MLDSA87,
};
use crate::evp_pkey::*;
use crate::pkcs8::Version;
use crate::pqdsa::key_pair::evp_key_pqdsa_generate;
use crate::pqdsa::AlgorithmID;
use crate::ptr::LcPtr;
#[test]
fn test_keygen() {
for nid in [NID_MLDSA44, NID_MLDSA65, NID_MLDSA87] {
let key = evp_key_pqdsa_generate(nid).unwrap();
println!("key size: {:?}", key.as_const().key_size_bytes());
test_serialization_for(&key, &AlgorithmID::from_nid(nid).unwrap());
test_signing_for(&key, &AlgorithmID::from_nid(nid).unwrap());
}
}
fn test_serialization_for(evp_pkey: &LcPtr<EVP_PKEY>, id: &AlgorithmID) {
let public_buffer = evp_pkey.as_const().marshal_rfc5280_public_key().unwrap();
println!("public marshall: {public_buffer:?}");
let key_public =
LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(&public_buffer, EVP_PKEY_PQDSA).unwrap();
let private_buffer = evp_pkey
.as_const()
.marshal_rfc5208_private_key(Version::V1)
.unwrap();
println!("private marshall: {private_buffer:?}");
let key_private =
LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(&private_buffer, EVP_PKEY_PQDSA).unwrap();
let raw_public_buffer = key_public.as_const().marshal_raw_public_key().unwrap();
assert_eq!(raw_public_buffer.len(), id.pub_key_size_bytes());
println!("raw public size: {}", raw_public_buffer.len());
let key_public2 =
LcPtr::<EVP_PKEY>::parse_raw_public_key(&raw_public_buffer, EVP_PKEY_PQDSA).unwrap();
assert_eq!(1, unsafe {
EVP_PKEY_cmp(*key_public.as_const(), *key_public2.as_const())
});
let raw_private_buffer = key_private.as_const().marshal_raw_private_key().unwrap();
assert_eq!(raw_private_buffer.len(), id.priv_key_size_bytes());
println!("raw private size: {}", raw_private_buffer.len());
let key_private2 =
LcPtr::<EVP_PKEY>::parse_raw_private_key(&raw_private_buffer, EVP_PKEY_PQDSA).unwrap();
assert_eq!(1, unsafe {
EVP_PKEY_cmp(*key_private.as_const(), *key_private2.as_const())
});
}
fn test_signing_for(evp_pkey: &LcPtr<EVP_PKEY>, id: &AlgorithmID) {
let message = b"hello world";
let signature = evp_pkey
.sign(message, None, No_EVP_PKEY_CTX_consumer)
.unwrap();
println!("signature size: {}", signature.len());
assert_eq!(signature.len(), evp_pkey.as_const().signature_size_bytes());
assert_eq!(signature.len(), id.signature_size_bytes());
evp_pkey
.verify(message, None, No_EVP_PKEY_CTX_consumer, &signature)
.unwrap();
println!("verified: {signature:?}");
}
}