use crate::proto::ids::{Key32, Sig64};
use crate::proto::record::RecordContext;
use ed25519_dalek::{Signer, SigningKey};
use zeroize::Zeroizing;
use crate::keys::random::random_bytes;
#[derive(Clone)]
pub struct WriterKey(SigningKey);
impl std::fmt::Debug for WriterKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "WriterKey(pub {:?})", self.public())
}
}
impl WriterKey {
pub fn generate() -> WriterKey {
WriterKey(SigningKey::from_bytes(&random_bytes::<32>()))
}
pub fn from_bytes(bytes: &[u8; 32]) -> WriterKey {
WriterKey(SigningKey::from_bytes(bytes))
}
pub(crate) fn to_bytes(&self) -> Zeroizing<[u8; 32]> {
Zeroizing::new(self.0.to_bytes())
}
pub fn public(&self) -> Key32 {
Key32(self.0.verifying_key().to_bytes())
}
pub fn sign(&self, ctx: &RecordContext) -> Sig64 {
Sig64(self.0.sign(&ctx.signing_input()).to_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::proto::ids::{NameHmac, VaultId};
use crate::proto::record::RecordKind;
#[test]
fn signatures_verify_only_under_the_matching_key_and_context() {
let w = WriterKey::generate();
let ctx = RecordContext::new(
VaultId([1; 16]),
1,
RecordKind::Secret,
NameHmac([2; 32]),
1,
0,
b"n",
Some(b"v"),
);
let sig = w.sign(&ctx);
assert!(ctx.verify(&w.public(), &sig).is_ok());
assert!(ctx.verify(&WriterKey::generate().public(), &sig).is_err());
let other = RecordContext {
version: 2,
..ctx.clone()
};
assert!(other.verify(&w.public(), &sig).is_err());
assert!(!format!("{w:?}").contains(&hex::encode(*w.to_bytes())));
}
}