use crate::traits::RandomSource;
use crate::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SignatureAlgorithm {
EcdsaP256Sha256,
EcdsaP384Sha384,
EcdsaP521Sha512,
Ed25519,
RsaPkcs1Sha256,
RsaPkcs1Sha384,
RsaPkcs1Sha512,
RsaPssSha256,
RsaPssSha384,
RsaPssSha512,
MlDsa44,
MlDsa65,
MlDsa87,
}
impl SignatureAlgorithm {
pub const ALL: &'static [SignatureAlgorithm] = &[
Self::EcdsaP256Sha256,
Self::EcdsaP384Sha384,
Self::EcdsaP521Sha512,
Self::Ed25519,
Self::RsaPkcs1Sha256,
Self::RsaPkcs1Sha384,
Self::RsaPkcs1Sha512,
Self::RsaPssSha256,
Self::RsaPssSha384,
Self::RsaPssSha512,
Self::MlDsa44,
Self::MlDsa65,
Self::MlDsa87,
];
pub const fn id(self) -> &'static str {
match self {
Self::EcdsaP256Sha256 => "ecdsa-p256-sha256",
Self::EcdsaP384Sha384 => "ecdsa-p384-sha384",
Self::EcdsaP521Sha512 => "ecdsa-p521-sha512",
Self::Ed25519 => "ed25519",
Self::RsaPkcs1Sha256 => "rsa-pkcs1-sha256",
Self::RsaPkcs1Sha384 => "rsa-pkcs1-sha384",
Self::RsaPkcs1Sha512 => "rsa-pkcs1-sha512",
Self::RsaPssSha256 => "rsa-pss-sha256",
Self::RsaPssSha384 => "rsa-pss-sha384",
Self::RsaPssSha512 => "rsa-pss-sha512",
Self::MlDsa44 => "ml-dsa-44",
Self::MlDsa65 => "ml-dsa-65",
Self::MlDsa87 => "ml-dsa-87",
}
}
pub fn from_id(id: &str) -> Option<Self> {
Self::ALL.iter().copied().find(|a| a.id() == id)
}
pub const fn max_signature_len(self) -> usize {
match self {
Self::EcdsaP256Sha256 => 72,
Self::EcdsaP384Sha384 => 104,
Self::EcdsaP521Sha512 => 139,
Self::Ed25519 => 64,
Self::RsaPkcs1Sha256
| Self::RsaPkcs1Sha384
| Self::RsaPkcs1Sha512
| Self::RsaPssSha256
| Self::RsaPssSha384
| Self::RsaPssSha512 => 512,
Self::MlDsa44 => 2420,
Self::MlDsa65 => 3309,
Self::MlDsa87 => 4627,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Custody {
Software,
Hardware,
Service,
}
impl Custody {
pub const fn id(self) -> &'static str {
match self {
Self::Software => "software",
Self::Hardware => "hardware",
Self::Service => "service",
}
}
}
pub trait Signer: Send + Sync {
fn algorithms(&self) -> &[SignatureAlgorithm];
fn public_key(&self) -> &[u8];
fn custody(&self) -> Custody;
fn sign(
&self,
algorithm: SignatureAlgorithm,
message: &[u8],
rng: &mut dyn RandomSource,
out: &mut [u8],
) -> Result<usize>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identifiers_are_unique_and_round_trip() {
for (i, a) in SignatureAlgorithm::ALL.iter().enumerate() {
assert_eq!(SignatureAlgorithm::from_id(a.id()), Some(*a));
assert!(SignatureAlgorithm::ALL[..i]
.iter()
.all(|b| b.id() != a.id()));
}
assert_eq!(SignatureAlgorithm::from_id("ecdsa-p256-sha1"), None);
}
#[test]
fn a_signer_can_be_a_trait_object() {
struct Fixed;
impl Signer for Fixed {
fn algorithms(&self) -> &[SignatureAlgorithm] {
&[SignatureAlgorithm::Ed25519]
}
fn public_key(&self) -> &[u8] {
&[]
}
fn custody(&self) -> Custody {
Custody::Hardware
}
fn sign(
&self,
_: SignatureAlgorithm,
_: &[u8],
_: &mut dyn RandomSource,
out: &mut [u8],
) -> Result<usize> {
out[0] = 7;
Ok(1)
}
}
struct NoRng;
impl RandomSource for NoRng {
fn fill(&mut self, _: &mut [u8]) -> Result<()> {
Ok(())
}
}
fn shareable<T: Send + Sync + ?Sized>(_: &T) {}
let signer: &dyn Signer = &Fixed;
shareable(signer);
let mut out = [0u8; 4];
assert_eq!(
signer
.sign(SignatureAlgorithm::Ed25519, b"m", &mut NoRng, &mut out)
.unwrap(),
1
);
assert_eq!(signer.custody().id(), "hardware");
}
}