use commonware_codec::{DecodeExt, Encode};
use commonware_cryptography::{
Signer as _, Verifier as _,
ed25519::{PrivateKey, PublicKey, Signature},
};
pub mod approval;
pub mod handoff;
pub mod toolcall;
pub const NAMESPACE: &[u8] = b"polychrome.v1";
#[derive(Clone)]
pub struct Signer(PrivateKey);
impl Signer {
#[must_use]
pub fn from_seed(seed: u64) -> Self {
Self(PrivateKey::from_seed(seed))
}
#[must_use]
pub fn public_key_bytes(&self) -> Vec<u8> {
self.0.public_key().encode().to_vec()
}
#[must_use]
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
self.0.sign(NAMESPACE, msg).encode().to_vec()
}
}
#[must_use]
pub fn verify(public_key: &[u8], msg: &[u8], sig: &[u8]) -> bool {
let Ok(pk) = PublicKey::decode(public_key) else {
return false;
};
let Ok(sig) = Signature::decode(sig) else {
return false;
};
pk.verify(NAMESPACE, msg, &sig)
}
#[cfg(test)]
mod tests {
#![allow(clippy::pedantic, clippy::nursery, missing_docs)]
use super::*;
#[test]
fn sign_then_verify_round_trips() {
let signer = Signer::from_seed(1);
let pk = signer.public_key_bytes();
let msg = b"tool-call canonical bytes";
let sig = signer.sign(msg);
assert!(verify(&pk, msg, &sig));
}
#[test]
fn wrong_message_fails() {
let signer = Signer::from_seed(1);
let pk = signer.public_key_bytes();
let sig = signer.sign(b"original");
assert!(!verify(&pk, b"tampered", &sig));
}
#[test]
fn wrong_key_fails() {
let signer = Signer::from_seed(1);
let other = Signer::from_seed(2);
let msg = b"msg";
let sig = signer.sign(msg);
assert!(!verify(&other.public_key_bytes(), msg, &sig));
}
#[test]
fn garbage_inputs_return_false_not_panic() {
assert!(!verify(b"not-a-key", b"m", b"not-a-sig"));
assert!(!verify(&[], &[], &[]));
}
}