use std::fs;
use ed25519_dalek::{Signer, SigningKey, Verifier};
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
struct TestVectors {
version: String,
vectors: Vectors,
}
#[derive(Debug, Deserialize, Serialize)]
struct Vectors {
mnemonic_to_keypair: Vec<MnemonicVector>,
signing_verification: Vec<SigningVector>,
encoding: Vec<EncodingVector>,
key_generation: Vec<KeyGenerationVector>,
}
#[derive(Debug, Deserialize, Serialize)]
struct MnemonicVector {
id: String,
description: String,
input: MnemonicInput,
expected: MnemonicExpected,
}
#[derive(Debug, Deserialize, Serialize)]
struct MnemonicInput {
mnemonic: String,
passphrase: String,
account_index: u32,
derivation_path: String,
}
#[derive(Debug, Deserialize, Serialize)]
struct MnemonicExpected {
public_key_hex: String,
public_key_base58: String,
secret_key_hex: String,
}
#[derive(Debug, Deserialize, Serialize)]
struct SigningVector {
id: String,
description: String,
input: SigningInput,
expected: SigningExpected,
}
#[derive(Debug, Deserialize, Serialize)]
struct SigningInput {
secret_key_hex: String,
message_hex: String,
#[serde(skip_serializing_if = "Option::is_none")]
message_utf8: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
struct SigningExpected {
signature_hex: String,
signature_base58: String,
verification_result: bool,
}
#[derive(Debug, Deserialize, Serialize)]
struct EncodingVector {
id: String,
description: String,
input: EncodingInput,
expected: EncodingExpected,
}
#[derive(Debug, Deserialize, Serialize)]
struct EncodingInput {
#[serde(skip_serializing_if = "Option::is_none")]
public_key_hex: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
signature_hex: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
struct EncodingExpected {
base58: String,
}
#[derive(Debug, Deserialize, Serialize)]
struct KeyGenerationVector {
id: String,
description: String,
input: KeyGenerationInput,
expected: KeyGenerationExpected,
}
#[derive(Debug, Deserialize, Serialize)]
struct KeyGenerationInput {
secret_key_hex: String,
}
#[derive(Debug, Deserialize, Serialize)]
struct KeyGenerationExpected {
public_key_hex: String,
public_key_base58: String,
}
fn hex_to_bytes(hex: &str) -> Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
fn bytes_to_hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
fn load_test_vectors() -> TestVectors {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let vectors_path = format!("{manifest_dir}/test-vectors/crypto-vectors.json");
let data = fs::read_to_string(&vectors_path)
.unwrap_or_else(|_| panic!("Failed to read test vectors from {}", vectors_path));
serde_json::from_str(&data).expect("Failed to parse test vectors")
}
#[cfg(all(feature = "mnemonic", feature = "hd-wallet"))]
#[test]
fn test_mnemonic_to_keypair_vectors() {
use rialo_cdk::wallet::mnemonic::derive_keypair;
let vectors = load_test_vectors();
for vector in vectors.vectors.mnemonic_to_keypair {
println!("Testing: {}", vector.description);
let passphrase = if vector.input.passphrase.is_empty() {
None
} else {
Some(vector.input.passphrase.as_str())
};
let keypair = derive_keypair(
&vector.input.mnemonic,
passphrase,
vector.input.account_index,
)
.unwrap_or_else(|e| panic!("Failed to derive keypair for {}: {}", vector.id, e));
let verifying_key = keypair.verifying_key();
let public_bytes = verifying_key.to_bytes();
let actual_public_key_hex = bytes_to_hex(&public_bytes);
assert_eq!(
actual_public_key_hex, vector.expected.public_key_hex,
"Public key mismatch for {}: expected {}, got {}",
vector.id, vector.expected.public_key_hex, actual_public_key_hex
);
let actual_public_key_base58 = bs58::encode(&public_bytes).into_string();
assert_eq!(
actual_public_key_base58, vector.expected.public_key_base58,
"Public key base58 mismatch for {}: expected {}, got {}",
vector.id, vector.expected.public_key_base58, actual_public_key_base58
);
let private_bytes = keypair.to_bytes();
let actual_secret_key_hex = bytes_to_hex(&private_bytes);
assert_eq!(
actual_secret_key_hex, vector.expected.secret_key_hex,
"Secret key mismatch for {}: expected {}, got {}",
vector.id, vector.expected.secret_key_hex, actual_secret_key_hex
);
println!(" ✓ {} passed", vector.id);
}
}
#[test]
fn test_signing_verification_vectors() {
let vectors = load_test_vectors();
for vector in vectors.vectors.signing_verification {
println!("Testing: {}", vector.description);
let secret_key_bytes = hex_to_bytes(&vector.input.secret_key_hex);
let secret_key_array: [u8; 32] = secret_key_bytes
.try_into()
.expect("Secret key must be 32 bytes");
let keypair = SigningKey::from_bytes(&secret_key_array);
let message_bytes = hex_to_bytes(&vector.input.message_hex);
let signature = keypair.sign(&message_bytes);
let actual_signature_hex = bytes_to_hex(signature.to_bytes().as_ref());
assert_eq!(
actual_signature_hex, vector.expected.signature_hex,
"Signature mismatch for {}: expected {}, got {}",
vector.id, vector.expected.signature_hex, actual_signature_hex
);
let actual_signature_base58 = bs58::encode(signature.to_bytes()).into_string();
assert_eq!(
actual_signature_base58, vector.expected.signature_base58,
"Signature base58 mismatch for {}: expected {}, got {}",
vector.id, vector.expected.signature_base58, actual_signature_base58
);
let public_key = keypair.verifying_key();
let is_valid = public_key.verify(&message_bytes, &signature).is_ok();
assert_eq!(
is_valid, vector.expected.verification_result,
"Verification result mismatch for {}: expected {}, got {}",
vector.id, vector.expected.verification_result, is_valid
);
println!(" ✓ {} passed", vector.id);
}
}
#[test]
fn test_encoding_vectors() {
let vectors = load_test_vectors();
for vector in vectors.vectors.encoding {
println!("Testing: {}", vector.description);
if let Some(public_key_hex) = &vector.input.public_key_hex {
let bytes = hex_to_bytes(public_key_hex);
let base58 = bs58::encode(&bytes).into_string();
assert_eq!(
base58, vector.expected.base58,
"Public key base58 mismatch for {}: expected {}, got {}",
vector.id, vector.expected.base58, base58
);
} else if let Some(signature_hex) = &vector.input.signature_hex {
let bytes = hex_to_bytes(signature_hex);
let base58 = bs58::encode(&bytes).into_string();
assert_eq!(
base58, vector.expected.base58,
"Signature base58 mismatch for {}: expected {}, got {}",
vector.id, vector.expected.base58, base58
);
}
println!(" ✓ {} passed", vector.id);
}
}
#[test]
fn test_key_generation_vectors() {
let vectors = load_test_vectors();
for vector in vectors.vectors.key_generation {
println!("Testing: {}", vector.description);
let secret_key_bytes = hex_to_bytes(&vector.input.secret_key_hex);
let secret_key_array: [u8; 32] = secret_key_bytes
.try_into()
.expect("Secret key must be 32 bytes");
let keypair = SigningKey::from_bytes(&secret_key_array);
let actual_public_key_hex = bytes_to_hex(keypair.verifying_key().as_bytes());
assert_eq!(
actual_public_key_hex, vector.expected.public_key_hex,
"Public key mismatch for {}: expected {}, got {}",
vector.id, vector.expected.public_key_hex, actual_public_key_hex
);
let actual_public_key_base58 =
bs58::encode(keypair.verifying_key().as_bytes()).into_string();
assert_eq!(
actual_public_key_base58, vector.expected.public_key_base58,
"Public key base58 mismatch for {}: expected {}, got {}",
vector.id, vector.expected.public_key_base58, actual_public_key_base58
);
println!(" ✓ {} passed", vector.id);
}
}
#[test]
fn test_round_trip_encoding() {
let keypair = SigningKey::generate(&mut rand::thread_rng());
let verifying_key = keypair.verifying_key();
let original_public_bytes = verifying_key.as_bytes();
let base58 = bs58::encode(original_public_bytes).into_string();
let decoded_bytes = bs58::decode(&base58).into_vec().unwrap();
assert_eq!(
original_public_bytes,
decoded_bytes.as_slice(),
"Public key round-trip encoding failed"
);
let message = b"test message";
let signature = keypair.sign(message);
let original_sig_bytes = signature.to_bytes();
let sig_base58 = bs58::encode(&original_sig_bytes).into_string();
let decoded_sig_bytes = bs58::decode(&sig_base58).into_vec().unwrap();
assert_eq!(
original_sig_bytes.as_ref(),
decoded_sig_bytes.as_slice(),
"Signature round-trip encoding failed"
);
}