use super::mldsa::{PUBLIC_KEY_LEN, SIGNATURE_LEN, Seed};
use aws_lc_sys::{
EVP_DigestSign, EVP_DigestSignInit, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_MD_CTX,
EVP_MD_CTX_free, EVP_MD_CTX_new, EVP_PKEY, EVP_PKEY_CTX,
EVP_PKEY_CTX_set1_signature_context_string, EVP_PKEY_free, EVP_PKEY_get_raw_public_key,
EVP_PKEY_pqdsa_new_raw_private_key, EVP_PKEY_pqdsa_new_raw_public_key, NID_MLDSA44,
};
use std::ptr::{NonNull, null, null_mut};
const SUCCESS: i32 = 1;
pub(crate) struct MlDsa44(NonNull<EVP_PKEY>);
unsafe impl Send for MlDsa44 {}
unsafe impl Sync for MlDsa44 {}
impl Drop for MlDsa44 {
fn drop(&mut self) {
unsafe { EVP_PKEY_free(self.0.as_ptr()) }
}
}
impl MlDsa44 {
pub(crate) fn from_seed(seed: &Seed) -> Option<Self> {
NonNull::new(unsafe {
EVP_PKEY_pqdsa_new_raw_private_key(NID_MLDSA44, seed.as_ptr(), seed.len())
})
.map(Self)
}
pub(crate) fn from_public_key(key: &[u8; PUBLIC_KEY_LEN]) -> Option<Self> {
NonNull::new(unsafe {
EVP_PKEY_pqdsa_new_raw_public_key(NID_MLDSA44, key.as_ptr(), key.len())
})
.map(Self)
}
pub(crate) fn public_key(&self) -> Option<[u8; PUBLIC_KEY_LEN]> {
let mut key = [0u8; PUBLIC_KEY_LEN];
let mut len = key.len();
let status =
unsafe { EVP_PKEY_get_raw_public_key(self.0.as_ptr(), key.as_mut_ptr(), &mut len) };
(status == SUCCESS && len == PUBLIC_KEY_LEN).then_some(key)
}
pub(crate) fn sign(&self, message: &[u8], context: &[u8]) -> Option<[u8; SIGNATURE_LEN]> {
let ctx = DigestContext::new()?;
let pctx = ctx.init(self, Operation::Sign)?;
let mut signature = [0u8; SIGNATURE_LEN];
let mut len = signature.len();
let status = unsafe {
if EVP_PKEY_CTX_set1_signature_context_string(pctx, context.as_ptr(), context.len())
!= SUCCESS
{
return None;
}
EVP_DigestSign(
ctx.0.as_ptr(),
signature.as_mut_ptr(),
&mut len,
message.as_ptr(),
message.len(),
)
};
(status == SUCCESS && len == SIGNATURE_LEN).then_some(signature)
}
pub(crate) fn verify(
&self,
message: &[u8],
context: &[u8],
signature: &[u8; SIGNATURE_LEN],
) -> bool {
let Some(ctx) = DigestContext::new() else {
return false;
};
let Some(pctx) = ctx.init(self, Operation::Verify) else {
return false;
};
unsafe {
EVP_PKEY_CTX_set1_signature_context_string(pctx, context.as_ptr(), context.len())
== SUCCESS
&& EVP_DigestVerify(
ctx.0.as_ptr(),
signature.as_ptr(),
signature.len(),
message.as_ptr(),
message.len(),
) == SUCCESS
}
}
}
#[derive(Clone, Copy)]
enum Operation {
Sign,
Verify,
}
struct DigestContext(NonNull<EVP_MD_CTX>);
impl DigestContext {
fn new() -> Option<Self> {
NonNull::new(unsafe { EVP_MD_CTX_new() }).map(Self)
}
fn init(&self, key: &MlDsa44, operation: Operation) -> Option<*mut EVP_PKEY_CTX> {
let mut pctx: *mut EVP_PKEY_CTX = null_mut();
let status = unsafe {
match operation {
Operation::Sign => EVP_DigestSignInit(
self.0.as_ptr(),
&mut pctx,
null(),
null_mut(),
key.0.as_ptr(),
),
Operation::Verify => EVP_DigestVerifyInit(
self.0.as_ptr(),
&mut pctx,
null(),
null_mut(),
key.0.as_ptr(),
),
}
};
(status == SUCCESS && !pctx.is_null()).then_some(pctx)
}
}
impl Drop for DigestContext {
fn drop(&mut self) {
unsafe { EVP_MD_CTX_free(self.0.as_ptr()) }
}
}