mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-02 07:09:29 +00:00
addressing comments
This commit is contained in:
parent
aa3935bdaa
commit
738dfc0cc4
@ -1,6 +1,6 @@
|
||||
# LEZ v0.3 specifications for Key Agreement
|
||||
# LEE v0.3 specifications for Key Agreement
|
||||
|
||||
## LEZ v0.3 basic types and constants
|
||||
## LEE v0.3 basic types and constants
|
||||
|
||||
```rust
|
||||
/// The ML-KEM-768 KEM ciphertext produced during encapsulation (1088 bytes) of a message.
|
||||
@ -24,9 +24,9 @@ struct EncryptedAccountData {
|
||||
When creating a private account output, the sender uses the recipient's viewing public key to encapsulate a random message that is used to establish a shared secret between the sender and recipient:
|
||||
|
||||
- Sender: $(\mathsf{ss},\, \mathsf{epk}) = \mathsf{encapsulate}(\mathsf{vpk\_recipient})$. The 1088-byte ciphertext `epk` is included in the transaction as the `EphemeralPublicKey` field.
|
||||
- Receiver: $\mathsf{ss} = \mathsf{decapsulate}(\mathsf{epk},\, vsk.d,\, vks.r)$
|
||||
- Receiver: $\mathsf{ss} = \mathsf{decapsulate}(\mathsf{epk},\, vsk.d,\, vsk.z)$
|
||||
|
||||
where `vpk` is the receiver's `ViewingPublicKey` and `(vsk.d, vsk.r)` are the two 32-byte halves of the receiver's `ViewingSecretKey`.
|
||||
where `vpk` is the receiver's `ViewingPublicKey` and `(vsk.d, vsk.z)` are the two 32-byte halves of the receiver's `ViewingSecretKey`.
|
||||
|
||||
#### KDF
|
||||
|
||||
@ -86,6 +86,11 @@ pub enum InputAccountIdentity {
|
||||
npk: NullifierPublicKey,
|
||||
ssk: SharedSecretKey,
|
||||
identifier: Identifier,
|
||||
/// When `Some((seed, authority_program_id))`, the circuit binds this position via
|
||||
/// `AccountId::for_private_pda(authority_program_id, seed, npk, identifier) == pre_state.account_id`
|
||||
/// rather than requiring a `Claim::Pda` or caller `pda_seeds`. The `pre_state` must
|
||||
/// have `is_authorized == false`.
|
||||
seed: Option<(PdaSeed, ProgramId)>,
|
||||
},
|
||||
|
||||
/// Update of an existing private PDA. npk is derived from nsk.
|
||||
@ -95,6 +100,11 @@ pub enum InputAccountIdentity {
|
||||
nsk: NullifierSecretKey,
|
||||
membership_proof: MembershipProof,
|
||||
identifier: Identifier,
|
||||
/// When `Some((seed, authority_program_id))`, the circuit binds this position via
|
||||
/// `AccountId::for_private_pda(authority_program_id, seed, npk, identifier) == pre_state.account_id`
|
||||
/// rather than requiring a caller `pda_seeds`. The `pre_state` must have
|
||||
/// `is_authorized == false`.
|
||||
seed: Option<(PdaSeed, ProgramId)>,
|
||||
},
|
||||
}
|
||||
```
|
||||
@ -102,9 +112,9 @@ pub enum InputAccountIdentity {
|
||||
The `ssk` field carries the **shared secret key** — the 32-byte shared secret used to encrypt the post-state. Note that the key protocol uses `ssk` for "spending secret key" (the master key that derives `nsk` and `vsk`); here `ssk` means the per-output KEM shared secret. It is established via ML-KEM-768:
|
||||
|
||||
- Sender: `(ssk, epk) = encapsulate(vpk)`
|
||||
- Receiver: `ssk = decapsulate(epk, vsk.d, vsk.r)`
|
||||
- Receiver: `ssk = decapsulate(epk, vsk.d, vsk.z)`
|
||||
|
||||
where `epk` is the ML-KEM-768 ciphertext (1088 bytes) stored as the `EphemeralPublicKey`, `vpk` is the recipient's `ViewingPublicKey` (1184 bytes), and `(vsk.d, vsk.r)` are the 32-byte seed halves of the recipient's `ViewingSecretKey`.
|
||||
where `epk` is the ML-KEM-768 ciphertext (1088 bytes) stored as the `EphemeralPublicKey`, `vpk` is the recipient's `ViewingPublicKey` (1184 bytes), and `(vsk.d, vsk.z)` are the 32-byte seed halves of the recipient's `ViewingSecretKey`.
|
||||
|
||||
## Encrypted private account discovery and tagging
|
||||
|
||||
@ -114,12 +124,12 @@ Each private account output includes a 1-byte view tag to allow wallets to quick
|
||||
|
||||
$$\mathsf{ViewTag} = \mathsf{SHA256}(\text{"/LEE/v0.3/ViewTag/"} \;||\; \mathsf{Npk} \;||\; \mathsf{Vpk})[0]$$
|
||||
|
||||
where `Npk` is the 32-byte nullifier public key and `Vpk` is the 1184 byte `ViewingPublicKey` of the recipient. On average only 1 in 256 outputs will pass this filter for a given account, avoiding expensive ECDH on irrelevant outputs.
|
||||
where `Npk` is the 32-byte nullifier public key and `Vpk` is the 1184 byte `ViewingPublicKey` of the recipient. On average only 1 in 256 outputs will pass this filter for a given account, avoiding expensive ML-KEM decapsulation on irrelevant outputs.
|
||||
|
||||
### Private account discovery with viewing keys
|
||||
|
||||
1. For each encrypted output, compute the expected view tag from `(Npk, Vpk)`. Skip if it does not match.
|
||||
2. Decapsulate using ML-KEM-768: `ss = decapsulate(epk, vsk.d, vsk.r)`.
|
||||
2. Decapsulate using ML-KEM-768: `ss = decapsulate(epk, vsk.d, vsk.z)`.
|
||||
3. Run `kdf(ss, commitment, output_index)` to derive the symmetric key.
|
||||
4. Decrypt the ciphertext with ChaCha20.
|
||||
5. Parse the 81-byte header to recover `PrivateAccountKind`.
|
||||
@ -144,7 +154,7 @@ fn private_account_discovery(
|
||||
if encrypted_account.view_tag != expected_tag {
|
||||
continue;
|
||||
}
|
||||
let ss = SharedSecretKey::decapsulate(&encrypted_account.epk, &vsk.d, &vsk.r);
|
||||
let ss = SharedSecretKey::decapsulate(&encrypted_account.epk, &vsk.d, &vsk.z);
|
||||
if let Some((kind, account)) = EncryptionScheme::decrypt(
|
||||
&encrypted_account.ciphertext, &ss, commitment, output_index as u32
|
||||
) {
|
||||
|
||||
@ -108,7 +108,7 @@ async fn group_invite_join_key_agreement() -> Result<()> {
|
||||
.sealing_secret_key()
|
||||
.context("Sealing key not found")?;
|
||||
let sealing_pk = key_protocol::key_management::group_key_holder::SealingPublicKey::from_bytes(
|
||||
nssa_core::encryption::ViewingPublicKey::from_seed(&sealing_sk.d, &sealing_sk.r).0,
|
||||
nssa_core::encryption::ViewingPublicKey::from_seed(&sealing_sk.d, &sealing_sk.z).0,
|
||||
);
|
||||
|
||||
let holder = ctx
|
||||
|
||||
@ -32,7 +32,7 @@ impl SealingPublicKey {
|
||||
}
|
||||
|
||||
/// Secret key used to unseal a `GroupKeyHolder` received from another member.
|
||||
/// Holds the two 32-byte FIPS 203 seed halves `d` and `r`.
|
||||
/// Holds the two 32-byte FIPS 203 seed halves `d` and `z`.
|
||||
pub type SealingSecretKey = ViewingSecretKey;
|
||||
|
||||
/// Manages shared viewing keys for a group of controllers owning private PDAs.
|
||||
@ -198,7 +198,8 @@ impl GroupKeyHolder {
|
||||
let nonce = aes_gcm::Nonce::from_slice(&sealed[KEM_CT_LEN..HEADER_LEN]);
|
||||
let ciphertext = &sealed[HEADER_LEN..];
|
||||
|
||||
let shared = SharedSecretKey::decapsulate(&kem_ct, &own_key.d, &own_key.r);
|
||||
let shared = SharedSecretKey::decapsulate(&kem_ct, &own_key.d, &own_key.z)
|
||||
.expect("key_protocol::group_key_holder::GroupKeyHolder::unseal: KEM_CT_LEN guarantees exactly 1088 bytes");
|
||||
let aes_key = Self::seal_kdf(&shared);
|
||||
let cipher = Aes256Gcm::new(&aes_key.into());
|
||||
|
||||
@ -478,7 +479,7 @@ mod tests {
|
||||
fn unseal_too_short_fails() {
|
||||
let vsk = SealingSecretKey {
|
||||
d: [7_u8; 32],
|
||||
r: [0_u8; 32],
|
||||
z: [0_u8; 32],
|
||||
};
|
||||
let result = GroupKeyHolder::unseal(&[0_u8; 10], &vsk);
|
||||
assert!(matches!(result, Err(super::SealError::TooShort)));
|
||||
|
||||
@ -65,7 +65,7 @@ impl ChildKeysPrivate {
|
||||
parent_hash.update([9_u8]);
|
||||
parent_hash.update(self.value.0.private_key_holder.nullifier_secret_key);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.d);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.r);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.z);
|
||||
let parent_pt = parent_hash.finalize();
|
||||
|
||||
let mut input = vec![];
|
||||
@ -171,7 +171,7 @@ mod tests {
|
||||
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,
|
||||
],
|
||||
r: [
|
||||
z: [
|
||||
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,
|
||||
],
|
||||
@ -285,7 +285,7 @@ mod tests {
|
||||
81, 154, 68, 152, 72, 163, 82, 17, 125, 156, 193, 135, 129, 93, 227, 55, 224, 104,
|
||||
119, 232, 13, 101, 241, 20, 175, 72, 192, 186, 176, 246, 140, 211,
|
||||
],
|
||||
r: [
|
||||
z: [
|
||||
31, 40, 109, 41, 185, 61, 173, 79, 102, 171, 158, 245, 232, 71, 57, 157, 142, 117,
|
||||
184, 235, 216, 71, 55, 44, 33, 156, 167, 133, 184, 92, 47, 174,
|
||||
],
|
||||
|
||||
@ -69,10 +69,9 @@ impl KeyChain {
|
||||
pub fn calculate_shared_secret_receiver(
|
||||
&self,
|
||||
ephemeral_public_key_sender: &EphemeralPublicKey,
|
||||
_index: Option<u32>,
|
||||
) -> SharedSecretKey {
|
||||
) -> Option<SharedSecretKey> {
|
||||
let vsk = &self.private_key_holder.viewing_secret_key;
|
||||
SharedSecretKey::decapsulate(ephemeral_public_key_sender, &vsk.d, &vsk.r)
|
||||
SharedSecretKey::decapsulate(ephemeral_public_key_sender, &vsk.d, &vsk.z)
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +103,7 @@ mod tests {
|
||||
// Create a proper KEM ciphertext by encapsulating toward this key chain's VPK.
|
||||
let (_, epk) = SharedSecretKey::encapsulate(&account_id_key_holder.viewing_public_key);
|
||||
|
||||
let _shared_secret = account_id_key_holder.calculate_shared_secret_receiver(&epk, None);
|
||||
let _shared_secret = account_id_key_holder.calculate_shared_secret_receiver(&epk);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -177,8 +176,8 @@ mod tests {
|
||||
|
||||
let key_sender = eph_key_holder.calculate_shared_secret_sender();
|
||||
let key_receiver =
|
||||
keys.calculate_shared_secret_receiver(eph_key_holder.ephemeral_public_key(), Some(2));
|
||||
keys.calculate_shared_secret_receiver(eph_key_holder.ephemeral_public_key());
|
||||
|
||||
assert_eq!(key_sender.0, key_receiver.0);
|
||||
assert_eq!(key_sender.0, key_receiver.unwrap().0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,12 +17,12 @@ pub struct SeedHolder {
|
||||
/// Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SecretSpendingKey(pub [u8; 32]);
|
||||
/// Viewing secret key: the KEM seed split into its two 32-byte halves `d` and `r` (= z in
|
||||
/// FIPS 203), from which the ML-KEM 768 decapsulation key is derived deterministically.
|
||||
/// Viewing secret key: the FIPS 203 KEM seed split into its two 32-byte halves `d` and `z`,
|
||||
/// from which the ML-KEM-768 decapsulation key is derived deterministically.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ViewingSecretKey {
|
||||
pub d: [u8; 32],
|
||||
pub r: [u8; 32],
|
||||
pub z: [u8; 32],
|
||||
}
|
||||
|
||||
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
|
||||
@ -143,7 +143,7 @@ impl SecretSpendingKey {
|
||||
d: *full_seed
|
||||
.first_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
||||
r: *full_seed
|
||||
z: *full_seed
|
||||
.last_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32"),
|
||||
}
|
||||
@ -153,7 +153,7 @@ impl SecretSpendingKey {
|
||||
pub const fn generate_viewing_secret_key(seed: [u8; 64]) -> ViewingSecretKey {
|
||||
ViewingSecretKey {
|
||||
d: *seed.first_chunk::<32>().expect("seed is 64 bytes"),
|
||||
r: *seed.last_chunk::<32>().expect("seed is 64 bytes"),
|
||||
z: *seed.last_chunk::<32>().expect("seed is 64 bytes"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ impl From<&ViewingSecretKey> for ViewingPublicKey {
|
||||
use ml_kem::{Kem, KeyExport as _, MlKem768, Seed};
|
||||
let mut seed_bytes = [0_u8; 64];
|
||||
seed_bytes[..32].copy_from_slice(&sk.d);
|
||||
seed_bytes[32..].copy_from_slice(&sk.r);
|
||||
seed_bytes[32..].copy_from_slice(&sk.z);
|
||||
let dk = <MlKem768 as Kem>::DecapsulationKey::from_seed(Seed::from(seed_bytes));
|
||||
Self(dk.encapsulation_key().to_bytes().to_vec())
|
||||
}
|
||||
|
||||
@ -161,11 +161,11 @@ mod tests {
|
||||
#[test]
|
||||
fn kem_to_chacha20_round_trip() {
|
||||
let d = [1_u8; 32];
|
||||
let r = [2_u8; 32];
|
||||
let vpk = shared_key_derivation::ViewingPublicKey::from_seed(&d, &r);
|
||||
let z = [2_u8; 32];
|
||||
let vpk = shared_key_derivation::ViewingPublicKey::from_seed(&d, &z);
|
||||
|
||||
let (sender_ss, epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
let receiver_ss = SharedSecretKey::decapsulate(&epk, &d, &r);
|
||||
let receiver_ss = SharedSecretKey::decapsulate(&epk, &d, &z).unwrap();
|
||||
|
||||
let account = Account {
|
||||
program_owner: [12_u32; 8],
|
||||
|
||||
@ -30,14 +30,14 @@ impl ViewingPublicKey {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Derive the ML-KEM-768 encapsulation key from the FIPS 203 seed halves `d` and `r`.
|
||||
/// Derive the ML-KEM-768 encapsulation key from the FIPS 203 seed halves `d` and `z`.
|
||||
/// Allows any crate to construct a VPK from raw seed bytes without importing
|
||||
/// `key_protocol::ViewingSecretKey`.
|
||||
#[must_use]
|
||||
pub fn from_seed(d: &[u8; 32], r: &[u8; 32]) -> Self {
|
||||
pub fn from_seed(d: &[u8; 32], z: &[u8; 32]) -> Self {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(d);
|
||||
seed[32..].copy_from_slice(r);
|
||||
seed[32..].copy_from_slice(z);
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
Self(dk.encapsulation_key().to_bytes().to_vec())
|
||||
}
|
||||
@ -104,21 +104,22 @@ impl SharedSecretKey {
|
||||
|
||||
/// Receiver: decapsulate the shared secret from a KEM ciphertext.
|
||||
///
|
||||
/// `d` and `r` are the two 32-byte halves of the FIPS 203 `ViewingSecretKey` seed.
|
||||
/// Returns `None` if the `EphemeralPublicKey` is not exactly 1088 bytes — callers on
|
||||
/// the wallet scan path should skip the output rather than panic on malformed chain data.
|
||||
///
|
||||
/// `d` and `z` are the two 32-byte halves of the FIPS 203 `ViewingSecretKey` seed.
|
||||
#[must_use]
|
||||
pub fn decapsulate(ciphertext: &EphemeralPublicKey, d: &[u8; 32], r: &[u8; 32]) -> Self {
|
||||
pub fn decapsulate(ciphertext: &EphemeralPublicKey, d: &[u8; 32], z: &[u8; 32]) -> Option<Self> {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(d);
|
||||
seed[32..].copy_from_slice(r);
|
||||
seed[32..].copy_from_slice(z);
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
let ss = dk
|
||||
.decapsulate_slice(&ciphertext.0)
|
||||
.expect("EphemeralPublicKey must be 1088 bytes (ML-KEM-768 ciphertext)");
|
||||
let ss = dk.decapsulate_slice(&ciphertext.0).ok()?;
|
||||
let ss_bytes: [u8; 32] = ss
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("ML-KEM shared key is 32 bytes");
|
||||
Self(ss_bytes)
|
||||
Some(Self(ss_bytes))
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,18 +132,18 @@ mod tests {
|
||||
#[test]
|
||||
fn encapsulate_decapsulate_round_trip() {
|
||||
let d = [1_u8; 32];
|
||||
let r = [2_u8; 32];
|
||||
let z = [2_u8; 32];
|
||||
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(&d);
|
||||
seed[32..].copy_from_slice(&r);
|
||||
seed[32..].copy_from_slice(&z);
|
||||
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
let ek_bytes = dk.encapsulation_key().to_bytes();
|
||||
let vpk = ViewingPublicKey(ek_bytes.to_vec());
|
||||
|
||||
let (sender_ss, epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
let receiver_ss = SharedSecretKey::decapsulate(&epk, &d, &r);
|
||||
let receiver_ss = SharedSecretKey::decapsulate(&epk, &d, &z).unwrap();
|
||||
|
||||
assert_eq!(sender_ss.0, receiver_ss.0, "shared secrets must match");
|
||||
assert_eq!(epk.0.len(), 1088, "ML-KEM-768 ciphertext is 1088 bytes");
|
||||
@ -153,22 +154,49 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decapsulate_returns_none_for_malformed_epk() {
|
||||
let d = [1_u8; 32];
|
||||
let z = [2_u8; 32];
|
||||
|
||||
// Too short — 100 bytes instead of 1088.
|
||||
let short_epk = EphemeralPublicKey(vec![42_u8; 100]);
|
||||
assert!(
|
||||
SharedSecretKey::decapsulate(&short_epk, &d, &z).is_none(),
|
||||
"short EphemeralPublicKey must return None"
|
||||
);
|
||||
|
||||
// Too long — 1089 bytes instead of 1088.
|
||||
let long_epk = EphemeralPublicKey(vec![42_u8; 1089]);
|
||||
assert!(
|
||||
SharedSecretKey::decapsulate(&long_epk, &d, &z).is_none(),
|
||||
"long EphemeralPublicKey must return None"
|
||||
);
|
||||
|
||||
// Empty.
|
||||
let empty_epk = EphemeralPublicKey(vec![]);
|
||||
assert!(
|
||||
SharedSecretKey::decapsulate(&empty_epk, &d, &z).is_none(),
|
||||
"empty EphemeralPublicKey must return None"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_vpks_produce_different_shared_secrets() {
|
||||
let (d1, r1) = ([1_u8; 32], [2_u8; 32]);
|
||||
let (d2, r2) = ([3_u8; 32], [4_u8; 32]);
|
||||
let (d1, z1) = ([1_u8; 32], [2_u8; 32]);
|
||||
let (d2, z2) = ([3_u8; 32], [4_u8; 32]);
|
||||
|
||||
let vpk1 = {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(&d1);
|
||||
seed[32..].copy_from_slice(&r1);
|
||||
seed[32..].copy_from_slice(&z1);
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
ViewingPublicKey(dk.encapsulation_key().to_bytes().to_vec())
|
||||
};
|
||||
let vpk2 = {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(&d2);
|
||||
seed[32..].copy_from_slice(&r2);
|
||||
seed[32..].copy_from_slice(&z2);
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
ViewingPublicKey(dk.encapsulation_key().to_bytes().to_vec())
|
||||
};
|
||||
|
||||
@ -519,7 +519,7 @@ pub mod tests {
|
||||
pub struct TestPrivateKeys {
|
||||
pub nsk: NullifierSecretKey,
|
||||
pub d: [u8; 32],
|
||||
pub r: [u8; 32],
|
||||
pub z: [u8; 32],
|
||||
}
|
||||
|
||||
impl TestPrivateKeys {
|
||||
@ -528,7 +528,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
pub fn vpk(&self) -> ViewingPublicKey {
|
||||
ViewingPublicKey::from_seed(&self.d, &self.r)
|
||||
ViewingPublicKey::from_seed(&self.d, &self.z)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1315,7 +1315,7 @@ pub mod tests {
|
||||
TestPrivateKeys {
|
||||
nsk: [13; 32],
|
||||
d: [31; 32],
|
||||
r: [32; 32],
|
||||
z: [32; 32],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1323,7 +1323,7 @@ pub mod tests {
|
||||
TestPrivateKeys {
|
||||
nsk: [38; 32],
|
||||
d: [83; 32],
|
||||
r: [84; 32],
|
||||
z: [84; 32],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ const VSK_D_PRIV_ACC_A: [u8; 32] = [
|
||||
187, 41, 163, 19, 231, 232, 122, 225, 55, 134, 184,
|
||||
];
|
||||
|
||||
const VSK_R_PRIV_ACC_A: [u8; 32] = [
|
||||
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,
|
||||
];
|
||||
@ -53,7 +53,7 @@ const VSK_D_PRIV_ACC_B: [u8; 32] = [
|
||||
12, 178, 229, 236, 255, 120, 146, 211, 169, 117, 153, 180,
|
||||
];
|
||||
|
||||
const VSK_R_PRIV_ACC_B: [u8; 32] = [
|
||||
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,
|
||||
];
|
||||
@ -138,11 +138,11 @@ pub fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialD
|
||||
nullifier_secret_key: NSK_PRIV_ACC_A,
|
||||
viewing_secret_key: ViewingSecretKey {
|
||||
d: VSK_D_PRIV_ACC_A,
|
||||
r: VSK_R_PRIV_ACC_A,
|
||||
z: 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_R_PRIV_ACC_A),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_A, &VSK_Z_PRIV_ACC_A),
|
||||
};
|
||||
|
||||
let key_chain_2 = KeyChain {
|
||||
@ -151,11 +151,11 @@ pub fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialD
|
||||
nullifier_secret_key: NSK_PRIV_ACC_B,
|
||||
viewing_secret_key: ViewingSecretKey {
|
||||
d: VSK_D_PRIV_ACC_B,
|
||||
r: VSK_R_PRIV_ACC_B,
|
||||
z: 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_R_PRIV_ACC_B),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_B, &VSK_Z_PRIV_ACC_B),
|
||||
};
|
||||
|
||||
vec![
|
||||
|
||||
@ -153,11 +153,11 @@ impl WalletSubcommand for GroupSubcommand {
|
||||
}
|
||||
|
||||
let mut d = [0_u8; 32];
|
||||
let mut r = [0_u8; 32];
|
||||
let mut z = [0_u8; 32];
|
||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut d);
|
||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut r);
|
||||
let secret = ViewingSecretKey { d, r };
|
||||
let ek_bytes = nssa_core::encryption::ViewingPublicKey::from_seed(&d, &r).0;
|
||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut z);
|
||||
let secret = ViewingSecretKey { d, z };
|
||||
let ek_bytes = nssa_core::encryption::ViewingPublicKey::from_seed(&d, &z).0;
|
||||
let public_key = SealingPublicKey::from_bytes(ek_bytes);
|
||||
|
||||
wallet_core.set_sealing_secret_key(secret);
|
||||
|
||||
@ -669,7 +669,7 @@ impl WalletCore {
|
||||
.storage
|
||||
.key_chain()
|
||||
.private_account_key_chains()
|
||||
.flat_map(|(_account_id, key_chain, index)| {
|
||||
.flat_map(|(_account_id, key_chain, _index)| {
|
||||
let view_tag = EncryptedAccountData::compute_view_tag(
|
||||
&key_chain.nullifier_public_key,
|
||||
&key_chain.viewing_public_key,
|
||||
@ -684,10 +684,8 @@ impl WalletCore {
|
||||
.filter_map(move |(ciph_id, encrypted_data)| {
|
||||
let ciphertext = &encrypted_data.ciphertext;
|
||||
let commitment = &new_commitments[ciph_id];
|
||||
let shared_secret = key_chain.calculate_shared_secret_receiver(
|
||||
&encrypted_data.epk,
|
||||
index.and_then(ChainIndex::index),
|
||||
);
|
||||
let shared_secret = key_chain
|
||||
.calculate_shared_secret_receiver(&encrypted_data.epk)?;
|
||||
|
||||
nssa_core::EncryptionScheme::decrypt(
|
||||
ciphertext,
|
||||
@ -769,8 +767,11 @@ impl WalletCore {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shared_secret =
|
||||
SharedSecretKey::decapsulate(&encrypted_data.epk, &vsk.d, &vsk.r);
|
||||
let Some(shared_secret) =
|
||||
SharedSecretKey::decapsulate(&encrypted_data.epk, &vsk.d, &vsk.z)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let commitment = &tx.message.new_commitments[ciph_id];
|
||||
|
||||
if let Some((_kind, new_acc)) = nssa_core::EncryptionScheme::decrypt(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user