use std::fmt;
use arcanum_hash::prelude::{Blake3, Hasher as ArcanumHasher};
use arcanum_signatures::ed25519::Ed25519BatchVerifier;
use arcanum_signatures::prelude::{
Ed25519Signature, Ed25519SigningKey, Ed25519VerifyingKey, Signature as ArcanumSignature,
SigningKey as ArcanumSigningKey, VerifyingKey as ArcanumVerifyingKey,
};
use arcanum_signatures::BatchVerifier;
use serde::{Deserialize, Serialize};
use crate::error::{Error, Result};
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Hash([u8; 32]);
impl Hash {
pub const ZERO: Self = Self([0u8; 32]);
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn from_hex(s: &str) -> Result<Self> {
let bytes = hex::decode(s)?;
if bytes.len() != 32 {
return Err(Error::invalid_hash(format!(
"expected 32 bytes, got {}",
bytes.len()
)));
}
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Ok(Self(arr))
}
pub fn to_hex(&self) -> String {
hex::encode(self.0)
}
pub fn is_zero(&self) -> bool {
self == &Self::ZERO
}
}
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Hash({})", &self.to_hex()[..16])
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
pub fn hash(data: &[u8]) -> Hash {
let output = Blake3::hash(data);
let bytes: [u8; 32] = output.to_array().expect("BLAKE3 always outputs 32 bytes");
Hash(bytes)
}
pub fn hash_pair(left: Hash, right: Hash) -> Hash {
let mut hasher = Blake3::new();
hasher.update(left.as_bytes());
hasher.update(right.as_bytes());
let output = hasher.finalize();
let bytes: [u8; 32] = output.to_array().expect("BLAKE3 always outputs 32 bytes");
Hash(bytes)
}
pub fn hash_all<T: AsRef<[u8]>>(items: &[T]) -> Hash {
let mut hasher = Blake3::new();
for item in items {
hasher.update(item.as_ref());
}
let output = hasher.finalize();
let bytes: [u8; 32] = output.to_array().expect("BLAKE3 always outputs 32 bytes");
Hash(bytes)
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PublicKey(#[serde(with = "public_key_serde")] Ed25519VerifyingKey);
mod public_key_serde {
use super::*;
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(
key: &Ed25519VerifyingKey,
s: S,
) -> std::result::Result<S::Ok, S::Error> {
let bytes = ArcanumVerifyingKey::to_bytes(key);
let arr: [u8; 32] = bytes
.try_into()
.map_err(|_| serde::ser::Error::custom("invalid key length"))?;
arr.serialize(s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
d: D,
) -> std::result::Result<Ed25519VerifyingKey, D::Error> {
let bytes: [u8; 32] = Deserialize::deserialize(d)?;
Ed25519VerifyingKey::from_bytes(&bytes).map_err(serde::de::Error::custom)
}
}
impl PublicKey {
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
let key = Ed25519VerifyingKey::from_bytes(bytes)
.map_err(|e| Error::invalid_key(e.to_string()))?;
Ok(Self(key))
}
pub fn as_bytes(&self) -> [u8; 32] {
let bytes = ArcanumVerifyingKey::to_bytes(&self.0);
bytes
.try_into()
.expect("Ed25519 public key is always 32 bytes")
}
pub fn id(&self) -> Hash {
hash(&self.as_bytes())
}
pub fn verify(&self, message: &[u8], signature: &Sig) -> Result<()> {
ArcanumVerifyingKey::verify(&self.0, message, &signature.0)
.map_err(|_| Error::invalid_signature())
}
pub(crate) fn inner(&self) -> &Ed25519VerifyingKey {
&self.0
}
}
impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PublicKey({})", &hex::encode(&self.as_bytes()[..8]))
}
}
impl std::hash::Hash for PublicKey {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
#[derive(Clone)]
pub struct SecretKey(Ed25519SigningKey);
impl SecretKey {
pub fn generate() -> Self {
Self(Ed25519SigningKey::generate())
}
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self> {
let key =
Ed25519SigningKey::from_bytes(bytes).map_err(|e| Error::invalid_key(e.to_string()))?;
Ok(Self(key))
}
pub fn as_bytes(&self) -> [u8; 32] {
let bytes = ArcanumSigningKey::to_bytes(&self.0);
bytes
.try_into()
.expect("Ed25519 secret key is always 32 bytes")
}
pub fn public_key(&self) -> PublicKey {
PublicKey(ArcanumSigningKey::verifying_key(&self.0))
}
pub fn sign(&self, message: &[u8]) -> Sig {
Sig(ArcanumSigningKey::sign(&self.0, message))
}
}
impl fmt::Debug for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretKey([redacted])")
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Sig(#[serde(with = "sig_serde")] Ed25519Signature);
impl PartialEq for Sig {
fn eq(&self, other: &Self) -> bool {
self.to_bytes() == other.to_bytes()
}
}
impl Eq for Sig {}
mod sig_serde {
use super::*;
use serde::{Deserializer, Serializer};
pub fn serialize<S: Serializer>(
sig: &Ed25519Signature,
s: S,
) -> std::result::Result<S::Ok, S::Error> {
let bytes = ArcanumSignature::to_bytes(sig);
let (first, second) = bytes.split_at(32);
let first: [u8; 32] = first
.try_into()
.map_err(|_| serde::ser::Error::custom("invalid signature length"))?;
let second: [u8; 32] = second
.try_into()
.map_err(|_| serde::ser::Error::custom("invalid signature length"))?;
(first, second).serialize(s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
d: D,
) -> std::result::Result<Ed25519Signature, D::Error> {
let (first, second): ([u8; 32], [u8; 32]) = Deserialize::deserialize(d)?;
let mut bytes = [0u8; 64];
bytes[..32].copy_from_slice(&first);
bytes[32..].copy_from_slice(&second);
Ed25519Signature::from_bytes(&bytes).map_err(serde::de::Error::custom)
}
}
impl Sig {
pub fn empty() -> Self {
Self(Ed25519Signature::from_bytes(&[0u8; 64]).expect("zero bytes is valid"))
}
pub fn from_bytes(bytes: &[u8; 64]) -> Result<Self> {
Ok(Self(
Ed25519Signature::from_bytes(bytes).map_err(|_| Error::invalid_signature())?,
))
}
pub fn to_bytes(&self) -> [u8; 64] {
ArcanumSignature::to_bytes(&self.0)
.try_into()
.expect("Ed25519 signature is always 64 bytes")
}
pub fn is_empty(&self) -> bool {
self.to_bytes() == [0u8; 64]
}
pub(crate) fn inner(&self) -> &Ed25519Signature {
&self.0
}
}
pub fn batch_verify(items: &[(&PublicKey, &[u8], &Sig)]) -> Result<()> {
if items.is_empty() {
return Ok(());
}
let arcanum_items: Vec<(&Ed25519VerifyingKey, &[u8], &Ed25519Signature)> = items
.iter()
.map(|(pk, msg, sig)| (pk.inner(), *msg, sig.inner()))
.collect();
Ed25519BatchVerifier::verify_batch(&arcanum_items).map_err(|_| Error::invalid_signature())
}
#[derive(Debug, Clone)]
pub struct BatchVerifyResult {
pub invalid_indices: Vec<usize>,
}
impl BatchVerifyResult {
pub fn all_valid(&self) -> bool {
self.invalid_indices.is_empty()
}
}
pub fn batch_verify_with_fallback(items: &[(&PublicKey, &[u8], &Sig)]) -> BatchVerifyResult {
if batch_verify(items).is_ok() {
return BatchVerifyResult {
invalid_indices: Vec::new(),
};
}
let invalid_indices = items
.iter()
.enumerate()
.filter_map(|(i, (pk, msg, sig))| {
if pk.verify(msg, sig).is_err() {
Some(i)
} else {
None
}
})
.collect();
BatchVerifyResult { invalid_indices }
}
impl fmt::Debug for Sig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Sig({})", &hex::encode(&self.to_bytes()[..8]))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sig_bincode_roundtrip() {
let key = SecretKey::generate();
let sig = key.sign(b"test message");
let bytes = bincode::serialize(&sig).expect("serialize should work");
println!("Serialized sig size: {} bytes", bytes.len());
let restored: Sig = bincode::deserialize(&bytes).expect("deserialize should work");
assert_eq!(sig.to_bytes(), restored.to_bytes());
}
#[test]
fn test_pubkey_bincode_roundtrip() {
let key = SecretKey::generate();
let pk = key.public_key();
let bytes = bincode::serialize(&pk).expect("serialize should work");
println!("Serialized pubkey size: {} bytes", bytes.len());
let restored: PublicKey = bincode::deserialize(&bytes).expect("deserialize should work");
assert_eq!(pk.as_bytes(), restored.as_bytes());
}
#[test]
fn test_hash_basic() {
let h1 = hash(b"hello");
let h2 = hash(b"hello");
let h3 = hash(b"world");
assert_eq!(h1, h2);
assert_ne!(h1, h3);
assert!(!h1.is_zero());
assert!(Hash::ZERO.is_zero());
}
#[test]
fn test_hash_matches_blake3() {
let h = hash(b"hello");
assert_eq!(
h.to_hex(),
"ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f"
);
}
#[test]
fn test_hash_hex_roundtrip() {
let h = hash(b"test data");
let hex_str = h.to_hex();
let h2 = Hash::from_hex(&hex_str).unwrap();
assert_eq!(h, h2);
}
#[test]
fn test_hash_pair_order_matters() {
let a = hash(b"a");
let b = hash(b"b");
let ab = hash_pair(a, b);
let ba = hash_pair(b, a);
assert_ne!(ab, ba);
}
#[test]
fn test_sign_verify() {
let sk = SecretKey::generate();
let pk = sk.public_key();
let message = b"audit event data";
let sig = sk.sign(message);
assert!(pk.verify(message, &sig).is_ok());
assert!(pk.verify(b"wrong message", &sig).is_err());
}
#[test]
fn test_key_id_deterministic() {
let sk = SecretKey::generate();
let pk = sk.public_key();
let id1 = pk.id();
let id2 = pk.id();
assert_eq!(id1, id2);
}
#[test]
fn test_secret_key_roundtrip() {
let sk = SecretKey::generate();
let bytes = sk.as_bytes();
let restored = SecretKey::from_bytes(&bytes).unwrap();
assert_eq!(sk.public_key().as_bytes(), restored.public_key().as_bytes());
}
}