use miden_protocol::Word;
use miden_protocol::crypto::dsa::falcon512_rpo::{PublicKey, Signature};
use miden_protocol::utils::{Deserializable, Serializable};
pub trait IntoHex {
fn into_hex(self) -> String;
}
pub trait FromHex: Sized {
fn from_hex(hex: &str) -> Result<Self, String>;
}
impl IntoHex for &PublicKey {
fn into_hex(self) -> String {
let mut pubkey_bytes = Vec::new();
self.write_into(&mut pubkey_bytes);
format!("0x{}", hex::encode(pubkey_bytes))
}
}
impl IntoHex for PublicKey {
fn into_hex(self) -> String {
(&self).into_hex()
}
}
impl FromHex for PublicKey {
fn from_hex(hex: &str) -> Result<Self, String> {
let hex_str = hex.strip_prefix("0x").unwrap_or(hex);
let bytes = hex::decode(hex_str).map_err(|e| format!("Invalid public key hex: {e}"))?;
PublicKey::read_from_bytes(&bytes)
.map_err(|e| format!("Failed to deserialize public key: {e}"))
}
}
impl IntoHex for Signature {
fn into_hex(self) -> String {
let signature_bytes = self.to_bytes();
format!("0x{}", hex::encode(&signature_bytes))
}
}
impl FromHex for Signature {
fn from_hex(hex: &str) -> Result<Self, String> {
let hex_str = hex.strip_prefix("0x").unwrap_or(hex);
let bytes = hex::decode(hex_str).map_err(|e| format!("Invalid signature hex: {e}"))?;
const EXPECTED_SIG_LEN: usize = 1524;
if bytes.len() != EXPECTED_SIG_LEN {
return Err(format!(
"Signature must be exactly {EXPECTED_SIG_LEN} bytes, got {} bytes",
bytes.len()
));
}
Signature::read_from_bytes(&bytes)
.map_err(|e| format!("Failed to deserialize signature: {e}"))
}
}
impl IntoHex for Word {
fn into_hex(self) -> String {
format!("0x{}", hex::encode(self.as_bytes()))
}
}
impl IntoHex for &Word {
fn into_hex(self) -> String {
format!("0x{}", hex::encode(self.as_bytes()))
}
}
impl FromHex for Word {
fn from_hex(hex: &str) -> Result<Self, String> {
let hex_str = hex.strip_prefix("0x").unwrap_or(hex);
let bytes = hex::decode(hex_str).map_err(|e| format!("Invalid word hex: {e}"))?;
Word::read_from_bytes(&bytes).map_err(|e| format!("Failed to deserialize word: {e}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey;
#[test]
fn test_public_key_into_hex() {
let secret_key = SecretKey::new();
let public_key = secret_key.public_key();
let hex1 = (&public_key).into_hex();
assert!(hex1.starts_with("0x"));
assert_eq!(hex1.len(), 2 + (897 * 2));
let hex2 = public_key.into_hex();
assert_eq!(hex1, hex2);
}
#[test]
fn test_public_key_from_hex_roundtrip() {
let secret_key = SecretKey::new();
let original_pubkey = secret_key.public_key();
let hex = original_pubkey.into_hex();
let parsed_pubkey = PublicKey::from_hex(&hex).expect("Failed to parse public key");
assert_eq!(hex, parsed_pubkey.into_hex());
}
#[test]
fn test_public_key_from_hex_without_prefix() {
let secret_key = SecretKey::new();
let public_key = secret_key.public_key();
let hex_with_prefix = public_key.into_hex();
let hex_without_prefix = hex_with_prefix.strip_prefix("0x").unwrap();
let pubkey1 = PublicKey::from_hex(&hex_with_prefix).unwrap();
let pubkey2 = PublicKey::from_hex(hex_without_prefix).unwrap();
assert_eq!(pubkey1.into_hex(), pubkey2.into_hex());
}
#[test]
fn test_signature_into_hex() {
use miden_protocol::Word;
let secret_key = SecretKey::new();
let message = Word::from([1u32, 2, 3, 4]);
let signature = secret_key.sign(message);
let hex = signature.into_hex();
assert!(hex.starts_with("0x"));
assert_eq!(hex.len(), 2 + (1524 * 2));
}
#[test]
fn test_signature_from_hex_roundtrip() {
use miden_protocol::Word;
let secret_key = SecretKey::new();
let message = Word::from([1u32, 2, 3, 4]);
let original_sig = secret_key.sign(message);
let hex = original_sig.into_hex();
let parsed_sig = Signature::from_hex(&hex).expect("Failed to parse signature");
assert_eq!(hex, parsed_sig.into_hex());
}
#[test]
fn test_signature_from_hex_validates_length() {
let result = Signature::from_hex("0x1234");
assert!(result.is_err());
assert!(result.unwrap_err().contains("1524 bytes"));
}
#[test]
fn test_word_into_hex() {
let word = Word::from([1u32, 2, 3, 4]);
let hex = word.into_hex();
assert!(hex.starts_with("0x"));
assert_eq!(hex.len(), 2 + (32 * 2));
}
#[test]
fn test_word_from_hex_roundtrip() {
let original = Word::from([0xdeadbeefu32, 0xcafebabe, 0x12345678, 0x87654321]);
let hex = original.into_hex();
let parsed = Word::from_hex(&hex).expect("Failed to parse word");
assert_eq!(original, parsed);
}
#[test]
fn test_word_from_hex_without_prefix() {
let word = Word::from([1u32, 2, 3, 4]);
let hex_with_prefix = word.into_hex();
let hex_without_prefix = hex_with_prefix.strip_prefix("0x").unwrap();
let word1 = Word::from_hex(&hex_with_prefix).unwrap();
let word2 = Word::from_hex(hex_without_prefix).unwrap();
assert_eq!(word1, word2);
}
#[test]
fn test_word_reference_into_hex() {
let word = Word::from([1u32, 2, 3, 4]);
let hex1 = (&word).into_hex();
let hex2 = word.into_hex();
assert_eq!(hex1, hex2);
}
}