use ed25519_dalek::{
Signature as DalekSignature, Signer, SigningKey, Verifier, VerifyingKey,
};
use thiserror::Error;
#[derive(Clone)]
pub struct Ed25519SecretKey {
inner: SigningKey,
}
impl Ed25519SecretKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Ed25519Error> {
if bytes.len() != 32 {
return Err(Ed25519Error::InvalidKeyLength {
expected: 32,
actual: bytes.len(),
});
}
let mut key_bytes = [0u8; 32];
key_bytes.copy_from_slice(bytes);
let inner = SigningKey::from_bytes(&key_bytes);
Ok(Self { inner })
}
pub fn random() -> Self {
let mut csprng = rand::thread_rng();
let inner = SigningKey::generate(&mut csprng);
Self { inner }
}
pub fn public_key(&self) -> Ed25519PublicKey {
Ed25519PublicKey {
inner: self.inner.verifying_key(),
}
}
pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
let sig = self.inner.sign(message);
Ed25519Signature { inner: sig }
}
pub fn to_bytes(&self) -> [u8; 32] {
self.inner.to_bytes()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ed25519PublicKey {
inner: VerifyingKey,
}
impl Ed25519PublicKey {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Ed25519Error> {
if bytes.len() != 32 {
return Err(Ed25519Error::InvalidKeyLength {
expected: 32,
actual: bytes.len(),
});
}
let mut key_bytes = [0u8; 32];
key_bytes.copy_from_slice(bytes);
let inner = VerifyingKey::from_bytes(&key_bytes)
.map_err(|e| Ed25519Error::InvalidKey(e.to_string()))?;
Ok(Self { inner })
}
pub fn to_bytes(&self) -> [u8; 32] {
self.inner.to_bytes()
}
pub fn to_hex(&self) -> String {
format!("0x{}", hex::encode(self.to_bytes()))
}
pub fn from_hex(s: &str) -> Result<Self, Ed25519Error> {
let s = s.strip_prefix("0x").unwrap_or(s);
let bytes = hex::decode(s).map_err(|e| Ed25519Error::InvalidHex(e.to_string()))?;
Self::from_bytes(&bytes)
}
pub fn verify(&self, message: &[u8], signature: &Ed25519Signature) -> bool {
self.inner.verify(message, &signature.inner).is_ok()
}
pub fn tendermint_address(&self) -> [u8; 20] {
use sha2::{Digest, Sha256};
let hash = Sha256::digest(self.to_bytes());
let mut address = [0u8; 20];
address.copy_from_slice(&hash[..20]);
address
}
}
#[derive(Clone, Debug)]
pub struct Ed25519Signature {
inner: DalekSignature,
}
impl Ed25519Signature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Ed25519Error> {
if bytes.len() != 64 {
return Err(Ed25519Error::InvalidSignatureLength {
expected: 64,
actual: bytes.len(),
});
}
let mut sig_bytes = [0u8; 64];
sig_bytes.copy_from_slice(bytes);
let inner = DalekSignature::from_bytes(&sig_bytes);
Ok(Self { inner })
}
pub fn to_bytes(&self) -> [u8; 64] {
self.inner.to_bytes()
}
pub fn to_hex(&self) -> String {
format!("0x{}", hex::encode(self.to_bytes()))
}
}
#[derive(Clone)]
pub struct Ed25519Keypair {
pub secret: Ed25519SecretKey,
pub public: Ed25519PublicKey,
}
impl Ed25519Keypair {
pub fn from_secret(secret: Ed25519SecretKey) -> Self {
let public = secret.public_key();
Self { secret, public }
}
pub fn random() -> Self {
Self::from_secret(Ed25519SecretKey::random())
}
pub fn generate() -> Self {
Self::random()
}
pub fn public_key_bytes(&self) -> [u8; 32] {
self.public.to_bytes()
}
pub fn public_key_bytes_padded(&self) -> [u8; 48] {
let mut padded = [0u8; 48];
padded[..32].copy_from_slice(&self.public.to_bytes());
padded
}
pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
self.secret.sign(message)
}
pub fn verify(&self, message: &[u8], signature: &Ed25519Signature) -> bool {
self.public.verify(message, signature)
}
pub fn tendermint_address(&self) -> [u8; 20] {
self.public.tendermint_address()
}
}
#[derive(Debug, Error)]
pub enum Ed25519Error {
#[error("Invalid key length: expected {expected}, got {actual}")]
InvalidKeyLength { expected: usize, actual: usize },
#[error("Invalid signature length: expected {expected}, got {actual}")]
InvalidSignatureLength { expected: usize, actual: usize },
#[error("Invalid key: {0}")]
InvalidKey(String),
#[error("Invalid signature: {0}")]
InvalidSignature(String),
#[error("Invalid hex: {0}")]
InvalidHex(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keypair_generation() {
let keypair = Ed25519Keypair::random();
let pubkey_bytes = keypair.public.to_bytes();
assert_eq!(pubkey_bytes.len(), 32);
}
#[test]
fn test_sign_and_verify() {
let keypair = Ed25519Keypair::random();
let message = b"test message";
let signature = keypair.sign(message);
assert!(keypair.verify(message, &signature));
assert!(!keypair.verify(b"wrong message", &signature));
}
#[test]
fn test_public_key_hex() {
let keypair = Ed25519Keypair::random();
let hex = keypair.public.to_hex();
assert!(hex.starts_with("0x"));
assert_eq!(hex.len(), 66);
let recovered = Ed25519PublicKey::from_hex(&hex).unwrap();
assert_eq!(recovered, keypair.public);
}
#[test]
fn test_signature_serialization() {
let keypair = Ed25519Keypair::random();
let message = b"test message";
let signature = keypair.sign(message);
let bytes = signature.to_bytes();
assert_eq!(bytes.len(), 64);
let recovered = Ed25519Signature::from_bytes(&bytes).unwrap();
assert!(keypair.verify(message, &recovered));
}
#[test]
fn test_tendermint_address() {
let keypair = Ed25519Keypair::random();
let address = keypair.tendermint_address();
assert_eq!(address.len(), 20);
}
#[test]
fn test_padded_pubkey() {
let keypair = Ed25519Keypair::random();
let padded = keypair.public_key_bytes_padded();
assert_eq!(padded.len(), 48);
assert_eq!(&padded[..32], &keypair.public.to_bytes());
assert_eq!(&padded[32..], &[0u8; 16]);
}
#[test]
fn test_secret_key_roundtrip() {
let original = Ed25519SecretKey::random();
let bytes = original.to_bytes();
let recovered = Ed25519SecretKey::from_bytes(&bytes).unwrap();
let message = b"test";
let sig1 = original.sign(message);
let sig2 = recovered.sign(message);
assert_eq!(sig1.to_bytes(), sig2.to_bytes());
}
}