diff --git a/nssa/core/src/encryption/mod.rs b/nssa/core/src/encryption/mod.rs index 6cf3c06a..f5aaa0dc 100644 --- a/nssa/core/src/encryption/mod.rs +++ b/nssa/core/src/encryption/mod.rs @@ -9,87 +9,15 @@ use serde::{Deserialize, Serialize}; pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, ViewingPublicKey}; use crate::{ - Commitment, Identifier, + Commitment, account::Account, - program::{PdaSeed, ProgramId}, + program::PrivateAccountKind, }; #[cfg(feature = "host")] pub mod shared_key_derivation; pub type Scalar = [u8; 32]; -/// Discriminates the type of private account a ciphertext belongs to, carrying the data needed -/// to reconstruct the account's [`AccountId`] on the receiver side. -/// -/// [`AccountId`]: crate::account::AccountId -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum PrivateAccountKind { - Regular(Identifier), - Pda { - program_id: ProgramId, - seed: PdaSeed, - identifier: Identifier, - }, -} - -impl PrivateAccountKind { - /// Regular(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 { - Self::Regular(identifier) => *identifier, - 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::Regular(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::Regular(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)] pub struct SharedSecretKey(pub [u8; 32]); @@ -198,7 +126,7 @@ impl EncryptionScheme { #[cfg(test)] mod tests { use super::*; - use crate::account::{Account, AccountId}; + use crate::{account::{Account, AccountId}, program::PdaSeed}; #[test] fn encrypt_same_length_for_account_and_pda() { diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index 8392f18c..7d9d6d3b 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -10,7 +10,8 @@ pub use commitment::{ Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof, compute_digest_for_path, }; -pub use encryption::{EncryptionScheme, PrivateAccountKind, SharedSecretKey}; +pub use encryption::{EncryptionScheme, SharedSecretKey}; +pub use program::PrivateAccountKind; pub use nullifier::{Identifier, Nullifier, NullifierPublicKey, NullifierSecretKey}; pub mod account; diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index c001b8d6..8f696a98 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize}; use crate::{ BlockId, Identifier, NullifierPublicKey, Timestamp, account::{Account, AccountId, AccountWithMetadata}, - encryption::PrivateAccountKind, }; pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8]; @@ -49,6 +48,78 @@ impl AsRef<[u8]> for PdaSeed { } } +/// Discriminates the type of private account a ciphertext belongs to, carrying the data needed +/// to reconstruct the account's [`AccountId`] on the receiver side. +/// +/// [`AccountId`]: crate::account::AccountId +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum PrivateAccountKind { + Regular(Identifier), + Pda { + program_id: ProgramId, + seed: PdaSeed, + identifier: Identifier, + }, +} + +impl PrivateAccountKind { + /// Regular(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 { + Self::Regular(identifier) => *identifier, + 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::Regular(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::Regular(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, + } + } +} + impl AccountId { /// Derives an [`AccountId`] for a public PDA from the program ID and seed. #[must_use] diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index cce9c190..9bc46380 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -179,8 +179,7 @@ mod tests { Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, Nullifier, PrivacyPreservingCircuitOutput, SharedSecretKey, account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data}, - encryption::PrivateAccountKind, - program::PdaSeed, + program::{PdaSeed, PrivateAccountKind}, }; fn decrypt_kind(