mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-08-11 09:23:20 +00:00
Merge pull request #669 from logos-blockchain/artem/private-authorization-key
feat!: private authorization key
This commit is contained in:
commit
0457b9bb60
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -348,9 +348,9 @@ Check the `run_hello_world_private.rs` file to see how it is used.
|
||||
|
||||
# 8. Account authorization mechanism
|
||||
The Hello world example does not enforce any authorization on the input account. This means any user can execute it on any account, regardless of ownership.
|
||||
LEE provides a mechanism for programs to enforce proper authorization before an execution can succeed. The meaning of authorization differs between public and private accounts:
|
||||
- Public accounts: authorization requires that the transaction is signed with the account’s signing key.
|
||||
- Private accounts: authorization requires that the circuit verifies knowledge of the account’s nullifier secret key.
|
||||
LEE provides a mechanism for programs to enforce proper authorization before an execution can succeed. For both private and public accounts, the authorization is checked against knowledge of a secret key, yet the check is different:
|
||||
- Public accounts: the transaction is signed with the account’s signing key.
|
||||
- Private accounts: the circuit verifies knowledge of the account’s authorization secret key (`ask`), the key from which the account’s nullifier secret key is derived.
|
||||
|
||||
From the program development perspective it is very simple: input accounts come with a flag indicating whether they has been properly authorized. And so, the only difference between the program `hello_world.rs` and `hello_world_with_authorization.rs` is in the lines
|
||||
|
||||
|
||||
@ -592,7 +592,8 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
|
||||
let faucet_program_id = programs::faucet().id();
|
||||
let vault_program_id = programs::vault().id();
|
||||
let auth_transfer_program_id = programs::authenticated_transfer().id();
|
||||
let nsk: lee_core::NullifierSecretKey = [3; 32];
|
||||
let ask = lee_core::AuthorizationSecretKey([3; 32]);
|
||||
let nsk = lee_core::NullifierSecretKey::from(&ask);
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
|
||||
let attacker_vault_id = {
|
||||
@ -661,7 +662,8 @@ async fn prove_init_with_commitment_root(
|
||||
sender_id,
|
||||
);
|
||||
|
||||
let nsk: lee_core::NullifierSecretKey = [7; 32];
|
||||
let ask = lee_core::AuthorizationSecretKey([7; 32]);
|
||||
let nsk = lee_core::NullifierSecretKey::from(&ask);
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
|
||||
let recipient_account_id = AccountId::for_regular_private_account(&npk, &vpk, 0);
|
||||
@ -678,7 +680,7 @@ async fn prove_init_with_commitment_root(
|
||||
vpk,
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular { ask: Some(ask) },
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk,
|
||||
commitment_root,
|
||||
@ -697,7 +699,8 @@ async fn init_with_dummy_commitment_root_produces_valid_root() -> Result<()> {
|
||||
|
||||
let (_, expected_digest) = ctx.sequencer_client().get_proofs_and_root(vec![]).await?;
|
||||
|
||||
let nsk: lee_core::NullifierSecretKey = [7; 32];
|
||||
let ask = lee_core::AuthorizationSecretKey([7; 32]);
|
||||
let nsk = lee_core::NullifierSecretKey::from(&ask);
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
|
||||
let recipient_account_id = AccountId::for_regular_private_account(&npk, &vpk, 0);
|
||||
|
||||
@ -22,8 +22,8 @@ use lee::{
|
||||
public_transaction as putx,
|
||||
};
|
||||
use lee_core::{
|
||||
DUMMY_COMMITMENT_HASH, InputAccountIdentity, MembershipProof, NullifierPublicKey,
|
||||
NullifierWitness, PrivateWitness, WitnessKind,
|
||||
AuthorizationSecretKey, DUMMY_COMMITMENT_HASH, InputAccountIdentity, MembershipProof,
|
||||
NullifierPublicKey, NullifierSecretKey, NullifierWitness, PrivateWitness, WitnessKind,
|
||||
account::{AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::ViewingPublicKey,
|
||||
};
|
||||
@ -255,7 +255,8 @@ pub async fn tps_test() -> Result<()> {
|
||||
#[expect(dead_code, reason = "No idea if we need this, should we remove it?")]
|
||||
fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
let program = programs::authenticated_transfer();
|
||||
let sender_nsk = [1; 32];
|
||||
let sender_ask = AuthorizationSecretKey([1; 32]);
|
||||
let sender_nsk = NullifierSecretKey::from(&sender_ask);
|
||||
let sender_vpk = ViewingPublicKey::from_seed(&[99_u8; 32], &[100_u8; 32]);
|
||||
let sender_npk = NullifierPublicKey::from(&sender_nsk);
|
||||
let sender_pre = AccountWithMetadata::new(
|
||||
@ -268,7 +269,8 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
true,
|
||||
AccountId::for_regular_private_account(&sender_npk, &sender_vpk, 0),
|
||||
);
|
||||
let recipient_nsk = [2; 32];
|
||||
let recipient_ask = AuthorizationSecretKey([2; 32]);
|
||||
let recipient_nsk = NullifierSecretKey::from(&recipient_ask);
|
||||
let recipient_vpk = ViewingPublicKey::from_seed(&[101_u8; 32], &[102_u8; 32]);
|
||||
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
|
||||
let recipient_pre = AccountWithMetadata::new(
|
||||
@ -296,7 +298,9 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
vpk: sender_vpk,
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_nsk,
|
||||
@ -307,7 +311,9 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
vpk: recipient_vpk,
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_npk,
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
|
||||
@ -339,7 +339,7 @@ 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
|
||||
/// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` ask/nsk/npk chain, or the
|
||||
/// `AccountId::for_private_pda` formula breaks this test. Mirrors the pinned-value
|
||||
/// pattern from `for_private_pda_matches_pinned_value` in `lee_core`.
|
||||
#[test]
|
||||
@ -357,8 +357,8 @@ mod tests {
|
||||
let account_id = AccountId::for_private_pda(&program_id, &seed, &npk, &vpk, 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,
|
||||
73, 227, 101, 209, 210, 127, 85, 171, 217, 20, 52, 68, 63, 127, 88, 157, 162, 165, 221,
|
||||
15, 86, 162, 128, 15, 56, 89, 95, 33, 216, 229, 181, 123,
|
||||
]);
|
||||
// AccountId is derived from (program_id, seed, npk), so it changes when npk changes.
|
||||
// We verify npk is pinned, and AccountId is deterministically derived from it.
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use lee_core::{NullifierPublicKey, PrivateAccountKind, encryption::ViewingPublicKey};
|
||||
use lee_core::{
|
||||
NullifierPublicKey, NullifierSecretKey, PrivateAccountKind, encryption::ViewingPublicKey,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest as _;
|
||||
|
||||
@ -32,11 +34,13 @@ impl ChildKeysPrivate {
|
||||
|
||||
#[must_use]
|
||||
pub fn nth_child(&self, cci: u32) -> Self {
|
||||
const DOMAIN: &[u8; 21] = b"/LEE/v0.3/Keys/Parent";
|
||||
|
||||
// `parent_hash`` is used to incorporate entropy based on the parent node's keys
|
||||
// to generate the `ssk` and `ccc` values.
|
||||
let mut parent_hash = sha2::Sha256::new();
|
||||
parent_hash.update(b"LEE/keys");
|
||||
parent_hash.update(self.value.0.private_key_holder.nullifier_secret_key);
|
||||
parent_hash.update(DOMAIN);
|
||||
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);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.z);
|
||||
let parent_pt = parent_hash.finalize();
|
||||
@ -58,10 +62,10 @@ impl ChildKeysPrivate {
|
||||
}
|
||||
|
||||
fn from_ssk_and_ccc(ssk: SecretSpendingKey, ccc: [u8; 32], cci: Option<u32>) -> Self {
|
||||
let nsk = ssk.generate_nullifier_secret_key(cci);
|
||||
let ask = ssk.generate_authorization_secret_key(cci);
|
||||
let vsk = ssk.generate_viewing_secret_seed_key(cci);
|
||||
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let npk = NullifierPublicKey::from(&NullifierSecretKey::from(&ask));
|
||||
let vpk = ViewingPublicKey::from(&vsk);
|
||||
|
||||
Self {
|
||||
@ -71,7 +75,7 @@ impl ChildKeysPrivate {
|
||||
nullifier_public_key: npk,
|
||||
viewing_public_key: vpk,
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: nsk,
|
||||
authorization_secret_key: ask,
|
||||
viewing_secret_key: vsk,
|
||||
},
|
||||
},
|
||||
@ -130,95 +134,104 @@ mod tests {
|
||||
111, 13, 5, 195, 75, 20, 255, 162, 85, 40, 251, 8, 168,
|
||||
];
|
||||
|
||||
let expected_ask = lee_core::AuthorizationSecretKey([
|
||||
3, 154, 34, 187, 166, 138, 64, 10, 172, 210, 224, 75, 165, 157, 94, 27, 81, 209, 194,
|
||||
189, 60, 171, 252, 226, 25, 136, 158, 59, 56, 39, 60, 175,
|
||||
]);
|
||||
|
||||
let expected_nsk: NullifierSecretKey = [
|
||||
154, 102, 103, 5, 34, 235, 227, 13, 22, 182, 226, 11, 7, 67, 110, 162, 99, 193, 174,
|
||||
34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163,
|
||||
227, 13, 41, 248, 160, 185, 37, 158, 48, 134, 157, 185, 50, 249, 13, 114, 128, 43, 92,
|
||||
148, 161, 91, 158, 206, 209, 246, 46, 49, 114, 165, 72, 64,
|
||||
];
|
||||
|
||||
let expected_npk = lee_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,
|
||||
42, 60, 83, 112, 244, 198, 238, 159, 150, 105, 13, 134, 103, 228, 213, 247, 121, 42,
|
||||
65, 51, 122, 196, 228, 163, 244, 251, 219, 119, 8, 14, 68, 16,
|
||||
]);
|
||||
|
||||
let expected_vsk = ViewingSecretKey::new(
|
||||
[
|
||||
187, 143, 146, 12, 68, 148, 25, 203, 21, 92, 131, 2, 221, 81, 117, 62, 98, 194,
|
||||
159, 177, 102, 254, 236, 182, 76, 242, 116, 219, 17, 166, 99, 36,
|
||||
92, 182, 50, 80, 228, 152, 149, 83, 44, 33, 179, 59, 237, 153, 45, 46, 216, 142,
|
||||
62, 31, 28, 18, 44, 27, 130, 54, 10, 13, 148, 111, 214, 107,
|
||||
],
|
||||
[
|
||||
80, 97, 83, 209, 145, 99, 168, 99, 89, 29, 153, 236, 82, 99, 134, 114, 168, 19,
|
||||
223, 69, 34, 47, 76, 76, 15, 97, 245, 184, 25, 103, 251, 82,
|
||||
135, 73, 174, 183, 171, 136, 40, 174, 28, 18, 73, 1, 183, 13, 208, 39, 113, 79,
|
||||
136, 163, 234, 119, 117, 192, 103, 49, 193, 16, 188, 111, 4, 78,
|
||||
],
|
||||
);
|
||||
|
||||
// Length matches MlKem768EncapsulationKey::LEN.
|
||||
// Length matches MlKem768EncapsulationKey::LEN. Oracle-sourced from the ML-KEM-768
|
||||
// implementation, unlike every other vector here; its trailing 32 bytes are
|
||||
// rho = SHA3-512(d || 3)[..32] per FIPS-203, checked against the independent `d`.
|
||||
let expected_vpk: [u8; 1184] = [
|
||||
127, 229, 162, 212, 104, 117, 4, 150, 192, 103, 122, 195, 14, 35, 12, 60, 52, 23, 220,
|
||||
150, 100, 203, 34, 34, 127, 232, 156, 43, 218, 109, 6, 160, 67, 35, 210, 194, 25, 181,
|
||||
118, 237, 25, 129, 51, 160, 189, 51, 99, 184, 57, 28, 121, 240, 236, 2, 170, 198, 26,
|
||||
91, 172, 110, 52, 32, 186, 35, 179, 202, 234, 249, 15, 242, 100, 198, 168, 163, 120,
|
||||
205, 118, 85, 195, 210, 187, 95, 150, 154, 8, 68, 165, 237, 87, 166, 101, 57, 4, 18,
|
||||
11, 122, 235, 180, 199, 154, 165, 158, 55, 136, 30, 237, 43, 167, 215, 68, 80, 102, 0,
|
||||
71, 90, 130, 206, 240, 215, 69, 199, 83, 7, 60, 184, 128, 230, 184, 61, 93, 201, 204,
|
||||
165, 104, 9, 127, 220, 52, 246, 217, 131, 251, 2, 170, 133, 6, 51, 40, 224, 101, 61,
|
||||
16, 135, 32, 182, 201, 68, 58, 171, 54, 161, 184, 243, 38, 106, 200, 251, 17, 172, 8,
|
||||
24, 73, 230, 55, 85, 20, 147, 222, 165, 200, 116, 135, 47, 20, 227, 56, 220, 64, 120,
|
||||
215, 245, 58, 86, 102, 149, 252, 193, 163, 160, 59, 82, 138, 249, 171, 1, 54, 199, 193,
|
||||
171, 85, 38, 64, 56, 121, 106, 84, 57, 252, 94, 147, 16, 191, 196, 104, 47, 129, 84,
|
||||
21, 252, 160, 81, 207, 184, 199, 3, 177, 74, 117, 115, 175, 138, 108, 36, 198, 5, 32,
|
||||
15, 218, 3, 20, 19, 15, 251, 209, 86, 128, 139, 148, 78, 10, 34, 144, 149, 74, 102, 48,
|
||||
59, 70, 124, 47, 193, 100, 26, 9, 104, 178, 102, 156, 199, 242, 101, 147, 161, 87, 27,
|
||||
234, 192, 204, 41, 36, 43, 83, 219, 15, 211, 66, 91, 76, 73, 13, 113, 155, 203, 193,
|
||||
160, 130, 84, 103, 47, 70, 100, 147, 169, 65, 119, 84, 121, 122, 161, 76, 203, 144,
|
||||
248, 145, 22, 8, 46, 121, 44, 77, 20, 149, 66, 179, 56, 149, 231, 98, 184, 9, 64, 14,
|
||||
67, 196, 34, 8, 123, 21, 80, 169, 168, 223, 230, 133, 0, 66, 159, 230, 69, 201, 205,
|
||||
169, 105, 196, 21, 71, 84, 70, 58, 165, 165, 134, 186, 232, 60, 70, 51, 57, 239, 74,
|
||||
174, 116, 234, 36, 178, 49, 42, 168, 250, 104, 141, 106, 0, 109, 52, 86, 104, 243, 62,
|
||||
214, 137, 48, 107, 2, 152, 206, 227, 175, 147, 236, 19, 113, 27, 191, 231, 235, 167,
|
||||
114, 104, 23, 126, 203, 94, 242, 149, 171, 115, 170, 89, 244, 58, 29, 176, 73, 203, 44,
|
||||
8, 32, 9, 226, 32, 78, 246, 38, 235, 149, 133, 25, 243, 47, 124, 180, 200, 211, 165,
|
||||
137, 56, 169, 117, 31, 244, 65, 91, 135, 146, 158, 20, 75, 102, 32, 65, 250, 103, 199,
|
||||
36, 48, 31, 155, 164, 191, 222, 85, 37, 66, 243, 17, 120, 104, 0, 228, 83, 200, 116, 6,
|
||||
199, 106, 236, 139, 246, 216, 152, 241, 211, 85, 106, 200, 44, 231, 240, 66, 3, 193,
|
||||
147, 16, 145, 65, 49, 33, 53, 247, 69, 47, 44, 113, 86, 117, 6, 20, 193, 183, 128, 178,
|
||||
181, 21, 251, 99, 39, 149, 210, 146, 106, 181, 186, 7, 36, 63, 186, 234, 191, 164, 193,
|
||||
162, 127, 250, 122, 189, 219, 21, 92, 48, 86, 209, 184, 99, 160, 201, 162, 145, 20,
|
||||
138, 154, 18, 37, 180, 209, 165, 165, 51, 187, 78, 193, 175, 135, 6, 55, 216, 178, 10,
|
||||
40, 246, 98, 128, 80, 14, 38, 69, 113, 123, 54, 94, 43, 50, 106, 167, 17, 77, 163, 148,
|
||||
117, 225, 9, 7, 253, 240, 157, 96, 103, 33, 100, 37, 37, 20, 53, 138, 234, 55, 45, 232,
|
||||
154, 9, 150, 192, 116, 36, 119, 106, 95, 119, 34, 220, 84, 174, 19, 227, 33, 209, 96,
|
||||
197, 148, 230, 197, 59, 117, 130, 7, 116, 11, 0, 197, 16, 249, 151, 31, 4, 64, 29, 165,
|
||||
247, 110, 176, 166, 4, 112, 136, 101, 208, 7, 179, 38, 183, 134, 58, 107, 207, 160, 38,
|
||||
159, 67, 112, 20, 225, 199, 179, 133, 117, 144, 54, 199, 15, 204, 80, 154, 116, 84, 88,
|
||||
109, 113, 5, 207, 226, 21, 62, 247, 122, 14, 156, 9, 8, 76, 26, 148, 67, 196, 128, 176,
|
||||
78, 51, 161, 151, 75, 248, 154, 31, 168, 9, 4, 3, 107, 222, 245, 178, 21, 84, 7, 25,
|
||||
155, 118, 97, 135, 63, 89, 233, 11, 207, 148, 155, 38, 106, 104, 102, 140, 104, 67,
|
||||
149, 20, 30, 196, 44, 197, 128, 34, 182, 80, 30, 32, 137, 34, 212, 164, 177, 164, 12,
|
||||
115, 41, 156, 111, 71, 230, 120, 111, 218, 25, 117, 218, 75, 167, 32, 37, 57, 50, 99,
|
||||
181, 203, 40, 105, 248, 150, 114, 121, 73, 127, 198, 191, 161, 44, 56, 213, 243, 71, 2,
|
||||
56, 192, 243, 107, 179, 27, 96, 21, 116, 169, 64, 15, 97, 166, 151, 200, 11, 40, 204,
|
||||
71, 168, 220, 9, 55, 43, 146, 244, 212, 166, 192, 180, 189, 237, 162, 42, 29, 33, 52,
|
||||
193, 4, 178, 157, 244, 28, 209, 44, 26, 36, 147, 126, 94, 164, 37, 47, 115, 38, 23,
|
||||
165, 96, 106, 140, 42, 69, 146, 194, 93, 71, 175, 49, 147, 32, 246, 97, 94, 41, 116,
|
||||
127, 174, 18, 16, 14, 163, 17, 180, 213, 203, 166, 33, 139, 214, 18, 170, 27, 41, 59,
|
||||
175, 200, 101, 14, 128, 45, 179, 167, 136, 232, 138, 56, 124, 145, 75, 233, 132, 161,
|
||||
196, 164, 72, 80, 60, 187, 38, 90, 90, 17, 66, 134, 59, 2, 165, 29, 76, 24, 38, 211,
|
||||
177, 83, 119, 20, 239, 59, 77, 34, 3, 42, 47, 60, 89, 46, 103, 168, 120, 17, 199, 50,
|
||||
17, 103, 107, 48, 8, 53, 220, 159, 212, 65, 198, 80, 8, 11, 235, 97, 203, 196, 240, 44,
|
||||
56, 121, 77, 91, 196, 160, 129, 242, 149, 226, 57, 106, 180, 76, 161, 203, 18, 37, 166,
|
||||
153, 44, 40, 28, 74, 8, 11, 6, 166, 54, 10, 103, 247, 23, 35, 7, 47, 173, 133, 71, 85,
|
||||
3, 168, 250, 120, 126, 174, 37, 80, 128, 107, 7, 161, 130, 155, 136, 92, 48, 215, 119,
|
||||
196, 124, 85, 157, 234, 2, 166, 137, 65, 121, 222, 112, 47, 17, 43, 23, 111, 88, 5,
|
||||
195, 41, 8, 191, 227, 21, 173, 35, 199, 196, 188, 162, 191, 195, 204, 137, 54, 16, 73,
|
||||
178, 150, 249, 234, 22, 216, 123, 157, 144, 218, 118, 53, 193, 67, 65, 84, 162, 244,
|
||||
165, 24, 110, 246, 146, 228, 212, 180, 150, 116, 201, 37, 128, 76, 41, 188, 42, 79,
|
||||
148, 52, 196, 176, 178, 224, 48, 168, 13, 129, 193, 131, 185, 131, 93, 40, 145, 56,
|
||||
180, 29, 153, 83, 39, 69, 232, 96, 238, 137, 104, 150, 2, 202, 239, 149, 248, 154, 115,
|
||||
115, 127, 3, 8, 32, 61, 96, 66, 25, 181, 14, 72, 73, 97, 186, 134, 140, 33, 69, 33, 74,
|
||||
95, 42, 170, 49, 164, 173, 200, 156, 66, 32, 71, 126, 122, 140, 148, 144, 114, 143,
|
||||
233, 199, 104, 82, 179, 49, 43, 114, 130, 182, 71, 4, 45, 101, 65, 136, 196, 72, 129,
|
||||
128, 204, 239, 137, 84, 230, 210, 18, 214, 252, 40, 198, 210, 24, 158, 53, 151, 166,
|
||||
24, 47, 143, 8, 158, 119, 240, 204, 210, 242, 96, 191, 147, 106, 98, 198, 93, 193, 163,
|
||||
31, 132, 36, 16, 50, 83, 24, 225, 250, 106, 55, 231, 188, 90, 194, 128, 10, 225, 186,
|
||||
41, 225, 165, 126, 57, 32, 163, 129, 42, 68, 113, 177, 239, 106, 144, 217, 188, 192,
|
||||
174, 38, 161, 189, 24, 107, 14, 54, 167, 221, 120, 194, 6, 22, 163, 86, 96, 47, 220,
|
||||
227, 176, 173, 52, 150, 183, 25, 40, 200, 19, 134, 51, 172, 126, 35, 147, 79, 207, 235,
|
||||
9, 243, 197, 84, 4, 194, 142, 207, 118, 121, 133, 58, 12, 58, 226, 22, 106, 172, 56,
|
||||
223, 161, 145, 60, 28, 47, 95, 84, 127, 1, 235, 72, 0, 131, 202, 15, 151, 93, 52, 18,
|
||||
13, 247, 91, 80, 240, 229, 85, 72, 135, 84, 230, 113, 196, 162, 3, 24, 87, 176, 80,
|
||||
202, 99, 44, 87, 229, 96, 254, 27, 181, 181, 58, 191, 116, 19, 68, 235, 35, 86, 227,
|
||||
89, 49, 70, 102, 54, 153, 224, 117, 34, 113, 57, 121, 202, 42, 248, 24, 125, 134, 134,
|
||||
57, 126, 204, 131, 191, 181, 71, 197, 184, 137, 48, 76, 29, 174, 137, 154, 253, 50, 68,
|
||||
184, 122, 173, 106, 144, 207, 48, 213, 156, 182, 26, 103, 203, 133, 131, 47, 184, 189,
|
||||
109, 4, 182, 126, 71, 180, 153, 18, 82, 77, 201, 23, 176, 92, 12, 146, 48, 26, 236,
|
||||
139, 157, 174, 214, 77, 253, 163, 94, 52, 133, 88, 200, 251, 156, 197, 201, 7, 239,
|
||||
117, 83, 57, 188, 85, 31, 196, 106, 164, 147, 36, 32, 241, 143, 54, 121, 195, 183, 98,
|
||||
182, 135, 90, 84, 118, 212, 91, 115, 41, 75, 193, 156, 44, 9, 196, 199, 241, 123, 148,
|
||||
31, 105, 126, 160, 234, 16, 196, 149, 192, 66, 34, 199, 132, 160, 98, 229, 90, 158, 46,
|
||||
108, 112, 126, 165, 115, 234, 128, 164, 241, 132, 171, 186, 212, 121, 74, 217, 165,
|
||||
111, 216, 21, 169, 89, 86, 173, 163, 183, 61, 28, 117, 104, 211, 206, 30, 194, 180, 34,
|
||||
180, 151, 150, 212, 90, 75, 139, 138, 253, 52, 60, 252, 5, 126, 152, 12, 153, 77, 232,
|
||||
167, 14, 163, 130, 76, 18, 117, 96, 113, 144, 234, 22, 56, 106, 210, 78, 83, 50, 43,
|
||||
99, 120, 20, 172, 89, 61, 10, 75, 121, 118, 226, 153, 53, 161, 144, 53, 246, 37, 213,
|
||||
216, 48, 183, 124, 58, 161, 145, 126, 238, 120, 112, 103, 65, 176, 40, 104, 60, 47, 10,
|
||||
138, 154, 89, 174, 164, 69, 182, 168, 196, 131, 68, 18, 189, 204, 74, 180, 16, 233,
|
||||
178, 175, 57, 180, 212, 58, 148, 92, 2, 16, 255, 103, 27, 212, 117, 12, 10, 54, 105,
|
||||
253, 9, 124, 250, 210, 14, 127, 151, 74, 49, 209, 59, 125, 184, 183, 175, 251, 200,
|
||||
172, 120, 59, 41, 89, 199, 3, 161, 189, 138, 50, 69, 108, 102, 155, 210, 17, 73, 235,
|
||||
75, 145, 132, 67, 89, 88, 225, 182, 156, 248, 199, 112, 52, 22, 134, 80, 40, 250, 42,
|
||||
185, 57, 200, 90, 137, 16, 158, 98, 114, 48, 151, 35, 128, 49, 49, 118, 195, 57, 40,
|
||||
94, 103, 156, 186, 1, 112, 130, 178, 59, 22, 71, 153, 173, 195, 178, 216, 149, 24, 202,
|
||||
245, 123, 117, 106, 44, 55, 128, 37, 165, 26, 103, 158, 52, 10, 188, 10, 195, 146, 204,
|
||||
85, 66, 66, 162, 73, 25, 59, 107, 57, 149, 100, 216, 24, 69, 49, 134, 233, 96, 29, 176,
|
||||
8, 188, 121, 145, 44, 35, 199, 4, 48, 24, 76, 69, 250, 92, 126, 40, 52, 162, 72, 113,
|
||||
81, 96, 116, 105, 150, 59, 211, 236, 141, 87, 178, 9, 17, 117, 43, 139, 17, 150, 153,
|
||||
114, 195, 212, 2, 192, 56, 91, 70, 200, 75, 2, 57, 171, 147, 184, 236, 15, 64, 26, 191,
|
||||
131, 179, 13, 195, 195, 166, 208, 180, 93, 186, 155, 102, 189, 57, 82, 73, 39, 44, 249,
|
||||
249, 183, 33, 112, 59, 130, 20, 193, 41, 40, 128, 131, 106, 136, 51, 75, 56, 188, 167,
|
||||
119, 5, 118, 73, 84, 168, 38, 121, 182, 190, 252, 182, 87, 142, 33, 66, 131, 75, 36,
|
||||
216, 181, 186, 213, 148, 191, 182, 115, 159, 83, 1, 14, 170, 55, 21, 251, 65, 135, 117,
|
||||
171, 147, 38, 210, 129, 251, 151, 177, 213, 1, 18, 22, 241, 62, 173, 80, 76, 85, 129,
|
||||
139, 192, 137, 205, 203, 114, 181, 121, 40, 141, 9, 194, 58, 20, 200, 126, 151, 51,
|
||||
129, 146, 92, 156, 93, 192, 72, 26, 33, 138, 107, 138, 124, 193, 138, 8, 244, 84, 116,
|
||||
28, 156, 123, 1, 19, 186, 119, 231, 157, 70, 160, 5, 34, 80, 201, 4, 39, 38, 217, 85,
|
||||
53, 10, 40, 136, 145, 225, 26, 65, 32, 76, 33, 245, 72, 166, 5, 165, 44, 67, 86, 99,
|
||||
87, 9, 148, 131, 72, 223, 71, 179, 243, 39, 36, 34, 145, 86, 134, 12, 127, 103, 3, 191,
|
||||
254, 216, 195, 12, 197, 184, 238, 67, 34, 226, 4, 100, 135, 165, 40, 164, 113, 110,
|
||||
132, 68, 100, 72, 217, 67, 169, 199, 96, 120, 152, 27, 26, 241, 103, 61, 162, 154, 113,
|
||||
55, 75, 156, 17, 114, 105, 145, 158, 13, 251, 50, 221, 219, 150, 88, 5, 184, 92, 137,
|
||||
164, 25, 117, 51, 87, 233, 93, 5, 84, 125, 251, 162, 110, 231, 36, 2, 235, 251, 185,
|
||||
45, 180, 132, 53, 104, 206, 144, 133, 67, 164, 76, 84, 152, 236, 157, 253, 115, 97,
|
||||
195, 177, 172, 233, 51, 161, 196, 66, 59, 233, 88, 133, 12, 146, 172, 148, 236, 58, 5,
|
||||
226, 48, 53, 219, 185, 72, 86, 7, 249, 151, 205, 32, 57, 163, 17, 71, 37, 162, 97, 137,
|
||||
142, 252, 190, 58, 196, 70, 181, 4, 48, 123, 9, 75, 198, 100, 134, 36, 18, 45, 99, 18,
|
||||
191, 75, 55, 30, 144, 197, 0, 44, 71, 199, 78, 121, 92, 76, 84, 43, 133, 139, 77, 105,
|
||||
83, 178, 221, 215, 108, 55, 58, 7, 106, 96, 146, 9, 70, 140, 250, 187, 206, 95, 54, 74,
|
||||
30, 146, 15, 182, 5, 79, 41, 135, 59, 75, 103, 82, 63, 39, 69, 178, 215, 49, 234, 146,
|
||||
127, 186, 192, 189, 107, 140, 11, 39, 162, 120, 90, 133, 106, 184, 87, 144, 5, 80, 80,
|
||||
22, 241, 181, 128, 201, 61, 186, 124, 9, 165, 192, 78, 67, 141, 57, 10, 94, 36, 75,
|
||||
118, 21, 105, 252, 45, 196, 60, 23, 182, 189, 252, 152, 182, 72, 229, 213, 89, 165,
|
||||
222, 151, 52, 182, 110, 127, 158,
|
||||
];
|
||||
assert!(expected_ssk == keys.value.0.secret_spending_key);
|
||||
assert!(expected_ccc == keys.ccc);
|
||||
assert!(expected_nsk == keys.value.0.private_key_holder.nullifier_secret_key);
|
||||
assert!(expected_ask == keys.value.0.private_key_holder.authorization_secret_key);
|
||||
assert!(expected_nsk == keys.value.0.private_key_holder.nullifier_secret_key());
|
||||
assert!(expected_npk == keys.value.0.nullifier_public_key);
|
||||
assert!(expected_vsk == keys.value.0.private_key_holder.viewing_secret_key);
|
||||
assert!(expected_vpk == keys.value.0.viewing_public_key.to_bytes());
|
||||
@ -230,105 +243,120 @@ mod tests {
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
|
||||
|
||||
let expected_ssk = key_management::secret_holders::SecretSpendingKey([
|
||||
151, 183, 113, 151, 215, 187, 207, 64, 197, 182, 207, 32, 5, 49, 180, 98, 119, 14, 248,
|
||||
175, 39, 100, 47, 109, 148, 173, 217, 253, 159, 234, 209, 113,
|
||||
109, 21, 107, 97, 112, 105, 143, 134, 185, 35, 168, 205, 138, 110, 125, 155, 193, 57,
|
||||
36, 19, 214, 180, 194, 46, 107, 235, 43, 80, 132, 19, 254, 231,
|
||||
]);
|
||||
|
||||
let expected_ccc = [
|
||||
138, 243, 142, 163, 62, 107, 63, 131, 230, 158, 185, 60, 204, 50, 243, 222, 13, 123,
|
||||
98, 116, 131, 194, 7, 25, 129, 209, 163, 72, 178, 143, 192, 240,
|
||||
64, 218, 41, 59, 115, 126, 128, 3, 77, 77, 54, 84, 87, 253, 181, 112, 244, 254, 176,
|
||||
243, 86, 127, 219, 255, 64, 164, 218, 129, 83, 65, 176, 179,
|
||||
];
|
||||
|
||||
let expected_ask = lee_core::AuthorizationSecretKey([
|
||||
33, 29, 199, 63, 191, 14, 196, 15, 51, 70, 236, 125, 93, 120, 78, 99, 90, 239, 220,
|
||||
168, 226, 46, 208, 238, 70, 117, 17, 28, 163, 89, 177, 129,
|
||||
]);
|
||||
|
||||
let expected_nsk: NullifierSecretKey = [
|
||||
196, 33, 11, 39, 220, 84, 119, 182, 187, 194, 135, 20, 124, 33, 244, 205, 96, 58, 102,
|
||||
52, 74, 67, 110, 213, 24, 16, 160, 64, 247, 3, 107, 235,
|
||||
97, 83, 94, 170, 125, 55, 27, 105, 106, 42, 73, 99, 169, 221, 210, 124, 117, 52, 98,
|
||||
131, 98, 202, 79, 95, 151, 196, 239, 242, 6, 127, 160, 160,
|
||||
];
|
||||
let expected_npk = lee_core::NullifierPublicKey([
|
||||
247, 253, 217, 86, 157, 208, 39, 172, 59, 190, 88, 165, 7, 173, 183, 106, 172, 211, 4,
|
||||
180, 51, 107, 177, 107, 51, 117, 231, 176, 200, 103, 1, 121,
|
||||
81, 199, 141, 154, 209, 135, 105, 75, 106, 240, 124, 190, 245, 233, 152, 34, 225, 234,
|
||||
212, 221, 121, 68, 255, 97, 231, 142, 26, 54, 169, 53, 69, 242,
|
||||
]);
|
||||
|
||||
let expected_vsk = ViewingSecretKey::new(
|
||||
[
|
||||
185, 209, 179, 92, 7, 131, 98, 121, 215, 46, 154, 56, 238, 106, 162, 225, 83, 82,
|
||||
134, 3, 80, 186, 35, 178, 161, 204, 205, 163, 28, 19, 149, 18,
|
||||
14, 173, 255, 235, 8, 1, 246, 119, 243, 18, 235, 31, 209, 92, 142, 7, 175, 223,
|
||||
228, 201, 173, 165, 148, 137, 141, 160, 175, 161, 110, 139, 54, 183,
|
||||
],
|
||||
[
|
||||
174, 24, 72, 205, 129, 123, 131, 9, 146, 152, 224, 151, 10, 184, 224, 109, 94, 149,
|
||||
117, 60, 26, 10, 212, 125, 113, 147, 87, 67, 73, 26, 101, 193,
|
||||
10, 253, 101, 172, 213, 221, 88, 85, 178, 89, 218, 73, 28, 212, 1, 2, 105, 161,
|
||||
180, 24, 49, 6, 182, 155, 22, 220, 121, 42, 175, 59, 233, 109,
|
||||
],
|
||||
);
|
||||
|
||||
// Length matches MlKem768EncapsulationKey::LEN.
|
||||
// Length matches MlKem768EncapsulationKey::LEN. Oracle-sourced from the ML-KEM-768
|
||||
// implementation, unlike every other vector here; its trailing 32 bytes are
|
||||
// rho = SHA3-512(d || 3)[..32] per FIPS-203, checked against the independent `d`.
|
||||
let expected_vpk: [u8; 1184] = [
|
||||
215, 229, 207, 120, 148, 177, 148, 197, 72, 222, 134, 3, 231, 146, 123, 226, 36, 84,
|
||||
232, 179, 205, 16, 241, 142, 9, 81, 58, 54, 12, 115, 148, 182, 19, 245, 22, 203, 57,
|
||||
71, 11, 204, 156, 130, 30, 170, 199, 201, 25, 2, 21, 34, 155, 136, 124, 145, 223, 128,
|
||||
177, 207, 92, 38, 252, 165, 118, 61, 128, 71, 154, 242, 105, 165, 52, 7, 6, 244, 120,
|
||||
227, 134, 191, 25, 169, 150, 123, 246, 138, 25, 196, 126, 156, 144, 33, 123, 120, 44,
|
||||
142, 89, 201, 49, 219, 205, 87, 236, 110, 64, 129, 102, 100, 155, 26, 101, 121, 42,
|
||||
236, 82, 111, 141, 117, 75, 71, 194, 73, 123, 170, 110, 69, 149, 107, 96, 195, 55, 122,
|
||||
140, 131, 106, 140, 156, 147, 75, 28, 128, 138, 113, 86, 37, 63, 173, 214, 200, 2, 214,
|
||||
84, 234, 176, 120, 252, 184, 99, 192, 65, 112, 150, 99, 26, 174, 187, 183, 187, 64, 90,
|
||||
248, 100, 66, 63, 195, 3, 44, 43, 128, 59, 149, 107, 66, 180, 67, 200, 183, 200, 36,
|
||||
91, 7, 65, 228, 159, 79, 44, 89, 35, 163, 145, 92, 227, 104, 2, 72, 5, 7, 193, 21, 51,
|
||||
116, 198, 184, 6, 192, 188, 68, 183, 163, 193, 142, 244, 217, 155, 197, 187, 189, 174,
|
||||
225, 45, 126, 112, 93, 194, 156, 102, 150, 1, 188, 222, 76, 108, 73, 149, 44, 28, 219,
|
||||
66, 95, 215, 204, 148, 217, 16, 36, 121, 112, 2, 51, 10, 195, 137, 12, 93, 203, 146,
|
||||
138, 211, 15, 201, 42, 72, 146, 186, 160, 222, 235, 127, 83, 48, 182, 49, 248, 29, 138,
|
||||
16, 32, 232, 179, 163, 187, 161, 174, 152, 187, 93, 76, 166, 48, 230, 219, 111, 123,
|
||||
181, 103, 130, 28, 109, 235, 115, 45, 57, 193, 206, 160, 17, 52, 92, 194, 25, 3, 80,
|
||||
97, 142, 249, 151, 94, 250, 95, 12, 57, 11, 165, 92, 47, 85, 182, 48, 22, 60, 97, 244,
|
||||
59, 194, 135, 180, 133, 106, 227, 56, 192, 60, 91, 15, 241, 146, 89, 240, 130, 219,
|
||||
202, 187, 43, 85, 98, 50, 104, 64, 114, 113, 80, 54, 69, 69, 5, 43, 90, 19, 0, 0, 188,
|
||||
251, 184, 70, 160, 18, 117, 76, 53, 209, 166, 96, 34, 224, 137, 115, 183, 168, 243, 19,
|
||||
1, 255, 4, 97, 162, 199, 104, 72, 213, 111, 62, 54, 172, 82, 184, 82, 143, 71, 99, 25,
|
||||
104, 74, 120, 70, 84, 235, 32, 22, 20, 218, 163, 77, 194, 125, 75, 22, 72, 236, 192,
|
||||
200, 107, 91, 156, 201, 10, 178, 87, 19, 181, 211, 91, 17, 145, 200, 17, 179, 65, 75,
|
||||
200, 186, 89, 144, 91, 184, 116, 214, 51, 91, 42, 162, 243, 202, 92, 18, 54, 0, 213,
|
||||
67, 149, 151, 51, 29, 220, 196, 160, 201, 68, 113, 210, 164, 175, 152, 121, 168, 231,
|
||||
161, 91, 132, 218, 1, 171, 176, 84, 100, 57, 1, 3, 2, 196, 194, 76, 181, 79, 171, 157,
|
||||
35, 162, 155, 192, 210, 149, 142, 120, 189, 127, 151, 96, 202, 225, 73, 242, 81, 112,
|
||||
237, 224, 155, 130, 130, 34, 196, 153, 131, 161, 113, 163, 172, 114, 48, 207, 32, 151,
|
||||
172, 83, 145, 79, 210, 100, 161, 92, 82, 216, 90, 104, 238, 212, 38, 50, 107, 17, 228,
|
||||
195, 190, 6, 151, 165, 148, 245, 102, 51, 8, 185, 8, 85, 59, 247, 219, 95, 219, 170,
|
||||
155, 233, 123, 27, 64, 251, 56, 24, 200, 16, 181, 212, 146, 61, 116, 106, 215, 214, 62,
|
||||
118, 27, 68, 233, 148, 73, 135, 199, 74, 184, 89, 159, 217, 139, 24, 208, 250, 30, 224,
|
||||
97, 185, 237, 193, 8, 216, 23, 186, 5, 50, 41, 161, 203, 22, 217, 23, 194, 191, 148,
|
||||
124, 10, 212, 171, 209, 210, 145, 184, 171, 74, 35, 220, 43, 145, 241, 23, 43, 92, 171,
|
||||
216, 43, 114, 77, 155, 147, 156, 86, 56, 170, 27, 1, 54, 182, 169, 96, 22, 201, 51,
|
||||
145, 94, 143, 133, 106, 47, 176, 112, 197, 197, 96, 80, 73, 164, 207, 179, 22, 229,
|
||||
171, 201, 223, 219, 13, 219, 1, 91, 224, 252, 171, 199, 217, 25, 60, 128, 135, 9, 71,
|
||||
105, 231, 86, 34, 21, 155, 50, 0, 105, 72, 117, 108, 175, 140, 9, 181, 249, 139, 97, 3,
|
||||
161, 66, 248, 42, 67, 113, 132, 8, 119, 232, 6, 169, 18, 157, 222, 53, 176, 56, 137,
|
||||
120, 18, 115, 199, 187, 112, 48, 223, 211, 206, 152, 252, 108, 179, 129, 20, 227, 248,
|
||||
183, 234, 87, 202, 49, 17, 69, 215, 118, 89, 188, 180, 33, 238, 245, 206, 40, 179, 129,
|
||||
242, 59, 73, 254, 117, 114, 250, 179, 103, 109, 250, 202, 99, 152, 2, 167, 130, 169,
|
||||
35, 71, 89, 211, 140, 71, 103, 154, 121, 108, 147, 191, 186, 73, 10, 73, 203, 23, 55,
|
||||
106, 144, 98, 227, 157, 25, 27, 81, 67, 11, 57, 88, 227, 116, 61, 100, 94, 23, 166,
|
||||
146, 57, 226, 72, 124, 33, 65, 226, 35, 167, 206, 156, 202, 213, 213, 158, 89, 249,
|
||||
181, 19, 113, 109, 217, 71, 168, 142, 180, 122, 30, 5, 54, 170, 155, 73, 56, 170, 124,
|
||||
139, 4, 165, 103, 82, 32, 183, 84, 7, 239, 117, 135, 239, 48, 24, 28, 210, 49, 137, 6,
|
||||
158, 65, 211, 113, 205, 135, 146, 83, 10, 46, 90, 27, 97, 135, 135, 185, 173, 69, 58,
|
||||
34, 247, 141, 150, 6, 158, 117, 23, 198, 139, 65, 81, 179, 187, 194, 247, 203, 127,
|
||||
106, 232, 119, 122, 215, 197, 110, 69, 203, 174, 227, 63, 185, 106, 14, 184, 104, 113,
|
||||
233, 83, 92, 104, 38, 188, 9, 135, 107, 108, 121, 193, 33, 209, 89, 39, 137, 17, 208,
|
||||
26, 21, 238, 169, 86, 181, 193, 153, 82, 8, 151, 53, 39, 88, 91, 252, 3, 33, 75, 127,
|
||||
9, 168, 53, 34, 1, 173, 202, 123, 157, 174, 170, 199, 254, 187, 196, 144, 37, 29, 48,
|
||||
112, 173, 107, 147, 155, 69, 134, 137, 156, 247, 123, 242, 72, 5, 43, 106, 89, 179,
|
||||
204, 41, 15, 60, 48, 78, 214, 180, 26, 170, 67, 71, 66, 146, 113, 220, 159, 153, 201,
|
||||
176, 116, 154, 21, 186, 33, 180, 72, 39, 187, 240, 80, 112, 132, 144, 173, 210, 12, 76,
|
||||
184, 146, 89, 178, 178, 82, 109, 71, 201, 241, 160, 207, 219, 124, 77, 2, 105, 124,
|
||||
178, 71, 3, 38, 64, 41, 83, 170, 137, 82, 242, 144, 76, 102, 82, 7, 25, 149, 141, 169,
|
||||
46, 4, 68, 40, 244, 146, 131, 107, 148, 18, 111, 85, 104, 243, 28, 75, 176, 249, 88,
|
||||
82, 123, 89, 29, 104, 135, 230, 117, 67, 26, 249, 108, 145, 76, 38, 175, 89, 185, 94,
|
||||
106, 128, 201, 150, 151, 194, 133, 21, 81, 213, 231, 15, 117, 44, 61, 86, 223, 162, 56,
|
||||
190, 166, 177, 157, 137, 60, 208, 155, 234, 158, 252, 30,
|
||||
153, 195, 136, 113, 42, 34, 28, 116, 169, 190, 53, 81, 59, 6, 163, 170, 199, 98, 201,
|
||||
54, 24, 171, 26, 191, 144, 248, 95, 63, 199, 113, 27, 67, 97, 49, 210, 66, 190, 74, 54,
|
||||
252, 12, 143, 197, 243, 27, 111, 198, 43, 23, 146, 3, 183, 171, 41, 72, 92, 46, 112,
|
||||
184, 160, 0, 25, 155, 109, 203, 70, 196, 88, 135, 121, 147, 12, 96, 230, 205, 217, 66,
|
||||
55, 78, 215, 196, 6, 8, 160, 186, 73, 56, 218, 76, 10, 31, 148, 182, 170, 131, 26, 101,
|
||||
210, 125, 125, 242, 160, 146, 35, 195, 59, 182, 55, 130, 198, 67, 109, 245, 66, 229,
|
||||
226, 154, 189, 112, 5, 160, 39, 7, 139, 152, 58, 197, 167, 81, 160, 37, 96, 175, 250,
|
||||
78, 232, 181, 152, 131, 41, 92, 45, 33, 184, 142, 25, 157, 62, 166, 111, 148, 137, 143,
|
||||
241, 176, 74, 176, 243, 178, 26, 232, 204, 119, 181, 17, 128, 91, 134, 8, 90, 35, 11,
|
||||
124, 98, 76, 76, 43, 82, 135, 85, 215, 72, 177, 37, 218, 131, 158, 139, 59, 155, 104,
|
||||
13, 13, 150, 34, 192, 54, 115, 90, 44, 84, 132, 51, 174, 155, 64, 26, 42, 246, 28, 30,
|
||||
252, 136, 227, 69, 174, 27, 233, 56, 214, 102, 117, 141, 177, 133, 181, 177, 8, 150,
|
||||
85, 104, 243, 123, 22, 25, 7, 42, 152, 226, 88, 93, 213, 201, 197, 235, 17, 185, 84,
|
||||
57, 161, 179, 37, 251, 129, 62, 214, 48, 161, 247, 124, 147, 120, 180, 197, 9, 83, 63,
|
||||
38, 182, 206, 20, 116, 28, 143, 134, 173, 55, 54, 1, 254, 86, 9, 67, 213, 144, 95, 48,
|
||||
29, 47, 119, 156, 188, 70, 18, 37, 202, 65, 173, 22, 177, 14, 244, 120, 166, 20, 82,
|
||||
203, 162, 24, 56, 240, 154, 249, 11, 31, 182, 55, 71, 171, 196, 3, 20, 151, 112, 159,
|
||||
131, 91, 201, 80, 116, 228, 6, 184, 140, 55, 205, 4, 91, 116, 46, 224, 32, 216, 146,
|
||||
46, 232, 75, 156, 109, 54, 193, 146, 248, 2, 49, 17, 102, 214, 150, 202, 1, 6, 110, 2,
|
||||
202, 34, 162, 107, 205, 241, 123, 83, 108, 152, 145, 231, 219, 158, 24, 171, 48, 121,
|
||||
2, 193, 15, 21, 9, 173, 172, 30, 111, 58, 94, 0, 18, 1, 140, 7, 52, 209, 214, 59, 127,
|
||||
228, 30, 154, 102, 119, 233, 170, 160, 120, 108, 154, 117, 28, 23, 70, 56, 35, 11, 9,
|
||||
39, 198, 86, 79, 119, 213, 88, 193, 5, 15, 214, 139, 199, 236, 243, 159, 171, 185, 22,
|
||||
35, 216, 20, 149, 137, 17, 74, 117, 165, 55, 116, 139, 171, 181, 204, 196, 104, 178,
|
||||
246, 103, 73, 249, 165, 108, 237, 234, 155, 133, 35, 139, 98, 136, 51, 220, 98, 181,
|
||||
40, 151, 80, 58, 242, 84, 206, 242, 199, 145, 156, 160, 61, 116, 100, 77, 89, 184, 213,
|
||||
119, 25, 4, 107, 26, 23, 87, 101, 85, 68, 115, 221, 171, 170, 123, 246, 26, 51, 108,
|
||||
13, 101, 217, 17, 0, 103, 110, 15, 136, 70, 255, 12, 197, 89, 225, 158, 25, 116, 32,
|
||||
53, 169, 122, 4, 32, 108, 30, 181, 180, 142, 250, 141, 217, 8, 33, 111, 89, 48, 196,
|
||||
90, 158, 207, 213, 165, 97, 196, 21, 193, 114, 199, 167, 123, 95, 118, 101, 107, 130,
|
||||
161, 186, 67, 27, 27, 161, 192, 157, 252, 98, 131, 186, 124, 64, 224, 199, 126, 47,
|
||||
230, 66, 92, 97, 38, 0, 35, 197, 178, 219, 121, 240, 149, 197, 18, 165, 39, 3, 137, 58,
|
||||
8, 227, 152, 124, 247, 103, 85, 150, 194, 140, 108, 96, 151, 57, 117, 182, 177, 47,
|
||||
168, 112, 184, 214, 42, 48, 65, 3, 30, 179, 187, 137, 179, 122, 98, 210, 3, 7, 102,
|
||||
235, 3, 145, 107, 200, 72, 4, 153, 135, 43, 202, 155, 170, 174, 148, 161, 37, 43, 219,
|
||||
16, 229, 65, 45, 225, 49, 126, 168, 215, 93, 153, 179, 49, 108, 51, 131, 227, 82, 134,
|
||||
77, 28, 175, 237, 136, 49, 137, 164, 197, 224, 42, 85, 250, 48, 109, 196, 70, 12, 236,
|
||||
65, 121, 254, 3, 37, 183, 201, 124, 246, 130, 174, 89, 7, 81, 235, 147, 148, 40, 147,
|
||||
174, 204, 99, 22, 150, 133, 195, 22, 108, 119, 59, 21, 104, 37, 251, 61, 92, 82, 9,
|
||||
122, 73, 48, 97, 82, 52, 170, 10, 79, 134, 17, 8, 247, 96, 108, 186, 230, 6, 138, 90,
|
||||
16, 36, 56, 45, 179, 121, 179, 17, 1, 174, 106, 4, 170, 81, 185, 60, 47, 201, 26, 67,
|
||||
199, 160, 52, 60, 63, 12, 82, 114, 122, 87, 166, 141, 168, 101, 177, 129, 197, 127,
|
||||
226, 166, 159, 168, 108, 218, 112, 31, 246, 4, 73, 134, 155, 197, 192, 8, 71, 135, 163,
|
||||
9, 122, 183, 127, 156, 60, 178, 124, 135, 129, 7, 185, 105, 232, 250, 199, 173, 80, 18,
|
||||
15, 118, 183, 3, 105, 56, 189, 107, 157, 44, 101, 103, 60, 99, 69, 187, 220, 146, 116,
|
||||
202, 85, 115, 219, 87, 241, 209, 39, 14, 184, 154, 236, 211, 31, 117, 106, 24, 68, 233,
|
||||
161, 49, 229, 105, 179, 58, 171, 215, 194, 178, 6, 243, 155, 2, 148, 202, 45, 145, 118,
|
||||
210, 233, 112, 208, 186, 157, 150, 68, 148, 94, 184, 49, 69, 36, 16, 33, 89, 36, 188,
|
||||
156, 122, 117, 124, 48, 83, 99, 153, 72, 148, 163, 14, 116, 15, 162, 169, 50, 10, 244,
|
||||
155, 136, 105, 76, 118, 209, 144, 130, 88, 102, 85, 234, 137, 222, 195, 37, 93, 169,
|
||||
179, 227, 195, 162, 203, 242, 19, 175, 252, 19, 197, 230, 97, 174, 167, 72, 10, 169,
|
||||
184, 26, 249, 41, 49, 199, 54, 205, 183, 178, 241, 5, 35, 142, 227, 196, 245, 2, 91,
|
||||
201, 6, 41, 62, 214, 158, 48, 39, 106, 189, 134, 27, 86, 2, 83, 27, 170, 30, 140, 103,
|
||||
7, 87, 65, 87, 163, 117, 188, 132, 38, 15, 116, 251, 23, 157, 113, 156, 165, 96, 41,
|
||||
112, 103, 3, 81, 200, 3, 166, 137, 98, 14, 86, 183, 200, 161, 100, 212, 199, 206, 167,
|
||||
114, 15, 235, 131, 103, 77, 103, 188, 254, 5, 30, 172, 183, 174, 176, 252, 9, 29, 90,
|
||||
78, 178, 188, 101, 158, 74, 200, 231, 75, 89, 233, 113, 206, 254, 122, 136, 41, 148,
|
||||
20, 10, 217, 144, 177, 108, 42, 124, 210, 7, 40, 138, 167, 223, 156, 151, 106, 236, 94,
|
||||
84, 183, 48, 39, 81, 77, 54, 129, 140, 133, 183, 107, 9, 8, 193, 99, 90, 185, 41, 229,
|
||||
43, 138, 11, 86, 161, 39, 124, 40, 114, 119, 122, 101, 124, 226, 227, 192, 241, 54,
|
||||
182, 89, 37, 186, 17, 90, 171, 245, 185, 119, 76, 21, 11, 170, 146, 46, 110, 228, 51,
|
||||
213, 162, 5, 193, 211, 153, 112, 148, 208, 43, 3, 254, 16, 173, 51, 88, 252, 219, 69,
|
||||
48, 242, 206, 198, 185, 105, 26,
|
||||
];
|
||||
|
||||
assert!(expected_ssk == child_node.value.0.secret_spending_key);
|
||||
assert!(expected_ccc == child_node.ccc);
|
||||
assert!(expected_nsk == child_node.value.0.private_key_holder.nullifier_secret_key);
|
||||
assert!(
|
||||
expected_ask
|
||||
== child_node
|
||||
.value
|
||||
.0
|
||||
.private_key_holder
|
||||
.authorization_secret_key
|
||||
);
|
||||
assert!(expected_nsk == child_node.value.0.private_key_holder.nullifier_secret_key());
|
||||
assert!(expected_npk == child_node.value.0.nullifier_public_key);
|
||||
assert!(expected_vsk == child_node.value.0.private_key_holder.viewing_secret_key);
|
||||
assert!(expected_vpk == child_node.value.0.viewing_public_key.to_bytes());
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use bip39::Mnemonic;
|
||||
use common::HashType;
|
||||
use lee_core::{NullifierPublicKey, NullifierSecretKey, encryption::ViewingPublicKey};
|
||||
use lee_core::{
|
||||
AuthorizationSecretKey, NullifierPublicKey, NullifierSecretKey, encryption::ViewingPublicKey,
|
||||
};
|
||||
use ml_kem;
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -36,7 +38,7 @@ impl ViewingSecretKey {
|
||||
/// for recepient.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct PrivateKeyHolder {
|
||||
pub nullifier_secret_key: NullifierSecretKey,
|
||||
pub authorization_secret_key: AuthorizationSecretKey,
|
||||
pub viewing_secret_key: ViewingSecretKey,
|
||||
}
|
||||
|
||||
@ -87,41 +89,35 @@ impl SeedHolder {
|
||||
impl SecretSpendingKey {
|
||||
#[must_use]
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
pub fn generate_nullifier_secret_key(&self, index: Option<u32>) -> NullifierSecretKey {
|
||||
const PREFIX: &[u8; 8] = b"LEE/keys";
|
||||
const SUFFIX_1: &[u8; 1] = &[1];
|
||||
const SUFFIX_2: &[u8; 19] = &[0; 19];
|
||||
pub fn generate_authorization_secret_key(&self, index: Option<u32>) -> AuthorizationSecretKey {
|
||||
const DOMAIN: &[u8; 35] = b"/LEE/v0.3/Keys/Authorization/Secret";
|
||||
|
||||
let index = index.unwrap_or(0);
|
||||
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(PREFIX);
|
||||
hasher.update(DOMAIN);
|
||||
hasher.update(self.0);
|
||||
hasher.update(SUFFIX_1);
|
||||
hasher.update(index.to_be_bytes());
|
||||
hasher.update(SUFFIX_2);
|
||||
|
||||
<NullifierSecretKey>::from(hasher.finalize_fixed())
|
||||
AuthorizationSecretKey(hasher.finalize_fixed().into())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_nullifier_secret_key(&self, index: Option<u32>) -> NullifierSecretKey {
|
||||
<NullifierSecretKey>::from(&self.generate_authorization_secret_key(index))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
pub fn generate_viewing_secret_seed_key(&self, index: Option<u32>) -> ViewingSecretKey {
|
||||
const PREFIX: &[u8; 8] = b"LEE/keys";
|
||||
const SUFFIX_1: &[u8; 1] = &[2];
|
||||
const SUFFIX_2: &[u8; 19] = &[0; 19];
|
||||
const DOMAIN: &[u8; 29] = b"/LEE/v0.3/Keys/Viewing/Secret";
|
||||
|
||||
let index = index.unwrap_or(0);
|
||||
|
||||
let mut bytes: Vec<u8> = Vec::with_capacity(64);
|
||||
bytes.extend_from_slice(PREFIX);
|
||||
bytes.extend_from_slice(&self.0);
|
||||
bytes.extend_from_slice(SUFFIX_1);
|
||||
bytes.extend_from_slice(&index.to_be_bytes());
|
||||
bytes.extend_from_slice(SUFFIX_2);
|
||||
let bytes: [u8; 64] = bytes
|
||||
.try_into()
|
||||
.expect("`generate_viewing_secret_seed_key`: bytes must be exactly 64");
|
||||
let mut bytes = [0_u8; 29 + 32 + 4];
|
||||
bytes[..29].copy_from_slice(DOMAIN);
|
||||
bytes[29..61].copy_from_slice(&self.0);
|
||||
bytes[61..].copy_from_slice(&index.to_be_bytes());
|
||||
|
||||
let full_seed = hmac_sha512::HMAC::mac(bytes, b"LEE_viewing_seed");
|
||||
|
||||
@ -139,7 +135,7 @@ impl SecretSpendingKey {
|
||||
#[must_use]
|
||||
pub fn produce_private_key_holder(&self, index: Option<u32>) -> PrivateKeyHolder {
|
||||
PrivateKeyHolder {
|
||||
nullifier_secret_key: self.generate_nullifier_secret_key(index),
|
||||
authorization_secret_key: self.generate_authorization_secret_key(index),
|
||||
viewing_secret_key: self.generate_viewing_secret_seed_key(index),
|
||||
}
|
||||
}
|
||||
@ -158,9 +154,14 @@ impl From<&ViewingSecretKey> for ViewingPublicKey {
|
||||
}
|
||||
|
||||
impl PrivateKeyHolder {
|
||||
#[must_use]
|
||||
pub fn nullifier_secret_key(&self) -> NullifierSecretKey {
|
||||
(&self.authorization_secret_key).into()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_nullifier_public_key(&self) -> NullifierPublicKey {
|
||||
(&self.nullifier_secret_key).into()
|
||||
NullifierPublicKey::from(&self.nullifier_secret_key())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, DummyInput, EncryptedAccountData, EncryptionScheme,
|
||||
EphemeralSecretKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierSecretKey,
|
||||
NullifierWitness, PrivacyPreservingCircuitOutput, PrivateAccountKind, PrivateAction,
|
||||
PrivateWitness, PublicAction, SharedSecretKey, WitnessKind,
|
||||
EphemeralSecretKey, InputAccountIdentity, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, NullifierWitness, PrivacyPreservingCircuitOutput, PrivateAccountKind,
|
||||
PrivateAction, PrivateWitness, PublicAction, SharedSecretKey, WitnessKind,
|
||||
account::{Account, AccountId, Nonce},
|
||||
compute_digest_for_path,
|
||||
encryption::{ViewTag, ViewingPublicKey},
|
||||
@ -48,7 +48,7 @@ pub fn compute_circuit_output(
|
||||
nullifier,
|
||||
}) => {
|
||||
let account_id = match kind {
|
||||
WitnessKind::Regular => {
|
||||
WitnessKind::Regular { .. } => {
|
||||
let derived = AccountId::for_regular_private_account(
|
||||
&nullifier.npk(),
|
||||
vpk,
|
||||
@ -68,12 +68,31 @@ pub fn compute_circuit_output(
|
||||
|
||||
match (kind, nullifier) {
|
||||
(
|
||||
WitnessKind::Regular,
|
||||
WitnessKind::Regular { ask },
|
||||
NullifierWitness::Init { .. } | NullifierWitness::Update { .. },
|
||||
) => assert!(
|
||||
pre_state.is_authorized,
|
||||
"Regular private account pre-state must be authorized"
|
||||
),
|
||||
) => {
|
||||
if let Some(ask) = ask {
|
||||
let derived = NullifierSecretKey::from(ask);
|
||||
match nullifier {
|
||||
// Check that the authorization key is actually bound to the
|
||||
// account Id.
|
||||
NullifierWitness::Update { nsk, .. } => assert_eq!(
|
||||
derived, *nsk,
|
||||
"Authorization secret key does not derive this account's nullifier secret key"
|
||||
),
|
||||
NullifierWitness::Init { npk, .. } => assert_eq!(
|
||||
NullifierPublicKey::from(&derived),
|
||||
*npk,
|
||||
"Authorization secret key does not derive this account's nullifier public key"
|
||||
),
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
pre_state.is_authorized,
|
||||
ask.is_some(),
|
||||
"Regular private account authorization must match the supplied credential"
|
||||
);
|
||||
}
|
||||
(WitnessKind::Pda { .. }, NullifierWitness::Init { .. }) => assert!(
|
||||
!pre_state.is_authorized,
|
||||
"Private PDA init requires unauthorized pre_state"
|
||||
@ -126,7 +145,7 @@ pub fn compute_circuit_output(
|
||||
};
|
||||
|
||||
let account_kind = match kind {
|
||||
WitnessKind::Regular => PrivateAccountKind::Regular(*identifier),
|
||||
WitnessKind::Regular { .. } => PrivateAccountKind::Regular(*identifier),
|
||||
WitnessKind::Pda { .. } => {
|
||||
let (authority_program_id, seed) = pda_seed_by_position
|
||||
.get(&pos)
|
||||
|
||||
@ -2,8 +2,8 @@ use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
Commitment, CommitmentSetDigest, Identifier, MembershipProof, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey,
|
||||
AuthorizationSecretKey, Commitment, CommitmentSetDigest, Identifier, MembershipProof,
|
||||
Nullifier, NullifierPublicKey, NullifierSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::{EncryptedAccountData, ViewTag, ViewingPublicKey},
|
||||
program::{BlockValidityWindow, PdaSeed, ProgramId, ProgramOutput, TimestampValidityWindow},
|
||||
@ -48,8 +48,9 @@ pub struct PrivateWitness {
|
||||
pub enum WitnessKind {
|
||||
/// Standalone private account. The `account_id` is derived as
|
||||
/// `AccountId::for_regular_private_account(&npk, vpk, identifier)` and matched against
|
||||
/// `pre_state.account_id`.
|
||||
Regular,
|
||||
/// `pre_state.account_id`. An honest authorized account's `npk` for Id computation gets
|
||||
/// derived from the supplied `ask`.
|
||||
Regular { ask: Option<AuthorizationSecretKey> },
|
||||
/// Private PDA. The npk-to-account_id binding is proven upstream via `Claim::Pda(seed)` or a
|
||||
/// caller's `pda_seeds` match. The identifier diversifies the PDA within the
|
||||
/// `(program_id, seed, npk)` family: `AccountId::for_private_pda` uses it as the 4th input.
|
||||
|
||||
@ -15,7 +15,9 @@ pub use encryption::{
|
||||
EncryptedAccountData, EncryptionScheme, EphemeralPublicKey, EphemeralSecretKey,
|
||||
ML_KEM_768_CIPHERTEXT_LEN, SharedSecretKey, ViewTag,
|
||||
};
|
||||
pub use nullifier::{Identifier, Nullifier, NullifierPublicKey, NullifierSecretKey};
|
||||
pub use nullifier::{
|
||||
AuthorizationSecretKey, Identifier, Nullifier, NullifierPublicKey, NullifierSecretKey,
|
||||
};
|
||||
pub use program::PrivateAccountKind;
|
||||
|
||||
pub mod account;
|
||||
|
||||
@ -48,16 +48,29 @@ impl AsRef<[u8]> for NullifierPublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Hash))]
|
||||
pub struct AuthorizationSecretKey(pub [u8; 32]);
|
||||
|
||||
impl From<&AuthorizationSecretKey> for NullifierSecretKey {
|
||||
fn from(value: &AuthorizationSecretKey) -> Self {
|
||||
const DOMAIN: &[u8; 31] = b"/LEE/v0.3/Keys/Nullifier/Secret";
|
||||
let mut bytes = [0_u8; 31 + 32];
|
||||
bytes[..31].copy_from_slice(DOMAIN);
|
||||
bytes[31..].copy_from_slice(&value.0);
|
||||
Impl::hash_bytes(&bytes)
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.expect("hash should be exactly 32 bytes long")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&NullifierSecretKey> for NullifierPublicKey {
|
||||
fn from(value: &NullifierSecretKey) -> Self {
|
||||
const PREFIX: &[u8; 8] = b"LEE/keys";
|
||||
const SUFFIX_1: &[u8; 1] = &[7];
|
||||
const SUFFIX_2: &[u8; 23] = &[0; 23];
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(PREFIX);
|
||||
bytes.extend_from_slice(value);
|
||||
bytes.extend_from_slice(SUFFIX_1);
|
||||
bytes.extend_from_slice(SUFFIX_2);
|
||||
const DOMAIN: &[u8; 31] = b"/LEE/v0.3/Keys/Nullifier/Public";
|
||||
let mut bytes = [0_u8; 31 + 32];
|
||||
bytes[..31].copy_from_slice(DOMAIN);
|
||||
bytes[31..].copy_from_slice(value);
|
||||
Self(
|
||||
Impl::hash_bytes(&bytes)
|
||||
.as_bytes()
|
||||
@ -154,6 +167,17 @@ mod tests {
|
||||
assert_eq!(nullifier, expected_nullifier);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_authorization_key() {
|
||||
let ask = AuthorizationSecretKey([0; 32]);
|
||||
let expected_nsk: NullifierSecretKey = [
|
||||
31, 33, 90, 89, 193, 14, 149, 46, 107, 38, 51, 65, 178, 242, 118, 11, 235, 198, 242,
|
||||
144, 192, 64, 39, 205, 244, 122, 210, 55, 11, 245, 117, 29,
|
||||
];
|
||||
let nsk = NullifierSecretKey::from(&ask);
|
||||
assert_eq!(nsk, expected_nsk);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_secret_key() {
|
||||
let nsk = [
|
||||
@ -161,8 +185,8 @@ mod tests {
|
||||
196, 134, 22, 224, 211, 237, 120, 136, 225, 188, 220, 249, 28,
|
||||
];
|
||||
let expected_npk = NullifierPublicKey([
|
||||
78, 20, 20, 5, 177, 198, 233, 100, 175, 134, 174, 200, 24, 205, 68, 215, 130, 74, 35,
|
||||
54, 154, 184, 219, 42, 168, 106, 126, 147, 133, 244, 18, 218,
|
||||
58, 181, 207, 24, 227, 133, 192, 231, 242, 216, 230, 219, 31, 227, 236, 94, 99, 245,
|
||||
206, 251, 237, 189, 88, 218, 215, 106, 66, 227, 136, 152, 140, 218,
|
||||
]);
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
assert_eq!(npk, expected_npk);
|
||||
@ -177,8 +201,8 @@ mod tests {
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
|
||||
let expected_account_id = AccountId::new([
|
||||
242, 239, 57, 244, 89, 109, 65, 201, 223, 100, 43, 87, 205, 83, 148, 161, 176, 22, 208,
|
||||
220, 68, 135, 10, 171, 182, 80, 54, 74, 228, 244, 236, 7,
|
||||
226, 149, 99, 147, 82, 211, 97, 152, 31, 46, 87, 113, 237, 244, 197, 108, 71, 191, 161,
|
||||
199, 140, 177, 247, 73, 95, 64, 202, 90, 8, 157, 188, 147,
|
||||
]);
|
||||
|
||||
let account_id = AccountId::for_regular_private_account(&npk, &vpk, 0);
|
||||
@ -195,8 +219,8 @@ mod tests {
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
|
||||
let expected_account_id = AccountId::new([
|
||||
149, 125, 157, 109, 119, 81, 9, 163, 231, 181, 214, 43, 57, 113, 221, 72, 180, 149,
|
||||
189, 170, 32, 181, 255, 231, 19, 92, 235, 59, 153, 185, 172, 206,
|
||||
44, 36, 222, 50, 57, 159, 215, 6, 246, 54, 45, 150, 94, 148, 148, 71, 212, 113, 165,
|
||||
10, 187, 162, 184, 70, 96, 35, 42, 230, 251, 72, 237, 80,
|
||||
]);
|
||||
|
||||
let account_id = AccountId::for_regular_private_account(&npk, &vpk, 1);
|
||||
@ -214,8 +238,8 @@ mod tests {
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_seed(&[1_u8; 32], &[2_u8; 32]);
|
||||
let expected_account_id = AccountId::new([
|
||||
30, 232, 222, 201, 233, 125, 124, 194, 58, 39, 121, 96, 185, 84, 168, 109, 80, 111,
|
||||
159, 112, 84, 100, 133, 244, 16, 34, 221, 35, 128, 131, 98, 159,
|
||||
50, 122, 67, 122, 59, 36, 150, 204, 128, 54, 55, 152, 14, 220, 163, 211, 246, 221, 197,
|
||||
75, 12, 199, 163, 151, 234, 194, 188, 13, 27, 120, 249, 220,
|
||||
]);
|
||||
|
||||
let account_id = AccountId::for_regular_private_account(&npk, &vpk, identifier);
|
||||
|
||||
@ -93,7 +93,9 @@ fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts()
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -151,7 +153,7 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
|
||||
commitment_set.extend(std::slice::from_ref(&commitment_sender));
|
||||
let expected_new_nullifiers = vec![
|
||||
(
|
||||
Nullifier::for_account_update(&commitment_sender, &sender_keys.nsk),
|
||||
Nullifier::for_account_update(&commitment_sender, &sender_keys.nsk()),
|
||||
commitment_set.digest(),
|
||||
),
|
||||
(
|
||||
@ -165,7 +167,7 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
|
||||
let expected_private_account_1 = Account {
|
||||
program_owner: program.id(),
|
||||
balance: 100 - balance_to_move,
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk),
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk()),
|
||||
..Default::default()
|
||||
};
|
||||
let expected_private_account_2 = Account {
|
||||
@ -182,7 +184,7 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
|
||||
let esk_1 = EphemeralSecretKey::new(
|
||||
&sender_account_id,
|
||||
&[0; 32],
|
||||
&sender_nonce.private_account_nonce_increment(&sender_keys.nsk),
|
||||
&sender_nonce.private_account_nonce_increment(&sender_keys.nsk()),
|
||||
);
|
||||
let shared_secret_1 = SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &esk_1).0;
|
||||
|
||||
@ -199,10 +201,12 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: commitment_set
|
||||
.get_proof_for(&commitment_sender)
|
||||
.expect("sender's commitment must be in the set"),
|
||||
@ -212,7 +216,9 @@ fn prove_privacy_preserving_execution_circuit_fully_private() {
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -284,7 +290,9 @@ fn init_note_view_tag_is_derived_from_account_keys() {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -329,10 +337,12 @@ fn update_note_view_tag_is_the_supplied_value() {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: fed_tag,
|
||||
nsk: keys.nsk,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof: commitment_set.get_proof_for(&commitment).unwrap(),
|
||||
},
|
||||
})],
|
||||
@ -381,7 +391,9 @@ fn circuit_fails_when_chained_validity_windows_have_empty_intersection() {
|
||||
vpk: account_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(account_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: account_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -574,7 +586,9 @@ fn shared_account_receives_via_simple_transfer() {
|
||||
vpk: shared_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: shared_identifier,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(shared_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: shared_npk,
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -613,9 +627,11 @@ fn private_authorized_init_encrypts_regular_kind_with_identifier() {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: NullifierPublicKey::from(&keys.nsk),
|
||||
npk: NullifierPublicKey::from(&keys.nsk()),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
},
|
||||
})],
|
||||
@ -653,7 +669,9 @@ fn private_foreign_init_encrypts_regular_kind_with_identifier() {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -680,7 +698,7 @@ fn private_authorized_update_encrypts_regular_kind_with_identifier() {
|
||||
let esk = EphemeralSecretKey::new(
|
||||
&account_id,
|
||||
&[0; 32],
|
||||
&Nonce::default().private_account_nonce_increment(&keys.nsk),
|
||||
&Nonce::default().private_account_nonce_increment(&keys.nsk()),
|
||||
);
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
|
||||
let account = Account {
|
||||
@ -701,10 +719,12 @@ fn private_authorized_update_encrypts_regular_kind_with_identifier() {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof: commitment_set.get_proof_for(&commitment).unwrap(),
|
||||
},
|
||||
})],
|
||||
@ -718,6 +738,215 @@ fn private_authorized_update_encrypts_regular_kind_with_identifier() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds an on-chain regular private account owned by `program`, returning its id, pre-state
|
||||
/// and a membership proof for its commitment.
|
||||
fn seeded_regular_account(
|
||||
keys: &crate::state::tests::TestPrivateKeys,
|
||||
program: &Program,
|
||||
identifier: u128,
|
||||
) -> (AccountId, AccountWithMetadata, lee_core::MembershipProof) {
|
||||
let account_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), identifier);
|
||||
let account = Account {
|
||||
program_owner: program.id(),
|
||||
balance: 1,
|
||||
..Account::default()
|
||||
};
|
||||
let commitment = Commitment::new(&account_id, &account);
|
||||
let mut commitment_set = CommitmentSet::with_capacity(1);
|
||||
commitment_set.extend(std::slice::from_ref(&commitment));
|
||||
let proof = commitment_set.get_proof_for(&commitment).unwrap();
|
||||
(
|
||||
account_id,
|
||||
AccountWithMetadata::new(account, false, account_id),
|
||||
proof,
|
||||
)
|
||||
}
|
||||
|
||||
/// Spending without consenting. The witness carries no `ask`, so the pre-state is unauthorized,
|
||||
/// and the nullifier is still produced from the `nsk`.
|
||||
#[test]
|
||||
fn private_regular_update_without_ask_is_spendable() {
|
||||
let program = crate::test_methods::noop();
|
||||
let keys = test_private_account_keys_1();
|
||||
let (_, pre, membership_proof) = seeded_regular_account(&keys, &program, 0);
|
||||
assert!(!pre.is_authorized);
|
||||
|
||||
execute_and_prove(
|
||||
vec![pre],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::Private(PrivateWitness {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular { ask: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof,
|
||||
},
|
||||
})],
|
||||
&program.into(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Claiming authorization without supplying an `ask` is rejected.
|
||||
#[test]
|
||||
fn private_regular_witness_without_ask_cannot_assert_authorization() {
|
||||
let program = crate::test_methods::noop();
|
||||
let keys = test_private_account_keys_1();
|
||||
let (account_id, pre, membership_proof) = seeded_regular_account(&keys, &program, 0);
|
||||
let pre = AccountWithMetadata::new(pre.account, true, account_id);
|
||||
|
||||
let result = execute_and_prove(
|
||||
vec![pre],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::Private(PrivateWitness {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular { ask: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof,
|
||||
},
|
||||
})],
|
||||
&program.into(),
|
||||
);
|
||||
|
||||
assert!(matches!(result, Err(LeeError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
/// An `ask` that does not derive this account's `nsk` is not a credential for it.
|
||||
#[test]
|
||||
fn regular_update_with_wrong_ask_nsk_is_rejected() {
|
||||
let program = crate::test_methods::noop();
|
||||
let keys = test_private_account_keys_1();
|
||||
let foreign = test_private_account_keys_2();
|
||||
let (account_id, pre, membership_proof) = seeded_regular_account(&keys, &program, 0);
|
||||
let pre = AccountWithMetadata::new(pre.account, true, account_id);
|
||||
|
||||
let result = execute_and_prove(
|
||||
vec![pre],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::Private(PrivateWitness {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(foreign.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof,
|
||||
},
|
||||
})],
|
||||
&program.into(),
|
||||
);
|
||||
|
||||
assert!(matches!(result, Err(LeeError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
/// An `ask` that does not derive this account's `npk` is not a credential for it.
|
||||
#[test]
|
||||
fn regular_init_with_non_chaining_ask_npk_is_rejected() {
|
||||
let program = crate::test_methods::claimer();
|
||||
let keys = test_private_account_keys_1();
|
||||
let foreign = test_private_account_keys_2();
|
||||
let account_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), 0);
|
||||
let pre = AccountWithMetadata::new(Account::default(), true, account_id);
|
||||
|
||||
let result = execute_and_prove(
|
||||
vec![pre],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::Private(PrivateWitness {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(foreign.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
},
|
||||
})],
|
||||
&program.into(),
|
||||
);
|
||||
|
||||
assert!(matches!(result, Err(LeeError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unauthorized_private_init_can_be_claimed() {
|
||||
let program = crate::test_methods::claimer();
|
||||
let program_id = program.id();
|
||||
let keys = test_private_account_keys_1();
|
||||
let recipient_id = AccountId::for_regular_private_account(&keys.npk(), &keys.vpk(), 0);
|
||||
let recipient = AccountWithMetadata::new(Account::default(), false, recipient_id);
|
||||
let esk = EphemeralSecretKey::new(
|
||||
&recipient_id,
|
||||
&[0; 32],
|
||||
&Nonce::private_account_nonce_init(&recipient_id),
|
||||
);
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
|
||||
|
||||
let (output, _) = execute_and_prove(
|
||||
vec![recipient],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::Private(PrivateWitness {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular { ask: None },
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
},
|
||||
})],
|
||||
&program.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (_, claimed) = EncryptionScheme::decrypt(
|
||||
&output.private_actions[0].encrypted_post_state.ciphertext,
|
||||
&ssk,
|
||||
&output.private_actions[0].nullifier,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(claimed.program_owner, program_id);
|
||||
}
|
||||
|
||||
/// A program that asserts authorization over its pre-states rejects a regular private account
|
||||
/// whose witness supplied no `ask`.
|
||||
#[test]
|
||||
fn auth_asserting_program_rejects_unauthorized_regular_private_account() {
|
||||
let program = crate::test_methods::auth_asserting_noop();
|
||||
let keys = test_private_account_keys_1();
|
||||
let (_, pre, membership_proof) = seeded_regular_account(&keys, &program, 0);
|
||||
|
||||
let result = execute_and_prove(
|
||||
vec![pre],
|
||||
Program::serialize_instruction(()).unwrap(),
|
||||
vec![InputAccountIdentity::Private(PrivateWitness {
|
||||
vpk: keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular { ask: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof,
|
||||
},
|
||||
})],
|
||||
&program.into(),
|
||||
);
|
||||
|
||||
assert!(matches!(result, Err(LeeError::ProgramProveFailed(_))));
|
||||
}
|
||||
|
||||
/// A private-PDA update with a non-default identifier produces a ciphertext that decrypts
|
||||
/// to `PrivateAccountKind::Pda` carrying the correct `(program_id, seed, identifier)`.
|
||||
#[test]
|
||||
@ -733,7 +962,7 @@ fn private_pda_update_encrypts_pda_kind_with_identifier() {
|
||||
let esk = EphemeralSecretKey::new(
|
||||
&pda_id,
|
||||
&[0; 32],
|
||||
&Nonce::default().private_account_nonce_increment(&keys.nsk),
|
||||
&Nonce::default().private_account_nonce_increment(&keys.nsk()),
|
||||
);
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &esk).0;
|
||||
let pda_account = Account {
|
||||
@ -764,7 +993,7 @@ fn private_pda_update_encrypts_pda_kind_with_identifier() {
|
||||
kind: WitnessKind::Pda { binding: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(),
|
||||
},
|
||||
}),
|
||||
@ -847,7 +1076,7 @@ fn private_pda_update_identifier_mismatch_fails() {
|
||||
kind: WitnessKind::Pda { binding: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: keys.nsk,
|
||||
nsk: keys.nsk(),
|
||||
membership_proof: commitment_set.get_proof_for(&pda_commitment).unwrap(),
|
||||
},
|
||||
}),
|
||||
|
||||
@ -75,10 +75,12 @@ fn private_changer_claimer_no_data_change_no_claim_succeeds() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
})],
|
||||
@ -109,10 +111,12 @@ fn private_changer_claimer_data_change_no_claim_fails() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
})],
|
||||
|
||||
@ -65,10 +65,12 @@ fn circuit_fails_if_invalid_auth_keys_are_provided() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: recipient_keys.nsk,
|
||||
nsk: recipient_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -76,7 +78,9 @@ fn circuit_fails_if_invalid_auth_keys_are_provided() {
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -121,10 +125,12 @@ fn circuit_should_fail_if_new_private_account_with_non_default_balance_is_provid
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -132,7 +138,9 @@ fn circuit_should_fail_if_new_private_account_with_non_default_balance_is_provid
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -177,10 +185,12 @@ fn circuit_should_fail_if_new_private_account_with_non_default_program_owner_is_
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -188,7 +198,9 @@ fn circuit_should_fail_if_new_private_account_with_non_default_program_owner_is_
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -233,10 +245,12 @@ fn circuit_should_fail_if_new_private_account_with_non_default_data_is_provided(
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -244,7 +258,9 @@ fn circuit_should_fail_if_new_private_account_with_non_default_data_is_provided(
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -289,10 +305,12 @@ fn circuit_should_fail_if_new_private_account_with_non_default_nonce_is_provided
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -300,7 +318,9 @@ fn circuit_should_fail_if_new_private_account_with_non_default_nonce_is_provided
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -343,10 +363,12 @@ fn circuit_should_fail_if_new_private_account_is_provided_with_default_values_bu
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (0, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -354,7 +376,9 @@ fn circuit_should_fail_if_new_private_account_is_provided_with_default_values_bu
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -749,10 +773,12 @@ fn circuit_should_fail_if_there_are_repeated_ids() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (1, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -760,10 +786,12 @@ fn circuit_should_fail_if_there_are_repeated_ids() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: (1, vec![]),
|
||||
},
|
||||
}),
|
||||
@ -802,9 +830,11 @@ fn private_authorized_uninitialized_account() {
|
||||
vpk: private_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(private_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: NullifierPublicKey::from(&private_keys.nsk),
|
||||
npk: NullifierPublicKey::from(&private_keys.nsk()),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
},
|
||||
})],
|
||||
@ -851,7 +881,9 @@ fn private_unauthorized_uninitialized_account_can_still_be_claimed() {
|
||||
vpk: private_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(private_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: private_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -904,9 +936,11 @@ fn private_account_claimed_then_used_without_init_flag_should_fail() {
|
||||
vpk: private_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(private_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: NullifierPublicKey::from(&private_keys.nsk),
|
||||
npk: NullifierPublicKey::from(&private_keys.nsk()),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
},
|
||||
})],
|
||||
@ -949,9 +983,11 @@ fn private_account_claimed_then_used_without_init_flag_should_fail() {
|
||||
vpk: private_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(private_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: NullifierPublicKey::from(&private_keys.nsk),
|
||||
npk: NullifierPublicKey::from(&private_keys.nsk()),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
},
|
||||
})],
|
||||
@ -1104,7 +1140,7 @@ fn two_private_pda_family_members_receive_and_spend() {
|
||||
kind: WitnessKind::Pda { binding: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: alice_keys.nsk,
|
||||
nsk: alice_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&commitment_pda_0)
|
||||
.expect("pda_0 must be in state"),
|
||||
@ -1143,7 +1179,7 @@ fn two_private_pda_family_members_receive_and_spend() {
|
||||
kind: WitnessKind::Pda { binding: None },
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: alice_keys.nsk,
|
||||
nsk: alice_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&commitment_pda_1)
|
||||
.expect("pda_1 must be in state"),
|
||||
@ -1174,7 +1210,7 @@ fn two_private_pda_family_members_receive_and_spend() {
|
||||
balance: 0,
|
||||
nonce: alice_pda_1_account
|
||||
.nonce
|
||||
.private_account_nonce_increment(&alice_keys.nsk),
|
||||
.private_account_nonce_increment(&alice_keys.nsk()),
|
||||
..Account::default()
|
||||
};
|
||||
let commitment_pda_1_after_spend =
|
||||
@ -1199,7 +1235,7 @@ fn two_private_pda_family_members_receive_and_spend() {
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: alice_keys.nsk,
|
||||
nsk: alice_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&commitment_pda_1_after_spend)
|
||||
.expect("pda_1 after spend must be in state"),
|
||||
|
||||
@ -329,10 +329,12 @@ fn authorized_public_account_claiming_succeeds_when_executed_privately() {
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&sender_commitment)
|
||||
.expect("sender's commitment must be in state"),
|
||||
@ -353,7 +355,7 @@ fn authorized_public_account_claiming_succeeds_when_executed_privately() {
|
||||
.transition_from_privacy_preserving_transaction(&tx, 1, 0)
|
||||
.unwrap();
|
||||
|
||||
let nullifier = Nullifier::for_account_update(&sender_commitment, &sender_keys.nsk);
|
||||
let nullifier = Nullifier::for_account_update(&sender_commitment, &sender_keys.nsk());
|
||||
assert!(state.private_state.1.contains(&nullifier));
|
||||
|
||||
assert_eq!(
|
||||
@ -420,8 +422,8 @@ fn private_chained_call(number_of_calls: u32) {
|
||||
dependencies.insert(simple_transfers.id(), simple_transfers);
|
||||
let program_with_deps = ProgramWithDependencies::new(chain_caller, dependencies);
|
||||
|
||||
let from_new_nonce = Nonce::default().private_account_nonce_increment(&from_keys.nsk);
|
||||
let to_new_nonce = Nonce::default().private_account_nonce_increment(&to_keys.nsk);
|
||||
let from_new_nonce = Nonce::default().private_account_nonce_increment(&from_keys.nsk());
|
||||
let to_new_nonce = Nonce::default().private_account_nonce_increment(&to_keys.nsk());
|
||||
|
||||
let from_expected_post = Account {
|
||||
balance: initial_balance - u128::from(number_of_calls) * amount,
|
||||
@ -446,10 +448,12 @@ fn private_chained_call(number_of_calls: u32) {
|
||||
vpk: from_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(from_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: from_keys.nsk,
|
||||
nsk: from_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&from_commitment)
|
||||
.expect("from's commitment must be in state"),
|
||||
@ -459,10 +463,12 @@ fn private_chained_call(number_of_calls: u32) {
|
||||
vpk: to_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(to_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: to_keys.nsk,
|
||||
nsk: to_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&to_commitment)
|
||||
.expect("to's commitment must be in state"),
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use lee_core::{
|
||||
BlockId, Commitment, DUMMY_COMMITMENT_HASH, InputAccountIdentity, Nullifier,
|
||||
NullifierPublicKey, NullifierSecretKey, NullifierWitness, PrivateWitness, Timestamp,
|
||||
AuthorizationSecretKey, BlockId, Commitment, DUMMY_COMMITMENT_HASH, InputAccountIdentity,
|
||||
Nullifier, NullifierPublicKey, NullifierSecretKey, NullifierWitness, PrivateWitness, Timestamp,
|
||||
WitnessKind,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::ViewingPublicKey,
|
||||
@ -138,14 +138,18 @@ impl TestPublicKeys {
|
||||
}
|
||||
|
||||
pub struct TestPrivateKeys {
|
||||
pub nsk: NullifierSecretKey,
|
||||
pub ask: AuthorizationSecretKey,
|
||||
pub d: [u8; 32],
|
||||
pub z: [u8; 32],
|
||||
}
|
||||
|
||||
impl TestPrivateKeys {
|
||||
pub fn nsk(&self) -> NullifierSecretKey {
|
||||
(&self.ask).into()
|
||||
}
|
||||
|
||||
pub fn npk(&self) -> NullifierPublicKey {
|
||||
NullifierPublicKey::from(&self.nsk)
|
||||
NullifierPublicKey::from(&self.nsk())
|
||||
}
|
||||
|
||||
pub fn vpk(&self) -> ViewingPublicKey {
|
||||
@ -241,7 +245,7 @@ fn test_public_account_keys_2() -> TestPublicKeys {
|
||||
|
||||
pub fn test_private_account_keys_1() -> TestPrivateKeys {
|
||||
TestPrivateKeys {
|
||||
nsk: [13; 32],
|
||||
ask: AuthorizationSecretKey([13; 32]),
|
||||
d: [31; 32],
|
||||
z: [32; 32],
|
||||
}
|
||||
@ -249,7 +253,7 @@ pub fn test_private_account_keys_1() -> TestPrivateKeys {
|
||||
|
||||
pub fn test_private_account_keys_2() -> TestPrivateKeys {
|
||||
TestPrivateKeys {
|
||||
nsk: [38; 32],
|
||||
ask: AuthorizationSecretKey([38; 32]),
|
||||
d: [83; 32],
|
||||
z: [84; 32],
|
||||
}
|
||||
@ -284,7 +288,9 @@ fn shielded_balance_transfer_for_tests(
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -331,10 +337,12 @@ fn private_balance_transfer_for_tests(
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&sender_commitment)
|
||||
.expect("sender's commitment must be in state"),
|
||||
@ -344,7 +352,9 @@ fn private_balance_transfer_for_tests(
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: recipient_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -392,10 +402,12 @@ fn deshielded_balance_transfer_for_tests(
|
||||
vpk: sender_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(sender_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.nsk,
|
||||
nsk: sender_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&sender_commitment)
|
||||
.expect("sender's commitment must be in state"),
|
||||
|
||||
@ -76,7 +76,7 @@ fn transition_from_privacy_preserving_transaction_private() {
|
||||
&sender_account_id,
|
||||
&Account {
|
||||
program_owner: crate::test_methods::simple_balance_transfer().id(),
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk),
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk()),
|
||||
balance: sender_private_account.balance - balance_to_move,
|
||||
data: Data::default(),
|
||||
},
|
||||
@ -84,7 +84,7 @@ fn transition_from_privacy_preserving_transaction_private() {
|
||||
|
||||
let sender_pre_commitment = Commitment::new(&sender_account_id, &sender_private_account);
|
||||
let expected_new_nullifier =
|
||||
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk);
|
||||
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk());
|
||||
|
||||
let expected_new_commitment_2 = Commitment::new(
|
||||
&recipient_account_id,
|
||||
@ -211,7 +211,7 @@ fn transition_from_privacy_preserving_transaction_deshielded() {
|
||||
&sender_account_id,
|
||||
&Account {
|
||||
program_owner: crate::test_methods::simple_balance_transfer().id(),
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk),
|
||||
nonce: sender_nonce.private_account_nonce_increment(&sender_keys.nsk()),
|
||||
balance: sender_private_account.balance - balance_to_move,
|
||||
data: Data::default(),
|
||||
},
|
||||
@ -219,7 +219,7 @@ fn transition_from_privacy_preserving_transaction_deshielded() {
|
||||
|
||||
let sender_pre_commitment = Commitment::new(&sender_account_id, &sender_private_account);
|
||||
let expected_new_nullifier =
|
||||
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk);
|
||||
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk());
|
||||
|
||||
assert!(state.private_state.0.contains(&sender_pre_commitment));
|
||||
assert!(!state.private_state.0.contains(&expected_new_commitment));
|
||||
@ -525,10 +525,12 @@ fn malicious_authorization_changer_should_fail_in_privacy_preserving_circuit() {
|
||||
vpk: recipient_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(recipient_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: recipient_keys.nsk,
|
||||
nsk: recipient_keys.nsk(),
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&recipient_commitment)
|
||||
.expect("recipient's commitment must be in state"),
|
||||
|
||||
@ -142,7 +142,9 @@ fn validity_window_works_in_privacy_preserving_transactions(
|
||||
vpk: account_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(account_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: account_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
@ -210,7 +212,9 @@ fn timestamp_validity_window_works_in_privacy_preserving_transactions(
|
||||
vpk: account_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(account_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Init {
|
||||
npk: account_keys.npk(),
|
||||
commitment_root: DUMMY_COMMITMENT_HASH,
|
||||
|
||||
@ -168,10 +168,12 @@ fn privacy_malicious_programs_cannot_drain_public_victim() {
|
||||
vpk: attacker_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(attacker_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: attacker_keys.nsk,
|
||||
nsk: attacker_keys.nsk(),
|
||||
membership_proof,
|
||||
},
|
||||
}),
|
||||
@ -330,10 +332,12 @@ fn privacy_malicious_programs_cannot_drain_private_victim() {
|
||||
vpk: attacker_keys.vpk(),
|
||||
random_seed: [0; 32],
|
||||
identifier: 0,
|
||||
kind: WitnessKind::Regular,
|
||||
kind: WitnessKind::Regular {
|
||||
ask: Some(attacker_keys.ask),
|
||||
},
|
||||
nullifier: NullifierWitness::Update {
|
||||
view_tag: 0,
|
||||
nsk: attacker_keys.nsk,
|
||||
nsk: attacker_keys.nsk(),
|
||||
membership_proof,
|
||||
},
|
||||
}),
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use key_protocol::key_management::{
|
||||
KeyChain,
|
||||
key_tree::chain_index::ChainIndex,
|
||||
secret_holders::{PrivateKeyHolder, SecretSpendingKey, ViewingSecretKey},
|
||||
KeyChain, key_tree::chain_index::ChainIndex, secret_holders::SecretSpendingKey,
|
||||
};
|
||||
use lee::{Account, AccountId, Data, PrivateKey, PublicKey, V03State, program::Program};
|
||||
use lee_core::{NullifierPublicKey, encryption::ViewingPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const PRIVATE_KEY_PUB_ACC_A: [u8; 32] = [
|
||||
@ -29,46 +26,6 @@ const SSK_PRIV_ACC_B: [u8; 32] = [
|
||||
180, 43, 120, 55, 151, 50, 21, 113, 22, 254, 83, 148, 56,
|
||||
];
|
||||
|
||||
const NSK_PRIV_ACC_A: [u8; 32] = [
|
||||
25, 21, 186, 59, 180, 224, 101, 64, 163, 208, 228, 43, 13, 185, 100, 123, 156, 47, 80, 179, 72,
|
||||
51, 115, 11, 180, 99, 21, 201, 48, 194, 118, 144,
|
||||
];
|
||||
|
||||
const NSK_PRIV_ACC_B: [u8; 32] = [
|
||||
99, 82, 190, 140, 234, 10, 61, 163, 15, 211, 179, 54, 70, 166, 87, 5, 182, 68, 117, 244, 217,
|
||||
23, 99, 9, 4, 177, 230, 125, 109, 91, 160, 30,
|
||||
];
|
||||
|
||||
const VSK_D_PRIV_ACC_A: [u8; 32] = [
|
||||
255, 250, 140, 26, 222, 223, 174, 95, 132, 108, 124, 88, 30, 247, 82, 72, 52, 70, 84, 139, 241,
|
||||
187, 41, 163, 19, 231, 232, 122, 225, 55, 134, 184,
|
||||
];
|
||||
|
||||
const VSK_Z_PRIV_ACC_A: [u8; 32] = [
|
||||
225, 24, 98, 78, 31, 203, 175, 248, 213, 17, 133, 207, 10, 135, 132, 151, 59, 184, 5, 81, 28,
|
||||
238, 137, 62, 233, 227, 99, 17, 236, 159, 244, 63,
|
||||
];
|
||||
|
||||
const VSK_D_PRIV_ACC_B: [u8; 32] = [
|
||||
128, 85, 85, 103, 226, 218, 119, 56, 60, 252, 31, 113, 232, 215, 156, 2, 159, 247, 156, 192,
|
||||
12, 178, 229, 236, 255, 120, 146, 211, 169, 117, 153, 180,
|
||||
];
|
||||
|
||||
const VSK_Z_PRIV_ACC_B: [u8; 32] = [
|
||||
165, 80, 169, 87, 248, 88, 167, 154, 27, 67, 131, 122, 50, 130, 111, 40, 164, 180, 204, 75,
|
||||
188, 140, 110, 132, 113, 133, 222, 8, 49, 123, 187, 18,
|
||||
];
|
||||
|
||||
const NPK_PRIV_ACC_A: [u8; 32] = [
|
||||
167, 108, 50, 153, 74, 47, 151, 188, 140, 79, 195, 31, 181, 9, 40, 167, 201, 32, 175, 129, 45,
|
||||
245, 223, 193, 210, 170, 247, 128, 167, 140, 155, 129,
|
||||
];
|
||||
|
||||
const NPK_PRIV_ACC_B: [u8; 32] = [
|
||||
32, 67, 72, 164, 106, 53, 66, 239, 141, 15, 52, 230, 136, 177, 2, 236, 207, 243, 134, 135, 210,
|
||||
143, 87, 232, 215, 128, 194, 120, 113, 224, 4, 165,
|
||||
];
|
||||
|
||||
const DEFAULT_PROGRAM_OWNER: [u32; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
const PUB_ACC_A_INITIAL_BALANCE: u128 = 10000;
|
||||
@ -133,26 +90,23 @@ pub fn initial_pub_accounts_private_keys() -> Vec<PublicAccountPrivateInitialDat
|
||||
]
|
||||
}
|
||||
|
||||
fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialData> {
|
||||
let key_chain_1 = KeyChain {
|
||||
secret_spending_key: SecretSpendingKey(SSK_PRIV_ACC_A),
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: NSK_PRIV_ACC_A,
|
||||
viewing_secret_key: ViewingSecretKey::new(VSK_D_PRIV_ACC_A, VSK_Z_PRIV_ACC_A),
|
||||
},
|
||||
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_A),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_A, &VSK_Z_PRIV_ACC_A),
|
||||
};
|
||||
fn key_chain_from_ssk(ssk: [u8; 32]) -> KeyChain {
|
||||
let secret_spending_key = SecretSpendingKey(ssk);
|
||||
let private_key_holder = secret_spending_key.produce_private_key_holder(None);
|
||||
let nullifier_public_key = private_key_holder.generate_nullifier_public_key();
|
||||
let viewing_public_key = private_key_holder.generate_viewing_public_key();
|
||||
|
||||
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::new(VSK_D_PRIV_ACC_B, VSK_Z_PRIV_ACC_B),
|
||||
},
|
||||
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_B),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_B, &VSK_Z_PRIV_ACC_B),
|
||||
};
|
||||
KeyChain {
|
||||
secret_spending_key,
|
||||
private_key_holder,
|
||||
nullifier_public_key,
|
||||
viewing_public_key,
|
||||
}
|
||||
}
|
||||
|
||||
fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialData> {
|
||||
let key_chain_1 = key_chain_from_ssk(SSK_PRIV_ACC_A);
|
||||
let key_chain_2 = key_chain_from_ssk(SSK_PRIV_ACC_B);
|
||||
|
||||
vec![
|
||||
PrivateAccountPrivateInitialData {
|
||||
@ -313,13 +267,35 @@ pub fn initial_state_testnet() -> V03State {
|
||||
mod tests {
|
||||
use std::str::FromStr as _;
|
||||
|
||||
use key_protocol::key_management::secret_holders::ViewingSecretKey;
|
||||
|
||||
use super::*;
|
||||
|
||||
const VSK_D_PRIV_ACC_A: [u8; 32] = [
|
||||
4, 118, 187, 42, 14, 254, 144, 150, 125, 176, 205, 240, 109, 81, 234, 177, 244, 236, 108,
|
||||
71, 107, 10, 107, 169, 95, 134, 75, 193, 213, 57, 81, 218,
|
||||
];
|
||||
|
||||
const VSK_Z_PRIV_ACC_A: [u8; 32] = [
|
||||
117, 29, 113, 136, 175, 148, 38, 38, 110, 220, 157, 155, 245, 13, 239, 244, 106, 126, 188,
|
||||
90, 204, 28, 82, 70, 200, 16, 219, 33, 43, 210, 125, 239,
|
||||
];
|
||||
|
||||
const VSK_D_PRIV_ACC_B: [u8; 32] = [
|
||||
100, 59, 111, 232, 245, 32, 102, 179, 205, 119, 145, 238, 9, 235, 62, 38, 55, 252, 179,
|
||||
217, 219, 211, 6, 188, 85, 160, 68, 54, 61, 114, 102, 81,
|
||||
];
|
||||
|
||||
const VSK_Z_PRIV_ACC_B: [u8; 32] = [
|
||||
123, 246, 87, 46, 116, 95, 39, 122, 251, 71, 207, 144, 70, 227, 120, 27, 98, 59, 67, 247,
|
||||
209, 194, 110, 231, 250, 247, 205, 243, 31, 142, 104, 208,
|
||||
];
|
||||
|
||||
const PUB_ACC_A_TEXT_ADDR: &str = "6iArKUXxhUJqS7kCaPNhwMWt3ro71PDyBj7jwAyE2VQV";
|
||||
const PUB_ACC_B_TEXT_ADDR: &str = "7wHg9sbJwc6h3NP1S9bekfAzB8CHifEcxKswCKUt3YQo";
|
||||
|
||||
const PRIV_ACC_A_TEXT_ADDR: &str = "EVesBKsYRVtkjnTcsbk8tWHkBn2xZmzAXzwgrP3ZaVoZ";
|
||||
const PRIV_ACC_B_TEXT_ADDR: &str = "94MXhZnueurjX6v37CYDKVEKYBiyhYArvtEdceq2XDQP";
|
||||
const PRIV_ACC_A_TEXT_ADDR: &str = "GSx3EttJzQqhFPibttxguyhKXkiD4DJmA2dMmuszEmFv";
|
||||
const PRIV_ACC_B_TEXT_ADDR: &str = "Dec1rT4DynCafh6k5pmywLGUU16RpxcxCdrSVYq8ukaN";
|
||||
|
||||
#[test]
|
||||
fn pub_state_consistency() {
|
||||
@ -358,78 +334,24 @@ mod tests {
|
||||
let init_private_accs_keys = initial_priv_accounts_private_keys();
|
||||
let init_comms = initial_commitments();
|
||||
|
||||
// `nsk`/`npk` carry no constants of their own: the key chains derive from `SSK_*`, and the
|
||||
// two address canaries below pin H(PREFIX || npk || vpk || identifier), so drift anywhere
|
||||
// in ask -> nsk -> npk or in vsk -> vpk moves one of them. Nothing is left unpinned.
|
||||
// `VSK_*` stays pinned separately because it is the last value on the vsk -> vpk leg that
|
||||
// a test can compare directly.
|
||||
assert_eq!(
|
||||
init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.secret_spending_key
|
||||
.produce_private_key_holder(None)
|
||||
.nullifier_secret_key,
|
||||
init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.nullifier_secret_key
|
||||
);
|
||||
assert_eq!(
|
||||
init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.secret_spending_key
|
||||
.produce_private_key_holder(None)
|
||||
.viewing_secret_key,
|
||||
init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.viewing_secret_key
|
||||
);
|
||||
assert_eq!(
|
||||
init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.generate_nullifier_public_key(),
|
||||
init_private_accs_keys[0].key_chain.nullifier_public_key
|
||||
);
|
||||
assert_eq!(
|
||||
init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.generate_viewing_public_key(),
|
||||
init_private_accs_keys[0].key_chain.viewing_public_key
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.secret_spending_key
|
||||
.produce_private_key_holder(None)
|
||||
.nullifier_secret_key,
|
||||
init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.nullifier_secret_key
|
||||
ViewingSecretKey::new(VSK_D_PRIV_ACC_A, VSK_Z_PRIV_ACC_A)
|
||||
);
|
||||
assert_eq!(
|
||||
init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.secret_spending_key
|
||||
.produce_private_key_holder(None)
|
||||
.private_key_holder
|
||||
.viewing_secret_key,
|
||||
init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.viewing_secret_key
|
||||
);
|
||||
assert_eq!(
|
||||
init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.generate_nullifier_public_key(),
|
||||
init_private_accs_keys[1].key_chain.nullifier_public_key
|
||||
);
|
||||
assert_eq!(
|
||||
init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.private_key_holder
|
||||
.generate_viewing_public_key(),
|
||||
init_private_accs_keys[1].key_chain.viewing_public_key
|
||||
ViewingSecretKey::new(VSK_D_PRIV_ACC_B, VSK_Z_PRIV_ACC_B)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -453,7 +375,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
init_comms[0],
|
||||
PrivateAccountPublicInitialData {
|
||||
npk: NullifierPublicKey(NPK_PRIV_ACC_A),
|
||||
npk: init_private_accs_keys[0].key_chain.nullifier_public_key,
|
||||
vpk: init_private_accs_keys[0]
|
||||
.key_chain
|
||||
.viewing_public_key
|
||||
@ -470,7 +392,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
init_comms[1],
|
||||
PrivateAccountPublicInitialData {
|
||||
npk: NullifierPublicKey(NPK_PRIV_ACC_B),
|
||||
npk: init_private_accs_keys[1].key_chain.nullifier_public_key,
|
||||
vpk: init_private_accs_keys[1]
|
||||
.key_chain
|
||||
.viewing_public_key
|
||||
|
||||
@ -361,6 +361,7 @@ pub unsafe extern "C" fn wallet_ffi_free_account_identity(
|
||||
kind: _,
|
||||
account_id: _,
|
||||
key_path,
|
||||
authorization_secret_key: _,
|
||||
nullifier_secret_key: _,
|
||||
nullifier_public_key: _,
|
||||
viewing_public_key,
|
||||
|
||||
@ -8,7 +8,10 @@ use std::{
|
||||
};
|
||||
|
||||
use lee::{Data, ProgramId, SharedSecretKey};
|
||||
use lee_core::{encryption::MlKem768EncapsulationKey, program::PdaSeed, NullifierPublicKey};
|
||||
use lee_core::{
|
||||
encryption::MlKem768EncapsulationKey, program::PdaSeed, AuthorizationSecretKey,
|
||||
NullifierPublicKey, NullifierSecretKey,
|
||||
};
|
||||
use wallet::{account::AccountIdWithPrivacy, AccountIdentity};
|
||||
|
||||
use crate::error::WalletFfiError;
|
||||
@ -238,6 +241,7 @@ pub struct FfiAccountIdentity {
|
||||
pub account_id: FfiBytes32,
|
||||
/// C-compatible string.
|
||||
pub key_path: *mut c_char,
|
||||
pub authorization_secret_key: FfiBytes32,
|
||||
pub nullifier_secret_key: FfiBytes32,
|
||||
pub nullifier_public_key: FfiBytes32,
|
||||
pub viewing_public_key: *const u8,
|
||||
@ -251,6 +255,7 @@ impl Default for FfiAccountIdentity {
|
||||
kind: FfiAccountIdentityKind::Public,
|
||||
account_id: FfiBytes32::default(),
|
||||
key_path: std::ptr::null_mut(),
|
||||
authorization_secret_key: FfiBytes32::default(),
|
||||
nullifier_secret_key: FfiBytes32::default(),
|
||||
nullifier_public_key: FfiBytes32::default(),
|
||||
viewing_public_key: std::ptr::null(),
|
||||
@ -444,8 +449,7 @@ impl From<AccountIdentity> for FfiAccountIdentity {
|
||||
}
|
||||
}
|
||||
AccountIdentity::PrivateShared {
|
||||
nsk,
|
||||
npk,
|
||||
ask,
|
||||
vpk,
|
||||
identifier,
|
||||
} => {
|
||||
@ -458,10 +462,13 @@ impl From<AccountIdentity> for FfiAccountIdentity {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
let nsk = NullifierSecretKey::from(&ask);
|
||||
|
||||
Self {
|
||||
kind: FfiAccountIdentityKind::PrivateShared,
|
||||
authorization_secret_key: ask.0.into(),
|
||||
nullifier_secret_key: nsk.into(),
|
||||
nullifier_public_key: npk.0.into(),
|
||||
nullifier_public_key: NullifierPublicKey::from(&nsk).0.into(),
|
||||
viewing_public_key: vpk_data,
|
||||
viewing_public_key_len: vpk_len,
|
||||
identifier: identifier.into(),
|
||||
@ -471,7 +478,6 @@ impl From<AccountIdentity> for FfiAccountIdentity {
|
||||
AccountIdentity::PrivatePdaShared {
|
||||
account_id,
|
||||
nsk,
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
} => {
|
||||
@ -488,7 +494,7 @@ impl From<AccountIdentity> for FfiAccountIdentity {
|
||||
kind: FfiAccountIdentityKind::PrivatePdaShared,
|
||||
account_id: account_id.into(),
|
||||
nullifier_secret_key: nsk.into(),
|
||||
nullifier_public_key: npk.0.into(),
|
||||
nullifier_public_key: NullifierPublicKey::from(&nsk).0.into(),
|
||||
viewing_public_key: vpk_data,
|
||||
viewing_public_key_len: vpk_len,
|
||||
identifier: identifier.into(),
|
||||
@ -578,9 +584,16 @@ impl TryFrom<&FfiAccountIdentity> for AccountIdentity {
|
||||
Err(WalletFfiError::InvalidKeyValue)
|
||||
}?;
|
||||
|
||||
let ask = AuthorizationSecretKey(value.authorization_secret_key.data);
|
||||
let nsk = NullifierSecretKey::from(&ask);
|
||||
if value.nullifier_secret_key.data != nsk
|
||||
|| value.nullifier_public_key.data != NullifierPublicKey::from(&nsk).0
|
||||
{
|
||||
return Err(WalletFfiError::InvalidKeyValue);
|
||||
}
|
||||
|
||||
Ok(Self::PrivateShared {
|
||||
nsk: value.nullifier_secret_key.data,
|
||||
npk: NullifierPublicKey(value.nullifier_public_key.data),
|
||||
ask,
|
||||
vpk,
|
||||
identifier: value.identifier.into(),
|
||||
})
|
||||
@ -599,10 +612,14 @@ impl TryFrom<&FfiAccountIdentity> for AccountIdentity {
|
||||
Err(WalletFfiError::InvalidKeyValue)
|
||||
}?;
|
||||
|
||||
let nsk = value.nullifier_secret_key.data;
|
||||
if value.nullifier_public_key.data != NullifierPublicKey::from(&nsk).0 {
|
||||
return Err(WalletFfiError::InvalidKeyValue);
|
||||
}
|
||||
|
||||
Ok(Self::PrivatePdaShared {
|
||||
account_id: value.account_id.into(),
|
||||
nsk: value.nullifier_secret_key.data,
|
||||
npk: NullifierPublicKey(value.nullifier_public_key.data),
|
||||
nsk,
|
||||
vpk,
|
||||
identifier: value.identifier.into(),
|
||||
})
|
||||
@ -658,10 +675,13 @@ impl From<FfiAccountIdWithPrivacy> for AccountIdWithPrivacy {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lee::{AccountId, PrivateKey, PublicKey};
|
||||
use lee_core::{encryption::ViewingPublicKey, program::PdaSeed, PrivateAccountKind};
|
||||
use lee_core::{
|
||||
encryption::ViewingPublicKey, program::PdaSeed, AuthorizationSecretKey, NullifierSecretKey,
|
||||
PrivateAccountKind,
|
||||
};
|
||||
use wallet::AccountIdentity;
|
||||
|
||||
use crate::{FfiAccountIdentity, FfiAccountIdentityKind};
|
||||
use crate::{error::WalletFfiError, FfiAccountIdentity, FfiAccountIdentityKind, FfiBytes32};
|
||||
|
||||
#[test]
|
||||
fn account_identity_roundtrip() {
|
||||
@ -669,7 +689,8 @@ mod tests {
|
||||
let public_key = PublicKey::new_from_private_key(&private_key);
|
||||
let pub_acc_id = (&public_key).into();
|
||||
|
||||
let nsk = [43; 32];
|
||||
let ask = AuthorizationSecretKey([43; 32]);
|
||||
let nsk = NullifierSecretKey::from(&ask);
|
||||
let vpk = ViewingPublicKey::from_seed(&[44; 32], &[54; 32]);
|
||||
let npk = (&nsk).into();
|
||||
let identifier = u128::from_le_bytes([45; 16]);
|
||||
@ -708,15 +729,13 @@ mod tests {
|
||||
identifier,
|
||||
};
|
||||
let acc_identity_7 = AccountIdentity::PrivateShared {
|
||||
nsk,
|
||||
npk,
|
||||
ask,
|
||||
vpk: vpk.clone(),
|
||||
identifier,
|
||||
};
|
||||
let acc_identity_8 = AccountIdentity::PrivatePdaShared {
|
||||
account_id: private_pda_acc_id,
|
||||
nsk,
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
};
|
||||
@ -765,6 +784,10 @@ mod tests {
|
||||
FfiAccountIdentityKind::PrivatePdaShared
|
||||
);
|
||||
|
||||
assert_eq!(ffi_acc_identity_7.nullifier_secret_key.data, nsk);
|
||||
assert_eq!(ffi_acc_identity_7.nullifier_public_key.data, npk.0);
|
||||
assert_eq!(ffi_acc_identity_8.nullifier_public_key.data, npk.0);
|
||||
|
||||
let acc_identity_res_1: AccountIdentity = (&ffi_acc_identity_1).try_into().unwrap();
|
||||
let acc_identity_res_2: AccountIdentity = (&ffi_acc_identity_2).try_into().unwrap();
|
||||
let acc_identity_res_2_5: AccountIdentity = (&ffi_acc_identity_2_5).try_into().unwrap();
|
||||
@ -785,4 +808,49 @@ mod tests {
|
||||
assert_eq!(acc_identity_res_7, acc_identity_7);
|
||||
assert_eq!(acc_identity_res_8, acc_identity_8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inconsistent_derived_keys_are_rejected() {
|
||||
let ask = AuthorizationSecretKey([43; 32]);
|
||||
let nsk = NullifierSecretKey::from(&ask);
|
||||
let vpk = ViewingPublicKey::from_seed(&[44; 32], &[54; 32]);
|
||||
let identifier = u128::from_le_bytes([45; 16]);
|
||||
|
||||
let shared = AccountIdentity::PrivateShared {
|
||||
ask,
|
||||
vpk: vpk.clone(),
|
||||
identifier,
|
||||
};
|
||||
let pda_shared = AccountIdentity::PrivatePdaShared {
|
||||
account_id: AccountId::new([46; 32]),
|
||||
nsk,
|
||||
vpk,
|
||||
identifier,
|
||||
};
|
||||
|
||||
let mut tampered_nsk: FfiAccountIdentity = shared.clone().into();
|
||||
tampered_nsk.nullifier_secret_key.data[0] ^= 1;
|
||||
let mut tampered_npk: FfiAccountIdentity = shared.clone().into();
|
||||
tampered_npk.nullifier_public_key.data[0] ^= 1;
|
||||
let mut zeroed: FfiAccountIdentity = shared.into();
|
||||
zeroed.nullifier_secret_key = FfiBytes32::default();
|
||||
zeroed.nullifier_public_key = FfiBytes32::default();
|
||||
let mut tampered_pda_npk: FfiAccountIdentity = pda_shared.clone().into();
|
||||
tampered_pda_npk.nullifier_public_key.data[0] ^= 1;
|
||||
let mut zeroed_pda: FfiAccountIdentity = pda_shared.into();
|
||||
zeroed_pda.nullifier_public_key = FfiBytes32::default();
|
||||
|
||||
for inconsistent in [
|
||||
&tampered_nsk,
|
||||
&tampered_npk,
|
||||
&zeroed,
|
||||
&tampered_pda_npk,
|
||||
&zeroed_pda,
|
||||
] {
|
||||
assert_eq!(
|
||||
AccountIdentity::try_from(inconsistent).unwrap_err(),
|
||||
WalletFfiError::InvalidKeyValue
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,6 +249,7 @@ typedef struct FfiAccountIdentity {
|
||||
* C-compatible string.
|
||||
*/
|
||||
char *key_path;
|
||||
struct FfiBytes32 authorization_secret_key;
|
||||
struct FfiBytes32 nullifier_secret_key;
|
||||
struct FfiBytes32 nullifier_public_key;
|
||||
const uint8_t *viewing_public_key;
|
||||
|
||||
@ -4,9 +4,9 @@ use anyhow::Result;
|
||||
use keycard_wallet::KeycardWallet;
|
||||
use lee::{AccountId, PrivateKey, PublicKey, Signature};
|
||||
use lee_core::{
|
||||
Commitment, CommitmentSetDigest, DummyInput, Identifier, InputAccountIdentity, MembershipProof,
|
||||
NullifierPublicKey, NullifierSecretKey, NullifierWitness, PrivateAccountKind, PrivateWitness,
|
||||
SharedSecretKey, WitnessKind,
|
||||
AuthorizationSecretKey, Commitment, CommitmentSetDigest, DummyInput, Identifier,
|
||||
InputAccountIdentity, MembershipProof, NullifierPublicKey, NullifierSecretKey,
|
||||
NullifierWitness, PrivateAccountKind, PrivateWitness, SharedSecretKey, WitnessKind,
|
||||
account::{Account, AccountWithMetadata, Nonce},
|
||||
compute_digest_for_path,
|
||||
encryption::{
|
||||
@ -45,20 +45,20 @@ pub enum AccountIdentity {
|
||||
identifier: Identifier,
|
||||
},
|
||||
/// A shared regular private account with externally-provided keys (e.g. from GMS).
|
||||
/// Uses standard `AccountId = from((&npk, identifier))` with authorized/unauthorized private
|
||||
/// paths. Works with `authenticated_transfer` and all existing programs out of the box.
|
||||
/// Carries the authorization secret key: the `nsk` and `npk` behind
|
||||
/// `AccountId = from((&npk, &vpk, identifier))` are derived from it.
|
||||
/// Works with `authenticated_transfer` and all existing programs out of the box.
|
||||
PrivateShared {
|
||||
nsk: NullifierSecretKey,
|
||||
npk: NullifierPublicKey,
|
||||
ask: AuthorizationSecretKey,
|
||||
vpk: ViewingPublicKey,
|
||||
identifier: Identifier,
|
||||
},
|
||||
/// A shared private PDA with externally-provided keys (e.g. from GMS).
|
||||
/// `account_id` was derived via [`AccountId::for_private_pda`].
|
||||
/// `account_id` was derived via [`AccountId::for_private_pda`]; its `npk` is derived from
|
||||
/// the `nsk` at use.
|
||||
PrivatePdaShared {
|
||||
account_id: AccountId,
|
||||
nsk: NullifierSecretKey,
|
||||
npk: NullifierPublicKey,
|
||||
vpk: ViewingPublicKey,
|
||||
identifier: Identifier,
|
||||
},
|
||||
@ -102,20 +102,15 @@ impl fmt::Debug for AccountIdentity {
|
||||
.field("identifier", identifier)
|
||||
.finish(),
|
||||
Self::PrivateShared {
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
..
|
||||
vpk, identifier, ..
|
||||
} => f
|
||||
.debug_struct("PrivateShared")
|
||||
.field("nsk", &"<redacted>")
|
||||
.field("npk", npk)
|
||||
.field("ask", &"<redacted>")
|
||||
.field("vpk", vpk)
|
||||
.field("identifier", identifier)
|
||||
.finish(),
|
||||
Self::PrivatePdaShared {
|
||||
account_id,
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
..
|
||||
@ -123,7 +118,6 @@ impl fmt::Debug for AccountIdentity {
|
||||
.debug_struct("PrivatePdaShared")
|
||||
.field("account_id", account_id)
|
||||
.field("nsk", &"<redacted>")
|
||||
.field("npk", npk)
|
||||
.field("vpk", vpk)
|
||||
.field("identifier", identifier)
|
||||
.finish(),
|
||||
@ -266,21 +260,10 @@ impl AccountManager {
|
||||
vpk,
|
||||
identifier,
|
||||
} => {
|
||||
let acc = lee_core::account::Account::default();
|
||||
let auth_acc = AccountWithMetadata::new(acc, true, (&npk, &vpk, identifier));
|
||||
let random_seed = random_bytes();
|
||||
let pre = AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
identifier,
|
||||
vpk,
|
||||
pre_state: auth_acc,
|
||||
proof: None,
|
||||
random_seed,
|
||||
is_pda: false,
|
||||
};
|
||||
|
||||
State::Private(pre)
|
||||
let account_id = lee::AccountId::from((&npk, &vpk, identifier));
|
||||
State::Private(private_foreign_acc_preparation(
|
||||
account_id, npk, vpk, identifier, false,
|
||||
))
|
||||
}
|
||||
AccountIdentity::PrivatePdaOwned(account_id) => {
|
||||
let pre = private_key_tree_acc_preparation(wallet, account_id, true)?;
|
||||
@ -291,31 +274,25 @@ impl AccountManager {
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
} => {
|
||||
let acc = lee_core::account::Account::default();
|
||||
let auth_acc = AccountWithMetadata::new(acc, false, account_id);
|
||||
let random_seed = random_bytes();
|
||||
let pre = AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
identifier,
|
||||
vpk,
|
||||
pre_state: auth_acc,
|
||||
proof: None,
|
||||
random_seed,
|
||||
is_pda: true,
|
||||
};
|
||||
State::Private(pre)
|
||||
}
|
||||
} => State::Private(private_foreign_acc_preparation(
|
||||
account_id, npk, vpk, identifier, true,
|
||||
)),
|
||||
AccountIdentity::PrivateShared {
|
||||
nsk,
|
||||
npk,
|
||||
ask,
|
||||
vpk,
|
||||
identifier,
|
||||
} => {
|
||||
let nsk = NullifierSecretKey::from(&ask);
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let account_id = lee::AccountId::from((&npk, &vpk, identifier));
|
||||
let pre = private_shared_acc_preparation(
|
||||
wallet, account_id, nsk, npk, vpk, identifier, false,
|
||||
wallet,
|
||||
account_id,
|
||||
nsk,
|
||||
vpk,
|
||||
identifier,
|
||||
Some(ask),
|
||||
false,
|
||||
);
|
||||
|
||||
State::Private(pre)
|
||||
@ -323,12 +300,11 @@ impl AccountManager {
|
||||
AccountIdentity::PrivatePdaShared {
|
||||
account_id,
|
||||
nsk,
|
||||
npk,
|
||||
vpk,
|
||||
identifier,
|
||||
} => {
|
||||
let pre = private_shared_acc_preparation(
|
||||
wallet, account_id, nsk, npk, vpk, identifier, true,
|
||||
wallet, account_id, nsk, vpk, identifier, None, true,
|
||||
);
|
||||
|
||||
State::Private(pre)
|
||||
@ -448,7 +424,7 @@ impl AccountManager {
|
||||
kind: if pre.is_pda {
|
||||
WitnessKind::Pda { binding: None }
|
||||
} else {
|
||||
WitnessKind::Regular
|
||||
WitnessKind::Regular { ask: pre.ask }
|
||||
},
|
||||
nullifier: match (pre.nsk, pre.proof.clone()) {
|
||||
(Some(nsk), Some(membership_proof)) => NullifierWitness::Update {
|
||||
@ -527,6 +503,7 @@ impl AccountManager {
|
||||
}
|
||||
|
||||
struct AccountPreparedData {
|
||||
ask: Option<AuthorizationSecretKey>,
|
||||
nsk: Option<NullifierSecretKey>,
|
||||
npk: NullifierPublicKey,
|
||||
identifier: Identifier,
|
||||
@ -550,7 +527,8 @@ fn private_key_tree_acc_preparation(
|
||||
|
||||
let from_identifier = from_acc.kind.identifier();
|
||||
let from_keys = &from_acc.key_chain;
|
||||
let nsk = from_keys.private_key_holder.nullifier_secret_key;
|
||||
let ask = from_keys.private_key_holder.authorization_secret_key;
|
||||
let nsk = from_keys.private_key_holder.nullifier_secret_key();
|
||||
let from_npk = from_keys.nullifier_public_key;
|
||||
let from_vpk = from_keys.viewing_public_key.clone();
|
||||
|
||||
@ -561,6 +539,8 @@ fn private_key_tree_acc_preparation(
|
||||
let random_seed = random_bytes();
|
||||
|
||||
Ok(AccountPreparedData {
|
||||
// A PDA is program-authorized and carries no credential of its own.
|
||||
ask: (!is_pda).then_some(ask),
|
||||
nsk: Some(nsk),
|
||||
npk: from_npk,
|
||||
identifier: from_identifier,
|
||||
@ -572,15 +552,40 @@ fn private_key_tree_acc_preparation(
|
||||
})
|
||||
}
|
||||
|
||||
fn private_shared_acc_preparation(
|
||||
wallet: &WalletCore,
|
||||
/// Prepare a private account with no secret key knowledge, i.e. for inits.
|
||||
fn private_foreign_acc_preparation(
|
||||
account_id: AccountId,
|
||||
nsk: NullifierSecretKey,
|
||||
npk: NullifierPublicKey,
|
||||
vpk: ViewingPublicKey,
|
||||
identifier: Identifier,
|
||||
is_pda: bool,
|
||||
) -> AccountPreparedData {
|
||||
AccountPreparedData {
|
||||
// The wallet holds no key for a recipient, so it can neither spend the account nor
|
||||
// consent on its behalf. The program still claims it: a private claim never requires
|
||||
// authorization.
|
||||
ask: None,
|
||||
nsk: None,
|
||||
npk,
|
||||
identifier,
|
||||
vpk,
|
||||
pre_state: AccountWithMetadata::new(Account::default(), false, account_id),
|
||||
proof: None,
|
||||
random_seed: random_bytes(),
|
||||
is_pda,
|
||||
}
|
||||
}
|
||||
|
||||
fn private_shared_acc_preparation(
|
||||
wallet: &WalletCore,
|
||||
account_id: AccountId,
|
||||
nsk: NullifierSecretKey,
|
||||
vpk: ViewingPublicKey,
|
||||
identifier: Identifier,
|
||||
ask: Option<AuthorizationSecretKey>,
|
||||
is_pda: bool,
|
||||
) -> AccountPreparedData {
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let acc = wallet
|
||||
.storage()
|
||||
.key_chain()
|
||||
@ -593,6 +598,7 @@ fn private_shared_acc_preparation(
|
||||
let random_seed = random_bytes();
|
||||
|
||||
AccountPreparedData {
|
||||
ask,
|
||||
nsk: Some(nsk),
|
||||
npk,
|
||||
identifier,
|
||||
@ -701,8 +707,7 @@ mod tests {
|
||||
#[test]
|
||||
fn private_shared_is_private() {
|
||||
let acc = AccountIdentity::PrivateShared {
|
||||
nsk: [0; 32],
|
||||
npk: NullifierPublicKey([1; 32]),
|
||||
ask: AuthorizationSecretKey([0; 32]),
|
||||
vpk: ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]),
|
||||
identifier: 42,
|
||||
};
|
||||
@ -715,6 +720,7 @@ mod tests {
|
||||
let vpk = ViewingPublicKey::from_seed(&[0; 32], &[0; 32]);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, (&npk, &vpk, 0));
|
||||
State::Private(AccountPreparedData {
|
||||
ask: None,
|
||||
nsk: None,
|
||||
npk,
|
||||
identifier: 0,
|
||||
@ -741,6 +747,23 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreign_private_init_is_unauthorized() {
|
||||
let npk = NullifierPublicKey([7; 32]);
|
||||
let vpk = ViewingPublicKey::from_seed(&[8; 32], &[9; 32]);
|
||||
let account_id = lee::AccountId::from((&npk, &vpk, 0));
|
||||
let pre = private_foreign_acc_preparation(account_id, npk, vpk, 0, false);
|
||||
|
||||
assert!(pre.ask.is_none());
|
||||
assert!(!pre.pre_state.is_authorized);
|
||||
|
||||
let identities = manager(vec![State::Private(pre)]).account_identities();
|
||||
let InputAccountIdentity::Private(witness) = &identities[0] else {
|
||||
panic!("expected a private witness");
|
||||
};
|
||||
assert!(matches!(witness.kind, WitnessKind::Regular { ask: None }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_inputs_default_pads_private_count_to_max() {
|
||||
let max = AccountManager::MAX_PRIVATE_ACCOUNTS;
|
||||
|
||||
@ -373,23 +373,19 @@ impl WalletCore {
|
||||
.key_chain()
|
||||
.shared_private_account(account_id)?;
|
||||
let keys = self.storage.key_chain().derive_shared_account_keys(entry)?;
|
||||
let nsk = keys.nullifier_secret_key;
|
||||
let npk = keys.generate_nullifier_public_key();
|
||||
let vpk = keys.generate_viewing_public_key();
|
||||
let identifier = entry.identifier;
|
||||
|
||||
if entry.pda_seed.is_some() {
|
||||
Some(AccountIdentity::PrivatePdaShared {
|
||||
account_id,
|
||||
nsk,
|
||||
npk,
|
||||
nsk: keys.nullifier_secret_key(),
|
||||
vpk,
|
||||
identifier,
|
||||
})
|
||||
} else {
|
||||
Some(AccountIdentity::PrivateShared {
|
||||
nsk,
|
||||
npk,
|
||||
ask: keys.authorization_secret_key,
|
||||
vpk,
|
||||
identifier,
|
||||
})
|
||||
@ -989,7 +985,7 @@ impl WalletCore {
|
||||
&key_chain.viewing_public_key,
|
||||
&kind,
|
||||
);
|
||||
let nsk = key_chain.private_key_holder.nullifier_secret_key;
|
||||
let nsk = key_chain.private_key_holder.nullifier_secret_key();
|
||||
(account_id, kind, res_acc, nsk)
|
||||
})
|
||||
})
|
||||
@ -1028,7 +1024,7 @@ impl WalletCore {
|
||||
let keys = self.storage.key_chain().derive_shared_account_keys(entry)?;
|
||||
let npk = keys.generate_nullifier_public_key();
|
||||
let vpk = keys.generate_viewing_public_key();
|
||||
let nsk = keys.nullifier_secret_key;
|
||||
let nsk = keys.nullifier_secret_key();
|
||||
let vsk = keys.viewing_secret_key;
|
||||
Some((account_id, npk, vpk, vsk, nsk))
|
||||
})
|
||||
|
||||
@ -365,7 +365,7 @@ impl UserKeyChain {
|
||||
&found.key_chain.viewing_public_key,
|
||||
found.kind,
|
||||
);
|
||||
let nsk = found.key_chain.private_key_holder.nullifier_secret_key;
|
||||
let nsk = found.key_chain.private_key_holder.nullifier_secret_key();
|
||||
index.track(account_id, found.account, &nsk);
|
||||
}
|
||||
|
||||
@ -374,7 +374,7 @@ impl UserKeyChain {
|
||||
let Some(keys) = self.derive_shared_account_keys(entry) else {
|
||||
continue;
|
||||
};
|
||||
let nsk = keys.nullifier_secret_key;
|
||||
let nsk = keys.nullifier_secret_key();
|
||||
index.track(account_id, &entry.account, &nsk);
|
||||
}
|
||||
|
||||
@ -426,14 +426,14 @@ impl UserKeyChain {
|
||||
&keys.viewing_secret_key.d,
|
||||
&keys.viewing_secret_key.z,
|
||||
)?;
|
||||
(keys.nullifier_secret_key, secret, true)
|
||||
(keys.nullifier_secret_key(), secret, true)
|
||||
} else {
|
||||
let found = self.private_account(account_id)?;
|
||||
let secret = found
|
||||
.key_chain
|
||||
.calculate_shared_secret_receiver(&encrypted.epk)?;
|
||||
(
|
||||
found.key_chain.private_key_holder.nullifier_secret_key,
|
||||
found.key_chain.private_key_holder.nullifier_secret_key(),
|
||||
secret,
|
||||
false,
|
||||
)
|
||||
@ -459,14 +459,14 @@ impl UserKeyChain {
|
||||
return Some(NullifierIndex::next_update_nullifier(
|
||||
account_id,
|
||||
&entry.account,
|
||||
&keys.nullifier_secret_key,
|
||||
&keys.nullifier_secret_key(),
|
||||
));
|
||||
}
|
||||
let acc = self.private_account(account_id)?;
|
||||
Some(NullifierIndex::next_update_nullifier(
|
||||
account_id,
|
||||
acc.account,
|
||||
&acc.key_chain.private_key_holder.nullifier_secret_key,
|
||||
&acc.key_chain.private_key_holder.nullifier_secret_key(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -898,7 +898,7 @@ mod tests {
|
||||
let mut kc = UserKeyChain::default();
|
||||
|
||||
let key_chain = KeyChain::new_os_random();
|
||||
let nsk = key_chain.private_key_holder.nullifier_secret_key;
|
||||
let nsk = key_chain.private_key_holder.nullifier_secret_key();
|
||||
let identifier = 0;
|
||||
let account_id = AccountId::for_private_account(
|
||||
&key_chain.nullifier_public_key,
|
||||
@ -966,7 +966,7 @@ mod tests {
|
||||
let keys = holder.derive_regular_shared_account_keys_from_identifier(identifier);
|
||||
let npk = keys.generate_nullifier_public_key();
|
||||
let vpk = keys.generate_viewing_public_key();
|
||||
let nsk = keys.nullifier_secret_key;
|
||||
let nsk = keys.nullifier_secret_key();
|
||||
let account_id = AccountId::from((&npk, &vpk, identifier));
|
||||
|
||||
kc.insert_group_key_holder(label.clone(), holder);
|
||||
@ -1036,7 +1036,7 @@ mod tests {
|
||||
let keys = holder.derive_regular_shared_account_keys_from_identifier(identifier);
|
||||
let npk = keys.generate_nullifier_public_key();
|
||||
let vpk = keys.generate_viewing_public_key();
|
||||
let nsk = keys.nullifier_secret_key;
|
||||
let nsk = keys.nullifier_secret_key();
|
||||
let account_id = AccountId::from((&npk, &vpk, identifier));
|
||||
|
||||
kc.insert_group_key_holder(label.clone(), holder);
|
||||
|
||||
Binary file not shown.
@ -12,7 +12,7 @@ cargo bench -p crypto_primitives_bench --bench primitives
|
||||
|
||||
Criterion's per-operation report (point estimate, 95% CI, outlier counts) for:
|
||||
|
||||
- `keychain/new_os_random`: full mnemonic → SSK → NSK/VSK + public-key derivation (HMAC-SHA512 PBKDF dominates).
|
||||
- `keychain/new_os_random`: full mnemonic → SSK → ASK → NSK, plus SSK → VSK, and public-key derivation (HMAC-SHA512 PBKDF dominates).
|
||||
- `keychain/new_mnemonic`: same pipeline, mnemonic exposed.
|
||||
- `shared_secret_key/sender_dh`: secp256k1 ECDH per recipient (includes ephemeral key gen).
|
||||
- `encryption/encrypt` / `decrypt`: ChaCha20 over an Account note.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user