fix unit tests

This commit is contained in:
jonesmarvin8 2026-05-20 17:40:02 -04:00
parent 8492f995df
commit f246ebe0fa
8 changed files with 42 additions and 33 deletions

View File

@ -65,7 +65,7 @@ async fn private_transfer_to_foreign_account() -> Result<()> {
let from: AccountId = ctx.existing_private_accounts()[0];
let to_npk = NullifierPublicKey([42; 32]);
let to_npk_string = hex::encode(to_npk.0);
let to_vpk = ViewingPublicKey::from_seed(&to_npk.0, &[0_u8; 32]);
let to_vpk = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
@ -268,7 +268,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
let to_npk = NullifierPublicKey([42; 32]);
let to_npk_string = hex::encode(to_npk.0);
let to_vpk = ViewingPublicKey::from_seed(&to_npk.0, &[0_u8; 32]);
let to_vpk = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
let from: AccountId = ctx.existing_public_accounts()[0];
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {

View File

@ -227,10 +227,10 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
// Fresh recipients — hardcoded npks not in any wallet.
let recipient_npk_0 = NullifierPublicKey([0xAA; 32]);
let recipient_vpk_0 = ViewingPublicKey::from_seed(&recipient_npk_0.0, &[0_u8; 32]);
let recipient_vpk_0 = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
let recipient_npk_1 = NullifierPublicKey([0xBB; 32]);
let recipient_vpk_1 = ViewingPublicKey::from_seed(&recipient_npk_1.0, &[0_u8; 32]);
let recipient_vpk_1 = ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]);
let amount_spend_0: u128 = 13;
let amount_spend_1: u128 = 37;

View File

