use crate::CoreError;
use ed25519_dalek::pkcs8::{DecodePrivateKey, EncodePrivateKey};
use ed25519_dalek::{Signer as DalekSigner, SigningKey, Verifier as DalekVerifier, VerifyingKey};
use fips204::ml_dsa_87;
use fips204::traits::{KeyGen, SerDes, Signer, Verifier};
use zeroize::{Zeroize, ZeroizeOnDrop};
pub const ED25519_PUBLIC_KEY_SIZE: usize = 32;
pub const ED25519_SIGNATURE_SIZE: usize = 64;
pub mod ed25519_consts {
pub use super::{ED25519_PUBLIC_KEY_SIZE, ED25519_SIGNATURE_SIZE};
}
pub const ML_DSA_87_PRIVATE_KEY_SIZE: usize = 4896;
pub const ML_DSA_87_PUBLIC_KEY_SIZE: usize = 2592;
pub const ML_DSA_87_SIGNATURE_SIZE: usize = 4627;
pub mod pq2025_consts {
pub use super::{
ML_DSA_87_PRIVATE_KEY_SIZE, ML_DSA_87_PUBLIC_KEY_SIZE, ML_DSA_87_SIGNATURE_SIZE,
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SigningAlgorithm {
Ed25519,
Pq2025,
}
impl SigningAlgorithm {
pub fn as_str(&self) -> &'static str {
match self {
SigningAlgorithm::Ed25519 => "ed25519",
SigningAlgorithm::Pq2025 => "pq2025",
}
}
pub fn from_wire_str(raw: &str) -> Option<Self> {
match raw {
"ed25519" | "ring-Ed25519" => Some(SigningAlgorithm::Ed25519),
"pq2025" => Some(SigningAlgorithm::Pq2025),
_ => None,
}
}
}
impl std::fmt::Display for SigningAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub trait DetachedSigner: Send + Sync {
fn algorithm(&self) -> SigningAlgorithm;
fn public_key(&self) -> &[u8];
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, CoreError>;
fn clear_secrets(&mut self);
fn export_private_key_bytes(&self) -> Result<Vec<u8>, CoreError>;
}
pub struct Pq2025Signer {
private_key: Option<ZeroizingPq2025Private>,
public_key: Vec<u8>,
}
struct ZeroizingPq2025Private {
bytes: [u8; ML_DSA_87_PRIVATE_KEY_SIZE],
}
impl Zeroize for ZeroizingPq2025Private {
fn zeroize(&mut self) {
self.bytes.zeroize();
}
}
impl Drop for ZeroizingPq2025Private {
fn drop(&mut self) {
self.zeroize();
}
}
impl ZeroizeOnDrop for ZeroizingPq2025Private {}
impl Pq2025Signer {
pub fn generate() -> Result<Self, CoreError> {
let (pk, sk) = ml_dsa_87::KG::try_keygen().map_err(|e| {
CoreError::EncryptionFailed(format!("ML-DSA-87 key generation failed: {e}"))
})?;
let sk_bytes_arr = sk.into_bytes();
let pk_bytes_vec = pk.into_bytes().to_vec();
Ok(Self {
private_key: Some(ZeroizingPq2025Private {
bytes: sk_bytes_arr,
}),
public_key: pk_bytes_vec,
})
}
pub fn from_private_bytes(private_key: &[u8]) -> Result<Self, CoreError> {
if private_key.len() != ML_DSA_87_PRIVATE_KEY_SIZE {
return Err(CoreError::MalformedKey(format!(
"ML-DSA-87 private key: expected {} bytes, got {}",
ML_DSA_87_PRIVATE_KEY_SIZE,
private_key.len()
)));
}
let mut bytes = [0u8; ML_DSA_87_PRIVATE_KEY_SIZE];
bytes.copy_from_slice(private_key);
let sk = ml_dsa_87::PrivateKey::try_from_bytes(bytes).map_err(|e| {
CoreError::MalformedKey(format!("ML-DSA-87 private key deserialization failed: {e}"))
})?;
let pk = sk.get_public_key();
let public_key = pk.into_bytes().to_vec();
Ok(Self {
private_key: Some(ZeroizingPq2025Private { bytes }),
public_key,
})
}
pub fn export_private_bytes(&self) -> Result<Vec<u8>, CoreError> {
let inner = self.private_key.as_ref().ok_or(CoreError::Locked)?;
Ok(inner.bytes.to_vec())
}
pub fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<(), CoreError> {
if public_key.len() != ML_DSA_87_PUBLIC_KEY_SIZE {
return Err(CoreError::MalformedKey(format!(
"ML-DSA-87 public key: expected {} bytes, got {}",
ML_DSA_87_PUBLIC_KEY_SIZE,
public_key.len()
)));
}
if signature.len() != ML_DSA_87_SIGNATURE_SIZE {
return Err(CoreError::SignatureInvalid(format!(
"ML-DSA-87 signature: expected {} bytes, got {}",
ML_DSA_87_SIGNATURE_SIZE,
signature.len()
)));
}
let mut pk_arr = [0u8; ML_DSA_87_PUBLIC_KEY_SIZE];
pk_arr.copy_from_slice(public_key);
let pk = ml_dsa_87::PublicKey::try_from_bytes(pk_arr).map_err(|e| {
CoreError::MalformedKey(format!("ML-DSA-87 public key deserialization failed: {e}"))
})?;
let mut sig_arr = [0u8; ML_DSA_87_SIGNATURE_SIZE];
sig_arr.copy_from_slice(signature);
if pk.verify(message, &sig_arr, b"") {
Ok(())
} else {
Err(CoreError::SignatureInvalid(
"ML-DSA-87 verification failed".into(),
))
}
}
}
impl std::fmt::Debug for Pq2025Signer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Pq2025Signer")
.field("algorithm", &SigningAlgorithm::Pq2025)
.field("public_key_len", &self.public_key.len())
.field("unlocked", &self.private_key.is_some())
.finish()
}
}
impl DetachedSigner for Pq2025Signer {
fn algorithm(&self) -> SigningAlgorithm {
SigningAlgorithm::Pq2025
}
fn public_key(&self) -> &[u8] {
&self.public_key
}
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, CoreError> {
let priv_bytes = self.private_key.as_ref().ok_or(CoreError::Locked)?.bytes;
let sk = ml_dsa_87::PrivateKey::try_from_bytes(priv_bytes).map_err(|e| {
CoreError::MalformedKey(format!("ML-DSA-87 private key deserialization failed: {e}"))
})?;
let sig = sk
.try_sign(message, b"")
.map_err(|e| CoreError::EncryptionFailed(format!("ML-DSA-87 signing failed: {e}")))?;
Ok(sig.to_vec())
}
fn clear_secrets(&mut self) {
self.private_key = None;
}
fn export_private_key_bytes(&self) -> Result<Vec<u8>, CoreError> {
self.export_private_bytes()
}
}
pub struct Ed25519DalekSigner {
signing_key: Option<SigningKey>,
public_key: [u8; ED25519_PUBLIC_KEY_SIZE],
}
impl Ed25519DalekSigner {
pub fn generate() -> Result<Self, CoreError> {
let mut csprng = aes_gcm::aead::OsRng;
let signing_key = SigningKey::generate(&mut csprng);
let public_key = signing_key.verifying_key().to_bytes();
Ok(Self {
signing_key: Some(signing_key),
public_key,
})
}
pub fn from_pkcs8(pkcs8_bytes: &[u8]) -> Result<Self, CoreError> {
let signing_key = SigningKey::from_pkcs8_der(pkcs8_bytes)
.map_err(|e| CoreError::MalformedKey(format!("Ed25519 PKCS#8 decode failed: {e}")))?;
let public_key = signing_key.verifying_key().to_bytes();
Ok(Self {
signing_key: Some(signing_key),
public_key,
})
}
pub fn from_private_scalar(scalar: &[u8]) -> Result<Self, CoreError> {
if scalar.len() != 32 {
return Err(CoreError::MalformedKey(format!(
"Ed25519 private scalar: expected 32 bytes, got {}",
scalar.len()
)));
}
let mut arr = [0u8; 32];
arr.copy_from_slice(scalar);
let signing_key = SigningKey::from_bytes(&arr);
arr.zeroize();
let public_key = signing_key.verifying_key().to_bytes();
Ok(Self {
signing_key: Some(signing_key),
public_key,
})
}
pub fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<(), CoreError> {
if public_key.len() != ED25519_PUBLIC_KEY_SIZE {
return Err(CoreError::MalformedKey(format!(
"Ed25519 public key: expected {} bytes, got {}",
ED25519_PUBLIC_KEY_SIZE,
public_key.len()
)));
}
if signature.len() != ED25519_SIGNATURE_SIZE {
return Err(CoreError::SignatureInvalid(format!(
"Ed25519 signature: expected {} bytes, got {}",
ED25519_SIGNATURE_SIZE,
signature.len()
)));
}
let mut pk_arr = [0u8; ED25519_PUBLIC_KEY_SIZE];
pk_arr.copy_from_slice(public_key);
let verifying_key = VerifyingKey::from_bytes(&pk_arr).map_err(|e| {
CoreError::MalformedKey(format!("Ed25519 public key deserialization failed: {e}"))
})?;
let mut sig_arr = [0u8; ED25519_SIGNATURE_SIZE];
sig_arr.copy_from_slice(signature);
let sig = ed25519_dalek::Signature::from_bytes(&sig_arr);
verifying_key
.verify(message, &sig)
.map_err(|e| CoreError::SignatureInvalid(format!("Ed25519 verification failed: {e}")))
}
pub fn export_pkcs8_v2(&self) -> Result<Vec<u8>, CoreError> {
let key = self.signing_key.as_ref().ok_or(CoreError::Locked)?;
let der = key.to_pkcs8_der().map_err(|e| {
CoreError::EncryptionFailed(format!("Ed25519 PKCS#8 export failed: {e}"))
})?;
Ok(der.as_bytes().to_vec())
}
}
impl std::fmt::Debug for Ed25519DalekSigner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ed25519DalekSigner")
.field("algorithm", &SigningAlgorithm::Ed25519)
.field("public_key_len", &self.public_key.len())
.field("unlocked", &self.signing_key.is_some())
.finish()
}
}
impl DetachedSigner for Ed25519DalekSigner {
fn algorithm(&self) -> SigningAlgorithm {
SigningAlgorithm::Ed25519
}
fn public_key(&self) -> &[u8] {
&self.public_key
}
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, CoreError> {
let key = self.signing_key.as_ref().ok_or(CoreError::Locked)?;
let sig = key.sign(message);
Ok(sig.to_bytes().to_vec())
}
fn clear_secrets(&mut self) {
self.signing_key = None;
}
fn export_private_key_bytes(&self) -> Result<Vec<u8>, CoreError> {
self.export_pkcs8_v2()
}
}