use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use rand::rngs::OsRng;
use crate::error::Error;
pub const PUBKEY_LEN: usize = 32;
pub const PRIVKEY_LEN: usize = 32;
pub const SIG_LEN: usize = 64;
pub fn generate_keypair() -> ([u8; PRIVKEY_LEN], [u8; PUBKEY_LEN]) {
let signing = SigningKey::generate(&mut OsRng);
let verifying = signing.verifying_key();
(signing.to_bytes(), verifying.to_bytes())
}
pub fn sign(secret_key: &[u8; PRIVKEY_LEN], message: &[u8]) -> [u8; SIG_LEN] {
let signing = SigningKey::from_bytes(secret_key);
signing.sign(message).to_bytes()
}
pub fn verify(public_key: &[u8; PUBKEY_LEN], message: &[u8], signature: &[u8]) -> bool {
let Ok(verifying) = VerifyingKey::from_bytes(public_key) else {
return false;
};
let Ok(sig_bytes) = <[u8; SIG_LEN]>::try_from(signature) else {
return false;
};
let sig = Signature::from_bytes(&sig_bytes);
verifying.verify(message, &sig).is_ok()
}
pub fn pubkey_from_hex(hex_str: &str) -> Result<[u8; PUBKEY_LEN], Error> {
let v = hex::decode(hex_str).map_err(|e| Error::InvalidHex(e.to_string()))?;
if v.len() != PUBKEY_LEN {
return Err(Error::InvalidPubkey);
}
let mut out = [0u8; PUBKEY_LEN];
out.copy_from_slice(&v);
Ok(out)
}
pub fn sig_from_hex(hex_str: &str) -> Result<[u8; SIG_LEN], Error> {
let v = hex::decode(hex_str).map_err(|e| Error::InvalidHex(e.to_string()))?;
if v.len() != SIG_LEN {
return Err(Error::BadSignature);
}
let mut out = [0u8; SIG_LEN];
out.copy_from_slice(&v);
Ok(out)
}
pub fn to_hex(bytes: &[u8]) -> String {
hex::encode(bytes)
}
pub fn pubkey_to_prefixed_str(pubkey: &[u8; PUBKEY_LEN]) -> String {
format!("ed25519:{}", hex::encode(pubkey))
}
pub fn pubkey_from_prefixed_str(s: &str) -> Result<[u8; PUBKEY_LEN], Error> {
let stripped = s.strip_prefix("ed25519:").ok_or_else(|| {
Error::InvalidHex(format!("unsupported key format: {}", &s[..s.len().min(10)]))
})?;
pubkey_from_hex(stripped)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_sign_verify() {
let (sk, pk) = generate_keypair();
let msg = b"hello parley";
let sig = sign(&sk, msg);
assert!(verify(&pk, msg, &sig));
assert!(!verify(&pk, b"other", &sig));
}
#[test]
fn deterministic_sk_pk() {
let sk = [1u8; 32];
let signing = SigningKey::from_bytes(&sk);
let pk = signing.verifying_key().to_bytes();
assert_eq!(
hex::encode(pk),
"8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c"
);
}
}