use alkvault::derivation::{derive_path_from_seed, PATHS};
use alkvault::mnemonic::{Language, Mnemonic};
use alkvault::protocol::KeyType;
#[test]
fn test_bip39_mnemonic_to_seed_with_passphrase() {
let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed_with_pass = mnemonic.to_seed(Some("TREZOR"));
assert_eq!(
seed_with_pass.as_bytes().len(),
64,
"BIP39 seed must be 64 bytes"
);
let mnemonic2 = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed2 = mnemonic2.to_seed(Some("TREZOR"));
assert_eq!(
seed_with_pass.as_bytes(),
seed2.as_bytes(),
"Same mnemonic + passphrase must produce same seed"
);
}
#[test]
fn test_bip39_mnemonic_to_seed_no_passphrase() {
let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed_no_pass = mnemonic.to_seed(None);
assert_eq!(
seed_no_pass.as_bytes().len(),
64,
"BIP39 seed must be 64 bytes"
);
let mnemonic2 = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed_with_pass = mnemonic2.to_seed(Some("TREZOR"));
assert_ne!(
seed_no_pass.as_bytes(),
seed_with_pass.as_bytes(),
"Seeds with different passphrases must differ"
);
}
#[test]
fn test_bip39_different_mnemonics_different_seeds() {
let mnemonic1 = Mnemonic::generate(24).unwrap();
let mnemonic2 = Mnemonic::generate(24).unwrap();
let seed1 = mnemonic1.to_seed(None);
let seed2 = mnemonic2.to_seed(None);
assert_ne!(
seed1.as_bytes(),
seed2.as_bytes(),
"Different mnemonics must produce different seeds"
);
}
#[test]
fn test_slip0010_master_key_from_known_seed() {
let seed_hex = "000102030405060708090a0b0c0d0e0f";
let seed_bytes = hex::decode(seed_hex).unwrap();
let master = derive_path_from_seed(&seed_bytes, "m").unwrap();
assert_eq!(
master.private_key().len(),
32,
"Master private key must be 32 bytes"
);
assert_eq!(
master.public_key().len(),
32,
"Master public key must be 32 bytes"
);
let master2 = derive_path_from_seed(&seed_bytes, "m").unwrap();
assert_eq!(
master.private_key(),
master2.private_key(),
"Master key derivation must be deterministic"
);
assert_eq!(
master.public_key(),
master2.public_key(),
"Master public key derivation must be deterministic"
);
}
#[test]
fn test_slip0010_child_key_m_0h() {
let seed_hex = "000102030405060708090a0b0c0d0e0f";
let seed_bytes = hex::decode(seed_hex).unwrap();
let child = derive_path_from_seed(&seed_bytes, "m/0'").unwrap();
assert_eq!(child.private_key().len(), 32);
assert_eq!(child.public_key().len(), 32);
let master = derive_path_from_seed(&seed_bytes, "m").unwrap();
assert_ne!(
child.private_key(),
master.private_key(),
"Child key must differ from master key"
);
let child2 = derive_path_from_seed(&seed_bytes, "m/0'").unwrap();
assert_eq!(
child.private_key(),
child2.private_key(),
"Child key derivation must be deterministic"
);
}
#[test]
fn test_slip0010_child_key_m_0h_1h_2h() {
let seed_hex = "000102030405060708090a0b0c0d0e0f";
let seed_bytes = hex::decode(seed_hex).unwrap();
let child = derive_path_from_seed(&seed_bytes, "m/0'/1'/2'").unwrap();
assert_eq!(child.private_key().len(), 32);
assert_eq!(child.public_key().len(), 32);
let child_0 = derive_path_from_seed(&seed_bytes, "m/0'").unwrap();
let child_0_1 = derive_path_from_seed(&seed_bytes, "m/0'/1'").unwrap();
assert_ne!(
child.private_key(),
child_0.private_key(),
"Deeper path must differ from shallower"
);
assert_ne!(
child.private_key(),
child_0_1.private_key(),
"Each path must produce a unique key"
);
let child2 = derive_path_from_seed(&seed_bytes, "m/0'/1'/2'").unwrap();
assert_eq!(child.private_key(), child2.private_key());
assert_eq!(child.public_key(), child2.public_key());
}
#[test]
fn test_cross_consistency_mnemonic_seed_derive_identity() {
let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed = mnemonic.to_seed(None);
let key = derive_path_from_seed(seed.as_bytes(), PATHS::IDENTITY).unwrap();
assert_eq!(key.private_key().len(), 32, "Private key must be 32 bytes");
assert_eq!(key.public_key().len(), 32, "Public key must be 32 bytes");
let mnemonic2 = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed2 = mnemonic2.to_seed(None);
let key2 = derive_path_from_seed(seed2.as_bytes(), PATHS::IDENTITY).unwrap();
assert_eq!(
key.private_key(),
key2.private_key(),
"Same seed + same path must produce same private key"
);
assert_eq!(
key.public_key(),
key2.public_key(),
"Same seed + same path must produce same public key"
);
}
#[test]
fn test_cross_consistency_different_paths_different_keys() {
let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed = mnemonic.to_seed(None);
let identity = derive_path_from_seed(seed.as_bytes(), PATHS::IDENTITY).unwrap();
let encryption = derive_path_from_seed(seed.as_bytes(), PATHS::ENCRYPTION).unwrap();
let ssh = derive_path_from_seed(seed.as_bytes(), PATHS::SSH_HOST).unwrap();
assert_ne!(identity.private_key(), encryption.private_key());
assert_ne!(identity.private_key(), ssh.private_key());
assert_ne!(encryption.private_key(), ssh.private_key());
}
#[test]
fn test_aes256gcm_known_key_encrypt_decrypt() {
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce,
};
let key_bytes: [u8; 32] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
0x1e, 0x1f,
];
let cipher = Aes256Gcm::new((&key_bytes).into());
let nonce_bytes: [u8; 12] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
];
let nonce = Nonce::from(nonce_bytes);
let plaintext = b"hello, alkvault!";
let ciphertext = cipher.encrypt(&nonce, plaintext.as_ref()).unwrap();
let decrypted = cipher.decrypt(&nonce, ciphertext.as_ref()).unwrap();
assert_eq!(
decrypted, plaintext,
"Decrypted plaintext must match original"
);
}
#[test]
fn test_alknet_identity_path_regression() {
let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed = mnemonic.to_seed(None);
let key = derive_path_from_seed(seed.as_bytes(), PATHS::IDENTITY).unwrap();
assert_eq!(key.private_key().len(), 32);
assert_eq!(key.public_key().len(), 32);
assert!(
key.private_key().iter().any(|&b| b != 0),
"Private key must not be all zeros"
);
assert!(
key.public_key().iter().any(|&b| b != 0),
"Public key must not be all zeros"
);
let private_hex = hex::encode(key.private_key());
let public_hex = hex::encode(key.public_key());
let key2 = derive_path_from_seed(seed.as_bytes(), PATHS::IDENTITY).unwrap();
assert_eq!(hex::encode(key2.private_key()), private_hex);
assert_eq!(hex::encode(key2.public_key()), public_hex);
}
#[test]
fn test_alknet_encryption_path_regression() {
let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
let seed = mnemonic.to_seed(None);
let key = derive_path_from_seed(seed.as_bytes(), PATHS::ENCRYPTION).unwrap();
let key2 = derive_path_from_seed(seed.as_bytes(), PATHS::ENCRYPTION).unwrap();
assert_eq!(key.private_key(), key2.private_key());
assert_eq!(key.public_key(), key2.public_key());
let identity = derive_path_from_seed(seed.as_bytes(), PATHS::IDENTITY).unwrap();
assert_ne!(key.private_key(), identity.private_key());
}
#[test]
fn test_service_derive_matches_direct_derivation() {
use alkvault::service::VaultServiceHandle;
let service = VaultServiceHandle::new();
let phrase = service.unlock_new(24).unwrap();
let service_key = service.derive_ed25519(PATHS::IDENTITY).unwrap();
let mnemonic = Mnemonic::from_phrase(&phrase, Language::English).unwrap();
let seed = mnemonic.to_seed(None);
let direct_key = derive_path_from_seed(seed.as_bytes(), PATHS::IDENTITY).unwrap();
assert_eq!(service_key.key_type, KeyType::Ed25519);
assert_eq!(service_key.private_key, direct_key.private_key());
assert_eq!(service_key.public_key, direct_key.public_key());
}