mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-25 17:39:27 +00:00
Merge fa569dab419210a6b7ad627c26bc6653525e2b54 into 694e48422847771b9d3d1948dc796830986003f2
This commit is contained in:
commit
e33b17296a
@ -16,7 +16,6 @@ ignore = [
|
||||
{ id = "RUSTSEC-2026-0097", reason = "`rand` v0.8.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0118", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0119", reason = "`hickory-proto` v0.25.0-alpha.5 is present transitively from logos crates, modification may break integration" },
|
||||
{ id = "RUSTSEC-2026-0145", reason = "`astral-tokio-tar` v0.6.1 is pulled transitively via testcontainers (integration_tests dev/test path); waiting on upstream fix" },
|
||||
]
|
||||
yanked = "deny"
|
||||
unused-ignored-advisory = "deny"
|
||||
|
||||
201
Cargo.lock
generated
201
Cargo.lock
generated
@ -258,7 +258,7 @@ dependencies = [
|
||||
"ark-std 0.4.0",
|
||||
"blake2",
|
||||
"derivative",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
@ -278,7 +278,7 @@ dependencies = [
|
||||
"ark-std 0.5.0",
|
||||
"blake2",
|
||||
"derivative",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"fnv",
|
||||
"merlin",
|
||||
"sha2",
|
||||
@ -344,7 +344,7 @@ dependencies = [
|
||||
"ark-serialize 0.4.2",
|
||||
"ark-std 0.4.0",
|
||||
"derivative",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"itertools 0.10.5",
|
||||
"num-bigint 0.4.6",
|
||||
"num-traits",
|
||||
@ -364,7 +364,7 @@ dependencies = [
|
||||
"ark-serialize 0.5.0",
|
||||
"ark-std 0.5.0",
|
||||
"arrayvec",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"educe",
|
||||
"itertools 0.13.0",
|
||||
"num-bigint 0.4.6",
|
||||
@ -526,7 +526,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5"
|
||||
dependencies = [
|
||||
"ark-serialize-derive 0.4.2",
|
||||
"ark-std 0.4.0",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"num-bigint 0.4.6",
|
||||
]
|
||||
|
||||
@ -539,7 +539,7 @@ dependencies = [
|
||||
"ark-serialize-derive 0.5.0",
|
||||
"ark-std 0.5.0",
|
||||
"arrayvec",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"num-bigint 0.4.6",
|
||||
]
|
||||
|
||||
@ -1180,7 +1180,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1569,6 +1569,12 @@ dependencies = [
|
||||
"nssa_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
@ -1704,6 +1710,12 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||
|
||||
[[package]]
|
||||
name = "const-str"
|
||||
version = "0.4.3"
|
||||
@ -1935,7 +1947,9 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
|
||||
dependencies = [
|
||||
"getrandom 0.4.2",
|
||||
"hybrid-array",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1945,7 +1959,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"key_protocol",
|
||||
"nssa_core",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@ -1959,6 +1972,15 @@ dependencies = [
|
||||
"cipher 0.4.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
@ -1968,7 +1990,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
@ -2107,11 +2129,21 @@ version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"const-oid 0.9.6",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
|
||||
dependencies = [
|
||||
"const-oid 0.10.2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "10.0.0"
|
||||
@ -2230,11 +2262,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid",
|
||||
"const-oid 0.9.6",
|
||||
"crypto-common 0.1.7",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"crypto-common 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "6.0.0"
|
||||
@ -2328,7 +2370,7 @@ version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ac1e888d6830712d565b2f3a974be3200be9296bc1b03db8251a4cbf18a4a34"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"futures",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
@ -2360,13 +2402,13 @@ version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"der 0.7.10",
|
||||
"digest 0.10.7",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"serdect",
|
||||
"signature",
|
||||
"spki",
|
||||
"spki 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2375,7 +2417,7 @@ version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||
dependencies = [
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"serde",
|
||||
"signature",
|
||||
]
|
||||
@ -2437,12 +2479,12 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"generic-array 0.14.7",
|
||||
"group",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"serdect",
|
||||
@ -3334,7 +3376,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3456,6 +3498,7 @@ version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8655f91cd07f2b9d0c24137bd650fe69617773435ee5ec83022377777ce65ef1"
|
||||
dependencies = [
|
||||
"ctutils",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
@ -4370,6 +4413,26 @@ dependencies = [
|
||||
"cpufeatures 0.2.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kem"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd"
|
||||
dependencies = [
|
||||
"crypto-common 0.2.1",
|
||||
"rand_core 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "key_protocol"
|
||||
version = "0.1.0"
|
||||
@ -4384,6 +4447,7 @@ dependencies = [
|
||||
"hmac-sha512",
|
||||
"itertools 0.14.0",
|
||||
"k256",
|
||||
"ml-kem",
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
"rand 0.8.5",
|
||||
@ -5977,7 +6041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"keccak",
|
||||
"keccak 0.1.6",
|
||||
"rand_core 0.6.4",
|
||||
"zeroize",
|
||||
]
|
||||
@ -6040,6 +6104,31 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ml-kem"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e15f3e5b957493873e396a66914e83e616b6afe335cdef7efe5c6e1216aba66"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"kem",
|
||||
"module-lattice",
|
||||
"pkcs8 0.11.0",
|
||||
"rand_core 0.10.1",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "module-lattice"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febe"
|
||||
dependencies = [
|
||||
"ctutils",
|
||||
"hybrid-array",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moka"
|
||||
version = "0.12.15"
|
||||
@ -6275,10 +6364,10 @@ dependencies = [
|
||||
"ark-ec 0.4.2",
|
||||
"ark-ff 0.4.2",
|
||||
"ark-serialize 0.4.2",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"generic-array 0.14.7",
|
||||
"hex",
|
||||
"keccak",
|
||||
"keccak 0.1.6",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"zeroize",
|
||||
@ -6367,7 +6456,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"bytesize",
|
||||
"chacha20",
|
||||
"k256",
|
||||
"ml-kem",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -6866,9 +6955,9 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
"der 0.7.10",
|
||||
"pkcs8 0.10.2",
|
||||
"spki 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6877,8 +6966,18 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
"der 0.7.10",
|
||||
"spki 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7"
|
||||
dependencies = [
|
||||
"der 0.8.0",
|
||||
"spki 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7729,7 +7828,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"keccak",
|
||||
"keccak 0.1.6",
|
||||
"liblzma",
|
||||
"paste",
|
||||
"rayon",
|
||||
@ -7912,7 +8011,7 @@ dependencies = [
|
||||
"borsh",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"hex",
|
||||
"hex-literal 0.4.1",
|
||||
@ -7951,7 +8050,7 @@ dependencies = [
|
||||
"gdbstub_arch",
|
||||
"gimli",
|
||||
"hex",
|
||||
"keccak",
|
||||
"keccak 0.1.6",
|
||||
"lazy-regex",
|
||||
"num-bigint 0.4.6",
|
||||
"num-traits",
|
||||
@ -8058,16 +8157,16 @@ version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"const-oid 0.9.6",
|
||||
"digest 0.10.7",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"rand_core 0.6.4",
|
||||
"signature",
|
||||
"spki",
|
||||
"spki 0.7.3",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@ -8373,9 +8472,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"der 0.7.10",
|
||||
"generic-array 0.14.7",
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"serdect",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
@ -8761,7 +8860,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8772,7 +8871,17 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"
|
||||
dependencies = [
|
||||
"digest 0.11.3",
|
||||
"keccak 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8806,7 +8915,7 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
@ -8905,7 +9014,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
"der 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -151,6 +151,7 @@ k256 = { version = "0.13.3", features = [
|
||||
"serde",
|
||||
"pem",
|
||||
] }
|
||||
ml-kem = { version = "0.3", features = ["hazmat"] }
|
||||
elliptic-curve = { version = "0.13.8", features = ["arithmetic"] }
|
||||
actix-web = { version = "4.13.0", default-features = false, features = [
|
||||
"macros",
|
||||
|
||||
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.
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.
@ -8,7 +8,7 @@ use integration_tests::{
|
||||
};
|
||||
use log::info;
|
||||
use nssa::{AccountId, program::Program};
|
||||
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
||||
use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::{
|
||||
@ -65,7 +65,7 @@ async fn private_transfer_to_foreign_account() -> Result<()> {
|
||||
let from: AccountId = ctx.existing_private_accounts()[0];
|
||||
let to_npk = NullifierPublicKey([42; 32]);
|
||||
let to_npk_string = hex::encode(to_npk.0);
|
||||
let to_vpk = Secp256k1Point::from_scalar(to_npk.0);
|
||||
let to_vpk = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
|
||||
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
from: private_mention(from),
|
||||
@ -268,7 +268,7 @@ async fn shielded_transfer_to_foreign_account() -> Result<()> {
|
||||
|
||||
let to_npk = NullifierPublicKey([42; 32]);
|
||||
let to_npk_string = hex::encode(to_npk.0);
|
||||
let to_vpk = Secp256k1Point::from_scalar(to_npk.0);
|
||||
let to_vpk = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
|
||||
let from: AccountId = ctx.existing_public_accounts()[0];
|
||||
|
||||
let command = Command::AuthTransfer(AuthTransferSubcommand::Send {
|
||||
@ -654,9 +654,9 @@ async fn ppt_that_chain_calls_faucet_is_dropped() -> Result<()> {
|
||||
let auth_transfer_program_id = Program::authenticated_transfer_program().id();
|
||||
let nsk: nssa_core::NullifierSecretKey = [3; 32];
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = Secp256k1Point::from_scalar([4; 32]);
|
||||
let ssk = SharedSecretKey::new([55; 32], &vpk);
|
||||
let epk = EphemeralPublicKey::from_scalar([55; 32]);
|
||||
let vpk = ViewingPublicKey(vec![4_u8; 1184]);
|
||||
let ssk = SharedSecretKey([55_u8; 32]);
|
||||
let epk = EphemeralPublicKey(vec![55_u8; 1088]);
|
||||
let attacker_vault_id = {
|
||||
let seed = vault_core::compute_vault_seed(attacker_id);
|
||||
AccountId::for_private_pda(&vault_program_id, &seed, &npk, 1337)
|
||||
|
||||
@ -227,10 +227,10 @@ async fn private_pda_family_members_receive_and_spend() -> Result<()> {
|
||||
|
||||
// Fresh recipients — hardcoded npks not in any wallet.
|
||||
let recipient_npk_0 = NullifierPublicKey([0xAA; 32]);
|
||||
let recipient_vpk_0 = ViewingPublicKey::from_scalar(recipient_npk_0.0);
|
||||
let recipient_vpk_0 = ViewingPublicKey::from_seed(&[0_u8; 32], &[1_u8; 32]);
|
||||
|
||||
let recipient_npk_1 = NullifierPublicKey([0xBB; 32]);
|
||||
let recipient_vpk_1 = ViewingPublicKey::from_scalar(recipient_npk_1.0);
|
||||
let recipient_vpk_1 = ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]);
|
||||
|
||||
let amount_spend_0: u128 = 13;
|
||||
let amount_spend_1: u128 = 37;
|
||||
|
||||
@ -107,8 +107,9 @@ async fn group_invite_join_key_agreement() -> Result<()> {
|
||||
.key_chain()
|
||||
.sealing_secret_key()
|
||||
.context("Sealing key not found")?;
|
||||
let sealing_pk =
|
||||
key_protocol::key_management::group_key_holder::SealingPublicKey::from_scalar(sealing_sk);
|
||||
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,
|
||||
);
|
||||
|
||||
let holder = ctx
|
||||
.wallet()
|
||||
|
||||
@ -193,8 +193,7 @@ pub async fn tps_test() -> Result<()> {
|
||||
fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let sender_nsk = [1; 32];
|
||||
let sender_vsk = [99; 32];
|
||||
let sender_vpk = ViewingPublicKey::from_scalar(sender_vsk);
|
||||
let sender_vpk = ViewingPublicKey::from_seed(&[99_u8; 32], &[100_u8; 32]);
|
||||
let sender_npk = NullifierPublicKey::from(&sender_nsk);
|
||||
let sender_pre = AccountWithMetadata::new(
|
||||
Account {
|
||||
@ -207,8 +206,7 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
AccountId::for_regular_private_account(&sender_npk, 0),
|
||||
);
|
||||
let recipient_nsk = [2; 32];
|
||||
let recipient_vsk = [99; 32];
|
||||
let recipient_vpk = ViewingPublicKey::from_scalar(recipient_vsk);
|
||||
let recipient_vpk = ViewingPublicKey::from_seed(&[101_u8; 32], &[102_u8; 32]);
|
||||
let recipient_npk = NullifierPublicKey::from(&recipient_nsk);
|
||||
let recipient_pre = AccountWithMetadata::new(
|
||||
Account::default(),
|
||||
@ -216,13 +214,13 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
|
||||
AccountId::for_regular_private_account(&recipient_npk, 0),
|
||||
);
|
||||
|
||||
let eph_holder_from = EphemeralKeyHolder::new(&sender_npk);
|
||||
let sender_ss = eph_holder_from.calculate_shared_secret_sender(&sender_vpk);
|
||||
let sender_epk = eph_holder_from.generate_ephemeral_public_key();
|
||||
let eph_holder_from = EphemeralKeyHolder::new(&sender_vpk);
|
||||
let sender_ss = eph_holder_from.calculate_shared_secret_sender();
|
||||
let sender_epk = eph_holder_from.ephemeral_public_key().clone();
|
||||
|
||||
let eph_holder_to = EphemeralKeyHolder::new(&recipient_npk);
|
||||
let recipient_ss = eph_holder_to.calculate_shared_secret_sender(&recipient_vpk);
|
||||
let recipient_epk = eph_holder_from.generate_ephemeral_public_key();
|
||||
let eph_holder_to = EphemeralKeyHolder::new(&recipient_vpk);
|
||||
let recipient_ss = eph_holder_to.calculate_shared_secret_sender();
|
||||
let recipient_epk = eph_holder_to.ephemeral_public_key().clone();
|
||||
|
||||
let balance_to_move: u128 = 1;
|
||||
let proof: MembershipProof = (
|
||||
|
||||
@ -19,6 +19,7 @@ common.workspace = true
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
k256.workspace = true
|
||||
ml-kem.workspace = true
|
||||
sha2.workspace = true
|
||||
rand.workspace = true
|
||||
hex.workspace = true
|
||||
|
||||
@ -1,53 +1,61 @@
|
||||
use nssa_core::{
|
||||
NullifierPublicKey, SharedSecretKey,
|
||||
encryption::{EphemeralPublicKey, EphemeralSecretKey, ViewingPublicKey},
|
||||
SharedSecretKey,
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use sha2::Digest as _;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral
|
||||
/// public keys. Can produce shared secret for sender.
|
||||
/// Ephemeral key holder for the sender side of a KEM-based shared-secret exchange.
|
||||
///
|
||||
/// Non-clonable as intended for one-time use: construction encapsulates once and
|
||||
/// stores both the shared secret and the ciphertext (`EphemeralPublicKey`) that must
|
||||
/// be sent to the receiver.
|
||||
pub struct EphemeralKeyHolder {
|
||||
ephemeral_secret_key: EphemeralSecretKey,
|
||||
shared_secret: SharedSecretKey,
|
||||
ephemeral_public_key: EphemeralPublicKey,
|
||||
}
|
||||
|
||||
// SharedSecretKey does not implement Debug (intentional — leaking key material via
|
||||
// debug output would be a security risk). We implement Debug manually here, redacting the
|
||||
// shared secret while still allowing the ephemeral public key (KEM ciphertext) to be inspected.
|
||||
impl std::fmt::Debug for EphemeralKeyHolder {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EphemeralKeyHolder")
|
||||
.field("shared_secret", &"<redacted>")
|
||||
.field("ephemeral_public_key", &self.ephemeral_public_key)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EphemeralKeyHolder {
|
||||
#[must_use]
|
||||
pub fn new(receiver_nullifier_public_key: &NullifierPublicKey) -> Self {
|
||||
let mut nonce_bytes = [0; 16];
|
||||
OsRng.fill_bytes(&mut nonce_bytes);
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(receiver_nullifier_public_key);
|
||||
hasher.update(nonce_bytes);
|
||||
|
||||
pub fn new(receiver_viewing_public_key: &ViewingPublicKey) -> Self {
|
||||
let (shared_secret, ephemeral_public_key) =
|
||||
SharedSecretKey::encapsulate(receiver_viewing_public_key);
|
||||
Self {
|
||||
ephemeral_secret_key: hasher.finalize().into(),
|
||||
shared_secret,
|
||||
ephemeral_public_key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the KEM ciphertext to be transmitted to the receiver as the `EphemeralPublicKey`.
|
||||
#[must_use]
|
||||
pub fn generate_ephemeral_public_key(&self) -> EphemeralPublicKey {
|
||||
EphemeralPublicKey::from_scalar(self.ephemeral_secret_key)
|
||||
pub const fn ephemeral_public_key(&self) -> &EphemeralPublicKey {
|
||||
&self.ephemeral_public_key
|
||||
}
|
||||
|
||||
/// Returns the sender-side shared secret (established at construction time).
|
||||
#[must_use]
|
||||
pub fn calculate_shared_secret_sender(
|
||||
&self,
|
||||
receiver_viewing_public_key: &ViewingPublicKey,
|
||||
) -> SharedSecretKey {
|
||||
SharedSecretKey::new(self.ephemeral_secret_key, receiver_viewing_public_key)
|
||||
pub const fn calculate_shared_secret_sender(&self) -> SharedSecretKey {
|
||||
self.shared_secret
|
||||
}
|
||||
}
|
||||
|
||||
/// Encapsulates a fresh shared secret toward `vpk` and returns `(shared_secret, ciphertext)`.
|
||||
///
|
||||
/// Used when the local side is acting as an "ephemeral receiver" — i.e. generating a
|
||||
/// one-sided encryption that only the holder of the VSK can decrypt.
|
||||
#[must_use]
|
||||
pub fn produce_one_sided_shared_secret_receiver(
|
||||
vpk: &ViewingPublicKey,
|
||||
) -> (SharedSecretKey, EphemeralPublicKey) {
|
||||
let mut esk = [0; 32];
|
||||
OsRng.fill_bytes(&mut esk);
|
||||
(
|
||||
SharedSecretKey::new(esk, vpk),
|
||||
EphemeralPublicKey::from_scalar(esk),
|
||||
)
|
||||
SharedSecretKey::encapsulate(vpk)
|
||||
}
|
||||
|
||||
@ -1,44 +1,39 @@
|
||||
use aes_gcm::{Aes256Gcm, KeyInit as _, aead::Aead as _};
|
||||
use nssa_core::{
|
||||
SharedSecretKey,
|
||||
encryption::{Scalar, shared_key_derivation::Secp256k1Point},
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
program::{PdaSeed, ProgramId},
|
||||
};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
|
||||
use super::secret_holders::{PrivateKeyHolder, SecretSpendingKey};
|
||||
use super::secret_holders::{PrivateKeyHolder, SecretSpendingKey, ViewingSecretKey};
|
||||
|
||||
/// Public key used to seal a `GroupKeyHolder` for distribution to a recipient.
|
||||
///
|
||||
/// Wraps a secp256k1 point but is a distinct type from `ViewingPublicKey` to enforce
|
||||
/// key separation: viewing keys encrypt account state, sealing keys encrypt the GMS
|
||||
/// for off-chain distribution.
|
||||
pub struct SealingPublicKey(Secp256k1Point);
|
||||
/// Wraps the ML-KEM-768 encapsulation key bytes (1184 bytes). Distinct from
|
||||
/// `ViewingPublicKey` to enforce key separation: viewing keys encrypt account state,
|
||||
/// sealing keys encrypt the GMS for off-chain distribution.
|
||||
pub struct SealingPublicKey(Vec<u8>);
|
||||
|
||||
impl SealingPublicKey {
|
||||
/// Derive the sealing public key from a secret scalar.
|
||||
#[must_use]
|
||||
pub fn from_scalar(scalar: Scalar) -> Self {
|
||||
Self(Secp256k1Point::from_scalar(scalar))
|
||||
}
|
||||
|
||||
/// Construct from raw serialized bytes (e.g. received from another wallet).
|
||||
/// Construct from raw serialized encapsulation-key bytes (e.g. received from another wallet).
|
||||
#[must_use]
|
||||
pub const fn from_bytes(bytes: Vec<u8>) -> Self {
|
||||
Self(Secp256k1Point(bytes))
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
/// Returns the raw bytes for display or transmission.
|
||||
#[must_use]
|
||||
pub fn to_bytes(&self) -> &[u8] {
|
||||
&self.0.0
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Secret key used to unseal a `GroupKeyHolder` received from another member.
|
||||
pub type SealingSecretKey = Scalar;
|
||||
/// Holds the two 32-byte FIPS 203 seed halves `d` and `r`.
|
||||
pub type SealingSecretKey = ViewingSecretKey;
|
||||
|
||||
/// Manages shared viewing keys for a group of controllers owning private PDAs.
|
||||
///
|
||||
@ -153,18 +148,16 @@ impl GroupKeyHolder {
|
||||
|
||||
/// Encrypts this holder's GMS under the recipient's [`SealingPublicKey`].
|
||||
///
|
||||
/// Uses an ephemeral ECDH key exchange to derive a shared secret, then AES-256-GCM
|
||||
/// to encrypt the payload. The returned bytes are
|
||||
/// `ephemeral_pubkey (33) || nonce (12) || ciphertext+tag (48)` = 93 bytes.
|
||||
/// Uses ML-KEM-768 encapsulation to derive a shared secret, then AES-256-GCM to encrypt
|
||||
/// the payload. The returned bytes are
|
||||
/// `kem_ciphertext (1088) || nonce (12) || ciphertext+tag (48)` = 1148 bytes.
|
||||
///
|
||||
/// Each call generates a fresh ephemeral key, so two seals of the same holder produce
|
||||
/// Each call generates a fresh KEM encapsulation, so two seals of the same holder produce
|
||||
/// different ciphertexts.
|
||||
#[must_use]
|
||||
pub fn seal_for(&self, recipient_key: &SealingPublicKey) -> Vec<u8> {
|
||||
let mut ephemeral_scalar: Scalar = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut ephemeral_scalar);
|
||||
let ephemeral_pubkey = Secp256k1Point::from_scalar(ephemeral_scalar);
|
||||
let shared = SharedSecretKey::new(ephemeral_scalar, &recipient_key.0);
|
||||
let vpk = ViewingPublicKey(recipient_key.0.clone());
|
||||
let (shared, kem_ct) = SharedSecretKey::encapsulate(&vpk);
|
||||
let aes_key = Self::seal_kdf(&shared);
|
||||
let cipher = Aes256Gcm::new(&aes_key.into());
|
||||
|
||||
@ -176,12 +169,12 @@ impl GroupKeyHolder {
|
||||
.encrypt(&nonce, self.gms.as_ref())
|
||||
.expect("AES-GCM encryption should not fail with valid key/nonce");
|
||||
|
||||
let capacity = 33_usize
|
||||
let capacity = 1088_usize
|
||||
.checked_add(12)
|
||||
.and_then(|n| n.checked_add(ciphertext.len()))
|
||||
.expect("seal capacity overflow");
|
||||
let mut out = Vec::with_capacity(capacity);
|
||||
out.extend_from_slice(&ephemeral_pubkey.0);
|
||||
out.extend_from_slice(&kem_ct.0);
|
||||
out.extend_from_slice(&nonce_bytes);
|
||||
out.extend_from_slice(&ciphertext);
|
||||
out
|
||||
@ -189,20 +182,23 @@ impl GroupKeyHolder {
|
||||
|
||||
/// Decrypts a sealed `GroupKeyHolder` using the recipient's [`SealingSecretKey`].
|
||||
///
|
||||
/// Returns `Err` if the ciphertext is too short, the ECDH point is invalid, or the
|
||||
/// AES-GCM authentication tag doesn't verify (wrong key or tampered data).
|
||||
pub fn unseal(sealed: &[u8], own_key: SealingSecretKey) -> Result<Self, SealError> {
|
||||
const HEADER_LEN: usize = 33 + 12;
|
||||
/// Returns `Err` if the ciphertext is too short or the AES-GCM authentication tag
|
||||
/// doesn't verify (wrong key or tampered data).
|
||||
pub fn unseal(sealed: &[u8], own_key: &SealingSecretKey) -> Result<Self, SealError> {
|
||||
// kem_ciphertext (1088) + nonce (12) = header, then AES-GCM tag (16) minimum.
|
||||
const KEM_CT_LEN: usize = 1088;
|
||||
const HEADER_LEN: usize = KEM_CT_LEN + 12;
|
||||
const MIN_LEN: usize = HEADER_LEN + 16;
|
||||
|
||||
if sealed.len() < MIN_LEN {
|
||||
return Err(SealError::TooShort);
|
||||
}
|
||||
// MIN_LEN (61) > HEADER_LEN (45), so all slicing below is in bounds.
|
||||
let ephemeral_pubkey = Secp256k1Point(sealed[..33].to_vec());
|
||||
let nonce = aes_gcm::Nonce::from_slice(&sealed[33..HEADER_LEN]);
|
||||
|
||||
let kem_ct = EphemeralPublicKey(sealed[..KEM_CT_LEN].to_vec());
|
||||
let nonce = aes_gcm::Nonce::from_slice(&sealed[KEM_CT_LEN..HEADER_LEN]);
|
||||
let ciphertext = &sealed[HEADER_LEN..];
|
||||
|
||||
let shared = SharedSecretKey::new(own_key, &ephemeral_pubkey);
|
||||
let shared = SharedSecretKey::decapsulate(&kem_ct, &own_key.d, &own_key.r);
|
||||
let aes_key = Self::seal_kdf(&shared);
|
||||
let cipher = Aes256Gcm::new(&aes_key.into());
|
||||
|
||||
@ -219,7 +215,7 @@ impl GroupKeyHolder {
|
||||
Ok(Self::from_gms(gms))
|
||||
}
|
||||
|
||||
/// Derives an AES-256 key from the ECDH shared secret via SHA-256 with a domain prefix.
|
||||
/// Derives an AES-256 key from the ML-KEM shared secret via SHA-256 with a domain prefix.
|
||||
fn seal_kdf(shared: &SharedSecretKey) -> [u8; 32] {
|
||||
const PREFIX: &[u8; 32] = b"/LEE/v0.3/GroupKeySeal/AES\x00\x00\x00\x00\x00\x00";
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
@ -408,7 +404,7 @@ mod tests {
|
||||
let recipient_vsk = recipient_keys.viewing_secret_key;
|
||||
|
||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
let restored = GroupKeyHolder::unseal(&sealed, recipient_vsk).expect("unseal");
|
||||
let restored = GroupKeyHolder::unseal(&sealed, &recipient_vsk).expect("unseal");
|
||||
|
||||
assert_eq!(restored.dangerous_raw_gms(), holder.dangerous_raw_gms());
|
||||
|
||||
@ -433,13 +429,12 @@ mod tests {
|
||||
.produce_private_key_holder(None)
|
||||
.generate_viewing_public_key();
|
||||
|
||||
let wrong_ssk = SecretSpendingKey([99_u8; 32]);
|
||||
let wrong_vsk = wrong_ssk
|
||||
let wrong_vsk = SecretSpendingKey([99_u8; 32])
|
||||
.produce_private_key_holder(None)
|
||||
.viewing_secret_key;
|
||||
|
||||
let sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
let result = GroupKeyHolder::unseal(&sealed, wrong_vsk);
|
||||
let result = GroupKeyHolder::unseal(&sealed, &wrong_vsk);
|
||||
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
||||
}
|
||||
|
||||
@ -454,15 +449,15 @@ mod tests {
|
||||
let recipient_vsk = recipient_keys.viewing_secret_key;
|
||||
|
||||
let mut sealed = holder.seal_for(&SealingPublicKey::from_bytes(recipient_vpk.0));
|
||||
// Flip a byte in the ciphertext portion (after ephemeral_pubkey + nonce)
|
||||
// Flip a byte in the AES-GCM ciphertext portion (after KEM ciphertext + nonce).
|
||||
let last = sealed.len() - 1;
|
||||
sealed[last] ^= 0xFF;
|
||||
|
||||
let result = GroupKeyHolder::unseal(&sealed, recipient_vsk);
|
||||
let result = GroupKeyHolder::unseal(&sealed, &recipient_vsk);
|
||||
assert!(matches!(result, Err(super::SealError::DecryptionFailed)));
|
||||
}
|
||||
|
||||
/// Two seals of the same holder produce different ciphertexts (ephemeral randomness).
|
||||
/// Two seals of the same holder produce different ciphertexts (KEM randomness).
|
||||
#[test]
|
||||
fn two_seals_produce_different_ciphertexts() {
|
||||
let holder = GroupKeyHolder::from_gms([42_u8; 32]);
|
||||
@ -481,14 +476,15 @@ mod tests {
|
||||
/// Sealed payload is too short.
|
||||
#[test]
|
||||
fn unseal_too_short_fails() {
|
||||
let vsk: SealingSecretKey = [7_u8; 32];
|
||||
let result = GroupKeyHolder::unseal(&[0_u8; 10], vsk);
|
||||
let vsk = SealingSecretKey {
|
||||
d: [7_u8; 32],
|
||||
r: [0_u8; 32],
|
||||
};
|
||||
let result = GroupKeyHolder::unseal(&[0_u8; 10], &vsk);
|
||||
assert!(matches!(result, Err(super::SealError::TooShort)));
|
||||
}
|
||||
|
||||
/// Degenerate GMS values (all-zeros, all-ones, single-bit) must still produce valid,
|
||||
/// non-zero, pairwise-distinct npks. Rules out accidental "if gms == default { return
|
||||
/// default }" style shortcuts in the derivation.
|
||||
/// Degenerate GMS values must still produce valid, non-zero, pairwise-distinct npks.
|
||||
#[test]
|
||||
fn degenerate_gms_produces_distinct_non_zero_keys() {
|
||||
let seed = PdaSeed::new([1; 32]);
|
||||
@ -526,11 +522,9 @@ mod tests {
|
||||
let pda_seed = PdaSeed::new([42_u8; 32]);
|
||||
let program_id: nssa_core::program::ProgramId = [1; 8];
|
||||
|
||||
// Derive Alice's keys
|
||||
let alice_keys = alice_holder.derive_keys_for_pda(&TEST_PROGRAM_ID, &pda_seed);
|
||||
let alice_npk = alice_keys.generate_nullifier_public_key();
|
||||
|
||||
// Seal GMS for Bob using Bob's viewing key, Bob unseals
|
||||
let bob_ssk = SecretSpendingKey([77_u8; 32]);
|
||||
let bob_keys = bob_ssk.produce_private_key_holder(None);
|
||||
let bob_vpk = bob_keys.generate_viewing_public_key();
|
||||
@ -538,9 +532,8 @@ mod tests {
|
||||
|
||||
let sealed = alice_holder.seal_for(&SealingPublicKey::from_bytes(bob_vpk.0));
|
||||
let bob_holder =
|
||||
GroupKeyHolder::unseal(&sealed, bob_vsk).expect("Bob should unseal the GMS");
|
||||
GroupKeyHolder::unseal(&sealed, &bob_vsk).expect("Bob should unseal the GMS");
|
||||
|
||||
// Key agreement: both derive identical NPK and AccountId
|
||||
let bob_npk = bob_holder
|
||||
.derive_keys_for_pda(&TEST_PROGRAM_ID, &pda_seed)
|
||||
.generate_nullifier_public_key();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use k256::{Scalar, elliptic_curve::PrimeField as _};
|
||||
use nssa_core::{NullifierPublicKey, PrivateAccountKind, encryption::ViewingPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Digest as _;
|
||||
|
||||
use crate::key_management::{
|
||||
KeyChain,
|
||||
@ -34,10 +34,10 @@ impl ChildKeysPrivate {
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
||||
|
||||
let nsk = ssk.generate_nullifier_secret_key(None);
|
||||
let vsk = ssk.generate_viewing_secret_key(None);
|
||||
let vsk = ssk.generate_viewing_secret_seed_key(None);
|
||||
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_scalar(vsk);
|
||||
let vpk = ViewingPublicKey::from(&vsk);
|
||||
|
||||
Self {
|
||||
value: (
|
||||
@ -59,16 +59,18 @@ impl ChildKeysPrivate {
|
||||
|
||||
#[must_use]
|
||||
pub fn nth_child(&self, cci: u32) -> Self {
|
||||
#[expect(clippy::arithmetic_side_effects, reason = "TODO: fix later")]
|
||||
let parent_pt =
|
||||
Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into())
|
||||
.expect("Key generated as scalar, must be valid representation")
|
||||
* Scalar::from_repr(self.value.0.private_key_holder.viewing_secret_key.into())
|
||||
.expect("Key generated as scalar, must be valid representation");
|
||||
let mut input = vec![];
|
||||
let mut parent_hash = sha2::Sha256::new();
|
||||
parent_hash.update(b"LEE/keys");
|
||||
parent_hash.update([0_u8; 16]);
|
||||
parent_hash.update([9_u8]);
|
||||
parent_hash.update(self.value.0.private_key_holder.nullifier_secret_key);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.d);
|
||||
parent_hash.update(self.value.0.private_key_holder.viewing_secret_key.r);
|
||||
let parent_pt = parent_hash.finalize();
|
||||
|
||||
let mut input = vec![];
|
||||
input.extend_from_slice(b"LEE_seed_priv");
|
||||
input.extend_from_slice(&parent_pt.to_bytes());
|
||||
input.extend_from_slice(&parent_pt);
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
input.extend_from_slice(&cci.to_be_bytes());
|
||||
|
||||
@ -84,10 +86,10 @@ impl ChildKeysPrivate {
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
||||
|
||||
let nsk = ssk.generate_nullifier_secret_key(Some(cci));
|
||||
let vsk = ssk.generate_viewing_secret_key(Some(cci));
|
||||
let vsk = ssk.generate_viewing_secret_seed_key(Some(cci));
|
||||
|
||||
let npk = NullifierPublicKey::from(&nsk);
|
||||
let vpk = ViewingPublicKey::from_scalar(vsk);
|
||||
let vpk = ViewingPublicKey::from(&vsk);
|
||||
|
||||
Self {
|
||||
value: (
|
||||
@ -128,12 +130,11 @@ impl KeyTreeNode for ChildKeysPrivate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nssa_core::{NullifierPublicKey, NullifierSecretKey};
|
||||
use nssa_core::NullifierSecretKey;
|
||||
|
||||
use super::*;
|
||||
use crate::key_management::{self, secret_holders::ViewingSecretKey};
|
||||
|
||||
#[expect(clippy::redundant_type_annotations, reason = "TODO: clippy requires")]
|
||||
#[test]
|
||||
fn master_key_generation() {
|
||||
let seed: [u8; 64] = [
|
||||
@ -145,7 +146,7 @@ mod tests {
|
||||
|
||||
let keys = ChildKeysPrivate::root(seed);
|
||||
|
||||
let expected_ssk: SecretSpendingKey = key_management::secret_holders::SecretSpendingKey([
|
||||
let expected_ssk = key_management::secret_holders::SecretSpendingKey([
|
||||
246, 79, 26, 124, 135, 95, 52, 51, 201, 27, 48, 194, 2, 144, 51, 219, 245, 128, 139,
|
||||
222, 42, 195, 105, 33, 115, 97, 186, 0, 97, 14, 218, 191,
|
||||
]);
|
||||
@ -160,26 +161,92 @@ mod tests {
|
||||
34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163,
|
||||
];
|
||||
|
||||
let expected_npk: NullifierPublicKey = nssa_core::NullifierPublicKey([
|
||||
let expected_npk = nssa_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,
|
||||
]);
|
||||
let expected_vsk = [
|
||||
155, 90, 54, 75, 228, 130, 68, 201, 129, 251, 180, 195, 250, 64, 34, 230, 241, 204,
|
||||
216, 50, 149, 156, 10, 67, 208, 74, 9, 10, 47, 59, 50, 202,
|
||||
];
|
||||
|
||||
let expected_vpk_as_bytes: [u8; 33] = [
|
||||
2, 191, 99, 102, 114, 40, 131, 109, 166, 8, 222, 186, 107, 29, 156, 106, 206, 96, 127,
|
||||
80, 170, 66, 217, 79, 38, 80, 11, 74, 147, 123, 221, 159, 166,
|
||||
];
|
||||
let expected_vsk: ViewingSecretKey = ViewingSecretKey {
|
||||
d: [
|
||||
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: [
|
||||
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,
|
||||
],
|
||||
};
|
||||
|
||||
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,
|
||||
];
|
||||
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_npk == keys.value.0.nullifier_public_key);
|
||||
assert!(expected_vsk == keys.value.0.private_key_holder.viewing_secret_key);
|
||||
assert!(expected_vpk_as_bytes == keys.value.0.viewing_public_key.to_bytes());
|
||||
assert!(expected_vpk == keys.value.0.viewing_public_key.to_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -194,33 +261,107 @@ mod tests {
|
||||
let root_node = ChildKeysPrivate::root(seed);
|
||||
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
|
||||
|
||||
let expected_ccc: [u8; 32] = [
|
||||
27, 73, 133, 213, 214, 63, 217, 184, 164, 17, 172, 140, 223, 95, 255, 157, 11, 0, 58,
|
||||
53, 82, 147, 121, 120, 199, 50, 30, 28, 103, 24, 121, 187,
|
||||
let expected_ssk = key_management::secret_holders::SecretSpendingKey([
|
||||
215, 207, 70, 52, 161, 220, 88, 88, 241, 149, 81, 130, 217, 214, 252, 170, 51, 232,
|
||||
230, 158, 195, 173, 174, 37, 27, 101, 49, 35, 79, 13, 44, 225,
|
||||
]);
|
||||
|
||||
let expected_ccc = [
|
||||
113, 136, 96, 232, 12, 136, 185, 254, 36, 103, 64, 44, 238, 176, 240, 92, 219, 184,
|
||||
143, 35, 183, 54, 170, 15, 126, 56, 115, 21, 89, 142, 236, 217,
|
||||
];
|
||||
|
||||
let expected_nsk: NullifierSecretKey = [
|
||||
124, 61, 40, 92, 33, 135, 3, 41, 200, 234, 3, 69, 102, 184, 57, 191, 106, 151, 194,
|
||||
192, 103, 132, 141, 112, 249, 108, 192, 117, 24, 48, 70, 216,
|
||||
27, 167, 3, 140, 113, 16, 209, 83, 21, 77, 65, 91, 26, 191, 203, 102, 66, 140, 157,
|
||||
220, 101, 104, 227, 135, 216, 215, 216, 126, 194, 196, 43, 34,
|
||||
];
|
||||
let expected_npk = nssa_core::NullifierPublicKey([
|
||||
116, 231, 246, 189, 145, 240, 37, 59, 219, 223, 216, 246, 116, 171, 223, 55, 197, 200,
|
||||
134, 192, 221, 40, 218, 167, 239, 5, 11, 95, 147, 247, 162, 226,
|
||||
30, 208, 29, 96, 156, 95, 79, 16, 182, 0, 10, 194, 209, 90, 35, 177, 110, 224, 247, 67,
|
||||
219, 114, 113, 16, 42, 27, 220, 96, 151, 124, 8, 65,
|
||||
]);
|
||||
|
||||
let expected_vsk: ViewingSecretKey = [
|
||||
33, 155, 68, 60, 102, 70, 47, 105, 194, 129, 44, 26, 143, 198, 44, 244, 185, 31, 236,
|
||||
252, 205, 89, 138, 107, 39, 38, 154, 73, 109, 166, 41, 114,
|
||||
];
|
||||
let expected_vpk_as_bytes: [u8; 33] = [
|
||||
2, 78, 213, 113, 117, 105, 162, 248, 175, 68, 128, 232, 106, 204, 208, 159, 11, 78, 48,
|
||||
244, 127, 112, 46, 0, 93, 184, 1, 77, 132, 160, 75, 152, 88,
|
||||
let expected_vsk: ViewingSecretKey = ViewingSecretKey {
|
||||
d: [
|
||||
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: [
|
||||
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,
|
||||
],
|
||||
};
|
||||
|
||||
let expected_vpk: [u8; 1184] = [
|
||||
67, 150, 145, 133, 41, 124, 194, 102, 104, 131, 195, 8, 168, 170, 200, 40, 210, 84, 85,
|
||||
117, 50, 99, 52, 23, 144, 23, 22, 140, 187, 76, 49, 224, 189, 64, 249, 72, 219, 35, 49,
|
||||
162, 146, 121, 27, 179, 183, 215, 84, 177, 62, 37, 103, 97, 209, 201, 8, 162, 38, 109,
|
||||
87, 44, 103, 136, 112, 236, 120, 60, 235, 130, 60, 212, 209, 77, 77, 220, 28, 156, 34,
|
||||
7, 31, 35, 179, 102, 21, 54, 77, 99, 157, 210, 247, 151, 214, 182, 30, 57, 219, 40, 42,
|
||||
188, 32, 30, 134, 126, 7, 22, 51, 241, 152, 8, 96, 5, 87, 168, 64, 62, 81, 247, 33,
|
||||
228, 44, 180, 203, 60, 49, 66, 247, 143, 113, 106, 189, 44, 11, 182, 213, 247, 9, 22,
|
||||
3, 208, 125, 2, 8, 103, 195, 202, 21, 33, 72, 139, 233, 19, 171, 172, 69, 253, 212, 37,
|
||||
197, 66, 165, 207, 168, 69, 18, 24, 1, 100, 200, 175, 163, 247, 115, 17, 124, 84, 183,
|
||||
92, 96, 142, 204, 149, 2, 90, 53, 110, 246, 188, 135, 240, 160, 231, 145, 23, 90, 209,
|
||||
93, 166, 17, 119, 240, 49, 67, 234, 41, 187, 71, 23, 152, 159, 54, 206, 207, 26, 11,
|
||||
32, 134, 202, 185, 201, 25, 59, 199, 182, 18, 236, 175, 254, 227, 195, 98, 52, 139,
|
||||
162, 172, 195, 102, 178, 115, 59, 113, 108, 96, 89, 175, 145, 71, 202, 231, 153, 69, 3,
|
||||
25, 60, 43, 215, 35, 70, 119, 16, 235, 98, 184, 252, 50, 36, 161, 244, 57, 13, 214,
|
||||
115, 106, 225, 166, 7, 59, 44, 130, 197, 85, 69, 220, 81, 10, 1, 130, 227, 225, 47, 78,
|
||||
251, 49, 232, 55, 2, 66, 64, 180, 220, 65, 140, 231, 188, 172, 153, 153, 152, 15, 186,
|
||||
74, 6, 39, 16, 251, 216, 165, 145, 134, 3, 88, 131, 80, 114, 156, 119, 72, 130, 54,
|
||||
159, 202, 23, 7, 130, 127, 156, 252, 113, 108, 85, 22, 120, 104, 12, 151, 187, 102, 64,
|
||||
96, 137, 184, 68, 201, 20, 196, 196, 226, 220, 139, 174, 76, 109, 1, 179, 81, 156, 26,
|
||||
136, 238, 106, 41, 197, 18, 16, 179, 91, 9, 8, 213, 123, 108, 58, 3, 102, 12, 87, 92,
|
||||
217, 207, 166, 131, 17, 218, 134, 170, 27, 129, 145, 0, 65, 85, 99, 163, 97, 78, 228,
|
||||
15, 54, 85, 201, 58, 204, 160, 250, 66, 41, 36, 165, 78, 50, 137, 78, 197, 103, 57, 79,
|
||||
26, 14, 167, 104, 165, 129, 128, 90, 104, 148, 121, 135, 24, 126, 139, 235, 84, 183,
|
||||
165, 115, 111, 83, 48, 184, 55, 84, 250, 115, 171, 195, 91, 114, 213, 104, 51, 110, 86,
|
||||
148, 37, 139, 83, 49, 165, 171, 144, 90, 19, 91, 195, 111, 82, 185, 133, 211, 24, 186,
|
||||
48, 230, 172, 190, 6, 65, 230, 26, 139, 8, 9, 34, 54, 28, 103, 84, 116, 38, 252, 105,
|
||||
86, 123, 40, 31, 39, 64, 14, 253, 215, 147, 182, 218, 111, 148, 2, 18, 3, 197, 4, 129,
|
||||
107, 136, 89, 122, 56, 47, 9, 179, 66, 227, 24, 193, 32, 4, 172, 210, 29, 152, 114,
|
||||
134, 65, 249, 201, 178, 16, 206, 209, 39, 193, 109, 91, 122, 194, 26, 206, 37, 227, 55,
|
||||
160, 214, 85, 196, 64, 97, 96, 66, 80, 34, 177, 83, 200, 44, 137, 175, 149, 114, 42,
|
||||
229, 168, 248, 96, 106, 110, 182, 155, 62, 27, 179, 229, 139, 9, 213, 181, 116, 59,
|
||||
118, 142, 91, 23, 165, 80, 43, 118, 18, 41, 143, 125, 59, 102, 61, 224, 120, 186, 10,
|
||||
63, 119, 241, 168, 196, 87, 117, 138, 3, 151, 1, 129, 76, 154, 87, 200, 114, 124, 90,
|
||||
212, 182, 54, 94, 20, 165, 243, 88, 77, 76, 152, 69, 19, 164, 106, 196, 204, 46, 239,
|
||||
116, 42, 179, 65, 79, 39, 145, 63, 169, 199, 142, 6, 103, 118, 130, 49, 184, 208, 203,
|
||||
36, 162, 216, 9, 188, 17, 86, 45, 35, 20, 178, 218, 121, 164, 243, 145, 57, 208, 130,
|
||||
26, 27, 28, 100, 161, 148, 195, 54, 66, 114, 108, 146, 135, 66, 69, 232, 33, 197, 213,
|
||||
131, 107, 31, 19, 162, 155, 164, 161, 103, 8, 192, 127, 188, 196, 252, 2, 155, 18, 130,
|
||||
105, 53, 235, 200, 87, 203, 162, 95, 50, 158, 96, 210, 1, 45, 8, 26, 3, 192, 201, 182,
|
||||
148, 192, 157, 106, 5, 161, 248, 66, 89, 56, 141, 126, 243, 143, 68, 90, 133, 193, 181,
|
||||
198, 3, 169, 72, 66, 215, 195, 38, 37, 196, 103, 229, 89, 162, 210, 118, 12, 233, 162,
|
||||
95, 164, 107, 97, 11, 120, 255, 164, 60, 117, 37, 108, 144, 185, 167, 40, 124, 69, 23,
|
||||
37, 148, 222, 233, 43, 50, 16, 58, 53, 252, 8, 102, 88, 109, 28, 18, 22, 5, 49, 66,
|
||||
149, 114, 203, 95, 216, 175, 10, 87, 206, 46, 9, 101, 212, 226, 84, 4, 231, 161, 106,
|
||||
185, 31, 6, 101, 27, 54, 49, 85, 54, 84, 12, 250, 4, 49, 184, 134, 186, 23, 146, 54,
|
||||
90, 186, 134, 129, 68, 10, 241, 201, 65, 251, 69, 110, 127, 220, 148, 38, 250, 148, 83,
|
||||
32, 100, 131, 83, 133, 195, 54, 132, 63, 229, 85, 34, 172, 126, 68, 99, 197, 18, 197,
|
||||
91, 221, 234, 66, 203, 156, 73, 46, 0, 54, 205, 11, 52, 172, 114, 193, 127, 171, 134,
|
||||
109, 92, 37, 124, 181, 167, 191, 209, 148, 232, 26, 136, 230, 133, 181, 248, 117, 11,
|
||||
45, 156, 136, 117, 144, 126, 239, 230, 144, 90, 57, 109, 158, 167, 19, 131, 215, 136,
|
||||
85, 136, 10, 49, 9, 146, 64, 81, 28, 171, 53, 78, 40, 225, 94, 238, 70, 174, 125, 186,
|
||||
155, 177, 202, 157, 63, 39, 152, 44, 105, 184, 140, 179, 204, 32, 210, 109, 35, 150,
|
||||
194, 14, 98, 148, 176, 73, 185, 49, 135, 135, 244, 151, 147, 17, 103, 35, 242, 130, 3,
|
||||
158, 198, 152, 83, 240, 198, 254, 145, 181, 67, 163, 14, 237, 249, 179, 252, 220, 67,
|
||||
239, 7, 118, 131, 229, 137, 172, 151, 57, 121, 138, 204, 6, 208, 52, 168, 236, 123,
|
||||
104, 68, 36, 141, 25, 168, 56, 199, 40, 200, 52, 97, 59, 55, 184, 196, 234, 204, 108,
|
||||
75, 65, 177, 82, 207, 127, 128, 157, 0, 68, 163, 127, 152, 85, 123, 209, 163, 21, 119,
|
||||
62, 250, 236, 58, 229, 220, 99, 209, 147, 10, 177, 115, 172, 96, 192, 80, 240, 66, 191,
|
||||
138, 91, 52, 200, 132, 126, 255, 69, 98, 12, 140, 8, 158, 2, 153, 66, 211, 74, 242,
|
||||
147, 148, 209, 6, 161, 76, 149, 158, 209, 163, 20, 76, 75, 192, 193, 162, 71, 134, 72,
|
||||
160, 192, 10, 203, 4, 213, 23, 140, 196, 39, 231, 39, 16, 209, 228, 112, 244, 29, 27,
|
||||
181, 190, 19, 134, 116, 173, 135, 190, 118, 4, 214, 194, 189, 224, 164, 91, 211, 182,
|
||||
162, 226,
|
||||
];
|
||||
|
||||
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_npk == child_node.value.0.nullifier_public_key);
|
||||
assert!(expected_vsk == child_node.value.0.private_key_holder.viewing_secret_key);
|
||||
assert!(expected_vpk_as_bytes == child_node.value.0.viewing_public_key.to_bytes());
|
||||
assert!(expected_vpk == child_node.value.0.viewing_public_key.to_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,21 +69,16 @@ impl KeyChain {
|
||||
pub fn calculate_shared_secret_receiver(
|
||||
&self,
|
||||
ephemeral_public_key_sender: &EphemeralPublicKey,
|
||||
index: Option<u32>,
|
||||
_index: Option<u32>,
|
||||
) -> SharedSecretKey {
|
||||
SharedSecretKey::new(
|
||||
self.secret_spending_key.generate_viewing_secret_key(index),
|
||||
ephemeral_public_key_sender,
|
||||
)
|
||||
let vsk = &self.private_key_holder.viewing_secret_key;
|
||||
SharedSecretKey::decapsulate(ephemeral_public_key_sender, &vsk.d, &vsk.r)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use aes_gcm::aead::OsRng;
|
||||
use base58::ToBase58 as _;
|
||||
use k256::{AffinePoint, elliptic_curve::group::GroupEncoding as _};
|
||||
use rand::RngCore as _;
|
||||
|
||||
use super::*;
|
||||
use crate::key_management::{
|
||||
@ -106,14 +101,10 @@ mod tests {
|
||||
fn calculate_shared_secret_receiver() {
|
||||
let account_id_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Generate a random ephemeral public key sender
|
||||
let mut scalar = [0; 32];
|
||||
OsRng.fill_bytes(&mut scalar);
|
||||
let ephemeral_public_key_sender = EphemeralPublicKey::from_scalar(scalar);
|
||||
// Create a proper KEM ciphertext by encapsulating toward this key chain's VPK.
|
||||
let (_, epk) = SharedSecretKey::encapsulate(&account_id_key_holder.viewing_public_key);
|
||||
|
||||
// Calculate shared secret
|
||||
let _shared_secret = account_id_key_holder
|
||||
.calculate_shared_secret_receiver(&ephemeral_public_key_sender, None);
|
||||
let _shared_secret = account_id_key_holder.calculate_shared_secret_receiver(&epk, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -135,12 +126,6 @@ mod tests {
|
||||
println!("======Prerequisites======");
|
||||
println!();
|
||||
|
||||
println!(
|
||||
"Group generator {:?}",
|
||||
hex::encode(AffinePoint::GENERATOR.to_bytes())
|
||||
);
|
||||
println!();
|
||||
|
||||
println!("======Holders======");
|
||||
println!();
|
||||
|
||||
@ -188,13 +173,11 @@ mod tests {
|
||||
fn non_trivial_chain_index() {
|
||||
let keys = account_with_chain_index_2_for_tests();
|
||||
|
||||
let eph_key_holder = EphemeralKeyHolder::new(&keys.nullifier_public_key);
|
||||
let eph_key_holder = EphemeralKeyHolder::new(&keys.viewing_public_key);
|
||||
|
||||
let key_sender = eph_key_holder.calculate_shared_secret_sender(&keys.viewing_public_key);
|
||||
let key_receiver = keys.calculate_shared_secret_receiver(
|
||||
&eph_key_holder.generate_ephemeral_public_key(),
|
||||
Some(2),
|
||||
);
|
||||
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));
|
||||
|
||||
assert_eq!(key_sender.0, key_receiver.0);
|
||||
}
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
use bip39::Mnemonic;
|
||||
use common::HashType;
|
||||
use nssa_core::{
|
||||
NullifierPublicKey, NullifierSecretKey,
|
||||
encryption::{Scalar, ViewingPublicKey},
|
||||
};
|
||||
use ml_kem;
|
||||
use nssa_core::{NullifierPublicKey, NullifierSecretKey, encryption::ViewingPublicKey};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest as _, digest::FixedOutput as _};
|
||||
@ -19,8 +17,13 @@ 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]);
|
||||
|
||||
pub type ViewingSecretKey = Scalar;
|
||||
/// Viewing secret key: the KEM seed split into its two 32-byte halves `d` and `r` (= z in
|
||||
/// FIPS 203), from which the ML-KEM 768 decapsulation key is derived deterministically.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ViewingSecretKey {
|
||||
pub d: [u8; 32],
|
||||
pub r: [u8; 32],
|
||||
}
|
||||
|
||||
/// Private key holder. Produces public keys. Can produce `account_id`. Can produce shared secret
|
||||
/// for recepient.
|
||||
@ -114,7 +117,7 @@ impl SecretSpendingKey {
|
||||
|
||||
#[must_use]
|
||||
#[expect(clippy::big_endian_bytes, reason = "BIP-032 uses big endian")]
|
||||
pub fn generate_viewing_secret_key(&self, index: Option<u32>) -> ViewingSecretKey {
|
||||
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];
|
||||
@ -124,25 +127,56 @@ impl SecretSpendingKey {
|
||||
_ => index.expect("Expect a valid u32"),
|
||||
};
|
||||
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(PREFIX);
|
||||
hasher.update(self.0);
|
||||
hasher.update(SUFFIX_1);
|
||||
hasher.update(index.to_be_bytes());
|
||||
hasher.update(SUFFIX_2);
|
||||
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");
|
||||
|
||||
hasher.finalize_fixed().into()
|
||||
let full_seed = hmac_sha512::HMAC::mac(bytes, b"LEE_viewing_seed");
|
||||
|
||||
ViewingSecretKey {
|
||||
d: *full_seed
|
||||
.first_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
||||
r: *full_seed
|
||||
.last_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn produce_private_key_holder(&self, index: Option<u32>) -> PrivateKeyHolder {
|
||||
PrivateKeyHolder {
|
||||
nullifier_secret_key: self.generate_nullifier_secret_key(index),
|
||||
viewing_secret_key: self.generate_viewing_secret_key(index),
|
||||
viewing_secret_key: self.generate_viewing_secret_seed_key(index),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ViewingSecretKey> for ViewingPublicKey {
|
||||
fn from(sk: &ViewingSecretKey) -> Self {
|
||||
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);
|
||||
let dk = <MlKem768 as Kem>::DecapsulationKey::from_seed(Seed::from(seed_bytes));
|
||||
Self(dk.encapsulation_key().to_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKeyHolder {
|
||||
#[must_use]
|
||||
pub fn generate_nullifier_public_key(&self) -> NullifierPublicKey {
|
||||
@ -151,7 +185,7 @@ impl PrivateKeyHolder {
|
||||
|
||||
#[must_use]
|
||||
pub fn generate_viewing_public_key(&self) -> ViewingPublicKey {
|
||||
ViewingPublicKey::from_scalar(self.viewing_secret_key)
|
||||
ViewingPublicKey::from(&self.viewing_secret_key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,8 +217,7 @@ mod tests {
|
||||
assert_eq!(seed_holder.seed.len(), 64);
|
||||
|
||||
let top_secret_key_holder = seed_holder.produce_top_secret_key_holder();
|
||||
|
||||
let _vsk = top_secret_key_holder.generate_viewing_secret_key(None);
|
||||
let _vsk = top_secret_key_holder.generate_viewing_secret_seed_key(None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -16,7 +16,7 @@ thiserror.workspace = true
|
||||
bytemuck.workspace = true
|
||||
bytesize.workspace = true
|
||||
base58.workspace = true
|
||||
k256 = { workspace = true, optional = true }
|
||||
ml-kem = { workspace = true, optional = true, features = ["getrandom"] }
|
||||
chacha20 = { version = "0.10" }
|
||||
|
||||
[dev-dependencies]
|
||||
@ -24,4 +24,4 @@ serde_json.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
host = ["dep:k256"]
|
||||
host = ["dep:ml-kem"]
|
||||
|
||||
@ -7,7 +7,7 @@ use std::io::Read as _;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::Nullifier;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::encryption::shared_key_derivation::Secp256k1Point;
|
||||
use crate::encryption::EphemeralPublicKey;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::error::NssaCoreError;
|
||||
use crate::{
|
||||
@ -158,16 +158,17 @@ impl Ciphertext {
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl Secp256k1Point {
|
||||
/// Converts the point to bytes.
|
||||
impl EphemeralPublicKey {
|
||||
/// Serializes the ML-KEM-768 ciphertext to bytes (always 1088 bytes).
|
||||
#[must_use]
|
||||
pub fn to_bytes(&self) -> [u8; 33] {
|
||||
self.0.clone().try_into().unwrap()
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
/// Deserializes a secp256k1 point from a cursor.
|
||||
/// Deserializes an ML-KEM-768 ciphertext from a cursor.
|
||||
/// Reads exactly 1088 bytes — the fixed ciphertext size for ML-KEM-768.
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
|
||||
let mut value = vec![0; 33];
|
||||
let mut value = vec![0_u8; 1088];
|
||||
cursor.read_exact(&mut value)?;
|
||||
Ok(Self(value))
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ use chacha20::{
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "host")]
|
||||
pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, ViewingPublicKey};
|
||||
pub use shared_key_derivation::{EphemeralPublicKey, ViewingPublicKey};
|
||||
|
||||
use crate::{Commitment, account::Account, program::PrivateAccountKind};
|
||||
#[cfg(feature = "host")]
|
||||
@ -154,4 +154,41 @@ mod tests {
|
||||
|
||||
assert_eq!(account_ct.0.len(), pda_ct.0.len());
|
||||
}
|
||||
|
||||
/// Verifies the full account-note pipeline: ML-KEM-768 encapsulation/decapsulation
|
||||
/// feeds the correct shared secret into the SHA-256 KDF and `ChaCha20` round-trip.
|
||||
#[cfg(feature = "host")]
|
||||
#[test]
|
||||
fn kem_to_chacha20_round_trip() {
|
||||
let d = [1_u8; 32];
|
||||
let r = [2_u8; 32];
|
||||
let vpk = shared_key_derivation::ViewingPublicKey::from_seed(&d, &r);
|
||||
|
||||
let (sender_ss, epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
let receiver_ss = SharedSecretKey::decapsulate(&epk, &d, &r);
|
||||
|
||||
let account = Account {
|
||||
program_owner: [12_u32; 8],
|
||||
balance: 999,
|
||||
..Account::default()
|
||||
};
|
||||
let kind = PrivateAccountKind::Regular(0);
|
||||
let commitment = crate::Commitment::new(&AccountId::new([7_u8; 32]), &account);
|
||||
|
||||
let ct = EncryptionScheme::encrypt(&account, &kind, &sender_ss, &commitment, 0);
|
||||
let (decoded_kind, decoded_account) =
|
||||
EncryptionScheme::decrypt(&ct, &receiver_ss, &commitment, 0)
|
||||
.expect("decryption must succeed with correct shared secret");
|
||||
|
||||
assert_eq!(decoded_account, account);
|
||||
assert_eq!(decoded_kind, kind);
|
||||
|
||||
// Wrong shared secret must not decrypt correctly.
|
||||
let wrong_ss = SharedSecretKey([0_u8; 32]);
|
||||
let bad = EncryptionScheme::decrypt(&ct, &wrong_ss, &commitment, 0);
|
||||
assert!(
|
||||
bad.is_none() || bad.is_some_and(|(_, a)| a.balance != 999),
|
||||
"wrong shared secret must not produce the correct plaintext"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,78 +1,181 @@
|
||||
#![expect(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "Multiplication of finite field elements can't overflow"
|
||||
)]
|
||||
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use k256::{
|
||||
AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint,
|
||||
elliptic_curve::{
|
||||
PrimeField as _,
|
||||
sec1::{FromEncodedPoint as _, ToEncodedPoint as _},
|
||||
},
|
||||
};
|
||||
use ml_kem::{Decapsulate as _, Encapsulate as _, KeyExport as _, Seed};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{SharedSecretKey, encryption::Scalar};
|
||||
use crate::SharedSecretKey;
|
||||
|
||||
/// The ML-KEM-768 ciphertext produced during encapsulation; transmitted on-wire in place of the
|
||||
/// former ECDH ephemeral public key. Always 1088 bytes for ML-KEM-768.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct EphemeralPublicKey(pub Vec<u8>);
|
||||
|
||||
/// ML-KEM-768 encapsulation key bytes (1184 bytes, opaque to this crate).
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, BorshSerialize, BorshDeserialize,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
BorshSerialize,
|
||||
BorshDeserialize,
|
||||
)]
|
||||
pub struct Secp256k1Point(pub Vec<u8>);
|
||||
pub struct ViewingPublicKey(pub Vec<u8>);
|
||||
|
||||
impl std::fmt::Debug for Secp256k1Point {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let hex: String = self.0.iter().fold(String::new(), |mut acc, b| {
|
||||
write!(acc, "{b:02x}").expect("writing to string should not fail");
|
||||
acc
|
||||
});
|
||||
write!(f, "Secp256k1Point({hex})")
|
||||
}
|
||||
}
|
||||
|
||||
impl Secp256k1Point {
|
||||
impl ViewingPublicKey {
|
||||
#[must_use]
|
||||
pub fn from_scalar(value: Scalar) -> Self {
|
||||
let x_bytes: FieldBytes = value.into();
|
||||
let x = k256::Scalar::from_repr(x_bytes).unwrap();
|
||||
|
||||
let p = ProjectivePoint::GENERATOR * x;
|
||||
let q = AffinePoint::from(p);
|
||||
let enc = q.to_encoded_point(true);
|
||||
|
||||
Self(enc.as_bytes().to_vec())
|
||||
pub fn to_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub type EphemeralSecretKey = Scalar;
|
||||
pub type EphemeralPublicKey = Secp256k1Point;
|
||||
pub type ViewingPublicKey = Secp256k1Point;
|
||||
impl From<&EphemeralSecretKey> for EphemeralPublicKey {
|
||||
fn from(value: &EphemeralSecretKey) -> Self {
|
||||
Self::from_scalar(*value)
|
||||
/// Derive the ML-KEM-768 encapsulation key from the FIPS 203 seed halves `d` and `r`.
|
||||
/// 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 {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(d);
|
||||
seed[32..].copy_from_slice(r);
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
Self(dk.encapsulation_key().to_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedSecretKey {
|
||||
/// Creates a new shared secret key from a scalar and a point.
|
||||
/// Sender: encapsulate a fresh shared secret toward `vpk`.
|
||||
///
|
||||
/// Returns `(shared_secret, ciphertext)`. The ciphertext must be included in the transaction
|
||||
/// as the `EphemeralPublicKey`; the receiver recovers the same shared secret via
|
||||
/// [`decapsulate`][Self::decapsulate].
|
||||
#[must_use]
|
||||
pub fn new(scalar: Scalar, point: &Secp256k1Point) -> Self {
|
||||
let scalar = k256::Scalar::from_repr(scalar.into()).unwrap();
|
||||
let point: [u8; 33] = point.0.clone().try_into().unwrap();
|
||||
pub fn encapsulate(vpk: &ViewingPublicKey) -> (Self, EphemeralPublicKey) {
|
||||
let ek_bytes: ml_kem::kem::Key<ml_kem::EncapsulationKey768> = vpk
|
||||
.0
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("ViewingPublicKey must be 1184 bytes (ML-KEM-768 encapsulation key)");
|
||||
let ek = ml_kem::EncapsulationKey768::new(&ek_bytes)
|
||||
.expect("ViewingPublicKey bytes must encode a valid ML-KEM-768 encapsulation key");
|
||||
let (ct, ss) = ek.encapsulate();
|
||||
let ss_bytes: [u8; 32] = ss
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("ML-KEM shared key is 32 bytes");
|
||||
(Self(ss_bytes), EphemeralPublicKey(ct.to_vec()))
|
||||
}
|
||||
|
||||
let encoded = EncodedPoint::from_bytes(point).unwrap();
|
||||
let pubkey_affine = AffinePoint::from_encoded_point(&encoded).unwrap();
|
||||
/// Sender: deterministically encapsulate a shared secret toward `vpk`.
|
||||
///
|
||||
/// The KEM randomness is derived as `SHA-256(message_hash || output_index_le)`,
|
||||
/// making the ciphertext reproducible from the same `(vpk, message_hash, output_index)`
|
||||
/// triple. Use a distinct `output_index` for each private account output in the same
|
||||
/// transaction to ensure per-output EPK uniqueness.
|
||||
#[must_use]
|
||||
pub fn encapsulate_deterministic(
|
||||
vpk: &ViewingPublicKey,
|
||||
message_hash: &[u8; 32],
|
||||
output_index: u32,
|
||||
) -> (Self, EphemeralPublicKey) {
|
||||
use risc0_zkvm::sha::{Impl, Sha256 as _};
|
||||
|
||||
let shared = ProjectivePoint::from(pubkey_affine) * scalar;
|
||||
let shared_affine = shared.to_affine();
|
||||
let mut input = Vec::with_capacity(36);
|
||||
input.extend_from_slice(message_hash);
|
||||
input.extend_from_slice(&output_index.to_le_bytes());
|
||||
let hash = Impl::hash_bytes(&input);
|
||||
let m: ml_kem::B32 =
|
||||
ml_kem::array::Array::try_from(hash.as_bytes()).expect("SHA-256 output is 32 bytes");
|
||||
|
||||
let shared_affine_encoded = shared_affine.to_encoded_point(false);
|
||||
let x_bytes_slice = shared_affine_encoded.x().unwrap();
|
||||
let mut x_bytes = [0_u8; 32];
|
||||
x_bytes.copy_from_slice(x_bytes_slice);
|
||||
let ek_bytes: ml_kem::kem::Key<ml_kem::EncapsulationKey768> = vpk
|
||||
.0
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("ViewingPublicKey must be 1184 bytes (ML-KEM-768 encapsulation key)");
|
||||
let ek = ml_kem::EncapsulationKey768::new(&ek_bytes)
|
||||
.expect("ViewingPublicKey bytes must encode a valid ML-KEM-768 encapsulation key");
|
||||
let (ct, ss) = ek.encapsulate_deterministic(&m);
|
||||
let ss_bytes: [u8; 32] = ss
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("ML-KEM shared key is 32 bytes");
|
||||
(Self(ss_bytes), EphemeralPublicKey(ct.to_vec()))
|
||||
}
|
||||
|
||||
Self(x_bytes)
|
||||
/// Receiver: decapsulate the shared secret from a KEM ciphertext.
|
||||
///
|
||||
/// `d` and `r` 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 {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(d);
|
||||
seed[32..].copy_from_slice(r);
|
||||
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_bytes: [u8; 32] = ss
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.expect("ML-KEM shared key is 32 bytes");
|
||||
Self(ss_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ml_kem::KeyExport as _;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn encapsulate_decapsulate_round_trip() {
|
||||
let d = [1_u8; 32];
|
||||
let r = [2_u8; 32];
|
||||
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(&d);
|
||||
seed[32..].copy_from_slice(&r);
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
assert_eq!(
|
||||
vpk.0.len(),
|
||||
1184,
|
||||
"ML-KEM-768 encapsulation key is 1184 bytes"
|
||||
);
|
||||
}
|
||||
|
||||
#[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 vpk1 = {
|
||||
let mut seed = Seed::default();
|
||||
seed[..32].copy_from_slice(&d1);
|
||||
seed[32..].copy_from_slice(&r1);
|
||||
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);
|
||||
let dk = ml_kem::DecapsulationKey768::from_seed(seed);
|
||||
ViewingPublicKey(dk.encapsulation_key().to_bytes().to_vec())
|
||||
};
|
||||
|
||||
let (ss1, _) = SharedSecretKey::encapsulate(&vpk1);
|
||||
let (ss2, _) = SharedSecretKey::encapsulate(&vpk2);
|
||||
|
||||
assert_ne!(ss1.0, ss2.0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,8 +243,8 @@ mod tests {
|
||||
|
||||
let expected_sender_pre = sender.clone();
|
||||
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &recipient_keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![sender, recipient],
|
||||
@ -340,11 +340,11 @@ mod tests {
|
||||
Commitment::new(&recipient_account_id, &expected_private_account_2),
|
||||
];
|
||||
|
||||
let esk_1 = [3; 32];
|
||||
let shared_secret_1 = SharedSecretKey::new(esk_1, &sender_keys.vpk());
|
||||
let shared_secret_1 =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let esk_2 = [5; 32];
|
||||
let shared_secret_2 = SharedSecretKey::new(esk_2, &recipient_keys.vpk());
|
||||
let shared_secret_2 =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 1).0;
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![sender_pre, recipient],
|
||||
@ -418,8 +418,8 @@ mod tests {
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &account_keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let program_with_deps = ProgramWithDependencies::new(
|
||||
validity_window_chain_caller,
|
||||
@ -449,7 +449,8 @@ mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let identifier: u128 = 99;
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, identifier);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -486,7 +487,8 @@ mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret_pda = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret_pda =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// PDA (new, mask 3)
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 0);
|
||||
@ -524,7 +526,8 @@ mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret_pda = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret_pda =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// PDA (new, private PDA)
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 0);
|
||||
@ -578,7 +581,8 @@ mod tests {
|
||||
let shared_keys = test_private_account_keys_1();
|
||||
let shared_npk = shared_keys.npk();
|
||||
let shared_identifier: u128 = 42;
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &shared_keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&shared_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// Sender: public account with balance, owned by auth-transfer
|
||||
let sender_id = AccountId::new([99; 32]);
|
||||
@ -629,7 +633,7 @@ mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let account_id = AccountId::for_regular_private_account(&keys.npk(), identifier);
|
||||
let pre = AccountWithMetadata::new(Account::default(), true, account_id);
|
||||
|
||||
@ -659,7 +663,7 @@ mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let sender = AccountWithMetadata::new(
|
||||
Account {
|
||||
@ -704,7 +708,7 @@ mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let keys = test_private_account_keys_1();
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let account_id = AccountId::for_regular_private_account(&keys.npk(), identifier);
|
||||
let account = Account {
|
||||
program_owner: program.id(),
|
||||
@ -753,7 +757,7 @@ mod tests {
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let identifier: u128 = 99;
|
||||
let ssk = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let auth_transfer_id = auth_transfer.id();
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, identifier);
|
||||
@ -807,7 +811,8 @@ mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 5);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -833,7 +838,7 @@ mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let ssk = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let ssk = SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let auth_transfer_id = auth_transfer.id();
|
||||
let pda_id = AccountId::for_private_pda(&program.id(), &seed, &npk, 5);
|
||||
|
||||
@ -143,7 +143,7 @@ pub mod tests {
|
||||
Commitment, EncryptionScheme, Nullifier, NullifierPublicKey, PrivateAccountKind,
|
||||
SharedSecretKey,
|
||||
account::{Account, AccountId, Nonce},
|
||||
encryption::{EphemeralPublicKey, ViewingPublicKey},
|
||||
encryption::ViewingPublicKey,
|
||||
program::{BlockValidityWindow, TimestampValidityWindow},
|
||||
};
|
||||
use sha2::{Digest as _, Sha256};
|
||||
@ -208,7 +208,7 @@ pub mod tests {
|
||||
let nonces_bytes: &[u8] = &[1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
// all remaining vec fields are empty: u32 len=0
|
||||
let empty_vec_bytes: &[u8] = &[0_u8; 4];
|
||||
// validity windows: unbounded = {from: None (0u8), to: None (0u8)}
|
||||
// validity windows: unbounded = {from: None (0_u8), to: None (0_u8)}
|
||||
let unbounded_window_bytes: &[u8] = &[0_u8; 2];
|
||||
|
||||
let expected_borsh_vec: Vec<u8> = [
|
||||
@ -246,13 +246,11 @@ pub mod tests {
|
||||
#[test]
|
||||
fn encrypted_account_data_constructor() {
|
||||
let npk = NullifierPublicKey::from(&[1; 32]);
|
||||
let vpk = ViewingPublicKey::from_scalar([2; 32]);
|
||||
let vpk = ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]);
|
||||
let account = Account::default();
|
||||
let account_id = nssa_core::account::AccountId::for_regular_private_account(&npk, 0);
|
||||
let commitment = Commitment::new(&account_id, &account);
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &vpk);
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) = SharedSecretKey::encapsulate_deterministic(&vpk, &[0_u8; 32], 0);
|
||||
let ciphertext = EncryptionScheme::encrypt(
|
||||
&account,
|
||||
&PrivateAccountKind::Regular(0),
|
||||
|
||||
@ -404,7 +404,7 @@ pub mod tests {
|
||||
BlockId, Commitment, InputAccountIdentity, Nullifier, NullifierPublicKey,
|
||||
NullifierSecretKey, SharedSecretKey, Timestamp,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce, data::Data},
|
||||
encryption::{EphemeralPublicKey, Scalar, ViewingPublicKey},
|
||||
encryption::ViewingPublicKey,
|
||||
program::{
|
||||
BlockValidityWindow, ExecutionValidationError, PdaSeed, ProgramId,
|
||||
TimestampValidityWindow, WrappedBalanceSum,
|
||||
@ -518,7 +518,8 @@ pub mod tests {
|
||||
|
||||
pub struct TestPrivateKeys {
|
||||
pub nsk: NullifierSecretKey,
|
||||
pub vsk: Scalar,
|
||||
pub d: [u8; 32],
|
||||
pub r: [u8; 32],
|
||||
}
|
||||
|
||||
impl TestPrivateKeys {
|
||||
@ -527,7 +528,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
pub fn vpk(&self) -> ViewingPublicKey {
|
||||
ViewingPublicKey::from_scalar(self.vsk)
|
||||
ViewingPublicKey::from_seed(&self.d, &self.r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1313,14 +1314,16 @@ pub mod tests {
|
||||
pub fn test_private_account_keys_1() -> TestPrivateKeys {
|
||||
TestPrivateKeys {
|
||||
nsk: [13; 32],
|
||||
vsk: [31; 32],
|
||||
d: [31; 32],
|
||||
r: [32; 32],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_private_account_keys_2() -> TestPrivateKeys {
|
||||
TestPrivateKeys {
|
||||
nsk: [38; 32],
|
||||
vsk: [83; 32],
|
||||
d: [83; 32],
|
||||
r: [84; 32],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1341,9 +1344,8 @@ pub mod tests {
|
||||
let recipient =
|
||||
AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
|
||||
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &recipient_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![sender, recipient],
|
||||
@ -1393,13 +1395,11 @@ pub mod tests {
|
||||
let recipient_pre =
|
||||
AccountWithMetadata::new(Account::default(), false, (&recipient_keys.npk(), 0));
|
||||
|
||||
let esk_1 = [3; 32];
|
||||
let shared_secret_1 = SharedSecretKey::new(esk_1, &sender_keys.vpk());
|
||||
let epk_1 = EphemeralPublicKey::from_scalar(esk_1);
|
||||
let (shared_secret_1, epk_1) =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let esk_2 = [3; 32];
|
||||
let shared_secret_2 = SharedSecretKey::new(esk_2, &recipient_keys.vpk());
|
||||
let epk_2 = EphemeralPublicKey::from_scalar(esk_2);
|
||||
let (shared_secret_2, epk_2) =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 1);
|
||||
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![sender_pre, recipient_pre],
|
||||
@ -1463,9 +1463,8 @@ pub mod tests {
|
||||
*recipient_account_id,
|
||||
);
|
||||
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &sender_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (output, proof) = circuit::execute_and_prove(
|
||||
vec![sender_pre, recipient_pre],
|
||||
@ -1973,14 +1972,24 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([55; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
nsk: recipient_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::new([56; 32], &recipient_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
@ -2019,14 +2028,24 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([55; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::new([56; 32], &recipient_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
@ -2065,14 +2084,24 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([55; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::new([56; 32], &recipient_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
@ -2111,14 +2140,24 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([55; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::new([56; 32], &recipient_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
@ -2157,14 +2196,24 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([55; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::new([56; 32], &recipient_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
@ -2201,14 +2250,24 @@ pub mod tests {
|
||||
Program::serialize_instruction(10_u128).unwrap(),
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([55; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&sender_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::PrivateUnauthorized {
|
||||
npk: recipient_keys.npk(),
|
||||
ssk: SharedSecretKey::new([56; 32], &recipient_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(
|
||||
&recipient_keys.vpk(),
|
||||
&[0_u8; 32],
|
||||
0,
|
||||
)
|
||||
.0,
|
||||
identifier: 0,
|
||||
},
|
||||
],
|
||||
@ -2227,7 +2286,8 @@ pub mod tests {
|
||||
let program = Program::simple_balance_transfer();
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let public_account_1 = AccountWithMetadata::new(
|
||||
Account {
|
||||
program_owner: program.id(),
|
||||
@ -2268,7 +2328,8 @@ pub mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&program.id(), &seed, &npk, u128::MAX);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -2304,7 +2365,8 @@ pub mod tests {
|
||||
let npk_a = keys_a.npk();
|
||||
let npk_b = keys_b.npk();
|
||||
let seed = PdaSeed::new([42; 32]);
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys_b.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys_b.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// `account_id` is derived from `npk_a`, but `npk_b` is supplied for this pre_state.
|
||||
// `AccountId::for_private_pda(program, seed, npk_b) != account_id`, so the claim check in
|
||||
@ -2338,7 +2400,8 @@ pub mod tests {
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let seed = PdaSeed::new([77; 32]);
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&delegator.id(), &seed, &npk, u128::MAX);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -2376,7 +2439,8 @@ pub mod tests {
|
||||
let npk = keys.npk();
|
||||
let claim_seed = PdaSeed::new([77; 32]);
|
||||
let wrong_delegated_seed = PdaSeed::new([88; 32]);
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_id = AccountId::for_private_pda(&delegator.id(), &claim_seed, &npk, u128::MAX);
|
||||
let pre_state = AccountWithMetadata::new(Account::default(), false, account_id);
|
||||
@ -2413,8 +2477,8 @@ pub mod tests {
|
||||
let keys_a = test_private_account_keys_1();
|
||||
let keys_b = test_private_account_keys_2();
|
||||
let seed = PdaSeed::new([55; 32]);
|
||||
let shared_a = SharedSecretKey::new([66; 32], &keys_a.vpk());
|
||||
let shared_b = SharedSecretKey::new([77; 32], &keys_b.vpk());
|
||||
let shared_a = SharedSecretKey::encapsulate_deterministic(&keys_a.vpk(), &[0_u8; 32], 0).0;
|
||||
let shared_b = SharedSecretKey::encapsulate_deterministic(&keys_b.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let account_a = AccountId::for_private_pda(&program.id(), &seed, &keys_a.npk(), u128::MAX);
|
||||
let account_b = AccountId::for_private_pda(&program.id(), &seed, &keys_b.npk(), u128::MAX);
|
||||
@ -2459,7 +2523,8 @@ pub mod tests {
|
||||
let program = Program::noop();
|
||||
let keys = test_private_account_keys_1();
|
||||
let npk = keys.npk();
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let seed = PdaSeed::new([99; 32]);
|
||||
|
||||
// Simulate a previously-claimed private PDA: program_owner != DEFAULT, is_authorized =
|
||||
@ -2558,7 +2623,8 @@ pub mod tests {
|
||||
(&sender_keys.npk(), 0),
|
||||
);
|
||||
|
||||
let shared_secret = SharedSecretKey::new([55; 32], &sender_keys.vpk());
|
||||
let shared_secret =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
let result = execute_and_prove(
|
||||
vec![private_account_1.clone(), private_account_1],
|
||||
Program::serialize_instruction(100_u128).unwrap(),
|
||||
@ -2902,9 +2968,8 @@ pub mod tests {
|
||||
AccountId::from(&PublicKey::new_from_private_key(&recipient_private_key));
|
||||
let recipient_pre =
|
||||
AccountWithMetadata::new(Account::default(), true, recipient_account_id);
|
||||
let esk = [5; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &sender_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let balance = 37;
|
||||
|
||||
@ -3008,13 +3073,11 @@ pub mod tests {
|
||||
None,
|
||||
);
|
||||
|
||||
let from_esk = [3; 32];
|
||||
let from_ss = SharedSecretKey::new(from_esk, &from_keys.vpk());
|
||||
let from_epk = EphemeralPublicKey::from_scalar(from_esk);
|
||||
let (from_ss, from_epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&from_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let to_esk = [3; 32];
|
||||
let to_ss = SharedSecretKey::new(to_esk, &to_keys.vpk());
|
||||
let to_epk = EphemeralPublicKey::from_scalar(to_esk);
|
||||
let (to_ss, to_epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&to_keys.vpk(), &[0_u8; 32], 1);
|
||||
|
||||
let mut dependencies = HashMap::new();
|
||||
|
||||
@ -3311,9 +3374,8 @@ pub mod tests {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
|
||||
// Set up parameters for the new account
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &private_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = authenticated_transfer_core::Instruction::Initialize;
|
||||
|
||||
@ -3363,9 +3425,8 @@ pub mod tests {
|
||||
AccountWithMetadata::new(Account::default(), false, (&private_keys.npk(), 0));
|
||||
|
||||
let program = Program::claimer();
|
||||
let esk = [5; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &private_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![unauthorized_account],
|
||||
@ -3413,9 +3474,8 @@ pub mod tests {
|
||||
let claimer_program = Program::claimer();
|
||||
|
||||
// Set up parameters for claiming the new account
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &private_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = authenticated_transfer_core::Instruction::Initialize;
|
||||
|
||||
@ -3463,8 +3523,8 @@ pub mod tests {
|
||||
};
|
||||
|
||||
let noop_program = Program::noop();
|
||||
let esk2 = [4; 32];
|
||||
let shared_secret2 = SharedSecretKey::new(esk2, &private_keys.vpk());
|
||||
let shared_secret2 =
|
||||
SharedSecretKey::encapsulate_deterministic(&private_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
// Step 3: Try to execute noop program with authentication but without initialization
|
||||
let res = execute_and_prove(
|
||||
@ -3548,7 +3608,8 @@ pub mod tests {
|
||||
vec![private_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([3; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
@ -3574,7 +3635,8 @@ pub mod tests {
|
||||
vec![private_account],
|
||||
Program::serialize_instruction(instruction).unwrap(),
|
||||
vec![InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
ssk: SharedSecretKey::new([3; 32], &sender_keys.vpk()),
|
||||
ssk: SharedSecretKey::encapsulate_deterministic(&sender_keys.vpk(), &[0_u8; 32], 0)
|
||||
.0,
|
||||
nsk: sender_keys.nsk,
|
||||
membership_proof: (0, vec![]),
|
||||
identifier: 0,
|
||||
@ -3620,8 +3682,8 @@ pub mod tests {
|
||||
let balance_to_transfer = 10_u128;
|
||||
let instruction = (balance_to_transfer, auth_transfers.id());
|
||||
|
||||
let recipient_esk = [3; 32];
|
||||
let recipient = SharedSecretKey::new(recipient_esk, &recipient_keys.vpk());
|
||||
let recipient =
|
||||
SharedSecretKey::encapsulate_deterministic(&recipient_keys.vpk(), &[0_u8; 32], 0).0;
|
||||
|
||||
let mut dependencies = HashMap::new();
|
||||
dependencies.insert(auth_transfers.id(), auth_transfers);
|
||||
@ -3777,9 +3839,8 @@ pub mod tests {
|
||||
let pre = AccountWithMetadata::new(Account::default(), false, (&account_keys.npk(), 0));
|
||||
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs();
|
||||
let tx = {
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &account_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = (
|
||||
block_validity_window,
|
||||
@ -3847,9 +3908,8 @@ pub mod tests {
|
||||
let pre = AccountWithMetadata::new(Account::default(), false, (&account_keys.npk(), 0));
|
||||
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0).with_test_programs();
|
||||
let tx = {
|
||||
let esk = [3; 32];
|
||||
let shared_secret = SharedSecretKey::new(esk, &account_keys.vpk());
|
||||
let epk = EphemeralPublicKey::from_scalar(esk);
|
||||
let (shared_secret, epk) =
|
||||
SharedSecretKey::encapsulate_deterministic(&account_keys.vpk(), &[0_u8; 32], 0);
|
||||
|
||||
let instruction = (
|
||||
BlockValidityWindow::new_unbounded(),
|
||||
@ -4403,8 +4463,10 @@ pub mod tests {
|
||||
..Account::default()
|
||||
};
|
||||
|
||||
let alice_shared_0 = SharedSecretKey::new([10; 32], &alice_keys.vpk());
|
||||
let alice_shared_1 = SharedSecretKey::new([11; 32], &alice_keys.vpk());
|
||||
let (alice_shared_0, alice_epk_0) =
|
||||
SharedSecretKey::encapsulate_deterministic(&alice_keys.vpk(), &[0_u8; 32], 0);
|
||||
let (alice_shared_1, alice_epk_1) =
|
||||
SharedSecretKey::encapsulate_deterministic(&alice_keys.vpk(), &[0_u8; 32], 1);
|
||||
|
||||
// Fund alice_pda_0
|
||||
{
|
||||
@ -4430,11 +4492,7 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![funder_id],
|
||||
vec![funder_nonce],
|
||||
vec![(
|
||||
alice_npk,
|
||||
alice_keys.vpk(),
|
||||
EphemeralPublicKey::from_scalar([10; 32]),
|
||||
)],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_0.clone())],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
@ -4472,11 +4530,7 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![funder_id],
|
||||
vec![funder_nonce],
|
||||
vec![(
|
||||
alice_npk,
|
||||
alice_keys.vpk(),
|
||||
EphemeralPublicKey::from_scalar([11; 32]),
|
||||
)],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_1.clone())],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
@ -4522,11 +4576,7 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![Nonce(0)],
|
||||
vec![(
|
||||
alice_npk,
|
||||
alice_keys.vpk(),
|
||||
EphemeralPublicKey::from_scalar([10; 32]),
|
||||
)],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_0)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
@ -4566,11 +4616,7 @@ pub mod tests {
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![recipient_id],
|
||||
vec![],
|
||||
vec![(
|
||||
alice_npk,
|
||||
alice_keys.vpk(),
|
||||
EphemeralPublicKey::from_scalar([11; 32]),
|
||||
)],
|
||||
vec![(alice_npk, alice_keys.vpk(), alice_epk_1)],
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -523,7 +523,6 @@ mod tests {
|
||||
use nssa_core::{
|
||||
Commitment, InputAccountIdentity, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::EphemeralPublicKey,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -550,9 +549,7 @@ mod tests {
|
||||
// Attacker controls a private account.
|
||||
let attacker_keys = test_private_account_keys_1();
|
||||
let attacker_id = AccountId::for_regular_private_account(&attacker_keys.npk(), 0);
|
||||
let attacker_esk = [12_u8; 32];
|
||||
let attacker_ssk = SharedSecretKey::new(attacker_esk, &attacker_keys.vpk());
|
||||
let attacker_epk = EphemeralPublicKey::from_scalar(attacker_esk);
|
||||
let (attacker_ssk, attacker_epk) = SharedSecretKey::encapsulate(&attacker_keys.vpk());
|
||||
|
||||
let victim_id = AccountId::new([20_u8; 32]);
|
||||
let recipient_id = AccountId::new([42_u8; 32]);
|
||||
@ -674,7 +671,6 @@ mod tests {
|
||||
use nssa_core::{
|
||||
Commitment, InputAccountIdentity, SharedSecretKey,
|
||||
account::{Account, AccountWithMetadata},
|
||||
encryption::EphemeralPublicKey,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -704,9 +700,7 @@ mod tests {
|
||||
// Attacker controls a private account.
|
||||
let attacker_keys = test_private_account_keys_1();
|
||||
let attacker_id = AccountId::for_regular_private_account(&attacker_keys.npk(), 0);
|
||||
let attacker_esk = [12_u8; 32];
|
||||
let attacker_ssk = SharedSecretKey::new(attacker_esk, &attacker_keys.vpk());
|
||||
let attacker_epk = EphemeralPublicKey::from_scalar(attacker_esk);
|
||||
let (attacker_ssk, attacker_epk) = SharedSecretKey::encapsulate(&attacker_keys.vpk());
|
||||
|
||||
// Victim is a private account — not registered in public chain state.
|
||||
let victim_keys = test_private_account_keys_2();
|
||||
|
||||
@ -2,10 +2,10 @@ use common::PINATA_BASE58;
|
||||
use key_protocol::key_management::{
|
||||
KeyChain,
|
||||
key_tree::chain_index::ChainIndex,
|
||||
secret_holders::{PrivateKeyHolder, SecretSpendingKey},
|
||||
secret_holders::{PrivateKeyHolder, SecretSpendingKey, ViewingSecretKey},
|
||||
};
|
||||
use nssa::{Account, AccountId, Data, PrivateKey, PublicKey, V03State};
|
||||
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point};
|
||||
use nssa_core::{NullifierPublicKey, encryption::ViewingPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const PRIVATE_KEY_PUB_ACC_A: [u8; 32] = [
|
||||
@ -38,24 +38,24 @@ const NSK_PRIV_ACC_B: [u8; 32] = [
|
||||
23, 99, 9, 4, 177, 230, 125, 109, 91, 160, 30,
|
||||
];
|
||||
|
||||
const VSK_PRIV_ACC_A: [u8; 32] = [
|
||||
5, 85, 114, 119, 141, 187, 202, 170, 122, 253, 198, 81, 150, 8, 155, 21, 192, 65, 24, 124, 116,
|
||||
98, 110, 106, 137, 90, 165, 239, 80, 13, 222, 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_PRIV_ACC_B: [u8; 32] = [
|
||||
205, 32, 76, 251, 255, 236, 96, 119, 61, 111, 65, 100, 75, 218, 12, 22, 17, 170, 55, 226, 21,
|
||||
154, 161, 34, 208, 74, 27, 1, 119, 13, 88, 128,
|
||||
const VSK_R_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 VPK_PRIV_ACC_A: [u8; 33] = [
|
||||
2, 210, 206, 38, 213, 4, 182, 198, 220, 47, 93, 148, 61, 84, 148, 250, 158, 45, 8, 81, 48, 80,
|
||||
46, 230, 87, 210, 47, 204, 76, 58, 214, 167, 81,
|
||||
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 VPK_PRIV_ACC_B: [u8; 33] = [
|
||||
2, 79, 110, 46, 203, 29, 206, 205, 18, 86, 27, 189, 104, 103, 113, 181, 110, 53, 78, 172, 11,
|
||||
171, 190, 18, 126, 214, 81, 77, 192, 154, 58, 195, 238,
|
||||
const VSK_R_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] = [
|
||||
@ -136,20 +136,26 @@ pub fn initial_priv_accounts_private_keys() -> Vec<PrivateAccountPrivateInitialD
|
||||
secret_spending_key: SecretSpendingKey(SSK_PRIV_ACC_A),
|
||||
private_key_holder: PrivateKeyHolder {
|
||||
nullifier_secret_key: NSK_PRIV_ACC_A,
|
||||
viewing_secret_key: VSK_PRIV_ACC_A,
|
||||
viewing_secret_key: ViewingSecretKey {
|
||||
d: VSK_D_PRIV_ACC_A,
|
||||
r: VSK_R_PRIV_ACC_A,
|
||||
},
|
||||
},
|
||||
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_A),
|
||||
viewing_public_key: Secp256k1Point(VPK_PRIV_ACC_A.to_vec()),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_A, &VSK_R_PRIV_ACC_A),
|
||||
};
|
||||
|
||||
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: VSK_PRIV_ACC_B,
|
||||
viewing_secret_key: ViewingSecretKey {
|
||||
d: VSK_D_PRIV_ACC_B,
|
||||
r: VSK_R_PRIV_ACC_B,
|
||||
},
|
||||
},
|
||||
nullifier_public_key: NullifierPublicKey(NPK_PRIV_ACC_B),
|
||||
viewing_public_key: Secp256k1Point(VPK_PRIV_ACC_B.to_vec()),
|
||||
viewing_public_key: ViewingPublicKey::from_seed(&VSK_D_PRIV_ACC_B, &VSK_R_PRIV_ACC_B),
|
||||
};
|
||||
|
||||
vec![
|
||||
|
||||
@ -15,4 +15,3 @@ nssa_core = { workspace = true, features = ["host"] }
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
rand = { workspace = true }
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
//! Measures:
|
||||
//! - `KeyChain::new_os_random` (mnemonic → SSK → NSK/VSK + public keys)
|
||||
//! - `KeyChain::new_mnemonic` (same, but mnemonic exposed)
|
||||
//! - `SharedSecretKey::new` (Diffie-Hellman shared key derivation, the per-recipient cost)
|
||||
//! - `SharedSecretKey::encapsulate` (ML-KEM-768 encapsulation, the per-recipient cost)
|
||||
//! - `EncryptionScheme::encrypt` / `decrypt` (Account note encryption)
|
||||
//!
|
||||
//! Reports best-of-N wall time per operation. No live stack required.
|
||||
@ -24,10 +24,8 @@ use key_protocol::key_management::KeyChain;
|
||||
use nssa_core::{
|
||||
Commitment, EncryptionScheme, SharedSecretKey,
|
||||
account::{Account, AccountId},
|
||||
encryption::{EphemeralPublicKey, EphemeralSecretKey},
|
||||
program::PrivateAccountKind,
|
||||
};
|
||||
use rand::{RngCore as _, rngs::OsRng};
|
||||
use serde::Serialize;
|
||||
|
||||
const ITERS: usize = 100;
|
||||
@ -84,28 +82,23 @@ fn main() -> Result<()> {
|
||||
let (_kc, _mnemonic) = KeyChain::new_mnemonic("");
|
||||
}));
|
||||
|
||||
// SharedSecretKey: caller has ephemeral secret, recipient has VSK→VPK.
|
||||
// We bench the SENDER side: derive ephemeral pubkey, then SharedSecretKey::new(scalar, point).
|
||||
// SharedSecretKey: caller has recipient VPK; we bench the SENDER side —
|
||||
// ML-KEM-768 encapsulation (replaces the old ECDH scalar multiplication).
|
||||
let recipient_kc = KeyChain::new_os_random();
|
||||
let vpk = recipient_kc.viewing_public_key;
|
||||
results.push(time("SharedSecretKey::new (sender DH)", ITERS, || {
|
||||
let mut bytes = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
let esk: EphemeralSecretKey = bytes;
|
||||
let _epk = EphemeralPublicKey::from(&esk);
|
||||
let _ssk = SharedSecretKey::new(esk, &vpk);
|
||||
}));
|
||||
results.push(time(
|
||||
"SharedSecretKey::encapsulate (sender KEM)",
|
||||
ITERS,
|
||||
|| {
|
||||
let (_ssk, _epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
},
|
||||
));
|
||||
|
||||
// EncryptionScheme::encrypt / decrypt over a small Account note.
|
||||
let account = Account::default();
|
||||
let account_id = AccountId::new([7; 32]);
|
||||
let commitment = Commitment::new(&account_id, &account);
|
||||
let shared = {
|
||||
let mut bytes = [0_u8; 32];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
let esk: EphemeralSecretKey = bytes;
|
||||
SharedSecretKey::new(esk, &vpk)
|
||||
};
|
||||
let (shared, _epk) = SharedSecretKey::encapsulate(&vpk);
|
||||
let kind = PrivateAccountKind::Regular(0_u128);
|
||||
let output_index: u32 = 0;
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ pub unsafe extern "C" fn wallet_ffi_get_private_account_keys(
|
||||
// NPK is a 32-byte array
|
||||
let npk_bytes = key_chain.nullifier_public_key.0;
|
||||
|
||||
// VPK is a compressed secp256k1 point (33 bytes)
|
||||
// VPK is an ML-KEM-768 encapsulation key (1184 bytes)
|
||||
let vpk_bytes = key_chain.viewing_public_key.to_bytes();
|
||||
let vpk_len = vpk_bytes.len();
|
||||
let vpk_vec = vpk_bytes.to_vec();
|
||||
|
||||
@ -4,7 +4,6 @@ use core::slice;
|
||||
use std::{ffi::c_char, ptr};
|
||||
|
||||
use nssa::Data;
|
||||
use nssa_core::encryption::shared_key_derivation::Secp256k1Point;
|
||||
|
||||
use crate::error::WalletFfiError;
|
||||
|
||||
@ -72,9 +71,9 @@ impl Default for FfiAccount {
|
||||
pub struct FfiPrivateAccountKeys {
|
||||
/// Nullifier public key (32 bytes).
|
||||
pub nullifier_public_key: FfiBytes32,
|
||||
/// viewing public key (compressed secp256k1 point).
|
||||
/// Viewing public key (ML-KEM-768 encapsulation key, 1184 bytes).
|
||||
pub viewing_public_key: *const u8,
|
||||
/// Length of viewing public key (typically 33 bytes).
|
||||
/// Length of viewing public key (always 1184 bytes for ML-KEM-768).
|
||||
pub viewing_public_key_len: usize,
|
||||
}
|
||||
|
||||
@ -161,11 +160,11 @@ impl FfiPrivateAccountKeys {
|
||||
}
|
||||
|
||||
pub fn vpk(&self) -> Result<nssa_core::encryption::ViewingPublicKey, WalletFfiError> {
|
||||
if self.viewing_public_key_len == 33 {
|
||||
if self.viewing_public_key_len == 1184 {
|
||||
let slice = unsafe {
|
||||
slice::from_raw_parts(self.viewing_public_key, self.viewing_public_key_len)
|
||||
};
|
||||
Ok(Secp256k1Point(slice.to_vec()))
|
||||
Ok(nssa_core::encryption::ViewingPublicKey(slice.to_vec()))
|
||||
} else {
|
||||
Err(WalletFfiError::InvalidKeyValue)
|
||||
}
|
||||
|
||||
@ -135,11 +135,11 @@ typedef struct FfiPrivateAccountKeys {
|
||||
*/
|
||||
struct FfiBytes32 nullifier_public_key;
|
||||
/**
|
||||
* viewing public key (compressed secp256k1 point).
|
||||
* Viewing public key (ML-KEM-768 encapsulation key, 1184 bytes).
|
||||
*/
|
||||
const uint8_t *viewing_public_key;
|
||||
/**
|
||||
* Length of viewing public key (typically 33 bytes).
|
||||
* Length of viewing public key (always 1184 bytes for ML-KEM-768).
|
||||
*/
|
||||
uintptr_t viewing_public_key_len;
|
||||
} FfiPrivateAccountKeys;
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
use anyhow::{Context as _, Result};
|
||||
use clap::Subcommand;
|
||||
use key_protocol::key_management::group_key_holder::{GroupKeyHolder, SealingPublicKey};
|
||||
use key_protocol::key_management::{
|
||||
group_key_holder::{GroupKeyHolder, SealingPublicKey},
|
||||
secret_holders::ViewingSecretKey,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
WalletCore,
|
||||
@ -149,9 +152,13 @@ impl WalletSubcommand for GroupSubcommand {
|
||||
anyhow::bail!("Sealing key already exists. Each wallet has one sealing key.");
|
||||
}
|
||||
|
||||
let mut secret: nssa_core::encryption::Scalar = [0_u8; 32];
|
||||
rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut secret);
|
||||
let public_key = SealingPublicKey::from_scalar(secret);
|
||||
let mut d = [0_u8; 32];
|
||||
let mut r = [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;
|
||||
let public_key = SealingPublicKey::from_bytes(ek_bytes);
|
||||
|
||||
wallet_core.set_sealing_secret_key(secret);
|
||||
wallet_core.store_persistent_data()?;
|
||||
|
||||
@ -340,10 +340,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
let to_npk = nssa_core::NullifierPublicKey(to_npk);
|
||||
|
||||
let to_vpk_res = hex::decode(to_vpk)?;
|
||||
let mut to_vpk = [0_u8; 33];
|
||||
to_vpk.copy_from_slice(&to_vpk_res);
|
||||
let to_vpk =
|
||||
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_vpk.to_vec());
|
||||
let to_vpk = nssa_core::encryption::ViewingPublicKey(to_vpk_res);
|
||||
|
||||
let (tx_hash, [secret_from, _]) = NativeTokenTransfer(wallet_core)
|
||||
.send_private_transfer_to_outer_account(
|
||||
@ -417,10 +414,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
let to_npk = nssa_core::NullifierPublicKey(to_npk);
|
||||
|
||||
let to_vpk_res = hex::decode(to_vpk)?;
|
||||
let mut to_vpk = [0_u8; 33];
|
||||
to_vpk.copy_from_slice(&to_vpk_res);
|
||||
let to_vpk =
|
||||
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_vpk.to_vec());
|
||||
let to_vpk = nssa_core::encryption::ViewingPublicKey(to_vpk_res);
|
||||
|
||||
let (tx_hash, _) = NativeTokenTransfer(wallet_core)
|
||||
.send_shielded_transfer_to_outer_account(
|
||||
|
||||
@ -765,11 +765,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
let recipient_npk = nssa_core::NullifierPublicKey(recipient_npk);
|
||||
|
||||
let recipient_vpk_res = hex::decode(recipient_vpk)?;
|
||||
let mut recipient_vpk = [0_u8; 33];
|
||||
recipient_vpk.copy_from_slice(&recipient_vpk_res);
|
||||
let recipient_vpk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
recipient_vpk.to_vec(),
|
||||
);
|
||||
let recipient_vpk = nssa_core::encryption::ViewingPublicKey(recipient_vpk_res);
|
||||
|
||||
let (tx_hash, [secret_sender, _]) = Token(wallet_core)
|
||||
.send_transfer_transaction_private_foreign_account(
|
||||
@ -877,11 +873,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
let holder_npk = nssa_core::NullifierPublicKey(holder_npk);
|
||||
|
||||
let holder_vpk_res = hex::decode(holder_vpk)?;
|
||||
let mut holder_vpk = [0_u8; 33];
|
||||
holder_vpk.copy_from_slice(&holder_vpk_res);
|
||||
let holder_vpk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
holder_vpk.to_vec(),
|
||||
);
|
||||
let holder_vpk = nssa_core::encryption::ViewingPublicKey(holder_vpk_res);
|
||||
|
||||
let (tx_hash, [secret_definition, _]) = Token(wallet_core)
|
||||
.send_mint_transaction_private_foreign_account(
|
||||
@ -1033,11 +1025,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
let recipient_npk = nssa_core::NullifierPublicKey(recipient_npk);
|
||||
|
||||
let recipient_vpk_res = hex::decode(recipient_vpk)?;
|
||||
let mut recipient_vpk = [0_u8; 33];
|
||||
recipient_vpk.copy_from_slice(&recipient_vpk_res);
|
||||
let recipient_vpk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
recipient_vpk.to_vec(),
|
||||
);
|
||||
let recipient_vpk = nssa_core::encryption::ViewingPublicKey(recipient_vpk_res);
|
||||
|
||||
let (tx_hash, _) = Token(wallet_core)
|
||||
.send_transfer_transaction_shielded_foreign_account(
|
||||
@ -1164,11 +1152,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
let holder_npk = nssa_core::NullifierPublicKey(holder_npk);
|
||||
|
||||
let holder_vpk_res = hex::decode(holder_vpk)?;
|
||||
let mut holder_vpk = [0_u8; 33];
|
||||
holder_vpk.copy_from_slice(&holder_vpk_res);
|
||||
let holder_vpk = nssa_core::encryption::shared_key_derivation::Secp256k1Point(
|
||||
holder_vpk.to_vec(),
|
||||
);
|
||||
let holder_vpk = nssa_core::encryption::ViewingPublicKey(holder_vpk_res);
|
||||
|
||||
let (tx_hash, _) = Token(wallet_core)
|
||||
.send_mint_transaction_shielded_foreign_account(
|
||||
|
||||
@ -266,7 +266,10 @@ impl WalletCore {
|
||||
}
|
||||
|
||||
/// Set the wallet's dedicated sealing secret key.
|
||||
pub const fn set_sealing_secret_key(&mut self, key: nssa_core::encryption::Scalar) {
|
||||
pub const fn set_sealing_secret_key(
|
||||
&mut self,
|
||||
key: key_protocol::key_management::secret_holders::ViewingSecretKey,
|
||||
) {
|
||||
self.storage.key_chain_mut().set_sealing_secret_key(key);
|
||||
}
|
||||
|
||||
@ -766,7 +769,8 @@ impl WalletCore {
|
||||
continue;
|
||||
}
|
||||
|
||||
let shared_secret = SharedSecretKey::new(vsk, &encrypted_data.epk);
|
||||
let shared_secret =
|
||||
SharedSecretKey::decapsulate(&encrypted_data.epk, &vsk.d, &vsk.r);
|
||||
let commitment = &tx.message.new_commitments[ciph_id];
|
||||
|
||||
if let Some((_kind, new_acc)) = nssa_core::EncryptionScheme::decrypt(
|
||||
|
||||
@ -121,9 +121,9 @@ impl AccountManager {
|
||||
} => {
|
||||
let acc = nssa_core::account::Account::default();
|
||||
let auth_acc = AccountWithMetadata::new(acc, false, (&npk, identifier));
|
||||
let eph_holder = EphemeralKeyHolder::new(&npk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender(&vpk);
|
||||
let epk = eph_holder.generate_ephemeral_public_key();
|
||||
let eph_holder = EphemeralKeyHolder::new(&vpk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender();
|
||||
let epk = eph_holder.ephemeral_public_key().clone();
|
||||
let pre = AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
@ -150,9 +150,9 @@ impl AccountManager {
|
||||
} => {
|
||||
let acc = nssa_core::account::Account::default();
|
||||
let auth_acc = AccountWithMetadata::new(acc, false, account_id);
|
||||
let eph_holder = EphemeralKeyHolder::new(&npk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender(&vpk);
|
||||
let epk = eph_holder.generate_ephemeral_public_key();
|
||||
let eph_holder = EphemeralKeyHolder::new(&vpk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender();
|
||||
let epk = eph_holder.ephemeral_public_key().clone();
|
||||
let pre = AccountPreparedData {
|
||||
nsk: None,
|
||||
npk,
|
||||
@ -348,9 +348,9 @@ async fn private_key_tree_acc_preparation(
|
||||
// support from that in the wallet.
|
||||
let sender_pre = AccountWithMetadata::new(from_acc.account.clone(), true, account_id);
|
||||
|
||||
let eph_holder = EphemeralKeyHolder::new(&from_npk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender(&from_vpk);
|
||||
let epk = eph_holder.generate_ephemeral_public_key();
|
||||
let eph_holder = EphemeralKeyHolder::new(&from_vpk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender();
|
||||
let epk = eph_holder.ephemeral_public_key().clone();
|
||||
|
||||
Ok(AccountPreparedData {
|
||||
nsk: Some(nsk),
|
||||
@ -388,9 +388,10 @@ async fn private_shared_acc_preparation(
|
||||
.await
|
||||
.unwrap_or(None);
|
||||
|
||||
let eph_holder = EphemeralKeyHolder::new(&npk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender(&vpk);
|
||||
let epk = eph_holder.generate_ephemeral_public_key();
|
||||
let eph_holder = EphemeralKeyHolder::new(&vpk);
|
||||
let ssk = eph_holder.calculate_shared_secret_sender();
|
||||
let epk = eph_holder.ephemeral_public_key().clone();
|
||||
|
||||
Ok(AccountPreparedData {
|
||||
nsk: Some(nsk),
|
||||
npk,
|
||||
@ -413,7 +414,7 @@ mod tests {
|
||||
let acc = PrivacyPreservingAccount::PrivateShared {
|
||||
nsk: [0; 32],
|
||||
npk: NullifierPublicKey([1; 32]),
|
||||
vpk: ViewingPublicKey::from_scalar([2; 32]),
|
||||
vpk: ViewingPublicKey::from_seed(&[2_u8; 32], &[3_u8; 32]),
|
||||
identifier: 42,
|
||||
};
|
||||
assert!(acc.is_private());
|
||||
|
||||
@ -6,7 +6,7 @@ use key_protocol::key_management::{
|
||||
KeyChain,
|
||||
group_key_holder::GroupKeyHolder,
|
||||
key_tree::{KeyTreePrivate, KeyTreePublic, chain_index::ChainIndex, traits::KeyTreeNode as _},
|
||||
secret_holders::SeedHolder,
|
||||
secret_holders::{SeedHolder, ViewingSecretKey},
|
||||
};
|
||||
use log::{debug, warn};
|
||||
use nssa::{Account, AccountId};
|
||||
@ -79,7 +79,7 @@ pub struct UserKeyChain {
|
||||
/// Dedicated sealing secret key for GMS distribution. Generated once via
|
||||
/// `wallet group new-sealing-key`. The corresponding public key is shared with
|
||||
/// group members so they can seal GMS for this wallet.
|
||||
sealing_secret_key: Option<nssa_core::encryption::Scalar>,
|
||||
sealing_secret_key: Option<ViewingSecretKey>,
|
||||
}
|
||||
|
||||
impl UserKeyChain {
|
||||
@ -509,12 +509,12 @@ impl UserKeyChain {
|
||||
|
||||
/// Returns the sealing secret key for GMS distribution, if it exists.
|
||||
#[must_use]
|
||||
pub const fn sealing_secret_key(&self) -> Option<nssa_core::encryption::Scalar> {
|
||||
self.sealing_secret_key
|
||||
pub const fn sealing_secret_key(&self) -> Option<&ViewingSecretKey> {
|
||||
self.sealing_secret_key.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the sealing secret key for GMS distribution.
|
||||
pub const fn set_sealing_secret_key(&mut self, key: nssa_core::encryption::Scalar) {
|
||||
pub const fn set_sealing_secret_key(&mut self, key: ViewingSecretKey) {
|
||||
self.sealing_secret_key = Some(key);
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ impl UserKeyChain {
|
||||
|
||||
KeyChainPersistentData {
|
||||
accounts,
|
||||
sealing_secret_key: *sealing_secret_key,
|
||||
sealing_secret_key: sealing_secret_key.clone(),
|
||||
group_key_holders: group_key_holders.clone(),
|
||||
shared_private_accounts: shared_private_accounts.clone(),
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ use key_protocol::key_management::{
|
||||
key_tree::{
|
||||
chain_index::ChainIndex, keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic,
|
||||
},
|
||||
secret_holders::ViewingSecretKey,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use testnet_initial_state::{PrivateAccountPrivateInitialData, PublicAccountPrivateInitialData};
|
||||
@ -26,7 +27,7 @@ pub struct PersistentStorage {
|
||||
pub struct KeyChainPersistentData {
|
||||
pub accounts: Vec<PersistentAccountData>,
|
||||
#[serde(default)]
|
||||
pub sealing_secret_key: Option<nssa_core::encryption::Scalar>,
|
||||
pub sealing_secret_key: Option<ViewingSecretKey>,
|
||||
#[serde(default)]
|
||||
pub group_key_holders: BTreeMap<Label, GroupKeyHolder>,
|
||||
#[serde(default)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user