use rand::rngs::OsRng;
use tari_crypto::{
keys::{PublicKey, SecretKey},
ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey},
tari_utilities::ByteArray,
};
use crate::error::OotleWasmError;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct KeypairResult {
pub secret_key: String,
pub public_key: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SchnorrSignatureResult {
pub public_nonce: String,
pub signature: String,
}
pub fn generate_keypair() -> KeypairResult {
let secret_key = RistrettoSecretKey::random(&mut OsRng);
let public_key = RistrettoPublicKey::from_secret_key(&secret_key);
KeypairResult {
secret_key: hex::encode(secret_key.as_bytes()),
public_key: hex::encode(public_key.as_bytes()),
}
}
pub fn schnorr_sign(secret_key_hex: &str, message: &[u8]) -> Result<SchnorrSignatureResult, OotleWasmError> {
let secret_key = secret_key_from_hex(secret_key_hex)?;
let sig = RistrettoSchnorr::sign(&secret_key, message, &mut OsRng)
.map_err(|e| OotleWasmError::SigningFailed(e.to_string()))?;
Ok(SchnorrSignatureResult {
public_nonce: hex::encode(sig.get_public_nonce().as_bytes()),
signature: hex::encode(sig.get_signature().as_bytes()),
})
}
fn secret_key_from_hex(hex_str: &str) -> Result<RistrettoSecretKey, OotleWasmError> {
let bytes = hex::decode(hex_str)?;
RistrettoSecretKey::from_canonical_bytes(&bytes).map_err(|e| OotleWasmError::InvalidSecretKey(e.to_string()))
}
pub fn public_key_from_secret_key(secret_key_hex: &str) -> Result<String, OotleWasmError> {
use tari_crypto::keys::PublicKey;
let secret_key = secret_key_from_hex(secret_key_hex)?;
let public_key = RistrettoPublicKey::from_secret_key(&secret_key);
Ok(hex::encode(public_key.as_bytes()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sign_and_verify_round_trip() {
let secret = RistrettoSecretKey::random(&mut OsRng);
let secret_hex = hex::encode(secret.as_bytes());
let message = b"hello world";
let result = schnorr_sign(&secret_hex, message).unwrap();
assert!(!result.public_nonce.is_empty());
assert!(!result.signature.is_empty());
}
#[test]
fn public_key_derivation() {
let secret = RistrettoSecretKey::random(&mut OsRng);
let secret_hex = hex::encode(secret.as_bytes());
let expected = RistrettoPublicKey::from_secret_key(&secret);
let result = public_key_from_secret_key(&secret_hex).unwrap();
assert_eq!(result, hex::encode(expected.as_bytes()));
}
#[test]
fn generate_keypair_is_valid() {
let kp = generate_keypair();
assert_eq!(kp.secret_key.len(), 64);
assert_eq!(kp.public_key.len(), 64);
let derived = public_key_from_secret_key(&kp.secret_key).unwrap();
assert_eq!(derived, kp.public_key);
}
#[test]
fn generate_keypair_is_unique() {
let kp1 = generate_keypair();
let kp2 = generate_keypair();
assert_ne!(kp1.secret_key, kp2.secret_key);
assert_ne!(kp1.public_key, kp2.public_key);
}
}