mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-03 23:59:32 +00:00
add test and other fixes
This commit is contained in:
parent
05484a5cef
commit
690ae36324
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -2357,7 +2357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"crypto-common 0.2.1",
|
||||
"crypto-common 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4570,7 +4570,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd"
|
||||
dependencies = [
|
||||
"crypto-common 0.2.1",
|
||||
"crypto-common 0.2.2",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
@ -4700,6 +4700,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"chacha20",
|
||||
"k256",
|
||||
"ml-kem",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@ -12,7 +12,7 @@ use lee::{
|
||||
};
|
||||
use lee_core::{
|
||||
InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata,
|
||||
encryption::shared_key_derivation::Secp256k1Point,
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
};
|
||||
use log::info;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
@ -71,7 +71,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),
|
||||
@ -274,7 +274,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,8 +654,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(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)
|
||||
|
||||
@ -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_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;
|
||||
|
||||
@ -256,7 +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_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 {
|
||||
@ -269,7 +269,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 lee_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
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,6 +328,80 @@ mod tests {
|
||||
|
||||
/// Pins the end-to-end derivation for a fixed (GMS, `ProgramId`, `PdaSeed`). Any change
|
||||
/// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` nsk/npk chain, or the
|
||||
/// `AccountId::for_private_pda` formula breaks this test.
|
||||
#[test]
|
||||
fn pinned_end_to_end_derivation_for_private_pda() {
|
||||
use lee_core::{account::AccountId, program::ProgramId};
|
||||
|
||||
let gms = [42_u8; 32];
|
||||
let seed = PdaSeed::new([1; 32]);
|
||||
let program_id: ProgramId = [9; 8];
|
||||
|
||||
let holder = GroupKeyHolder::from_gms(gms);
|
||||
let npk = holder
|
||||
.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed)
|
||||
.generate_nullifier_public_key();
|
||||
let account_id = AccountId::for_private_pda(&program_id, &seed, &npk, u128::MAX);
|
||||
|
||||
let expected_npk = NullifierPublicKey([
|
||||
136, 176, 234, 71, 208, 8, 143, 142, 126, 155, 132, 18, 71, 27, 88, 56, 100, 90, 79,
|
||||
215, 76, 92, 60, 166, 104, 35, 51, 91, 16, 114, 188, 112,
|
||||
]);
|
||||
let expected_account_id =
|
||||
AccountId::for_private_pda(&program_id, &seed, &expected_npk, u128::MAX);
|
||||
|
||||
assert_eq!(npk, expected_npk);
|
||||
assert_eq!(account_id, expected_account_id);
|
||||
}
|
||||
|
||||
/// Wallets persist `GroupKeyHolder` to disk and reload it on startup.
|
||||
#[test]
|
||||
fn gms_serde_round_trip_preserves_derivation() {
|
||||
let original = GroupKeyHolder::from_gms([7_u8; 32]);
|
||||
let encoded = bincode::serialize(&original).expect("serialize");
|
||||
let restored: GroupKeyHolder = bincode::deserialize(&encoded).expect("deserialize");
|
||||
|
||||
let seed = PdaSeed::new([1; 32]);
|
||||
let npk_original = original
|
||||
.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed)
|
||||
.generate_nullifier_public_key();
|
||||
let npk_restored = restored
|
||||
.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed)
|
||||
.generate_nullifier_public_key();
|
||||
|
||||
assert_eq!(npk_original, npk_restored);
|
||||
assert_eq!(original.dangerous_raw_gms(), restored.dangerous_raw_gms());
|
||||
}
|
||||
|
||||
/// A `GroupKeyHolder` constructed from the same 32 bytes as a personal
|
||||
/// `SecretSpendingKey` must not derive the same `NullifierPublicKey`.
|
||||
#[test]
|
||||
fn group_derivation_does_not_collide_with_personal_path_at_shared_bytes() {
|
||||
let shared_bytes = [13_u8; 32];
|
||||
let seed = PdaSeed::new([5; 32]);
|
||||
|
||||
let group_npk = GroupKeyHolder::from_gms(shared_bytes)
|
||||
.derive_keys_for_pda(&TEST_PROGRAM_ID, &seed)
|
||||
.generate_nullifier_public_key();
|
||||
|
||||
let personal_npk = SecretSpendingKey(shared_bytes)
|
||||
.produce_private_key_holder(None)
|
||||
.generate_nullifier_public_key();
|
||||
|
||||
assert_ne!(group_npk, personal_npk);
|
||||
}
|
||||
|
||||
/// Seal then unseal recovers the same GMS and derived keys.
|
||||
#[test]
|
||||
fn seal_unseal_round_trip() {
|
||||
let holder = GroupKeyHolder::from_gms([42_u8; 32]);
|
||||
|
||||
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;
|
||||
|
||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
let restored = GroupKeyHolder::unseal(&sealed, &recipient_vsk).expect("unseal");
|
||||
|
||||
assert_eq!(restored.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
||||
@ -355,8 +429,7 @@ 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);
|
||||
@ -371,7 +444,7 @@ 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).
|
||||
@ -453,7 +526,7 @@ 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 =
|
||||
|
||||
@ -62,7 +62,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);
|
||||
@ -147,7 +147,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,
|
||||
]);
|
||||
@ -260,11 +260,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,
|
||||
]);
|
||||
|
||||
@ -22,7 +22,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],
|
||||
@ -30,7 +30,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,
|
||||
@ -153,7 +153,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"),
|
||||
@ -172,11 +172,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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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::LeeCoreError;
|
||||
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, LeeCoreError> {
|
||||
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};
|
||||
|
||||
/// 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);
|
||||
@ -489,7 +489,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);
|
||||
@ -529,7 +529,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);
|
||||
@ -585,7 +585,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]);
|
||||
@ -636,7 +636,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);
|
||||
|
||||
@ -666,7 +666,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 {
|
||||
@ -711,7 +711,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(),
|
||||
@ -760,7 +760,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);
|
||||
@ -816,7 +816,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);
|
||||
@ -843,7 +843,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 = lee_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),
|
||||
|
||||
@ -1365,7 +1365,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],
|
||||
@ -1416,10 +1416,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],
|
||||
@ -1484,7 +1484,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],
|
||||
@ -1994,7 +1994,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2006,7 +2006,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2050,7 +2050,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2062,7 +2062,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2106,7 +2106,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2118,7 +2118,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2162,7 +2162,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2174,7 +2174,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2218,7 +2218,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2230,7 +2230,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2272,7 +2272,7 @@ pub mod tests {
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2284,7 +2284,7 @@ pub mod tests {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0u8; 32],
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
@ -2307,7 +2307,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(),
|
||||
@ -2350,7 +2350,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);
|
||||
@ -2388,7 +2388,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
|
||||
@ -2424,7 +2424,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);
|
||||
@ -2464,7 +2464,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);
|
||||
@ -2502,8 +2502,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);
|
||||
@ -2545,7 +2545,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 =
|
||||
@ -2646,7 +2646,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(),
|
||||
@ -2991,7 +2991,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;
|
||||
|
||||
@ -3096,10 +3096,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();
|
||||
|
||||
@ -3397,7 +3397,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;
|
||||
|
||||
@ -3448,7 +3448,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],
|
||||
@ -3497,7 +3497,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;
|
||||
|
||||
@ -3546,7 +3546,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(
|
||||
@ -3630,7 +3630,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![]),
|
||||
@ -3657,7 +3657,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![]),
|
||||
@ -3705,7 +3705,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);
|
||||
@ -3862,7 +3862,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,
|
||||
@ -3931,7 +3931,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(),
|
||||
@ -4486,9 +4486,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 via authenticated_transfer directly.
|
||||
{
|
||||
@ -4603,7 +4603,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();
|
||||
@ -4644,7 +4644,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![
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -431,7 +431,7 @@ mod tests {
|
||||
let acc = AccountIdentity::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());
|
||||
|
||||
@ -269,7 +269,7 @@ 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -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 lee::{Account, AccountId};
|
||||
use lee_core::{Identifier, PrivateAccountKind};
|
||||
@ -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<lee_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<lee_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: lee_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<lee_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