use ed448_goldilocks::SigningKey as InnerSigningKey;
use ed448_goldilocks::VerifyingKey as InnerVerifyingKey;
use ed448_goldilocks::Signature as InnerSignature;
use crate::error::{CryptoError, Result};
pub const SECRET_KEY_SIZE: usize = 57;
pub const PUBLIC_KEY_SIZE: usize = 57;
pub const SIGNATURE_SIZE: usize = 114;
pub struct SigningKey(InnerSigningKey);
impl SigningKey {
pub fn from_bytes(bytes: &[u8; SECRET_KEY_SIZE]) -> Result<Self> {
InnerSigningKey::try_from(bytes.as_slice())
.map(SigningKey)
.map_err(|e| CryptoError::InvalidKey(format!("invalid Ed448 secret key: {e}")))
}
pub fn sign(&self, message: &[u8]) -> Signature {
Signature(self.0.sign_raw(message))
}
pub fn verifying_key(&self) -> VerifyingKey {
VerifyingKey(self.0.verifying_key())
}
pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
let mut out = [0u8; SECRET_KEY_SIZE];
out.copy_from_slice(&self.0.to_bytes()[..]);
out
}
}
impl Drop for SigningKey {
fn drop(&mut self) {
}
}
impl std::fmt::Debug for SigningKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SigningKey")
.field("public_key", &hex::encode(self.verifying_key().to_bytes()))
.finish_non_exhaustive()
}
}
pub struct VerifyingKey(InnerVerifyingKey);
impl Clone for VerifyingKey {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl std::fmt::Debug for VerifyingKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VerifyingKey")
.field("bytes", &hex::encode(self.0.to_bytes()))
.finish()
}
}
impl VerifyingKey {
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_SIZE]) -> Result<Self> {
InnerVerifyingKey::from_bytes(bytes)
.map(VerifyingKey)
.map_err(|e| CryptoError::InvalidKey(format!("invalid Ed448 public key: {e}")))
}
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
self.0.to_bytes()
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<()> {
self.0
.verify_raw(&signature.0, message)
.map_err(|_| CryptoError::AuthenticationFailed)
}
}
pub struct Signature(InnerSignature);
impl Signature {
pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] {
self.0.to_bytes()
}
pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Result<Self> {
InnerSignature::try_from(bytes.as_slice())
.map(Signature)
.map_err(|e| CryptoError::InvalidParameter(format!("invalid Ed448 signature: {e}")))
}
}
impl Clone for Signature {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl std::fmt::Debug for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Signature")
.field("bytes", &hex::encode(self.to_bytes()))
.finish()
}
}
const _: () = {
assert!(SECRET_KEY_SIZE == 57);
assert!(PUBLIC_KEY_SIZE == 57);
assert!(SIGNATURE_SIZE == 114);
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn key_sizes() {
assert_eq!(SECRET_KEY_SIZE, 57);
assert_eq!(PUBLIC_KEY_SIZE, 57);
assert_eq!(SIGNATURE_SIZE, 114);
}
fn sk(seed_byte: u8) -> SigningKey {
let seed = [seed_byte; SECRET_KEY_SIZE];
SigningKey::from_bytes(&seed).expect("test seed must be valid")
}
#[test]
fn sign_verify_roundtrip() {
let sk = sk(0x42);
let pk = sk.verifying_key();
let msg = b"hello Ed448";
let sig = sk.sign(msg);
assert!(pk.verify(msg, &sig).is_ok());
}
#[test]
fn verify_rejects_tampered_message() {
let sk = sk(0x42);
let pk = sk.verifying_key();
let sig = sk.sign(b"original");
assert!(pk.verify(b"tampered", &sig).is_err());
}
#[test]
fn verify_rejects_tampered_signature() {
let sk = sk(0x42);
let pk = sk.verifying_key();
let mut sig = sk.sign(b"x");
let mut bytes = sig.to_bytes();
bytes[10] ^= 0x01;
let bad_sig = Signature::from_bytes(&bytes).unwrap();
assert!(pk.verify(b"x", &bad_sig).is_err());
}
#[test]
fn deterministic_signatures() {
let sk1 = sk(0x42);
let sk2 = sk(0x42);
let msg = b"deterministic test";
let sig1 = sk1.sign(msg);
let sig2 = sk2.sign(msg);
assert_eq!(sig1.to_bytes(), sig2.to_bytes());
}
#[test]
fn public_key_derivation() {
let sk = sk(0x42);
let pk = sk.verifying_key();
let bytes = pk.to_bytes();
let restored = VerifyingKey::from_bytes(&bytes).unwrap();
assert_eq!(restored.to_bytes(), pk.to_bytes());
}
}