use rand::{CryptoRng, Rng, RngCore};
use crate::{
ecdh,
hacl::{self, ed25519, p256},
};
use self::rsa_pss::RsaPssSignature;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
SigningError,
InvalidSignature,
KeyGenError,
InvalidKey,
InputTooLarge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DigestAlgorithm {
Sha256,
Sha384,
Sha512,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Algorithm {
EcDsaP256(DigestAlgorithm),
Ed25519,
RsaPss(DigestAlgorithm),
}
#[derive(Debug)]
pub enum Signature {
EcDsaP256(EcDsaP256Signature),
Ed25519(Ed25519Signature),
RsaPss(RsaPssSignature),
}
impl Signature {
pub fn into_vec(self) -> Vec<u8> {
match self {
Signature::EcDsaP256(s) => {
let mut out = s.r.to_vec();
out.extend_from_slice(&s.s);
out
}
Signature::Ed25519(s) => s.signature.to_vec(),
Signature::RsaPss(s) => s.value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EcDsaP256Signature {
r: [u8; 32],
s: [u8; 32],
alg: Algorithm,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ed25519Signature {
signature: [u8; 64],
}
pub mod rsa_pss {
use libcrux_hacl::{
hacl_free, Hacl_RSAPSS_new_rsapss_load_pkey, Hacl_RSAPSS_new_rsapss_load_skey,
Hacl_RSAPSS_rsapss_sign, Hacl_RSAPSS_rsapss_verify,
};
use super::{DigestAlgorithm, Error};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RsaPssSignature {
pub(super) value: Vec<u8>,
}
impl RsaPssSignature {
pub fn as_bytes(&self) -> &[u8] {
&self.value
}
}
impl From<&[u8]> for RsaPssSignature {
fn from(value: &[u8]) -> Self {
Self {
value: value.to_vec(),
}
}
}
impl<const L: usize> From<[u8; L]> for RsaPssSignature {
fn from(value: [u8; L]) -> Self {
Self {
value: value.to_vec(),
}
}
}
impl From<Vec<u8>> for RsaPssSignature {
fn from(value: Vec<u8>) -> Self {
Self { value }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RsaPssPublicKey {
n: Vec<u8>,
}
fn rsa_pss_digest(hash_algorithm: DigestAlgorithm) -> u8 {
match hash_algorithm {
DigestAlgorithm::Sha256 => libcrux_hacl::Spec_Hash_Definitions_SHA2_256 as u8,
DigestAlgorithm::Sha384 => libcrux_hacl::Spec_Hash_Definitions_SHA2_384 as u8,
DigestAlgorithm::Sha512 => libcrux_hacl::Spec_Hash_Definitions_SHA2_512 as u8,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
pub enum RsaPssKeySize {
N2048 = 256,
N3072 = 384,
N4096 = 512,
N6144 = 768,
N8192 = 1024,
}
const E_BITS: u32 = 24;
const E: [u8; 3] = [0x01, 0x00, 0x01];
impl RsaPssPublicKey {
pub fn new(key_size: RsaPssKeySize, n: &[u8]) -> Result<Self, Error> {
if n.len() != key_size as usize {
return Err(Error::InvalidKey);
}
Ok(Self { n: n.into() })
}
#[must_use = "The result of the signature verification must be used."]
pub fn verify(
&self,
hash_algorithm: DigestAlgorithm,
signature: &RsaPssSignature,
msg: &[u8],
salt_len: usize,
) -> Result<(), Error> {
let key_size_bits = (self.n.len() as u32) * 8;
unsafe {
let pkey = Hacl_RSAPSS_new_rsapss_load_pkey(
key_size_bits,
E_BITS,
self.n.as_ptr() as _,
E.as_ptr() as _,
);
if Hacl_RSAPSS_rsapss_verify(
rsa_pss_digest(hash_algorithm),
key_size_bits,
E_BITS,
pkey,
salt_len as u32,
signature.value.len() as u32,
signature.value.as_ptr() as _,
msg.len() as u32,
msg.as_ptr() as _,
) {
return Ok(());
}
}
Err(Error::InvalidSignature)
}
}
pub struct RsaPssPrivateKey<'a> {
pk: &'a RsaPssPublicKey,
d: Vec<u8>,
}
impl<'a> RsaPssPrivateKey<'a> {
pub fn new(pk: &'a RsaPssPublicKey, d: &[u8]) -> Result<Self, Error> {
if pk.n.len() != d.len() {
return Err(Error::InvalidKey);
}
Ok(Self { pk, d: d.into() })
}
pub fn sign(
&self,
hash_algorithm: DigestAlgorithm,
salt: &[u8],
msg: &[u8],
) -> Result<RsaPssSignature, Error> {
if salt.len() > (u32::MAX as usize) || msg.len() > (u32::MAX as usize) {
return Err(Error::InputTooLarge);
}
let key_len = self.d.len();
let mut signature = vec![0; key_len];
let key_size_bits = (key_len as u32) * 8;
unsafe {
let s_key = Hacl_RSAPSS_new_rsapss_load_skey(
key_size_bits,
E_BITS,
key_size_bits,
self.pk.n.as_ptr() as _,
E.as_ptr() as _,
self.d.as_ptr() as _,
);
if !Hacl_RSAPSS_rsapss_sign(
rsa_pss_digest(hash_algorithm),
key_size_bits,
E_BITS,
key_size_bits,
s_key,
salt.len() as u32,
salt.as_ptr() as _,
msg.len() as u32,
msg.as_ptr() as _,
signature.as_mut_ptr(),
) {
hacl_free(s_key as _);
return Err(Error::SigningError);
}
hacl_free(s_key as _);
}
Ok(RsaPssSignature { value: signature })
}
}
}
impl Ed25519Signature {
pub fn from_bytes(signature: [u8; 64]) -> Self {
Self { signature }
}
pub fn from_slice(bytes: &[u8]) -> Result<Self, Error> {
Ok(Self {
signature: bytes.try_into().map_err(|_| Error::InvalidSignature)?,
})
}
pub fn as_bytes(&self) -> &[u8; 64] {
&self.signature
}
}
impl EcDsaP256Signature {
pub fn from_raw(r: [u8; 32], s: [u8; 32], alg: Algorithm) -> Self {
Self { r, s, alg }
}
pub fn from_bytes(signature_bytes: [u8; 64], alg: Algorithm) -> Self {
Self {
r: signature_bytes[0..32].try_into().unwrap(),
s: signature_bytes[32..].try_into().unwrap(),
alg,
}
}
pub fn as_bytes(&self) -> (&[u8; 32], &[u8; 32]) {
(&self.r, &self.s)
}
}
fn ecdsa_p256_sign_prep(
private_key: &[u8],
rng: &mut (impl CryptoRng + RngCore),
) -> Result<(ecdh::p256::PrivateKey, [u8; 32]), Error> {
let private_key = p256::validate_scalar_slice(private_key).map_err(|_| Error::SigningError)?;
let mut nonce = [0u8; 32];
loop {
rng.try_fill_bytes(&mut nonce)
.map_err(|_| Error::SigningError)?;
if p256::validate_scalar_slice(&nonce).is_ok() {
break;
}
}
Ok((private_key, nonce))
}
fn ecdsa_p256_sign_post(signature: [u8; 64], alg: Algorithm) -> Result<Signature, Error> {
Ok(Signature::EcDsaP256(EcDsaP256Signature {
r: signature[..32]
.try_into()
.map_err(|_| Error::SigningError)?,
s: signature[32..]
.try_into()
.map_err(|_| Error::SigningError)?,
alg,
}))
}
fn into_signing_error(_e: impl Into<hacl::Error>) -> Error {
Error::SigningError
}
pub fn sign(
alg: Algorithm,
payload: &[u8],
private_key: &[u8],
rng: &mut (impl CryptoRng + RngCore),
) -> Result<Signature, Error> {
let signature = match alg {
Algorithm::EcDsaP256(DigestAlgorithm::Sha256) => {
let (private_key, nonce) = ecdsa_p256_sign_prep(private_key, rng)?;
ecdsa_p256_sign_post(
p256::ecdsa::sign_sha256(payload, private_key.as_ref(), &nonce)
.map_err(into_signing_error)?,
alg,
)?
}
Algorithm::EcDsaP256(DigestAlgorithm::Sha384) => {
let (private_key, nonce) = ecdsa_p256_sign_prep(private_key, rng)?;
ecdsa_p256_sign_post(
p256::ecdsa::sign_sha384(payload, private_key.as_ref(), &nonce)
.map_err(into_signing_error)?,
alg,
)?
}
Algorithm::EcDsaP256(DigestAlgorithm::Sha512) => {
let (private_key, nonce) = ecdsa_p256_sign_prep(private_key, rng)?;
ecdsa_p256_sign_post(
p256::ecdsa::sign_sha512(payload, private_key.as_ref(), &nonce)
.map_err(into_signing_error)?,
alg,
)?
}
Algorithm::Ed25519 => {
let signature = ed25519::sign(
payload,
private_key.try_into().map_err(|_| Error::SigningError)?,
)
.map_err(into_signing_error)?;
Signature::Ed25519(Ed25519Signature { signature })
}
Algorithm::RsaPss(_) => {
todo!()
}
};
Ok(signature)
}
fn into_verify_error(_e: impl Into<hacl::Error>) -> Error {
Error::InvalidSignature
}
fn ecdsa_p256_verify_prep(public_key: &[u8]) -> Result<[u8; 64], Error> {
if public_key.is_empty() {
return Err(Error::SigningError);
}
let pk = if let Ok(pk) = p256::uncompressed_to_coordinates(public_key) {
pk
} else {
if let Ok(pk) = p256::compressed_to_coordinates(public_key) {
pk
} else {
public_key.try_into().map_err(|_| Error::InvalidSignature)?
}
};
p256::validate_point(ecdh::p256::PublicKey(pk))
.map(|()| pk)
.map_err(into_verify_error)
}
pub fn verify(payload: &[u8], signature: &Signature, public_key: &[u8]) -> Result<(), Error> {
match signature {
Signature::EcDsaP256(signature) => match signature.alg {
Algorithm::EcDsaP256(DigestAlgorithm::Sha256) => {
let pk = ecdsa_p256_verify_prep(public_key)?;
p256::ecdsa::verify_sha256(payload, &pk, &signature.r, &signature.s)
}
Algorithm::EcDsaP256(DigestAlgorithm::Sha384) => {
let pk = ecdsa_p256_verify_prep(public_key)?;
p256::ecdsa::verify_sha384(payload, &pk, &signature.r, &signature.s)
}
Algorithm::EcDsaP256(DigestAlgorithm::Sha512) => {
let pk = ecdsa_p256_verify_prep(public_key)?;
p256::ecdsa::verify_sha512(payload, &pk, &signature.r, &signature.s)
}
_ => Err(p256::Error::InvalidInput),
}
.map_err(into_verify_error),
Signature::Ed25519(signature) => {
let public_key = public_key.try_into().map_err(|_| Error::InvalidSignature)?;
ed25519::verify(payload, public_key, &signature.signature).map_err(into_verify_error)
}
Signature::RsaPss(_) => todo!(),
}
}
pub fn key_gen(
alg: Algorithm,
rng: &mut (impl CryptoRng + Rng),
) -> Result<(Vec<u8>, Vec<u8>), Error> {
match alg {
Algorithm::EcDsaP256(_) => {
ecdh::key_gen(ecdh::Algorithm::P256, rng).map_err(|_| Error::KeyGenError)
}
Algorithm::Ed25519 => {
const LIMIT: usize = 100;
let mut sk = [0u8; 32];
for _ in 0..LIMIT {
rng.try_fill_bytes(&mut sk)
.map_err(|_| Error::KeyGenError)?;
if sk.iter().all(|&b| b == 0) {
sk = [0u8; 32];
continue;
}
sk[0] = sk[0] & 248u8;
sk[31] = sk[31] & 127u8;
sk[31] = sk[31] | 64u8;
break;
}
let pk = ed25519::secret_to_public(&sk);
Ok((sk.to_vec(), pk.to_vec()))
}
Algorithm::RsaPss(_) => todo!(),
}
}