use ed25519_dalek::{
Signature as Ed25519Signature, Signer as _, SigningKey as Ed25519SigningKey, Verifier as _,
VerifyingKey as Ed25519VerifyingKey,
};
use ed448_rust::{PrivateKey as Ed448PrivateKey, PublicKey as Ed448PublicKey};
use sha3::{Digest, Sha3_256};
use crate::crypto::address::get_key_type_from_public_key;
use crate::crypto::constants::KeyType;
use crate::error::{Result, ZeraError};
pub fn expand_ed448_seed_to_private_key(seed_32: &[u8]) -> Result<[u8; 57]> {
if seed_32.len() != 32 {
return Err(ZeraError::Crypto(format!(
"SLIP-0010 private key must be 32 bytes, got {}",
seed_32.len()
)));
}
let seed_hash = Sha3_256::digest(seed_32);
use hmac::{Hmac, Mac};
use sha2::Sha512;
let mut mac = Hmac::<Sha512>::new_from_slice(seed_hash.as_slice())
.map_err(|e| ZeraError::Crypto(format!("failed to init ed448 expansion HMAC: {e}")))?;
mac.update(b"ed448-expansion");
let expanded = mac.finalize().into_bytes();
let mut out = [0_u8; 57];
out.copy_from_slice(&expanded[..57]);
out[56] &= 0xFC;
Ok(out)
}
#[derive(Debug, Clone)]
pub struct Ed25519KeyPair {
private_key: [u8; 32],
public_key: [u8; 32],
}
impl Ed25519KeyPair {
pub fn new_random() -> Self {
let private_key: [u8; 32] = rand::random();
let signing = Ed25519SigningKey::from_bytes(&private_key);
let public_key = signing.verifying_key().to_bytes();
Self {
private_key,
public_key,
}
}
pub fn from_private_key(private_key: &[u8]) -> Result<Self> {
let pk: [u8; 32] = private_key.try_into().map_err(|_| {
ZeraError::Crypto(format!(
"invalid Ed25519 private key length: {}",
private_key.len()
))
})?;
let signing = Ed25519SigningKey::from_bytes(&pk);
Ok(Self {
private_key: pk,
public_key: signing.verifying_key().to_bytes(),
})
}
pub fn private_key_bytes(&self) -> [u8; 32] {
self.private_key
}
pub fn public_key_bytes(&self) -> [u8; 32] {
self.public_key
}
pub fn sign(&self, data: &[u8]) -> Vec<u8> {
let signing = Ed25519SigningKey::from_bytes(&self.private_key);
signing.sign(data).to_bytes().to_vec()
}
pub fn verify(&self, signature: &[u8], data: &[u8]) -> bool {
let Ok(sig_bytes): std::result::Result<[u8; 64], _> = signature.try_into() else {
return false;
};
let signature = Ed25519Signature::from_bytes(&sig_bytes);
let Ok(verifying) = Ed25519VerifyingKey::from_bytes(&self.public_key) else {
return false;
};
verifying.verify(data, &signature).is_ok()
}
pub fn private_key_base58(&self) -> String {
bs58::encode(self.private_key).into_string()
}
pub fn public_key_base58(&self) -> String {
bs58::encode(self.public_key).into_string()
}
}
#[derive(Debug, Clone)]
pub struct Ed448KeyPair {
private_key: [u8; 57],
public_key: [u8; 57],
}
impl Ed448KeyPair {
pub fn new_random() -> Self {
let secret = Ed448PrivateKey::new(&mut rand::rngs::OsRng);
let private_key = *secret.as_bytes();
let public_key = Ed448PublicKey::from(&secret).as_byte();
Self {
private_key,
public_key,
}
}
pub fn from_private_key(private_key: &[u8]) -> Result<Self> {
let private_key: [u8; 57] = match private_key.len() {
32 => expand_ed448_seed_to_private_key(private_key)?,
57 => private_key.try_into().map_err(|_| {
ZeraError::Crypto("failed to parse 57-byte Ed448 private key".into())
})?,
n => {
return Err(ZeraError::Crypto(format!(
"Invalid private key length: {n}. Expected 32 (SLIP0010 seed) or 57 (ED448 private key) bytes."
)))
}
};
let secret = Ed448PrivateKey::from(private_key);
let public_key = Ed448PublicKey::from(&secret).as_byte();
Ok(Self {
private_key,
public_key,
})
}
pub fn private_key_bytes(&self) -> [u8; 57] {
self.private_key
}
pub fn public_key_bytes(&self) -> [u8; 57] {
self.public_key
}
pub fn sign(&self, data: &[u8]) -> Vec<u8> {
let secret = Ed448PrivateKey::from(self.private_key);
secret
.sign(data, None)
.expect("ed448 sign should succeed")
.to_vec()
}
pub fn verify(&self, signature: &[u8], data: &[u8]) -> bool {
if signature.len() != 114 {
return false;
}
let secret = Ed448PrivateKey::from(self.private_key);
let public = Ed448PublicKey::from(&secret);
std::panic::catch_unwind(|| public.verify(data, signature, None).is_ok()).unwrap_or(false)
}
pub fn private_key_base58(&self) -> String {
bs58::encode(self.private_key).into_string()
}
pub fn public_key_base58(&self) -> String {
bs58::encode(self.public_key).into_string()
}
}
pub fn sign_transaction_data(
data: &[u8],
private_key_base58: &str,
public_key_identifier: &str,
) -> Result<Vec<u8>> {
let key_type = get_key_type_from_public_key(public_key_identifier)?;
let mut private_key_bytes = bs58::decode(private_key_base58)
.into_vec()
.map_err(|e| ZeraError::Crypto(format!("invalid base58 private key: {e}")))?;
match key_type {
KeyType::Ed25519 => {
if private_key_bytes.len() == 64 {
private_key_bytes = private_key_bytes[..32].to_vec();
}
let key = Ed25519KeyPair::from_private_key(&private_key_bytes)?;
Ok(key.sign(data))
}
KeyType::Ed448 => {
let key = Ed448KeyPair::from_private_key(&private_key_bytes)?;
Ok(key.sign(data))
}
}
}
pub fn create_transaction_hash(data: &[u8]) -> Vec<u8> {
Sha3_256::digest(data).to_vec()
}