diff --git a/packages/wasm-mps/Cargo.lock b/packages/wasm-mps/Cargo.lock index 12a74c3dd5f..686e912cbb0 100644 --- a/packages/wasm-mps/Cargo.lock +++ b/packages/wasm-mps/Cargo.lock @@ -12,6 +12,44 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "base16ct" version = "0.2.0" @@ -30,6 +68,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bech32" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" + [[package]] name = "bincode" version = "1.3.3" @@ -51,6 +95,17 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2b_simd" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -60,6 +115,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "bs58" version = "0.5.1" @@ -95,12 +161,57 @@ dependencies = [ "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -139,12 +250,39 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.18", +] + +[[package]] +name = "const-crc32-nostd" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808ac43170e95b11dd23d78aa9eaac5bea45776a602955552c4e833f3f0f823d" + [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "corez" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df6f98652d30167eaeea34d77b730e07c8caba6df17bd4551842b9b8da01deb" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -154,6 +292,12 @@ dependencies = [ "libc", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crunchy" version = "0.2.4" @@ -168,6 +312,7 @@ checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core", + "serdect", "subtle", "zeroize", ] @@ -259,6 +404,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -271,6 +427,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -311,6 +476,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -331,6 +502,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "ff" version = "0.13.1" @@ -348,12 +531,77 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fpe" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c4b37de5ae15812a764c958297cfc50f5c010438f60c6ce75d11b802abd404" +dependencies = [ + "cbc", + "cipher", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "frost-core" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ef2787af391c7e8bedc037a3b9ea03dde803fbd93e778e6bb369547800e5cd" +dependencies = [ + "byteorder", + "const-crc32-nostd", + "derive-getters", + "document-features", + "hex", + "itertools", + "postcard", + "rand_core", + "serde", + "serdect", + "thiserror 2.0.18", + "visibility", + "zeroize", + "zeroize_derive", +] + +[[package]] +name = "frost-rerandomized" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4c5cedd2426728adef2c0b1720f57676354c473836d1ccc50d0f0d1c91942b" +dependencies = [ + "derive-getters", + "document-features", + "frost-core", + "hex", + "rand_core", +] + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "garbled-circuit" +version = "1.3.1-pre.2" +source = "git+ssh://git@github.com/silence-laboratories/garbling?rev=b18fb707#b18fb707884fc30e54563265a2afdb99a0076001" +dependencies = [ + "aes", + "generic-array", + "rand", + "rand_chacha", + "sha2", + "signature", + "sl-compute-common", + "sl-messages", + "thiserror 1.0.69", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -378,6 +626,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getset" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "group" version = "0.13.0" @@ -400,6 +660,41 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "halo2_poseidon" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa3da60b81f02f9b33ebc6252d766f843291fb4d2247a07ae73d20b791fc56f" +dependencies = [ + "bitvec", + "ff", + "group", + "pasta_curves", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "hex" version = "0.4.3" @@ -418,6 +713,15 @@ dependencies = [ "digest", ] +[[package]] +name = "incrementalmerkletree" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30821f91f0fa8660edca547918dc59812893b497d07c1144f326f07fdd94aba9" +dependencies = [ + "either", +] + [[package]] name = "inout" version = "0.1.4" @@ -427,6 +731,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "js-sys" version = "0.3.85" @@ -437,6 +750,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jubjub" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8499f7a74008aafbecb2a2e608a3e13e4dd3e84df198b604451efe93f2de6e61" +dependencies = [ + "bitvec", + "bls12_381", + "ff", + "group", + "rand_core", + "subtle", +] + [[package]] name = "k256" version = "0.13.4" @@ -449,17 +776,55 @@ dependencies = [ "serdect", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + [[package]] name = "libc" version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memuse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" + [[package]] name = "multi-party-schnorr" -version = "1.2.0-pre.1" -source = "git+https://github.com/silence-laboratories/multi-party-schnorr.git?rev=7511971e757a2260afa797283cf239c9cdfd5f19#7511971e757a2260afa797283cf239c9cdfd5f19" +version = "1.3.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34a88fef93793ae54ec5e6435be1b0851911b7ab2e6f47458df08c3d238e759f" dependencies = [ + "blake2b_simd", "bytemuck", "ciborium", "crypto-bigint", @@ -469,20 +834,59 @@ dependencies = [ "ed25519-dalek", "elliptic-curve", "ff", + "group", "hmac", "k256", "pasta_curves", "rand", "rand_chacha", + "rand_core", + "reddsa", "serde", "serde_bytes", "sha2", "signature", + "sl-mpc-derive", "sl-mpc-mate", + "sl-transcript", "thiserror 1.0.69", "zeroize", ] +[[package]] +name = "nonempty" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549e471b99ccaf2f89101bec68f4d244457d5a95a9c3d0672e9564124397741d" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -495,21 +899,63 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "orchard" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497e74492624a1d1cc8c9675a7afb17b430d32fd9efc171513d0840140b5f0c7" +dependencies = [ + "aes", + "bitvec", + "blake2b_simd", + "corez", + "ff", + "fpe", + "getset", + "group", + "halo2_poseidon", + "hex", + "incrementalmerkletree", + "lazy_static", + "memuse", + "nonempty", + "pasta_curves", + "rand", + "rand_core", + "reddsa", + "serde", + "sinsemilla", + "subtle", + "tracing", + "visibility", + "zcash_note_encryption", + "zcash_spec", + "zip32", +] + [[package]] name = "pasta_curves" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ + "blake2b_simd", "ff", "group", "hex", + "lazy_static", "rand", "serde", "static_assertions", "subtle", ] +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "pkcs8" version = "0.10.2" @@ -531,6 +977,19 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -540,6 +999,28 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -583,6 +1064,7 @@ checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", + "serde", ] [[package]] @@ -594,6 +1076,25 @@ dependencies = [ "getrandom", ] +[[package]] +name = "reddsa" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4784b85c8bfd17b36b86e664e6e504ecdb586001086ee23749e4a633bbb84832" +dependencies = [ + "blake2b_simd", + "byteorder", + "frost-rerandomized", + "group", + "hex", + "jubjub", + "pasta_curves", + "rand_core", + "serde", + "thiserror 2.0.18", + "zeroize", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -627,6 +1128,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sec1" version = "0.7.3" @@ -719,6 +1226,69 @@ dependencies = [ "rand_core", ] +[[package]] +name = "sinsemilla" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d268ae0ea06faafe1662e9967cd4f9022014f5eeb798e0c302c876df8b7af9c" +dependencies = [ + "group", + "pasta_curves", + "subtle", +] + +[[package]] +name = "sl-compute-common" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b202c662800c00d9f5149457220a52aa704ecaf57e5b19a08b2e59d792db3" +dependencies = [ + "aead", + "chacha20", + "crypto-bigint", + "rand", + "rand_chacha", + "serde", +] + +[[package]] +name = "sl-messages" +version = "1.3.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54aeea5765d7fa65dbe33cd40727518e1e5c8655749c024f76e8ed8fca285af" +dependencies = [ + "aead", + "bytemuck", + "bytes", + "chacha20", + "chacha20poly1305", + "derivation-path", + "generic-array", + "rand_core", + "sha2", + "signature", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "sl-mpc-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ad793fb84ed507aa8b9225c5d2c7b8f4db81538a399875d58888d1a0a1966" +dependencies = [ + "crypto-bigint", + "curve25519-dalek", + "ed25519-dalek", + "elliptic-curve", + "ff", + "hmac", + "rand", + "rand_chacha", + "sha2", + "thiserror 1.0.69", +] + [[package]] name = "sl-mpc-mate" version = "1.1.0" @@ -742,6 +1312,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sl-secret-sharing" +version = "0.1.0-pre.1" +source = "git+ssh://git@github.com/silence-laboratories/garbling?rev=b18fb707#b18fb707884fc30e54563265a2afdb99a0076001" +dependencies = [ + "ff", +] + +[[package]] +name = "sl-transcript" +version = "0.1.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33f0e5dbfa7ca28f40e1a16bb2763e468d10e6a3716f24d2ecf498d262832e3" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -752,6 +1348,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" @@ -836,6 +1438,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" + [[package]] name = "typenum" version = "1.19.0" @@ -864,6 +1482,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -925,11 +1554,14 @@ dependencies = [ "getrandom", "js-sys", "multi-party-schnorr", + "orchard", + "pasta_curves", "rand", "serde", "thiserror 2.0.18", "wasm-bindgen", "web-sys", + "zcash", ] [[package]] @@ -951,6 +1583,65 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "zcash" +version = "0.1.0-pre.10" +source = "git+ssh://git@github.com/silence-laboratories/garbling?rev=b18fb707#b18fb707884fc30e54563265a2afdb99a0076001" +dependencies = [ + "ciborium", + "crypto_box", + "ff", + "garbled-circuit", + "group", + "hex", + "hmac", + "multi-party-schnorr", + "orchard", + "pasta_curves", + "rand", + "rand_chacha", + "serde", + "serde_bytes", + "sha2", + "sl-compute-common", + "sl-messages", + "sl-secret-sharing", +] + +[[package]] +name = "zcash_note_encryption" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77efec759c3798b6e4d829fcc762070d9b229b0f13338c40bf993b7b609c2272" +dependencies = [ + "chacha20", + "chacha20poly1305", + "cipher", + "rand_core", + "subtle", +] + +[[package]] +name = "zcash_spec" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded3f58b93486aa79b85acba1001f5298f27a46489859934954d262533ee2915" +dependencies = [ + "blake2b_simd", +] + [[package]] name = "zerocopy" version = "0.8.39" @@ -990,3 +1681,16 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zip32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64bf5186a8916f7a48f2a98ef599bf9c099e2458b36b819e393db1c0e768c4b" +dependencies = [ + "bech32", + "blake2b_simd", + "memuse", + "subtle", + "zcash_spec", +] diff --git a/packages/wasm-mps/Cargo.toml b/packages/wasm-mps/Cargo.toml index 8c374f3f607..76320a9f48f 100644 --- a/packages/wasm-mps/Cargo.toml +++ b/packages/wasm-mps/Cargo.toml @@ -17,7 +17,10 @@ bincode = "1.3" crypto_box = "0.9" getrandom = { version = "0.2", features = ["js"] } js-sys = "0.3" -multi-party-schnorr = { git = "https://github.com/silence-laboratories/multi-party-schnorr.git", rev = "7511971e757a2260afa797283cf239c9cdfd5f19", features = ["serde"]} +multi-party-schnorr = { version = "1.3.0-pre.1", features = ["serde", "eddsa", "redpallas"] } +orchard = { version = "0.13", default-features = false } +pasta_curves = { version = "0.5", default-features = false } +zcash = { git = "ssh://git@github.com/silence-laboratories/garbling", rev = "b18fb707", features = ["dkg"] } rand = "0.8" serde = { version = "1.0", features = ["derive"] } thiserror = "2.0.18" @@ -29,4 +32,5 @@ ed25519-dalek = "2.1.0" [profile.release] strip = true - +lto = "fat" +codegen-units = 1 diff --git a/packages/wasm-mps/Makefile b/packages/wasm-mps/Makefile index 406deb84fb4..f77b02fffab 100644 --- a/packages/wasm-mps/Makefile +++ b/packages/wasm-mps/Makefile @@ -30,7 +30,7 @@ endef # run wasm-opt separately so we can pass `--enable-bulk-memory` define WASM_OPT_COMMAND - $(WASM_OPT) --enable-bulk-memory --enable-nontrapping-float-to-int --enable-sign-ext -Oz $(1)/*.wasm -o $(1)/*.wasm + $(WASM_OPT) --enable-bulk-memory --enable-nontrapping-float-to-int --enable-sign-ext --enable-simd -O3 $(1)/*.wasm -o $(1)/*.wasm endef define REMOVE_GITIGNORE diff --git a/packages/wasm-mps/package.json b/packages/wasm-mps/package.json index 500b6ed4b75..cf1918f9e32 100644 --- a/packages/wasm-mps/package.json +++ b/packages/wasm-mps/package.json @@ -46,7 +46,7 @@ ], "scripts": { "test": "npm run test:mocha", - "test:mocha": "mocha --recursive test", + "test:mocha": "mocha --recursive test --node-option=disable-warning=ExperimentalWarning", "build:wasm": "make js/wasm && make dist/esm/js/wasm && make dist/cjs/js/wasm && make dist/web/js/wasm", "build:ts-esm": "tsc", "build:ts-cjs": "tsc --project tsconfig.cjs.json", diff --git a/packages/wasm-mps/src/lib.rs b/packages/wasm-mps/src/lib.rs index b8a65a40b8b..2d18031606f 100644 --- a/packages/wasm-mps/src/lib.rs +++ b/packages/wasm-mps/src/lib.rs @@ -4,6 +4,7 @@ mod mps { use multi_party_schnorr::{ common::{ + redpallas::{RedPallasPoint, RedPallasPointBytes}, ser::Serializable, traits::{GroupElem, Round, ScalarReduce}, }, @@ -13,12 +14,15 @@ mod mps { }, sign::{ messages::{SignMsg1, SignMsg2, SignMsg3}, - PartialSign, SignerParty, R0 as DsgR0, R1 as DsgR1, R2 as DsgR2, + PartialSign, SignError, SignReady, SignerParty, R0 as DsgR0, R1 as DsgR1, R2 as DsgR2, }, }; use serde::{Deserialize, Serialize}; use std::sync::Arc; use thiserror::Error; + use zcash::derivation_session::{ + DerivationStatus, Message as DrvMessage, Session as DerivationSession, + }; /// Errors that can be returned as results. #[derive(Debug, Error)] @@ -34,6 +38,9 @@ mod mps { #[error("Protocol Error")] ProtocolError, + + #[error("Protocol Error: {0}")] + ProtocolErrorDetail(String), } /// Internal DKG state used for round 1. @@ -43,6 +50,7 @@ mod mps { G: GroupElem, G::Scalar: ScalarReduce<[u8; 32]> + Serializable, { + pub party_id: u8, pub msg: KeygenMsg1, #[serde(bound( @@ -54,22 +62,26 @@ mod mps { /// Internal DKG state used for round 2. #[derive(Serialize, Deserialize)] + #[serde(bound( + serialize = "G::Scalar: Serializable", + deserialize = "G::Scalar: Serializable" + ))] struct DkgStateR2 where G: GroupElem, G::Scalar: ScalarReduce<[u8; 32]> + Serializable, { + pub party_id: u8, pub msg: KeygenMsg2, - - #[serde(bound( - serialize = "KeygenParty: Serialize", - deserialize = "KeygenParty: Deserialize<'de>" - ))] pub party: KeygenParty, } /// Internal DSG state used for round 1. #[derive(Serialize, Deserialize)] + #[serde(bound( + serialize = "G::Scalar: Serializable", + deserialize = "G::Scalar: Serializable" + ))] struct DsgStateR1 where G: GroupElem, @@ -81,6 +93,10 @@ mod mps { /// Internal DSG state used for round 2. #[derive(Serialize, Deserialize)] + #[serde(bound( + serialize = "G::Scalar: Serializable", + deserialize = "G::Scalar: Serializable" + ))] struct DsgStateR2 where G: GroupElem, @@ -92,6 +108,10 @@ mod mps { /// Internal DSG state used for round 3. #[derive(Serialize, Deserialize)] + #[serde(bound( + serialize = "G::Scalar: Serializable", + deserialize = "G::Scalar: Serializable" + ))] struct DsgStateR3 where G: GroupElem, @@ -115,6 +135,75 @@ mod mps { pub chaincode: [u8; 32], } + pub struct DerivationMsg { + pub from_id: u8, + pub to_id: u8, + pub payload: Vec, + } + + pub struct MsgDerivationInit { + pub share: Vec, + pub pk: [u8; 32], + pub outgoing: Vec, + pub drv_state: Vec, + } + + pub struct MsgDerivation { + pub outgoing: Vec, + pub drv_state: Vec, + pub done: bool, + pub ask: [u8; 32], + pub nk: [u8; 32], + pub rivk: [u8; 32], + pub internal_ivk: [u8; 64], + pub external_ivk: [u8; 64], + } + + fn encode_drv_message(msg: &DrvMessage) -> Result { + let from_id = msg.sender(); + let to_id = msg.receiver().unwrap_or(u8::MAX); + let payload = bincode::serialize(msg).map_err(|_| MpsError::SerializationError)?; + Ok(DerivationMsg { + from_id, + to_id, + payload, + }) + } + + fn decode_drv_message(msg: &DerivationMsg) -> Result { + bincode::deserialize(&msg.payload).map_err(|_| MpsError::DeserializationError) + } + + fn internal_dsg_round2_process( + round2_message: &[u8], + state: &[u8], + ) -> Result + where + G: GroupElem, + G::Scalar: ScalarReduce<[u8; 32]> + Serializable, + SignerParty, G>: + Round>, Output = SignReady, Error = SignError>, + SignReady: Round, SignMsg3), Error = SignError>, + { + let state: DsgStateR2 = + bincode::deserialize(state).map_err(|_| MpsError::DeserializationError)?; + let i0_msg2: SignMsg2 = + bincode::deserialize(round2_message).map_err(|_| MpsError::DeserializationError)?; + let ready = state + .party + .process(vec![i0_msg2, state.msg]) + .map_err(|_| MpsError::ProtocolError)?; + let (p3, msg3) = ready.process(()).map_err(|_| MpsError::ProtocolError)?; + let new_state = DsgStateR3 { + msg: msg3.clone(), + party: p3, + }; + Ok(MsgState { + msg: bincode::serialize(&msg3).map_err(|_| MpsError::SerializationError)?, + state: bincode::serialize(&new_state).map_err(|_| MpsError::SerializationError)?, + }) + } + fn internal_dkg_round0_process( party_id: u8, decryption_key: &[u8; 32], @@ -122,7 +211,7 @@ mod mps { seed: &[u8; 32], ) -> Result where - G: GroupElem + Serialize, + G: GroupElem, G::Scalar: ScalarReduce<[u8; 32]> + Serializable, { if party_id >= 3 { @@ -171,6 +260,7 @@ mod mps { // Create the state for storage between rounds let state = DkgStateR1 { + party_id, msg: msg1, party: p1, }; @@ -200,7 +290,7 @@ mod mps { state: &[u8], ) -> Result where - G: GroupElem + Serialize, + G: GroupElem, G::Scalar: ScalarReduce<[u8; 32]> + Serializable, { // Parse state @@ -222,6 +312,7 @@ mod mps { // Create the state for storage between rounds let state = DkgStateR2 { + party_id: state.party_id, msg: msg2.clone(), party: p2, }; @@ -245,9 +336,9 @@ mod mps { fn internal_dkg_round2_process( round2_messages: &[Vec; 2], state: &[u8], - ) -> Result, MpsError> + ) -> Result<(Keyshare, u8), MpsError> where - G: GroupElem + Serialize + for<'de> Deserialize<'de>, + G: GroupElem, G::Scalar: ScalarReduce<[u8; 32]> + Serializable, { // Deserialize round2 messages from other parties @@ -260,13 +351,15 @@ mod mps { let state: DkgStateR2 = bincode::deserialize(state).map_err(|_| MpsError::DeserializationError)?; + let party_id = state.party_id; + // Generate share let share = state .party .process(vec![i0_msg2, i1_msg2, state.msg.clone()]) .map_err(|_| MpsError::ProtocolError)?; - Ok(share) + Ok((share, party_id)) } /// Process round 2 of DKG protocol. @@ -276,7 +369,7 @@ mod mps { round2_messages: &[Vec; 2], state: &[u8], ) -> Result { - let share = internal_dkg_round2_process::(round2_messages, state)?; + let (share, _) = internal_dkg_round2_process::(round2_messages, state)?; Ok(Share { share: bincode::serialize(&share).map_err(|_| MpsError::SerializationError)?, pk: share.public_key.compress().to_bytes(), @@ -286,7 +379,7 @@ mod mps { fn internal_dsg_round0_process(p0: SignerParty) -> Result where - G: GroupElem + Serialize, + G: GroupElem, G::Scalar: Serializable, { // Generate message @@ -335,7 +428,7 @@ mod mps { state: &[u8], ) -> Result where - G: GroupElem + Serialize + for<'de> Deserialize<'de>, + G: GroupElem, G::Scalar: ScalarReduce<[u8; 32]> + Serializable, { // Parse state @@ -412,29 +505,260 @@ mod mps { }) } - /// Process round 3 of DSG protocol. - /// round3_messages: Public messages from other parties. - /// state: Private state result from round 2. - pub fn ed25519_dsg_round3_process( + fn internal_dsg_round3_process( round3_message: &[u8], state: &[u8], - ) -> Result, MpsError> { - // Parse state - let state: DsgStateR3 = + ) -> Result<(Vec, G), MpsError> + where + G: GroupElem, + G::Scalar: ScalarReduce<[u8; 32]> + Serializable, + PartialSign: Round>, Output = (S, SC), Error = SignError>, + S: Into<[u8; 64]>, + { + let state: DsgStateR3 = bincode::deserialize(state).map_err(|_| MpsError::DeserializationError)?; - - // Parse messages - let i0_msg3: SignMsg3 = + let i0_msg3: SignMsg3 = bincode::deserialize(round3_message).map_err(|_| MpsError::DeserializationError)?; - let msgs = vec![i0_msg3, state.msg]; - - // Process all round2 messages together - let (signature, _) = state + let public_key = state.party.public_key; + let (sig, _) = state .party - .process(msgs) + .process(vec![i0_msg3, state.msg]) .map_err(|_| MpsError::ProtocolError)?; + let sig_bytes: [u8; 64] = sig.into(); + Ok((sig_bytes.to_vec(), public_key)) + } + + /// Process round 3 of DSG protocol; returns the 64-byte signature. + pub fn ed25519_dsg_round3_process( + round3_message: &[u8], + state: &[u8], + ) -> Result, MpsError> { + let (sig, _) = internal_dsg_round3_process::(round3_message, state)?; + Ok(sig) + } + + /// Process round 0 of RedPallas DKG (same flow as ed25519). + pub fn redpallas_dkg_round0_process( + party_id: u8, + decryption_key: &[u8; 32], + encryption_keys: &[Vec; 2], + seed: &[u8; 32], + ) -> Result { + internal_dkg_round0_process::( + party_id, + decryption_key, + encryption_keys, + seed, + ) + } + + /// Process round 1 of RedPallas DKG (same flow as ed25519). + pub fn redpallas_dkg_round1_process( + round1_messages: &[Vec; 2], + state: &[u8], + ) -> Result { + internal_dkg_round1_process::(round1_messages, state) + } - Ok(signature.to_vec()) + /// Process round 2 of RedPallas DKG; finalizes keyshare and starts derivation session. + pub fn redpallas_dkg_round2_process( + round2_messages: &[Vec; 2], + state: &[u8], + derivation_seed: &[u8; 32], + ) -> Result { + let (share, party_id) = + internal_dkg_round2_process::(round2_messages, state)?; + let pk = RedPallasPointBytes::from(share.public_key).0; + let share_bytes = bincode::serialize(&share).map_err(|_| MpsError::SerializationError)?; + + let (drv_session, initial_outgoing) = + DerivationSession::new(party_id, *share.shamir_share(), *derivation_seed) + .map_err(|_| MpsError::ProtocolError)?; + + let outgoing = initial_outgoing + .iter() + .map(encode_drv_message) + .collect::, _>>()?; + + let drv_state = + bincode::serialize(&drv_session).map_err(|_| MpsError::SerializationError)?; + + Ok(MsgDerivationInit { + share: share_bytes, + pk, + outgoing, + drv_state, + }) + } + + /// Drive one step of the Orchard derivation session; call repeatedly until done == true. + pub fn redpallas_derivation_process( + incoming: Vec, + drv_state: &[u8], + ) -> Result { + let mut session: DerivationSession = + bincode::deserialize(drv_state).map_err(|_| MpsError::DeserializationError)?; + + let messages: Vec = incoming + .iter() + .map(decode_drv_message) + .collect::, _>>()?; + + let mut outgoing_raw: Vec = Vec::new(); + let mut done = false; + for (idx, message) in messages.into_iter().enumerate() { + let phase = session.current_phase_name(); + let status = session + .handle_messages(vec![message], &mut outgoing_raw) + .map_err(|e| { + MpsError::ProtocolErrorDetail(format!("msg_idx={idx} phase={phase}: {e:?}")) + })?; + match status { + DerivationStatus::Aborted(reason) => { + return Err(MpsError::ProtocolErrorDetail(format!( + "Aborted: {reason:?}" + ))); + } + DerivationStatus::Complete => { + done = true; + break; + } + DerivationStatus::Waiting => {} + } + } + + let outgoing = outgoing_raw + .iter() + .map(encode_drv_message) + .collect::, _>>()?; + + let new_drv_state = + bincode::serialize(&session).map_err(|_| MpsError::SerializationError)?; + + let (ask, nk, rivk, internal_ivk, external_ivk) = if done { + let keys = session.derived_keys().ok_or(MpsError::ProtocolError)?; + ( + keys.ask, + keys.nk, + keys.rivk, + keys.internal_ivk, + keys.external_ivk, + ) + } else { + ([0u8; 32], [0u8; 32], [0u8; 32], [0u8; 64], [0u8; 64]) + }; + + Ok(MsgDerivation { + outgoing, + drv_state: new_drv_state, + done, + ask, + nk, + rivk, + internal_ivk, + external_ivk, + }) + } + + /// Process round 0 of RedPallas DSG. + pub fn redpallas_dsg_round0_process( + share: &[u8], + message: &[u8], + ) -> Result { + let keyshare: Keyshare = + bincode::deserialize(share).map_err(|_| MpsError::DeserializationError)?; + let p0 = SignerParty::::new( + Arc::new(keyshare), + message.to_vec(), + "m".parse().map_err(|_| MpsError::InvalidInput)?, + &mut rand::thread_rng(), + ); + internal_dsg_round0_process(p0) + } + + /// Process round 1 of RedPallas DSG. + pub fn redpallas_dsg_round1_process( + round1_message: &[u8], + state: &[u8], + ) -> Result { + internal_dsg_round1_process::(round1_message, state) + } + + /// Process round 2 of RedPallas DSG. + pub fn redpallas_dsg_round2_process( + round2_message: &[u8], + state: &[u8], + ) -> Result { + internal_dsg_round2_process::(round2_message, state) + } + + /// Orchard incoming viewing keys derived from FVK components. + pub struct RedPallasIvks { + pub internal_ivk: [u8; 64], + pub external_ivk: [u8; 64], + } + + /// Derive Orchard incoming viewing keys from FVK components (ask, nk, rivk). + /// Applies sign correction to ask so the resulting ak has a positive x-coordinate + /// (bit 255 of encoding == 0), matching the Orchard FVK convention. + pub fn redpallas_fvk_to_ivks( + ask: &[u8; 32], + nk: &[u8; 32], + rivk: &[u8; 32], + ) -> Result { + use multi_party_schnorr::group::ff::PrimeField; + use orchard::{ + keys::{FullViewingKey, Scope}, + primitives::redpallas::{SigningKey, SpendAuth, VerificationKey}, + }; + use pasta_curves::pallas; + + // Compute ak = ask * G_SpendAuth with sign correction (bit 255 must be 0). + let mut ask_eff: pallas::Scalar = + Option::from(pallas::Scalar::from_repr(*ask)).ok_or(MpsError::InvalidInput)?; + let ak_bytes: [u8; 32] = loop { + let sk: SigningKey = ask_eff + .to_repr() + .try_into() + .map_err(|_| MpsError::InvalidInput)?; + let vk: VerificationKey = (&sk).into(); + let ak_candidate: [u8; 32] = (&vk).into(); + if (ak_candidate[31] >> 7) == 1 { + ask_eff = -ask_eff; + continue; + } + break ak_candidate; + }; + + let mut fvk_bytes = [0u8; 96]; + fvk_bytes[0..32].copy_from_slice(&ak_bytes); + fvk_bytes[32..64].copy_from_slice(nk); + fvk_bytes[64..96].copy_from_slice(rivk); + + let fvk = FullViewingKey::from_bytes(&fvk_bytes).ok_or(MpsError::InvalidInput)?; + + Ok(RedPallasIvks { + internal_ivk: fvk.to_ivk(Scope::Internal).to_bytes(), + external_ivk: fvk.to_ivk(Scope::External).to_bytes(), + }) + } + + /// Result from round 3 of RedPallas DSG. + pub struct RedPallasSignature { + pub signature: Vec, + pub rk: [u8; 32], + } + + /// Process round 3 of RedPallas DSG; returns the 64-byte signature and the + /// per-session randomized verification key (rk) against which it verifies. + pub fn redpallas_dsg_round3_process( + round3_message: &[u8], + state: &[u8], + ) -> Result { + let (signature, pk) = + internal_dsg_round3_process::(round3_message, state)?; + let rk = RedPallasPointBytes::from(pk).0; + Ok(RedPallasSignature { signature, rk }) } } @@ -751,6 +1075,156 @@ impl MsgShare { } } +#[wasm_bindgen] +pub struct DerivationMsg { + from_id: u8, + to_id: u8, + payload: Vec, +} + +#[wasm_bindgen] +impl DerivationMsg { + #[wasm_bindgen(getter)] + pub fn from_id(&self) -> u8 { + self.from_id + } + + #[wasm_bindgen(getter)] + pub fn to_id(&self) -> u8 { + self.to_id + } + + #[wasm_bindgen(getter)] + pub fn payload(&self) -> Vec { + self.payload.clone() + } +} + +#[wasm_bindgen] +pub struct MsgDerivationInit { + share: Vec, + pk: Vec, + outgoing: Vec, + drv_state: Vec, +} + +#[wasm_bindgen] +impl MsgDerivationInit { + #[wasm_bindgen(getter)] + pub fn share(&self) -> Vec { + self.share.clone() + } + + #[wasm_bindgen(getter)] + pub fn pk(&self) -> Vec { + self.pk.clone() + } + + #[wasm_bindgen(getter)] + pub fn drv_state(&self) -> Vec { + self.drv_state.clone() + } + + pub fn outgoing(&self) -> js_sys::Array { + let arr = js_sys::Array::new(); + for m in &self.outgoing { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"from_id".into(), &JsValue::from(m.from_id as u32)) + .unwrap(); + js_sys::Reflect::set(&obj, &"to_id".into(), &JsValue::from(m.to_id as u32)).unwrap(); + let payload = js_sys::Uint8Array::from(m.payload.as_slice()); + js_sys::Reflect::set(&obj, &"payload".into(), &payload.into()).unwrap(); + arr.push(&obj); + } + arr + } +} + +#[wasm_bindgen] +pub struct MsgDerivation { + outgoing: Vec, + drv_state: Vec, + done: bool, + ask: Vec, + nk: Vec, + rivk: Vec, + internal_ivk: Vec, + external_ivk: Vec, +} + +#[wasm_bindgen] +impl MsgDerivation { + #[wasm_bindgen(getter)] + pub fn drv_state(&self) -> Vec { + self.drv_state.clone() + } + + #[wasm_bindgen(getter)] + pub fn done(&self) -> bool { + self.done + } + + #[wasm_bindgen(getter)] + pub fn ask(&self) -> Vec { + self.ask.clone() + } + + #[wasm_bindgen(getter)] + pub fn nk(&self) -> Vec { + self.nk.clone() + } + + #[wasm_bindgen(getter)] + pub fn rivk(&self) -> Vec { + self.rivk.clone() + } + + #[wasm_bindgen(getter)] + pub fn internal_ivk(&self) -> Vec { + self.internal_ivk.clone() + } + + #[wasm_bindgen(getter)] + pub fn external_ivk(&self) -> Vec { + self.external_ivk.clone() + } + + pub fn outgoing(&self) -> js_sys::Array { + let arr = js_sys::Array::new(); + for m in &self.outgoing { + let obj = js_sys::Object::new(); + js_sys::Reflect::set(&obj, &"from_id".into(), &JsValue::from(m.from_id as u32)) + .unwrap(); + js_sys::Reflect::set(&obj, &"to_id".into(), &JsValue::from(m.to_id as u32)).unwrap(); + let payload = js_sys::Uint8Array::from(m.payload.as_slice()); + js_sys::Reflect::set(&obj, &"payload".into(), &payload.into()).unwrap(); + arr.push(&obj); + } + arr + } +} + +/// Extract two `Vec` from a JS array whose elements must both be `Uint8Array` +/// (or a subclass such as Node.js `Buffer`). Returns `Err` without invoking any +/// JS property on `undefined` so callers never produce wasm-bindgen console spam. +fn js_array_to_2_bufs(arr: &Array) -> Result<[Vec; 2], String> { + use wasm_bindgen::JsCast; + if arr.length() < 2 { + return Err(mps::MpsError::InvalidInput.to_string()); + } + let b0 = arr + .get(0) + .dyn_into::() + .map_err(|_| mps::MpsError::InvalidInput.to_string())? + .to_vec(); + let b1 = arr + .get(1) + .dyn_into::() + .map_err(|_| mps::MpsError::InvalidInput.to_string())? + .to_vec(); + Ok([b0, b1]) +} + #[wasm_bindgen] pub fn ed25519_dkg_round0_process( party_id: u8, @@ -762,16 +1236,10 @@ pub fn ed25519_dkg_round0_process( .try_into() .map_err(|_| "Deserialization Error")?; let seed_32: [u8; 32] = seed[..32].try_into().map_err(|_| "Deserialization Error")?; - let result = mps::ed25519_dkg_round0_process( - party_id, - &decryption_key_32, - &[ - js_sys::Uint8Array::from(encryption_keys.get(0)).to_vec(), - js_sys::Uint8Array::from(encryption_keys.get(1)).to_vec(), - ], - &seed_32, - ) - .map_err(|e| e.to_string())?; + let [ek0, ek1] = js_array_to_2_bufs(&encryption_keys)?; + let result = + mps::ed25519_dkg_round0_process(party_id, &decryption_key_32, &[ek0, ek1], &seed_32) + .map_err(|e| e.to_string())?; Ok(MsgState { msg: result.msg, @@ -784,14 +1252,8 @@ pub fn ed25519_dkg_round1_process( round1_messages: Array, state: &[u8], ) -> Result { - let result = mps::ed25519_dkg_round1_process( - &[ - js_sys::Uint8Array::from(round1_messages.get(0)).to_vec(), - js_sys::Uint8Array::from(round1_messages.get(1)).to_vec(), - ], - state, - ) - .map_err(|e| e.to_string())?; + let [m0, m1] = js_array_to_2_bufs(&round1_messages)?; + let result = mps::ed25519_dkg_round1_process(&[m0, m1], state).map_err(|e| e.to_string())?; Ok(MsgState { msg: result.msg, @@ -801,14 +1263,8 @@ pub fn ed25519_dkg_round1_process( #[wasm_bindgen] pub fn ed25519_dkg_round2_process(round2_messages: Array, state: &[u8]) -> Result { - let result = mps::ed25519_dkg_round2_process( - &[ - js_sys::Uint8Array::from(round2_messages.get(0)).to_vec(), - js_sys::Uint8Array::from(round2_messages.get(1)).to_vec(), - ], - state, - ) - .map_err(|e| e.to_string())?; + let [m0, m1] = js_array_to_2_bufs(&round2_messages)?; + let result = mps::ed25519_dkg_round2_process(&[m0, m1], state).map_err(|e| e.to_string())?; Ok(Share { share: result.share, @@ -861,3 +1317,233 @@ pub fn ed25519_dsg_round3_process(round2_message: &[u8], state: &[u8]) -> Result Ok(result.to_vec()) } + +#[wasm_bindgen] +pub fn redpallas_dkg_round0_process( + party_id: u8, + decryption_key: &[u8], + encryption_keys: Array, + seed: &[u8], +) -> Result { + let decryption_key_32: [u8; 32] = decryption_key[..32] + .try_into() + .map_err(|_| "Deserialization Error")?; + let seed_32: [u8; 32] = seed[..32].try_into().map_err(|_| "Deserialization Error")?; + let [ek0, ek1] = js_array_to_2_bufs(&encryption_keys)?; + let result = + mps::redpallas_dkg_round0_process(party_id, &decryption_key_32, &[ek0, ek1], &seed_32) + .map_err(|e| e.to_string())?; + + Ok(MsgState { + msg: result.msg, + state: result.state, + }) +} + +#[wasm_bindgen] +pub fn redpallas_dkg_round1_process( + round1_messages: Array, + state: &[u8], +) -> Result { + let [m0, m1] = js_array_to_2_bufs(&round1_messages)?; + let result = mps::redpallas_dkg_round1_process(&[m0, m1], state).map_err(|e| e.to_string())?; + + Ok(MsgState { + msg: result.msg, + state: result.state, + }) +} + +#[wasm_bindgen] +pub fn redpallas_dkg_round2_process( + round2_messages: Array, + state: &[u8], + derivation_seed: &[u8], +) -> Result { + let derivation_seed_32: [u8; 32] = derivation_seed[..32] + .try_into() + .map_err(|_| "Deserialization Error")?; + let [m0, m1] = js_array_to_2_bufs(&round2_messages)?; + let result = mps::redpallas_dkg_round2_process(&[m0, m1], state, &derivation_seed_32) + .map_err(|e| e.to_string())?; + + Ok(MsgDerivationInit { + share: result.share, + pk: result.pk.to_vec(), + outgoing: result.outgoing, + drv_state: result.drv_state, + }) +} + +fn js_array_to_drv_msgs(incoming: Array) -> Result, String> { + incoming + .iter() + .map(|val| { + use js_sys::Reflect; + let from_id = Reflect::get(&val, &"from_id".into()) + .map_err(|_| "missing from_id".to_string())? + .as_f64() + .ok_or_else(|| "from_id not a number".to_string())? as u8; + let to_id = Reflect::get(&val, &"to_id".into()) + .map_err(|_| "missing to_id".to_string())? + .as_f64() + .ok_or_else(|| "to_id not a number".to_string())? as u8; + let payload = js_sys::Uint8Array::from( + Reflect::get(&val, &"payload".into()).map_err(|_| "missing payload".to_string())?, + ) + .to_vec(); + Ok(mps::DerivationMsg { + from_id, + to_id, + payload, + }) + }) + .collect() +} + +#[wasm_bindgen] +pub fn redpallas_derivation_process( + incoming: Array, + drv_state: &[u8], +) -> Result { + let msgs = js_array_to_drv_msgs(incoming)?; + let result = mps::redpallas_derivation_process(msgs, drv_state).map_err(|e| e.to_string())?; + + Ok(MsgDerivation { + outgoing: result.outgoing, + drv_state: result.drv_state, + done: result.done, + ask: result.ask.to_vec(), + nk: result.nk.to_vec(), + rivk: result.rivk.to_vec(), + internal_ivk: result.internal_ivk.to_vec(), + external_ivk: result.external_ivk.to_vec(), + }) +} + +#[wasm_bindgen] +pub fn redpallas_dsg_round0_process(share: &[u8], message: &[u8]) -> Result { + let result = mps::redpallas_dsg_round0_process(share, message).map_err(|e| e.to_string())?; + + Ok(MsgState { + msg: result.msg, + state: result.state, + }) +} + +#[wasm_bindgen] +pub fn redpallas_dsg_round1_process( + round1_message: &[u8], + state: &[u8], +) -> Result { + let result = + mps::redpallas_dsg_round1_process(round1_message, state).map_err(|e| e.to_string())?; + + Ok(MsgState { + msg: result.msg, + state: result.state, + }) +} + +#[wasm_bindgen] +pub fn redpallas_dsg_round2_process( + round2_message: &[u8], + state: &[u8], +) -> Result { + let result = + mps::redpallas_dsg_round2_process(round2_message, state).map_err(|e| e.to_string())?; + + Ok(MsgState { + msg: result.msg, + state: result.state, + }) +} + +#[wasm_bindgen] +pub struct RedPallasSignature { + signature: Vec, + rk: Vec, +} + +#[wasm_bindgen] +impl RedPallasSignature { + #[wasm_bindgen(getter)] + pub fn signature(&self) -> Vec { + self.signature.clone() + } + + #[wasm_bindgen(getter)] + pub fn rk(&self) -> Vec { + self.rk.clone() + } +} + +#[wasm_bindgen] +pub fn redpallas_dsg_round3_process( + round3_message: &[u8], + state: &[u8], +) -> Result { + let result = + mps::redpallas_dsg_round3_process(round3_message, state).map_err(|e| e.to_string())?; + Ok(RedPallasSignature { + signature: result.signature, + rk: result.rk.to_vec(), + }) +} + +#[wasm_bindgen] +pub struct RedPallasIvks { + internal_ivk: Vec, + external_ivk: Vec, +} + +#[wasm_bindgen] +impl RedPallasIvks { + #[wasm_bindgen(getter)] + pub fn internal_ivk(&self) -> Vec { + self.internal_ivk.clone() + } + + #[wasm_bindgen(getter)] + pub fn external_ivk(&self) -> Vec { + self.external_ivk.clone() + } +} + +#[wasm_bindgen] +pub fn redpallas_fvk_to_ivks(ask: &[u8], nk: &[u8], rivk: &[u8]) -> Result { + let ask_32: [u8; 32] = ask.try_into().map_err(|_| "ask must be 32 bytes")?; + let nk_32: [u8; 32] = nk.try_into().map_err(|_| "nk must be 32 bytes")?; + let rivk_32: [u8; 32] = rivk.try_into().map_err(|_| "rivk must be 32 bytes")?; + let result = + mps::redpallas_fvk_to_ivks(&ask_32, &nk_32, &rivk_32).map_err(|e| e.to_string())?; + Ok(RedPallasIvks { + internal_ivk: result.internal_ivk.to_vec(), + external_ivk: result.external_ivk.to_vec(), + }) +} + +#[wasm_bindgen] +pub fn redpallas_verify(pk: &[u8], sig: &[u8], msg: &[u8]) -> Result { + use orchard::primitives::redpallas::{Signature, SpendAuth, VerificationKey}; + + let pk_bytes: [u8; 32] = pk.try_into().map_err(|_| "pk must be 32 bytes")?; + let sig_bytes: [u8; 64] = sig.try_into().map_err(|_| "sig must be 64 bytes")?; + + let vk = VerificationKey::::try_from(pk_bytes).map_err(|e| e.to_string())?; + let signature = Signature::::from(sig_bytes); + + Ok(vk.verify(msg, &signature).is_ok()) +} + +/// Compute the spending verification key ak = ask * G_SpendAuth. +#[wasm_bindgen] +pub fn redpallas_ask_to_ak(ask: &[u8]) -> Result, String> { + use orchard::primitives::redpallas::{SigningKey, SpendAuth, VerificationKey}; + + let ask_bytes: [u8; 32] = ask.try_into().map_err(|_| "ask must be 32 bytes")?; + let sk = SigningKey::::try_from(ask_bytes).map_err(|e| e.to_string())?; + let vk = VerificationKey::::from(&sk); + let ak_bytes: [u8; 32] = (&vk).into(); + Ok(ak_bytes.to_vec()) +} diff --git a/packages/wasm-mps/test/mps.ts b/packages/wasm-mps/test/mps.ts index cd3730ee45d..5f436031c25 100644 --- a/packages/wasm-mps/test/mps.ts +++ b/packages/wasm-mps/test/mps.ts @@ -19,280 +19,495 @@ describe("mps", function () { } }); - describe("dkg", function () { - it("performs round 0", function () { - for (let i = 0; i < keypairs.length; i++) { - mps.ed25519_dkg_round0_process( - i, - keypairs[i].privateKey, - otherIndices[i].map((i) => keypairs[i].publicKey), - crypto.randomBytes(32), - ); - } - }); - - let results1: Array; + describe("ed25519", function () { + describe("dkg", function () { + it("performs round 0", function () { + for (let i = 0; i < keypairs.length; i++) { + mps.ed25519_dkg_round0_process( + i, + keypairs[i].privateKey, + otherIndices[i].map((i) => keypairs[i].publicKey), + crypto.randomBytes(32), + ); + } + }); - before("performs round 0", function () { - results1 = [0, 1, 2].map((i) => - mps.ed25519_dkg_round0_process( - i, - keypairs[i].privateKey, - otherIndices[i].map((i) => keypairs[i].publicKey), - crypto.randomBytes(32), - ), - ); - }); + let results1: Array; - it("performs round 1", function () { - for (let i = 0; i < results1.length; i++) { - mps.ed25519_dkg_round1_process( - otherIndices[i].map((i) => results1[i].msg), - results1[i].state, + before("performs round 0", function () { + results1 = [0, 1, 2].map((i) => + mps.ed25519_dkg_round0_process( + i, + keypairs[i].privateKey, + otherIndices[i].map((i) => keypairs[i].publicKey), + crypto.randomBytes(32), + ), ); - } - }); + }); - let results2: Array; + it("performs round 1", function () { + for (let i = 0; i < results1.length; i++) { + mps.ed25519_dkg_round1_process( + otherIndices[i].map((i) => results1[i].msg), + results1[i].state, + ); + } + }); - before("performs round 1", function () { - results2 = [0, 1, 2].map((i) => - mps.ed25519_dkg_round1_process( - otherIndices[i].map((i) => results1[i].msg), - results1[i].state, - ), - ); - }); + let results2: Array; - it("performs round 2", function () { - const results3 = [0, 1, 2].map((i) => - mps.ed25519_dkg_round2_process( - otherIndices[i].map((i) => results2[i].msg), - results2[i].state, - ), - ); - for (let i = 0; i < 2; i++) { - assert.ok(results3[i].pk.every((value, index) => value === results3[2].pk[index])); - assert.ok( - results3[i].chaincode.every((value, index) => value === results3[2].chaincode[index]), + before("performs round 1", function () { + results2 = [0, 1, 2].map((i) => + mps.ed25519_dkg_round1_process( + otherIndices[i].map((i) => results1[i].msg), + results1[i].state, + ), ); - } - }); + }); - describe("input handling", function () { - function shouldThrow(fn: () => unknown): unknown { - try { - fn(); - } catch (e: unknown) { - return e; - } - throw new Error("Expected function to throw an error"); - } - - describe("round0_process", function () { - it("does not panic on bad party size", function () { - shouldThrow(() => - mps.ed25519_dkg_round0_process( - "255", - Buffer.alloc(32), - [Buffer.alloc(32), Buffer.alloc(32)], - crypto.randomBytes(32), - ), + it("performs round 2", function () { + const results3 = [0, 1, 2].map((i) => + mps.ed25519_dkg_round2_process( + otherIndices[i].map((i) => results2[i].msg), + results2[i].state, + ), + ); + for (let i = 0; i < 2; i++) { + assert.ok(results3[i].pk.every((value, index) => value === results3[2].pk[index])); + assert.ok( + results3[i].chaincode.every((value, index) => value === results3[2].chaincode[index]), ); - }); + } + }); - it("does not panic on bad encryption key", function () { - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - "encryption key", - [Buffer.alloc(32), Buffer.alloc(32)], - crypto.randomBytes(32), - ), - ); - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - [Buffer.alloc(32), Buffer.alloc(32)], - crypto.randomBytes(32), - ), - ); + describe("input handling", function () { + function shouldThrow(fn: () => unknown): unknown { + try { + fn(); + } catch (e: unknown) { + return e; + } + throw new Error("Expected function to throw an error"); + } + + describe("round0_process", function () { + it("does not panic on bad party size", function () { + shouldThrow(() => + mps.ed25519_dkg_round0_process( + "255", + Buffer.alloc(32), + [Buffer.alloc(32), Buffer.alloc(32)], + crypto.randomBytes(32), + ), + ); + }); + + it("does not panic on bad encryption key", function () { + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + "encryption key", + [Buffer.alloc(32), Buffer.alloc(32)], + crypto.randomBytes(32), + ), + ); + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + [Buffer.alloc(32), Buffer.alloc(32)], + crypto.randomBytes(32), + ), + ); + }); + + it("does not panic on bad decryption keys", function () { + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + "decryption keys", + crypto.randomBytes(32), + ), + ); + shouldThrow(() => + mps.ed25519_dkg_round0_process(0, Buffer.alloc(0), [], crypto.randomBytes(32)), + ); + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + ["decryption key"], + crypto.randomBytes(32), + ), + ); + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + [Buffer.alloc(0)], + crypto.randomBytes(32), + ), + ); + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + [Buffer.alloc(32), Buffer.alloc(0)], + crypto.randomBytes(32), + ), + ); + }); + + it("does not panic on bad seed", function () { + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + [Buffer.alloc(32), Buffer.alloc(32)], + "seed", + ), + ); + shouldThrow(() => + mps.ed25519_dkg_round0_process( + 0, + Buffer.alloc(0), + [Buffer.alloc(32), Buffer.alloc(32)], + Buffer.alloc(0), + ), + ); + }); }); - it("does not panic on bad decryption keys", function () { - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - "decryption keys", - crypto.randomBytes(32), - ), - ); - shouldThrow(() => - mps.ed25519_dkg_round0_process(0, Buffer.alloc(0), [], crypto.randomBytes(32)), - ); - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - ["decryption key"], - crypto.randomBytes(32), - ), - ); - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - [Buffer.alloc(0)], - crypto.randomBytes(32), - ), - ); - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - [Buffer.alloc(32), Buffer.alloc(0)], - crypto.randomBytes(32), - ), - ); + describe("round1_process", function () { + it("does not panic on bad messages", function () { + shouldThrow(() => mps.ed25519_dkg_round1_process("messages", Buffer.alloc(1224))); + shouldThrow(() => mps.ed25519_dkg_round1_process([], Buffer.alloc(1224))); + shouldThrow(() => mps.ed25519_dkg_round1_process(["message"], Buffer.alloc(1224))); + shouldThrow(() => + mps.ed25519_dkg_round1_process([Buffer.alloc(0), Buffer.alloc(1224)]), + ); + }); + + it("does not panic on bad state", function () { + shouldThrow(() => + mps.ed25519_dkg_round1_process([Buffer.alloc(65), Buffer.alloc(65)], "state"), + ); + shouldThrow(() => + mps.ed25519_dkg_round1_process([Buffer.alloc(65), Buffer.alloc(65)], Buffer.alloc(0)), + ); + }); }); - it("does not panic on bad seed", function () { - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - [Buffer.alloc(32), Buffer.alloc(32)], - "seed", - ), - ); - shouldThrow(() => - mps.ed25519_dkg_round0_process( - 0, - Buffer.alloc(0), - [Buffer.alloc(32), Buffer.alloc(32)], - Buffer.alloc(0), - ), - ); + describe("round2_process", function () { + it("does not panic on bad messages", function () { + shouldThrow(() => mps.ed25519_dkg_round2_process("messages", Buffer.alloc(1224))); + shouldThrow(() => mps.ed25519_dkg_round2_process([], Buffer.alloc(1224))); + shouldThrow(() => mps.ed25519_dkg_round2_process(["message"], Buffer.alloc(1224))); + shouldThrow(() => + mps.ed25519_dkg_round2_process([Buffer.alloc(0), Buffer.alloc(1224)]), + ); + }); + + it("does not panic on bad state", function () { + shouldThrow(() => + mps.ed25519_dkg_round2_process([Buffer.alloc(65), Buffer.alloc(65)], "state"), + ); + shouldThrow(() => + mps.ed25519_dkg_round2_process([Buffer.alloc(65), Buffer.alloc(65)], Buffer.alloc(0)), + ); + }); }); }); + }); - describe("round1_process", function () { - it("does not panic on bad messages", function () { - shouldThrow(() => mps.ed25519_dkg_round1_process("messages", Buffer.alloc(1224))); - shouldThrow(() => mps.ed25519_dkg_round1_process([], Buffer.alloc(1224))); - shouldThrow(() => mps.ed25519_dkg_round1_process(["message"], Buffer.alloc(1224))); - shouldThrow(() => mps.ed25519_dkg_round1_process([Buffer.alloc(0), Buffer.alloc(1224)])); - }); + describe("dsg", function () { + const otherIndex = [1, 0]; + let shares: Array; + + before("performs dkg", function () { + const results1 = [0, 1, 2].map((i) => + mps.ed25519_dkg_round0_process( + i, + keypairs[i].privateKey, + otherIndices[i].map((i) => keypairs[i].publicKey), + crypto.randomBytes(32), + ), + ); + const results2 = [0, 1, 2].map((i) => + mps.ed25519_dkg_round1_process( + otherIndices[i].map((i) => results1[i].msg), + results1[i].state, + ), + ); + shares = [0, 1, 2].map((i) => + mps.ed25519_dkg_round2_process( + otherIndices[i].map((i) => results2[i].msg), + results2[i].state, + ), + ); + }); - it("does not panic on bad state", function () { - shouldThrow(() => - mps.ed25519_dkg_round1_process([Buffer.alloc(65), Buffer.alloc(65)], "state"), - ); - shouldThrow(() => - mps.ed25519_dkg_round1_process([Buffer.alloc(65), Buffer.alloc(65)], Buffer.alloc(0)), - ); - }); + const message = Buffer.from( + "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks", + ); + + it("performs round 0", function () { + for (const i of [0, 2]) { + mps.ed25519_dsg_round0_process(shares[i].share, "m", message); + } }); - describe("round2_process", function () { - it("does not panic on bad messages", function () { - shouldThrow(() => mps.ed25519_dkg_round2_process("messages", Buffer.alloc(1224))); - shouldThrow(() => mps.ed25519_dkg_round2_process([], Buffer.alloc(1224))); - shouldThrow(() => mps.ed25519_dkg_round2_process(["message"], Buffer.alloc(1224))); - shouldThrow(() => mps.ed25519_dkg_round2_process([Buffer.alloc(0), Buffer.alloc(1224)])); - }); + let results1: Array; - it("does not panic on bad state", function () { - shouldThrow(() => - mps.ed25519_dkg_round2_process([Buffer.alloc(65), Buffer.alloc(65)], "state"), - ); - shouldThrow(() => - mps.ed25519_dkg_round2_process([Buffer.alloc(65), Buffer.alloc(65)], Buffer.alloc(0)), - ); - }); + before("performs round 0", function () { + results1 = [0, 2].map((i) => mps.ed25519_dsg_round0_process(shares[i].share, "m", message)); + }); + + it("performs round 1", function () { + for (let i = 0; i < results1.length; i++) { + mps.ed25519_dsg_round1_process(results1[otherIndex[i]].msg, results1[i].state); + } + }); + + let results2: Array; + + before("performs round 1", function () { + results2 = [0, 1].map((i) => + mps.ed25519_dsg_round1_process(results1[otherIndex[i]].msg, results1[i].state), + ); + }); + + it("performs round 2", function () { + for (let i = 0; i < results2.length; i++) { + mps.ed25519_dsg_round2_process(results2[otherIndex[i]].msg, results2[i].state); + } + }); + + let results3: Array; + + before("performs round 2", function () { + results3 = [0, 1].map((i) => + mps.ed25519_dsg_round2_process(results2[otherIndex[i]].msg, results2[i].state), + ); + }); + + it("performs round 3", function () { + const signatures = [0, 1].map((i) => + mps.ed25519_dsg_round3_process(results3[otherIndex[i]].msg, results3[i].state), + ); + assert(sodium.crypto_sign_verify_detached(signatures[0], message, shares[0].pk)); + assert(sodium.crypto_sign_verify_detached(signatures[1], message, shares[2].pk)); }); }); }); - describe("dsg", function () { - const otherIndex = [1, 0]; - let shares: Array; - - before("performs dkg", function () { - const results1 = [0, 1, 2].map((i) => - mps.ed25519_dkg_round0_process( - i, - keypairs[i].privateKey, - otherIndices[i].map((i) => keypairs[i].publicKey), - crypto.randomBytes(32), - ), - ); - const results2 = [0, 1, 2].map((i) => - mps.ed25519_dkg_round1_process( - otherIndices[i].map((i) => results1[i].msg), - results1[i].state, - ), - ); - shares = [0, 1, 2].map((i) => - mps.ed25519_dkg_round2_process( - otherIndices[i].map((i) => results2[i].msg), - results2[i].state, - ), - ); - }); + describe("redpallas", function () { + describe("dkg", function () { + it("performs round 0", function () { + for (let i = 0; i < keypairs.length; i++) { + mps.redpallas_dkg_round0_process( + i, + keypairs[i].privateKey, + otherIndices[i].map((i) => keypairs[i].publicKey), + crypto.randomBytes(32), + ); + } + }); - const message = Buffer.from( - "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks", - ); + let results1: Array; - it("performs round 0", function () { - for (const i of [0, 2]) { - mps.ed25519_dsg_round0_process(shares[i].share, "m", message); - } - }); + before("performs round 0", function () { + results1 = [0, 1, 2].map((i) => + mps.redpallas_dkg_round0_process( + i, + keypairs[i].privateKey, + otherIndices[i].map((i) => keypairs[i].publicKey), + crypto.randomBytes(32), + ), + ); + }); + + it("performs round 1", function () { + for (let i = 0; i < results1.length; i++) { + mps.redpallas_dkg_round1_process( + otherIndices[i].map((i) => results1[i].msg), + results1[i].state, + ); + } + }); - let results1: Array; + let results2: Array; - before("performs round 0", function () { - results1 = [0, 2].map((i) => mps.ed25519_dsg_round0_process(shares[i].share, "m", message)); - }); + before("performs round 1", function () { + results2 = [0, 1, 2].map((i) => + mps.redpallas_dkg_round1_process( + otherIndices[i].map((i) => results1[i].msg), + results1[i].state, + ), + ); + }); - it("performs round 1", function () { - for (let i = 0; i < results1.length; i++) { - mps.ed25519_dsg_round1_process(results1[otherIndex[i]].msg, results1[i].state); - } - }); + it("performs round 2", function () { + const results3 = [0, 1, 2].map((i) => + mps.redpallas_dkg_round2_process( + otherIndices[i].map((i) => results2[i].msg), + results2[i].state, + crypto.randomBytes(32), + ), + ); + for (let i = 0; i < 2; i++) { + assert.ok(results3[i].pk.every((value, index) => value === results3[2].pk[index])); + } + }); - let results2: Array; + let results3: Array; - before("performs round 1", function () { - results2 = [0, 1].map((i) => - mps.ed25519_dsg_round1_process(results1[otherIndex[i]].msg, results1[i].state), - ); - }); + before("performs round 2", function () { + results3 = [0, 1, 2].map((i) => + mps.redpallas_dkg_round2_process( + otherIndices[i].map((i) => results2[i].msg), + results2[i].state, + crypto.randomBytes(32), + ), + ); + }); - it("performs round 2", function () { - for (let i = 0; i < results2.length; i++) { - mps.ed25519_dsg_round2_process(results2[otherIndex[i]].msg, results2[i].state); - } + it("runs derivation to completion", function () { + this.timeout(30000); + const queue: Array = []; + for (const init of results3) { + queue.push(...(Array.from(init.outgoing()) as Array)); + } + const partyDone: Array = [false, false, false]; + const partyResults: Array = []; + const states = results3.map((d) => d.drv_state); + for (let step = 0; step < 10000 && !partyDone.every((done) => done); step++) { + assert.ok(queue.length > 0, "derivation queue empty before completion"); + const msg = queue.shift(); + const to = msg.to_id; + const result = mps.redpallas_derivation_process([msg], states[to]); + states[to] = result.drv_state; + queue.push(...(Array.from(result.outgoing()) as Array)); + if (result.done) { + partyDone[to] = true; + partyResults[to] = result; + } + } + assert.ok(partyDone.every(Boolean), "derivation did not complete within 10000 steps"); + const derivedKeys: Array = partyResults; + for (let i = 0; i < 3; i++) { + const k = derivedKeys[i]; + assert.equal(k.ask.length, 32); + assert.equal(k.nk.length, 32); + assert.equal(k.rivk.length, 32); + assert.equal(k.internal_ivk.length, 64); + assert.equal(k.external_ivk.length, 64); + } + for (let i = 0; i < 2; i++) { + assert.deepStrictEqual(derivedKeys[i].ask, derivedKeys[2].ask); + assert.deepStrictEqual(derivedKeys[i].nk, derivedKeys[2].nk); + assert.deepStrictEqual(derivedKeys[i].rivk, derivedKeys[2].rivk); + assert.deepStrictEqual(derivedKeys[i].internal_ivk, derivedKeys[2].internal_ivk); + assert.deepStrictEqual(derivedKeys[i].external_ivk, derivedKeys[2].external_ivk); + } + for (let i = 0; i < 3; i++) { + const k = derivedKeys[i]; + assert(!k.ask.every((b) => b === 0)); + assert(!k.nk.every((b) => b === 0)); + assert(!k.rivk.every((b) => b === 0)); + assert(!k.internal_ivk.every((b) => b === 0)); + assert(!k.external_ivk.every((b) => b === 0)); + } + for (let i = 0; i < 3; i++) { + const k = derivedKeys[i]; + const ivks = mps.redpallas_fvk_to_ivks(k.ask, k.nk, k.rivk); + assert.deepStrictEqual(ivks.internal_ivk, k.internal_ivk); + assert.deepStrictEqual(ivks.external_ivk, k.external_ivk); + } + }); }); - let results3: Array; + describe("dsg", function () { + const otherIndex = [1, 0]; + let shares: Array; + + before("performs dkg", function () { + const results1 = [0, 1, 2].map((i) => + mps.redpallas_dkg_round0_process( + i, + keypairs[i].privateKey, + otherIndices[i].map((j) => keypairs[j].publicKey), + crypto.randomBytes(32), + ), + ); + const results2 = [0, 1, 2].map((i) => + mps.redpallas_dkg_round1_process( + otherIndices[i].map((j) => results1[j].msg), + results1[i].state, + ), + ); + shares = [0, 1, 2].map((i) => + mps.redpallas_dkg_round2_process( + otherIndices[i].map((j) => results2[j].msg), + results2[i].state, + crypto.randomBytes(32), + ), + ); + }); - before("performs round 2", function () { - results3 = [0, 1].map((i) => - mps.ed25519_dsg_round2_process(results2[otherIndex[i]].msg, results2[i].state), + const message = Buffer.from( + "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks", ); - }); - it("performs round 3", function () { - const signatures = [0, 1].map((i) => - mps.ed25519_dsg_round3_process(results3[otherIndex[i]].msg, results3[i].state), - ); - assert(sodium.crypto_sign_verify_detached(signatures[0], message, shares[0].pk)); - assert(sodium.crypto_sign_verify_detached(signatures[1], message, shares[2].pk)); + it("performs round 0", function () { + for (const i of [0, 2]) { + mps.redpallas_dsg_round0_process(shares[i].share, message); + } + }); + + let results1: Array; + + before("performs round 0", function () { + results1 = [0, 2].map((i) => mps.redpallas_dsg_round0_process(shares[i].share, message)); + }); + + it("performs round 1", function () { + for (let i = 0; i < results1.length; i++) { + mps.redpallas_dsg_round1_process(results1[otherIndex[i]].msg, results1[i].state); + } + }); + + let results2: Array; + + before("performs round 1", function () { + results2 = [0, 1].map((i) => + mps.redpallas_dsg_round1_process(results1[otherIndex[i]].msg, results1[i].state), + ); + }); + + it("performs round 2", function () { + for (let i = 0; i < results2.length; i++) { + mps.redpallas_dsg_round2_process(results2[otherIndex[i]].msg, results2[i].state); + } + }); + + let results3: Array; + + before("performs round 2", function () { + results3 = [0, 1].map((i) => + mps.redpallas_dsg_round2_process(results2[otherIndex[i]].msg, results2[i].state), + ); + }); + + it("performs round 3", function () { + const results4 = [0, 1].map((i) => + mps.redpallas_dsg_round3_process(results3[otherIndex[i]].msg, results3[i].state), + ); + for (let i = 0; i < 2; i++) { + assert(mps.redpallas_verify(results4[i].rk, results4[i].signature, message)); + } + }); }); }); });