mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-22 07:59:29 +00:00
add test and other fixes
This commit is contained in:
parent
dc7378da1f
commit
8253002739
@ -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, &[0u8; 32]);
|
||||
let to_vpk = ViewingPublicKey::from_seed(&to_npk.0, &[0_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, &[0u8; 32]);
|
||||
let to_vpk = ViewingPublicKey::from_seed(&to_npk.0, &[0_u8; 32]);
|
||||
let from: AccountId = ctx.existing_public_accounts()[0];
|
||||
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
@ -654,9 +654,9 @@ async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> {
|
||||
let auth_transfer_program_id = Program::authenticated_transfer_program().id();
|
||||
let nsk: nssa_core::NullifierSecretKey = [3; 32];
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = Secp256k1Point::from_scalar([4; 32]);
|
||||
let ssk = SharedSecretKey::new([55; 32], &vpk);
|
||||
let epk = EphemeralPublicKey::from_scalar([55; 32]);
|
||||
let vpk = ViewingPublicKey(vec![4_u8; 1184]);
|
||||
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)
|
||||
|
||||
@ -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, &[0u8; 32]);
|
||||
let recipient_vpk_0 = ViewingPublicKey::from_seed(&recipient_npk_0.0, &[0_u8; 32]);
|
||||
|
||||
let recipient_npk_1 = NullifierPublicKey([0xBB; 32]);
|
||||
let recipient_vpk_1 = ViewingPublicKey::from_seed(&recipient_npk_1.0, &[0u8; 32]);
|
||||
let recipient_vpk_1 = ViewingPublicKey::from_seed(&recipient_npk_1.0, &[0_u8; 32]);
|
||||
|
||||
let amount_spend_0: u128 = 13;
|
||||
let amount_spend_1: u128 = 37;
|
||||
|
||||
@ -193,7 +193,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_vpk = ViewingPublicKey::from_seed(&[99u8; 32], &[100u8; 32]);
|
||||
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 {
|
||||
@ -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(&[99u8; 32], &[100u8; 32]);
|
||||
let recipient_vpk = ViewingPublicKey::from_seed(&[99_u8; 32], &[100_u8; 32]);
|
||||
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
|
||||
let recipient_pre = AccountWithMetadata::new(
|
||||
Account::default(),
|
||||
|
||||
@ -6,7 +6,7 @@ use nssa_core::{
|
||||
/// Ephemeral key holder for the sender side of a KEM-based shared-secret exchange.
|
||||
///
|
||||
/// Non-clonable as intended for one-time use: construction encapsulates once and
|
||||
/// stores both the shared secret and the ciphertext (EphemeralPublicKey) that must
|
||||
/// stores both the shared secret and the ciphertext (`EphemeralPublicKey`) that must
|
||||
/// be sent to the receiver.
|
||||
pub struct EphemeralKeyHolder {
|
||||
shared_secret: SharedSecretKey,
|
||||
@ -36,15 +36,15 @@ impl EphemeralKeyHolder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the KEM ciphertext to be transmitted to the receiver as the EphemeralPublicKey.
|
||||
/// Returns the KEM ciphertext to be transmitted to the receiver as the `EphemeralPublicKey`.
|
||||
#[must_use]
|
||||
pub fn ephemeral_public_key(&self) -> &EphemeralPublicKey {
|
||||
pub const fn ephemeral_public_key(&self) -> &EphemeralPublicKey {
|
||||
&self.ephemeral_public_key
|
||||
}
|
||||
|
||||
/// Returns the sender-side shared secret (established at construction time).
|
||||
#[must_use]
|
||||
pub fn calculate_shared_secret_sender(&self) -> SharedSecretKey {
|
||||
pub const fn calculate_shared_secret_sender(&self) -> SharedSecretKey {
|
||||
self.shared_secret
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,10 +399,10 @@ mod tests {
|
||||
let recipient_ssk = SecretSpendingKey([7_u8; 32]);
|
||||
let recipient_keys = recipient_ssk.produce_private_key_holder(None);
|
||||
let recipient_vpk = recipient_keys.generate_viewing_public_key();
|
||||
let recipient_vsk = recipient_keys.viewing_secret_key.clone();
|
||||
let recipient_vsk = recipient_keys.viewing_secret_key;
|
||||
|
||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
let restored = GroupKeyHolder::unseal(&sealed, recipient_vsk).expect("unseal");
|
||||
let restored = GroupKeyHolder::unseal(&sealed, &recipient_vsk).expect("unseal");
|
||||
|
||||
assert_eq!(restored.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
||||
|
||||
@ -429,11 +429,10 @@ mod tests {
|
||||
|
||||
let wrong_vsk = SecretSpendingKey([99_u8; 32])
|
||||
.produce_private_key_holder(None)
|
||||
.viewing_secret_key
|
||||
.clone();
|
||||
.viewing_secret_key;
|
||||
|
||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
let result = GroupKeyHolder::unseal(&sealed, wrong_vsk);
|
||||
let result = GroupKeyHolder::unseal(&sealed, &wrong_vsk);
|
||||
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
||||
}
|
||||
|
||||
@ -445,14 +444,14 @@ mod tests {
|
||||
let recipient_ssk = SecretSpendingKey([7_u8; 32]);
|
||||
let recipient_keys = recipient_ssk.produce_private_key_holder(None);
|
||||
let recipient_vpk = recipient_keys.generate_viewing_public_key();
|
||||
let recipient_vsk = recipient_keys.viewing_secret_key.clone();
|
||||
let recipient_vsk = recipient_keys.viewing_secret_key;
|
||||
|
||||
let mut sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
// Flip a byte in the AES-GCM ciphertext portion (after KEM ciphertext + nonce).
|
||||
let last = sealed.len() - 1;
|
||||
sealed[last] ^= 0xFF;
|
||||
|
||||
let result = GroupKeyHolder::unseal(&sealed, recipient_vsk);
|
||||
let result = GroupKeyHolder::unseal(&sealed, &recipient_vsk);
|
||||
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
||||
}
|
||||
|
||||
@ -527,11 +526,11 @@ mod tests {
|
||||
let bob_ssk = SecretSpendingKey([77_u8; 32]);
|
||||
let bob_keys = bob_ssk.produce_private_key_holder(None);
|
||||
let bob_vpk = bob_keys.generate_viewing_public_key();
|
||||
let bob_vsk = bob_keys.viewing_secret_key.clone();
|
||||
let bob_vsk = bob_keys.viewing_secret_key;
|
||||
|
||||
let sealed = alice_holder.seal_for(&SealingPublicKey::from_bytes(bob_vpk.0));
|
||||
let bob_holder =
|
||||
GroupKeyHolder::unseal(&sealed, bob_vsk).expect("Bob should unseal the GMS");
|
||||
GroupKeyHolder::unseal(&sealed, &bob_vsk).expect("Bob should unseal the GMS");
|
||||
|
||||
let bob_npk = bob_holder
|
||||
.derive_keys_for_pda(&TEST_PROGRAM_ID, &pda_seed)
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use nssa_core::{NullifierPublicKey, PrivateAccountKind, encryption::ViewingPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest as _;
|
||||
@ -59,7 +61,7 @@ impl ChildKeysPrivate {
|
||||
pub fn nth_child(&self, cci: u32) -> Self {
|
||||
let mut parent_hash = sha2::Sha256::new();
|
||||
parent_hash.update(b"LEE/keys");
|
||||
parent_hash.update([0u8; 16]);
|
||||
parent_hash.update([0_u8; 16]);
|
||||
parent_hash.update([9_u8]);
|
||||
parent_hash.update(self.value.0.private_key_holder.nullifier_secret_key);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.d);
|
||||
@ -128,7 +130,7 @@ impl KeyTreeNode for ChildKeysPrivate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nssa_core::{NullifierPublicKey, NullifierSecretKey};
|
||||
use nssa_core::NullifierSecretKey;
|
||||
|
||||
use super::*;
|
||||
use crate::key_management::{self, secret_holders::ViewingSecretKey};
|
||||
@ -144,7 +146,7 @@ mod tests {
|
||||
|
||||
let keys = ChildKeysPrivate::root(seed);
|
||||
|
||||
let expected_ssk: SecretSpendingKey = key_management::secret_holders::SecretSpendingKey([
|
||||
let expected_ssk = key_management::secret_holders::SecretSpendingKey([
|
||||
246, 79, 26, 124, 135, 95, 52, 51, 201, 27, 48, 194, 2, 144, 51, 219, 245, 128, 139,
|
||||
222, 42, 195, 105, 33, 115, 97, 186, 0, 97, 14, 218, 191,
|
||||
]);
|
||||
@ -159,7 +161,7 @@ mod tests {
|
||||
34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163,
|
||||
];
|
||||
|
||||
let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([
|
||||
let expected_npk = nssa_core::NullifierPublicKey([
|
||||
7, 123, 125, 191, 233, 183, 201, 4, 20, 214, 155, 210, 45, 234, 27, 240, 194, 111, 97,
|
||||
247, 155, 113, 122, 246, 192, 0, 70, 61, 76, 71, 70, 2,
|
||||
]);
|
||||
@ -257,11 +259,10 @@ mod tests {
|
||||
114, 39, 38, 118, 197, 205, 225,
|
||||
];
|
||||
|
||||
// Marvin-pq this test currently fails
|
||||
let root_node = ChildKeysPrivate::root(seed);
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
|
||||
|
||||
let expected_ssk: SecretSpendingKey = key_management::secret_holders::SecretSpendingKey([
|
||||
let expected_ssk = key_management::secret_holders::SecretSpendingKey([
|
||||
215, 207, 70, 52, 161, 220, 88, 88, 241, 149, 81, 130, 217, 214, 252, 170, 51, 232,
|
||||
230, 158, 195, 173, 174, 37, 27, 101, 49, 35, 79, 13, 44, 225,
|
||||
]);
|
||||
|
||||
@ -19,7 +19,7 @@ pub struct SeedHolder {
|
||||
pub struct SecretSpendingKey(pub [u8; 32]);
|
||||
/// Viewing secret key: the KEM seed split into its two 32-byte halves `d` and `r` (= z in
|
||||
/// FIPS 203), from which the ML-KEM 768 decapsulation key is derived deterministically.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ViewingSecretKey {
|
||||
pub d: [u8; 32],
|
||||
pub r: [u8; 32],
|
||||
@ -27,7 +27,7 @@ pub struct ViewingSecretKey {
|
||||
|
||||
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
|
||||
/// for recepient.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct PrivateKeyHolder {
|
||||
pub nullifier_secret_key: NullifierSecretKey,
|
||||
pub viewing_secret_key: ViewingSecretKey,
|
||||
@ -150,7 +150,7 @@ impl SecretSpendingKey {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_viewing_secret_key(seed: [u8; 64]) -> ViewingSecretKey {
|
||||
pub const fn generate_viewing_secret_key(seed: [u8; 64]) -> ViewingSecretKey {
|
||||
ViewingSecretKey {
|
||||
d: *seed.first_chunk::<32>().expect("seed is 64 bytes"),
|
||||
r: *seed.last_chunk::<32>().expect("seed is 64 bytes"),
|
||||
@ -169,11 +169,11 @@ impl SecretSpendingKey {
|
||||
impl From<&ViewingSecretKey> for ViewingPublicKey {
|
||||
fn from(sk: &ViewingSecretKey) -> Self {
|
||||
use ml_kem::{Kem, KeyExport as _, MlKem768, Seed};
|
||||
let mut seed_bytes = [0u8; 64];
|
||||
let mut seed_bytes = [0_u8; 64];
|
||||
seed_bytes[..32].copy_from_slice(&sk.d);
|
||||
seed_bytes[32..].copy_from_slice(&sk.r);
|
||||
let dk = <MlKem768 as Kem>::DecapsulationKey::from_seed(Seed::from(seed_bytes));
|
||||
ViewingPublicKey(dk.encapsulation_key().to_bytes().to_vec())
|
||||
Self(dk.encapsulation_key().to_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,421 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use k256::AffinePoint;
|
||||
use nssa::{Account, AccountId};
|
||||
use nssa_core::{Identifier, PrivateAccountKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::key_management::{
|
||||
KeyChain,
|
||||
group_key_holder::GroupKeyHolder,
|
||||
key_tree::{KeyTreePrivate, KeyTreePublic, chain_index::ChainIndex},
|
||||
secret_holders::SeedHolder,
|
||||
};
|
||||
|
||||
pub type PublicKey = AffinePoint;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct UserPrivateAccountData {
|
||||
pub key_chain: KeyChain,
|
||||
pub accounts: Vec<(PrivateAccountKind, Account)>,
|
||||
}
|
||||
|
||||
/// Metadata for a shared account (GMS-derived), stored alongside the cached plaintext state.
|
||||
/// The group label and identifier (or PDA seed) are needed to re-derive keys during sync.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SharedAccountEntry {
|
||||
pub group_label: String,
|
||||
pub identifier: Identifier,
|
||||
/// For PDA accounts, the seed and program ID used to derive keys via `derive_keys_for_pda`.
|
||||
/// `None` for regular shared accounts (keys derived from identifier via derivation seed).
|
||||
#[serde(default)]
|
||||
pub pda_seed: Option<nssa_core::program::PdaSeed>,
|
||||
#[serde(default)]
|
||||
pub pda_program_id: Option<nssa_core::program::ProgramId>,
|
||||
pub account: Account,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NSSAUserData {
|
||||
/// Default public accounts.
|
||||
pub default_pub_account_signing_keys: BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
/// Default private accounts.
|
||||
pub default_user_private_accounts: BTreeMap<AccountId, UserPrivateAccountData>,
|
||||
/// Tree of public keys.
|
||||
pub public_key_tree: KeyTreePublic,
|
||||
/// Tree of private keys.
|
||||
pub private_key_tree: KeyTreePrivate,
|
||||
/// Group key holders for shared account management, keyed by a human-readable label.
|
||||
pub group_key_holders: BTreeMap<String, GroupKeyHolder>,
|
||||
/// Cached plaintext state of shared private accounts (PDAs and regular shared accounts),
|
||||
/// keyed by `AccountId`. Each entry stores the group label and identifier needed
|
||||
/// to re-derive keys during sync.
|
||||
pub shared_private_accounts: BTreeMap<nssa::AccountId, SharedAccountEntry>,
|
||||
/// Dedicated sealing secret key for GMS distribution. Generated once via
|
||||
/// `wallet group new-sealing-key`. The corresponding public key is shared with
|
||||
/// group members so they can seal GMS for this wallet.
|
||||
pub sealing_secret_key: Option<crate::key_management::secret_holders::ViewingSecretKey>,
|
||||
}
|
||||
|
||||
impl NSSAUserData {
|
||||
fn valid_public_key_transaction_pairing_check(
|
||||
accounts_keys_map: &BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
) -> bool {
|
||||
let mut check_res = true;
|
||||
for (account_id, key) in accounts_keys_map {
|
||||
let expected_account_id =
|
||||
nssa::AccountId::from(&nssa::PublicKey::new_from_private_key(key));
|
||||
if &expected_account_id != account_id {
|
||||
println!("{expected_account_id}, {account_id}");
|
||||
check_res = false;
|
||||
}
|
||||
}
|
||||
check_res
|
||||
}
|
||||
|
||||
fn valid_private_key_transaction_pairing_check(
|
||||
accounts_keys_map: &BTreeMap<AccountId, UserPrivateAccountData>,
|
||||
) -> bool {
|
||||
let mut check_res = true;
|
||||
for (account_id, entry) in accounts_keys_map {
|
||||
let npk = &entry.key_chain.nullifier_public_key;
|
||||
let any_match = entry
|
||||
.accounts
|
||||
.iter()
|
||||
.any(|(kind, _)| nssa::AccountId::for_private_account(npk, kind) == *account_id);
|
||||
if !any_match {
|
||||
println!("No matching entry found for account_id {account_id}");
|
||||
check_res = false;
|
||||
}
|
||||
}
|
||||
check_res
|
||||
}
|
||||
|
||||
pub fn new_with_accounts(
|
||||
default_accounts_keys: BTreeMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
default_accounts_key_chains: BTreeMap<AccountId, UserPrivateAccountData>,
|
||||
public_key_tree: KeyTreePublic,
|
||||
private_key_tree: KeyTreePrivate,
|
||||
) -> Result<Self> {
|
||||
if !Self::valid_public_key_transaction_pairing_check(&default_accounts_keys) {
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there are public account_ids, which are not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
if !Self::valid_private_key_transaction_pairing_check(&default_accounts_key_chains) {
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there are private account_ids, which are not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
default_pub_account_signing_keys: default_accounts_keys,
|
||||
default_user_private_accounts: default_accounts_key_chains,
|
||||
public_key_tree,
|
||||
private_key_tree,
|
||||
group_key_holders: BTreeMap::new(),
|
||||
shared_private_accounts: BTreeMap::new(),
|
||||
sealing_secret_key: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generated new private key for public transaction signatures.
|
||||
///
|
||||
/// Returns the `account_id` of new account.
|
||||
pub fn generate_new_public_transaction_private_key(
|
||||
&mut self,
|
||||
parent_cci: Option<ChainIndex>,
|
||||
) -> (nssa::AccountId, ChainIndex) {
|
||||
match parent_cci {
|
||||
Some(parent_cci) => self
|
||||
.public_key_tree
|
||||
.generate_new_public_node(&parent_cci)
|
||||
.expect("Parent must be present in a tree"),
|
||||
None => self
|
||||
.public_key_tree
|
||||
.generate_new_public_node_layered()
|
||||
.expect("Search for new node slot failed"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the signing key for public transaction signatures.
|
||||
#[must_use]
|
||||
pub fn get_pub_account_signing_key(
|
||||
&self,
|
||||
account_id: nssa::AccountId,
|
||||
) -> Option<&nssa::PrivateKey> {
|
||||
self.default_pub_account_signing_keys
|
||||
.get(&account_id)
|
||||
.or_else(|| self.public_key_tree.get_node(account_id).map(Into::into))
|
||||
}
|
||||
|
||||
/// Creates a new receiving key node and returns its `ChainIndex`.
|
||||
pub fn create_private_accounts_key(&mut self, parent_cci: Option<ChainIndex>) -> ChainIndex {
|
||||
match parent_cci {
|
||||
Some(parent_cci) => self
|
||||
.private_key_tree
|
||||
.create_private_accounts_key_node(&parent_cci)
|
||||
.expect("Parent must be present in a tree"),
|
||||
None => self
|
||||
.private_key_tree
|
||||
.create_private_accounts_key_node_layered()
|
||||
.expect("Search for new node slot failed"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers an additional identifier on an existing private key node, deriving and recording
|
||||
/// the corresponding `AccountId`. Returns `None` if the node does not exist or the identifier
|
||||
/// is already registered.
|
||||
pub fn register_identifier_on_private_key_chain(
|
||||
&mut self,
|
||||
cci: &ChainIndex,
|
||||
identifier: Identifier,
|
||||
) -> Option<nssa::AccountId> {
|
||||
self.private_key_tree
|
||||
.register_identifier_on_node(cci, identifier)
|
||||
}
|
||||
|
||||
/// Returns the key chain and account data for the given private account ID.
|
||||
#[must_use]
|
||||
pub fn get_private_account(
|
||||
&self,
|
||||
account_id: nssa::AccountId,
|
||||
) -> Option<(KeyChain, nssa_core::account::Account, Identifier)> {
|
||||
// Check default accounts
|
||||
if let Some(entry) = self.default_user_private_accounts.get(&account_id) {
|
||||
let npk = &entry.key_chain.nullifier_public_key;
|
||||
if let Some((kind, account)) = entry
|
||||
.accounts
|
||||
.iter()
|
||||
.find(|(kind, _)| nssa::AccountId::for_private_account(npk, kind) == account_id)
|
||||
{
|
||||
return Some((entry.key_chain.clone(), account.clone(), kind.identifier()));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
// Check tree
|
||||
if let Some(node) = self.private_key_tree.get_node(account_id) {
|
||||
let key_chain = &node.value.0;
|
||||
let npk = &key_chain.nullifier_public_key;
|
||||
if let Some((kind, account)) = node
|
||||
.value
|
||||
.1
|
||||
.iter()
|
||||
.find(|(kind, _)| nssa::AccountId::for_private_account(npk, kind) == account_id)
|
||||
{
|
||||
return Some((key_chain.clone(), account.clone(), kind.identifier()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
||||
self.public_account_ids().chain(self.private_account_ids())
|
||||
}
|
||||
|
||||
pub fn public_account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
||||
self.default_pub_account_signing_keys
|
||||
.keys()
|
||||
.copied()
|
||||
.chain(self.public_key_tree.account_id_map.keys().copied())
|
||||
}
|
||||
|
||||
pub fn private_account_ids(&self) -> impl Iterator<Item = nssa::AccountId> {
|
||||
self.default_user_private_accounts
|
||||
.keys()
|
||||
.copied()
|
||||
.chain(self.private_key_tree.account_id_map.keys().copied())
|
||||
}
|
||||
|
||||
/// Returns the `GroupKeyHolder` for the given label, if it exists.
|
||||
#[must_use]
|
||||
pub fn group_key_holder(&self, label: &str) -> Option<&GroupKeyHolder> {
|
||||
self.group_key_holders.get(label)
|
||||
}
|
||||
|
||||
/// Inserts or replaces a `GroupKeyHolder` under the given label.
|
||||
///
|
||||
/// If a holder already exists under this label, it is silently replaced and the old
|
||||
/// GMS is lost. Callers must ensure label uniqueness across groups.
|
||||
pub fn insert_group_key_holder(&mut self, label: String, holder: GroupKeyHolder) {
|
||||
self.group_key_holders.insert(label, holder);
|
||||
}
|
||||
|
||||
/// Returns the cached account for a shared private account, if it exists.
|
||||
#[must_use]
|
||||
pub fn shared_private_account(
|
||||
&self,
|
||||
account_id: &nssa::AccountId,
|
||||
) -> Option<&SharedAccountEntry> {
|
||||
self.shared_private_accounts.get(account_id)
|
||||
}
|
||||
|
||||
/// Inserts or replaces a shared private account entry.
|
||||
pub fn insert_shared_private_account(
|
||||
&mut self,
|
||||
account_id: nssa::AccountId,
|
||||
entry: SharedAccountEntry,
|
||||
) {
|
||||
self.shared_private_accounts.insert(account_id, entry);
|
||||
}
|
||||
|
||||
/// Updates the cached account state for a shared private account.
|
||||
pub fn update_shared_private_account_state(
|
||||
&mut self,
|
||||
account_id: &nssa::AccountId,
|
||||
account: nssa_core::account::Account,
|
||||
) {
|
||||
if let Some(entry) = self.shared_private_accounts.get_mut(account_id) {
|
||||
entry.account = account;
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over all shared private accounts.
|
||||
pub fn shared_private_accounts_iter(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&nssa::AccountId, &SharedAccountEntry)> {
|
||||
self.shared_private_accounts.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NSSAUserData {
|
||||
fn default() -> Self {
|
||||
let (seed_holder, _mnemonic) = SeedHolder::new_mnemonic("");
|
||||
Self::new_with_accounts(
|
||||
BTreeMap::new(),
|
||||
BTreeMap::new(),
|
||||
KeyTreePublic::new(&seed_holder),
|
||||
KeyTreePrivate::new(&seed_holder),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn group_key_holder_storage_round_trip() {
|
||||
let mut user_data = NSSAUserData::default();
|
||||
assert!(user_data.group_key_holder("test-group").is_none());
|
||||
|
||||
let holder = GroupKeyHolder::from_gms([42_u8; 32]);
|
||||
user_data.insert_group_key_holder(String::from("test-group"), holder.clone());
|
||||
|
||||
let retrieved = user_data
|
||||
.group_key_holder("test-group")
|
||||
.expect("should exist");
|
||||
assert_eq!(retrieved.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_key_holders_default_empty() {
|
||||
let user_data = NSSAUserData::default();
|
||||
assert!(user_data.group_key_holders.is_empty());
|
||||
assert!(user_data.shared_private_accounts.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_account_entry_serde_round_trip() {
|
||||
use nssa_core::program::PdaSeed;
|
||||
|
||||
let entry = SharedAccountEntry {
|
||||
group_label: String::from("test-group"),
|
||||
identifier: 42,
|
||||
pda_seed: None,
|
||||
pda_program_id: None,
|
||||
account: nssa_core::account::Account::default(),
|
||||
};
|
||||
let encoded = bincode::serialize(&entry).expect("serialize");
|
||||
let decoded: SharedAccountEntry = bincode::deserialize(&encoded).expect("deserialize");
|
||||
assert_eq!(decoded.group_label, "test-group");
|
||||
assert_eq!(decoded.identifier, 42);
|
||||
assert!(decoded.pda_seed.is_none());
|
||||
|
||||
let pda_entry = SharedAccountEntry {
|
||||
group_label: String::from("pda-group"),
|
||||
identifier: u128::MAX,
|
||||
pda_seed: Some(PdaSeed::new([7_u8; 32])),
|
||||
pda_program_id: Some([9; 8]),
|
||||
account: nssa_core::account::Account::default(),
|
||||
};
|
||||
let pda_encoded = bincode::serialize(&pda_entry).expect("serialize pda");
|
||||
let pda_decoded: SharedAccountEntry =
|
||||
bincode::deserialize(&pda_encoded).expect("deserialize pda");
|
||||
assert_eq!(pda_decoded.group_label, "pda-group");
|
||||
assert_eq!(pda_decoded.identifier, u128::MAX);
|
||||
assert_eq!(pda_decoded.pda_seed.unwrap(), PdaSeed::new([7_u8; 32]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_account_entry_none_pda_seed_round_trips() {
|
||||
// Verify that an entry with pda_seed=None serializes and deserializes correctly,
|
||||
// confirming the #[serde(default)] attribute works for backward compatibility.
|
||||
let entry = SharedAccountEntry {
|
||||
group_label: String::from("old"),
|
||||
identifier: 1,
|
||||
pda_seed: None,
|
||||
pda_program_id: None,
|
||||
account: nssa_core::account::Account::default(),
|
||||
};
|
||||
let encoded = bincode::serialize(&entry).expect("serialize");
|
||||
let decoded: SharedAccountEntry = bincode::deserialize(&encoded).expect("deserialize");
|
||||
assert_eq!(decoded.group_label, "old");
|
||||
assert_eq!(decoded.identifier, 1);
|
||||
assert!(decoded.pda_seed.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_account_derives_consistent_keys_from_group() {
|
||||
use nssa_core::program::PdaSeed;
|
||||
|
||||
let mut user_data = NSSAUserData::default();
|
||||
let gms_holder = GroupKeyHolder::from_gms([42_u8; 32]);
|
||||
user_data.insert_group_key_holder(String::from("my-group"), gms_holder);
|
||||
|
||||
let holder = user_data.group_key_holder("my-group").unwrap();
|
||||
|
||||
// Regular shared account: derive via tag
|
||||
let tag = [1_u8; 32];
|
||||
let keys_a = holder.derive_keys_for_shared_account(&tag);
|
||||
let keys_b = holder.derive_keys_for_shared_account(&tag);
|
||||
assert_eq!(
|
||||
keys_a.generate_nullifier_public_key(),
|
||||
keys_b.generate_nullifier_public_key(),
|
||||
);
|
||||
|
||||
// PDA shared account: derive via seed
|
||||
let seed = PdaSeed::new([2_u8; 32]);
|
||||
let pda_keys_a = holder.derive_keys_for_pda(&[9; 8], &seed);
|
||||
let pda_keys_b = holder.derive_keys_for_pda(&[9; 8], &seed);
|
||||
assert_eq!(
|
||||
pda_keys_a.generate_nullifier_public_key(),
|
||||
pda_keys_b.generate_nullifier_public_key(),
|
||||
);
|
||||
|
||||
// PDA and shared derivations don't collide
|
||||
assert_ne!(
|
||||
keys_a.generate_nullifier_public_key(),
|
||||
pda_keys_a.generate_nullifier_public_key(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_account() {
|
||||
let mut user_data = NSSAUserData::default();
|
||||
|
||||
let chain_index = user_data.create_private_accounts_key(Some(ChainIndex::root()));
|
||||
|
||||
let is_key_chain_generated = user_data
|
||||
.private_key_tree
|
||||
.key_map
|
||||
.contains_key(&chain_index);
|
||||
assert!(is_key_chain_generated);
|
||||
|
||||
let key_chain = &user_data.private_key_tree.key_map[&chain_index].value.0;
|
||||
println!("{key_chain:#?}");
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ use std::io::Read as _;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::Nullifier;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::encryption::{EphemeralPublicKey, shared_key_derivation::Secp256k1Point};
|
||||
use crate::encryption::EphemeralPublicKey;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::error::NssaCoreError;
|
||||
use crate::{
|
||||
@ -157,25 +157,6 @@ impl Ciphertext {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl Secp256k1Point {
|
||||
/// Converts the point to bytes.
|
||||
#[must_use]
|
||||
pub fn to_bytes(&self) -> [u8; 33] {
|
||||
self.0.clone().try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Deserializes a secp256k1 point from a cursor.
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
let mut value = vec![0; 33];
|
||||
cursor.read_exact(&mut value)?;
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
// Marvin-pq: EphemeralPublicKey is now the ML-KEM-768 ciphertext (1088 bytes) produced by
|
||||
// SharedSecretKey::encapsulate. It replaces the old Secp256k1Point (33 bytes) on the wire.
|
||||
// Fixed size: 1088 bytes for ML-KEM-768 (EncodedUSize + EncodedVSize per FIPS 203 §7.2).
|
||||
#[cfg(feature = "host")]
|
||||
impl EphemeralPublicKey {
|
||||
/// Serializes the ML-KEM-768 ciphertext to bytes (always 1088 bytes).
|
||||
@ -187,7 +168,7 @@ impl EphemeralPublicKey {
|
||||
/// Deserializes an ML-KEM-768 ciphertext from a cursor.
|
||||
/// Reads exactly 1088 bytes — the fixed ciphertext size for ML-KEM-768.
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
let mut value = vec![0u8; 1088];
|
||||
let mut value = vec![0_u8; 1088];
|
||||
cursor.read_exact(&mut value)?;
|
||||
Ok(Self(value))
|
||||
}
|
||||
|
||||
@ -154,4 +154,41 @@ mod tests {
|
||||
|
||||
assert_eq!(account_ct.0.len(), pda_ct.0.len());
|
||||
}
|
||||
|
||||
/// Verifies the full account-note pipeline: ML-KEM-768 encapsulation/decapsulation
|
||||
/// feeds the correct shared secret into the SHA-256 KDF and ChaCha20 round-trip.
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn kem_to_chacha20_round_trip() {
|
||||
let d = [1_u8; 32];
|
||||
let r = [2_u8; 32];
|
||||
let vpk = shared_key_derivation::ViewingPublicKey::from_seed(&d, &r);
|
||||
|
||||
let (sender_ss, epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
let receiver_ss = SharedSecretKey::decapsulate(&epk, &d, &r);
|
||||
|
||||
let account = Account {
|
||||
program_owner: [12_u32; 8],
|
||||
balance: 999,
|
||||
..Account::default()
|
||||
};
|
||||
let kind = PrivateAccountKind::Regular(0);
|
||||
let commitment = crate::Commitment::new(&AccountId::new([7_u8; 32]), &account);
|
||||
|
||||
let ct = EncryptionScheme::encrypt(&account, &kind, &sender_ss, &commitment, 0);
|
||||
let (decoded_kind, decoded_account) =
|
||||
EncryptionScheme::decrypt(&ct, &receiver_ss, &commitment, 0)
|
||||
.expect("decryption must succeed with correct shared secret");
|
||||
|
||||
assert_eq!(decoded_account, account);
|
||||
assert_eq!(decoded_kind, kind);
|
||||
|
||||
// Wrong shared secret must not decrypt correctly.
|
||||
let wrong_ss = SharedSecretKey([0_u8; 32]);
|
||||
let bad = EncryptionScheme::decrypt(&ct, &wrong_ss, &commitment, 0);
|
||||
assert!(
|
||||
bad.is_none() || bad.is_some_and(|(_, a)| a.balance != 999),
|
||||
"wrong shared secret must not produce the correct plaintext"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,48 +1,8 @@
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "Multiplication of finite field elements can't overflow"
|
||||
)]
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use k256::{
|
||||
AffinePoint, FieldBytes, ProjectivePoint,
|
||||
elliptic_curve::{PrimeField as _, sec1::ToEncodedPoint as _},
|
||||
};
|
||||
use ml_kem::{Decapsulate as _, Encapsulate as _, KeyExport as _, Seed};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{SharedSecretKey, encryption::Scalar};
|
||||
/// Marvin-pq check this
|
||||
/// A compressed secp256k1 point (33 bytes).
|
||||
/// Kept for backward compatibility with Phase-2+ callers; no longer used as `EphemeralPublicKey`.
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Secp256k1Point(pub Vec<u8>);
|
||||
|
||||
impl std::fmt::Debug for Secp256k1Point {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let hex: String = self.0.iter().fold(String::new(), |mut acc, b| {
|
||||
write!(acc, "{b:02x}").expect("writing to string should not fail");
|
||||
acc
|
||||
});
|
||||
write!(f, "Secp256k1Point({hex})")
|
||||
}
|
||||
}
|
||||
|
||||
impl Secp256k1Point {
|
||||
#[must_use]
|
||||
pub fn from_scalar(value: Scalar) -> Self {
|
||||
let x_bytes: FieldBytes = value.into();
|
||||
let x = k256::Scalar::from_repr(x_bytes).unwrap();
|
||||
|
||||
let p = ProjectivePoint::GENERATOR * x;
|
||||
let q = AffinePoint::from(p);
|
||||
let enc = q.to_encoded_point(true);
|
||||
|
||||
Self(enc.as_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
use crate::SharedSecretKey;
|
||||
|
||||
/// The ML-KEM-768 ciphertext produced during encapsulation; transmitted on-wire in place of the
|
||||
/// former ECDH ephemeral public key. Always 1088 bytes for ML-KEM-768.
|
||||
@ -50,7 +10,7 @@ impl Secp256k1Point {
|
||||
pub struct EphemeralPublicKey(pub Vec<u8>);
|
||||
|
||||
/// ML-KEM-768 encapsulation key bytes (1184 bytes, opaque to this crate).
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ViewingPublicKey(pub Vec<u8>);
|
||||
|
||||
impl ViewingPublicKey {
|
||||
@ -113,7 +73,7 @@ impl SharedSecretKey {
|
||||
input.extend_from_slice(message_hash);
|
||||
input.extend_from_slice(&output_index.to_le_bytes());
|
||||
let hash = Impl::hash_bytes(&input);
|
||||
let m: ml_kem::B32 = ml_kem::array::Array::try_from(hash.as_bytes() as &[u8])
|
||||
let m: ml_kem::B32 = ml_kem::array::Array::try_from(hash.as_bytes())
|
||||
.expect("SHA-256 output is 32 bytes");
|
||||
|
||||
let ek_bytes: ml_kem::kem::Key<ml_kem::EncapsulationKey768> = vpk
|
||||
@ -159,8 +119,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn encapsulate_decapsulate_round_trip() {
|
||||
let d = [1u8; 32];
|
||||
let r = [2u8; 32];
|
||||
let d = [1_u8; 32];
|
||||
let r = [2_u8; 32];
|
||||
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(&d);
|
||||
@ -184,8 +144,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn different_vpks_produce_different_shared_secrets() {
|
||||
let (d1, r1) = ([1u8; 32], [2u8; 32]);
|
||||
let (d2, r2) = ([3u8; 32], [4u8; 32]);
|
||||
let (d1, r1) = ([1_u8; 32], [2_u8; 32]);
|
||||
let (d2, r2) = ([3_u8; 32], [4_u8; 32]);
|
||||
|
||||
let vpk1 = {
|
||||
let mut seed = Seed::default();
|
||||
|
||||
@ -244,7 +244,7 @@ mod tests {
|
||||
let expected_sender_pre = sender.clone();
|
||||
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![sender, recipient],
|
||||
@ -341,10 +341,10 @@ mod tests {
|
||||
];
|
||||
|
||||
let shared_secret_1 =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let shared_secret_2 =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0u8; 32], 1).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 1).0;
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![sender_pre, recipient],
|
||||
@ -419,7 +419,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let program_with_deps = ProgramWithDependencies::new(
|
||||
validity_window_chain_caller,
|
||||
@ -450,7 +450,7 @@ mod tests {
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let identifier: u128 = 99;
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, identifier);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -488,7 +488,7 @@ mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret_pda =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// PDA (new, mask 3)
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 0);
|
||||
@ -527,7 +527,7 @@ mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret_pda =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// PDA (new, private PDA)
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 0);
|
||||
@ -582,7 +582,7 @@ mod tests {
|
||||
let shared_npk = shared_keys.npk();
|
||||
let shared_identifier: u128 = 42;
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&shared_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&shared_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// Sender: public account with balance, owned by auth-transfer
|
||||
let sender_id = AccountId::new([99; 32]);
|
||||
@ -633,7 +633,7 @@ mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let account_id = AccountId::for_regular_private_account(&keys.npk(), identifier);
|
||||
let pre = AccountWithMetadata::new(Account::default(), true, account_id);
|
||||
|
||||
@ -663,7 +663,7 @@ mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let sender = AccountWithMetadata::new(
|
||||
Account {
|
||||
@ -708,7 +708,7 @@ mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let account_id = AccountId::for_regular_private_account(&keys.npk(), identifier);
|
||||
let account = Account {
|
||||
program_owner: program.id(),
|
||||
@ -757,7 +757,7 @@ mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let auth_transfer_id = auth_transfer.id();
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, identifier);
|
||||
@ -812,7 +812,7 @@ mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 5);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -838,7 +838,7 @@ mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let auth_transfer_id = auth_transfer.id();
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 5);
|
||||
|
||||
@ -208,7 +208,7 @@ pub mod tests {
|
||||
let nonces_bytes: &[u8] = &[1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
// all remaining vec fields are empty: u32 len=0
|
||||
let empty_vec_bytes: &[u8] = &[0_u8; 4];
|
||||
// validity windows: unbounded = {from: None (0u8), to: None (0u8)}
|
||||
// validity windows: unbounded = {from: None (0_u8), to: None (0_u8)}
|
||||
let unbounded_window_bytes: &[u8] = &[0_u8; 2];
|
||||
|
||||
let expected_borsh_vec: Vec<u8> = [
|
||||
@ -246,11 +246,11 @@ pub mod tests {
|
||||
#[test]
|
||||
fn encrypted_account_data_constructor() {
|
||||
let npk = NullifierPublicKey::from(&[1; 32]);
|
||||
let vpk = ViewingPublicKey::from_seed(&[2u8; 32], &[3u8; 32]);
|
||||
let vpk = ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]);
|
||||
let account = Account::default();
|
||||
let account_id = nssa_core::account::AccountId::for_regular_private_account(&npk, 0);
|
||||
let commitment = Commitment::new(&account_id, &account);
|
||||
let (shared_secret, epk) = SharedSecretKey::encapsulate_deterministic(&vpk, &[0u8; 32], 0);
|
||||
let (shared_secret, epk) = SharedSecretKey::encapsulate_deterministic(&vpk, &[0_u8; 32], 0);
|
||||
let ciphertext = EncryptionScheme::encrypt(
|
||||
&account,
|
||||
&PrivateAccountKind::Regular(0),
|
||||
|
||||
@ -1345,7 +1345,7 @@ pub mod tests {
|
||||
AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
|
||||
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![sender, recipient],
|
||||
@ -1396,10 +1396,10 @@ pub mod tests {
|
||||
AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
|
||||
|
||||
let (shared_secret_1, epk_1) =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (shared_secret_2, epk_2) =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0u8; 32], 1);
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 1);
|
||||
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![sender_pre, recipient_pre],
|
||||
@ -1464,7 +1464,7 @@ pub mod tests {
|
||||
);
|
||||
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![sender_pre, recipient_pre],
|
||||
@ -1974,7 +1974,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -1986,7 +1986,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2030,7 +2030,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2042,7 +2042,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2086,7 +2086,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2098,7 +2098,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2142,7 +2142,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2154,7 +2154,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2198,7 +2198,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2210,7 +2210,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2252,7 +2252,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2264,7 +2264,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2287,7 +2287,7 @@ pub mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let public_account_1 = AccountWithMetadata::new(
|
||||
Account {
|
||||
program_owner: program.id(),
|
||||
@ -2329,7 +2329,7 @@ pub mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, u128::MAX);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -2366,7 +2366,7 @@ pub mod tests {
|
||||
let npk_b = keys_b.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys_b.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys_b.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// `account_id` is derived from `npk_a`, but `npk_b` is supplied for this pre_state.
|
||||
// `AccountId::for_private_pda(program, seed, npk_b) != account_id`, so the claim check in
|
||||
@ -2401,7 +2401,7 @@ pub mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([77; 32]);
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk, u128::MAX);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -2440,7 +2440,7 @@ pub mod tests {
|
||||
let claim_seed = PdaSeed::new([77; 32]);
|
||||
let wrong_delegated_seed = PdaSeed::new([88; 32]);
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&delegator.id(), &claim_seed, &npk, u128::MAX);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -2477,8 +2477,8 @@ pub mod tests {
|
||||
let keys_a = test_private_account_keys_1();
|
||||
let keys_b = test_private_account_keys_2();
|
||||
let seed = PdaSeed::new([55; 32]);
|
||||
let shared_a = SharedSecretKey::encapsulate_deterministic(&keys_a.vpk(), &[0u8; 32], 0).0;
|
||||
let shared_b = SharedSecretKey::encapsulate_deterministic(&keys_b.vpk(), &[0u8; 32], 0).0;
|
||||
let shared_a = SharedSecretKey::encapsulate_deterministic(&keys_a.vpk(), &[0_u8; 32], 0).0;
|
||||
let shared_b = SharedSecretKey::encapsulate_deterministic(&keys_b.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_a = AccountId::for_private_pda(&program.id(), &seed, &keys_a.npk(), u128::MAX);
|
||||
let account_b = AccountId::for_private_pda(&program.id(), &seed, &keys_b.npk(), u128::MAX);
|
||||
@ -2524,7 +2524,7 @@ pub mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let seed = PdaSeed::new([99; 32]);
|
||||
|
||||
// Simulate a previously-claimed private PDA: program_owner != DEFAULT, is_authorized =
|
||||
@ -2624,7 +2624,7 @@ pub mod tests {
|
||||
);
|
||||
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let result = execute_and_prove(
|
||||
vec![private_account_1.clone(), private_account_1],
|
||||
Program::serialize_instruction(100_u128).unwrap(),
|
||||
@ -2969,7 +2969,7 @@ pub mod tests {
|
||||
let recipient_pre =
|
||||
AccountWithMetadata::new(Account::default(), true, recipient_account_id);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let balance = 37;
|
||||
|
||||
@ -3074,10 +3074,10 @@ pub mod tests {
|
||||
);
|
||||
|
||||
let (from_ss, from_epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&from_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&from_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (to_ss, to_epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&to_keys.vpk(), &[0u8; 32], 1);
|
||||
SharedSecretKey::encapsulate_deterministic(&to_keys.vpk(), &[0_u8; 32], 1);
|
||||
|
||||
let mut dependencies = HashMap::new();
|
||||
|
||||
@ -3375,7 +3375,7 @@ pub mod tests {
|
||||
|
||||
// Set up parameters for the new account
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = authenticated_transfer_core::Instruction::Initialize;
|
||||
|
||||
@ -3426,7 +3426,7 @@ pub mod tests {
|
||||
|
||||
let program = Program::claimer();
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![unauthorized_account],
|
||||
@ -3475,7 +3475,7 @@ pub mod tests {
|
||||
|
||||
// Set up parameters for claiming the new account
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = authenticated_transfer_core::Instruction::Initialize;
|
||||
|
||||
@ -3524,7 +3524,7 @@ pub mod tests {
|
||||
|
||||
let noop_program = Program::noop();
|
||||
let shared_secret2 =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// Step 3: Try to execute noop program with authentication but without initialization
|
||||
let res = execute_and_prove(
|
||||
@ -3608,7 +3608,7 @@ pub mod tests {
|
||||
vec![private_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0)
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
@ -3635,7 +3635,7 @@ pub mod tests {
|
||||
vec![private_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0u8; 32], 0)
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
@ -3683,7 +3683,7 @@ pub mod tests {
|
||||
let instruction = (balance_to_transfer, auth_transfers.id());
|
||||
|
||||
let recipient =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0u8; 32], 0).0;
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let mut dependencies = HashMap::new();
|
||||
dependencies.insert(auth_transfers.id(), auth_transfers);
|
||||
@ -3840,7 +3840,7 @@ pub mod tests {
|
||||
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs();
|
||||
let tx = {
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = (
|
||||
block_validity_window,
|
||||
@ -3909,7 +3909,7 @@ pub mod tests {
|
||||
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs();
|
||||
let tx = {
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = (
|
||||
BlockValidityWindow::new_unbounded(),
|
||||
@ -4464,9 +4464,9 @@ pub mod tests {
|
||||
};
|
||||
|
||||
let (alice_shared_0, alice_epk_0) =
|
||||
SharedSecretKey::encapsulate_deterministic(&alice_keys.vpk(), &[0u8; 32], 0);
|
||||
SharedSecretKey::encapsulate_deterministic(&alice_keys.vpk(), &[0_u8; 32], 0);
|
||||
let (alice_shared_1, alice_epk_1) =
|
||||
SharedSecretKey::encapsulate_deterministic(&alice_keys.vpk(), &[0u8; 32], 1);
|
||||
SharedSecretKey::encapsulate_deterministic(&alice_keys.vpk(), &[0_u8; 32], 1);
|
||||
|
||||
// Fund alice_pda_0
|
||||
{
|
||||
@ -4576,7 +4576,7 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![Nonce(0)],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_0.clone())],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_0)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
@ -4616,7 +4616,7 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_1.clone())],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_1)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -129,20 +129,20 @@ pub fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialD
|
||||
secret_spending_key: SecretSpendingKey(SSK_PRIV_ACC_A),
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: NSK_PRIV_ACC_A,
|
||||
viewing_secret_key: ViewingSecretKey { d: VSK_PRIV_ACC_A, r: [0u8; 32] },
|
||||
viewing_secret_key: ViewingSecretKey { d: VSK_PRIV_ACC_A, r: [0_u8; 32] },
|
||||
},
|
||||
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_A),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_PRIV_ACC_A, &[0u8; 32]),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_PRIV_ACC_A, &[0_u8; 32]),
|
||||
};
|
||||
|
||||
let key_chain_2 = KeyChain {
|
||||
secret_spending_key: SecretSpendingKey(SSK_PRIV_ACC_B),
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: NSK_PRIV_ACC_B,
|
||||
viewing_secret_key: ViewingSecretKey { d: VSK_PRIV_ACC_B, r: [0u8; 32] },
|
||||
viewing_secret_key: ViewingSecretKey { d: VSK_PRIV_ACC_B, r: [0_u8; 32] },
|
||||
},
|
||||
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_B),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_PRIV_ACC_B, &[0u8; 32]),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_PRIV_ACC_B, &[0_u8; 32]),
|
||||
};
|
||||
|
||||
vec![
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
//! Measures:
|
||||
//! - `KeyChain::new_os_random` (mnemonic → SSK → NSK/VSK + public keys)
|
||||
//! - `KeyChain::new_mnemonic` (same, but mnemonic exposed)
|
||||
//! - `SharedSecretKey::new` (Diffie-Hellman shared key derivation, the per-recipient cost)
|
||||
//! - `SharedSecretKey::encapsulate` (ML-KEM-768 encapsulation, the per-recipient cost)
|
||||
//! - `EncryptionScheme::encrypt` / `decrypt` (Account note encryption)
|
||||
//!
|
||||
//! Reports best-of-N wall time per operation. No live stack required.
|
||||
@ -24,10 +24,8 @@ use key_protocol::key_management::KeyChain;
|
||||
use nssa_core::{
|
||||
Commitment, EncryptionScheme, SharedSecretKey,
|
||||
account::{Account, AccountId},
|
||||
encryption::{EphemeralPublicKey, EphemeralSecretKey},
|
||||
program::PrivateAccountKind,
|
||||
};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use serde::Serialize;
|
||||
|
||||
const ITERS: usize = 100;
|
||||
@ -84,28 +82,19 @@ fn main() -> Result<()> {
|
||||
let (_kc, _mnemonic) = KeyChain::new_mnemonic("");
|
||||
}));
|
||||
|
||||
// SharedSecretKey: caller has ephemeral secret, recipient has VSK→VPK.
|
||||
// We bench the SENDER side: derive ephemeral pubkey, then SharedSecretKey::new(scalar, point).
|
||||
// SharedSecretKey: caller has recipient VPK; we bench the SENDER side —
|
||||
// ML-KEM-768 encapsulation (replaces the old ECDH scalar multiplication).
|
||||
let recipient_kc = KeyChain::new_os_random();
|
||||
let vpk = recipient_kc.viewing_public_key;
|
||||
results.push(time("SharedSecretKey::new (sender DH)", ITERS, || {
|
||||
let mut bytes = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
let esk: EphemeralSecretKey = bytes;
|
||||
let _epk = EphemeralPublicKey::from(&esk);
|
||||
let _ssk = SharedSecretKey::new(esk, &vpk);
|
||||
results.push(time("SharedSecretKey::encapsulate (sender KEM)", ITERS, || {
|
||||
let (_ssk, _epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
}));
|
||||
|
||||
// EncryptionScheme::encrypt / decrypt over a small Account note.
|
||||
let account = Account::default();
|
||||
let account_id = AccountId::new([7; 32]);
|
||||
let commitment = Commitment::new(&account_id, &account);
|
||||
let shared = {
|
||||
let mut bytes = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
let esk: EphemeralSecretKey = bytes;
|
||||
SharedSecretKey::new(esk, &vpk)
|
||||
};
|
||||
let (shared, _epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
let kind = PrivateAccountKind::Regular(0_u128);
|
||||
let output_index: u32 = 0;
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ pub unsafe extern "C" fn wallet_ffi_get_private_account_keys(
|
||||
// NPK is a 32-byte array
|
||||
let npk_bytes = key_chain.nullifier_public_key.0;
|
||||
|
||||
// VPK is a compressed secp256k1 point (33 bytes)
|
||||
// VPK is an ML-KEM-768 encapsulation key (1184 bytes)
|
||||
let vpk_bytes = key_chain.viewing_public_key.to_bytes();
|
||||
let vpk_len = vpk_bytes.len();
|
||||
let vpk_vec = vpk_bytes.to_vec();
|
||||
|
||||
@ -71,9 +71,9 @@ impl Default for FfiAccount {
|
||||
pub struct FfiPrivateAccountKeys {
|
||||
/// Nullifier public key (32 bytes).
|
||||
pub nullifier_public_key: FfiBytes32,
|
||||
/// viewing public key (compressed secp256k1 point).
|
||||
/// Viewing public key (ML-KEM-768 encapsulation key, 1184 bytes).
|
||||
pub viewing_public_key: *const u8,
|
||||
/// Length of viewing public key (typically 33 bytes).
|
||||
/// Length of viewing public key (always 1184 bytes for ML-KEM-768).
|
||||
pub viewing_public_key_len: usize,
|
||||
}
|
||||
|
||||
|
||||
@ -135,11 +135,11 @@ typedef struct FfiPrivateAccountKeys {
|
||||
*/
|
||||
struct FfiBytes32 nullifier_public_key;
|
||||
/**
|
||||
* viewing public key (compressed secp256k1 point).
|
||||
* Viewing public key (ML-KEM-768 encapsulation key, 1184 bytes).
|
||||
*/
|
||||
const uint8_t *viewing_public_key;
|
||||
/**
|
||||
* Length of viewing public key (typically 33 bytes).
|
||||
* Length of viewing public key (always 1184 bytes for ML-KEM-768).
|
||||
*/
|
||||
uintptr_t viewing_public_key_len;
|
||||
} FfiPrivateAccountKeys;
|
||||
|
||||
@ -266,8 +266,8 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
/// Set the wallet's dedicated sealing secret key.
|
||||
pub fn set_sealing_secret_key(&mut self, key: key_protocol::key_management::secret_holders::ViewingSecretKey) {
|
||||
self.storage.user_data.sealing_secret_key = Some(key);
|
||||
pub const fn set_sealing_secret_key(&mut self, key: key_protocol::key_management::secret_holders::ViewingSecretKey) {
|
||||
self.storage.key_chain_mut().set_sealing_secret_key(key);
|
||||
}
|
||||
|
||||
/// Resolve an `AccountId` to the appropriate `PrivacyPreservingAccount` variant.
|
||||
|
||||
@ -414,7 +414,7 @@ mod tests {
|
||||
let acc = PrivacyPreservingAccount::PrivateShared {
|
||||
nsk: [0; 32],
|
||||
npk: NullifierPublicKey([1; 32]),
|
||||
vpk: ViewingPublicKey::from_seed(&[2u8; 32], &[3u8; 32]),
|
||||
vpk: ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]),
|
||||
identifier: 42,
|
||||
};
|
||||
assert!(acc.is_private());
|
||||
|
||||
@ -6,7 +6,7 @@ use key_protocol::key_management::{
|
||||
KeyChain,
|
||||
group_key_holder::GroupKeyHolder,
|
||||
key_tree::{KeyTreePrivate, KeyTreePublic, chain_index::ChainIndex, traits::KeyTreeNode as _},
|
||||
secret_holders::SeedHolder,
|
||||
secret_holders::{SeedHolder, ViewingSecretKey},
|
||||
};
|
||||
use log::{debug, warn};
|
||||
use nssa::{Account, AccountId};
|
||||
@ -79,7 +79,7 @@ pub struct UserKeyChain {
|
||||
/// Dedicated sealing secret key for GMS distribution. Generated once via
|
||||
/// `wallet group new-sealing-key`. The corresponding public key is shared with
|
||||
/// group members so they can seal GMS for this wallet.
|
||||
sealing_secret_key: Option<nssa_core::encryption::Scalar>,
|
||||
sealing_secret_key: Option<ViewingSecretKey>,
|
||||
}
|
||||
|
||||
impl UserKeyChain {
|
||||
@ -509,12 +509,12 @@ impl UserKeyChain {
|
||||
|
||||
/// Returns the sealing secret key for GMS distribution, if it exists.
|
||||
#[must_use]
|
||||
pub const fn sealing_secret_key(&self) -> Option<nssa_core::encryption::Scalar> {
|
||||
self.sealing_secret_key
|
||||
pub const fn sealing_secret_key(&self) -> Option<&ViewingSecretKey> {
|
||||
self.sealing_secret_key.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the sealing secret key for GMS distribution.
|
||||
pub const fn set_sealing_secret_key(&mut self, key: nssa_core::encryption::Scalar) {
|
||||
pub const fn set_sealing_secret_key(&mut self, key: ViewingSecretKey) {
|
||||
self.sealing_secret_key = Some(key);
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ impl UserKeyChain {
|
||||
|
||||
KeyChainPersistentData {
|
||||
accounts,
|
||||
sealing_secret_key: *sealing_secret_key,
|
||||
sealing_secret_key: sealing_secret_key.clone(),
|
||||
group_key_holders: group_key_holders.clone(),
|
||||
shared_private_accounts: shared_private_accounts.clone(),
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ use key_protocol::key_management::{
|
||||
key_tree::{
|
||||
chain_index::ChainIndex, keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic,
|
||||
},
|
||||
secret_holders::ViewingSecretKey,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use testnet_initial_state::{PrivateAccountPrivateInitialData, PublicAccountPrivateInitialData};
|
||||
@ -26,7 +27,7 @@ pub struct PersistentStorage {
|
||||
pub struct KeyChainPersistentData {
|
||||
pub accounts: Vec<PersistentAccountData>,
|
||||
#[serde(default)]
|
||||
pub sealing_secret_key: Option<nssa_core::encryption::Scalar>,
|
||||
pub sealing_secret_key: Option<ViewingSecretKey>,
|
||||
#[serde(default)]
|
||||
pub group_key_holders: BTreeMap<Label, GroupKeyHolder>,
|
||||
#[serde(default)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user