diff --git a/artifacts/program_methods/privacy_preserving_circuit.bin b/artifacts/program_methods/privacy_preserving_circuit.bin index 5a66dc17..57cda10b 100644 Binary files a/artifacts/program_methods/privacy_preserving_circuit.bin and b/artifacts/program_methods/privacy_preserving_circuit.bin differ diff --git a/artifacts/test_program_methods/auth_asserting_noop.bin b/artifacts/test_program_methods/auth_asserting_noop.bin index eddfb92b..b4252de3 100644 Binary files a/artifacts/test_program_methods/auth_asserting_noop.bin and b/artifacts/test_program_methods/auth_asserting_noop.bin differ diff --git a/artifacts/test_program_methods/burner.bin b/artifacts/test_program_methods/burner.bin index 9373e039..69828f5f 100644 Binary files a/artifacts/test_program_methods/burner.bin and b/artifacts/test_program_methods/burner.bin differ diff --git a/artifacts/test_program_methods/chain_caller.bin b/artifacts/test_program_methods/chain_caller.bin index b0a65b7e..35cd9b76 100644 Binary files a/artifacts/test_program_methods/chain_caller.bin and b/artifacts/test_program_methods/chain_caller.bin differ diff --git a/artifacts/test_program_methods/changer_claimer.bin b/artifacts/test_program_methods/changer_claimer.bin index 42885442..c16d1bf4 100644 Binary files a/artifacts/test_program_methods/changer_claimer.bin and b/artifacts/test_program_methods/changer_claimer.bin differ diff --git a/artifacts/test_program_methods/clock_chain_caller.bin b/artifacts/test_program_methods/clock_chain_caller.bin index 1ff6f849..eb3849ba 100644 Binary files a/artifacts/test_program_methods/clock_chain_caller.bin and b/artifacts/test_program_methods/clock_chain_caller.bin differ diff --git a/artifacts/test_program_methods/data_changer.bin b/artifacts/test_program_methods/data_changer.bin index c7b4900f..3df1213b 100644 Binary files a/artifacts/test_program_methods/data_changer.bin and b/artifacts/test_program_methods/data_changer.bin differ diff --git a/artifacts/test_program_methods/extra_output.bin b/artifacts/test_program_methods/extra_output.bin index e7d210db..d8e9453b 100644 Binary files a/artifacts/test_program_methods/extra_output.bin and b/artifacts/test_program_methods/extra_output.bin differ diff --git a/artifacts/test_program_methods/flash_swap_callback.bin b/artifacts/test_program_methods/flash_swap_callback.bin index 84f245f5..8096c5d5 100644 Binary files a/artifacts/test_program_methods/flash_swap_callback.bin and b/artifacts/test_program_methods/flash_swap_callback.bin differ diff --git a/artifacts/test_program_methods/flash_swap_initiator.bin b/artifacts/test_program_methods/flash_swap_initiator.bin index a374d90e..0c06e900 100644 Binary files a/artifacts/test_program_methods/flash_swap_initiator.bin and b/artifacts/test_program_methods/flash_swap_initiator.bin differ diff --git a/artifacts/test_program_methods/malicious_authorization_changer.bin b/artifacts/test_program_methods/malicious_authorization_changer.bin index bd70757e..7db291f8 100644 Binary files a/artifacts/test_program_methods/malicious_authorization_changer.bin and b/artifacts/test_program_methods/malicious_authorization_changer.bin differ diff --git a/artifacts/test_program_methods/malicious_caller_program_id.bin b/artifacts/test_program_methods/malicious_caller_program_id.bin index b7538bf6..4f2f2fe6 100644 Binary files a/artifacts/test_program_methods/malicious_caller_program_id.bin and b/artifacts/test_program_methods/malicious_caller_program_id.bin differ diff --git a/artifacts/test_program_methods/malicious_self_program_id.bin b/artifacts/test_program_methods/malicious_self_program_id.bin index 3ee29400..da80c542 100644 Binary files a/artifacts/test_program_methods/malicious_self_program_id.bin and b/artifacts/test_program_methods/malicious_self_program_id.bin differ diff --git a/artifacts/test_program_methods/noop.bin b/artifacts/test_program_methods/noop.bin index 80d37034..d9e00f30 100644 Binary files a/artifacts/test_program_methods/noop.bin and b/artifacts/test_program_methods/noop.bin differ diff --git a/artifacts/test_program_methods/pda_claimer.bin b/artifacts/test_program_methods/pda_claimer.bin index f5d8f49b..916ddaef 100644 Binary files a/artifacts/test_program_methods/pda_claimer.bin and b/artifacts/test_program_methods/pda_claimer.bin differ diff --git a/artifacts/test_program_methods/private_pda_delegator.bin b/artifacts/test_program_methods/private_pda_delegator.bin index 33948d96..a00de630 100644 Binary files a/artifacts/test_program_methods/private_pda_delegator.bin and b/artifacts/test_program_methods/private_pda_delegator.bin differ diff --git a/artifacts/test_program_methods/simple_balance_transfer.bin b/artifacts/test_program_methods/simple_balance_transfer.bin index 12f5d9fd..bfa38005 100644 Binary files a/artifacts/test_program_methods/simple_balance_transfer.bin and b/artifacts/test_program_methods/simple_balance_transfer.bin differ diff --git a/artifacts/test_program_methods/time_locked_transfer.bin b/artifacts/test_program_methods/time_locked_transfer.bin index 7214c10f..d1050b1c 100644 Binary files a/artifacts/test_program_methods/time_locked_transfer.bin and b/artifacts/test_program_methods/time_locked_transfer.bin differ diff --git a/artifacts/test_program_methods/two_pda_claimer.bin b/artifacts/test_program_methods/two_pda_claimer.bin index 8eba6554..6c8d7a5d 100644 Binary files a/artifacts/test_program_methods/two_pda_claimer.bin and b/artifacts/test_program_methods/two_pda_claimer.bin differ diff --git a/artifacts/test_program_methods/validity_window.bin b/artifacts/test_program_methods/validity_window.bin index daca701e..8f36964a 100644 Binary files a/artifacts/test_program_methods/validity_window.bin and b/artifacts/test_program_methods/validity_window.bin differ diff --git a/artifacts/test_program_methods/validity_window_chain_caller.bin b/artifacts/test_program_methods/validity_window_chain_caller.bin index f7c9bab1..f936dbe8 100644 Binary files a/artifacts/test_program_methods/validity_window_chain_caller.bin and b/artifacts/test_program_methods/validity_window_chain_caller.bin differ diff --git a/nssa/core/src/encryption/mod.rs b/nssa/core/src/encryption/mod.rs index a190d691..5ac5f020 100644 --- a/nssa/core/src/encryption/mod.rs +++ b/nssa/core/src/encryption/mod.rs @@ -33,6 +33,10 @@ pub enum PrivateAccountKind { } impl PrivateAccountKind { + /// Account(ident): 0x00 || ident (16 LE) || [0u8; 64] + /// Pda { program_id, seed, ident }: 0x01 || program_id (32 LE) || seed (32) || ident (16 LE) + pub const HEADER_LEN: usize = 81; + #[must_use] pub fn identifier(&self) -> Identifier { match self { @@ -40,6 +44,50 @@ impl PrivateAccountKind { Self::Pda { identifier, .. } => *identifier, } } + + #[must_use] + pub fn to_header_bytes(&self) -> [u8; Self::HEADER_LEN] { + let mut bytes = [0u8; Self::HEADER_LEN]; + match self { + Self::Account(identifier) => { + bytes[0] = 0x00; + bytes[1..17].copy_from_slice(&identifier.to_le_bytes()); + // bytes[17..81] are zero padding + } + Self::Pda { program_id, seed, identifier } => { + bytes[0] = 0x01; + for (i, &word) in program_id.iter().enumerate() { + bytes[1 + i * 4..1 + (i + 1) * 4].copy_from_slice(&word.to_le_bytes()); + } + bytes[33..65].copy_from_slice(seed.as_bytes()); + bytes[65..81].copy_from_slice(&identifier.to_le_bytes()); + } + } + bytes + } + + #[cfg(feature = "host")] + #[must_use] + pub fn from_header_bytes(bytes: &[u8; Self::HEADER_LEN]) -> Option { + match bytes[0] { + 0x00 => { + let identifier = Identifier::from_le_bytes(bytes[1..17].try_into().unwrap()); + Some(Self::Account(identifier)) + } + 0x01 => { + let mut program_id = [0u32; 8]; + for (i, word) in program_id.iter_mut().enumerate() { + *word = u32::from_le_bytes( + bytes[1 + i * 4..1 + (i + 1) * 4].try_into().unwrap(), + ); + } + let seed = PdaSeed::new(bytes[33..65].try_into().unwrap()); + let identifier = Identifier::from_le_bytes(bytes[65..81].try_into().unwrap()); + Some(Self::Pda { program_id, seed, identifier }) + } + _ => None, + } + } } #[derive(Serialize, Deserialize, Clone, Copy)] @@ -68,13 +116,14 @@ impl EncryptionScheme { #[must_use] pub fn encrypt( account: &Account, - identifier: Identifier, + kind: &PrivateAccountKind, shared_secret: &SharedSecretKey, commitment: &Commitment, output_index: u32, ) -> Ciphertext { - // Plaintext: identifier (16 bytes, little-endian) || account bytes - let mut buffer = identifier.to_le_bytes().to_vec(); + // Plaintext: PrivateAccountKind::HEADER_LEN bytes header || account bytes. + // Both variants produce the same header length — see PrivateAccountKind::to_header_bytes. + let mut buffer = kind.to_header_bytes().to_vec(); buffer.extend_from_slice(&account.to_bytes()); Self::symmetric_transform(&mut buffer, shared_secret, commitment, output_index); Ciphertext(buffer) @@ -117,17 +166,19 @@ impl EncryptionScheme { shared_secret: &SharedSecretKey, commitment: &Commitment, output_index: u32, - ) -> Option<(Identifier, Account)> { + ) -> Option<(PrivateAccountKind, Account)> { use std::io::Cursor; let mut buffer = ciphertext.0.clone(); Self::symmetric_transform(&mut buffer, shared_secret, commitment, output_index); - if buffer.len() < 16 { + if buffer.len() < PrivateAccountKind::HEADER_LEN { return None; } - let identifier = Identifier::from_le_bytes(buffer[..16].try_into().unwrap()); + let header: &[u8; PrivateAccountKind::HEADER_LEN] = + buffer[..PrivateAccountKind::HEADER_LEN].try_into().unwrap(); + let kind = PrivateAccountKind::from_header_bytes(header)?; - let mut cursor = Cursor::new(&buffer[16..]); + let mut cursor = Cursor::new(&buffer[PrivateAccountKind::HEADER_LEN..]); Account::from_cursor(&mut cursor) .inspect_err(|err| { println!( @@ -140,6 +191,40 @@ impl EncryptionScheme { ); }) .ok() - .map(|account| (identifier, account)) + .map(|account| (kind, account)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::account::{Account, AccountId}; + + #[test] + fn encrypt_same_length_for_account_and_pda() { + let account = Account::default(); + let secret = SharedSecretKey([0u8; 32]); + let commitment = crate::Commitment::new(&AccountId::new([0u8; 32]), &Account::default()); + + let account_ct = EncryptionScheme::encrypt( + &account, + &PrivateAccountKind::Account(42), + &secret, + &commitment, + 0, + ); + let pda_ct = EncryptionScheme::encrypt( + &account, + &PrivateAccountKind::Pda { + program_id: [1u32; 8], + seed: PdaSeed::new([2u8; 32]), + identifier: 42, + }, + &secret, + &commitment, + 0, + ); + + assert_eq!(account_ct.0.len(), pda_ct.0.len()); } } diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index 14390f25..8a4a3a25 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -35,6 +35,11 @@ impl PdaSeed { pub const fn new(value: [u8; 32]) -> Self { Self(value) } + + #[must_use] + pub const fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } } impl AccountId { diff --git a/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/program_methods/guest/src/bin/privacy_preserving_circuit.rs index 1aedfd34..13101b0b 100644 --- a/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -5,6 +5,7 @@ use std::{ use nssa_core::{ Commitment, CommitmentSetDigest, DUMMY_COMMITMENT_HASH, EncryptionScheme, Identifier, + PrivateAccountKind, MembershipProof, Nullifier, NullifierPublicKey, NullifierSecretKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, SharedSecretKey, account::{Account, AccountId, AccountWithMetadata, Nonce}, @@ -606,7 +607,7 @@ fn compute_circuit_output( // Encrypt and push post state let encrypted_account = EncryptionScheme::encrypt( &post_with_updated_nonce, - *identifier, + &PrivateAccountKind::Account(*identifier), shared_secret, &commitment_post, output_index, @@ -690,7 +691,7 @@ fn compute_circuit_output( let encrypted_account = EncryptionScheme::encrypt( &post_with_updated_nonce, - *identifier, + &PrivateAccountKind::Account(*identifier), shared_secret, &commitment_post, output_index, diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index f0c69fea..8cac1f0c 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -357,7 +357,7 @@ impl WalletCore { let acc_ead = tx.message.encrypted_private_post_states[output_index].clone(); let acc_comm = tx.message.new_commitments[output_index].clone(); - let (identifier, res_acc) = nssa_core::EncryptionScheme::decrypt( + let (kind, res_acc) = nssa_core::EncryptionScheme::decrypt( &acc_ead.ciphertext, secret, &acc_comm, @@ -370,7 +370,7 @@ impl WalletCore { println!("Received new acc {res_acc:#?}"); self.storage - .insert_private_account_data(*acc_account_id, identifier, res_acc); + .insert_private_account_data(*acc_account_id, kind.identifier(), res_acc); } AccDecodeData::Skip => {} } @@ -544,7 +544,8 @@ impl WalletCore { .try_into() .expect("Ciphertext ID is expected to fit in u32"), ) - .map(|(identifier, res_acc)| { + .map(|(kind, res_acc)| { + let identifier = kind.identifier(); let account_id = nssa::AccountId::from(( &key_chain.nullifier_public_key, identifier,