Rebased to main

This commit is contained in:
Marvin Jones 2026-06-03 12:39:02 -04:00
parent 3296ff0266
commit b198e536eb
61 changed files with 183 additions and 435 deletions

219
Cargo.lock generated
View File

@ -1118,7 +1118,7 @@ version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"cexpr",
"clang-sys",
"itertools 0.13.0",
@ -1126,7 +1126,7 @@ dependencies = [
"quote",
"regex",
"rustc-hash",
"shlex",
"shlex 1.3.0",
"syn 2.0.117",
]
@ -1164,9 +1164,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.11.1"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
[[package]]
name = "bitvec"
@ -1221,7 +1221,7 @@ checksum = "ee04c4c84f1f811b017f2fbb7dd8815c976e7ca98593de9c1e2afad0f636bff4"
dependencies = [
"async-stream",
"base64",
"bitflags 2.11.1",
"bitflags 2.12.1",
"bollard-buildkit-proto",
"bollard-stubs",
"bytes",
@ -1447,14 +1447,14 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.62"
version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
"shlex 2.0.1",
]
[[package]]
@ -1619,15 +1619,9 @@ dependencies = [
[[package]]
name = "cmov"
<<<<<<< HEAD
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
=======
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
[[package]]
name = "cobs"
@ -2045,23 +2039,10 @@ dependencies = [
name = "crypto_primitives_bench"
version = "0.1.0"
dependencies = [
"anyhow",
"criterion",
"key_protocol",
<<<<<<< HEAD
"lee_core",
"rand 0.8.6",
=======
"nssa_core",
<<<<<<< HEAD
"serde",
"serde_json",
>>>>>>> 8492f995 (ci fixes)
=======
"rand 0.8.5",
"serde",
"serde_json",
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
]
[[package]]
@ -2222,7 +2203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090"
dependencies = [
"data-encoding",
"syn 2.0.117",
"syn 1.0.109",
]
[[package]]
@ -2376,11 +2357,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
<<<<<<< HEAD
"crypto-common 0.2.2",
=======
"crypto-common 0.2.1",
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
]
[[package]]
@ -2410,7 +2387,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@ -3103,7 +3080,7 @@ version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bafc7e33650ab9f05dcc16325f05d56b8d10393114e31a19a353b86fa60cfe7"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"cfg-if",
"log",
"managed",
@ -3134,9 +3111,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "1.4.1"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649"
checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351"
dependencies = [
"rustversion",
"serde_core",
@ -3639,9 +3616,9 @@ dependencies = [
[[package]]
name = "hyper"
version = "1.10.0"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc"
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
dependencies = [
"atomic-waker",
"bytes",
@ -3737,12 +3714,8 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
<<<<<<< HEAD
"socket2 0.6.4",
"system-configuration 0.7.0",
=======
"socket2 0.5.10",
>>>>>>> 8492f995 (ci fixes)
"tokio",
"tower-service",
"tracing",
@ -4297,9 +4270,9 @@ dependencies = [
[[package]]
name = "jiff"
version = "0.2.27"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "392c70591e8749fe235ddaf513e6f58b26bce3dcc16524cecc8936f75afa161e"
checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102"
dependencies = [
"jiff-static",
"log",
@ -4310,9 +4283,9 @@ dependencies = [
[[package]]
name = "jiff-static"
version = "0.2.27"
version = "0.2.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b605b0c050d845fc355bb11eb3f9a8deddc218ea60c76e61aa1f2adfb2c96a"
checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47"
dependencies = [
"proc-macro2",
"quote",
@ -4597,11 +4570,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edd"
dependencies = [
<<<<<<< HEAD
"crypto-common 0.2.2",
=======
"crypto-common 0.2.1",
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
"rand_core 0.10.1",
]
@ -4619,17 +4588,10 @@ dependencies = [
"hmac-sha512",
"itertools 0.14.0",
"k256",
<<<<<<< HEAD
"lee",
"lee_core",
"ml-kem",
"rand 0.8.6",
=======
"ml-kem",
"nssa",
"nssa_core",
"rand 0.8.5",
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
"serde",
"sha2",
"thiserror 2.0.18",
@ -4737,7 +4699,6 @@ dependencies = [
"bytemuck",
"bytesize",
"chacha20",
"k256",
"ml-kem",
"risc0-zkvm",
"serde",
@ -5439,9 +5400,9 @@ dependencies = [
[[package]]
name = "libz-sys"
version = "1.1.28"
version = "1.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22"
checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9"
dependencies = [
"cc",
"pkg-config",
@ -5483,9 +5444,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.30"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f"
[[package]]
name = "logos-blockchain-blend-crypto"
@ -5531,7 +5492,7 @@ version = "0.1.2"
source = "git+https://github.com/logos-blockchain/logos-blockchain.git?rev=dd055cc1ef7c130f710a52a190edd97bc7b0f71b#dd055cc1ef7c130f710a52a190edd97bc7b0f71b"
dependencies = [
"ed25519-dalek",
"generic-array 1.4.1",
"generic-array 1.4.3",
"hex",
"logos-blockchain-blend-crypto",
"logos-blockchain-groth16",
@ -5709,7 +5670,7 @@ dependencies = [
"ark-ff 0.4.2",
"ark-groth16 0.4.0",
"ark-serialize 0.4.2",
"generic-array 1.4.1",
"generic-array 1.4.3",
"hex",
"num-bigint 0.4.6",
"serde",
@ -5745,7 +5706,7 @@ dependencies = [
"async-trait",
"bytes",
"ed25519-dalek",
"generic-array 1.4.1",
"generic-array 1.4.3",
"hex",
"logos-blockchain-groth16",
"logos-blockchain-key-management-system-macros",
@ -6369,7 +6330,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"block",
"core-graphics-types",
"foreign-types 0.5.0",
@ -6637,7 +6598,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"libc",
"log",
"netlink-packet-core 0.8.1",
@ -6712,7 +6673,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"cfg-if",
"cfg_aliases",
"libc",
@ -6744,60 +6705,12 @@ dependencies = [
]
[[package]]
<<<<<<< HEAD
=======
name = "nssa"
version = "0.1.0"
dependencies = [
"anyhow",
"authenticated_transfer_core",
"borsh",
"clock_core",
"env_logger",
"faucet_core",
"hex",
"hex-literal 1.1.0",
"k256",
"log",
"nssa_core",
"rand 0.8.5",
"risc0-binfmt",
"risc0-build",
"risc0-zkvm",
"serde",
"serde_with",
"sha2",
"test-case",
"test_program_methods",
"thiserror 2.0.18",
"token_core",
]
[[package]]
name = "nssa_core"
version = "0.1.0"
dependencies = [
"base58",
"borsh",
"bytemuck",
"bytesize",
"chacha20",
"ml-kem",
"risc0-zkvm",
"serde",
"serde_json",
"serde_with",
"thiserror 2.0.18",
]
[[package]]
>>>>>>> 8492f995 (ci fixes)
name = "nu-ansi-term"
version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@ -7017,7 +6930,7 @@ version = "0.10.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"cfg-if",
"foreign-types 0.3.2",
"libc",
@ -7639,7 +7552,7 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"num-traits",
"rand 0.9.4",
"rand_chacha 0.9.0",
@ -7824,15 +7737,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
<<<<<<< HEAD
<<<<<<< HEAD
"socket2 0.6.4",
=======
"socket2 0.5.10",
>>>>>>> 8492f995 (ci fixes)
=======
"socket2 0.6.3",
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
"thiserror 2.0.18",
"tokio",
"tracing",
@ -7869,19 +7774,9 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
<<<<<<< HEAD
<<<<<<< HEAD
"socket2 0.6.4",
=======
"socket2 0.5.10",
>>>>>>> 8492f995 (ci fixes)
"tracing",
"windows-sys 0.60.2",
=======
"socket2 0.6.3",
"tracing",
"windows-sys 0.59.0",
>>>>>>> ee3cfb6e (fix clippy errors from main merge)
]
[[package]]
@ -8117,7 +8012,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
]
[[package]]
@ -8618,9 +8513,9 @@ checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
[[package]]
name = "rpassword"
version = "7.5.3"
version = "7.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835a57a69104632d64deb0df2e09a69945cd7a6eab4070fc9b1d7e50cf6c3edc"
checksum = "2da316a15f47e3d053de9cb2c439650bd8fa4aaeb9365f2e5f27f492ff73c196"
dependencies = [
"libc",
"rtoolbox",
@ -8788,7 +8683,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"errno",
"libc",
"linux-raw-sys",
@ -8812,9 +8707,9 @@ dependencies = [
[[package]]
name = "rustls-native-certs"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
dependencies = [
"openssl-probe",
"rustls-pki-types",
@ -9012,7 +8907,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"core-foundation 0.10.1",
"core-foundation-sys",
"libc",
@ -9422,6 +9317,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
@ -9706,7 +9607,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"core-foundation 0.9.4",
"system-configuration-sys",
]
@ -9717,7 +9618,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"core-foundation 0.9.4",
"system-configuration-sys",
]
@ -10334,7 +10235,7 @@ version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"bytes",
"futures-core",
"futures-util",
@ -10597,9 +10498,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.20.0"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "typetag"
@ -10666,9 +10567,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
version = "1.13.2"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unicode-width"
@ -10824,9 +10725,9 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.23.1"
version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
dependencies = [
"getrandom 0.4.2",
"js-sys",
@ -11123,7 +11024,7 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.12.1",
"hashbrown 0.15.5",
"indexmap 2.14.0",
"semver",
@ -11657,7 +11558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.11.1",
"bitflags 2.12.1",
"indexmap 2.14.0",
"log",
"serde",
@ -11813,18 +11714,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.49"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bce33a6288fa3f072a8c2c7d0f2fdbb90e28298f0135c1f99b96c3db2efcc60b"
checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.49"
version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd425244944f4ab65ccff928e7323354c5a018c75838362fdce749dfad2ee1e"
checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
dependencies = [
"proc-macro2",
"quote",

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.

View File

@ -1,169 +0,0 @@
# LEE v0.3 specifications for Key Agreement
## LEE v0.3 basic types and constants
```rust
/// The ML-KEM-768 KEM ciphertext produced during encapsulation (1088 bytes) of a message.
/// `EphemeralPublicKey` is used to not confuse the ciphertext of an encrypted private account.
type EphemeralPublicKey = [u8; 1088];
/// Private account Viewing Public Key is a ML-KEM-768 encapsulation key (1184 bytes).
type ViewingPublicKey = [u8; 1184];
/// The ML-KEM-768 shared secret (32 bytes).
type SharedSecretKey = [u8; 32];
struct EncryptedAccountData {
ciphertext: Ciphertext,
epk: EphemeralPublicKey,
view_tag: u8, // 1-byte view tag
}
```
#### Key agreement and shared secret
When creating a private account output, the sender uses the recipient's viewing public key to encapsulate a random message that is used to establish a shared secret between the sender and recipient:
- Sender: $(\mathsf{ss},\, \mathsf{epk}) = \mathsf{encapsulate}(\mathsf{vpk\_recipient})$. The 1088-byte ciphertext `epk` is included in the transaction as the `EphemeralPublicKey` field.
- Receiver: $\mathsf{ss} = \mathsf{decapsulate}(\mathsf{epk},\, vsk.d,\, vsk.z)$
where `vpk` is the receiver's `ViewingPublicKey` and `(vsk.d, vsk.z)` are the two 32-byte halves of the receiver's `ViewingSecretKey`.
#### KDF
```rust
fn kdf(
shared_secret: &SharedSecretKey, // 32-byte output of the KEM
commitment: &Commitment, // 32-byte output commitment
output_index: u32, // index of this output within the tx (LE)
) -> [u8; 32] {
let mut bytes = Vec::new();
bytes.extend_from_slice(b"NSSA/v0.2/KDF-SHA256/");
bytes.extend_from_slice(&shared_secret.0);
bytes.extend_from_slice(&commitment.to_byte_array());
bytes.extend_from_slice(&output_index.to_le_bytes());
sha256(bytes)
}
```
### Circuit input
```rust
pub enum InputAccountIdentity {
/// Public account. The guest reads pre/post state from program_outputs and emits no
/// commitment, ciphertext, or nullifier.
Public,
/// Initialization of a standalone private account the caller owns.
/// Pre-state must be Account::default().
/// AccountId = AccountId::from_private(npk(nsk), identifier).
PrivateAuthorizedInit {
ssk: SharedSecretKey,
nsk: NullifierSecretKey,
identifier: Identifier,
},
/// Update of an existing standalone private account the caller owns.
/// Membership proof for the current on-chain commitment is required.
PrivateAuthorizedUpdate {
ssk: SharedSecretKey,
nsk: NullifierSecretKey,
membership_proof: MembershipProof,
identifier: Identifier,
},
/// Initialization of a standalone private account the caller does not own
/// (e.g. a recipient who does not yet exist on chain). No nsk, no membership proof.
PrivateUnauthorized {
npk: NullifierPublicKey,
ssk: SharedSecretKey,
identifier: Identifier,
},
/// Initialization of a private PDA.
/// Authorization comes via Claim::Pda(seed) or the caller's pda_seeds.
/// The identifier diversifies the PDA within the (program_id, seed, npk) family.
PrivatePdaInit {
npk: NullifierPublicKey,
ssk: SharedSecretKey,
identifier: Identifier,
/// When `Some((seed, authority_program_id))`, the circuit binds this position via
/// `AccountId::for_private_pda(authority_program_id, seed, npk, identifier) == pre_state.account_id`
/// rather than requiring a `Claim::Pda` or caller `pda_seeds`. The `pre_state` must
/// have `is_authorized == false`.
seed: Option<(PdaSeed, ProgramId)>,
},
/// Update of an existing private PDA. npk is derived from nsk.
/// Membership proof is required.
PrivatePdaUpdate {
ssk: SharedSecretKey,
nsk: NullifierSecretKey,
membership_proof: MembershipProof,
identifier: Identifier,
/// When `Some((seed, authority_program_id))`, the circuit binds this position via
/// `AccountId::for_private_pda(authority_program_id, seed, npk, identifier) == pre_state.account_id`
/// rather than requiring a caller `pda_seeds`. The `pre_state` must have
/// `is_authorized == false`.
seed: Option<(PdaSeed, ProgramId)>,
},
}
```
The `ssk` field carries the **shared secret key** — the 32-byte shared secret used to encrypt the post-state. Note that the key protocol uses `ssk` for "spending secret key" (the master key that derives `nsk` and `vsk`); here `ssk` means the per-output KEM shared secret. It is established via ML-KEM-768:
- Sender: `(ssk, epk) = encapsulate(vpk)`
- Receiver: `ssk = decapsulate(epk, vsk.d, vsk.z)`
where `epk` is the ML-KEM-768 ciphertext (1088 bytes) stored as the `EphemeralPublicKey`, `vpk` is the recipient's `ViewingPublicKey` (1184 bytes), and `(vsk.d, vsk.z)` are the 32-byte seed halves of the recipient's `ViewingSecretKey`.
## Encrypted private account discovery and tagging
### Ephemeral view tags
Each private account output includes a 1-byte view tag to allow wallets to quickly filter outputs before attempting decryption:
$$\mathsf{ViewTag} = \mathsf{SHA256}(\text{"/LEE/v0.3/ViewTag/"} \;||\; \mathsf{Npk} \;||\; \mathsf{Vpk})[0]$$
where `Npk` is the 32-byte nullifier public key and `Vpk` is the 1184 byte `ViewingPublicKey` of the recipient. On average only 1 in 256 outputs will pass this filter for a given account, avoiding expensive ML-KEM decapsulation on irrelevant outputs.
### Private account discovery with viewing keys
1. For each encrypted output, compute the expected view tag from `(Npk, Vpk)`. Skip if it does not match.
2. Decapsulate using ML-KEM-768: `ss = decapsulate(epk, vsk.d, vsk.z)`.
3. Run `kdf(ss, commitment, output_index)` to derive the symmetric key.
4. Decrypt the ciphertext with ChaCha20.
5. Parse the 81-byte header to recover `PrivateAccountKind`.
6. Parse the remaining bytes to recover the `Account`.
7. Recompute the account ID from the kind and verify that `Commitment::new(account_id, account)` equals the on-chain commitment. Discard on mismatch (false positive).
```rust
fn private_account_discovery(
tx: &PrivacyPreservingTransaction,
vsk: &ViewingSecretKey,
npk: &NullifierPublicKey,
vpk: &ViewingPublicKey,
) -> Vec<(PrivateAccountKind, Account)> {
let expected_tag = EncryptedAccountData::compute_view_tag(npk, vpk);
let mut discovered = Vec::new();
for (output_index, (encrypted_account, commitment)) in tx.message.encrypted_private_post_states
.iter()
.zip(&tx.message.new_commitments)
.enumerate()
{
if encrypted_account.view_tag != expected_tag {
continue;
}
let ss = SharedSecretKey::decapsulate(&encrypted_account.epk, &vsk.d, &vsk.z);
if let Some((kind, account)) = EncryptionScheme::decrypt(
&encrypted_account.ciphertext, &ss, commitment, output_index as u32
) {
let account_id = AccountId::for_private_account(npk, &kind);
if Commitment::new(&account_id, &account) == *commitment {
discovered.push((kind, account));
}
}
}
discovered
}
```

View File

@ -11,7 +11,8 @@ use lee::{
privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program,
};
use lee_core::{
InputAccountIdentity, NullifierPublicKey, account::AccountWithMetadata,
InputAccountIdentity, NullifierPublicKey,
account::AccountWithMetadata,
encryption::{EphemeralPublicKey, ViewingPublicKey},
};
use log::info;
@ -664,7 +665,7 @@ async fn ppt_cant_chain_call_faucet() -> Result<()> {
let auth_transfer_program_id = Program::authenticated_transfer_program().id();
let nsk: lee_core::NullifierSecretKey = [3; 32];
let npk = NullifierPublicKey::from(&nsk);
let _vpk = MlKem768EncapsulationKey::from_bytes(vec![4_u8; 1184]).unwrap();
let _vpk = ViewingPublicKey::from_bytes(vec![4_u8; 1184]).unwrap();
let ssk = SharedSecretKey([55_u8; 32]);
let _epk = EphemeralPublicKey(vec![55_u8; 1088]);
let attacker_vault_id = {

View File

@ -30,6 +30,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
to: Some(public_mention(ctx.ctx().existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
amount: 100,
to_identifier: Some(0),
});
@ -67,6 +68,7 @@ fn indexer_ffi_state_consistency() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
amount: 100,
to_identifier: Some(0),
});

View File

@ -46,6 +46,7 @@ fn indexer_ffi_state_consistency_with_labels() -> Result<()> {
to: Some(to_label.into()),
to_npk: None,
to_vpk: None,
to_keys: None,
amount: 100,
to_identifier: Some(0),
});

View File

@ -25,6 +25,7 @@ async fn indexer_state_consistency() -> Result<()> {
to: Some(public_mention(ctx.existing_public_accounts()[1])),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});
@ -60,6 +61,7 @@ async fn indexer_state_consistency() -> Result<()> {
to: Some(private_mention(to)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});

View File

@ -43,6 +43,7 @@ async fn indexer_state_consistency_with_labels() -> Result<()> {
to: Some(CliAccountMention::Label(to_label)),
to_npk: None,
to_vpk: None,
to_keys: None,
to_identifier: Some(0),
amount: 100,
});

View File

@ -108,7 +108,7 @@ async fn group_invite_join_key_agreement() -> Result<()> {
.sealing_secret_key()
.context("Sealing key not found")?;
let sealing_pk = key_protocol::key_management::group_key_holder::SealingPublicKey::from_bytes(
nssa_core::encryption::ViewingPublicKey::from_seed(&sealing_sk.d, &sealing_sk.z)
lee_core::encryption::ViewingPublicKey::from_seed(&sealing_sk.d, &sealing_sk.z)
.to_bytes()
.to_vec(),
);

View File

@ -1,7 +1,7 @@
use aes_gcm::{Aes256Gcm, KeyInit as _, aead::Aead as _};
use lee_core::{
SharedSecretKey,
encryption::{EphemeralPublicKey, MlKem768EncapsulationKey},
encryption::{EphemeralPublicKey, ViewingPublicKey},
program::{PdaSeed, ProgramId},
};
use rand::{RngCore as _, rngs::OsRng};
@ -156,7 +156,7 @@ impl GroupKeyHolder {
/// different ciphertexts.
#[must_use]
pub fn seal_for(&self, recipient_key: &SealingPublicKey) -> Vec<u8> {
let sealing_key = MlKem768EncapsulationKey::from_bytes(recipient_key.0.clone())
let sealing_key = ViewingPublicKey::from_bytes(recipient_key.0.clone())
.expect("key_protocol::group_key_holder::GroupKeyHolder::seal_for: SealingPublicKey must be a valid ML-KEM-768 encapsulation key");
let (shared, kem_ct) = SharedSecretKey::encapsulate(&sealing_key);
let aes_key = Self::seal_kdf(&shared);
@ -324,7 +324,7 @@ mod tests {
/// Pins the end-to-end derivation for a fixed (GMS, `ProgramId`, `PdaSeed`). Any change
/// to `secret_spending_key_for_pda`, the `PrivateKeyHolder` nsk/npk chain, or the
/// `AccountId::for_private_pda` formula breaks this test. Mirrors the pinned-value
/// pattern from `for_private_pda_matches_pinned_value` in `nssa_core`.
/// pattern from `for_private_pda_matches_pinned_value` in `lee_core`.
#[test]
fn pinned_end_to_end_derivation_for_private_pda() {
use lee_core::{account::AccountId, program::ProgramId};

View File

@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use k256::{Scalar, elliptic_curve::PrimeField as _};
use lee_core::{NullifierPublicKey, PrivateAccountKind, encryption::ViewingPublicKey};
use serde::{Deserialize, Serialize};
use sha2::Digest as _;
@ -38,7 +37,7 @@ impl ChildKeysPrivate {
let vsk = ssk.generate_viewing_secret_seed_key(None);
let npk = NullifierPublicKey::from(&nsk);
let vpk = MlKem768EncapsulationKey::from(&vsk);
let vpk = ViewingPublicKey::from(&vsk);
Self {
value: (
@ -92,7 +91,7 @@ impl ChildKeysPrivate {
let vsk = ssk.generate_viewing_secret_seed_key(Some(cci));
let npk = NullifierPublicKey::from(&nsk);
let vpk = MlKem768EncapsulationKey::from(&vsk);
let vpk = ViewingPublicKey::from(&vsk);
Self {
value: (
@ -133,7 +132,7 @@ impl KeyTreeNode for ChildKeysPrivate {
#[cfg(test)]
mod tests {
use lee_core::{NullifierPublicKey, NullifierSecretKey};
use lee_core::NullifierSecretKey;
use super::*;
use crate::key_management::{self, secret_holders::ViewingSecretKey};
@ -164,7 +163,7 @@ mod tests {
34, 234, 19, 222, 2, 22, 12, 163, 252, 88, 11, 0, 163,
];
let expected_npk: NullifierPublicKey = lee_core::NullifierPublicKey([
let expected_npk = lee_core::NullifierPublicKey([
7, 123, 125, 191, 233, 183, 201, 4, 20, 214, 155, 210, 45, 234, 27, 240, 194, 111, 97,
247, 155, 113, 122, 246, 192, 0, 70, 61, 76, 71, 70, 2,
]);
@ -265,99 +264,99 @@ mod tests {
let child_node = ChildKeysPrivate::nth_child(&root_node, 42_u32);
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,
151, 183, 113, 151, 215, 187, 207, 64, 197, 182, 207, 32, 5, 49, 180, 98, 119, 14, 248,
175, 39, 100, 47, 109, 148, 173, 217, 253, 159, 234, 209, 113,
]);
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,
138, 243, 142, 163, 62, 107, 63, 131, 230, 158, 185, 60, 204, 50, 243, 222, 13, 123,
98, 116, 131, 194, 7, 25, 129, 209, 163, 72, 178, 143, 192, 240,
];
let expected_nsk: NullifierSecretKey = [
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,
196, 33, 11, 39, 220, 84, 119, 182, 187, 194, 135, 20, 124, 33, 244, 205, 96, 58, 102,
52, 74, 67, 110, 213, 24, 16, 160, 64, 247, 3, 107, 235,
];
let expected_npk = nssa_core::NullifierPublicKey([
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_npk = lee_core::NullifierPublicKey([
247, 253, 217, 86, 157, 208, 39, 172, 59, 190, 88, 165, 7, 173, 183, 106, 172, 211, 4,
180, 51, 107, 177, 107, 51, 117, 231, 176, 200, 103, 1, 121,
]);
let expected_vsk = ViewingSecretKey::new(
[
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,
185, 209, 179, 92, 7, 131, 98, 121, 215, 46, 154, 56, 238, 106, 162, 225, 83, 82,
134, 3, 80, 186, 35, 178, 161, 204, 205, 163, 28, 19, 149, 18,
],
[
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,
174, 24, 72, 205, 129, 123, 131, 9, 146, 152, 224, 151, 10, 184, 224, 109, 94, 149,
117, 60, 26, 10, 212, 125, 113, 147, 87, 67, 73, 26, 101, 193,
],
);
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,
215, 229, 207, 120, 148, 177, 148, 197, 72, 222, 134, 3, 231, 146, 123, 226, 36, 84,
232, 179, 205, 16, 241, 142, 9, 81, 58, 54, 12, 115, 148, 182, 19, 245, 22, 203, 57,
71, 11, 204, 156, 130, 30, 170, 199, 201, 25, 2, 21, 34, 155, 136, 124, 145, 223, 128,
177, 207, 92, 38, 252, 165, 118, 61, 128, 71, 154, 242, 105, 165, 52, 7, 6, 244, 120,
227, 134, 191, 25, 169, 150, 123, 246, 138, 25, 196, 126, 156, 144, 33, 123, 120, 44,
142, 89, 201, 49, 219, 205, 87, 236, 110, 64, 129, 102, 100, 155, 26, 101, 121, 42,
236, 82, 111, 141, 117, 75, 71, 194, 73, 123, 170, 110, 69, 149, 107, 96, 195, 55, 122,
140, 131, 106, 140, 156, 147, 75, 28, 128, 138, 113, 86, 37, 63, 173, 214, 200, 2, 214,
84, 234, 176, 120, 252, 184, 99, 192, 65, 112, 150, 99, 26, 174, 187, 183, 187, 64, 90,
248, 100, 66, 63, 195, 3, 44, 43, 128, 59, 149, 107, 66, 180, 67, 200, 183, 200, 36,
91, 7, 65, 228, 159, 79, 44, 89, 35, 163, 145, 92, 227, 104, 2, 72, 5, 7, 193, 21, 51,
116, 198, 184, 6, 192, 188, 68, 183, 163, 193, 142, 244, 217, 155, 197, 187, 189, 174,
225, 45, 126, 112, 93, 194, 156, 102, 150, 1, 188, 222, 76, 108, 73, 149, 44, 28, 219,
66, 95, 215, 204, 148, 217, 16, 36, 121, 112, 2, 51, 10, 195, 137, 12, 93, 203, 146,
138, 211, 15, 201, 42, 72, 146, 186, 160, 222, 235, 127, 83, 48, 182, 49, 248, 29, 138,
16, 32, 232, 179, 163, 187, 161, 174, 152, 187, 93, 76, 166, 48, 230, 219, 111, 123,
181, 103, 130, 28, 109, 235, 115, 45, 57, 193, 206, 160, 17, 52, 92, 194, 25, 3, 80,
97, 142, 249, 151, 94, 250, 95, 12, 57, 11, 165, 92, 47, 85, 182, 48, 22, 60, 97, 244,
59, 194, 135, 180, 133, 106, 227, 56, 192, 60, 91, 15, 241, 146, 89, 240, 130, 219,
202, 187, 43, 85, 98, 50, 104, 64, 114, 113, 80, 54, 69, 69, 5, 43, 90, 19, 0, 0, 188,
251, 184, 70, 160, 18, 117, 76, 53, 209, 166, 96, 34, 224, 137, 115, 183, 168, 243, 19,
1, 255, 4, 97, 162, 199, 104, 72, 213, 111, 62, 54, 172, 82, 184, 82, 143, 71, 99, 25,
104, 74, 120, 70, 84, 235, 32, 22, 20, 218, 163, 77, 194, 125, 75, 22, 72, 236, 192,
200, 107, 91, 156, 201, 10, 178, 87, 19, 181, 211, 91, 17, 145, 200, 17, 179, 65, 75,
200, 186, 89, 144, 91, 184, 116, 214, 51, 91, 42, 162, 243, 202, 92, 18, 54, 0, 213,
67, 149, 151, 51, 29, 220, 196, 160, 201, 68, 113, 210, 164, 175, 152, 121, 168, 231,
161, 91, 132, 218, 1, 171, 176, 84, 100, 57, 1, 3, 2, 196, 194, 76, 181, 79, 171, 157,
35, 162, 155, 192, 210, 149, 142, 120, 189, 127, 151, 96, 202, 225, 73, 242, 81, 112,
237, 224, 155, 130, 130, 34, 196, 153, 131, 161, 113, 163, 172, 114, 48, 207, 32, 151,
172, 83, 145, 79, 210, 100, 161, 92, 82, 216, 90, 104, 238, 212, 38, 50, 107, 17, 228,
195, 190, 6, 151, 165, 148, 245, 102, 51, 8, 185, 8, 85, 59, 247, 219, 95, 219, 170,
155, 233, 123, 27, 64, 251, 56, 24, 200, 16, 181, 212, 146, 61, 116, 106, 215, 214, 62,
118, 27, 68, 233, 148, 73, 135, 199, 74, 184, 89, 159, 217, 139, 24, 208, 250, 30, 224,
97, 185, 237, 193, 8, 216, 23, 186, 5, 50, 41, 161, 203, 22, 217, 23, 194, 191, 148,
124, 10, 212, 171, 209, 210, 145, 184, 171, 74, 35, 220, 43, 145, 241, 23, 43, 92, 171,
216, 43, 114, 77, 155, 147, 156, 86, 56, 170, 27, 1, 54, 182, 169, 96, 22, 201, 51,
145, 94, 143, 133, 106, 47, 176, 112, 197, 197, 96, 80, 73, 164, 207, 179, 22, 229,
171, 201, 223, 219, 13, 219, 1, 91, 224, 252, 171, 199, 217, 25, 60, 128, 135, 9, 71,
105, 231, 86, 34, 21, 155, 50, 0, 105, 72, 117, 108, 175, 140, 9, 181, 249, 139, 97, 3,
161, 66, 248, 42, 67, 113, 132, 8, 119, 232, 6, 169, 18, 157, 222, 53, 176, 56, 137,
120, 18, 115, 199, 187, 112, 48, 223, 211, 206, 152, 252, 108, 179, 129, 20, 227, 248,
183, 234, 87, 202, 49, 17, 69, 215, 118, 89, 188, 180, 33, 238, 245, 206, 40, 179, 129,
242, 59, 73, 254, 117, 114, 250, 179, 103, 109, 250, 202, 99, 152, 2, 167, 130, 169,
35, 71, 89, 211, 140, 71, 103, 154, 121, 108, 147, 191, 186, 73, 10, 73, 203, 23, 55,
106, 144, 98, 227, 157, 25, 27, 81, 67, 11, 57, 88, 227, 116, 61, 100, 94, 23, 166,
146, 57, 226, 72, 124, 33, 65, 226, 35, 167, 206, 156, 202, 213, 213, 158, 89, 249,
181, 19, 113, 109, 217, 71, 168, 142, 180, 122, 30, 5, 54, 170, 155, 73, 56, 170, 124,
139, 4, 165, 103, 82, 32, 183, 84, 7, 239, 117, 135, 239, 48, 24, 28, 210, 49, 137, 6,
158, 65, 211, 113, 205, 135, 146, 83, 10, 46, 90, 27, 97, 135, 135, 185, 173, 69, 58,
34, 247, 141, 150, 6, 158, 117, 23, 198, 139, 65, 81, 179, 187, 194, 247, 203, 127,
106, 232, 119, 122, 215, 197, 110, 69, 203, 174, 227, 63, 185, 106, 14, 184, 104, 113,
233, 83, 92, 104, 38, 188, 9, 135, 107, 108, 121, 193, 33, 209, 89, 39, 137, 17, 208,
26, 21, 238, 169, 86, 181, 193, 153, 82, 8, 151, 53, 39, 88, 91, 252, 3, 33, 75, 127,
9, 168, 53, 34, 1, 173, 202, 123, 157, 174, 170, 199, 254, 187, 196, 144, 37, 29, 48,
112, 173, 107, 147, 155, 69, 134, 137, 156, 247, 123, 242, 72, 5, 43, 106, 89, 179,
204, 41, 15, 60, 48, 78, 214, 180, 26, 170, 67, 71, 66, 146, 113, 220, 159, 153, 201,
176, 116, 154, 21, 186, 33, 180, 72, 39, 187, 240, 80, 112, 132, 144, 173, 210, 12, 76,
184, 146, 89, 178, 178, 82, 109, 71, 201, 241, 160, 207, 219, 124, 77, 2, 105, 124,
178, 71, 3, 38, 64, 41, 83, 170, 137, 82, 242, 144, 76, 102, 82, 7, 25, 149, 141, 169,
46, 4, 68, 40, 244, 146, 131, 107, 148, 18, 111, 85, 104, 243, 28, 75, 176, 249, 88,
82, 123, 89, 29, 104, 135, 230, 117, 67, 26, 249, 108, 145, 76, 38, 175, 89, 185, 94,
106, 128, 201, 150, 151, 194, 133, 21, 81, 213, 231, 15, 117, 44, 61, 86, 223, 162, 56,
190, 166, 177, 157, 137, 60, 208, 155, 234, 158, 252, 30,
];
assert!(expected_ssk == child_node.value.0.secret_spending_key);

View File

@ -1,10 +1,7 @@
use bip39::Mnemonic;
use common::HashType;
use lee_core::{NullifierPublicKey, NullifierSecretKey, encryption::ViewingPublicKey};
use ml_kem;
use lee_core::{
NullifierPublicKey, NullifierSecretKey,
encryption::ViewingPublicKey,
};
use rand::{RngCore as _, rngs::OsRng};
use serde::{Deserialize, Serialize};
use sha2::{Digest as _, digest::FixedOutput as _};
@ -176,7 +173,7 @@ impl SecretSpendingKey {
}
}
impl From<&ViewingSecretKey> for MlKem768EncapsulationKey {
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];
@ -195,8 +192,8 @@ impl PrivateKeyHolder {
}
#[must_use]
pub fn generate_viewing_public_key(&self) -> MlKem768EncapsulationKey {
MlKem768EncapsulationKey::from(&self.viewing_secret_key)
pub fn generate_viewing_public_key(&self) -> ViewingPublicKey {
ViewingPublicKey::from(&self.viewing_secret_key)
}
}

View File

@ -31,7 +31,7 @@ risc0-build = "3.0.3"
risc0-binfmt = "3.0.2"
[dev-dependencies]
nssa_core = { workspace = true, features = ["test_utils"] }
lee_core = { workspace = true, features = ["test_utils"] }
token_core.workspace = true
authenticated_transfer_core.workspace = true
test_program_methods.workspace = true

View File

@ -167,7 +167,7 @@ impl EphemeralPublicKey {
/// Deserializes an ML-KEM-768 ciphertext from a cursor.
/// Reads exactly 1088 bytes — the fixed ciphertext size for ML-KEM-768.
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaCoreError> {
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, LeeCoreError> {
let mut value = vec![0_u8; 1088];
cursor.read_exact(&mut value)?;
Ok(Self(value))

View File

@ -31,9 +31,9 @@ impl MlKem768EncapsulationKey {
pub const LEN: usize = 1184;
/// Construct from raw bytes, returning an error if the length is not [`Self::LEN`].
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, crate::error::NssaCoreError> {
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, crate::error::LeeCoreError> {
if bytes.len() != Self::LEN {
return Err(crate::error::NssaCoreError::DeserializationError(format!(
return Err(crate::error::LeeCoreError::DeserializationError(format!(
"MlKem768EncapsulationKey must be {} bytes, got {}",
Self::LEN,
bytes.len()

View File

@ -164,7 +164,10 @@ impl FfiPrivateAccountKeys {
let slice = unsafe {
slice::from_raw_parts(self.viewing_public_key, self.viewing_public_key_len)
};
Ok(lee_core::encryption::ViewingPublicKey(slice.to_vec()))
Ok(
lee_core::encryption::ViewingPublicKey::from_bytes(slice.to_vec())
.expect("wallet_ffi: length already validated to 1184 bytes"),
)
} else {
Err(WalletFfiError::InvalidKeyValue)
}

View File

@ -156,8 +156,10 @@ impl WalletSubcommand for GroupSubcommand {
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 = lee_core::encryption::ViewingPublicKey::from_seed(&d, &r).0;
let secret = ViewingSecretKey::new(d, r);
let ek_bytes = lee_core::encryption::ViewingPublicKey::from_seed(&d, &r)
.to_bytes()
.to_vec();
let public_key = SealingPublicKey::from_bytes(ek_bytes);
wallet_core.set_sealing_secret_key(secret);

View File

@ -365,7 +365,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
let to_vpk_res = hex::decode(&to_vpk)
.context("wallet::cli::programs::native_token_transfer: to_vpk must be a valid hex string")?;
let to_vpk = lee_core::encryption::ViewingPublicKey(to_vpk_res);
let to_vpk = lee_core::encryption::ViewingPublicKey::from_bytes(to_vpk_res)
.map_err(|e| anyhow::anyhow!("{e}"))?;
let (tx_hash, [secret_from, _]) = NativeTokenTransfer(wallet_core)
.send_private_transfer_to_outer_account(
@ -440,7 +441,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
let to_vpk_res = hex::decode(&to_vpk)
.context("wallet::cli::programs::native_token_transfer: to_vpk must be a valid hex string")?;
let to_vpk = lee_core::encryption::ViewingPublicKey(to_vpk_res);
let to_vpk = lee_core::encryption::ViewingPublicKey::from_bytes(to_vpk_res)
.map_err(|e| anyhow::anyhow!("{e}"))?;
let (tx_hash, _) = NativeTokenTransfer(wallet_core)
.send_shielded_transfer_to_outer_account(

View File

@ -791,7 +791,9 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
let recipient_vpk_res = hex::decode(&recipient_vpk).context(
"wallet::cli::programs::token: recipient_vpk must be a valid hex string",
)?;
let recipient_vpk = lee_core::encryption::ViewingPublicKey(recipient_vpk_res);
let recipient_vpk =
lee_core::encryption::ViewingPublicKey::from_bytes(recipient_vpk_res)
.map_err(|e| anyhow::anyhow!("{e}"))?;
let (tx_hash, [secret_sender, _]) = Token(wallet_core)
.send_transfer_transaction_private_foreign_account(
@ -901,7 +903,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
let holder_vpk_res = hex::decode(&holder_vpk).context(
"wallet::cli::programs::token: holder_vpk must be a valid hex string",
)?;
let holder_vpk = lee_core::encryption::ViewingPublicKey(holder_vpk_res);
let holder_vpk = lee_core::encryption::ViewingPublicKey::from_bytes(holder_vpk_res)
.map_err(|e| anyhow::anyhow!("{e}"))?;
let (tx_hash, [secret_definition, _]) = Token(wallet_core)
.send_mint_transaction_private_foreign_account(
@ -1055,7 +1058,9 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
let recipient_vpk_res = hex::decode(&recipient_vpk).context(
"wallet::cli::programs::token: recipient_vpk must be a valid hex string",
)?;
let recipient_vpk = lee_core::encryption::ViewingPublicKey(recipient_vpk_res);
let recipient_vpk =
lee_core::encryption::ViewingPublicKey::from_bytes(recipient_vpk_res)
.map_err(|e| anyhow::anyhow!("{e}"))?;
let (tx_hash, _) = Token(wallet_core)
.send_transfer_transaction_shielded_foreign_account(
@ -1184,7 +1189,8 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
let holder_vpk_res = hex::decode(&holder_vpk).context(
"wallet::cli::programs::token: holder_vpk must be a valid hex string",
)?;
let holder_vpk = lee_core::encryption::ViewingPublicKey(holder_vpk_res);
let holder_vpk = lee_core::encryption::ViewingPublicKey::from_bytes(holder_vpk_res)
.map_err(|e| anyhow::anyhow!("{e}"))?;
let (tx_hash, _) = Token(wallet_core)
.send_mint_transaction_shielded_foreign_account(