feat(key-protocol/nssa)!: PQ resistance primitives for vsk/vpk for shared key agreement (#474)

* initialize pq encryption changes

* key agreement update

* add test and other fixes

* ci fixes

* fix unit tests

* updates from main and ci

* added updated specs for pq encryption

* addressing comments

* addressed comments

* fix clippy errors from main merge

* Rebased to main
This commit is contained in:
jonesmarvin8
2026-06-03 14:40:06 -04:00
committed by GitHub
parent c64e40590b
commit 2104f71e39
94 changed files with 1427 additions and 625 deletions
+4
View File
@@ -132,6 +132,7 @@ async fn amm_public() -> Result<()> {
to: Some(public_mention(recipient_account_id_1)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 7,
};
@@ -158,6 +159,7 @@ async fn amm_public() -> Result<()> {
to: Some(public_mention(recipient_account_id_2)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 7,
};
@@ -530,6 +532,7 @@ async fn amm_new_pool_using_labels() -> Result<()> {
to: Some(public_mention(holding_a_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 5,
};
@@ -551,6 +554,7 @@ async fn amm_new_pool_using_labels() -> Result<()> {
to: Some(public_mention(holding_b_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 5,
};
+3
View File
@@ -260,6 +260,7 @@ async fn transfer_and_burn_via_ata() -> Result<()> {
to: Some(public_mention(sender_ata_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: fund_amount,
}),
@@ -487,6 +488,7 @@ async fn transfer_via_ata_private_owner() -> Result<()> {
to: Some(public_mention(sender_ata_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: fund_amount,
}),
@@ -598,6 +600,7 @@ async fn burn_via_ata_private_owner() -> Result<()> {
to: Some(public_mention(holder_ata_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: fund_amount,
}),
@@ -11,8 +11,9 @@ use lee::{
privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
};
use lee_core::{
InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata,
encryption::shared_key_derivation::Secp256k1Point,
InputAccountIdentity, NullifierPublicKey,
account::AccountWithMetadata,
encryption::{EphemeralPublicKey, ViewingPublicKey},
};
use log::info;
use sequencer_service_rpc::RpcClient as _;
@@ -38,6 +39,7 @@ async fn private_transfer_to_owned_account() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -71,13 +73,14 @@ 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 = Secp256k1Point::from_scalar(to_npk.0);
let to_vpk = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
from: private_mention(from),
to: None,
to_npk: Some(to_npk_string),
to_vpk: Some(hex::encode(to_vpk.0)),
to_vpk: Some(hex::encode(to_vpk.to_bytes())),
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -127,6 +130,7 @@ async fn deshielded_transfer_to_public_account() -> Result<()> {
to: Some(public_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -189,7 +193,8 @@ async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> {
from: private_mention(from),
to: None,
to_npk: Some(hex::encode(to.key_chain.nullifier_public_key.0)),
to_vpk: Some(hex::encode(&to.key_chain.viewing_public_key.0)),
to_vpk: Some(hex::encode(to.key_chain.viewing_public_key.to_bytes())),
to_keys: None,
to_identifier: Some(to.kind.identifier()),
amount: 100,
});
@@ -239,6 +244,7 @@ async fn shielded_transfer_to_owned_private_account() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -274,14 +280,15 @@ 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 = Secp256k1Point::from_scalar(to_npk.0);
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 {
from: public_mention(from),
to: None,
to_npk: Some(to_npk_string),
to_vpk: Some(hex::encode(to_vpk.0)),
to_vpk: Some(hex::encode(to_vpk.to_bytes())),
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -351,7 +358,8 @@ async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> {
from: private_mention(from),
to: None,
to_npk: Some(hex::encode(to.key_chain.nullifier_public_key.0)),
to_vpk: Some(hex::encode(&to.key_chain.viewing_public_key.0)),
to_vpk: Some(hex::encode(to.key_chain.viewing_public_key.to_bytes())),
to_keys: None,
to_identifier: Some(to.kind.identifier()),
amount: 100,
});
@@ -452,6 +460,7 @@ async fn private_transfer_using_from_label() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -545,7 +554,7 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
};
let npk_hex = hex::encode(npk.0);
let vpk_hex = hex::encode(vpk.0);
let vpk_hex = hex::encode(vpk.to_bytes());
let identifier_1 = 1_u128;
let identifier_2 = 2_u128;
@@ -560,6 +569,7 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
to: None,
to_npk: Some(npk_hex.clone()),
to_vpk: Some(vpk_hex.clone()),
to_keys: None,
to_identifier: Some(identifier_1),
amount: 100,
}),
@@ -573,6 +583,7 @@ async fn shielded_transfers_to_two_identifiers_same_npk() -> Result<()> {
to: None,
to_npk: Some(npk_hex),
to_vpk: Some(vpk_hex),
to_keys: None,
to_identifier: Some(identifier_2),
amount: 200,
}),
@@ -654,8 +665,9 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
let auth_transfer_program_id = Program::authenticated_transfer_program().id();
let nsk: lee_core::NullifierSecretKey = [3; 32];
let npk = NullifierPublicKey::from(&nsk);
let vpk = Secp256k1Point::from_scalar([4; 32]);
let ssk = SharedSecretKey::new([55; 32], &vpk);
let _vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
let ssk = SharedSecretKey([55_u8; 32]);
let _epk = EphemeralPublicKey(vec![55_u8; 1088]);
let attacker_vault_id = {
let seed = vault_core::compute_vault_seed(attacker_id);
AccountId::for_private_pda(&vault_program_id, &seed, &npk, 1337)
@@ -25,6 +25,7 @@ async fn successful_transfer_to_existing_account() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -83,6 +84,7 @@ pub async fn successful_transfer_to_new_account() -> Result<()> {
to: Some(public_mention(new_persistent_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -120,6 +122,7 @@ async fn failed_transfer_with_insufficient_balance() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 1_000_000,
});
@@ -159,6 +162,7 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -192,6 +196,7 @@ async fn two_consecutive_successful_transfers() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -274,6 +279,7 @@ async fn successful_transfer_using_from_label() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -319,6 +325,7 @@ async fn successful_transfer_using_to_label() -> Result<()> {
to: Some(CliAccountMention::Label(label)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -30,6 +30,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
to: Some(public_mention(ctx.ctx().existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
amount: 100,
to_identifier: Some(0),
});
@@ -67,6 +68,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
amount: 100,
to_identifier: Some(0),
});
@@ -46,6 +46,7 @@ fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
to: Some(to_label.into()),
to_npk: None,
to_vpk: None,
to_keys: None,
amount: 100,
to_identifier: Some(0),
});
@@ -25,6 +25,7 @@ async fn indexer_state_consistency() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -60,6 +61,7 @@ async fn indexer_state_consistency() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -43,6 +43,7 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
to: Some(CliAccountMention::Label(to_label)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
+10 -1
View File
@@ -71,7 +71,10 @@ async fn sync_private_account_with_non_zero_chain_index() -> Result<()> {
from: private_mention(from),
to: None,
to_npk: Some(hex::encode(to_account.key_chain.nullifier_public_key.0)),
to_vpk: Some(hex::encode(&to_account.key_chain.viewing_public_key.0)),
to_vpk: Some(hex::encode(
to_account.key_chain.viewing_public_key.to_bytes(),
)),
to_keys: None,
to_identifier: Some(to_account.kind.identifier()),
amount: 100,
});
@@ -147,6 +150,7 @@ async fn restore_keys_from_seed() -> Result<()> {
to: Some(private_mention(to_account_id1)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@@ -158,6 +162,7 @@ async fn restore_keys_from_seed() -> Result<()> {
to: Some(private_mention(to_account_id2)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 101,
});
@@ -197,6 +202,7 @@ async fn restore_keys_from_seed() -> Result<()> {
to: Some(public_mention(to_account_id3)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 102,
});
@@ -208,6 +214,7 @@ async fn restore_keys_from_seed() -> Result<()> {
to: Some(public_mention(to_account_id4)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 103,
});
@@ -268,6 +275,7 @@ async fn restore_keys_from_seed() -> Result<()> {
to: Some(private_mention(to_account_id2)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 10,
});
@@ -278,6 +286,7 @@ async fn restore_keys_from_seed() -> Result<()> {
to: Some(public_mention(to_account_id4)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 11,
});
+5 -5
View File
@@ -64,9 +64,9 @@ async fn fund_private_pda(
let sender_pre = AccountWithMetadata::new(sender_account.clone(), true, sender);
let pda_pre = AccountWithMetadata::new(Account::default(), false, pda_account_id);
let eph_holder = EphemeralKeyHolder::new(&npk);
let ssk = eph_holder.calculate_shared_secret_sender(&vpk);
let epk = eph_holder.generate_ephemeral_public_key();
let eph_holder = EphemeralKeyHolder::new(&vpk);
let ssk = eph_holder.calculate_shared_secret_sender();
let epk = eph_holder.ephemeral_public_key().clone();
let instruction = Program::serialize_instruction(AuthTransferInstruction::Transfer { amount })
.context("failed to serialize auth_transfer instruction")?;
@@ -272,10 +272,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_scalar(recipient_npk_0.0);
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_scalar(recipient_npk_1.0);
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;
+6 -2
View File
@@ -107,8 +107,11 @@ async fn group_invite_join_key_agreement() -> Result<()> {
.key_chain()
.sealing_secret_key()
.context("Sealing key not found")?;
let sealing_pk =
key_protocol::key_management::group_key_holder::SealingPublicKey::from_scalar(sealing_sk);
let sealing_pk = key_protocol::key_management::group_key_holder::SealingPublicKey::from_bytes(
lee_core::encryption::ViewingPublicKey::from_seed(&sealing_sk.d, &sealing_sk.z)
.to_bytes()
.to_vec(),
);
let holder = ctx
.wallet()
@@ -204,6 +207,7 @@ async fn fund_shared_account_from_public() -> Result<()> {
to: Some(private_mention(shared_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: None,
amount: 100,
});
+11 -1
View File
@@ -133,6 +133,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
to: Some(public_mention(recipient_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: transfer_amount,
};
@@ -223,6 +224,7 @@ async fn create_and_transfer_public_token() -> Result<()> {
holder: Some(public_mention(recipient_account_id)),
holder_npk: None,
holder_vpk: None,
holder_keys: None,
holder_identifier: None,
amount: mint_amount,
};
@@ -365,6 +367,7 @@ async fn create_and_transfer_token_with_private_supply() -> Result<()> {
to: Some(private_mention(recipient_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: transfer_amount,
};
@@ -554,6 +557,7 @@ async fn create_token_with_private_definition() -> Result<()> {
holder: Some(public_mention(recipient_account_id_public)),
holder_npk: None,
holder_vpk: None,
holder_keys: None,
holder_identifier: None,
amount: mint_amount_public,
};
@@ -601,6 +605,7 @@ async fn create_token_with_private_definition() -> Result<()> {
holder: Some(private_mention(recipient_account_id_private)),
holder_npk: None,
holder_vpk: None,
holder_keys: None,
holder_identifier: None,
amount: mint_amount_private,
};
@@ -740,6 +745,7 @@ async fn create_token_with_private_definition_and_supply() -> Result<()> {
to: Some(private_mention(recipient_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: transfer_amount,
};
@@ -868,6 +874,7 @@ async fn shielded_token_transfer() -> Result<()> {
to: Some(private_mention(recipient_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: transfer_amount,
};
@@ -991,6 +998,7 @@ async fn deshielded_token_transfer() -> Result<()> {
to: Some(public_mention(recipient_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: transfer_amount,
};
@@ -1124,7 +1132,8 @@ async fn token_claiming_path_with_private_accounts() -> Result<()> {
definition: private_mention(definition_account_id),
holder: None,
holder_npk: Some(hex::encode(holder_keys.nullifier_public_key.0)),
holder_vpk: Some(hex::encode(&holder_keys.viewing_public_key.0)),
holder_vpk: Some(hex::encode(holder_keys.viewing_public_key.to_bytes())),
holder_keys: None,
holder_identifier: Some(holder_identifier),
amount: mint_amount,
};
@@ -1323,6 +1332,7 @@ async fn transfer_token_using_from_label() -> Result<()> {
to: Some(public_mention(recipient_account_id)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: transfer_amount,
};
+8 -10
View File
@@ -256,8 +256,7 @@ pub async fn tps_test() -> Result<()> {
fn build_privacy_transaction() -> PrivacyPreservingTransaction {
let program = Program::authenticated_transfer_program();
let sender_nsk = [1; 32];
let sender_vsk = [99; 32];
let sender_vpk = ViewingPublicKey::from_scalar(sender_vsk);
let sender_vpk = ViewingPublicKey::from_seed(&[99_u8; 32], &[100_u8; 32]);
let sender_npk = NullifierPublicKey::from(&sender_nsk);
let sender_pre = AccountWithMetadata::new(
Account {
@@ -270,8 +269,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
AccountId::for_regular_private_account(&sender_npk, 0),
);
let recipient_nsk = [2; 32];
let recipient_vsk = [99; 32];
let recipient_vpk = ViewingPublicKey::from_scalar(recipient_vsk);
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(),
@@ -279,13 +277,13 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
AccountId::for_regular_private_account(&recipient_npk, 0),
);
let eph_holder_from = EphemeralKeyHolder::new(&sender_npk);
let sender_ss = eph_holder_from.calculate_shared_secret_sender(&sender_vpk);
let sender_epk = eph_holder_from.generate_ephemeral_public_key();
let eph_holder_from = EphemeralKeyHolder::new(&sender_vpk);
let sender_ss = eph_holder_from.calculate_shared_secret_sender();
let sender_epk = eph_holder_from.ephemeral_public_key().clone();
let eph_holder_to = EphemeralKeyHolder::new(&recipient_npk);
let recipient_ss = eph_holder_to.calculate_shared_secret_sender(&recipient_vpk);
let recipient_epk = eph_holder_from.generate_ephemeral_public_key();
let eph_holder_to = EphemeralKeyHolder::new(&recipient_vpk);
let recipient_ss = eph_holder_to.calculate_shared_secret_sender();
let recipient_epk = eph_holder_to.ephemeral_public_key().clone();
let balance_to_move: u128 = 1;
let proof: MembershipProof = (