From 19dcb1f607981ed8b81959e9d520244bc234e554 Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 2 Dec 2024 16:43:48 +0100 Subject: [PATCH 01/15] Initial version of BLS-style hash --- .gitignore | 1 + Cargo.lock | 1046 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 23 ++ src/lib.rs | 207 +++++++++++ src/mpc.rs | 70 ++++ 5 files changed, 1347 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/mpc.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e00ddf8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1046 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bls-style-hash" +version = "0.1.0" +dependencies = [ + "digest", + "futures", + "generic-ec", + "generic-ec-zkp", + "key-share", + "rand", + "rand_dev", + "round-based", + "serde", + "sha2", + "test-case", + "thiserror 2.0.3", + "tokio", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "group", + "rand_core", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.89", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "generic-ec" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e3268b3f97e2046ebf69e24a1e7e12dad4dec247d87f59268fd57ab514b5f8" +dependencies = [ + "curve25519-dalek", + "digest", + "generic-ec-core", + "generic-ec-curves", + "hex", + "phantom-type 0.4.2", + "rand_core", + "rand_hash", + "serde", + "serde_with", + "subtle", + "udigest", + "zeroize", +] + +[[package]] +name = "generic-ec-core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049128cc67cac6176ada5218e294ce46421470d92a7340c93d5cfd3ecfbc29a4" +dependencies = [ + "generic-array", + "rand_core", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "generic-ec-curves" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e663405a17b229dede990904edcfd2056cf6641f1240869a1f44fa13bd4ee4d" +dependencies = [ + "curve25519-dalek", + "generic-ec-core", + "group", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "generic-ec-zkp" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec6e906724b1f70cd13e7e2455165300f1ef2fd200e40aa6207a046a36af839d" +dependencies = [ + "generic-array", + "generic-ec", + "rand_core", + "serde", + "subtle", + "udigest", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "key-share" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea364cb2397405d8c79afd3de173ca7e2e1d83a4ddd94d359263480ad96f06f" +dependencies = [ + "displaydoc", + "generic-ec", + "generic-ec-zkp", + "rand_core", + "thiserror 1.0.69", +] + +[[package]] +name = "libc" +version = "0.2.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[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 = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "phantom-type" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f710afd11c9711b04f97ab61bb9747d5a04562fdf0f9f44abc3de92490084982" +dependencies = [ + "educe", +] + +[[package]] +name = "phantom-type" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68f5dc797c2a743e024e1c53215474598faf0408826a90249569ad7f47adeaa" +dependencies = [ + "educe", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_dev" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbee97c27dada05f03db49ffe6516872f6c926e0fd525f9ce0cb3c051adf145c" +dependencies = [ + "hex", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16bc1dd921383c6564eb0b8252f5b3f6622b84d40c6e35f5e6790e1fd7abb7a9" +dependencies = [ + "digest", + "rand_core", + "udigest", +] + +[[package]] +name = "round-based" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81564866f5617d497753563151d8beb80d61e925e904d94b7e8a202b721e931e" +dependencies = [ + "displaydoc", + "futures-util", + "phantom-type 0.3.1", + "round-based-derive", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "round-based-derive" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afa4d5b318bcafae8a7ebc57c1cb7d4b2db7358293e34d71bfd605fd327cc13" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", + "test-case-core", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "udigest" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cd61fa9fb78569e9fe34acf0048fd8cb9ebdbacc47af740745487287043ff0" +dependencies = [ + "digest", + "udigest-derive", +] + +[[package]] +name = "udigest-derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603329303137e0d59238ee4d6b9c085eada8e2a9d20666f3abd9dadf8f8543f4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e53b7ad --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "bls-style-digest" +version = "0.1.0" +edition = "2021" + +[dependencies] +digest = "0.10" +generic-ec = { version = "0.4", features = ["serde", "curve-ed25519", "hash-to-scalar"] } +generic-ec-zkp = "0.4" +key-share = "0.5" +round-based = { version = "0.3", features = ["derive"] } +sha2 = "0.10" +serde = "1" +thiserror = { version = "2" } + +[dev-dependencies] +futures = "0.3" +key-share = { version = "0.5", features = ["spof"] } +rand = "0.8" +rand_dev = "0.1" +round-based = { version = "0.3", features = ["dev"] } +test-case = "3" +tokio = { version = "1", features = ["macros"] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2cd232a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,207 @@ +//! This crate implements digesting into elliptic curve points keyed with a +//! secret scalar for this curve. This digest resembles BLS signatures, but +//! because we don't do signature verification, we're not limited in the choice +//! of curves. The procedure for a digest is as follows: +//! +//! 1. Input parameters: +//! * `D` - cryptographic hash function +//! * `E` - an elliptic curve +//! * `m` - message to digest, given as a byte string +//! * `x` - secret key, given as a scalar for `E` +//! 2. Create `HPRng` - a pseudo-random number generator based on `D` and `m`, +//! with a procedure described in +//! [`hash_rng`](https://docs.rs/rand_hash/0.1.1/rand_hash/index.html) +//! 3. Generate random `s` from `HPRng` by using a procedure defined for this +//! curve +//! 4. If `s` is zero, retry from step *3*. If `s` is zero after 100 attempts, +//! abort +//! 5. Compute `generator(E) * s * x` + +pub mod mpc; + +/// Compute digest of `data` keyed with `secret_key` +pub fn digest( + data: &[u8], + secret_key: &generic_ec::NonZero>, +) -> generic_ec::Point { + let plain_scalar = generic_ec::Scalar::::from_hash::(&data); + let plain = generic_ec::Point::generator() * plain_scalar; + plain * secret_key +} + +/// Compute partial digest of `data` keyed with a key of which `secret_share` is a +/// share of. Use [`aggregate`] to aggregate multiple partial digests into a full +/// digest +/// +/// Partial digest is exactly the same as a full digest; this is a convenient +/// alias if you want to distinguish the functionality in your code. +pub fn partial_digest( + data: &[u8], + secret_share: &generic_ec::NonZero>, +) -> generic_ec::Point { + digest::(data, secret_share) +} + +/// Aggregate `partials` - partial digest values - into a full value. +/// +/// `share_preimages` should be `None` for additive key shares. For SSS, it +/// gives the points at which the keyshare values are computed, and should be in +/// the same order by participant as `partials`. +pub fn aggregate( + partials: &[generic_ec::Point], + share_preimages: Option<&[generic_ec::NonZero>]>, +) -> Option> { + if let Some(share_preimages) = share_preimages { + // shamir aggregation + let lagrange_coefficients = (0..(share_preimages.len())) + .map(|j| generic_ec_zkp::polynomial::lagrange_coefficient_at_zero(j, share_preimages)) + .collect::>>()?; + Some(generic_ec::Scalar::multiscalar_mul( + lagrange_coefficients.into_iter().zip(partials), + )) + } else { + // additive aggregation + Some(partials.iter().sum()) + } +} + +/// Start an MPC protocol that digests the data with shared private key. Returns +/// digested data +/// +/// - `data` - byte string to digest +/// - `i` - index of party in this protocol invocation, used for message routing +/// - `key_share` - key share to use, can be additive or SSS +/// - `participants` - which key holders are participating in the protocol, +/// given as indexes into `share_preimages` in key share. Ignored for additive +/// shares. +/// - `party` - the `round-based` party +pub async fn start_digest( + data: &[u8], + i: u16, + key_share: &key_share::CoreKeyShare, + participants: &[u16], + party: M, +) -> Result, mpc::Error> +where + D: digest::Digest, + E: generic_ec::Curve, + M: round_based::Mpc>, +{ + let share_preimages = match key_share.vss_setup.as_ref() { + None => None, + Some(vss_setup) => Some( + participants + .iter() + .map(|i| vss_setup.I.get(usize::from(*i)).copied()) + .collect::>>() + .ok_or(mpc::Error::CreationFailed("share is not SSS"))?, + ), + }; + let share_preimages = share_preimages.as_ref().map(|v| v.as_ref()); + + mpc::run::( + data, + &key_share.x, + i, + key_share.min_signers(), + share_preimages, + party, + ) + .await +} + +#[cfg(test)] +mod test { + type E = generic_ec::curves::Ed25519; + + #[test_case::test_case(3, 5; "t3n5")] + #[test_case::test_case(5, 5; "t5n5")] + #[test_case::test_case(3, 7; "t3n7")] + fn aggregate_same_as_digest(t: u16, n: u16) { + let t_ = if t == n { None } else { Some(t) }; + let t = usize::from(t); + let mut rng = rand_dev::DevRng::new(); + + let secret_key = generic_ec::NonZero::>::random(&mut rng); + let shares = key_share::trusted_dealer::builder::(n) + .set_threshold(t_) + .set_shared_secret_key(secret_key.clone()) + .generate_shares(&mut rng) + .unwrap(); + let share_preimages = shares[0].vss_setup.as_ref().map(|vss| &vss.I[0..t]); + + let data = b"take that you worm"; + + let digest = super::digest::(data, &secret_key); + let partials = shares + .iter() + .map(|s| super::partial_digest::(data, &s.x)) + .collect::>(); + let restored = super::aggregate(&partials[0..t], share_preimages).unwrap(); + + assert_eq!(restored, digest); + } + + #[test_case::test_case(3, 5; "t3n5")] + #[test_case::test_case(5, 5; "t5n5")] + #[test_case::test_case(3, 7; "t3n7")] + #[tokio::test] + async fn protocol_same_as_digest(t: u16, n: u16) { + let mut rng = rand_dev::DevRng::new(); + + let secret_key = generic_ec::NonZero::>::random(&mut rng); + let shares = key_share::trusted_dealer::builder::(n) + .set_threshold(Some(t)) + .set_shared_secret_key(secret_key.clone()) + .generate_shares(&mut rng) + .unwrap(); + + let data = b"take that you worm"; + let party_indexes = random_participants(n, t, &mut rng); + let parties = party_indexes + .iter() + .map(|x| u16::try_from(*x).unwrap()) + .collect::>(); + + let mut simulation = round_based::simulation::Simulation::>::new(); + let mut outputs = vec![]; + + for (party_index, i) in party_indexes.iter().copied().zip(0..) { + let share = &shares[party_index]; + let party = simulation.add_party(); + let parties = &parties; // to prevent it being moved into async closure + + outputs.push(async move { + crate::start_digest::(data, i, share, parties, party).await + }); + } + let digests = futures::future::try_join_all(outputs).await.unwrap(); + + let golden = super::digest::(data, &secret_key); + for digest in &digests { + assert_eq!(golden, *digest); + } + } + + fn random_participants>( + n: Int, + t: Int, + rng: &mut impl rand::RngCore, + ) -> Vec { + let n = n.into(); + + let t = t.into(); + assert!(t <= n); + let mut r = Vec::with_capacity(t); + for _ in 0..t { + loop { + let x = rand::Rng::gen_range(rng, 0..n); + if !r.contains(&x) { + r.push(x); + break; + } + } + } + r + } +} diff --git a/src/mpc.rs b/src/mpc.rs new file mode 100644 index 0000000..eed7e50 --- /dev/null +++ b/src/mpc.rs @@ -0,0 +1,70 @@ +use round_based::SinkExt as _; + +#[derive(round_based::ProtocolMessage, Clone, serde::Serialize, serde::Deserialize)] +#[serde(bound = "")] +pub enum Msg { + Partial(MsgPartial), +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(bound = "")] +pub struct MsgPartial { + pub digest: generic_ec::Point, +} + +pub(crate) async fn run( + data: &[u8], + secret_share: &generic_ec::NonZero>, + i: u16, + n: u16, + share_preimages: Option<&[generic_ec::NonZero>]>, + party: M, +) -> Result, Error> +where + D: digest::Digest, + E: generic_ec::Curve, + M: round_based::Mpc>, +{ + let round_based::MpcParty { delivery, .. } = party.into_party(); + let (incomings, mut outgoings) = round_based::Delivery::split(delivery); + + let mut rounds = round_based::rounds_router::RoundsRouter::>::builder(); + let round = + rounds.add_round(round_based::rounds_router::simple_store::RoundInput::broadcast(i, n)); + let mut rounds = rounds.listen(incomings); + + let digest = super::partial_digest::(data, secret_share); + let my_partial = MsgPartial { digest }; + outgoings + .send(round_based::Outgoing::broadcast(Msg::Partial( + my_partial.clone(), + ))) + .await + .map_err(|e| Error::SendMessage(Box::new(e)))?; + + let partials = rounds + .complete(round) + .await + .map_err(|x| Error::RecvMessage(Box::new(x)))?; + let partials = partials + .into_iter_including_me(my_partial) + .map(|s| s.digest) + .collect::>(); + + super::aggregate(&partials, share_preimages).ok_or(Error::AggregateFailed) +} + +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum Error { + /// Send message error + #[error("send message")] + SendMessage(Box), + /// Receive message error + #[error("recv message")] + RecvMessage(Box), + #[error("aggregation failed")] + AggregateFailed, + #[error("creating protocol failed")] + CreationFailed(&'static str), +} From c86a24e5ebae42eaa621181c9fe7a6a51b512c49 Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 2 Dec 2024 17:38:05 +0100 Subject: [PATCH 02/15] Add default workflow --- .github/changelog.sh | 22 +++++++++++ .github/workflows/rust.yml | 79 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100755 .github/changelog.sh create mode 100644 .github/workflows/rust.yml diff --git a/.github/changelog.sh b/.github/changelog.sh new file mode 100755 index 0000000..fa3c35a --- /dev/null +++ b/.github/changelog.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +m_branch=m; +changelog_file=CHANGELOG.md; + +# fetch master since we might be in a shallow clone +git fetch origin "$m_branch:$m_branch" --depth=1 + +changed=0; +for log in "$changelog_file" */"$changelog_file"; do + dir=$(dirname "$log"); + # check if version changed + if git diff "$m_branch" -- "$dir/Cargo.toml" | grep -q "^-version = "; then + # check if changelog updated + if git diff --exit-code --no-patch "$m_branch" -- "$log"; then + echo "$dir version changed, but $log is not updated" + changed=1; + fi + fi +done + +exit "$changed"; diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..8ed336c --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,79 @@ +name: Rust + +on: + pull_request: + branches: [ "*" ] + +env: + CARGO_TERM_COLOR: always + CARGO_NET_GIT_FETCH_WITH_CLI: true + RUSTFLAGS: -D warnings + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Check no features + run: cargo check --no-default-features + check-all: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Check all features + run: cargo check --all-features + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Run tests + run: cargo test --all-features + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check formatting + run: cargo fmt --all -- --check + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Run clippy + run: cargo clippy --all --lib --all-features -- --no-deps -D clippy::all -D clippy::unwrap_used -D clippy::expect_used + clippy-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Run clippy tests + run: cargo clippy --tests --all-features -- -D clippy::all + check-doc: + runs-on: ubuntu-latest + steps: + - uses: dtolnay/rust-toolchain@nightly + - uses: actions/checkout@v3 + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: "true" + - name: Check docs + run: RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo +nightly doc --all-features --no-deps + check-changelog: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check changelogs + run: ./.github/changelog.sh From a075e8a38875481a186e76def974f9ca8f8a88ff Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 2 Dec 2024 17:53:35 +0100 Subject: [PATCH 03/15] Make sha2 a dev-dependency. Add changelog --- Cargo.lock | 2 +- Cargo.toml | 3 ++- changelog.md | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelog.md diff --git a/Cargo.lock b/Cargo.lock index e00ddf8..582f8c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,7 +48,7 @@ dependencies = [ ] [[package]] -name = "bls-style-hash" +name = "bls-style-digest" version = "0.1.0" dependencies = [ "digest", diff --git a/Cargo.toml b/Cargo.toml index e53b7ad..c05269f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,15 +9,16 @@ generic-ec = { version = "0.4", features = ["serde", "curve-ed25519", "hash-to-s generic-ec-zkp = "0.4" key-share = "0.5" round-based = { version = "0.3", features = ["derive"] } -sha2 = "0.10" serde = "1" thiserror = { version = "2" } [dev-dependencies] futures = "0.3" +generic-ec = { version = "0.4", features = ["curve-ed25519"] } key-share = { version = "0.5", features = ["spof"] } rand = "0.8" rand_dev = "0.1" round-based = { version = "0.3", features = ["dev"] } +sha2 = "0.10" test-case = "3" tokio = { version = "1", features = ["macros"] } diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..e1ce5d4 --- /dev/null +++ b/changelog.md @@ -0,0 +1,3 @@ +# v0.1.0 + +Initial implementation From a2f26cb93c4ed8d26f60d9bd5d44eee95e427c88 Mon Sep 17 00:00:00 2001 From: maurges Date: Fri, 6 Dec 2024 11:40:16 +0100 Subject: [PATCH 04/15] Use newer round-based --- Cargo.lock | 261 +---------------------------------------------------- Cargo.toml | 6 +- src/lib.rs | 41 ++++----- 3 files changed, 22 insertions(+), 286 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 582f8c2..08e9cf1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,42 +2,12 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -52,7 +22,6 @@ name = "bls-style-digest" version = "0.1.0" dependencies = [ "digest", - "futures", "generic-ec", "generic-ec-zkp", "key-share", @@ -63,7 +32,6 @@ dependencies = [ "sha2", "test-case", "thiserror 2.0.3", - "tokio", ] [[package]] @@ -72,12 +40,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" - [[package]] name = "cfg-if" version = "1.0.0" @@ -234,65 +196,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.89", -] - [[package]] name = "futures-sink" version = "0.3.31" @@ -311,16 +220,11 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", - "futures-io", - "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", - "slab", ] [[package]] @@ -406,12 +310,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "group" version = "0.13.0" @@ -454,21 +352,6 @@ version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -497,15 +380,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - [[package]] name = "phantom-type" version = "0.3.1" @@ -617,17 +491,14 @@ dependencies = [ [[package]] name = "round-based" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81564866f5617d497753563151d8beb80d61e925e904d94b7e8a202b721e931e" +checksum = "079e623c882b5ec9c1a4140cb077179ea89f5352f85a7ebd879428c33ce66399" dependencies = [ - "displaydoc", "futures-util", "phantom-type 0.3.1", "round-based-derive", - "thiserror 1.0.69", - "tokio", - "tokio-stream", + "thiserror 2.0.3", "tracing", ] @@ -642,12 +513,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - [[package]] name = "rustc_version" version = "0.4.1" @@ -716,15 +581,6 @@ dependencies = [ "digest", ] -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - [[package]] name = "strsim" version = "0.11.1" @@ -832,53 +688,6 @@ dependencies = [ "syn 2.0.89", ] -[[package]] -name = "tokio" -version = "1.41.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" -dependencies = [ - "backtrace", - "pin-project-lite", - "tokio-macros", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.89", -] - -[[package]] -name = "tokio-stream" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "tracing" version = "0.1.40" @@ -940,70 +749,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index c05269f..c324b47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,17 +8,15 @@ digest = "0.10" generic-ec = { version = "0.4", features = ["serde", "curve-ed25519", "hash-to-scalar"] } generic-ec-zkp = "0.4" key-share = "0.5" -round-based = { version = "0.3", features = ["derive"] } +round-based = { version = "0.4", features = ["derive"] } serde = "1" thiserror = { version = "2" } [dev-dependencies] -futures = "0.3" generic-ec = { version = "0.4", features = ["curve-ed25519"] } key-share = { version = "0.5", features = ["spof"] } rand = "0.8" rand_dev = "0.1" -round-based = { version = "0.3", features = ["dev"] } +round-based = { version = "0.4", features = ["sim"] } sha2 = "0.10" test-case = "3" -tokio = { version = "1", features = ["macros"] } diff --git a/src/lib.rs b/src/lib.rs index 2cd232a..8816e3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,16 +87,19 @@ where E: generic_ec::Curve, M: round_based::Mpc>, { - let share_preimages = match key_share.vss_setup.as_ref() { - None => None, - Some(vss_setup) => Some( - participants - .iter() - .map(|i| vss_setup.I.get(usize::from(*i)).copied()) - .collect::>>() - .ok_or(mpc::Error::CreationFailed("share is not SSS"))?, - ), - }; + let share_preimages = key_share + .vss_setup + .as_ref() + .map({ + |vss_setup| { + participants + .iter() + .map(|i| vss_setup.I.get(usize::from(*i)).copied()) + .collect::>>() + .ok_or(mpc::Error::CreationFailed("share is not SSS")) + } + }) + .transpose()?; let share_preimages = share_preimages.as_ref().map(|v| v.as_ref()); mpc::run::( @@ -145,8 +148,7 @@ mod test { #[test_case::test_case(3, 5; "t3n5")] #[test_case::test_case(5, 5; "t5n5")] #[test_case::test_case(3, 7; "t3n7")] - #[tokio::test] - async fn protocol_same_as_digest(t: u16, n: u16) { + fn protocol_same_as_digest(t: u16, n: u16) { let mut rng = rand_dev::DevRng::new(); let secret_key = generic_ec::NonZero::>::random(&mut rng); @@ -163,19 +165,10 @@ mod test { .map(|x| u16::try_from(*x).unwrap()) .collect::>(); - let mut simulation = round_based::simulation::Simulation::>::new(); - let mut outputs = vec![]; - - for (party_index, i) in party_indexes.iter().copied().zip(0..) { + let digests = round_based::sim::run_with_setup(party_indexes.iter().copied(), |i, party, party_index| { let share = &shares[party_index]; - let party = simulation.add_party(); - let parties = &parties; // to prevent it being moved into async closure - - outputs.push(async move { - crate::start_digest::(data, i, share, parties, party).await - }); - } - let digests = futures::future::try_join_all(outputs).await.unwrap(); + crate::start_digest::(data, i, share, &parties, party) + }).unwrap().expect_ok().into_vec(); let golden = super::digest::(data, &secret_key); for digest in &digests { From 38010984687112434c30e10ce22c83e0ef63ae40 Mon Sep 17 00:00:00 2001 From: maurges Date: Wed, 18 Dec 2024 16:39:37 +0100 Subject: [PATCH 05/15] Use hash to curve --- Cargo.lock | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 ++- src/lib.rs | 97 +++++++++++++++++++++++-------- src/mpc.rs | 11 +++- 4 files changed, 251 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08e9cf1..98295a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "block-buffer" version = "0.10.4" @@ -23,7 +35,9 @@ version = "0.1.0" dependencies = [ "digest", "generic-ec", + "generic-ec-curves", "generic-ec-zkp", + "k256", "key-share", "rand", "rand_dev", @@ -46,6 +60,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "cpufeatures" version = "0.2.15" @@ -55,6 +75,18 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -128,6 +160,16 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "digest" version = "0.10.7" @@ -135,7 +177,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -149,6 +193,20 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "educe" version = "0.4.23" @@ -161,6 +219,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "enum-ordinalize" version = "3.1.15" @@ -235,6 +312,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -278,9 +356,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e663405a17b229dede990904edcfd2056cf6641f1240869a1f44fa13bd4ee4d" dependencies = [ "curve25519-dalek", + "elliptic-curve", "generic-ec-core", "group", + "k256", "rand_core", + "sha2", "subtle", "zeroize", ] @@ -327,12 +408,35 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "key-share" version = "0.5.0" @@ -380,6 +484,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "phantom-type" version = "0.3.1" @@ -410,6 +520,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -489,6 +609,16 @@ dependencies = [ "udigest", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "round-based" version = "0.4.0" @@ -522,6 +652,20 @@ dependencies = [ "semver", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.23" @@ -581,6 +725,26 @@ dependencies = [ "digest", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index c324b47..1bb97d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,18 +5,21 @@ edition = "2021" [dependencies] digest = "0.10" -generic-ec = { version = "0.4", features = ["serde", "curve-ed25519", "hash-to-scalar"] } +generic-ec = { version = "0.4", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } generic-ec-zkp = "0.4" key-share = "0.5" round-based = { version = "0.4", features = ["derive"] } serde = "1" +sha2 = "0.10" thiserror = { version = "2" } +k256 = { version = "0.13", features = ["hash2curve"] } +generic-ec-curves = { version = "0.2", features = ["rust-crypto"] } + [dev-dependencies] generic-ec = { version = "0.4", features = ["curve-ed25519"] } key-share = { version = "0.5", features = ["spof"] } rand = "0.8" rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } -sha2 = "0.10" test-case = "3" diff --git a/src/lib.rs b/src/lib.rs index 8816e3a..c9919d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,31 +1,74 @@ //! This crate implements digesting into elliptic curve points keyed with a //! secret scalar for this curve. This digest resembles BLS signatures, but //! because we don't do signature verification, we're not limited in the choice -//! of curves. The procedure for a digest is as follows: +//! of curves. Hashing to curve is based on rfc9380 with SHA2 hash and +//! expand_message_xmd expansion. The procedure for a digest is as follows: //! //! 1. Input parameters: -//! * `D` - cryptographic hash function //! * `E` - an elliptic curve //! * `m` - message to digest, given as a byte string //! * `x` - secret key, given as a scalar for `E` -//! 2. Create `HPRng` - a pseudo-random number generator based on `D` and `m`, -//! with a procedure described in -//! [`hash_rng`](https://docs.rs/rand_hash/0.1.1/rand_hash/index.html) -//! 3. Generate random `s` from `HPRng` by using a procedure defined for this -//! curve -//! 4. If `s` is zero, retry from step *3*. If `s` is zero after 100 attempts, -//! abort -//! 5. Compute `generator(E) * s * x` - +//! 2. Initialize `i` to 0 +//! 2. Use the procedure in rfc9380 to hash the data `i || m` to curve point `p` +//! 4. If `p` the procedure failed, or if `p` is zero or an invalid point, +//! increment `i` and retry from step *3*. If `p` 100 attempts already +//! elapsed, abort +//! 5. Compute `p * x` + +#![warn(missing_docs, unsafe_code, unused_crate_dependencies)] +#![cfg_attr( + not(test), + deny(clippy::expect_used, clippy::unwrap_used, clippy::panic) +)] + +/// Helper types for the MPC execution pub mod mpc; +mod int { + pub trait HashToCurve: generic_ec::Curve { + fn hash_to_curve( + messages: &[&[u8]], + dst: &[u8], + ) -> Option>>; + } +} + +impl int::HashToCurve for generic_ec::curves::Secp256k1 { + fn hash_to_curve(messages: &[&[u8]], dst: &[u8]) -> Option>> { + use generic_ec::curves::Secp256k1; + type ExtendedHash = k256::elliptic_curve::hash2curve::ExpandMsgXmd; + use k256::elliptic_curve::hash2curve::GroupDigest as _; + // This can fail due to arithmetic overflows or internal bugs like + // passing an empty buffer to fill. They should never happen + // theoretically + let plain = k256::Secp256k1::hash_from_bytes::(messages, &[dst]).ok()?; + let plain = generic_ec_curves::rust_crypto::RustCryptoPoint(plain); + // Can fail if point: is not on curve, is not torsion free + let plain: generic_ec::Point = + generic_ec::as_raw::TryFromRaw::try_from_raw(plain)?; + // Can fail if point is zero + generic_ec::NonZero::try_from(plain).ok() + } +} + +fn hash_to_curve(message: &[u8], dst: &[u8]) -> generic_ec::NonZero> { + for i in 0..=255u8 { + if let Some(r) = E::hash_to_curve(&[&[i], message], dst) { + return r; + } + } + #[allow(clippy::panic)] + { + panic!("ugh"); + } +} + /// Compute digest of `data` keyed with `secret_key` -pub fn digest( +pub fn digest( data: &[u8], secret_key: &generic_ec::NonZero>, -) -> generic_ec::Point { - let plain_scalar = generic_ec::Scalar::::from_hash::(&data); - let plain = generic_ec::Point::generator() * plain_scalar; +) -> generic_ec::NonZero> { + let plain = hash_to_curve(data, b"dfns-bls-style-hash"); plain * secret_key } @@ -35,10 +78,10 @@ pub fn digest( /// /// Partial digest is exactly the same as a full digest; this is a convenient /// alias if you want to distinguish the functionality in your code. -pub fn partial_digest( +pub fn partial_digest( data: &[u8], secret_share: &generic_ec::NonZero>, -) -> generic_ec::Point { +) -> generic_ec::NonZero> { digest::(data, secret_share) } @@ -48,7 +91,7 @@ pub fn partial_digest( /// gives the points at which the keyshare values are computed, and should be in /// the same order by participant as `partials`. pub fn aggregate( - partials: &[generic_ec::Point], + partials: &[generic_ec::NonZero>], share_preimages: Option<&[generic_ec::NonZero>]>, ) -> Option> { if let Some(share_preimages) = share_preimages { @@ -84,7 +127,7 @@ pub async fn start_digest( ) -> Result, mpc::Error> where D: digest::Digest, - E: generic_ec::Curve, + E: int::HashToCurve, M: round_based::Mpc>, { let share_preimages = key_share @@ -115,7 +158,7 @@ where #[cfg(test)] mod test { - type E = generic_ec::curves::Ed25519; + type E = generic_ec::curves::Secp256k1; #[test_case::test_case(3, 5; "t3n5")] #[test_case::test_case(5, 5; "t5n5")] @@ -165,10 +208,16 @@ mod test { .map(|x| u16::try_from(*x).unwrap()) .collect::>(); - let digests = round_based::sim::run_with_setup(party_indexes.iter().copied(), |i, party, party_index| { - let share = &shares[party_index]; - crate::start_digest::(data, i, share, &parties, party) - }).unwrap().expect_ok().into_vec(); + let digests = round_based::sim::run_with_setup( + party_indexes.iter().copied(), + |i, party, party_index| { + let share = &shares[party_index]; + crate::start_digest::(data, i, share, &parties, party) + }, + ) + .unwrap() + .expect_ok() + .into_vec(); let golden = super::digest::(data, &secret_key); for digest in &digests { diff --git a/src/mpc.rs b/src/mpc.rs index eed7e50..ea00989 100644 --- a/src/mpc.rs +++ b/src/mpc.rs @@ -1,15 +1,19 @@ use round_based::SinkExt as _; +/// Protocol message #[derive(round_based::ProtocolMessage, Clone, serde::Serialize, serde::Deserialize)] #[serde(bound = "")] pub enum Msg { + /// The only round Partial(MsgPartial), } +/// Protocol message #[derive(Clone, serde::Serialize, serde::Deserialize)] #[serde(bound = "")] pub struct MsgPartial { - pub digest: generic_ec::Point, + /// Partial digest of a party + pub digest: generic_ec::NonZero>, } pub(crate) async fn run( @@ -22,7 +26,7 @@ pub(crate) async fn run( ) -> Result, Error> where D: digest::Digest, - E: generic_ec::Curve, + E: super::int::HashToCurve, M: round_based::Mpc>, { let round_based::MpcParty { delivery, .. } = party.into_party(); @@ -54,6 +58,7 @@ where super::aggregate(&partials, share_preimages).ok_or(Error::AggregateFailed) } +/// Error with a reason for protocol start or execution failure #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum Error { @@ -63,8 +68,10 @@ pub enum Error { /// Receive message error #[error("recv message")] RecvMessage(Box), + /// Aggregation failure #[error("aggregation failed")] AggregateFailed, + /// Failure to start #[error("creating protocol failed")] CreationFailed(&'static str), } From 70d60e306d672aea5533f39c4cc4cff3b7ff3c45 Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 30 Dec 2024 16:35:17 +0100 Subject: [PATCH 06/15] Use new non-failing generic-ec API. Better document failures --- Cargo.lock | 17 +++++++---------- Cargo.toml | 4 ++++ src/lib.rs | 30 ++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98295a4..11c11cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" @@ -317,9 +317,8 @@ dependencies = [ [[package]] name = "generic-ec" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e3268b3f97e2046ebf69e24a1e7e12dad4dec247d87f59268fd57ab514b5f8" +version = "0.4.4" +source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=d072be9085615ee63d34ddda7a3b39cf103533de#d072be9085615ee63d34ddda7a3b39cf103533de" dependencies = [ "curve25519-dalek", "digest", @@ -338,9 +337,8 @@ dependencies = [ [[package]] name = "generic-ec-core" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049128cc67cac6176ada5218e294ce46421470d92a7340c93d5cfd3ecfbc29a4" +version = "0.2.2" +source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=d072be9085615ee63d34ddda7a3b39cf103533de#d072be9085615ee63d34ddda7a3b39cf103533de" dependencies = [ "generic-array", "rand_core", @@ -351,9 +349,8 @@ dependencies = [ [[package]] name = "generic-ec-curves" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e663405a17b229dede990904edcfd2056cf6641f1240869a1f44fa13bd4ee4d" +version = "0.2.3" +source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=d072be9085615ee63d34ddda7a3b39cf103533de#d072be9085615ee63d34ddda7a3b39cf103533de" dependencies = [ "curve25519-dalek", "elliptic-curve", diff --git a/Cargo.toml b/Cargo.toml index 1bb97d3..39f24b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,7 @@ rand = "0.8" rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } test-case = "3" + +[patch.crates-io] +generic-ec = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "d072be9085615ee63d34ddda7a3b39cf103533de" } +generic-ec-curves = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "d072be9085615ee63d34ddda7a3b39cf103533de" } diff --git a/src/lib.rs b/src/lib.rs index c9919d5..d156e39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,9 @@ pub mod mpc; mod int { pub trait HashToCurve: generic_ec::Curve { + /// This function may fail, but the probability of that must be low. If + /// it fails, we retry with a different prefix. If it fails too many + /// times, we panic fn hash_to_curve( messages: &[&[u8]], dst: &[u8], @@ -34,24 +37,31 @@ mod int { } impl int::HashToCurve for generic_ec::curves::Secp256k1 { - fn hash_to_curve(messages: &[&[u8]], dst: &[u8]) -> Option>> { - use generic_ec::curves::Secp256k1; + fn hash_to_curve( + messages: &[&[u8]], + dst: &[u8], + ) -> Option>> { type ExtendedHash = k256::elliptic_curve::hash2curve::ExpandMsgXmd; use k256::elliptic_curve::hash2curve::GroupDigest as _; - // This can fail due to arithmetic overflows or internal bugs like - // passing an empty buffer to fill. They should never happen - // theoretically + // This can fail if: + // 1. No domain separation tag is given + // 2. Output length is zero - impossible + // 3. Output length is longer than u16::MAX - impossible + // 4. Output length is grater than 255 * 32 - impossible + // 5. Output length overflows usize - impossible let plain = k256::Secp256k1::hash_from_bytes::(messages, &[dst]).ok()?; let plain = generic_ec_curves::rust_crypto::RustCryptoPoint(plain); - // Can fail if point: is not on curve, is not torsion free - let plain: generic_ec::Point = - generic_ec::as_raw::TryFromRaw::try_from_raw(plain)?; + let plain: generic_ec::Point = + generic_ec::as_raw::FromRaw::from_raw(plain); // Can fail if point is zero generic_ec::NonZero::try_from(plain).ok() } } -fn hash_to_curve(message: &[u8], dst: &[u8]) -> generic_ec::NonZero> { +fn hash_to_curve( + message: &[u8], + dst: &[u8], +) -> generic_ec::NonZero> { for i in 0..=255u8 { if let Some(r) = E::hash_to_curve(&[&[i], message], dst) { return r; @@ -59,7 +69,7 @@ fn hash_to_curve(message: &[u8], dst: &[u8]) -> generic_ec: } #[allow(clippy::panic)] { - panic!("ugh"); + panic!("Bad curve or hash algorithm: too many failures"); } } From f911a2d51d67bfe45b8e92a1e62a2aca5278f579 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 16 Jan 2025 16:16:01 +0100 Subject: [PATCH 07/15] Slight rewrite to make it based on the t-BLS paper Previous version was naive thresholdizing --- Cargo.lock | 9 ++-- Cargo.toml | 7 ++- src/lib.rs | 107 ++++++++++++++++++++++++++++++++++++++++----- src/mpc.rs | 23 +++++++--- src/zkp.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 248 insertions(+), 23 deletions(-) create mode 100644 src/zkp.rs diff --git a/Cargo.lock b/Cargo.lock index 11c11cb..9b1a4b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,12 +40,15 @@ dependencies = [ "k256", "key-share", "rand", + "rand_core", "rand_dev", + "rand_hash", "round-based", "serde", "sha2", "test-case", "thiserror 2.0.3", + "udigest", ] [[package]] @@ -318,7 +321,7 @@ dependencies = [ [[package]] name = "generic-ec" version = "0.4.4" -source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=d072be9085615ee63d34ddda7a3b39cf103533de#d072be9085615ee63d34ddda7a3b39cf103533de" +source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=fafa3ed01a54c988ee78e4e7eab974fe4fd461c7#fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" dependencies = [ "curve25519-dalek", "digest", @@ -338,7 +341,7 @@ dependencies = [ [[package]] name = "generic-ec-core" version = "0.2.2" -source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=d072be9085615ee63d34ddda7a3b39cf103533de#d072be9085615ee63d34ddda7a3b39cf103533de" +source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=fafa3ed01a54c988ee78e4e7eab974fe4fd461c7#fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" dependencies = [ "generic-array", "rand_core", @@ -350,7 +353,7 @@ dependencies = [ [[package]] name = "generic-ec-curves" version = "0.2.3" -source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=d072be9085615ee63d34ddda7a3b39cf103533de#d072be9085615ee63d34ddda7a3b39cf103533de" +source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=fafa3ed01a54c988ee78e4e7eab974fe4fd461c7#fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" dependencies = [ "curve25519-dalek", "elliptic-curve", diff --git a/Cargo.toml b/Cargo.toml index 39f24b3..62f0af4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,13 @@ digest = "0.10" generic-ec = { version = "0.4", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } generic-ec-zkp = "0.4" key-share = "0.5" +rand_core = { version = "0.6", default-features = false } +rand_hash = { version = "0.1" } round-based = { version = "0.4", features = ["derive"] } serde = "1" sha2 = "0.10" thiserror = { version = "2" } +udigest = { version = "0.2", default-features = false, features = ["inline-struct", "derive"] } k256 = { version = "0.13", features = ["hash2curve"] } generic-ec-curves = { version = "0.2", features = ["rust-crypto"] } @@ -25,5 +28,5 @@ round-based = { version = "0.4", features = ["sim"] } test-case = "3" [patch.crates-io] -generic-ec = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "d072be9085615ee63d34ddda7a3b39cf103533de" } -generic-ec-curves = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "d072be9085615ee63d34ddda7a3b39cf103533de" } +generic-ec = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" } +generic-ec-curves = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" } diff --git a/src/lib.rs b/src/lib.rs index d156e39..7335cba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ /// Helper types for the MPC execution pub mod mpc; +mod zkp; mod int { pub trait HashToCurve: generic_ec::Curve { @@ -89,10 +90,21 @@ pub fn digest( /// Partial digest is exactly the same as a full digest; this is a convenient /// alias if you want to distinguish the functionality in your code. pub fn partial_digest( + shared_state: &impl udigest::Digestable, data: &[u8], secret_share: &generic_ec::NonZero>, -) -> generic_ec::NonZero> { - digest::(data, secret_share) + rng: &mut impl rand_core::RngCore, +) -> (generic_ec::NonZero>, zkp::Proof) { + let plain = hash_to_curve(data, b"dfns-bls-style-hash"); + let digest = plain * secret_share; + let proof_data = zkp::Data { + pub_share: (generic_ec::Point::generator() * secret_share).into_inner(), + base: plain.into_inner(), + value: digest.into_inner(), + }; + let r = generic_ec::Scalar::random(rng); + let proof = zkp::prove::(shared_state, secret_share, proof_data, r); + (digest, proof) } /// Aggregate `partials` - partial digest values - into a full value. @@ -100,24 +112,58 @@ pub fn partial_digest( /// `share_preimages` should be `None` for additive key shares. For SSS, it /// gives the points at which the keyshare values are computed, and should be in /// the same order by participant as `partials`. -pub fn aggregate( - partials: &[generic_ec::NonZero>], +pub fn aggregate( + data: &[u8], + partials: &[(generic_ec::NonZero>, zkp::Proof)], + public_shares: &[generic_ec::NonZero>], + eid: &[u8], share_preimages: Option<&[generic_ec::NonZero>]>, -) -> Option> { +) -> Result, AggregateFailed> { + let base = hash_to_curve(data, b"dfns-bls-style-hash"); + let mut blame = Vec::new(); + for (((value, proof), pub_share), j) in partials.iter().zip(public_shares).zip(0..) { + let shared_state = zkp::SharedState { + eid, + prover_index: j, + }; + let data = zkp::Data { + pub_share: pub_share.into_inner(), + base: base.into_inner(), + value: value.into_inner(), + }; + if zkp::verify::(&shared_state, data, *proof).is_err() { + blame.push(j); + }; + } if let Some(share_preimages) = share_preimages { // shamir aggregation let lagrange_coefficients = (0..(share_preimages.len())) .map(|j| generic_ec_zkp::polynomial::lagrange_coefficient_at_zero(j, share_preimages)) - .collect::>>()?; - Some(generic_ec::Scalar::multiscalar_mul( - lagrange_coefficients.into_iter().zip(partials), + .collect::>>() + .ok_or(AggregateFailed::Lagrange)?; + Ok(generic_ec::Scalar::multiscalar_mul( + lagrange_coefficients + .into_iter() + .zip(partials.iter().map(|t| t.0)), )) } else { // additive aggregation - Some(partials.iter().sum()) + Ok(partials.iter().map(|t| t.0).sum()) } } +/// Error for aggregation failing +#[derive(Debug, Clone, thiserror::Error)] +pub enum AggregateFailed { + /// Lagrange polynomial construction failed, probably because some points + /// repeat + #[error("lagrange interpolation failed")] + Lagrange, + /// Party ZKP verification failed + #[error("honesty verification failed for parties: {0:?}")] + Verification(Vec), +} + /// Start an MPC protocol that digests the data with shared private key. Returns /// digested data /// @@ -129,11 +175,13 @@ pub fn aggregate( /// shares. /// - `party` - the `round-based` party pub async fn start_digest( + eid: &[u8], data: &[u8], i: u16, key_share: &key_share::CoreKeyShare, participants: &[u16], party: M, + rng: &mut impl rand_core::RngCore, ) -> Result, mpc::Error> where D: digest::Digest, @@ -154,14 +202,22 @@ where }) .transpose()?; let share_preimages = share_preimages.as_ref().map(|v| v.as_ref()); + let public_shares = &key_share.public_shares; + let public_shares = participants + .iter() + .map(|i| public_shares[usize::from(*i)]) + .collect::>(); mpc::run::( + eid, data, &key_share.x, i, key_share.min_signers(), + &public_shares, share_preimages, party, + rng, ) .await } @@ -184,16 +240,36 @@ mod test { .set_shared_secret_key(secret_key.clone()) .generate_shares(&mut rng) .unwrap(); + let public_shares = &shares[0].public_shares; let share_preimages = shares[0].vss_setup.as_ref().map(|vss| &vss.I[0..t]); let data = b"take that you worm"; + let eid = b"test"; let digest = super::digest::(data, &secret_key); let partials = shares .iter() - .map(|s| super::partial_digest::(data, &s.x)) + .zip(0..) + .map(|(s, i)| { + super::partial_digest::( + &crate::zkp::SharedState { + prover_index: i, + eid, + }, + data, + &s.x, + &mut rng, + ) + }) .collect::>(); - let restored = super::aggregate(&partials[0..t], share_preimages).unwrap(); + let restored = super::aggregate::( + data, + &partials[0..t], + public_shares, + eid, + share_preimages, + ) + .unwrap(); assert_eq!(restored, digest); } @@ -221,8 +297,15 @@ mod test { let digests = round_based::sim::run_with_setup( party_indexes.iter().copied(), |i, party, party_index| { + let mut rng = rng.fork(); let share = &shares[party_index]; - crate::start_digest::(data, i, share, &parties, party) + let parties = &parties; + async move { + crate::start_digest::( + b"test", data, i, share, parties, party, &mut rng, + ) + .await + } }, ) .unwrap() diff --git a/src/mpc.rs b/src/mpc.rs index ea00989..aa9a776 100644 --- a/src/mpc.rs +++ b/src/mpc.rs @@ -14,15 +14,21 @@ pub enum Msg { pub struct MsgPartial { /// Partial digest of a party pub digest: generic_ec::NonZero>, + /// Proof of valid execution + pub proof: crate::zkp::Proof, } +#[allow(clippy::too_many_arguments)] pub(crate) async fn run( + eid: &[u8], data: &[u8], secret_share: &generic_ec::NonZero>, i: u16, n: u16, + public_shares: &[generic_ec::NonZero>], share_preimages: Option<&[generic_ec::NonZero>]>, party: M, + rng: &mut impl rand_core::RngCore, ) -> Result, Error> where D: digest::Digest, @@ -37,8 +43,12 @@ where rounds.add_round(round_based::rounds_router::simple_store::RoundInput::broadcast(i, n)); let mut rounds = rounds.listen(incomings); - let digest = super::partial_digest::(data, secret_share); - let my_partial = MsgPartial { digest }; + let shared_state = udigest::inline_struct!("PrEq" { + eid, + prover_index: i, + }); + let (digest, proof) = super::partial_digest::(&shared_state, data, secret_share, rng); + let my_partial = MsgPartial { digest, proof }; outgoings .send(round_based::Outgoing::broadcast(Msg::Partial( my_partial.clone(), @@ -52,10 +62,11 @@ where .map_err(|x| Error::RecvMessage(Box::new(x)))?; let partials = partials .into_iter_including_me(my_partial) - .map(|s| s.digest) + .map(|s| (s.digest, s.proof)) .collect::>(); - super::aggregate(&partials, share_preimages).ok_or(Error::AggregateFailed) + super::aggregate::(data, &partials, public_shares, eid, share_preimages) + .map_err(Error::AggregateFailed) } /// Error with a reason for protocol start or execution failure @@ -69,8 +80,8 @@ pub enum Error { #[error("recv message")] RecvMessage(Box), /// Aggregation failure - #[error("aggregation failed")] - AggregateFailed, + #[error("aggregation failed: {0}")] + AggregateFailed(crate::AggregateFailed), /// Failure to start #[error("creating protocol failed")] CreationFailed(&'static str), diff --git a/src/zkp.rs b/src/zkp.rs new file mode 100644 index 0000000..d38939d --- /dev/null +++ b/src/zkp.rs @@ -0,0 +1,125 @@ +const TAG: &str = "bls-style-digest.zkp"; + +#[derive(Debug, Clone, udigest::Digestable)] +pub struct SharedState<'a> { + pub eid: &'a [u8], + pub prover_index: u16, +} + +#[derive(Debug, Clone, Copy, udigest::Digestable)] +#[udigest(bound = "")] +pub struct Data { + pub pub_share: generic_ec::Point, + pub base: generic_ec::Point, + pub value: generic_ec::Point, +} + +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +#[serde(bound = "")] +pub struct Proof { + pub ch: generic_ec::Scalar, + pub res: generic_ec::Scalar, +} + +pub fn prove( + shared_state: &impl udigest::Digestable, + share: &generic_ec::SecretScalar, + data: Data, + r: generic_ec::Scalar, +) -> Proof { + let com1 = generic_ec::Point::generator() * r; + let com2 = data.base * r; + + let seed = udigest::inline_struct!(TAG { + shared_state, + data, + com1, + com2, + }); + let mut rng = rand_hash::HashRng::::from_seed(seed); + let ch = generic_ec::Scalar::random(&mut rng); + + let res = r + share * ch; + Proof { ch, res } +} + +pub fn verify( + shared_state: &impl udigest::Digestable, + data: Data, + proof: Proof, +) -> Result<(), InvalidProof> { + let com1 = generic_ec::Point::generator() * proof.res - data.pub_share * proof.ch; + let com2 = data.base * proof.res - data.value * proof.ch; + + let seed = udigest::inline_struct!(TAG { + shared_state, + data, + com1, + com2, + }); + let mut rng = rand_hash::HashRng::::from_seed(seed); + let ch = generic_ec::Scalar::random(&mut rng); + + if ch != proof.ch { + Err(InvalidProof) + } else { + Ok(()) + } +} + +#[derive(Debug)] +pub struct InvalidProof; + +#[cfg(test)] +mod test { + fn passing_test() { + let mut rng = rand_dev::DevRng::new(); + let shared_state = "shared state"; + + let secret_share = generic_ec::SecretScalar::random(&mut rng); + let public_share = generic_ec::Point::generator() * &secret_share; + + let raw_digest = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + let digest = raw_digest * &secret_share; + + let r = generic_ec::Scalar::random(&mut rng); + let data = super::Data { + pub_share: public_share, + base: raw_digest, + value: digest, + }; + let proof = super::prove::(&shared_state, &secret_share, data, r); + super::verify::(&shared_state, data, proof).unwrap(); + } + + fn failing_test() { + let mut rng = rand_dev::DevRng::new(); + let shared_state = "shared state"; + + let secret_share = generic_ec::SecretScalar::random(&mut rng); + let public_share = generic_ec::Point::generator() * &secret_share; + + let raw_digest = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + // fake digest is our concern + let digest = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); + + let r = generic_ec::Scalar::random(&mut rng); + let data = super::Data { + pub_share: public_share, + base: raw_digest, + value: digest, + }; + let proof = super::prove::(&shared_state, &secret_share, data, r); + assert!(super::verify::(&shared_state, data, proof).is_err()); + } + + #[test] + fn passing_k256_sha256() { + passing_test::(); + } + + #[test] + fn failing_k256_sha256() { + failing_test::(); + } +} From 51fe93f1c90282e664d258e9da36d9582fce4fdd Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 20 Jan 2025 14:17:54 +0100 Subject: [PATCH 08/15] Remove patched deps --- Cargo.lock | 15 +++++++++------ Cargo.toml | 10 +++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b1a4b1..4d150f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,8 +320,9 @@ dependencies = [ [[package]] name = "generic-ec" -version = "0.4.4" -source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=fafa3ed01a54c988ee78e4e7eab974fe4fd461c7#fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de1099ac0b4d87261d67ff5d4ed400af617a1da40b58908d759b9cf5fd8ed27" dependencies = [ "curve25519-dalek", "digest", @@ -340,8 +341,9 @@ dependencies = [ [[package]] name = "generic-ec-core" -version = "0.2.2" -source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=fafa3ed01a54c988ee78e4e7eab974fe4fd461c7#fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcba5fdf70cc3ce5805c487f8523b4ceeb32e8ec5237c71ffd93c1ca47a97fee" dependencies = [ "generic-array", "rand_core", @@ -352,8 +354,9 @@ dependencies = [ [[package]] name = "generic-ec-curves" -version = "0.2.3" -source = "git+https://github.com/LFDT-Lockness/generic-ec?rev=fafa3ed01a54c988ee78e4e7eab974fe4fd461c7#fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7c6d23001a5eb60eec2b785a63d2ca965fdfbaf3314b3b46df047398369e28" dependencies = [ "curve25519-dalek", "elliptic-curve", diff --git a/Cargo.toml b/Cargo.toml index 62f0af4..6919887 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] digest = "0.10" -generic-ec = { version = "0.4", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } +generic-ec = { version = "0.4.5", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } generic-ec-zkp = "0.4" key-share = "0.5" rand_core = { version = "0.6", default-features = false } @@ -17,16 +17,12 @@ thiserror = { version = "2" } udigest = { version = "0.2", default-features = false, features = ["inline-struct", "derive"] } k256 = { version = "0.13", features = ["hash2curve"] } -generic-ec-curves = { version = "0.2", features = ["rust-crypto"] } +generic-ec-curves = { version = "0.2.4", features = ["rust-crypto"] } [dev-dependencies] -generic-ec = { version = "0.4", features = ["curve-ed25519"] } +generic-ec = { version = "0.4.5", features = ["curve-ed25519"] } key-share = { version = "0.5", features = ["spof"] } rand = "0.8" rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } test-case = "3" - -[patch.crates-io] -generic-ec = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" } -generic-ec-curves = { git = "https://github.com/LFDT-Lockness/generic-ec", rev = "fafa3ed01a54c988ee78e4e7eab974fe4fd461c7" } From be853bc39b07505b97bdb300e1eb41aa79c0c6b1 Mon Sep 17 00:00:00 2001 From: maurges Date: Tue, 21 Jan 2025 17:00:12 +0100 Subject: [PATCH 09/15] Improve docs and paper compilance --- src/lib.rs | 218 ++++++++++++++++++++++++++++++++--------------------- src/mpc.rs | 24 +++--- 2 files changed, 142 insertions(+), 100 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7335cba..dcee5b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,11 +9,17 @@ //! * `m` - message to digest, given as a byte string //! * `x` - secret key, given as a scalar for `E` //! 2. Initialize `i` to 0 -//! 2. Use the procedure in rfc9380 to hash the data `i || m` to curve point `p` +//! 2. Use the procedure in rfc9380 to hash the data `i as u8 || m` to curve point `p` //! 4. If `p` the procedure failed, or if `p` is zero or an invalid point, -//! increment `i` and retry from step *3*. If `p` 100 attempts already +//! increment `i` and retry from step *3*. If 256 attempts have already //! elapsed, abort //! 5. Compute `p * x` +//! +//! The distributed digest is based on https://eprint.iacr.org/2020/096 +//! That is, the partial digest is computed in the same way as a regular digest +//! albeit with a shared key, and in addition a ZK proof of honest digest +//! computation is produced, which is checked when aggregating partial digests. +//! We use the DDH-based DVRF instatiation with non-compact proofs #![warn(missing_docs, unsafe_code, unused_crate_dependencies)] #![cfg_attr( @@ -25,57 +31,8 @@ pub mod mpc; mod zkp; -mod int { - pub trait HashToCurve: generic_ec::Curve { - /// This function may fail, but the probability of that must be low. If - /// it fails, we retry with a different prefix. If it fails too many - /// times, we panic - fn hash_to_curve( - messages: &[&[u8]], - dst: &[u8], - ) -> Option>>; - } -} - -impl int::HashToCurve for generic_ec::curves::Secp256k1 { - fn hash_to_curve( - messages: &[&[u8]], - dst: &[u8], - ) -> Option>> { - type ExtendedHash = k256::elliptic_curve::hash2curve::ExpandMsgXmd; - use k256::elliptic_curve::hash2curve::GroupDigest as _; - // This can fail if: - // 1. No domain separation tag is given - // 2. Output length is zero - impossible - // 3. Output length is longer than u16::MAX - impossible - // 4. Output length is grater than 255 * 32 - impossible - // 5. Output length overflows usize - impossible - let plain = k256::Secp256k1::hash_from_bytes::(messages, &[dst]).ok()?; - let plain = generic_ec_curves::rust_crypto::RustCryptoPoint(plain); - let plain: generic_ec::Point = - generic_ec::as_raw::FromRaw::from_raw(plain); - // Can fail if point is zero - generic_ec::NonZero::try_from(plain).ok() - } -} - -fn hash_to_curve( - message: &[u8], - dst: &[u8], -) -> generic_ec::NonZero> { - for i in 0..=255u8 { - if let Some(r) = E::hash_to_curve(&[&[i], message], dst) { - return r; - } - } - #[allow(clippy::panic)] - { - panic!("Bad curve or hash algorithm: too many failures"); - } -} - /// Compute digest of `data` keyed with `secret_key` -pub fn digest( +pub fn digest( data: &[u8], secret_key: &generic_ec::NonZero>, ) -> generic_ec::NonZero> { @@ -83,18 +40,45 @@ pub fn digest( plain * secret_key } +/// Evaluation of partial digest as outputted by parties, computed by +/// [`partial_digest`]. `t` partials can be aggregated with [`aggregate`] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(bound = "")] +pub struct PartialEvaluation { + /// Index of evaluating party + pub i: u16, + /// Partial digest + pub v: generic_ec::NonZero>, + /// ZK proof + pub pi: zkp::Proof, +} + /// Compute partial digest of `data` keyed with a key of which `secret_share` is a /// share of. Use [`aggregate`] to aggregate multiple partial digests into a full /// digest /// -/// Partial digest is exactly the same as a full digest; this is a convenient -/// alias if you want to distinguish the functionality in your code. -pub fn partial_digest( - shared_state: &impl udigest::Digestable, +/// Together with digest a proof of honest correctness is computed. Every party +/// is required to send this proof together with partial digest for aggregation. +/// +/// In paper this function is called `PartialEval(x, sk_i, vk_i)`, section IV.A +/// +/// - `eid` - execution id, used to prevent replay attacks. All parties +/// computing partial digests should agree on this value. This value cannot be +/// reused between executions as that leads to replay attacks +/// - `i` - index of this party among other computing parties. If `t` parties +/// are computing partial digests, each index should be from `0` to `t - 1`. +/// When using [`aggregate`], partial digests and proofs should be sorted by +/// this index. +/// - `data` - `x` in paper +/// - `secret_share` - `sk` from paper. The `vk` argument in paper is computed +/// from it +pub fn partial_digest( + eid: &[u8], + i: u16, data: &[u8], secret_share: &generic_ec::NonZero>, rng: &mut impl rand_core::RngCore, -) -> (generic_ec::NonZero>, zkp::Proof) { +) -> PartialEvaluation { let plain = hash_to_curve(data, b"dfns-bls-style-hash"); let digest = plain * secret_share; let proof_data = zkp::Data { @@ -103,38 +87,62 @@ pub fn partial_digest( value: digest.into_inner(), }; let r = generic_ec::Scalar::random(rng); - let proof = zkp::prove::(shared_state, secret_share, proof_data, r); - (digest, proof) + let shared_state = zkp::SharedState { + eid, + prover_index: i, + }; + let proof = zkp::prove::(&shared_state, secret_share, proof_data, r); + PartialEvaluation { + i, + v: digest, + pi: proof, + } } /// Aggregate `partials` - partial digest values - into a full value. /// -/// `share_preimages` should be `None` for additive key shares. For SSS, it -/// gives the points at which the keyshare values are computed, and should be in -/// the same order by participant as `partials`. -pub fn aggregate( +/// - `share_preimages` - should be `None` for additive key shares. For SSS, it +/// gives the points at which the keyshare values are computed, and should be in +/// the same order by participant as `partials`. +/// - `data` - `x` in paper +/// - `public_shares` - list of public shares of parties who computed partial +/// digests, given in the same order as partials and share preimages. `VK` in +/// paper +/// - `partials` - `E` in paper +/// +/// In paper this function is called `Combine(pk, VK, x, E)`, section IV.A +pub fn aggregate( + eid: &[u8], data: &[u8], - partials: &[(generic_ec::NonZero>, zkp::Proof)], + partials: &[PartialEvaluation], public_shares: &[generic_ec::NonZero>], - eid: &[u8], share_preimages: Option<&[generic_ec::NonZero>]>, ) -> Result, AggregateFailed> { let base = hash_to_curve(data, b"dfns-bls-style-hash"); + + // Verify the proofs let mut blame = Vec::new(); - for (((value, proof), pub_share), j) in partials.iter().zip(public_shares).zip(0..) { + for (partial, pub_share) in partials.iter().zip(public_shares) { let shared_state = zkp::SharedState { eid, - prover_index: j, + prover_index: partial.i, }; let data = zkp::Data { pub_share: pub_share.into_inner(), base: base.into_inner(), - value: value.into_inner(), + value: partial.v.into_inner(), }; - if zkp::verify::(&shared_state, data, *proof).is_err() { - blame.push(j); + if zkp::verify::(&shared_state, data, partial.pi).is_err() { + blame.push(partial.i); }; } + // The paper suggests to continue with an honest subset, but we prefer to + // abort + if !blame.is_empty() { + return Err(AggregateFailed::Verification(blame)); + } + + // Compute the aggregate digest if let Some(share_preimages) = share_preimages { // shamir aggregation let lagrange_coefficients = (0..(share_preimages.len())) @@ -144,11 +152,11 @@ pub fn aggregate( Ok(generic_ec::Scalar::multiscalar_mul( lagrange_coefficients .into_iter() - .zip(partials.iter().map(|t| t.0)), + .zip(partials.iter().map(|t| t.v)), )) } else { // additive aggregation - Ok(partials.iter().map(|t| t.0).sum()) + Ok(partials.iter().map(|t| t.v).sum()) } } @@ -167,6 +175,7 @@ pub enum AggregateFailed { /// Start an MPC protocol that digests the data with shared private key. Returns /// digested data /// +/// - `eid` - execution id, a nonce shared by every party /// - `data` - byte string to digest /// - `i` - index of party in this protocol invocation, used for message routing /// - `key_share` - key share to use, can be additive or SSS @@ -185,7 +194,7 @@ pub async fn start_digest( ) -> Result, mpc::Error> where D: digest::Digest, - E: int::HashToCurve, + E: internal::HashToCurve, M: round_based::Mpc>, { let share_preimages = key_share @@ -222,6 +231,55 @@ where .await } +mod internal { + pub trait HashToCurve: generic_ec::Curve { + /// This function may fail, but the probability of that must be low. If + /// it fails, we retry with a different prefix. If it fails too many + /// times, we panic + fn hash_to_curve( + messages: &[&[u8]], + dst: &[u8], + ) -> Option>>; + } +} + +impl internal::HashToCurve for generic_ec::curves::Secp256k1 { + fn hash_to_curve( + messages: &[&[u8]], + dst: &[u8], + ) -> Option>> { + type ExtendedHash = k256::elliptic_curve::hash2curve::ExpandMsgXmd; + use k256::elliptic_curve::hash2curve::GroupDigest as _; + // This can fail if: + // 1. No domain separation tag is given + // 2. Output length is zero - impossible + // 3. Output length is longer than u16::MAX - impossible + // 4. Output length is grater than 255 * 32 - impossible + // 5. Output length overflows usize - impossible + let plain = k256::Secp256k1::hash_from_bytes::(messages, &[dst]).ok()?; + let plain = generic_ec_curves::rust_crypto::RustCryptoPoint(plain); + let plain: generic_ec::Point = + generic_ec::as_raw::FromRaw::from_raw(plain); + // Can fail if point is zero + generic_ec::NonZero::try_from(plain).ok() + } +} + +fn hash_to_curve( + message: &[u8], + dst: &[u8], +) -> generic_ec::NonZero> { + for i in 0..=255u8 { + if let Some(r) = E::hash_to_curve(&[&[i], message], dst) { + return r; + } + } + #[allow(clippy::panic)] + { + panic!("Bad curve or hash algorithm: too many failures"); + } +} + #[cfg(test)] mod test { type E = generic_ec::curves::Secp256k1; @@ -250,23 +308,13 @@ mod test { let partials = shares .iter() .zip(0..) - .map(|(s, i)| { - super::partial_digest::( - &crate::zkp::SharedState { - prover_index: i, - eid, - }, - data, - &s.x, - &mut rng, - ) - }) + .map(|(s, i)| super::partial_digest::(eid, i, data, &s.x, &mut rng)) .collect::>(); let restored = super::aggregate::( + eid, data, &partials[0..t], public_shares, - eid, share_preimages, ) .unwrap(); diff --git a/src/mpc.rs b/src/mpc.rs index aa9a776..ec591f3 100644 --- a/src/mpc.rs +++ b/src/mpc.rs @@ -1,5 +1,3 @@ -use round_based::SinkExt as _; - /// Protocol message #[derive(round_based::ProtocolMessage, Clone, serde::Serialize, serde::Deserialize)] #[serde(bound = "")] @@ -12,10 +10,8 @@ pub enum Msg { #[derive(Clone, serde::Serialize, serde::Deserialize)] #[serde(bound = "")] pub struct MsgPartial { - /// Partial digest of a party - pub digest: generic_ec::NonZero>, - /// Proof of valid execution - pub proof: crate::zkp::Proof, + /// Partial evaluation of party + pub evaluation: crate::PartialEvaluation, } #[allow(clippy::too_many_arguments)] @@ -32,9 +28,11 @@ pub(crate) async fn run( ) -> Result, Error> where D: digest::Digest, - E: super::int::HashToCurve, + E: crate::internal::HashToCurve, M: round_based::Mpc>, { + use round_based::SinkExt as _; + let round_based::MpcParty { delivery, .. } = party.into_party(); let (incomings, mut outgoings) = round_based::Delivery::split(delivery); @@ -43,12 +41,8 @@ where rounds.add_round(round_based::rounds_router::simple_store::RoundInput::broadcast(i, n)); let mut rounds = rounds.listen(incomings); - let shared_state = udigest::inline_struct!("PrEq" { - eid, - prover_index: i, - }); - let (digest, proof) = super::partial_digest::(&shared_state, data, secret_share, rng); - let my_partial = MsgPartial { digest, proof }; + let evaluation = super::partial_digest::(eid, i, data, secret_share, rng); + let my_partial = MsgPartial { evaluation }; outgoings .send(round_based::Outgoing::broadcast(Msg::Partial( my_partial.clone(), @@ -62,10 +56,10 @@ where .map_err(|x| Error::RecvMessage(Box::new(x)))?; let partials = partials .into_iter_including_me(my_partial) - .map(|s| (s.digest, s.proof)) + .map(|s| s.evaluation) .collect::>(); - super::aggregate::(data, &partials, public_shares, eid, share_preimages) + super::aggregate::(eid, data, &partials, public_shares, share_preimages) .map_err(Error::AggregateFailed) } From 829c56705a7d9fa21188337493705cf9e7b7c1e9 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 30 Jan 2025 15:46:57 +0100 Subject: [PATCH 10/15] Pivot to t-ecdh. Update generic-ec-zkp --- Cargo.lock | 130 ++++++------------------------ Cargo.toml | 10 +-- src/lib.rs | 230 +++++++++++++++++++++-------------------------------- src/mpc.rs | 10 +-- src/zkp.rs | 125 ----------------------------- 5 files changed, 120 insertions(+), 385 deletions(-) delete mode 100644 src/zkp.rs diff --git a/Cargo.lock b/Cargo.lock index 4d150f9..3f83dde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,12 +14,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - [[package]] name = "block-buffer" version = "0.10.4" @@ -29,28 +23,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bls-style-digest" -version = "0.1.0" -dependencies = [ - "digest", - "generic-ec", - "generic-ec-curves", - "generic-ec-zkp", - "k256", - "key-share", - "rand", - "rand_core", - "rand_dev", - "rand_hash", - "round-based", - "serde", - "sha2", - "test-case", - "thiserror 2.0.3", - "udigest", -] - [[package]] name = "byteorder" version = "1.5.0" @@ -180,9 +152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", "crypto-common", - "subtle", ] [[package]] @@ -196,20 +166,6 @@ dependencies = [ "syn 2.0.89", ] -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - [[package]] name = "educe" version = "0.4.23" @@ -234,7 +190,6 @@ dependencies = [ "ff", "generic-array", "group", - "pkcs8", "rand_core", "sec1", "subtle", @@ -313,6 +268,7 @@ version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ + "serde", "typenum", "version_check", "zeroize", @@ -371,10 +327,11 @@ dependencies = [ [[package]] name = "generic-ec-zkp" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec6e906724b1f70cd13e7e2455165300f1ef2fd200e40aa6207a046a36af839d" +checksum = "bd3945c585fdddba3f86bda4e4cfba22d5e255001b3e145c9db305ad096c6d88" dependencies = [ + "digest", "generic-array", "generic-ec", "rand_core", @@ -411,15 +368,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -433,11 +381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", - "ecdsa", "elliptic-curve", - "once_cell", - "sha2", - "signature", ] [[package]] @@ -487,12 +431,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - [[package]] name = "phantom-type" version = "0.3.1" @@ -523,16 +461,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - [[package]] name = "ppv-lite86" version = "0.2.20" @@ -612,16 +540,6 @@ dependencies = [ "udigest", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "round-based" version = "0.4.0" @@ -664,7 +582,6 @@ dependencies = [ "base16ct", "der", "generic-array", - "pkcs8", "subtle", "zeroize", ] @@ -728,26 +645,6 @@ dependencies = [ "digest", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - [[package]] name = "strsim" version = "0.11.1" @@ -782,6 +679,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tecdh" +version = "0.1.0" +dependencies = [ + "digest", + "generic-ec", + "generic-ec-zkp", + "key-share", + "rand", + "rand_core", + "rand_dev", + "round-based", + "serde", + "sha2", + "test-case", + "thiserror 2.0.3", + "udigest", +] + [[package]] name = "test-case" version = "3.3.1" diff --git a/Cargo.toml b/Cargo.toml index 6919887..8d1ae99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,28 +1,24 @@ [package] -name = "bls-style-digest" +name = "tecdh" version = "0.1.0" edition = "2021" [dependencies] digest = "0.10" generic-ec = { version = "0.4.5", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } -generic-ec-zkp = "0.4" +generic-ec-zkp = { version = "0.4.4", features = ["serde", "udigest"] } key-share = "0.5" rand_core = { version = "0.6", default-features = false } -rand_hash = { version = "0.1" } round-based = { version = "0.4", features = ["derive"] } serde = "1" -sha2 = "0.10" thiserror = { version = "2" } udigest = { version = "0.2", default-features = false, features = ["inline-struct", "derive"] } -k256 = { version = "0.13", features = ["hash2curve"] } -generic-ec-curves = { version = "0.2.4", features = ["rust-crypto"] } - [dev-dependencies] generic-ec = { version = "0.4.5", features = ["curve-ed25519"] } key-share = { version = "0.5", features = ["spof"] } rand = "0.8" rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } +sha2 = "0.10" test-case = "3" diff --git a/src/lib.rs b/src/lib.rs index dcee5b5..6c4d1d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,14 @@ -//! This crate implements digesting into elliptic curve points keyed with a -//! secret scalar for this curve. This digest resembles BLS signatures, but -//! because we don't do signature verification, we're not limited in the choice -//! of curves. Hashing to curve is based on rfc9380 with SHA2 hash and -//! expand_message_xmd expansion. The procedure for a digest is as follows: +//! This crate implements Threshold Elliptic Curve Diffie-Hellman key +//! exchange. //! -//! 1. Input parameters: -//! * `E` - an elliptic curve -//! * `m` - message to digest, given as a byte string -//! * `x` - secret key, given as a scalar for `E` -//! 2. Initialize `i` to 0 -//! 2. Use the procedure in rfc9380 to hash the data `i as u8 || m` to curve point `p` -//! 4. If `p` the procedure failed, or if `p` is zero or an invalid point, -//! increment `i` and retry from step *3*. If 256 attempts have already -//! elapsed, abort -//! 5. Compute `p * x` +//! Threshold means that the private key is shared between multiple parties. +//! Only when a threshold amount of parties run the protocol together, can +//! they form the diffie-hellman session key //! -//! The distributed digest is based on https://eprint.iacr.org/2020/096 -//! That is, the partial digest is computed in the same way as a regular digest -//! albeit with a shared key, and in addition a ZK proof of honest digest -//! computation is produced, which is checked when aggregating partial digests. -//! We use the DDH-based DVRF instatiation with non-compact proofs +//! The procedure for running this protocol resembles tBLS signatures, and in +//! fact was directly adpated from . A big +//! difference from BLS is that since we don't need signature verification, we +//! don't need efficient pairings and can use any curve we want. #![warn(missing_docs, unsafe_code, unused_crate_dependencies)] #![cfg_attr( @@ -29,110 +18,107 @@ /// Helper types for the MPC execution pub mod mpc; -mod zkp; +use generic_ec_zkp::dlog_eq::non_interactive as dlog_eq; -/// Compute digest of `data` keyed with `secret_key` -pub fn digest( - data: &[u8], - secret_key: &generic_ec::NonZero>, +/// Compute elliptic curve diffie-hellman for the given public key received from +/// another party and a private key for this party +pub fn ecdh( + pubkey1: generic_ec::NonZero>, + privkey2: &generic_ec::NonZero>, ) -> generic_ec::NonZero> { - let plain = hash_to_curve(data, b"dfns-bls-style-hash"); - plain * secret_key + pubkey1 * privkey2 } -/// Evaluation of partial digest as outputted by parties, computed by -/// [`partial_digest`]. `t` partials can be aggregated with [`aggregate`] +/// Evaluation of partial ECDH as outputted by parties, computed by +/// [`partial_ecdh`]. `t` partials can be aggregated with [`aggregate`] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[serde(bound = "")] pub struct PartialEvaluation { /// Index of evaluating party pub i: u16, - /// Partial digest + /// Partial session key pub v: generic_ec::NonZero>, /// ZK proof - pub pi: zkp::Proof, + pub pi: dlog_eq::Proof, } -/// Compute partial digest of `data` keyed with a key of which `secret_share` is a -/// share of. Use [`aggregate`] to aggregate multiple partial digests into a full -/// digest +/// Compute partial result of key exchange, with `secret_share` being a share of +/// this party's private key. Use [`aggregate`] to aggregate multiple such +/// partial evaluations into a full session key. /// -/// Together with digest a proof of honest correctness is computed. Every party -/// is required to send this proof together with partial digest for aggregation. +/// Together with partial session key, a proof of honest correctness is +/// computed. Every party is required to send this proof together with their +/// partial evaluation for aggregation. /// /// In paper this function is called `PartialEval(x, sk_i, vk_i)`, section IV.A /// /// - `eid` - execution id, used to prevent replay attacks. All parties -/// computing partial digests should agree on this value. This value cannot be -/// reused between executions as that leads to replay attacks +/// computing the partial evaluations should agree on this value. This value +/// cannot be reused between executions as that leads to replay attacks /// - `i` - index of this party among other computing parties. If `t` parties -/// are computing partial digests, each index should be from `0` to `t - 1`. -/// When using [`aggregate`], partial digests and proofs should be sorted by +/// are performing the partial evaluation, each index should be from `0` to `t +/// - 1`. When using [`aggregate`], partial evaluations should be sorted by /// this index. -/// - `data` - `x` in paper -/// - `secret_share` - `sk` from paper. The `vk` argument in paper is computed -/// from it -pub fn partial_digest( +/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the +/// key exchange +/// - `secret_share` - `sk` from paper, share of the private key of the party +/// doing this key exchange. The `vk` argument which is present in paper but +/// not here is computed from it +pub fn partial_ecdh( eid: &[u8], i: u16, - data: &[u8], + other_key: generic_ec::NonZero>, secret_share: &generic_ec::NonZero>, - rng: &mut impl rand_core::RngCore, + rng: &mut impl rand_core::CryptoRngCore, ) -> PartialEvaluation { - let plain = hash_to_curve(data, b"dfns-bls-style-hash"); - let digest = plain * secret_share; - let proof_data = zkp::Data { - pub_share: (generic_ec::Point::generator() * secret_share).into_inner(), - base: plain.into_inner(), - value: digest.into_inner(), - }; - let r = generic_ec::Scalar::random(rng); - let shared_state = zkp::SharedState { + let value = other_key * secret_share; + let proof_data = dlog_eq::Data::from_secret_key(secret_share, other_key.into_inner()); + let shared_state = SharedState { eid, prover_index: i, }; - let proof = zkp::prove::(&shared_state, secret_share, proof_data, r); + let proof = dlog_eq::prove::(rng, &shared_state, secret_share, proof_data); PartialEvaluation { i, - v: digest, + v: value, pi: proof, } } -/// Aggregate `partials` - partial digest values - into a full value. +/// Aggregate partial ECDH session keys into a full session key /// /// - `share_preimages` - should be `None` for additive key shares. For SSS, it /// gives the points at which the keyshare values are computed, and should be in /// the same order by participant as `partials`. -/// - `data` - `x` in paper +/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the +/// key exchange /// - `public_shares` - list of public shares of parties who computed partial -/// digests, given in the same order as partials and share preimages. `VK` in +/// evaluations, given in the same order as partials and share preimages. `VK` in /// paper -/// - `partials` - `E` in paper +/// - `partials` - `E` in paper, partial ECDH session keys /// /// In paper this function is called `Combine(pk, VK, x, E)`, section IV.A -pub fn aggregate( +pub fn aggregate( eid: &[u8], - data: &[u8], + other_key: generic_ec::NonZero>, partials: &[PartialEvaluation], public_shares: &[generic_ec::NonZero>], share_preimages: Option<&[generic_ec::NonZero>]>, ) -> Result, AggregateFailed> { - let base = hash_to_curve(data, b"dfns-bls-style-hash"); - // Verify the proofs let mut blame = Vec::new(); for (partial, pub_share) in partials.iter().zip(public_shares) { - let shared_state = zkp::SharedState { + let shared_state = SharedState { eid, prover_index: partial.i, }; - let data = zkp::Data { - pub_share: pub_share.into_inner(), - base: base.into_inner(), - value: partial.v.into_inner(), + let data = dlog_eq::Data { + gen1: generic_ec::Point::generator().into(), + prod1: pub_share.into_inner(), + gen2: other_key.into_inner(), + prod2: partial.v.into_inner(), }; - if zkp::verify::(&shared_state, data, partial.pi).is_err() { + if dlog_eq::verify::(&shared_state, data, partial.pi).is_err() { blame.push(partial.i); }; } @@ -142,7 +128,7 @@ pub fn aggregate( return Err(AggregateFailed::Verification(blame)); } - // Compute the aggregate digest + // Compute the aggregate session key if let Some(share_preimages) = share_preimages { // shamir aggregation let lagrange_coefficients = (0..(share_preimages.len())) @@ -160,6 +146,14 @@ pub fn aggregate( } } +/// Shared state between proving in [`partial_ecdh`] and verifying in +/// [`aggregate`] +#[derive(udigest::Digestable)] +struct SharedState<'a> { + eid: &'a [u8], + prover_index: u16, +} + /// Error for aggregation failing #[derive(Debug, Clone, thiserror::Error)] pub enum AggregateFailed { @@ -172,29 +166,29 @@ pub enum AggregateFailed { Verification(Vec), } -/// Start an MPC protocol that digests the data with shared private key. Returns -/// digested data +/// Start an MPC protocol that performs threshold ECDH with shared private key. +/// Returns the session key /// /// - `eid` - execution id, a nonce shared by every party -/// - `data` - byte string to digest +/// - `other_key` - public key of the other party doing the key exchange /// - `i` - index of party in this protocol invocation, used for message routing /// - `key_share` - key share to use, can be additive or SSS /// - `participants` - which key holders are participating in the protocol, /// given as indexes into `share_preimages` in key share. Ignored for additive /// shares. /// - `party` - the `round-based` party -pub async fn start_digest( +pub async fn start_ecdh( eid: &[u8], - data: &[u8], + other_key: generic_ec::NonZero>, i: u16, key_share: &key_share::CoreKeyShare, participants: &[u16], party: M, - rng: &mut impl rand_core::RngCore, + rng: &mut impl rand_core::CryptoRngCore, ) -> Result, mpc::Error> where D: digest::Digest, - E: internal::HashToCurve, + E: generic_ec::Curve, M: round_based::Mpc>, { let share_preimages = key_share @@ -219,7 +213,7 @@ where mpc::run::( eid, - data, + other_key, &key_share.x, i, key_share.min_signers(), @@ -231,54 +225,6 @@ where .await } -mod internal { - pub trait HashToCurve: generic_ec::Curve { - /// This function may fail, but the probability of that must be low. If - /// it fails, we retry with a different prefix. If it fails too many - /// times, we panic - fn hash_to_curve( - messages: &[&[u8]], - dst: &[u8], - ) -> Option>>; - } -} - -impl internal::HashToCurve for generic_ec::curves::Secp256k1 { - fn hash_to_curve( - messages: &[&[u8]], - dst: &[u8], - ) -> Option>> { - type ExtendedHash = k256::elliptic_curve::hash2curve::ExpandMsgXmd; - use k256::elliptic_curve::hash2curve::GroupDigest as _; - // This can fail if: - // 1. No domain separation tag is given - // 2. Output length is zero - impossible - // 3. Output length is longer than u16::MAX - impossible - // 4. Output length is grater than 255 * 32 - impossible - // 5. Output length overflows usize - impossible - let plain = k256::Secp256k1::hash_from_bytes::(messages, &[dst]).ok()?; - let plain = generic_ec_curves::rust_crypto::RustCryptoPoint(plain); - let plain: generic_ec::Point = - generic_ec::as_raw::FromRaw::from_raw(plain); - // Can fail if point is zero - generic_ec::NonZero::try_from(plain).ok() - } -} - -fn hash_to_curve( - message: &[u8], - dst: &[u8], -) -> generic_ec::NonZero> { - for i in 0..=255u8 { - if let Some(r) = E::hash_to_curve(&[&[i], message], dst) { - return r; - } - } - #[allow(clippy::panic)] - { - panic!("Bad curve or hash algorithm: too many failures"); - } -} #[cfg(test)] mod test { @@ -287,7 +233,7 @@ mod test { #[test_case::test_case(3, 5; "t3n5")] #[test_case::test_case(5, 5; "t5n5")] #[test_case::test_case(3, 7; "t3n7")] - fn aggregate_same_as_digest(t: u16, n: u16) { + fn aggregate_same_as_ecdh(t: u16, n: u16) { let t_ = if t == n { None } else { Some(t) }; let t = usize::from(t); let mut rng = rand_dev::DevRng::new(); @@ -301,16 +247,17 @@ mod test { let public_shares = &shares[0].public_shares; let share_preimages = shares[0].vss_setup.as_ref().map(|vss| &vss.I[0..t]); - let data = b"take that you worm"; + let data = generic_ec::Point::generator() + * generic_ec::NonZero::>::random(&mut rng); let eid = b"test"; - let digest = super::digest::(data, &secret_key); + let ecdh = super::ecdh(data, &secret_key); let partials = shares .iter() .zip(0..) - .map(|(s, i)| super::partial_digest::(eid, i, data, &s.x, &mut rng)) + .map(|(s, i)| super::partial_ecdh::(eid, i, data, &s.x, &mut rng)) .collect::>(); - let restored = super::aggregate::( + let restored = super::aggregate::( eid, data, &partials[0..t], @@ -319,13 +266,13 @@ mod test { ) .unwrap(); - assert_eq!(restored, digest); + assert_eq!(restored, ecdh); } #[test_case::test_case(3, 5; "t3n5")] #[test_case::test_case(5, 5; "t5n5")] #[test_case::test_case(3, 7; "t3n7")] - fn protocol_same_as_digest(t: u16, n: u16) { + fn protocol_same_as_ecdh(t: u16, n: u16) { let mut rng = rand_dev::DevRng::new(); let secret_key = generic_ec::NonZero::>::random(&mut rng); @@ -335,21 +282,22 @@ mod test { .generate_shares(&mut rng) .unwrap(); - let data = b"take that you worm"; + let data = generic_ec::Point::generator() + * generic_ec::NonZero::>::random(&mut rng); let party_indexes = random_participants(n, t, &mut rng); let parties = party_indexes .iter() .map(|x| u16::try_from(*x).unwrap()) .collect::>(); - let digests = round_based::sim::run_with_setup( + let ecdhs = round_based::sim::run_with_setup( party_indexes.iter().copied(), |i, party, party_index| { let mut rng = rng.fork(); let share = &shares[party_index]; let parties = &parties; async move { - crate::start_digest::( + crate::start_ecdh::( b"test", data, i, share, parties, party, &mut rng, ) .await @@ -360,9 +308,9 @@ mod test { .expect_ok() .into_vec(); - let golden = super::digest::(data, &secret_key); - for digest in &digests { - assert_eq!(golden, *digest); + let golden = super::ecdh(data, &secret_key); + for ecdh in &ecdhs { + assert_eq!(golden, *ecdh); } } diff --git a/src/mpc.rs b/src/mpc.rs index ec591f3..54e2c41 100644 --- a/src/mpc.rs +++ b/src/mpc.rs @@ -17,18 +17,18 @@ pub struct MsgPartial { #[allow(clippy::too_many_arguments)] pub(crate) async fn run( eid: &[u8], - data: &[u8], + other_key: generic_ec::NonZero>, secret_share: &generic_ec::NonZero>, i: u16, n: u16, public_shares: &[generic_ec::NonZero>], share_preimages: Option<&[generic_ec::NonZero>]>, party: M, - rng: &mut impl rand_core::RngCore, + rng: &mut impl rand_core::CryptoRngCore, ) -> Result, Error> where D: digest::Digest, - E: crate::internal::HashToCurve, + E: generic_ec::Curve, M: round_based::Mpc>, { use round_based::SinkExt as _; @@ -41,7 +41,7 @@ where rounds.add_round(round_based::rounds_router::simple_store::RoundInput::broadcast(i, n)); let mut rounds = rounds.listen(incomings); - let evaluation = super::partial_digest::(eid, i, data, secret_share, rng); + let evaluation = super::partial_ecdh::(eid, i, other_key, secret_share, rng); let my_partial = MsgPartial { evaluation }; outgoings .send(round_based::Outgoing::broadcast(Msg::Partial( @@ -59,7 +59,7 @@ where .map(|s| s.evaluation) .collect::>(); - super::aggregate::(eid, data, &partials, public_shares, share_preimages) + super::aggregate::(eid, other_key, &partials, public_shares, share_preimages) .map_err(Error::AggregateFailed) } diff --git a/src/zkp.rs b/src/zkp.rs deleted file mode 100644 index d38939d..0000000 --- a/src/zkp.rs +++ /dev/null @@ -1,125 +0,0 @@ -const TAG: &str = "bls-style-digest.zkp"; - -#[derive(Debug, Clone, udigest::Digestable)] -pub struct SharedState<'a> { - pub eid: &'a [u8], - pub prover_index: u16, -} - -#[derive(Debug, Clone, Copy, udigest::Digestable)] -#[udigest(bound = "")] -pub struct Data { - pub pub_share: generic_ec::Point, - pub base: generic_ec::Point, - pub value: generic_ec::Point, -} - -#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] -#[serde(bound = "")] -pub struct Proof { - pub ch: generic_ec::Scalar, - pub res: generic_ec::Scalar, -} - -pub fn prove( - shared_state: &impl udigest::Digestable, - share: &generic_ec::SecretScalar, - data: Data, - r: generic_ec::Scalar, -) -> Proof { - let com1 = generic_ec::Point::generator() * r; - let com2 = data.base * r; - - let seed = udigest::inline_struct!(TAG { - shared_state, - data, - com1, - com2, - }); - let mut rng = rand_hash::HashRng::::from_seed(seed); - let ch = generic_ec::Scalar::random(&mut rng); - - let res = r + share * ch; - Proof { ch, res } -} - -pub fn verify( - shared_state: &impl udigest::Digestable, - data: Data, - proof: Proof, -) -> Result<(), InvalidProof> { - let com1 = generic_ec::Point::generator() * proof.res - data.pub_share * proof.ch; - let com2 = data.base * proof.res - data.value * proof.ch; - - let seed = udigest::inline_struct!(TAG { - shared_state, - data, - com1, - com2, - }); - let mut rng = rand_hash::HashRng::::from_seed(seed); - let ch = generic_ec::Scalar::random(&mut rng); - - if ch != proof.ch { - Err(InvalidProof) - } else { - Ok(()) - } -} - -#[derive(Debug)] -pub struct InvalidProof; - -#[cfg(test)] -mod test { - fn passing_test() { - let mut rng = rand_dev::DevRng::new(); - let shared_state = "shared state"; - - let secret_share = generic_ec::SecretScalar::random(&mut rng); - let public_share = generic_ec::Point::generator() * &secret_share; - - let raw_digest = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); - let digest = raw_digest * &secret_share; - - let r = generic_ec::Scalar::random(&mut rng); - let data = super::Data { - pub_share: public_share, - base: raw_digest, - value: digest, - }; - let proof = super::prove::(&shared_state, &secret_share, data, r); - super::verify::(&shared_state, data, proof).unwrap(); - } - - fn failing_test() { - let mut rng = rand_dev::DevRng::new(); - let shared_state = "shared state"; - - let secret_share = generic_ec::SecretScalar::random(&mut rng); - let public_share = generic_ec::Point::generator() * &secret_share; - - let raw_digest = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); - // fake digest is our concern - let digest = generic_ec::Point::generator() * generic_ec::Scalar::random(&mut rng); - - let r = generic_ec::Scalar::random(&mut rng); - let data = super::Data { - pub_share: public_share, - base: raw_digest, - value: digest, - }; - let proof = super::prove::(&shared_state, &secret_share, data, r); - assert!(super::verify::(&shared_state, data, proof).is_err()); - } - - #[test] - fn passing_k256_sha256() { - passing_test::(); - } - - #[test] - fn failing_k256_sha256() { - failing_test::(); - } -} From f326c7689190add5e14ece8f663d8adf6efb5f20 Mon Sep 17 00:00:00 2001 From: maurges Date: Thu, 30 Jan 2025 15:52:47 +0100 Subject: [PATCH 11/15] Workflows fixed --- src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6c4d1d0..d308bea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,8 +56,8 @@ pub struct PartialEvaluation { /// computing the partial evaluations should agree on this value. This value /// cannot be reused between executions as that leads to replay attacks /// - `i` - index of this party among other computing parties. If `t` parties -/// are performing the partial evaluation, each index should be from `0` to `t -/// - 1`. When using [`aggregate`], partial evaluations should be sorted by +/// are performing the partial evaluation, each index should be from `0` to +/// `t - 1`. When using [`aggregate`], partial evaluations should be sorted by /// this index. /// - `other_key` - `H_1(x)` in paper, public key of the other party doing the /// key exchange @@ -225,7 +225,6 @@ where .await } - #[cfg(test)] mod test { type E = generic_ec::curves::Secp256k1; From 222b991770312bef7c2c47393469f0a4b2240822 Mon Sep 17 00:00:00 2001 From: maurges Date: Mon, 10 Feb 2025 16:26:23 +0100 Subject: [PATCH 12/15] Move lowlevel functions to their module --- src/lib.rs | 203 ++++-------------------------------------------- src/lowlevel.rs | 179 ++++++++++++++++++++++++++++++++++++++++++ src/mpc.rs | 8 +- 3 files changed, 200 insertions(+), 190 deletions(-) create mode 100644 src/lowlevel.rs diff --git a/src/lib.rs b/src/lib.rs index d308bea..00209de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,155 +16,11 @@ deny(clippy::expect_used, clippy::unwrap_used, clippy::panic) )] +/// Functions to perform low-level operations. This can be misused, so they are +/// not recommended unless you know how tECDH works +pub mod lowlevel; /// Helper types for the MPC execution pub mod mpc; -use generic_ec_zkp::dlog_eq::non_interactive as dlog_eq; - -/// Compute elliptic curve diffie-hellman for the given public key received from -/// another party and a private key for this party -pub fn ecdh( - pubkey1: generic_ec::NonZero>, - privkey2: &generic_ec::NonZero>, -) -> generic_ec::NonZero> { - pubkey1 * privkey2 -} - -/// Evaluation of partial ECDH as outputted by parties, computed by -/// [`partial_ecdh`]. `t` partials can be aggregated with [`aggregate`] -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -#[serde(bound = "")] -pub struct PartialEvaluation { - /// Index of evaluating party - pub i: u16, - /// Partial session key - pub v: generic_ec::NonZero>, - /// ZK proof - pub pi: dlog_eq::Proof, -} - -/// Compute partial result of key exchange, with `secret_share` being a share of -/// this party's private key. Use [`aggregate`] to aggregate multiple such -/// partial evaluations into a full session key. -/// -/// Together with partial session key, a proof of honest correctness is -/// computed. Every party is required to send this proof together with their -/// partial evaluation for aggregation. -/// -/// In paper this function is called `PartialEval(x, sk_i, vk_i)`, section IV.A -/// -/// - `eid` - execution id, used to prevent replay attacks. All parties -/// computing the partial evaluations should agree on this value. This value -/// cannot be reused between executions as that leads to replay attacks -/// - `i` - index of this party among other computing parties. If `t` parties -/// are performing the partial evaluation, each index should be from `0` to -/// `t - 1`. When using [`aggregate`], partial evaluations should be sorted by -/// this index. -/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the -/// key exchange -/// - `secret_share` - `sk` from paper, share of the private key of the party -/// doing this key exchange. The `vk` argument which is present in paper but -/// not here is computed from it -pub fn partial_ecdh( - eid: &[u8], - i: u16, - other_key: generic_ec::NonZero>, - secret_share: &generic_ec::NonZero>, - rng: &mut impl rand_core::CryptoRngCore, -) -> PartialEvaluation { - let value = other_key * secret_share; - let proof_data = dlog_eq::Data::from_secret_key(secret_share, other_key.into_inner()); - let shared_state = SharedState { - eid, - prover_index: i, - }; - let proof = dlog_eq::prove::(rng, &shared_state, secret_share, proof_data); - PartialEvaluation { - i, - v: value, - pi: proof, - } -} - -/// Aggregate partial ECDH session keys into a full session key -/// -/// - `share_preimages` - should be `None` for additive key shares. For SSS, it -/// gives the points at which the keyshare values are computed, and should be in -/// the same order by participant as `partials`. -/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the -/// key exchange -/// - `public_shares` - list of public shares of parties who computed partial -/// evaluations, given in the same order as partials and share preimages. `VK` in -/// paper -/// - `partials` - `E` in paper, partial ECDH session keys -/// -/// In paper this function is called `Combine(pk, VK, x, E)`, section IV.A -pub fn aggregate( - eid: &[u8], - other_key: generic_ec::NonZero>, - partials: &[PartialEvaluation], - public_shares: &[generic_ec::NonZero>], - share_preimages: Option<&[generic_ec::NonZero>]>, -) -> Result, AggregateFailed> { - // Verify the proofs - let mut blame = Vec::new(); - for (partial, pub_share) in partials.iter().zip(public_shares) { - let shared_state = SharedState { - eid, - prover_index: partial.i, - }; - let data = dlog_eq::Data { - gen1: generic_ec::Point::generator().into(), - prod1: pub_share.into_inner(), - gen2: other_key.into_inner(), - prod2: partial.v.into_inner(), - }; - if dlog_eq::verify::(&shared_state, data, partial.pi).is_err() { - blame.push(partial.i); - }; - } - // The paper suggests to continue with an honest subset, but we prefer to - // abort - if !blame.is_empty() { - return Err(AggregateFailed::Verification(blame)); - } - - // Compute the aggregate session key - if let Some(share_preimages) = share_preimages { - // shamir aggregation - let lagrange_coefficients = (0..(share_preimages.len())) - .map(|j| generic_ec_zkp::polynomial::lagrange_coefficient_at_zero(j, share_preimages)) - .collect::>>() - .ok_or(AggregateFailed::Lagrange)?; - Ok(generic_ec::Scalar::multiscalar_mul( - lagrange_coefficients - .into_iter() - .zip(partials.iter().map(|t| t.v)), - )) - } else { - // additive aggregation - Ok(partials.iter().map(|t| t.v).sum()) - } -} - -/// Shared state between proving in [`partial_ecdh`] and verifying in -/// [`aggregate`] -#[derive(udigest::Digestable)] -struct SharedState<'a> { - eid: &'a [u8], - prover_index: u16, -} - -/// Error for aggregation failing -#[derive(Debug, Clone, thiserror::Error)] -pub enum AggregateFailed { - /// Lagrange polynomial construction failed, probably because some points - /// repeat - #[error("lagrange interpolation failed")] - Lagrange, - /// Party ZKP verification failed - #[error("honesty verification failed for parties: {0:?}")] - Verification(Vec), -} /// Start an MPC protocol that performs threshold ECDH with shared private key. /// Returns the session key @@ -225,49 +81,22 @@ where .await } +/// Error for aggregation failing +#[derive(Debug, Clone, thiserror::Error)] +pub enum AggregateFailed { + /// Lagrange polynomial construction failed, probably because some points + /// repeat + #[error("lagrange interpolation failed")] + Lagrange, + /// Party ZKP verification failed + #[error("honesty verification failed for parties: {0:?}")] + Verification(Vec), +} + #[cfg(test)] mod test { type E = generic_ec::curves::Secp256k1; - #[test_case::test_case(3, 5; "t3n5")] - #[test_case::test_case(5, 5; "t5n5")] - #[test_case::test_case(3, 7; "t3n7")] - fn aggregate_same_as_ecdh(t: u16, n: u16) { - let t_ = if t == n { None } else { Some(t) }; - let t = usize::from(t); - let mut rng = rand_dev::DevRng::new(); - - let secret_key = generic_ec::NonZero::>::random(&mut rng); - let shares = key_share::trusted_dealer::builder::(n) - .set_threshold(t_) - .set_shared_secret_key(secret_key.clone()) - .generate_shares(&mut rng) - .unwrap(); - let public_shares = &shares[0].public_shares; - let share_preimages = shares[0].vss_setup.as_ref().map(|vss| &vss.I[0..t]); - - let data = generic_ec::Point::generator() - * generic_ec::NonZero::>::random(&mut rng); - let eid = b"test"; - - let ecdh = super::ecdh(data, &secret_key); - let partials = shares - .iter() - .zip(0..) - .map(|(s, i)| super::partial_ecdh::(eid, i, data, &s.x, &mut rng)) - .collect::>(); - let restored = super::aggregate::( - eid, - data, - &partials[0..t], - public_shares, - share_preimages, - ) - .unwrap(); - - assert_eq!(restored, ecdh); - } - #[test_case::test_case(3, 5; "t3n5")] #[test_case::test_case(5, 5; "t5n5")] #[test_case::test_case(3, 7; "t3n7")] @@ -307,7 +136,7 @@ mod test { .expect_ok() .into_vec(); - let golden = super::ecdh(data, &secret_key); + let golden = crate::lowlevel::ecdh(data, &secret_key); for ecdh in &ecdhs { assert_eq!(golden, *ecdh); } diff --git a/src/lowlevel.rs b/src/lowlevel.rs new file mode 100644 index 0000000..7bd5f77 --- /dev/null +++ b/src/lowlevel.rs @@ -0,0 +1,179 @@ +use generic_ec_zkp::dlog_eq::non_interactive as dlog_eq; + +/// Compute elliptic curve diffie-hellman for the given public key received from +/// another party and a private key for this party +pub fn ecdh( + pubkey1: generic_ec::NonZero>, + privkey2: &generic_ec::NonZero>, +) -> generic_ec::NonZero> { + pubkey1 * privkey2 +} + +/// Evaluation of partial ECDH as outputted by parties, computed by +/// [`partial_ecdh`]. `t` partials can be aggregated with [`aggregate`] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(bound = "")] +pub struct PartialEvaluation { + /// Index of evaluating party + pub i: u16, + /// Partial session key + pub v: generic_ec::NonZero>, + /// ZK proof + pub pi: dlog_eq::Proof, +} + +/// Compute partial result of key exchange, with `secret_share` being a share of +/// this party's private key. Use [`aggregate`] to aggregate multiple such +/// partial evaluations into a full session key. +/// +/// Together with partial session key, a proof of honest correctness is +/// computed. Every party is required to send this proof together with their +/// partial evaluation for aggregation. +/// +/// In paper this function is called `PartialEval(x, sk_i, vk_i)`, section IV.A +/// +/// - `eid` - execution id, used to prevent replay attacks. All parties +/// computing the partial evaluations should agree on this value. This value +/// cannot be reused between executions as that leads to replay attacks +/// - `i` - index of this party among other computing parties. If `t` parties +/// are performing the partial evaluation, each index should be from `0` to +/// `t - 1`. When using [`aggregate`], partial evaluations should be sorted by +/// this index. +/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the +/// key exchange +/// - `secret_share` - `sk` from paper, share of the private key of the party +/// doing this key exchange. The `vk` argument which is present in paper but +/// not here is computed from it +pub fn partial_ecdh( + eid: &[u8], + i: u16, + other_key: generic_ec::NonZero>, + secret_share: &generic_ec::NonZero>, + rng: &mut impl rand_core::CryptoRngCore, +) -> PartialEvaluation { + let value = other_key * secret_share; + let proof_data = dlog_eq::Data::from_secret_key(secret_share, other_key.into_inner()); + let shared_state = SharedState { + eid, + prover_index: i, + }; + let proof = dlog_eq::prove::(rng, &shared_state, secret_share, proof_data); + PartialEvaluation { + i, + v: value, + pi: proof, + } +} + +/// Aggregate partial ECDH session keys into a full session key +/// +/// - `share_preimages` - should be `None` for additive key shares. For SSS, it +/// gives the points at which the keyshare values are computed, and should be in +/// the same order by participant as `partials`. +/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the +/// key exchange +/// - `public_shares` - list of public shares of parties who computed partial +/// evaluations, given in the same order as partials and share preimages. `VK` in +/// paper +/// - `partials` - `E` in paper, partial ECDH session keys +/// +/// In paper this function is called `Combine(pk, VK, x, E)`, section IV.A +pub fn aggregate( + eid: &[u8], + other_key: generic_ec::NonZero>, + partials: &[PartialEvaluation], + public_shares: &[generic_ec::NonZero>], + share_preimages: Option<&[generic_ec::NonZero>]>, +) -> Result, crate::AggregateFailed> { + // Verify the proofs + let mut blame = Vec::new(); + for (partial, pub_share) in partials.iter().zip(public_shares) { + let shared_state = SharedState { + eid, + prover_index: partial.i, + }; + let data = dlog_eq::Data { + gen1: generic_ec::Point::generator().into(), + prod1: pub_share.into_inner(), + gen2: other_key.into_inner(), + prod2: partial.v.into_inner(), + }; + if dlog_eq::verify::(&shared_state, data, partial.pi).is_err() { + blame.push(partial.i); + }; + } + // The paper suggests to continue with an honest subset, but we prefer to + // abort + if !blame.is_empty() { + return Err(crate::AggregateFailed::Verification(blame)); + } + + // Compute the aggregate session key + if let Some(share_preimages) = share_preimages { + // shamir aggregation + let lagrange_coefficients = (0..(share_preimages.len())) + .map(|j| generic_ec_zkp::polynomial::lagrange_coefficient_at_zero(j, share_preimages)) + .collect::>>() + .ok_or(crate::AggregateFailed::Lagrange)?; + Ok(generic_ec::Scalar::multiscalar_mul( + lagrange_coefficients + .into_iter() + .zip(partials.iter().map(|t| t.v)), + )) + } else { + // additive aggregation + Ok(partials.iter().map(|t| t.v).sum()) + } +} + +/// Shared state between proving in [`partial_ecdh`] and verifying in +/// [`aggregate`] +#[derive(udigest::Digestable)] +struct SharedState<'a> { + eid: &'a [u8], + prover_index: u16, +} + +#[cfg(test)] +mod test { + type E = generic_ec::curves::Secp256k1; + + #[test_case::test_case(3, 5; "t3n5")] + #[test_case::test_case(5, 5; "t5n5")] + #[test_case::test_case(3, 7; "t3n7")] + fn aggregate_same_as_ecdh(t: u16, n: u16) { + let t_ = if t == n { None } else { Some(t) }; + let t = usize::from(t); + let mut rng = rand_dev::DevRng::new(); + + let secret_key = generic_ec::NonZero::>::random(&mut rng); + let shares = key_share::trusted_dealer::builder::(n) + .set_threshold(t_) + .set_shared_secret_key(secret_key.clone()) + .generate_shares(&mut rng) + .unwrap(); + let public_shares = &shares[0].public_shares; + let share_preimages = shares[0].vss_setup.as_ref().map(|vss| &vss.I[0..t]); + + let data = generic_ec::Point::generator() + * generic_ec::NonZero::>::random(&mut rng); + let eid = b"test"; + + let ecdh = super::ecdh(data, &secret_key); + let partials = shares + .iter() + .zip(0..) + .map(|(s, i)| super::partial_ecdh::(eid, i, data, &s.x, &mut rng)) + .collect::>(); + let restored = super::aggregate::( + eid, + data, + &partials[0..t], + public_shares, + share_preimages, + ) + .unwrap(); + + assert_eq!(restored, ecdh); + } +} diff --git a/src/mpc.rs b/src/mpc.rs index 54e2c41..f70c01a 100644 --- a/src/mpc.rs +++ b/src/mpc.rs @@ -1,3 +1,5 @@ +use crate::lowlevel; + /// Protocol message #[derive(round_based::ProtocolMessage, Clone, serde::Serialize, serde::Deserialize)] #[serde(bound = "")] @@ -11,7 +13,7 @@ pub enum Msg { #[serde(bound = "")] pub struct MsgPartial { /// Partial evaluation of party - pub evaluation: crate::PartialEvaluation, + pub evaluation: lowlevel::PartialEvaluation, } #[allow(clippy::too_many_arguments)] @@ -41,7 +43,7 @@ where rounds.add_round(round_based::rounds_router::simple_store::RoundInput::broadcast(i, n)); let mut rounds = rounds.listen(incomings); - let evaluation = super::partial_ecdh::(eid, i, other_key, secret_share, rng); + let evaluation = lowlevel::partial_ecdh::(eid, i, other_key, secret_share, rng); let my_partial = MsgPartial { evaluation }; outgoings .send(round_based::Outgoing::broadcast(Msg::Partial( @@ -59,7 +61,7 @@ where .map(|s| s.evaluation) .collect::>(); - super::aggregate::(eid, other_key, &partials, public_shares, share_preimages) + lowlevel::aggregate::(eid, other_key, &partials, public_shares, share_preimages) .map_err(Error::AggregateFailed) } From f9dde5a16a1ab37d9b55a9908b6169ce61bec6a6 Mon Sep 17 00:00:00 2001 From: maurges Date: Fri, 21 Feb 2025 12:02:31 +0100 Subject: [PATCH 13/15] Improve docs and some names --- src/lib.rs | 15 ++++++++------- src/lowlevel.rs | 29 +++++++++++++++++------------ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 00209de..e20df1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,16 +26,17 @@ pub mod mpc; /// Returns the session key /// /// - `eid` - execution id, a nonce shared by every party -/// - `other_key` - public key of the other party doing the key exchange +/// - `counterparty_public_key` - public key of the other party doing the key +/// exchange /// - `i` - index of party in this protocol invocation, used for message routing /// - `key_share` - key share to use, can be additive or SSS /// - `participants` - which key holders are participating in the protocol, -/// given as indexes into `share_preimages` in key share. Ignored for additive -/// shares. +/// given as indexes into `share_preimages` in key share. These are the +/// indexes that the parties occupied at keygen /// - `party` - the `round-based` party -pub async fn start_ecdh( +pub async fn start( eid: &[u8], - other_key: generic_ec::NonZero>, + counterparty_public_key: generic_ec::NonZero>, i: u16, key_share: &key_share::CoreKeyShare, participants: &[u16], @@ -69,7 +70,7 @@ where mpc::run::( eid, - other_key, + counterparty_public_key, &key_share.x, i, key_share.min_signers(), @@ -125,7 +126,7 @@ mod test { let share = &shares[party_index]; let parties = &parties; async move { - crate::start_ecdh::( + crate::start::( b"test", data, i, share, parties, party, &mut rng, ) .await diff --git a/src/lowlevel.rs b/src/lowlevel.rs index 7bd5f77..6de1284 100644 --- a/src/lowlevel.rs +++ b/src/lowlevel.rs @@ -39,20 +39,21 @@ pub struct PartialEvaluation { /// are performing the partial evaluation, each index should be from `0` to /// `t - 1`. When using [`aggregate`], partial evaluations should be sorted by /// this index. -/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the -/// key exchange +/// - `counterparty_public_key` - `H_1(x)` in paper, public key of the other +/// party doing the key exchange /// - `secret_share` - `sk` from paper, share of the private key of the party /// doing this key exchange. The `vk` argument which is present in paper but /// not here is computed from it pub fn partial_ecdh( eid: &[u8], i: u16, - other_key: generic_ec::NonZero>, + counterparty_public_key: generic_ec::NonZero>, secret_share: &generic_ec::NonZero>, rng: &mut impl rand_core::CryptoRngCore, ) -> PartialEvaluation { - let value = other_key * secret_share; - let proof_data = dlog_eq::Data::from_secret_key(secret_share, other_key.into_inner()); + let value = counterparty_public_key * secret_share; + let proof_data = + dlog_eq::Data::from_secret_key(secret_share, counterparty_public_key.into_inner()); let shared_state = SharedState { eid, prover_index: i, @@ -67,20 +68,24 @@ pub fn partial_ecdh( /// Aggregate partial ECDH session keys into a full session key /// -/// - `share_preimages` - should be `None` for additive key shares. For SSS, it -/// gives the points at which the keyshare values are computed, and should be in -/// the same order by participant as `partials`. -/// - `other_key` - `H_1(x)` in paper, public key of the other party doing the -/// key exchange +/// - `counterparty_public_key` - `H_1(x)` in paper, public key of the other +/// party doing the key exchange /// - `public_shares` - list of public shares of parties who computed partial /// evaluations, given in the same order as partials and share preimages. `VK` in /// paper /// - `partials` - `E` in paper, partial ECDH session keys +/// - `share_preimages` - should be `None` for additive key shares. For SSS, it +/// gives the points at which the keyshare values are computed, and should be in +/// the same order by participant as `partials` +/// +/// Partials, public shares and share preimages each should correspond to the +/// same party, that is all be ordered in the same way by the index of the party +/// they come from. /// /// In paper this function is called `Combine(pk, VK, x, E)`, section IV.A pub fn aggregate( eid: &[u8], - other_key: generic_ec::NonZero>, + counterparty_public_key: generic_ec::NonZero>, partials: &[PartialEvaluation], public_shares: &[generic_ec::NonZero>], share_preimages: Option<&[generic_ec::NonZero>]>, @@ -95,7 +100,7 @@ pub fn aggregate( let data = dlog_eq::Data { gen1: generic_ec::Point::generator().into(), prod1: pub_share.into_inner(), - gen2: other_key.into_inner(), + gen2: counterparty_public_key.into_inner(), prod2: partial.v.into_inner(), }; if dlog_eq::verify::(&shared_state, data, partial.pi).is_err() { From d74025f958fdf3efec35f9195854a1c09cc784de Mon Sep 17 00:00:00 2001 From: Denis Varlakov Date: Mon, 14 Apr 2025 14:49:31 +0200 Subject: [PATCH 14/15] Update key-share dep Signed-off-by: Denis Varlakov --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f83dde..aabd145 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,9 +386,9 @@ dependencies = [ [[package]] name = "key-share" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea364cb2397405d8c79afd3de173ca7e2e1d83a4ddd94d359263480ad96f06f" +checksum = "3ee8e510bb9f738ac400b7dedd98aeb23677c6b48cd3b1b682651ea5091f4282" dependencies = [ "displaydoc", "generic-ec", diff --git a/Cargo.toml b/Cargo.toml index 8d1ae99..fd22f9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" digest = "0.10" generic-ec = { version = "0.4.5", features = ["serde", "curve-secp256k1", "hash-to-scalar"] } generic-ec-zkp = { version = "0.4.4", features = ["serde", "udigest"] } -key-share = "0.5" +key-share = "0.6" rand_core = { version = "0.6", default-features = false } round-based = { version = "0.4", features = ["derive"] } serde = "1" @@ -16,7 +16,7 @@ udigest = { version = "0.2", default-features = false, features = ["inline-struc [dev-dependencies] generic-ec = { version = "0.4.5", features = ["curve-ed25519"] } -key-share = { version = "0.5", features = ["spof"] } +key-share = { version = "0.6", features = ["spof"] } rand = "0.8" rand_dev = "0.1" round-based = { version = "0.4", features = ["sim"] } From 5ad4dcda466cf6b2db99babcbbd5cbb5bab52678 Mon Sep 17 00:00:00 2001 From: maurges Date: Tue, 15 Apr 2025 13:30:58 +0200 Subject: [PATCH 15/15] Clippy --- src/lowlevel.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lowlevel.rs b/src/lowlevel.rs index 6de1284..44914f2 100644 --- a/src/lowlevel.rs +++ b/src/lowlevel.rs @@ -42,8 +42,8 @@ pub struct PartialEvaluation { /// - `counterparty_public_key` - `H_1(x)` in paper, public key of the other /// party doing the key exchange /// - `secret_share` - `sk` from paper, share of the private key of the party -/// doing this key exchange. The `vk` argument which is present in paper but -/// not here is computed from it +/// doing this key exchange. The `vk` argument which is present in paper but +/// not here is computed from it pub fn partial_ecdh( eid: &[u8], i: u16,