@ -206,7 +206,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
AccountId::for_regular_private_account(&sender_npk, 0),
);
let recipient_nsk = [2; 32];
let recipient_vpk = ViewingPublicKey::from_seed(&[99_u8; 32], &[100_u8; 32]);
let recipient_vpk = ViewingPublicKey::from_seed(&[101_u8; 32], &[102_u8; 32]);
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
let recipient_pre = AccountWithMetadata::new(
Account::default(),

View File

@ -13,7 +13,7 @@ pub struct EphemeralKeyHolder {
ephemeral_public_key: EphemeralPublicKey,
}
// Marvin-pq: SharedSecretKey does not implement Debug (intentional — leaking key material via
// SharedSecretKey does not implement Debug (intentional — leaking key material via
// debug output would be a security risk). We implement Debug manually here, redacting the
// shared secret while still allowing the ephemeral public key (KEM ciphertext) to be inspected.
impl std::fmt::Debug for EphemeralKeyHolder {

View File

@ -146,13 +146,6 @@ impl GroupKeyHolder {
SecretSpendingKey(hasher.finalize_fixed().into()).produce_private_key_holder(None)
}
// Marvin-pq: seal_for/unseal switched from ECDH (Secp256k1) to ML-KEM-768.
// Wire format changed from:
// ephemeral_pubkey (33) || nonce (12) || ciphertext+tag (48) = 93 bytes
// to:
// kem_ciphertext (1088) || nonce (12) || ciphertext+tag (48) = 1148 bytes
// SealingSecretKey is now the FIPS 203 seed pair (d, r) = ViewingSecretKey.
/// Encrypts this holder's GMS under the recipient's [`SealingPublicKey`].
///
/// Uses ML-KEM-768 encapsulation to derive a shared secret, then AES-256-GCM to encrypt
@ -192,7 +185,7 @@ impl GroupKeyHolder {
/// Returns `Err` if the ciphertext is too short or the AES-GCM authentication tag
/// doesn't verify (wrong key or tampered data).
pub fn unseal(sealed: &[u8], own_key: &SealingSecretKey) -> Result<Self, SealError> {
// Marvin-pq: kem_ciphertext (1088) + nonce (12) = header, then AES-GCM tag (16) minimum.
// kem_ciphertext (1088) + nonce (12) = header, then AES-GCM tag (16) minimum.
const KEM_CT_LEN: usize = 1088;
const HEADER_LEN: usize = KEM_CT_LEN + 12;
const MIN_LEN: usize = HEADER_LEN + 16;
@ -328,7 +321,8 @@ mod tests {
/// Pins the end-to-end derivation for a fixed (GMS, `ProgramId`, `PdaSeed`). Any change
/// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` nsk/npk chain, or the
/// `AccountId::for_private_pda` formula breaks this test.
/// `AccountId::for_private_pda` formula breaks this test. Mirrors the pinned-value
/// pattern from `for_private_pda_matches_pinned_value` in `nssa_core`.
#[test]
fn pinned_end_to_end_derivation_for_private_pda() {
use nssa_core::{account::AccountId, program::ProgramId};
@ -347,6 +341,8 @@ mod tests {
136, 176, 234, 71, 208, 8, 143, 142, 126, 155, 132, 18, 71, 27, 88, 56, 100, 90, 79,
215, 76, 92, 60, 166, 104, 35, 51, 91, 16, 114, 188, 112,
]);
// AccountId is derived from (program_id, seed, npk), so it changes when npk changes.
// We verify npk is pinned, and AccountId is deterministically derived from it.
let expected_account_id =
AccountId::for_private_pda(&program_id, &seed, &expected_npk, u128::MAX);
@ -354,7 +350,10 @@ mod tests {
assert_eq!(account_id, expected_account_id);
}
/// Wallets persist `GroupKeyHolder` to disk and reload it on startup.
/// Wallets persist `GroupKeyHolder` to disk and reload it on startup. This test pins
/// the serde round-trip: serialize, deserialize, and assert the derived keys for a
/// sample seed match on both sides. A silent encoding drift would corrupt every
/// group-owned account.
#[test]
fn gms_serde_round_trip_preserves_derivation() {
let original = GroupKeyHolder::from_gms([7_u8; 32]);
@ -374,7 +373,10 @@ mod tests {
}
/// A `GroupKeyHolder` constructed from the same 32 bytes as a personal
/// `SecretSpendingKey` must not derive the same `NullifierPublicKey`.
/// `SecretSpendingKey` must not derive the same `NullifierPublicKey` as the personal
/// path, so a private PDA cannot be spent by a personal nullifier even under
/// adversarial key-material reuse. The safety rests on the group path's distinct
/// domain-separation prefix plus the seed mix-in (see `secret_spending_key_for_pda`).
#[test]
fn group_derivation_does_not_collide_with_personal_path_at_shared_bytes() {
let shared_bytes = [13_u8; 32];

View File

@ -166,7 +166,6 @@ mod tests {
247, 155, 113, 122, 246, 192, 0, 70, 61, 76, 71, 70, 2,
]);
// Marvin-pq double check the d, r labels
let expected_vsk: ViewingSecretKey = ViewingSecretKey {
d: [
187, 143, 146, 12, 68, 148, 25, 203, 21, 92, 131, 2, 221, 81, 117, 62, 98, 194,
@ -281,7 +280,6 @@ mod tests {
219, 114, 113, 16, 42, 27, 220, 96, 151, 124, 8, 65,
]);
// Marvin-pq double check the d, r labels
let expected_vsk: ViewingSecretKey = ViewingSecretKey {
d: [
81, 154, 68, 152, 72, 163, 82, 17, 125, 156, 193, 135, 129, 93, 227, 55, 224, 104,

View File

@ -135,7 +135,7 @@ impl SecretSpendingKey {
bytes.extend_from_slice(SUFFIX_2);
let bytes: [u8; 64] = bytes
.try_into()
.expect("`generate_viewing_secret_key`: bytes must be exactly 64");
.expect("`generate_viewing_secret_seed_key`: bytes must be exactly 64");
let full_seed = hmac_sha512::HMAC::mac(bytes, b"LEE_viewing_seed");
@ -217,7 +217,6 @@ mod tests {
assert_eq!(seed_holder.seed.len(), 64);
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder();
// Marvin-pq should drop seed from the fucntion name
let _vsk = top_secret_key_holder.generate_viewing_secret_seed_key(None);
}

View File

@ -38,14 +38,24 @@ const NSK_PRIV_ACC_B: [u8; 32] = [
23, 99, 9, 4, 177, 230, 125, 109, 91, 160, 30,
];
const VSK_PRIV_ACC_A: [u8; 32] = [
5, 85, 114, 119, 141, 187, 202, 170, 122, 253, 198, 81, 150, 8, 155, 21, 192, 65, 24, 124, 116,
98, 110, 106, 137, 90, 165, 239, 80, 13, 222, 30,
const VSK_D_PRIV_ACC_A: [u8; 32] = [
255, 250, 140, 26, 222, 223, 174, 95, 132, 108, 124, 88, 30, 247, 82, 72, 52, 70, 84, 139, 241,
187, 41, 163, 19, 231, 232, 122, 225, 55, 134, 184,
];
const VSK_PRIV_ACC_B: [u8; 32] = [
205, 32, 76, 251, 255, 236, 96, 119, 61, 111, 65, 100, 75, 218, 12, 22, 17, 170, 55, 226, 21,
154, 161, 34, 208, 74, 27, 1, 119, 13, 88, 128,
const VSK_R_PRIV_ACC_A: [u8; 32] = [
225, 24, 98, 78, 31, 203, 175, 248, 213, 17, 133, 207, 10, 135, 132, 151, 59, 184, 5, 81, 28,
238, 137, 62, 233, 227, 99, 17, 236, 159, 244, 63,
];
const VSK_D_PRIV_ACC_B: [u8; 32] = [
128, 85, 85, 103, 226, 218, 119, 56, 60, 252, 31, 113, 232, 215, 156, 2, 159, 247, 156, 192,
12, 178, 229, 236, 255, 120, 146, 211, 169, 117, 153, 180,
];
const VSK_R_PRIV_ACC_B: [u8; 32] = [
165, 80, 169, 87, 248, 88, 167, 154, 27, 67, 131, 122, 50, 130, 111, 40, 164, 180, 204, 75,
188, 140, 110, 132, 113, 133, 222, 8, 49, 123, 187, 18,
];
const NPK_PRIV_ACC_A: [u8; 32] = [
@ -127,12 +137,12 @@ pub fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialD
private_key_holder: PrivateKeyHolder {
nullifier_secret_key: NSK_PRIV_ACC_A,
viewing_secret_key: ViewingSecretKey {
d: VSK_PRIV_ACC_A,
r: [0_u8; 32],
d: VSK_D_PRIV_ACC_A,
r: VSK_R_PRIV_ACC_A,
},
},
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_A),
viewing_public_key: ViewingPublicKey::from_seed(&VSK_PRIV_ACC_A, &[0_u8; 32]),
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_A, &VSK_R_PRIV_ACC_A),
};
let key_chain_2 = KeyChain {
@ -140,12 +150,12 @@ pub fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialD
private_key_holder: PrivateKeyHolder {
nullifier_secret_key: NSK_PRIV_ACC_B,
viewing_secret_key: ViewingSecretKey {
d: VSK_PRIV_ACC_B,
r: [0_u8; 32],
d: VSK_D_PRIV_ACC_B,
r: VSK_R_PRIV_ACC_B,
},
},
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_B),
viewing_public_key: ViewingPublicKey::from_seed(&VSK_PRIV_ACC_B, &[0_u8; 32]),
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_B, &VSK_R_PRIV_ACC_B),
};
vec![