use ed25519_dalek::{SigningKey as DalekSigningKey, VerifyingKey as DalekVerifyingKey};
use crate::error::AttestationError;
#[derive(Clone)]
pub struct SigningKey {
inner: DalekSigningKey,
}
impl SigningKey {
#[must_use]
pub fn generate() -> Self {
let mut rng = rand::thread_rng();
Self {
inner: DalekSigningKey::generate(&mut rng),
}
}
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, AttestationError> {
Ok(Self {
inner: DalekSigningKey::from_bytes(bytes),
})
}
#[must_use]
pub fn to_bytes(&self) -> [u8; 32] {
self.inner.to_bytes()
}
#[must_use]
pub fn verifying_key(&self) -> VerifyingKey {
VerifyingKey {
inner: self.inner.verifying_key(),
}
}
pub(crate) fn as_dalek(&self) -> &DalekSigningKey {
&self.inner
}
}
impl std::fmt::Debug for SigningKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SigningKey")
.field("public_key", &self.verifying_key())
.finish_non_exhaustive()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct VerifyingKey {
inner: DalekVerifyingKey,
}
impl VerifyingKey {
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, AttestationError> {
DalekVerifyingKey::from_bytes(bytes)
.map(|inner| Self { inner })
.map_err(|e| AttestationError::InvalidKeyFormat {
reason: e.to_string(),
})
}
#[must_use]
pub fn to_bytes(&self) -> [u8; 32] {
self.inner.to_bytes()
}
}
impl std::fmt::Debug for VerifyingKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes = self.to_bytes();
write!(
f,
"VerifyingKey({:02x}{:02x}{:02x}{:02x}...)",
bytes[0], bytes[1], bytes[2], bytes[3]
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn signing_key_generates_unique_keys() {
let key1 = SigningKey::generate();
let key2 = SigningKey::generate();
assert_ne!(key1.to_bytes(), key2.to_bytes());
}
#[test]
fn signing_key_roundtrip_bytes() {
let original = SigningKey::generate();
let bytes = original.to_bytes();
let recovered = SigningKey::from_bytes(&bytes).unwrap();
assert_eq!(original.to_bytes(), recovered.to_bytes());
}
#[test]
fn verifying_key_roundtrip_bytes() {
let signing_key = SigningKey::generate();
let verifying_key = signing_key.verifying_key();
let bytes = verifying_key.to_bytes();
let recovered = VerifyingKey::from_bytes(&bytes).unwrap();
assert_eq!(verifying_key, recovered);
}
#[test]
fn verifying_key_from_invalid_bytes_fails() {
let test_cases = [
[0xFFu8; 32],
[0xEEu8; 32],
{
let mut b = [0u8; 32];
b[31] = 0x80; b
},
];
let mut found_invalid = false;
for invalid_bytes in &test_cases {
let result = VerifyingKey::from_bytes(invalid_bytes);
if result.is_err() {
found_invalid = true;
assert!(matches!(
result,
Err(AttestationError::InvalidKeyFormat { .. })
));
break;
}
}
if !found_invalid {
let signing_key = SigningKey::generate();
let valid_key = signing_key.verifying_key();
assert!(VerifyingKey::from_bytes(&valid_key.to_bytes()).is_ok());
}
}
#[test]
fn signing_key_debug_shows_public_key() {
let key = SigningKey::generate();
let debug_output = format!("{key:?}");
assert!(debug_output.contains("SigningKey"));
assert!(debug_output.contains("VerifyingKey"));
}
#[test]
fn verifying_key_debug_shows_partial_bytes() {
let signing_key = SigningKey::generate();
let verifying_key = signing_key.verifying_key();
let debug_output = format!("{verifying_key:?}");
assert!(debug_output.contains("VerifyingKey("));
assert!(debug_output.contains("..."));
}
}