diff --git a/Cargo.lock b/Cargo.lock index 4bb4c87a8..71abd4c30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,7 +209,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -497,7 +497,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "synstructure 0.13.1", ] @@ -520,7 +520,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -668,7 +668,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -735,7 +735,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -849,7 +849,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1381,7 +1381,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1405,12 +1405,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "cobs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1528,6 +1522,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-crc32-nostd" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808ac43170e95b11dd23d78aa9eaac5bea45776a602955552c4e833f3f0f823d" + [[package]] name = "const-hex" version = "1.12.0" @@ -1867,7 +1867,7 @@ dependencies = [ "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", - "fiat-crypto", + "fiat-crypto 0.2.9", "rand_core", "rustc_version 0.4.1", "subtle 2.6.1", @@ -1882,7 +1882,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1909,7 +1909,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1926,7 +1926,7 @@ checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1974,7 +1974,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1996,7 +1996,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2051,7 +2051,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", - "pem-rfc7468", "zeroize", ] @@ -2103,6 +2102,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-getters" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "derive-syn-parse" version = "0.2.0" @@ -2111,7 +2121,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2122,7 +2132,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2135,7 +2145,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2156,7 +2166,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "unicode-xid", ] @@ -2265,7 +2275,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2289,12 +2299,21 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.77", + "syn 2.0.90", "termcolor", "toml 0.8.19", "walkdir", ] +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -2408,17 +2427,15 @@ dependencies = [ ] [[package]] -name = "ed448-goldilocks-plus" -version = "0.11.2" -source = "git+https://github.com/webb-tools/Ed448-Goldilocks#31a201b82fbfd7e9c67530fd340c5b9f7087b592" +name = "ed448-goldilocks" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88322282bccdc6fa7ab65b0c30cb877fba541547653436d08bb775fa4a4307b4" dependencies = [ - "elliptic-curve", + "fiat-crypto 0.1.20", "hex", "rand_core", - "serde", - "sha3", "subtle 2.6.1", - "zeroize", ] [[package]] @@ -2442,7 +2459,7 @@ dependencies = [ "enum-ordinalize 4.3.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2461,35 +2478,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", - "base64ct", "crypto-bigint", "digest 0.10.7", "ff", "generic-array 0.14.7", "group", - "pem-rfc7468", "pkcs8", "rand_core", "sec1", - "serde_json", "serdect", "subtle 2.6.1", - "tap", "zeroize", ] -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - [[package]] name = "ena" version = "0.14.3" @@ -2553,7 +2554,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2566,7 +2567,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2586,7 +2587,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2606,7 +2607,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2798,7 +2799,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.77", + "syn 2.0.90", "toml 0.8.19", "walkdir", ] @@ -2816,7 +2817,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2842,7 +2843,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "syn 2.0.77", + "syn 2.0.90", "tempfile", "thiserror", "tiny-keccak", @@ -3122,7 +3123,7 @@ dependencies = [ "prettyplease 0.2.22", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3354,7 +3355,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "bitvec", "byteorder", "ff_derive", "rand_core", @@ -3377,6 +3377,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "fiat-crypto" version = "0.2.9" @@ -3741,7 +3747,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3873,7 +3879,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk?branch=stable2407)", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3885,7 +3891,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3895,7 +3901,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=stable2407#b5eb0 dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -3955,104 +3961,105 @@ dependencies = [ [[package]] name = "frost-core" -version = "1.2.3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5afd375261c34d31ff24dad068382f4bc3c95010c919d4fb8d483dc3d85c023" dependencies = [ "byteorder", + "const-crc32-nostd", "debugless-unwrap", + "derive-getters", + "document-features", "hex", - "parity-scale-codec", - "postcard", + "itertools 0.13.0", "rand_core", - "serde", - "serdect", - "sp-std", - "subtle 2.6.1", + "thiserror", + "thiserror-nostd-notrait", + "visibility", "zeroize", ] [[package]] name = "frost-ed25519" -version = "1.2.3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4186731878c57b4e4d5d1103c8e0d2a827d3cb63cf577826ce29d52c34be7d39" dependencies = [ "curve25519-dalek", + "document-features", "frost-core", - "parity-scale-codec", + "frost-rerandomized", "rand_core", "sha2 0.10.8", - "subtle 2.6.1", ] [[package]] name = "frost-ed448" -version = "1.2.3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c4dffab2dc334fc1b864cf1ce930aafc29e05e4ab9281c81858e92752c1410" dependencies = [ - "ed448-goldilocks-plus", + "document-features", + "ed448-goldilocks", "frost-core", - "parity-scale-codec", + "frost-rerandomized", "rand_core", "sha3", - "subtle 2.6.1", ] [[package]] name = "frost-p256" -version = "1.2.3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235c7f29e912d63953881fc9ee942922c0f63e97ce011e3af8772ced55db4fa6" dependencies = [ + "document-features", "frost-core", + "frost-rerandomized", "p256", - "parity-scale-codec", "rand_core", "sha2 0.10.8", - "subtle 2.6.1", ] [[package]] -name = "frost-p384" -version = "1.2.3" +name = "frost-rerandomized" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9d77595060546b53543d96b83dbeacaf3907e40a89763a8bb22124812b0cb6" dependencies = [ + "derive-getters", + "document-features", "frost-core", - "p384", - "parity-scale-codec", + "hex", "rand_core", - "sha2 0.10.8", - "subtle 2.6.1", ] [[package]] name = "frost-ristretto255" -version = "1.2.3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3f0e065fb99f6abb96b58bd63fd7a6255efe37ee9672d97a7404ac644e54a7" dependencies = [ "curve25519-dalek", + "document-features", "frost-core", - "parity-scale-codec", + "frost-rerandomized", "rand_core", "sha2 0.10.8", - "subtle 2.6.1", ] [[package]] name = "frost-secp256k1" -version = "1.2.3" -dependencies = [ - "frost-core", - "k256", - "parity-scale-codec", - "rand_core", - "sha2 0.10.8", - "subtle 2.6.1", -] - -[[package]] -name = "frost-taproot" -version = "1.2.3" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52edea5a88d11135b2bf069f03e756d405d39c5a1c9c9f4d6147505948d7fff7" dependencies = [ + "document-features", "frost-core", + "frost-rerandomized", "k256", - "parity-scale-codec", "rand_core", "sha2 0.10.8", - "signature", - "subtle 2.6.1", ] [[package]] @@ -4187,7 +4194,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -5375,7 +5382,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -5953,7 +5960,7 @@ dependencies = [ "proc-macro-warning 0.4.2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -6264,6 +6271,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.12" @@ -6335,7 +6348,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -6349,7 +6362,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -6360,7 +6373,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -6371,7 +6384,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -6601,7 +6614,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -6761,7 +6774,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -7076,7 +7089,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -7193,7 +7206,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -7244,20 +7257,10 @@ checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "sha2 0.10.8", ] -[[package]] -name = "p384" -version = "0.13.0" -source = "git+https://github.com/LIT-Protocol/elliptic-curves.git#67924afc93d236e1508afd5f55bbf738e1c41eaa" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder 0.13.6 (git+https://github.com/LIT-Protocol/elliptic-curves.git)", -] - [[package]] name = "pallet-airdrop-claims" version = "1.2.3" @@ -8081,10 +8084,8 @@ dependencies = [ "frost-ed25519", "frost-ed448", "frost-p256", - "frost-p384", "frost-ristretto255", "frost-secp256k1", - "frost-taproot", "hex-literal 0.4.1", "pallet-balances", "pallet-evm", @@ -8525,7 +8526,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -8915,15 +8916,6 @@ dependencies = [ "base64 0.13.1", ] -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -8961,7 +8953,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9034,7 +9026,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9072,7 +9064,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9190,7 +9182,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9200,7 +9192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429" dependencies = [ "polkavm-derive-impl", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9268,18 +9260,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" -[[package]] -name = "postcard" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" -dependencies = [ - "cobs", - "embedded-io 0.4.0", - "embedded-io 0.6.1", - "serde", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -9401,7 +9381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9413,14 +9393,6 @@ dependencies = [ "elliptic-curve", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "git+https://github.com/LIT-Protocol/elliptic-curves.git#67924afc93d236e1508afd5f55bbf738e1c41eaa" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "primitive-types" version = "0.12.2" @@ -9496,7 +9468,7 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9507,14 +9479,14 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -9553,7 +9525,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -9631,7 +9603,7 @@ dependencies = [ "prost 0.12.6", "prost-types 0.12.6", "regex", - "syn 2.0.77", + "syn 2.0.90", "tempfile", ] @@ -9658,7 +9630,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -10011,7 +9983,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -10774,7 +10746,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11727,7 +11699,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -11900,7 +11872,7 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.77", + "syn 2.0.90", "thiserror", ] @@ -12150,7 +12122,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -12205,7 +12177,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -12688,7 +12660,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -12903,7 +12875,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=stable2407#b5eb0 dependencies = [ "quote", "sp-crypto-hashing 0.1.0 (git+https://github.com/paritytech/polkadot-sdk?branch=stable2407)", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -12922,7 +12894,7 @@ source = "git+https://github.com/paritytech/polkadot-sdk?branch=stable2407#b5eb0 dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13135,7 +13107,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13320,7 +13292,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13591,7 +13563,7 @@ checksum = "13ffed6a893a0438ef248355db18ea1d2c49f6bd38f1f099a709e0d181e41a21" dependencies = [ "ff", "hex-literal 0.3.4", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "subtle 2.6.1", "zeroize", ] @@ -13755,7 +13727,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -13917,7 +13889,7 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.77", + "syn 2.0.90", "thiserror", "tokio", ] @@ -13988,7 +13960,7 @@ dependencies = [ "quote", "scale-typegen", "subxt-codegen", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14060,9 +14032,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -14095,7 +14067,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14574,7 +14546,27 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-nostd-notrait" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8444e638022c44d2a9337031dee8acb732bcc7fbf52ac654edc236b26408b61" +dependencies = [ + "thiserror-nostd-notrait-impl", +] + +[[package]] +name = "thiserror-nostd-notrait-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585e5ef40a784ce60b49c67d762110688d211d395d39e096be204535cf64590e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -14693,7 +14685,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -14877,7 +14869,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15105,7 +15097,7 @@ checksum = "d6132fb8382b1bdec1cd01062dbe1e88074610e8fe37c2d504043a45f6400108" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -15272,6 +15264,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "void" version = "1.0.2" @@ -15349,7 +15352,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -15383,7 +15386,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -16155,7 +16158,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -16227,7 +16230,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -16247,7 +16250,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 554a120c4..271371790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,6 @@ members = [ "pallets/services/rpc", "pallets/services/rpc/runtime-api", "pallets/tangle-lst/benchmarking", - "frost", - "frost/frost-*", "precompiles/pallet-democracy", "precompiles/batch", "precompiles/call-permit", @@ -129,14 +127,12 @@ p256 = { version = "0.13.2", default-features = false } ecdsa-core = { package = "ecdsa", version = "0.16.9", default-features = false } starknet-crypto = { version = "0.7.1", default-features = false, features = ["signature-display", "alloc"] } -frost-core = { path = "frost", default-features = false } -frost-ed25519 = { path = "frost/frost-ed25519", default-features = false } -frost-ed448 = { path = "frost/frost-ed448", default-features = false } -frost-ristretto255 = { path = "frost/frost-ristretto255", default-features = false } -frost-secp256k1 = { path = "frost/frost-secp256k1", default-features = false } -frost-p256 = { path = "frost/frost-p256", default-features = false } -frost-p384 = { path = "frost/frost-p384", default-features = false } -frost-taproot = { path = "frost/frost-taproot", default-features = false } +frost-core = { version = "2.0.0", default-features = false } +frost-ed25519 = { version = "2.0.0", default-features = false } +frost-ed448 = { version = "2.0.0", default-features = false } +frost-ristretto255 = { version = "2.0.0", default-features = false } +frost-secp256k1 = { version = "2.0.0", default-features = false } +frost-p256 = { version = "2.0.0", default-features = false } # Substrate dependencies sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2407", default-features = false } diff --git a/frost/Cargo.toml b/frost/Cargo.toml deleted file mode 100644 index 93f7b6f57..000000000 --- a/frost/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "frost-core" -version = { workspace = true } -authors = { workspace = true } -edition = { workspace = true } -homepage = { workspace = true } -license = { workspace = true } -repository = { workspace = true } - -[dependencies] -zeroize = { version = "1.8.1", default-features = false, features = ["derive"] } -byteorder = { workspace = true } -parity-scale-codec = { workspace = true } -serde = { workspace = true } -serdect = { workspace = true, features = ["alloc"] } -sp-std = { workspace = true } -subtle = { workspace = true } -postcard = { version = "1.0.10", default-features = false, features = ["alloc"] } -hex = { workspace = true, features = ["alloc"] } -rand_core = { workspace = true, optional = true } -debugless-unwrap = "0.0.4" - -[features] -default = ["std"] -std = [ - "byteorder/std", - "rand_core/std", - "hex/std", - "sp-std/std", -] diff --git a/frost/frost-ed25519/Cargo.toml b/frost/frost-ed25519/Cargo.toml deleted file mode 100644 index 16ce0c6b7..000000000 --- a/frost/frost-ed25519/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "frost-ed25519" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over Ed25519 that supports FROST." - -[dependencies] -parity-scale-codec = { workspace = true } -curve25519-dalek = { version = "4.1.3", default-features = false } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha2 = { workspace = true } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "rand_core/std", - "curve25519-dalek/rand_core", -] diff --git a/frost/frost-ed25519/src/lib.rs b/frost/frost-ed25519/src/lib.rs deleted file mode 100644 index c788a1984..000000000 --- a/frost/frost-ed25519/src/lib.rs +++ /dev/null @@ -1,223 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -use curve25519_dalek::{ - constants::ED25519_BASEPOINT_POINT, - edwards::{CompressedEdwardsY, EdwardsPoint}, - scalar::Scalar, - traits::Identity, -}; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha512}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct Ed25519ScalarField; - -impl Field for Ed25519ScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 32]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - // [`curve25519_dalek::scalar::Scalar`]'s Eq/PartialEq does a constant-time comparison using - // `ConstantTimeEq` - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - match Scalar::from_canonical_bytes(*buf).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - Self::serialize(scalar) - } -} - -/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Ed25519Group; - -impl Group for Ed25519Group { - type Field = Ed25519ScalarField; - - type Element = WrappedEdwardsPoint; - - type Serialization = [u8; 32]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedEdwardsPoint(EdwardsPoint::identity()) - } - - fn generator() -> Self::Element { - WrappedEdwardsPoint(ED25519_BASEPOINT_POINT) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - element.0.compress().to_bytes() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - match CompressedEdwardsY::from_slice(buf.as_ref()) - .map_err(|_| GroupError::MalformedElement)? - .decompress() - { - Some(point) => { - if WrappedEdwardsPoint(point) == Self::identity() { - Err(GroupError::InvalidIdentityElement) - } else if point.is_torsion_free() { - // At this point we should reject points which were not - // encoded canonically (i.e. Y coordinate >= p). - // However, we don't allow non-prime order elements, - // and that suffices to also reject non-canonical encodings - // per https://eprint.iacr.org/2020/1244.pdf: - // - // > There are 19 elliptic curve points that can be encoded in a non-canonical form. - // > (...) Among these points there are 2 points of small order and from the - // > remaining 17 y-coordinates only 10 decode to valid curve points all of mixed order. - Ok(WrappedEdwardsPoint(point)) - } else { - Err(GroupError::InvalidNonPrimeOrderElement) - } - }, - None => Err(GroupError::MalformedElement), - } - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 64] { - let mut h = Sha512::new(); - for i in inputs { - h.update(i); - } - let mut output = [0u8; 64]; - output.copy_from_slice(h.finalize().as_slice()); - output -} - -fn hash_to_scalar(inputs: &[&[u8]]) -> Scalar { - let output = hash_to_array(inputs); - Scalar::from_bytes_mod_order_wide(&output) -} - -/// Context string from the ciphersuite in the [spec] -/// -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-1 -const CONTEXT_STRING: &str = "FROST-ED25519-SHA512-v1"; - -/// An implementation of the FROST(Ed25519, SHA-512) ciphersuite. -#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] -pub struct Ed25519Sha512; - -impl Ciphersuite for Ed25519Sha512 { - const ID: &'static str = CONTEXT_STRING; - - type Group = Ed25519Group; - - type HashOutput = [u8; 64]; - - type SignatureSerialization = [u8; 64]; - - /// H1 for FROST(Ed25519, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho", m])) - } - - /// H2 for FROST(Ed25519, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[m])) - } - - /// H3 for FROST(Ed25519, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce", m])) - } - - /// H4 for FROST(Ed25519, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(Ed25519, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(Ed25519, SHA-512) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg", m]))) - } - - /// HID for FROST(Ed25519, SHA-512) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id", m]))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-ed25519/src/types.rs b/frost/frost-ed25519/src/types.rs deleted file mode 100644 index b480188f6..000000000 --- a/frost/frost-ed25519/src/types.rs +++ /dev/null @@ -1,147 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use curve25519_dalek::{edwards::CompressedEdwardsY, EdwardsPoint, Scalar}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`curve25519_dalek::scalar::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.as_bytes()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - Ok(WrappedScalar(Scalar::from_canonical_bytes(bytes).unwrap_or(Scalar::ZERO))) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`curve25519_dalek::edwards::EdwardsPoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedEdwardsPoint(pub EdwardsPoint); - -impl Encode for WrappedEdwardsPoint { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.compress().as_bytes()); - } -} - -impl Decode for WrappedEdwardsPoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - Ok(WrappedEdwardsPoint( - CompressedEdwardsY(bytes) - .decompress() - .ok_or(parity_scale_codec::Error::from("Invalid point"))?, - )) - } -} - -impl Sub for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn sub(self, rhs: WrappedEdwardsPoint) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(self.0 - rhs.0) - } -} - -impl Add for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn add(self, rhs: WrappedEdwardsPoint) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn neg(self) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedEdwardsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedEdwardsPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedEdwardsPoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/frost-ed448/Cargo.toml b/frost/frost-ed448/Cargo.toml deleted file mode 100644 index 6288ba93e..000000000 --- a/frost/frost-ed448/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "frost-ed448" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over Ed25519 that supports FROST." - -[package.metadata.docs.rs] -features = ["serde"] -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -parity-scale-codec = { workspace = true } -ed448-goldilocks = { git = "https://github.com/webb-tools/Ed448-Goldilocks", package = "ed448-goldilocks-plus", default-features = false, features = ["zeroize"] } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha3 = { version = "0.10", default-features = false } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "rand_core/std", - "parity-scale-codec/std", - "sha3/std", - "frost-core/std", - "ed448-goldilocks/std" -] diff --git a/frost/frost-ed448/src/lib.rs b/frost/frost-ed448/src/lib.rs deleted file mode 100644 index 750bc9d80..000000000 --- a/frost/frost-ed448/src/lib.rs +++ /dev/null @@ -1,221 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -use ed448_goldilocks::{ - elliptic_curve::generic_array::{typenum::U114, GenericArray}, - CompressedEdwardsY, EdwardsPoint, Scalar, ScalarBytes, -}; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; - -use sha3::{ - digest::{ExtendableOutput, Update, XofReader}, - Shake256, -}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(Ed448, SHAKE256) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct Ed448ScalarField; - -impl Field for Ed448ScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 57]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes_rfc_8032().into() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let buffer = ScalarBytes::clone_from_slice(buf); - match Scalar::from_canonical_bytes(&buffer).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - Self::serialize(scalar) - } -} - -/// An implementation of the FROST(Ed448, SHAKE256) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Ed448Group; - -impl Group for Ed448Group { - type Field = Ed448ScalarField; - - type Element = WrappedEdwardsPoint; - - type Serialization = [u8; 57]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedEdwardsPoint(EdwardsPoint::IDENTITY) - } - - fn generator() -> Self::Element { - WrappedEdwardsPoint(EdwardsPoint::GENERATOR) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - element.0.compress().0 - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let compressed = CompressedEdwardsY(*buf); - match Option::::from(compressed.decompress()) { - Some(point) => { - if point == EdwardsPoint::IDENTITY { - Err(GroupError::InvalidIdentityElement) - } else if point.is_torsion_free().into() { - // decompress() does not check for canonicality, so we - // check by recompressing and comparing - if point.compress().0 != compressed.0 { - Err(GroupError::MalformedElement) - } else { - Ok(WrappedEdwardsPoint(point)) - } - } else { - Err(GroupError::InvalidNonPrimeOrderElement) - } - }, - None => Err(GroupError::MalformedElement), - } - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 114] { - let mut h = Shake256::default(); - for i in inputs { - h.update(i); - } - let mut reader = h.finalize_xof(); - let mut output = [0u8; 114]; - reader.read(&mut output); - output -} - -fn hash_to_scalar(inputs: &[&[u8]]) -> Scalar { - let output = GenericArray::::clone_from_slice(&hash_to_array(inputs)); - Scalar::from_bytes_mod_order_wide(&output) -} - -/// Context string from the ciphersuite in the [spec] -/// -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-1 -const CONTEXT_STRING: &str = "FROST-ED448-SHAKE256-v1"; - -/// An implementation of the FROST(Ed448, SHAKE256) ciphersuite. -#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] -pub struct Ed448Shake256; - -impl Ciphersuite for Ed448Shake256 { - const ID: &'static str = CONTEXT_STRING; - - type Group = Ed448Group; - - type HashOutput = [u8; 114]; - - type SignatureSerialization = [u8; 114]; - - /// H1 for FROST(Ed448, SHAKE256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho", m])) - } - - /// H2 for FROST(Ed448, SHAKE256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[b"SigEd448\0\0", m])) - } - - /// H3 for FROST(Ed448, SHAKE256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce", m])) - } - - /// H4 for FROST(Ed448, SHAKE256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(Ed448, SHAKE256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.3-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(Ed448, SHAKE256) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg", m]))) - } - - /// HID for FROST(Ed448, SHAKE256) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id", m]))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-ed448/src/types.rs b/frost/frost-ed448/src/types.rs deleted file mode 100644 index 1d1770833..000000000 --- a/frost/frost-ed448/src/types.rs +++ /dev/null @@ -1,146 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use ed448_goldilocks::{CompressedEdwardsY, EdwardsPoint, Scalar, ScalarBytes}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`ed448_goldilocks::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 56 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_bytes().as_ref()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 56]; - input.read(&mut bytes)?; - let buffer = ScalarBytes::from_slice(&bytes); - Ok(WrappedScalar(Scalar::from_canonical_bytes(buffer).unwrap_or(Scalar::ZERO))) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`curve25519_dalek::edwards::EdwardsPoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedEdwardsPoint(pub EdwardsPoint); - -impl Encode for WrappedEdwardsPoint { - fn size_hint(&self) -> usize { - 57 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.compress().as_bytes()); - } -} - -impl Decode for WrappedEdwardsPoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 57]; - input.read(&mut bytes)?; - Ok(WrappedEdwardsPoint( - CompressedEdwardsY(bytes).decompress().unwrap_or(EdwardsPoint::default()), - )) - } -} - -impl Sub for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn sub(self, rhs: WrappedEdwardsPoint) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(self.0 - rhs.0) - } -} - -impl Add for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn add(self, rhs: WrappedEdwardsPoint) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedEdwardsPoint { - type Output = WrappedEdwardsPoint; - - fn neg(self) -> WrappedEdwardsPoint { - WrappedEdwardsPoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedEdwardsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedEdwardsPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedEdwardsPoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/frost-p256/Cargo.toml b/frost/frost-p256/Cargo.toml deleted file mode 100644 index b4be5966e..000000000 --- a/frost/frost-p256/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "frost-p256" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over Ed25519 that supports FROST." - -[dependencies] -parity-scale-codec = { workspace = true } -p256 = { workspace = true } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha2 = { workspace = true } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "rand_core/std", - "p256/std" -] \ No newline at end of file diff --git a/frost/frost-p256/src/lib.rs b/frost/frost-p256/src/lib.rs deleted file mode 100644 index 4a47bb858..000000000 --- a/frost/frost-p256/src/lib.rs +++ /dev/null @@ -1,248 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; - -use crate::alloc::borrow::ToOwned; -use p256::{ - elliptic_curve::{ - hash2curve::{hash_to_field, ExpandMsgXmd}, - sec1::{FromEncodedPoint, ToEncodedPoint}, - PrimeField, - }, - AffinePoint, ProjectivePoint, Scalar, -}; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha256}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(P-256, SHA-256) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct P256ScalarField; - -impl Field for P256ScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 32]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - // [`p256::Scalar`]'s Eq/PartialEq does a constant-time comparison using - // `ConstantTimeEq` - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert().unwrap())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - use p256::elliptic_curve::Field; - - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes().into() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let field_bytes: &p256::FieldBytes = buf.into(); - match Scalar::from_repr(*field_bytes).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - let mut array = Self::serialize(scalar); - array.reverse(); - array - } -} - -/// An implementation of the FROST(P-256, SHA-256) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct P256Group; - -impl Group for P256Group { - type Field = P256ScalarField; - - type Element = WrappedProjectivePoint; - - /// [SEC 1][1] serialization of a compressed point in P-256 takes 33 bytes - /// (1-byte prefix and 32 bytes for the coordinate). - /// - /// Note that, in the P-256 spec, the identity is encoded as a single null byte; - /// but here we pad with zeroes. This is acceptable as the identity _should_ never - /// be serialized in FROST, else we error. - /// - /// [1]: https://secg.org/sec1-v2.pdf - type Serialization = [u8; 33]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::IDENTITY) - } - - fn generator() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::GENERATOR) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - let mut fixed_serialized = [0; 33]; - let serialized_point = element.0.to_encoded_point(true); - let serialized = serialized_point.as_bytes(); - // Sanity check; either it takes all bytes or a single byte (identity). - assert!(serialized.len() == fixed_serialized.len() || serialized.len() == 1); - // Copy to the left of the buffer (i.e. pad the identity with zeroes). - // Note that identity elements shouldn't be serialized in FROST, but we - // do this padding so that this function doesn't have to return an error. - // If this encodes the identity, it will fail when deserializing. - { - let (left, _right) = fixed_serialized.split_at_mut(serialized.len()); - left.copy_from_slice(serialized); - } - fixed_serialized - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let encoded_point = - p256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?; - - match Option::::from(AffinePoint::from_encoded_point(&encoded_point)) { - Some(point) => { - if point.is_identity().into() { - // This is actually impossible since the identity is encoded in a single byte - // which will never happen since we receive a 33-byte buffer. - // We leave the check for consistency. - Err(GroupError::InvalidIdentityElement) - } else { - Ok(WrappedProjectivePoint(ProjectivePoint::from(point))) - } - }, - None => Err(GroupError::MalformedElement), - } - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { - let mut h = Sha256::new(); - for i in inputs { - h.update(i); - } - let mut output = [0u8; 32]; - output.copy_from_slice(h.finalize().as_slice()); - output -} - -fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar { - let mut u = [P256ScalarField::zero().0]; - hash_to_field::, Scalar>(&[msg], &[domain], &mut u) - .expect("should never return error according to error cases described in ExpandMsgXmd"); - u[0] -} - -/// Context string from the ciphersuite in the [spec] -/// -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-1 -const CONTEXT_STRING: &str = "FROST-P256-SHA256-v1"; - -/// An implementation of the FROST(P-256, SHA-256) ciphersuite. -#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] -pub struct P256Sha256; - -impl Ciphersuite for P256Sha256 { - const ID: &'static str = CONTEXT_STRING; - - type Group = P256Group; - - type HashOutput = [u8; 32]; - - type SignatureSerialization = [u8; 65]; - - /// H1 for FROST(P-256, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m)) - } - - /// H2 for FROST(P-256, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "chal").as_bytes(), m)) - } - - /// H3 for FROST(P-256, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m)) - } - - /// H4 for FROST(P-256, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(P-256, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(P-256, SHA-256) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "dkg").as_bytes(), m))) - } - - /// HID for FROST(P-256, SHA-256) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "id").as_bytes(), m))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-p256/src/types.rs b/frost/frost-p256/src/types.rs deleted file mode 100644 index 3f44f8bd2..000000000 --- a/frost/frost-p256/src/types.rs +++ /dev/null @@ -1,155 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use p256::{ - elliptic_curve::{ - sec1::{FromEncodedPoint, ToEncodedPoint}, - PrimeField, - }, - EncodedPoint, FieldBytes, ProjectivePoint, Scalar, -}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`p256::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_repr().encode().as_ref()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - Ok(WrappedScalar( - Scalar::from_repr(*FieldBytes::from_slice(&bytes)).unwrap_or(Scalar::ZERO), - )) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`p256::ProjectivePoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedProjectivePoint(pub ProjectivePoint); - -impl Encode for WrappedProjectivePoint { - fn size_hint(&self) -> usize { - 33 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_encoded_point(true).as_bytes()); - } -} - -impl Decode for WrappedProjectivePoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - let pt = ProjectivePoint::from_encoded_point( - &EncodedPoint::from_bytes(bytes).unwrap_or_default(), - ) - .unwrap_or(ProjectivePoint::default()); - Ok(WrappedProjectivePoint(pt)) - } -} - -impl Sub for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn sub(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 - rhs.0) - } -} - -impl Add for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn add(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn neg(self) -> WrappedProjectivePoint { - WrappedProjectivePoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedProjectivePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedProjectivePoint(ProjectivePoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedProjectivePoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/frost-p384/Cargo.toml b/frost/frost-p384/Cargo.toml deleted file mode 100644 index 294b46e67..000000000 --- a/frost/frost-p384/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "frost-p384" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over Ed25519 that supports FROST." - -[package.metadata.docs.rs] -features = ["serde"] -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -parity-scale-codec = { workspace = true } -p384 = { version = "0.13.0", features = ["hash2curve", "alloc"], git = "https://github.com/LIT-Protocol/elliptic-curves.git", default-features = false } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha2 = { workspace = true } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "rand_core/std", - "p384/std" -] \ No newline at end of file diff --git a/frost/frost-p384/src/lib.rs b/frost/frost-p384/src/lib.rs deleted file mode 100644 index 967683b5b..000000000 --- a/frost/frost-p384/src/lib.rs +++ /dev/null @@ -1,248 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; - -use crate::alloc::borrow::ToOwned; -use p384::{ - elliptic_curve::{ - hash2curve::{hash_to_field, ExpandMsgXmd}, - sec1::{FromEncodedPoint, ToEncodedPoint}, - PrimeField, - }, - AffinePoint, ProjectivePoint, Scalar, -}; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha384}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(P-384, SHA-384) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct P384ScalarField; - -impl Field for P384ScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 48]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - // [`p384::Scalar`]'s Eq/PartialEq does a constant-time comparison using - // `ConstantTimeEq` - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert().unwrap())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - use p384::elliptic_curve::Field; - - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes().into() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let field_bytes: &p384::FieldBytes = buf.into(); - match Scalar::from_repr(*field_bytes).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - let mut array = Self::serialize(scalar); - array.reverse(); - array - } -} - -/// An implementation of the FROST(P-384, SHA-384) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct P384Group; - -impl Group for P384Group { - type Field = P384ScalarField; - - type Element = WrappedProjectivePoint; - - /// [SEC 1][1] serialization of a compressed point in P-384 takes 49 bytes - /// (1-byte prefix and 48 bytes for the coordinate). - /// - /// Note that, in the P-384 spec, the identity is encoded as a single null byte; - /// but here we pad with zeroes. This is acceptable as the identity _should_ never - /// be serialized in FROST, else we error. - /// - /// [1]: https://secg.org/sec1-v2.pdf - type Serialization = [u8; 49]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::IDENTITY) - } - - fn generator() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::GENERATOR) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - let mut fixed_serialized = [0; 49]; - let serialized_point = element.0.to_encoded_point(true); - let serialized = serialized_point.as_bytes(); - // Sanity check; either it takes all bytes or a single byte (identity). - assert!(serialized.len() == fixed_serialized.len() || serialized.len() == 1); - // Copy to the left of the buffer (i.e. pad the identity with zeroes). - // Note that identity elements shouldn't be serialized in FROST, but we - // do this padding so that this function doesn't have to return an error. - // If this encodes the identity, it will fail when deserializing. - { - let (left, _right) = fixed_serialized.split_at_mut(serialized.len()); - left.copy_from_slice(serialized); - } - fixed_serialized - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let encoded_point = - p384::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?; - - match Option::::from(AffinePoint::from_encoded_point(&encoded_point)) { - Some(point) => { - if point.is_identity().into() { - // This is actually impossible since the identity is encoded in a single byte - // which will never happen since we receive a 33-byte buffer. - // We leave the check for consistency. - Err(GroupError::InvalidIdentityElement) - } else { - Ok(WrappedProjectivePoint(ProjectivePoint::from(point))) - } - }, - None => Err(GroupError::MalformedElement), - } - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 48] { - let mut h = Sha384::new(); - for i in inputs { - h.update(i); - } - let mut output = [0u8; 48]; - output.copy_from_slice(h.finalize().as_slice()); - output -} - -fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar { - let mut u = [P384ScalarField::zero().0]; - hash_to_field::, Scalar>(&[msg], &[domain], &mut u) - .expect("should never return error according to error cases described in ExpandMsgXmd"); - u[0] -} - -/// Context string from the ciphersuite in the [spec] -/// -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-1 -const CONTEXT_STRING: &str = "FROST-P384-SHA384-v1"; - -/// An implementation of the FROST(P-384, SHA-384) ciphersuite. -#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] -pub struct P384Sha384; - -impl Ciphersuite for P384Sha384 { - const ID: &'static str = CONTEXT_STRING; - - type Group = P384Group; - - type HashOutput = [u8; 48]; - - type SignatureSerialization = [u8; 97]; - - /// H1 for FROST(P-384, SHA-384) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m)) - } - - /// H2 for FROST(P-384, SHA-384) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "chal").as_bytes(), m)) - } - - /// H3 for FROST(P-384, SHA-384) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m)) - } - - /// H4 for FROST(P-384, SHA-384) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(P-384, SHA-384) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.4-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(P-384, SHA-384) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "dkg").as_bytes(), m))) - } - - /// HID for FROST(P-384, SHA-384) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "id").as_bytes(), m))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-p384/src/types.rs b/frost/frost-p384/src/types.rs deleted file mode 100644 index ef86092fa..000000000 --- a/frost/frost-p384/src/types.rs +++ /dev/null @@ -1,155 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use p384::{ - elliptic_curve::{ - sec1::{FromEncodedPoint, ToEncodedPoint}, - PrimeField, - }, - EncodedPoint, FieldBytes, ProjectivePoint, Scalar, -}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`p384::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 48 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_repr().encode().as_ref()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - Ok(WrappedScalar( - Scalar::from_repr(*FieldBytes::from_slice(&bytes)).unwrap_or(Scalar::ZERO), - )) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`p384::ProjectivePoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedProjectivePoint(pub ProjectivePoint); - -impl Encode for WrappedProjectivePoint { - fn size_hint(&self) -> usize { - 49 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_encoded_point(true).as_bytes()); - } -} - -impl Decode for WrappedProjectivePoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - let pt = ProjectivePoint::from_encoded_point( - &EncodedPoint::from_bytes(bytes).unwrap_or_default(), - ) - .unwrap_or(ProjectivePoint::default()); - Ok(WrappedProjectivePoint(pt)) - } -} - -impl Sub for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn sub(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 - rhs.0) - } -} - -impl Add for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn add(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn neg(self) -> WrappedProjectivePoint { - WrappedProjectivePoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedProjectivePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedProjectivePoint(ProjectivePoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedProjectivePoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/frost-ristretto255/Cargo.toml b/frost/frost-ristretto255/Cargo.toml deleted file mode 100644 index 979bbf5da..000000000 --- a/frost/frost-ristretto255/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "frost-ristretto255" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over Ed25519 that supports FROST." - -[dependencies] -parity-scale-codec = { workspace = true } -curve25519-dalek = { version = "4.1.3", default-features = false } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha2 = { workspace = true } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "rand_core/std", - "curve25519-dalek/rand_core", -] diff --git a/frost/frost-ristretto255/src/lib.rs b/frost/frost-ristretto255/src/lib.rs deleted file mode 100644 index a4d955fc5..000000000 --- a/frost/frost-ristretto255/src/lib.rs +++ /dev/null @@ -1,212 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -use curve25519_dalek::{ - constants::RISTRETTO_BASEPOINT_POINT, - ristretto::{CompressedRistretto, RistrettoPoint}, - scalar::Scalar, - traits::Identity, -}; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha512}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(ristretto255, SHA-512) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct RistrettoScalarField; - -impl Field for RistrettoScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 32]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - // [`curve25519_dalek::scalar::Scalar`]'s Eq/PartialEq does a constant-time comparison using - // `ConstantTimeEq` - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - match Scalar::from_canonical_bytes(*buf).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - Self::serialize(scalar) - } -} - -/// An implementation of the FROST(ristretto255, SHA-512) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct RistrettoGroup; - -impl Group for RistrettoGroup { - type Field = RistrettoScalarField; - - type Element = WrappedRistrettoPoint; - - type Serialization = [u8; 32]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedRistrettoPoint(RistrettoPoint::identity()) - } - - fn generator() -> Self::Element { - WrappedRistrettoPoint(RISTRETTO_BASEPOINT_POINT) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - element.0.compress().to_bytes() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - match CompressedRistretto::from_slice(buf.as_ref()) - .map_err(|_| GroupError::MalformedElement)? - .decompress() - { - Some(point) => { - if point == RistrettoPoint::identity() { - Err(GroupError::InvalidIdentityElement) - } else { - Ok(WrappedRistrettoPoint(point)) - } - }, - None => Err(GroupError::MalformedElement), - } - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 64] { - let mut h = Sha512::new(); - for i in inputs { - h.update(i); - } - let mut output = [0u8; 64]; - output.copy_from_slice(h.finalize().as_slice()); - output -} - -fn hash_to_scalar(inputs: &[&[u8]]) -> Scalar { - let output = hash_to_array(inputs); - Scalar::from_bytes_mod_order_wide(&output) -} - -/// Context string from the ciphersuite in the [spec] -/// -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.1-1 -const CONTEXT_STRING: &str = "FROST-RISTRETTO255-SHA512-v1"; - -/// An implementation of the FROST(ristretto255, SHA-512) ciphersuite. -#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] -pub struct Ristretto255Sha512; - -impl Ciphersuite for Ristretto255Sha512 { - const ID: &'static str = CONTEXT_STRING; - - type Group = RistrettoGroup; - - type HashOutput = [u8; 64]; - - type SignatureSerialization = [u8; 64]; - - /// H1 for FROST(ristretto255, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.2-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"rho", m])) - } - - /// H2 for FROST(ristretto255, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.2-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"chal", m])) - } - - /// H3 for FROST(ristretto255, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.2-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"nonce", m])) - } - - /// H4 for FROST(ristretto255, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.2-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(ristretto255, SHA-512) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.2-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(ristretto255, SHA-512) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"dkg", m]))) - } - - /// HID for FROST(ristretto255, SHA-512) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar(&[CONTEXT_STRING.as_bytes(), b"id", m]))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-ristretto255/src/types.rs b/frost/frost-ristretto255/src/types.rs deleted file mode 100644 index d1dce7678..000000000 --- a/frost/frost-ristretto255/src/types.rs +++ /dev/null @@ -1,150 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use curve25519_dalek::{ - ristretto::{CompressedRistretto, RistrettoPoint}, - Scalar, -}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`curve25519_dalek::scalar::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.as_bytes()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - Ok(WrappedScalar(Scalar::from_canonical_bytes(bytes).unwrap_or(Scalar::ZERO))) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`curve25519_dalek::edwards::RistrettoPoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedRistrettoPoint(pub RistrettoPoint); - -impl Encode for WrappedRistrettoPoint { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.compress().as_bytes()); - } -} - -impl Decode for WrappedRistrettoPoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - Ok(WrappedRistrettoPoint( - CompressedRistretto(bytes) - .decompress() - .ok_or(parity_scale_codec::Error::from("Invalid point"))?, - )) - } -} - -impl Sub for WrappedRistrettoPoint { - type Output = WrappedRistrettoPoint; - - fn sub(self, rhs: WrappedRistrettoPoint) -> WrappedRistrettoPoint { - WrappedRistrettoPoint(self.0 - rhs.0) - } -} - -impl Add for WrappedRistrettoPoint { - type Output = WrappedRistrettoPoint; - - fn add(self, rhs: WrappedRistrettoPoint) -> WrappedRistrettoPoint { - WrappedRistrettoPoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedRistrettoPoint { - type Output = WrappedRistrettoPoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedRistrettoPoint { - WrappedRistrettoPoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedRistrettoPoint { - type Output = WrappedRistrettoPoint; - - fn neg(self) -> WrappedRistrettoPoint { - WrappedRistrettoPoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedRistrettoPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedRistrettoPoint(RistrettoPoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedRistrettoPoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/frost-secp256k1/Cargo.toml b/frost/frost-secp256k1/Cargo.toml deleted file mode 100644 index 030ac34da..000000000 --- a/frost/frost-secp256k1/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "frost-secp256k1" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over Ed25519 that supports FROST." - -[package.metadata.docs.rs] -features = ["serde"] -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -parity-scale-codec = { workspace = true } -k256 = { workspace = true, features = ["arithmetic", "expose-field", "hash2curve", "alloc", "pkcs8"] } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha2 = { workspace = true } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "rand_core/std", - "k256/std", - "sha2/std", -] \ No newline at end of file diff --git a/frost/frost-secp256k1/src/lib.rs b/frost/frost-secp256k1/src/lib.rs deleted file mode 100644 index d6f941d21..000000000 --- a/frost/frost-secp256k1/src/lib.rs +++ /dev/null @@ -1,248 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; - -use crate::alloc::borrow::ToOwned; -use k256::{ - elliptic_curve::{ - group::prime::PrimeCurveAffine, - hash2curve::{hash_to_field, ExpandMsgXmd}, - sec1::{FromEncodedPoint, ToEncodedPoint}, - PrimeField, - }, - AffinePoint, ProjectivePoint, Scalar, -}; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha256}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct Secp256K1ScalarField; - -impl Field for Secp256K1ScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 32]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - // [`Scalar`]'s Eq/PartialEq does a constant-time comparison - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert().unwrap())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - use k256::elliptic_curve::Field; - - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes().into() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let field_bytes: &k256::FieldBytes = buf.into(); - match Scalar::from_repr(*field_bytes).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - let mut array = Self::serialize(scalar); - array.reverse(); - array - } -} - -/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Secp256K1Group; - -impl Group for Secp256K1Group { - type Field = Secp256K1ScalarField; - - type Element = WrappedProjectivePoint; - - /// [SEC 1][1] serialization of a compressed point in secp256k1 takes 33 bytes - /// (1-byte prefix and 32 bytes for the coordinate). - /// - /// Note that, in the SEC 1 spec, the identity is encoded as a single null byte; - /// but here we pad with zeroes. This is acceptable as the identity _should_ never - /// be serialized in FROST, else we error. - /// - /// [1]: https://secg.org/sec1-v2.pdf - type Serialization = [u8; 33]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::IDENTITY) - } - - fn generator() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::GENERATOR) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - let mut fixed_serialized = [0; 33]; - let serialized_point = element.0.to_affine().to_encoded_point(true); - let serialized = serialized_point.as_bytes(); - // Sanity check; either it takes all bytes or a single byte (identity). - assert!(serialized.len() == fixed_serialized.len() || serialized.len() == 1); - // Copy to the left of the buffer (i.e. pad the identity with zeroes). - // Note that identity elements shouldn't be serialized in FROST, but we - // do this padding so that this function doesn't have to return an error. - // If this encodes the identity, it will fail when deserializing. - { - let (left, _right) = fixed_serialized.split_at_mut(serialized.len()); - left.copy_from_slice(serialized); - } - fixed_serialized - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let encoded_point = - k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?; - - match Option::::from(AffinePoint::from_encoded_point(&encoded_point)) { - Some(point) => { - if point.is_identity().into() { - // This is actually impossible since the identity is encoded a a single byte - // which will never happen since we receive a 33-byte buffer. - // We leave the check for consistency. - Err(GroupError::InvalidIdentityElement) - } else { - Ok(WrappedProjectivePoint(ProjectivePoint::from(point))) - } - }, - None => Err(GroupError::MalformedElement), - } - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { - let mut h = Sha256::new(); - for i in inputs { - h.update(i); - } - let mut output = [0u8; 32]; - output.copy_from_slice(h.finalize().as_slice()); - output -} - -fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar { - let mut u = [Secp256K1ScalarField::zero().0]; - hash_to_field::, Scalar>(&[msg], &[domain], &mut u) - .expect("should never return error according to error cases described in ExpandMsgXmd"); - u[0] -} - -/// Context string from the ciphersuite in the [spec]. -/// -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-1 -const CONTEXT_STRING: &str = "FROST-secp256k1-SHA256-v1"; - -/// An implementation of the FROST(secp256k1, SHA-256) ciphersuite. -#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] -pub struct Secp256K1Sha256; - -impl Ciphersuite for Secp256K1Sha256 { - const ID: &'static str = CONTEXT_STRING; - - type Group = Secp256K1Group; - - type HashOutput = [u8; 32]; - - type SignatureSerialization = [u8; 65]; - - /// H1 for FROST(secp256k1, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m)) - } - - /// H2 for FROST(secp256k1, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "chal").as_bytes(), m)) - } - - /// H3 for FROST(secp256k1, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m)) - } - - /// H4 for FROST(secp256k1, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(secp256k1, SHA-256) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(secp256k1, SHA-256) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "dkg").as_bytes(), m))) - } - - /// HID for FROST(secp256k1, SHA-256) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "id").as_bytes(), m))) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-secp256k1/src/types.rs b/frost/frost-secp256k1/src/types.rs deleted file mode 100644 index 558b4e741..000000000 --- a/frost/frost-secp256k1/src/types.rs +++ /dev/null @@ -1,150 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use k256::{ - elliptic_curve::{group::GroupEncoding, sec1::ToEncodedPoint, PrimeField}, - CompressedPoint, FieldBytes, ProjectivePoint, Scalar, -}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`curve25519_dalek::scalar::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_bytes().as_ref()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - let buffer = FieldBytes::from_slice(&bytes); - Ok(WrappedScalar(Scalar::from_repr(*buffer).unwrap_or(Scalar::ZERO))) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`k256::ProjectivePoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedProjectivePoint(pub ProjectivePoint); - -impl Encode for WrappedProjectivePoint { - fn size_hint(&self) -> usize { - 33 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_encoded_point(true).as_bytes()); - } -} - -impl Decode for WrappedProjectivePoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 33]; - input.read(&mut bytes)?; - let pt = CompressedPoint::from_slice(&bytes); - Ok(WrappedProjectivePoint( - ProjectivePoint::from_bytes(pt).unwrap_or(ProjectivePoint::default()), - )) - } -} - -impl Sub for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn sub(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 - rhs.0) - } -} - -impl Add for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn add(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn neg(self) -> WrappedProjectivePoint { - WrappedProjectivePoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedProjectivePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedProjectivePoint(ProjectivePoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedProjectivePoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/frost-taproot/Cargo.toml b/frost/frost-taproot/Cargo.toml deleted file mode 100644 index 0091cbb33..000000000 --- a/frost/frost-taproot/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "frost-taproot" -edition = "2021" -# When releasing to crates.io: -# - Update html_root_url -# - Update CHANGELOG.md -# - Create git tag. -version.workspace = true -authors = [ - "Deirdre Connolly ", - "Chelsea Komlo ", - "Conrado Gouvea " -] -readme = "README.md" -license = "MIT OR Apache-2.0" -categories = ["cryptography"] -keywords = ["cryptography", "crypto", "ed25519", "threshold", "signature"] -description = "A Schnorr signature scheme over secp256k1 that supports FROST." - -[package.metadata.docs.rs] -features = ["serde"] -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -parity-scale-codec = { workspace = true } -k256 = { workspace = true, default-features = false, features = ["arithmetic", "schnorr", "expose-field", "hash2curve", "alloc", "pkcs8"] } -frost-core = { workspace = true } -rand_core = { workspace = true, optional = true } -sha2 = { workspace = true } -signature = { workspace = true } -subtle = { workspace = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "rand_core/std", - "k256/std", - "sha2/std", -] diff --git a/frost/frost-taproot/src/lib.rs b/frost/frost-taproot/src/lib.rs deleted file mode 100644 index f2c6f039b..000000000 --- a/frost/frost-taproot/src/lib.rs +++ /dev/null @@ -1,283 +0,0 @@ -#![allow(non_snake_case)] -#![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; - -use crate::alloc::borrow::ToOwned; -use alloc::vec::Vec; -use k256::{ - elliptic_curve::{ - group::prime::PrimeCurveAffine, - hash2curve::{hash_to_field, ExpandMsgXmd}, - ops::Reduce, - point::AffineCoordinates, - sec1::{FromEncodedPoint, ToEncodedPoint}, - PrimeField, - }, - AffinePoint, ProjectivePoint, Scalar, -}; -use subtle::Choice; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; -use sha2::{Digest, Sha256}; - -pub mod types; -pub use types::*; - -// Re-exports in our public API -pub use frost_core::{ - error::{FieldError, GroupError}, - traits::{Ciphersuite, Field, Group}, -}; - -#[cfg(feature = "std")] -pub use rand_core; - -/// An implementation of the FROST(secp256k1, Taproot) ciphersuite scalar field. -#[derive(Clone, Copy)] -pub struct Secp256K1TaprootScalarField; - -impl Field for Secp256K1TaprootScalarField { - type Scalar = WrappedScalar; - - type Serialization = [u8; 32]; - - fn zero() -> Self::Scalar { - WrappedScalar(Scalar::ZERO) - } - - fn one() -> Self::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn invert(scalar: &Self::Scalar) -> Result { - // [`Scalar`]'s Eq/PartialEq does a constant-time comparison - if *scalar == ::zero() { - Err(FieldError::InvalidZeroScalar) - } else { - Ok(WrappedScalar(scalar.0.invert().unwrap())) - } - } - - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar { - use k256::elliptic_curve::Field; - - WrappedScalar(Scalar::random(rng)) - } - - fn serialize(scalar: &Self::Scalar) -> Self::Serialization { - scalar.0.to_bytes().into() - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let field_bytes: &k256::FieldBytes = buf.into(); - match Scalar::from_repr(*field_bytes).into() { - Some(s) => Ok(WrappedScalar(s)), - None => Err(FieldError::MalformedScalar), - } - } - - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization { - let mut array = Self::serialize(scalar); - array.reverse(); - array - } -} - -/// An implementation of the FROST(secp256k1, Taproot) ciphersuite group. -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Secp256K1TaprootGroup; - -impl Group for Secp256K1TaprootGroup { - type Field = Secp256K1TaprootScalarField; - - type Element = WrappedProjectivePoint; - - /// [SEC 1][1] serialization of a compressed point in secp256k1 takes 33 bytes - /// (1-byte prefix and 32 bytes for the coordinate). - /// - /// Note that, in the SEC 1 spec, the identity is encoded as a single null byte; - /// but here we pad with zeroes. This is acceptable as the identity _should_ never - /// be serialized in FROST, else we error. - /// - /// [1]: https://secg.org/sec1-v2.pdf - type Serialization = [u8; 33]; - - fn cofactor() -> ::Scalar { - WrappedScalar(Scalar::ONE) - } - - fn identity() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::IDENTITY) - } - - fn generator() -> Self::Element { - WrappedProjectivePoint(ProjectivePoint::GENERATOR) - } - - fn serialize(element: &Self::Element) -> Self::Serialization { - let mut fixed_serialized = [0; 33]; - let serialized_point = element.0.to_affine().to_encoded_point(true); - let serialized = serialized_point.as_bytes(); - // Sanity check; either it takes all bytes or a single byte (identity). - assert!(serialized.len() == fixed_serialized.len() || serialized.len() == 1); - // Copy to the left of the buffer (i.e. pad the identity with zeroes). - // Note that identity elements shouldn't be serialized in FROST, but we - // do this padding so that this function doesn't have to return an error. - // If this encodes the identity, it will fail when deserializing. - { - let (left, _right) = fixed_serialized.split_at_mut(serialized.len()); - left.copy_from_slice(serialized); - } - fixed_serialized - } - - fn deserialize(buf: &Self::Serialization) -> Result { - let encoded_point = - k256::EncodedPoint::from_bytes(buf).map_err(|_| GroupError::MalformedElement)?; - - match Option::::from(AffinePoint::from_encoded_point(&encoded_point)) { - Some(point) => { - if point.is_identity().into() { - // This is actually impossible since the identity is encoded a a single byte - // which will never happen since we receive a 33-byte buffer. - // We leave the check for consistency. - Err(GroupError::InvalidIdentityElement) - } else { - Ok(WrappedProjectivePoint(ProjectivePoint::from(point))) - } - }, - None => Err(GroupError::MalformedElement), - } - } - - fn challenge_bytes(element: &Self::Element) -> Vec { - element.0.to_affine().x().to_vec() - } - - fn y_is_odd(element: &Self::Element) -> Choice { - element.0.to_affine().y_is_odd() - } -} - -fn hash_to_array(inputs: &[&[u8]]) -> [u8; 32] { - let mut h = Sha256::new(); - for i in inputs { - h.update(i); - } - let mut output = [0u8; 32]; - output.copy_from_slice(h.finalize().as_slice()); - output -} - -fn hash_to_scalar(domain: &[u8], msg: &[u8]) -> Scalar { - let mut u = [Secp256K1TaprootScalarField::zero().0]; - hash_to_field::, Scalar>(&[msg], &[domain], &mut u) - .expect("should never return error according to error cases described in ExpandMsgXmd"); - u[0] -} - -/// Context string from the ciphersuite in the [spec]. -/// similar to -/// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-1 -/// but uses the bitcoin taproot hash instead -const CONTEXT_STRING: &str = "FROST-secp256k1-Taproot-v1"; - -/// An implementation of the FROST(secp256k1, Taproot) ciphersuite. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] -pub struct Secp256K1Taproot; - -impl Ciphersuite for Secp256K1Taproot { - const ID: &'static str = CONTEXT_STRING; - - type Group = Secp256K1TaprootGroup; - - type HashOutput = [u8; 32]; - - type SignatureSerialization = [u8; 65]; - - /// H1 for FROST(secp256k1, Taproot) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.1 - fn H1(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "rho").as_bytes(), m)) - } - - /// H2 for FROST(secp256k1, Taproot) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.2 - fn H2(m: &[u8]) -> <::Field as Field>::Scalar { - const CHALLENGE_TAG: &[u8] = b"BIP0340/challenge"; - fn tagged_hash(tag: &[u8]) -> Sha256 { - let tag_hash = Sha256::digest(tag); - let mut digest = Sha256::new(); - digest.update(tag_hash); - digest.update(tag_hash); - digest - } - WrappedScalar(>::reduce_bytes( - &tagged_hash(CHALLENGE_TAG).chain_update(m).finalize(), - )) - } - - /// H3 for FROST(secp256k1, Taproot) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.3 - fn H3(m: &[u8]) -> <::Field as Field>::Scalar { - WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "nonce").as_bytes(), m)) - } - - /// H4 for FROST(secp256k1, Taproot) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.4 - fn H4(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"msg", m]) - } - - /// H5 for FROST(secp256k1, Taproot) - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-6.5-2.2.2.5 - fn H5(m: &[u8]) -> Self::HashOutput { - hash_to_array(&[CONTEXT_STRING.as_bytes(), b"com", m]) - } - - /// HDKG for FROST(secp256k1, Taproot) - fn HDKG(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "dkg").as_bytes(), m))) - } - - /// HID for FROST(secp256k1, Taproot) - fn HID(m: &[u8]) -> Option<<::Field as Field>::Scalar> { - Some(WrappedScalar(hash_to_scalar((CONTEXT_STRING.to_owned() + "id").as_bytes(), m))) - } -} - -type S = Secp256K1Taproot; - -/// A Schnorr signature on FROST(secp256k1, Taproot). -pub type Signature = frost_core::signature::Signature; - -/// A signing key for a Schnorr signature on FROST(secp256k1, Taproot). -pub type SigningKey = frost_core::signing_key::SigningKey; - -/// A valid verifying key for Schnorr signatures on FROST(secp256k1, Taproot). -pub type VerifyingKey = frost_core::verifying_key::VerifyingKey; - -#[cfg(test)] -mod tests { - use super::*; - use frost_core::{signing_key::SigningKey, verifying_key::VerifyingKey}; - - #[test] - fn test_sign_and_verify() { - let mut rng = rand_core::OsRng; - - let sk = SigningKey::::new(&mut rng); - let vk = VerifyingKey::::from(sk); - - let msg = b"Hello, world!"; - let signature = sk.sign(rng, msg); - assert!(vk.verify(msg, &signature).is_ok()); - } -} diff --git a/frost/frost-taproot/src/types.rs b/frost/frost-taproot/src/types.rs deleted file mode 100644 index 558b4e741..000000000 --- a/frost/frost-taproot/src/types.rs +++ /dev/null @@ -1,150 +0,0 @@ -use core::ops::{Add, Mul, Neg, Sub}; - -use k256::{ - elliptic_curve::{group::GroupEncoding, sec1::ToEncodedPoint, PrimeField}, - CompressedPoint, FieldBytes, ProjectivePoint, Scalar, -}; -use parity_scale_codec::{Decode, Encode}; -use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable}; - -/// A wrapper around a [`curve25519_dalek::scalar::Scalar`] to implement the [`Encode`,`Decode`] -/// traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedScalar(pub Scalar); - -impl Encode for WrappedScalar { - fn size_hint(&self) -> usize { - 32 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_bytes().as_ref()); - } -} - -impl Decode for WrappedScalar { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 32]; - input.read(&mut bytes)?; - let buffer = FieldBytes::from_slice(&bytes); - Ok(WrappedScalar(Scalar::from_repr(*buffer).unwrap_or(Scalar::ZERO))) - } -} - -impl Sub for WrappedScalar { - type Output = WrappedScalar; - - fn sub(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 - rhs.0) - } -} - -impl Add for WrappedScalar { - type Output = WrappedScalar; - - fn add(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 + rhs.0) - } -} - -impl Mul for WrappedScalar { - type Output = WrappedScalar; - - fn mul(self, rhs: WrappedScalar) -> WrappedScalar { - WrappedScalar(self.0 * rhs.0) - } -} - -impl Neg for WrappedScalar { - type Output = WrappedScalar; - - fn neg(self) -> WrappedScalar { - WrappedScalar(-self.0) - } -} - -impl ConditionallySelectable for WrappedScalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedScalar(Scalar::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedScalar { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} - -/// A wrapper around a [`k256::ProjectivePoint`] to implement the -/// [`Encode`,`Decode`] traits. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct WrappedProjectivePoint(pub ProjectivePoint); - -impl Encode for WrappedProjectivePoint { - fn size_hint(&self) -> usize { - 33 - } - - fn encode_to(&self, dest: &mut W) { - dest.write(self.0.to_encoded_point(true).as_bytes()); - } -} - -impl Decode for WrappedProjectivePoint { - fn decode( - input: &mut I, - ) -> Result { - let mut bytes = [0u8; 33]; - input.read(&mut bytes)?; - let pt = CompressedPoint::from_slice(&bytes); - Ok(WrappedProjectivePoint( - ProjectivePoint::from_bytes(pt).unwrap_or(ProjectivePoint::default()), - )) - } -} - -impl Sub for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn sub(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 - rhs.0) - } -} - -impl Add for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn add(self, rhs: WrappedProjectivePoint) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 + rhs.0) - } -} - -impl Mul for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn mul(self, rhs: WrappedScalar) -> WrappedProjectivePoint { - WrappedProjectivePoint(self.0 * rhs.0) - } -} - -impl Neg for WrappedProjectivePoint { - type Output = WrappedProjectivePoint; - - fn neg(self) -> WrappedProjectivePoint { - WrappedProjectivePoint(-self.0) - } -} - -impl ConditionallySelectable for WrappedProjectivePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - WrappedProjectivePoint(ProjectivePoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl ConditionallyNegatable for WrappedProjectivePoint { - fn conditional_negate(&mut self, choice: Choice) { - self.0.conditional_negate(choice); - } -} diff --git a/frost/src/challenge.rs b/frost/src/challenge.rs deleted file mode 100644 index 5af23a0b0..000000000 --- a/frost/src/challenge.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::traits::{Ciphersuite, Field, Group}; -use core::fmt::Debug; - -/// A type refinement for the scalar field element representing the per-message _[challenge]_. -/// -/// [challenge]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-challenge-computa -#[derive(Clone)] -pub struct Challenge(pub <::Field as Field>::Scalar); - -impl Challenge -where - C: Ciphersuite, -{ - /// Creates a challenge from a scalar. - pub fn from_scalar( - scalar: <<::Group as Group>::Field as Field>::Scalar, - ) -> Self { - Self(scalar) - } - - /// Return the underlying scalar. - pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { - self.0 - } -} - -impl Debug for Challenge -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.debug_tuple("Secret") - .field(&hex::encode(<::Field>::serialize(&self.0))) - .finish() - } -} diff --git a/frost/src/const_crc32.rs b/frost/src/const_crc32.rs deleted file mode 100644 index 44b7ae79b..000000000 --- a/frost/src/const_crc32.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! A `const fn` crc32 checksum implementation. -//! -//! # Examples -//! -//! ``` -//! const BYTES: &[u8] = "The quick brown fox jumps over the lazy dog".as_bytes(); -//! const CKSUM: u32 = frost_core::const_crc32::crc32(BYTES); -//! assert_eq!(CKSUM, 0x414fa339_u32); -//! ``` -/// used to generate up a [u32; 256] lookup table in `crc32`. this computes -/// the table on demand for a given "index" `i` -#[rustfmt::skip] -const fn table_fn(i: u32) -> u32 { - let mut out = i; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; - out -} -const fn get_table() -> [u32; 256] { - let mut table: [u32; 256] = [0u32; 256]; - let mut i = 0; - while i < 256 { - table[i] = table_fn(i as u32); - i += 1; - } - table -} -const TABLE: [u32; 256] = get_table(); -/// A `const fn` crc32 checksum implementation. -/// -/// Note: this is a naive implementation that should be expected to have poor performance -/// if used on dynamic data at runtime. Usage should generally be restricted to declaring -/// `const` variables based on `static` or `const` data available at build time. -pub const fn crc32(buf: &[u8]) -> u32 { - crc32_seed(buf, 0) -} -/// Calculate crc32 checksum, using provided `seed` as the initial state, instead of the -/// default initial state of `0u32`. -/// -/// # Examples -/// -/// Calculating the checksum from several parts of a larger input: -/// -/// ``` -/// const BYTES: &[u8] = "The quick brown fox jumps over the lazy dog".as_bytes(); -/// -/// let mut cksum = 0u32; -/// -/// cksum = frost_core::const_crc32::crc32_seed(&BYTES[0..10], cksum); -/// cksum = frost_core::const_crc32::crc32_seed(&BYTES[10..15], cksum); -/// cksum = frost_core::const_crc32::crc32_seed(&BYTES[15..], cksum); -/// -/// assert_eq!(cksum, frost_core::const_crc32::crc32(BYTES)); -/// ``` -/// -/// Using separate seeds for different kinds of data, to produce different checksums depending -/// on what kind of data the bytes represent: -/// -/// ``` -/// const THING_ONE_SEED: u32 = 0xbaaaaaad_u32; -/// const THING_TWO_SEED: u32 = 0x2bad2bad_u32; -/// -/// let thing_one_bytes = "bump! thump!".as_bytes(); -/// let thing_two_bytes = "thump! bump!".as_bytes(); -/// -/// let thing_one_cksum = frost_core::const_crc32::crc32_seed(thing_one_bytes, THING_ONE_SEED); -/// let thing_two_cksum = frost_core::const_crc32::crc32_seed(thing_two_bytes, THING_TWO_SEED); -/// -/// assert_ne!(thing_one_cksum, thing_two_cksum); -/// ``` -#[inline] -pub const fn crc32_seed(buf: &[u8], seed: u32) -> u32 { - let mut out = !seed; - let mut i = 0usize; - while i < buf.len() { - out = (out >> 8) ^ TABLE[((out & 0xff) ^ (buf[i] as u32)) as usize]; - i += 1; - } - !out -} diff --git a/frost/src/error.rs b/frost/src/error.rs deleted file mode 100644 index 23c6a73dd..000000000 --- a/frost/src/error.rs +++ /dev/null @@ -1,104 +0,0 @@ -use core::fmt::{Debug, Display}; - -/// An error related to a scalar Field. -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum FieldError { - /// The encoding of a group scalar was malformed. - MalformedScalar, - /// This scalar MUST NOT be zero. - InvalidZeroScalar, -} - -/// An error related to a Group (usually an elliptic curve or constructed from one) or one of its -/// Elements. -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum GroupError { - /// The encoding of a group element was malformed. - MalformedElement, - /// This element MUST NOT be the identity. - InvalidIdentityElement, - /// This element MUST have (large) prime order. - InvalidNonPrimeOrderElement, -} - -#[non_exhaustive] -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum Error { - /// An error related to a scalar Field. - Field(FieldError), - /// An error related to a Group (usually an elliptic curve or constructed from one) or one of - /// its Elements. - Group(GroupError), - /// Serialization error - SerializationError, - /// Deserialization error - DeserializationError, - IdentifierDerivationNotSupported, - /// An error related to a Malformed Signature. - MalformedSignature, - /// An error related to an invalid signature verification - InvalidSignature, - /// An error related to a VerifyingKey. - MalformedVerifyingKey, - /// An error related to a SigningKey - MalformedSigningKey, - /// Missing commitment - MissingCommitment, - /// Invalid signature share - InvalidSignatureShare, - /// Duplicated identifier - DuplicatedIdentifier, - /// Unknown identifier - UnknownIdentifier, - /// Incorrect number of identifiers - IncorrectNumberOfIdentifiers, - /// Identity commitment error - IdentityCommitment, - /// Invalid secret share - InvalidSecretShare, -} - -impl Debug for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Error::Field(e) => write!(f, "Field error: {:?}", e), - Error::Group(e) => write!(f, "Group error: {:?}", e), - Error::MalformedSignature => write!(f, "Malformed Signature error"), - Error::InvalidSignature => write!(f, "Invalid Signature error"), - Error::MalformedVerifyingKey => write!(f, "Malformed VerifyingKey"), - Error::MalformedSigningKey => write!(f, "Malformed SigningKey"), - Error::SerializationError => write!(f, "Serialization error"), - Error::DeserializationError => write!(f, "Deserialization error"), - Error::IdentifierDerivationNotSupported => { - write!(f, "Identifier derivation not supported") - }, - Error::MissingCommitment => write!(f, "Missing commitment"), - Error::InvalidSignatureShare => write!(f, "Invalid signature share"), - Error::DuplicatedIdentifier => write!(f, "Duplicated identifier"), - Error::UnknownIdentifier => write!(f, "Unknown identifier"), - Error::IncorrectNumberOfIdentifiers => write!(f, "Incorrect number of identifiers"), - Error::IdentityCommitment => write!(f, "Identity commitment error"), - Error::InvalidSecretShare => write!(f, "Invalid secret share"), - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - Debug::fmt(self, f) - } -} - -impl From for Error { - fn from(e: FieldError) -> Self { - Error::Field(e) - } -} - -impl From for Error { - fn from(e: GroupError) -> Self { - Error::Group(e) - } -} diff --git a/frost/src/identifier.rs b/frost/src/identifier.rs deleted file mode 100644 index f81425188..000000000 --- a/frost/src/identifier.rs +++ /dev/null @@ -1,190 +0,0 @@ -use core::{ - fmt::Debug, - hash::{Hash, Hasher}, -}; - -use crate::{ - error::{Error, FieldError}, - serialization::ScalarSerialization, - traits::{Ciphersuite, Field, Group, Scalar}, - util::scalar_is_valid, -}; - -#[derive(Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ScalarSerialization")] -#[serde(into = "ScalarSerialization")] -pub struct Identifier(Scalar); - -impl Identifier -where - C: Ciphersuite, -{ - /// Create a new Identifier from a scalar. For internal use only. - fn new(scalar: Scalar) -> Result { - if scalar == <::Field>::zero() { - Err(FieldError::InvalidZeroScalar.into()) - } else { - Ok(Self(scalar)) - } - } - - /// Derive an Identifier from an arbitrary byte string. - /// - /// This feature is not part of the specification and is just a convenient - /// way of creating identifiers. - /// - /// Each possible byte string will map to an uniformly random identifier. - /// Returns an error if the ciphersuite does not support identifier derivation, - /// or if the mapped identifier is zero (which is unpredictable, but should happen - /// with negligible probability). - pub fn derive(s: &[u8]) -> Result { - let scalar = C::HID(s).ok_or(Error::IdentifierDerivationNotSupported)?; - Self::new(scalar) - } - - /// Serialize the identifier using the ciphersuite encoding. - pub fn serialize(&self) -> <::Field as Field>::Serialization { - <::Field>::serialize(&self.0) - } - - /// Deserialize an Identifier from a serialized buffer. - /// Returns an error if it attempts to deserialize zero. - pub fn deserialize( - buf: &<::Field as Field>::Serialization, - ) -> Result { - let scalar = <::Field>::deserialize(buf)?; - Self::new(scalar) - } - - /// Check if the identifier is valid aka not zero - pub fn is_valid(&self) -> bool { - scalar_is_valid::(&self.0) - } -} - -impl TryFrom> for Identifier -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ScalarSerialization) -> Result { - Self::deserialize(&value.0) - } -} - -impl From> for ScalarSerialization -where - C: Ciphersuite, -{ - fn from(value: Identifier) -> Self { - Self(value.serialize()) - } -} - -impl Eq for Identifier where C: Ciphersuite {} - -impl Debug for Identifier -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_tuple("Identifier") - .field(&hex::encode(<::Field>::serialize(&self.0).as_ref())) - .finish() - } -} - -#[allow(clippy::derived_hash_with_manual_eq)] -impl Hash for Identifier -where - C: Ciphersuite, -{ - fn hash(&self, state: &mut H) { - <::Field>::serialize(&self.0).as_ref().hash(state) - } -} - -impl Ord for Identifier -where - C: Ciphersuite, -{ - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - let serialized_self = <::Field>::little_endian_serialize(&self.0); - let serialized_other = <::Field>::little_endian_serialize(&other.0); - // The default cmp uses lexicographic order; so we need the elements in big endian - serialized_self - .as_ref() - .iter() - .rev() - .cmp(serialized_other.as_ref().iter().rev()) - } -} - -impl PartialOrd for Identifier -where - C: Ciphersuite, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl core::ops::Mul> for Identifier -where - C: Ciphersuite, -{ - type Output = Scalar; - - fn mul(self, scalar: Scalar) -> Scalar { - self.0 * scalar - } -} - -impl core::ops::MulAssign> for Scalar -where - C: Ciphersuite, -{ - fn mul_assign(&mut self, identifier: Identifier) { - *self = *self * identifier.0 - } -} - -impl core::ops::Sub for Identifier -where - C: Ciphersuite, -{ - type Output = Self; - - fn sub(self, rhs: Identifier) -> Self::Output { - Self(self.0 - rhs.0) - } -} - -impl TryFrom for Identifier -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(n: u16) -> Result, Self::Error> { - if n == 0 { - Err(FieldError::InvalidZeroScalar.into()) - } else { - // Classic left-to-right double-and-add algorithm that skips the first bit 1 (since - // identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too. - let one = <::Field>::one(); - let mut sum = <::Field>::one(); - - let bits = (n.to_be_bytes().len() as u32) * 8; - for i in (0..(bits - n.leading_zeros() - 1)).rev() { - sum = sum + sum; - if n & (1 << i) != 0 { - sum = sum + one; - } - } - Ok(Self(sum)) - } - } -} diff --git a/frost/src/keygen.rs b/frost/src/keygen.rs deleted file mode 100644 index 391769571..000000000 --- a/frost/src/keygen.rs +++ /dev/null @@ -1,95 +0,0 @@ -pub mod round1 { - use crate::{ - keys::VerifiableSecretSharingCommitment, signature::Signature, traits::Ciphersuite, Header, - }; - - /// The package that must be broadcast by each participant to all other participants - /// between the first and second parts of the DKG protocol (round 1). - #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] - #[serde(bound = "C: Ciphersuite")] - #[serde(deny_unknown_fields)] - pub struct Package { - /// Serialization header - pub header: Header, - /// The public commitment from the participant (C_i) - pub commitment: VerifiableSecretSharingCommitment, - /// The proof of knowledge of the temporary secret (σ_i = (R_i, μ_i)) - pub proof_of_knowledge: Signature, - } - - impl Package - where - C: Ciphersuite, - { - /// Create a new [`Package`] instance. - pub fn new( - commitment: VerifiableSecretSharingCommitment, - proof_of_knowledge: Signature, - ) -> Self { - Self { header: Header::default(), commitment, proof_of_knowledge } - } - } - - #[cfg(feature = "serialization")] - impl Package - where - C: Ciphersuite, - { - /// Serialize the struct into a Vec. - pub fn serialize(&self) -> Result, Error> { - Serialize::serialize(&self) - } - - /// Deserialize the struct from a slice of bytes. - pub fn deserialize(bytes: &[u8]) -> Result> { - Deserialize::deserialize(bytes) - } - } -} - -pub mod round2 { - use crate::{keys::SigningShare, traits::Ciphersuite, Header}; - - /// A package that must be sent by each participant to some other participants - /// in Round 2 of the DKG protocol. Note that there is one specific package - /// for each specific recipient, in contrast to Round 1. - /// - /// # Security - /// - /// The package must be sent on an *confidential* and *authenticated* channel. - #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] - #[serde(bound = "C: Ciphersuite")] - #[serde(deny_unknown_fields)] - pub struct Package { - /// Serialization header - pub header: Header, - /// The secret share being sent. - pub signing_share: SigningShare, - } - - impl Package - where - C: Ciphersuite, - { - /// Create a new [`Package`] instance. - pub fn new(signing_share: SigningShare) -> Self { - Self { header: Header::default(), signing_share } - } - } - - #[cfg(feature = "serialization")] - impl Package - where - C: Ciphersuite, - { - /// Serialize the struct into a Vec. - pub fn serialize(&self) -> Result, Error> { - Serialize::serialize(&self) - } - - /// Deserialize the struct from a slice of bytes. - pub fn deserialize(bytes: &[u8]) -> Result> { - Deserialize::deserialize(bytes) - } - } -} diff --git a/frost/src/keys.rs b/frost/src/keys.rs deleted file mode 100644 index 1c2656da4..000000000 --- a/frost/src/keys.rs +++ /dev/null @@ -1,516 +0,0 @@ -use core::fmt::Debug; - -use crate::{ - error::Error, - identifier::Identifier, - serialization::{Deserialize, ElementSerialization, ScalarSerialization, Serialize}, - traits::{Ciphersuite, Element, Field, Group, Scalar}, - util::{element_is_valid, scalar_is_valid}, - verifying_key::VerifyingKey, - Header, -}; -use alloc::collections::BTreeMap; -use sp_std::vec::Vec; -use zeroize::{DefaultIsZeroes, Zeroize}; - -#[cfg(feature = "std")] -use hex::FromHex; - -/// A secret scalar value representing a signer's share of the group secret. -#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ScalarSerialization")] -#[serde(into = "ScalarSerialization")] -pub struct SigningShare(pub Scalar); - -impl SigningShare -where - C: Ciphersuite, -{ - /// Create a new [`SigningShare`] from a scalar. - pub fn new(scalar: Scalar) -> Self { - Self(scalar) - } - - /// Get the inner scalar. - pub fn to_scalar(&self) -> Scalar { - self.0 - } - - /// Deserialize from bytes - pub fn deserialize( - bytes: <::Field as Field>::Serialization, - ) -> Result { - <::Field>::deserialize(&bytes) - .map(|scalar| Self(scalar)) - .map_err(|e| e.into()) - } - - /// Serialize to bytes - pub fn serialize(&self) -> <::Field as Field>::Serialization { - <::Field>::serialize(&self.0) - } - - /// Computes the signing share from a list of coefficients. - #[allow(dead_code)] - pub fn from_coefficients(coefficients: &[Scalar], peer: Identifier) -> Self { - Self(evaluate_polynomial(peer, coefficients)) - } - - /// Verifies that a signing share is valid aka not zero - pub fn is_valid(&self) -> bool { - scalar_is_valid::(&self.0) - } -} - -impl Debug for SigningShare -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("SigningShare").field(&"").finish() - } -} - -impl Default for SigningShare -where - C: Ciphersuite, -{ - fn default() -> Self { - Self(<::Field>::zero()) - } -} - -// Implements [`Zeroize`] by overwriting a value with the [`Default::default()`] value -impl DefaultIsZeroes for SigningShare where C: Ciphersuite {} - -#[cfg(any(test, feature = "std"))] -impl FromHex for SigningShare -where - C: Ciphersuite, -{ - type Error = &'static str; - - fn from_hex>(hex: T) -> Result { - let v: Vec = FromHex::from_hex(hex).map_err(|_| "invalid hex")?; - match v.try_into() { - Ok(bytes) => Self::deserialize(bytes).map_err(|_| "malformed secret encoding"), - Err(_) => Err("malformed secret encoding"), - } - } -} - -impl TryFrom> for SigningShare -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ScalarSerialization) -> Result { - Self::deserialize(value.0) - } -} - -impl From> for ScalarSerialization -where - C: Ciphersuite, -{ - fn from(value: SigningShare) -> Self { - Self(value.serialize()) - } -} - -/// A public group element that represents a single signer's public verification share. -#[derive(Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ElementSerialization")] -#[serde(into = "ElementSerialization")] -pub struct VerifyingShare(pub Element) -where - C: Ciphersuite; - -impl VerifyingShare -where - C: Ciphersuite, -{ - /// Create a new [`VerifyingShare`] from a element. - pub fn new(element: Element) -> Self { - Self(element) - } - - /// Get the inner element. - pub fn to_element(&self) -> Element { - self.0 - } - - /// Deserialize from bytes - pub fn deserialize(bytes: ::Serialization) -> Result { - ::deserialize(&bytes) - .map(|element| Self(element)) - .map_err(|e| e.into()) - } - - /// Serialize to bytes - pub fn serialize(&self) -> ::Serialization { - ::serialize(&self.0) - } - - /// Verifies that a verifying share is valid aka not zero or the base point - pub fn is_valid(&self) -> bool { - element_is_valid::(&self.0) - } -} - -impl Debug for VerifyingShare -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_tuple("VerifyingShare").field(&hex::encode(self.serialize())).finish() - } -} - -impl TryFrom> for VerifyingShare -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ElementSerialization) -> Result { - Self::deserialize(value.0) - } -} - -impl From> for ElementSerialization -where - C: Ciphersuite, -{ - fn from(value: VerifyingShare) -> Self { - Self(value.serialize()) - } -} - -/// A [`Group::Element`] newtype that is a commitment to one coefficient of our secret polynomial. -/// -/// This is a (public) commitment to one coefficient of a secret polynomial used for performing -/// verifiable secret sharing for a Shamir secret share. -#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ElementSerialization")] -#[serde(into = "ElementSerialization")] -pub struct CoefficientCommitment(pub Element); - -impl CoefficientCommitment -where - C: Ciphersuite, -{ - /// Create a new CoefficientCommitment. - pub fn new(value: Element) -> Self { - Self(value) - } - - /// returns serialized element - pub fn serialize(&self) -> ::Serialization { - ::serialize(&self.0) - } - - /// Creates a new commitment from a coefficient input - pub fn deserialize( - coefficient: ::Serialization, - ) -> Result, Error> { - Ok(Self::new(::deserialize(&coefficient)?)) - } - - /// Returns inner element value - pub fn value(&self) -> Element { - self.0 - } - - /// Verifies that a coefficient commitment is valid aka not zero or the base point - pub fn is_valid(&self) -> bool { - element_is_valid::(&self.0) - } -} - -impl Debug for CoefficientCommitment -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("CoefficientCommitment") - .field(&hex::encode(self.serialize())) - .finish() - } -} - -impl TryFrom> for CoefficientCommitment -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ElementSerialization) -> Result { - Self::deserialize(value.0) - } -} - -impl From> for ElementSerialization -where - C: Ciphersuite, -{ - fn from(value: CoefficientCommitment) -> Self { - Self(value.serialize()) - } -} - -/// Contains the commitments to the coefficients for our secret polynomial _f_, -/// used to generate participants' key shares. -/// -/// [`VerifiableSecretSharingCommitment`] contains a set of commitments to the coefficients (which -/// themselves are scalars) for a secret polynomial f, where f is used to -/// generate each ith participant's key share f(i). Participants use this set of -/// commitments to perform verifiable secret sharing. -/// -/// Note that participants MUST be assured that they have the *same* -/// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using -/// some agreed-upon public location for publication, where each participant can -/// ensure that they received the correct (and same) value. -#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -pub struct VerifiableSecretSharingCommitment(pub Vec>); - -impl VerifiableSecretSharingCommitment -where - C: Ciphersuite, -{ - /// Create a new VerifiableSecretSharingCommitment. - pub fn new(coefficients: Vec>) -> Self { - Self(coefficients) - } - - /// Returns serialized coefficent commitments - pub fn serialize(&self) -> Vec<::Serialization> { - self.0 - .iter() - .map(|cc| <::Group as Group>::serialize(&cc.0)) - .collect() - } - - /// Returns VerifiableSecretSharingCommitment from a vector of serialized CoefficientCommitments - pub fn deserialize( - serialized_coefficient_commitments: Vec<::Serialization>, - ) -> Result { - let mut coefficient_commitments = Vec::new(); - for cc in serialized_coefficient_commitments { - coefficient_commitments.push(CoefficientCommitment::::deserialize(cc)?); - } - - Ok(Self::new(coefficient_commitments)) - } - - /// Get the VerifyingKey matching this commitment vector (which is the first - /// element in the vector), or an error if the vector is empty. - pub fn verifying_key(&self) -> Result, Error> { - Ok(VerifyingKey::new(self.0.first().ok_or(Error::MissingCommitment)?.0)) - } - - /// Returns the coefficient commitments. - pub fn coefficients(&self) -> &[CoefficientCommitment] { - &self.0 - } - - /// Verifies that all coefficients are valid aka not zero or the base point - pub fn is_valid(&self) -> bool { - self.0.iter().all(|cc| cc.is_valid()) - } -} - -/// A secret share generated by performing a (t-out-of-n) secret sharing scheme, -/// generated by a dealer performing [`generate_with_dealer`]. -/// -/// `n` is the total number of shares and `t` is the threshold required to reconstruct the secret; -/// in this case we use Shamir's secret sharing. -/// -/// As a solution to the secret polynomial _f_ (a 'point'), the `identifier` is the x-coordinate, -/// and the `value` is the y-coordinate. -/// -/// To derive a FROST keypair, the receiver of the [`SecretShare`] *must* call -/// .into(), which under the hood also performs validation. -#[derive(Clone, Debug, Zeroize, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(deny_unknown_fields)] -pub struct SecretShare { - /// Serialization header - pub header: Header, - /// The participant identifier of this [`SecretShare`]. - #[zeroize(skip)] - pub identifier: Identifier, - /// Secret Key. - pub signing_share: SigningShare, - #[zeroize(skip)] - /// The commitments to be distributed among signers. - pub commitment: VerifiableSecretSharingCommitment, -} - -impl SecretShare -where - C: Ciphersuite, -{ - /// Create a new [`SecretShare`] instance. - pub fn new( - identifier: Identifier, - signing_share: SigningShare, - commitment: VerifiableSecretSharingCommitment, - ) -> Self { - SecretShare { header: Header::default(), identifier, signing_share, commitment } - } - - /// Verifies that a secret share is consistent with a verifiable secret sharing commitment, - /// and returns the derived group info for the participant (their public verification share, - /// and the group public key) if successful. - /// - /// This ensures that this participant's share has been generated using the same - /// mechanism as all other signing participants. Note that participants *MUST* - /// ensure that they have the same view as all other participants of the - /// commitment! - /// - /// An implementation of `vss_verify()` from the [spec]. - /// This also implements `derive_group_info()` from the [spec] (which is very similar), - /// but only for this participant. - /// - /// [spec]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#appendix-C.2-4 - pub fn verify(&self) -> Result<(VerifyingShare, VerifyingKey), Error> { - let f_result = ::generator() * self.signing_share.0; - let result = evaluate_vss(self.identifier, &self.commitment); - - if !(f_result == result) { - return Err(Error::InvalidSecretShare); - } - - Ok((VerifyingShare(result), self.commitment.verifying_key()?)) - } - - /// Checks that all values are valid aka no zero or base point values - pub fn is_valid(&self) -> bool { - self.signing_share.is_valid() && self.commitment.is_valid() && self.commitment.is_valid() - } -} - -#[cfg(feature = "serialization")] -impl SecretShare -where - C: Ciphersuite, -{ - /// Serialize the struct into a Vec. - pub fn serialize(&self) -> Result, Error> { - Serialize::serialize(&self) - } - - /// Deserialize the struct from a slice of bytes. - pub fn deserialize(bytes: &[u8]) -> Result { - Deserialize::deserialize(bytes) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(deny_unknown_fields)] -pub struct PublicKeyPackage { - /// Serialization header - pub header: Header, - /// The verifying shares for all participants. Used to validate signature - /// shares they generate. - pub verifying_shares: BTreeMap, VerifyingShare>, - /// The joint public key for the entire group. - pub verifying_key: VerifyingKey, -} - -impl PublicKeyPackage -where - C: Ciphersuite, -{ - /// Serialize the struct into a Vec. - pub fn serialize(&self) -> Result, Error> { - Serialize::serialize(&self) - } - - /// Deserialize the struct from a slice of bytes. - pub fn deserialize(bytes: &[u8]) -> Result { - Deserialize::deserialize(bytes) - } -} - -// Default byte-oriented serialization for structs that need to be communicated. -// -// Note that we still manually implement these methods in each applicable type, -// instead of making these traits `pub` and asking users to import the traits. -// The reason is that ciphersuite traits would need to re-export these traits, -// parametrized with the ciphersuite, but trait aliases are not currently -// supported: - -#[cfg(feature = "serialization")] -pub trait Serialize { - /// Serialize the struct into a Vec. - fn serialize(&self) -> Result, Error>; -} - -#[cfg(feature = "serialization")] -pub trait Deserialize { - /// Deserialize the struct from a slice of bytes. - fn deserialize(bytes: &[u8]) -> Result - where - Self: core::marker::Sized; -} - -#[cfg(feature = "serialization")] -impl Serialize for T { - fn serialize(&self) -> Result, Error> { - postcard::to_allocvec(self).map_err(|_| Error::SerializationError) - } -} - -#[cfg(feature = "serialization")] -impl serde::Deserialize<'de>, C: Ciphersuite> Deserialize for T { - fn deserialize(bytes: &[u8]) -> Result { - postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError) - } -} - -/// Evaluate the polynomial with the given coefficients (constant term first) -/// at the point x=identifier using Horner's method. -/// -/// Implements [`polynomial_evaluate`] from the spec. -/// -/// [`polynomial_evaluate`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-evaluation-of-a-polynomial -pub fn evaluate_polynomial( - identifier: Identifier, - coefficients: &[Scalar], -) -> Scalar { - let mut value = <::Field>::zero(); - - let ell_scalar = identifier; - for coeff in coefficients.iter().skip(1).rev() { - value = value + *coeff; - value *= ell_scalar; - } - value = value + *coefficients.first().expect("coefficients must have at least one element"); - value -} - -/// Evaluates the right-hand side of the VSS verification equation, namely -/// ∏^{t−1}_{k=0} φ^{i^k mod q}_{ℓk} (multiplicative notation) using -/// `identifier` as `i` and the `commitment` as the commitment vector φ_ℓ. -/// -/// This is also used in Round 2, Step 4 of the DKG. -pub fn evaluate_vss( - identifier: Identifier, - commitment: &VerifiableSecretSharingCommitment, -) -> Element { - let i = identifier; - - let (_, result) = commitment.0.iter().fold( - (<::Field>::one(), ::identity()), - |(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k.0 * i_to_the_k), - ); - result -} diff --git a/frost/src/lib.rs b/frost/src/lib.rs deleted file mode 100644 index ede2024db..000000000 --- a/frost/src/lib.rs +++ /dev/null @@ -1,459 +0,0 @@ -// This file is part of Tangle. -// Copyright (C) 2022-2024 Tangle Foundation. -// -// Tangle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Tangle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Tangle. If not, see . - -//! # FROST no_std primitives -//! -//! A no_std copy of FROST primitives from https://github.com/LIT-Protocol/frost. -//! Needed in order to properly verify Schnorr threshold signatures based on FROST -//! protocol from this library, since the original library was not no_std compatible. -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(non_snake_case)] -extern crate alloc; - -pub mod challenge; -pub mod const_crc32; -pub mod error; -pub mod identifier; -pub mod keygen; -pub mod keys; -pub mod round1; -pub mod scalar_mul; -pub mod serialization; -pub mod signature; -pub mod signing_key; -pub mod traits; -pub mod util; -pub mod verifying_key; - -use alloc::collections::{BTreeMap, BTreeSet}; -use core::{fmt::Debug, marker::PhantomData}; -use scalar_mul::VartimeMultiscalarMul; -use sp_std::{vec, vec::Vec}; -use zeroize::Zeroize; - -use challenge::Challenge; -use error::Error; -use identifier::Identifier; -use serde::{Deserialize, Serialize}; -use traits::{Ciphersuite, Element, Field, Group, Scalar}; -use verifying_key::VerifyingKey; - -#[cfg(feature = "std")] -use hex::FromHex; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; - -/// Generates a random nonzero scalar. -/// -/// It assumes that the Scalar Eq/PartialEq implementation is constant-time. -#[cfg(feature = "std")] -pub fn random_nonzero(rng: &mut R) -> Scalar { - loop { - let scalar = <::Field>::random(rng); - - if scalar != <::Field>::zero() { - return scalar; - } - } -} - -#[derive(Copy, Clone, Debug, Zeroize, PartialEq, Eq, Serialize, Deserialize)] -pub struct Header { - /// Format version - pub version: u8, - /// Ciphersuite ID - pub ciphersuite: (), - #[serde(skip)] - pub phantom: PhantomData, -} - -impl Default for Header -where - C: Ciphersuite, -{ - fn default() -> Self { - Self { - version: Default::default(), - ciphersuite: Default::default(), - phantom: Default::default(), - } - } -} - -/// The binding factor, also known as _rho_ (ρ) -/// -/// Ensures each signature share is strongly bound to a signing set, specific set -/// of commitments, and a specific message. -/// -/// -#[derive(Clone, PartialEq, Eq)] -pub struct BindingFactor(Scalar); - -impl BindingFactor -where - C: Ciphersuite, -{ - /// Serializes [`BindingFactor`] to bytes. - pub fn serialize(&self) -> <::Field as Field>::Serialization { - <::Field>::serialize(&self.0) - } -} - -impl Debug for BindingFactor -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_tuple("BindingFactor").field(&hex::encode(self.serialize())).finish() - } -} - -/// A list of binding factors and their associated identifiers. -#[derive(Clone)] -pub struct BindingFactorList(BTreeMap, BindingFactor>); - -impl BindingFactorList -where - C: Ciphersuite, -{ - /// Create a new [`BindingFactorList`] from a map of identifiers to binding factors. - pub fn new(binding_factors: BTreeMap, BindingFactor>) -> Self { - Self(binding_factors) - } - - /// Get the [`BindingFactor`] for the given identifier, or None if not found. - pub fn get(&self, key: &Identifier) -> Option<&BindingFactor> { - self.0.get(key) - } -} - -/// [`compute_binding_factors`] in the spec -/// -/// [`compute_binding_factors`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-4.4 -pub fn compute_binding_factor_list( - signing_package: &SigningPackage, - verifying_key: &VerifyingKey, - additional_prefix: &[u8], -) -> BindingFactorList -where - C: Ciphersuite, -{ - let preimages = signing_package.binding_factor_preimages(verifying_key, additional_prefix); - - BindingFactorList( - preimages - .iter() - .map(|(identifier, preimage)| { - let binding_factor = C::H1(preimage); - (*identifier, BindingFactor(binding_factor)) - }) - .collect(), - ) -} - -#[cfg(any(test, feature = "std"))] -impl FromHex for BindingFactor -where - C: Ciphersuite, -{ - type Error = &'static str; - - fn from_hex>(hex: T) -> Result { - let v: Vec = FromHex::from_hex(hex).map_err(|_| "invalid hex")?; - - match v.try_into() { - Ok(bytes) => <::Field>::deserialize(&bytes) - .map(|scalar| Self(scalar)) - .map_err(|_| "malformed scalar encoding"), - Err(_) => Err("malformed scalar encoding"), - } - } -} - -/// Generates a lagrange coefficient. -/// -/// The Lagrange polynomial for a set of points (x_j, y_j) for 0 <= j <= k -/// is ∑_{i=0}^k y_i.ℓ_i(x), where ℓ_i(x) is the Lagrange basis polynomial: -/// -/// ℓ_i(x) = ∏_{0≤j≤k; j≠i} (x - x_j) / (x_i - x_j). -/// -/// This computes ℓ_j(x) for the set of points `xs` and for the j corresponding -/// to the given xj. -/// -/// If `x` is None, it uses 0 for it (since Identifiers can't be 0) -#[cfg_attr(feature = "internals", visibility::make(pub))] -#[cfg_attr(docsrs, doc(cfg(feature = "internals")))] -fn compute_lagrange_coefficient( - x_set: &BTreeSet>, - x: Option>, - x_i: Identifier, -) -> Result, Error> { - if x_set.is_empty() { - return Err(Error::IncorrectNumberOfIdentifiers); - } - let mut num = <::Field>::one(); - let mut den = <::Field>::one(); - - let mut x_i_found = false; - - for x_j in x_set.iter() { - if x_i == *x_j { - x_i_found = true; - continue; - } - - if let Some(x) = x { - num *= x - *x_j; - den *= x_i - *x_j; - } else { - // Both signs inverted just to avoid requiring Neg (-*xj) - num *= *x_j; - den *= *x_j - x_i; - } - } - if !x_i_found { - return Err(Error::UnknownIdentifier); - } - - Ok(num * <::Field>::invert(&den).map_err(|_| Error::DuplicatedIdentifier)?) -} - -/// Generates the lagrange coefficient for the i'th participant (for `signer_id`). -/// -/// Implements [`derive_interpolating_value()`] from the spec. -/// -/// [`derive_interpolating_value()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-polynomials -pub fn derive_interpolating_value( - signer_id: &Identifier, - signing_package: &SigningPackage, -) -> Result, Error> { - compute_lagrange_coefficient( - &signing_package.signing_commitments.keys().cloned().collect(), - None, - *signer_id, - ) -} - -// Generated by the coordinator of the signing operation and distributed to -/// each signing party -#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(deny_unknown_fields)] -pub struct SigningPackage { - /// Serialization header - pub header: Header, - /// The set of commitments participants published in the first round of the - /// protocol. - pub signing_commitments: BTreeMap, round1::SigningCommitments>, - /// Message which each participant will sign. - /// - /// Each signer should perform protocol-specific verification on the - /// message. - #[cfg_attr( - feature = "serde", - serde( - serialize_with = "serdect::slice::serialize_hex_lower_or_bin", - deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" - ) - )] - pub message: Vec, -} - -impl SigningPackage -where - C: Ciphersuite, -{ - /// Create a new `SigningPackage` - /// - /// The `signing_commitments` are sorted by participant `identifier`. - pub fn new( - signing_commitments: BTreeMap, round1::SigningCommitments>, - message: &[u8], - ) -> SigningPackage { - SigningPackage { header: Header::default(), signing_commitments, message: message.to_vec() } - } - - /// Get a signing commitment by its participant identifier, or None if not found. - pub fn signing_commitment( - &self, - identifier: &Identifier, - ) -> Option> { - self.signing_commitments.get(identifier).copied() - } - - /// Compute the preimages to H1 to compute the per-signer binding factors - // We separate this out into its own method so it can be tested - #[cfg_attr(feature = "internals", visibility::make(pub))] - #[cfg_attr(docsrs, doc(cfg(feature = "internals")))] - pub fn binding_factor_preimages( - &self, - verifying_key: &VerifyingKey, - additional_prefix: &[u8], - ) -> Vec<(Identifier, Vec)> { - let mut binding_factor_input_prefix = vec![]; - - // The length of a serialized verifying key of the same cipersuite does - // not change between runs of the protocol, so we don't need to hash to - // get a fixed length. - binding_factor_input_prefix.extend_from_slice(verifying_key.serialize().as_ref()); - - // The message is hashed with H4 to force the variable-length message - // into a fixed-length byte string, same for hashing the variable-sized - // (between runs of the protocol) set of group commitments, but with H5. - binding_factor_input_prefix.extend_from_slice(C::H4(self.message.as_slice()).as_ref()); - binding_factor_input_prefix.extend_from_slice( - C::H5(&round1::encode_group_commitments(&self.signing_commitments)[..]).as_ref(), - ); - binding_factor_input_prefix.extend_from_slice(additional_prefix); - - self.signing_commitments - .keys() - .map(|identifier| { - let mut binding_factor_input = vec![]; - - binding_factor_input.extend_from_slice(&binding_factor_input_prefix); - binding_factor_input.extend_from_slice(identifier.serialize().as_ref()); - (*identifier, binding_factor_input) - }) - .collect() - } - - /// Check if the signing package is valid. - pub fn is_valid(&self) -> bool { - self.signing_commitments.iter().all(|(i, c)| i.is_valid() && c.is_valid()) - } -} - -impl SigningPackage -where - C: Ciphersuite, -{ - /// Serialize the struct into a Vec. - pub fn serialize(&self) -> Result, Error> { - serialization::Serialize::serialize(&self) - } - - /// Deserialize the struct from a slice of bytes. - pub fn deserialize(bytes: &[u8]) -> Result { - serialization::Deserialize::deserialize(bytes) - } -} - -/// The product of all signers' individual commitments, published as part of the -/// final signature. -#[derive(Clone, PartialEq, Eq)] -pub struct GroupCommitment(pub Element); - -impl GroupCommitment -where - C: Ciphersuite, -{ - /// Return the underlying element. - pub fn to_element(self) -> ::Element { - self.0 - } -} - -/// Generates the group commitment which is published as part of the joint -/// Schnorr signature. -/// -/// Implements [`compute_group_commitment`] from the spec. -/// -/// [`compute_group_commitment`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-4.5 -pub fn compute_group_commitment( - signing_package: &SigningPackage, - binding_factor_list: &BindingFactorList, -) -> Result, Error> -where - C: Ciphersuite, -{ - let identity = ::identity(); - - let mut group_commitment = ::identity(); - - // Number of signing participants we are iterating over. - let n = signing_package.signing_commitments.len(); - - let mut binding_scalars = Vec::with_capacity(n); - - let mut binding_elements = Vec::with_capacity(n); - - for (commitment_identifier, commitment) in &signing_package.signing_commitments { - // The following check prevents a party from accidentally revealing their share. - // Note that the '&&' operator would be sufficient. - if identity == commitment.binding.0 || identity == commitment.hiding.0 { - return Err(Error::IdentityCommitment); - } - - let binding_factor = - binding_factor_list.get(commitment_identifier).ok_or(Error::UnknownIdentifier)?; - - // Collect the binding commitments and their binding factors for one big - // multiscalar multiplication at the end. - binding_elements.push(commitment.binding.0); - binding_scalars.push(binding_factor.0); - - group_commitment = group_commitment + commitment.hiding.0; - } - - let accumulated_binding_commitment: Element = - VartimeMultiscalarMul::::vartime_multiscalar_mul(binding_scalars, binding_elements); - - group_commitment = group_commitment + accumulated_binding_commitment; - - Ok(GroupCommitment(group_commitment)) -} - -/// Generates the challenge as is required for Schnorr signatures. -/// -/// Deals in bytes, so that [FROST] and singleton signing and verification can use it with different -/// types. -/// -/// This is the only invocation of the H2 hash function from the [RFC]. -/// -/// [FROST]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-challenge-computa -/// [RFC]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.2 -pub fn challenge(R: &Element, verifying_key: &VerifyingKey, msg: &[u8]) -> Challenge -where - C: Ciphersuite, -{ - let mut preimage = vec![]; - - preimage.extend_from_slice(::challenge_bytes(R).as_ref()); - preimage.extend_from_slice(::challenge_bytes(&verifying_key.element).as_ref()); - preimage.extend_from_slice(msg); - - Challenge(C::H2(&preimage[..])) -} - -/// Generates the challenge for the proof of knowledge to a secret for the DKG. -pub fn pok_challenge( - identifier: Identifier, - verifying_key: &VerifyingKey, - R: &Element, -) -> Option> -where - C: Ciphersuite, -{ - let mut preimage = vec![]; - - preimage.extend_from_slice(identifier.serialize().as_ref()); - preimage.extend_from_slice(::serialize(&verifying_key.element).as_ref()); - preimage.extend_from_slice(::serialize(R).as_ref()); - - Some(Challenge(C::HDKG(&preimage[..])?)) -} diff --git a/frost/src/round1.rs b/frost/src/round1.rs deleted file mode 100644 index 54039c005..000000000 --- a/frost/src/round1.rs +++ /dev/null @@ -1,158 +0,0 @@ -use core::fmt::Debug; - -use crate::{ - error::Error, - identifier::Identifier, - serialization::{Deserialize, ElementSerialization, Serialize}, - traits::{Ciphersuite, Element, Group}, - util::element_is_valid, - BindingFactor, Header, -}; -use alloc::collections::BTreeMap; -use sp_std::{vec, vec::Vec}; - -/// A group element that is a commitment to a signing nonce share. -#[derive(Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ElementSerialization")] -#[serde(into = "ElementSerialization")] -pub struct NonceCommitment(pub(super) Element); - -impl NonceCommitment -where - C: Ciphersuite, -{ - /// Deserialize [`NonceCommitment`] from bytes - pub fn deserialize(bytes: ::Serialization) -> Result { - ::deserialize(&bytes) - .map(|element| Self(element)) - .map_err(|e| e.into()) - } - - /// Serialize [`NonceCommitment`] to bytes - pub fn serialize(&self) -> ::Serialization { - ::serialize(&self.0) - } - - /// Checks if the commitment is valid. - pub fn is_valid(&self) -> bool { - element_is_valid::(&self.0) - } -} - -impl TryFrom> for NonceCommitment -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ElementSerialization) -> Result { - Self::deserialize(value.0) - } -} - -impl From> for ElementSerialization -where - C: Ciphersuite, -{ - fn from(value: NonceCommitment) -> Self { - Self(value.serialize()) - } -} - -impl Debug for NonceCommitment -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_tuple("NonceCommitment").field(&hex::encode(self.serialize())).finish() - } -} - -/// Published by each participant in the first round of the signing protocol. -/// -/// This step can be batched if desired by the implementation. Each -/// SigningCommitment can be used for exactly *one* signature. -#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(deny_unknown_fields)] -pub struct SigningCommitments { - /// Serialization header - pub(crate) header: Header, - /// Commitment to the hiding [`Nonce`]. - pub(crate) hiding: NonceCommitment, - /// Commitment to the binding [`Nonce`]. - pub(crate) binding: NonceCommitment, -} - -impl SigningCommitments -where - C: Ciphersuite, -{ - /// Create new SigningCommitments - pub fn new(hiding: NonceCommitment, binding: NonceCommitment) -> Self { - Self { header: Header::default(), hiding, binding } - } - - /// Computes the [signature commitment share] from these round one signing commitments. - /// - /// [signature commitment share]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-share-verificatio - pub fn to_group_commitment_share( - self, - binding_factor: &BindingFactor, - ) -> GroupCommitmentShare { - GroupCommitmentShare::(self.hiding.0 + (self.binding.0 * binding_factor.0)) - } - - /// Checks if the commitments are valid. - pub fn is_valid(&self) -> bool { - element_is_valid::(&self.hiding.0) - && element_is_valid::(&self.binding.0) - && self.hiding.0 != self.binding.0 - } -} - -impl SigningCommitments -where - C: Ciphersuite, -{ - /// Serialize the struct into a Vec. - pub fn serialize(&self) -> Result, Error> { - Serialize::serialize(&self) - } - - /// Deserialize the struct from a slice of bytes. - pub fn deserialize(bytes: &[u8]) -> Result { - Deserialize::deserialize(bytes) - } -} - -/// One signer's share of the group commitment, derived from their individual signing commitments -/// and the binding factor _rho_. -#[derive(Clone, Copy, PartialEq)] -pub struct GroupCommitmentShare(pub(super) Element); - -/// Encode the list of group signing commitments. -/// -/// Implements [`encode_group_commitment_list()`] from the spec. -/// -/// `signing_commitments` must contain the sorted map of participants -/// identifiers to the signing commitments they issued. -/// -/// Returns a byte string containing the serialized representation of the -/// commitment list. -/// -/// [`encode_group_commitment_list()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-list-operations -pub(super) fn encode_group_commitments( - signing_commitments: &BTreeMap, SigningCommitments>, -) -> Vec { - let mut bytes = vec![]; - - for (item_identifier, item) in signing_commitments { - bytes.extend_from_slice(item_identifier.serialize().as_ref()); - bytes.extend_from_slice(::serialize(&item.hiding.0).as_ref()); - bytes.extend_from_slice(::serialize(&item.binding.0).as_ref()); - } - - bytes -} diff --git a/frost/src/scalar_mul.rs b/frost/src/scalar_mul.rs deleted file mode 100644 index 8211ac83e..000000000 --- a/frost/src/scalar_mul.rs +++ /dev/null @@ -1,266 +0,0 @@ -//! Non-adjacent form (NAF) implementations for fast batch scalar multiplcation - -// We expect slicings in this module to never panic due to algorithmic -// constraints. -#![allow(clippy::indexing_slicing)] - -use core::{ - borrow::Borrow, - fmt::{Debug, Result}, - marker::PhantomData, -}; -use sp_std::{vec, vec::Vec}; - -use crate::{Ciphersuite, Element, Field, Group, Scalar}; - -/// Calculates the quotient of `self` and `rhs`, rounding the result towards positive infinity. -/// -/// # Panics -/// -/// This function will panic if `rhs` is 0 or the division results in overflow. -/// -/// This function is similar to `div_ceil` that is [available on -/// Nightly](https://github.com/rust-lang/rust/issues/88581). -// TODO: remove this function and use `div_ceil()` instead when `int_roundings` -// is stabilized. -const fn div_ceil(lhs: usize, rhs: usize) -> usize { - let d = lhs / rhs; - let r = lhs % rhs; - if r > 0 && rhs > 0 { - d + 1 - } else { - d - } -} - -/// A trait for transforming a scalar generic over a ciphersuite to a non-adjacent form (NAF). -pub trait NonAdjacentForm { - fn non_adjacent_form(&self, w: usize) -> Vec; -} - -impl NonAdjacentForm for Scalar -where - C: Ciphersuite, -{ - /// Computes a width-(w) "Non-Adjacent Form" of this scalar. - /// - /// Thanks to curve25519-dalek for the original implementation that informed this one. - /// - /// # Safety - /// - /// The full scalar field MUST fit in 256 bits in this implementation. - fn non_adjacent_form(&self, w: usize) -> Vec { - // required by the NAF definition - debug_assert!(w >= 2); - // required so that the NAF digits fit in i8 - debug_assert!(w <= 8); - - use byteorder::{ByteOrder, LittleEndian}; - - let serialized_scalar = <::Field>::little_endian_serialize(self); - // The canonical serialization length of this `Scalar` in bytes. - let serialization_len = serialized_scalar.as_ref().len(); - - // Compute the size of the non-adjacent form from the number of bytes needed to serialize - // `Scalar`s, plus 1 bit. - // - // The length of the NAF is at most one more than the bit length. - let naf_length: usize = serialization_len * u8::BITS as usize + 1; - - // Safety: - // - // The max value of `naf_length` (the number of bits to represent the - // scalar plus 1) _should_ have plenty of room in systems where usize is - // greater than 8 bits (aka, not a u8). If you are able to compile this - // code on a system with 8-bit pointers, well done, but this code will - // probably not compute the right thing for you, use a 16-bit or above - // system. Since the rest of this code uses u64's for limbs, we - // recommend a 64-bit system. - let mut naf = vec![0; naf_length]; - - // Get the number of 64-bit limbs we need. - let num_limbs: usize = div_ceil(naf_length, u64::BITS as usize); - - let mut x_u64 = vec![0u64; num_limbs]; - - // This length needs to be 8*destination.len(), so pad out to length num_limbs * 8. - let mut padded_le_serialized = vec![0u8; num_limbs * 8]; - - padded_le_serialized[..serialization_len].copy_from_slice(serialized_scalar.as_ref()); - - LittleEndian::read_u64_into(padded_le_serialized.as_ref(), &mut x_u64[0..num_limbs]); - - let width = 1 << w; - let window_mask = width - 1; - - let mut pos = 0; - let mut carry = 0; - while pos < naf_length { - // Construct a buffer of bits of the scalar, starting at bit `pos` - let u64_idx = pos / 64; - let bit_idx = pos % 64; - - let bit_buf: u64 = if bit_idx < 64 - w { - // This window's bits are contained in a single u64 - x_u64[u64_idx] >> bit_idx - } else { - // Combine the current u64's bits with the bits from the next u64 - (x_u64[u64_idx] >> bit_idx) | (x_u64[1 + u64_idx] << (64 - bit_idx)) - }; - - // Add the carry into the current window - let window = carry + (bit_buf & window_mask); - - if window & 1 == 0 { - // If the window value is even, preserve the carry and continue. - // Why is the carry preserved? - // If carry == 0 and window & 1 == 0, then the next carry should be 0 - // If carry == 1 and window & 1 == 0, then bit_buf & 1 == 1 so the next carry should - // be 1 - pos += 1; - continue; - } - - if window < width / 2 { - carry = 0; - naf[pos] = window as i8; - } else { - carry = 1; - naf[pos] = (window as i8).wrapping_sub(width as i8); - } - - pos += w; - } - - naf - } -} - -/// A trait for variable-time multiscalar multiplication without precomputation. -/// -/// Implement for a group element. -pub trait VartimeMultiscalarMul: Clone { - /// Given an iterator of public scalars and an iterator of - /// `Option`s of group elements, compute either `Some(Q)`, where - /// $$ - /// Q = c\_1 E\_1 + \cdots + c\_n E\_n, - /// $$ - /// if all points were `Some(E_i)`, or else return `None`. - fn optional_multiscalar_mul(scalars: I, elements: J) -> Option - where - I: IntoIterator, - I::Item: Borrow>, - J: IntoIterator>; - - /// Given an iterator of public scalars and an iterator of - /// public group elements, compute - /// $$ - /// Q = c\_1 E\_1 + \cdots + c\_n E\_n, - /// $$ - /// using variable-time operations. - /// - /// It is an error to call this function with two iterators of different lengths. - fn vartime_multiscalar_mul(scalars: I, elements: J) -> Self - where - I: IntoIterator, - I::Item: Borrow>, - J: IntoIterator, - J::Item: Borrow, - { - Self::optional_multiscalar_mul( - scalars, - elements.into_iter().map(|e| Some(e.borrow().clone())), - ) - .expect("all elements should be Some") - } -} - -impl VartimeMultiscalarMul for Element -where - C: Ciphersuite, -{ - #[allow(clippy::comparison_chain)] - fn optional_multiscalar_mul(scalars: I, elements: J) -> Option> - where - I: IntoIterator, - I::Item: Borrow>, - J: IntoIterator>>, - { - let nafs: Vec<_> = scalars - .into_iter() - .map(|c| NonAdjacentForm::::non_adjacent_form(c.borrow(), 5)) - .collect(); - - let lookup_tables = elements - .into_iter() - .map(|P_opt| P_opt.map(|P| LookupTable5::>::from(&P))) - .collect::>>()?; - - if nafs.len() != lookup_tables.len() { - return None; - } - - let mut r = ::identity(); - - // All NAFs will have the same size, so get it from the first - if nafs.is_empty() { - return Some(r); - } - let naf_length = nafs[0].len(); - - for i in (0..naf_length).rev() { - let mut t = r + r; - - for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { - if naf[i] > 0 { - t = t + lookup_table.select(naf[i] as usize); - } else if naf[i] < 0 { - t = t - lookup_table.select(-naf[i] as usize); - } - } - - r = t; - } - - Some(r) - } -} - -/// Holds odd multiples 1A, 3A, ..., 15A of a point A. -#[derive(Copy, Clone)] -pub(crate) struct LookupTable5 { - pub(crate) bytes: [T; 8], - pub(crate) _marker: PhantomData, -} - -impl LookupTable5 { - /// Given public, odd \\( x \\) with \\( 0 < x < 2^4 \\), return \\(xA\\). - pub fn select(&self, x: usize) -> T { - debug_assert_eq!(x & 1, 1); - debug_assert!(x < 16); - - self.bytes[x / 2] - } -} - -impl Debug for LookupTable5 { - fn fmt(&self, f: &mut core::fmt::Formatter) -> Result { - write!(f, "LookupTable5({:?})", self.bytes) - } -} - -impl<'a, C> From<&'a Element> for LookupTable5> -where - C: Ciphersuite, -{ - fn from(A: &'a Element) -> Self { - let mut Ai = [*A; 8]; - let A2 = *A + *A; - for i in 0..7 { - Ai[i + 1] = A2 + Ai[i]; - } - - // Now Ai = [A, 3A, 5A, 7A, 9A, 11A, 13A, 15A] - LookupTable5 { bytes: Ai, _marker: PhantomData } - } -} diff --git a/frost/src/serialization.rs b/frost/src/serialization.rs deleted file mode 100644 index 0da02b7a9..000000000 --- a/frost/src/serialization.rs +++ /dev/null @@ -1,177 +0,0 @@ -//! Serialization support. - -use crate::error::Error; - -use super::traits::{Ciphersuite, Field, Group}; -use alloc::string::String; -use sp_std::{vec, vec::Vec}; - -/// Helper struct to serialize a Scalar. -pub struct ScalarSerialization( - pub <<::Group as Group>::Field as Field>::Serialization, -); - -impl serde::Serialize for ScalarSerialization -where - C: Ciphersuite, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serdect::array::serialize_hex_lower_or_bin(&self.0.as_ref(), serializer) - } -} - -impl<'de, C> serde::Deserialize<'de> for ScalarSerialization -where - C: Ciphersuite, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - // Get size from the size of the zero scalar - let zero = <::Field as Field>::zero(); - let len = <::Field as Field>::serialize(&zero).as_ref().len(); - - let mut bytes = vec![0u8; len]; - serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?; - let array = - bytes.try_into().map_err(|_| serde::de::Error::custom("invalid byte length"))?; - Ok(Self(array)) - } -} - -pub struct ElementSerialization( - pub <::Group as Group>::Serialization, -); - -impl serde::Serialize for ElementSerialization -where - C: Ciphersuite, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serdect::array::serialize_hex_lower_or_bin(&self.0.as_ref(), serializer) - } -} - -impl<'de, C> serde::Deserialize<'de> for ElementSerialization -where - C: Ciphersuite, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - // Get size from the size of the generator - let generator = ::generator(); - let len = ::serialize(&generator).as_ref().len(); - - let mut bytes = vec![0u8; len]; - serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?; - let array = - bytes.try_into().map_err(|_| serde::de::Error::custom("invalid byte length"))?; - Ok(Self(array)) - } -} - -// The short 4-byte ID. Derived as the CRC-32 of the UTF-8 -// encoded ID in big endian format. -#[allow(unused)] -const fn short_id() -> [u8; 4] -where - C: Ciphersuite, -{ - super::const_crc32::crc32(C::ID.as_bytes()).to_be_bytes() -} - -/// Serialize a placeholder ciphersuite field with the ciphersuite ID string. -#[allow(unused)] -pub fn ciphersuite_serialize(_: &(), s: S) -> Result -where - S: serde::Serializer, - C: Ciphersuite, -{ - use serde::Serialize; - - if s.is_human_readable() { - s.serialize_str(C::ID) - } else { - serde::Serialize::serialize(&short_id::(), s) - } -} - -/// Deserialize a placeholder ciphersuite field, checking if it's the ciphersuite ID string. -#[allow(unused)] -pub fn ciphersuite_deserialize<'de, D, C>(deserializer: D) -> Result<(), D::Error> -where - D: serde::Deserializer<'de>, - C: Ciphersuite, -{ - if deserializer.is_human_readable() { - let s: String = serde::de::Deserialize::deserialize(deserializer)?; - if s != C::ID { - Err(serde::de::Error::custom("wrong ciphersuite")) - } else { - Ok(()) - } - } else { - let buffer: [u8; 4] = serde::de::Deserialize::deserialize(deserializer)?; - if buffer != short_id::() { - Err(serde::de::Error::custom("wrong ciphersuite")) - } else { - Ok(()) - } - } -} - -/// Deserialize a version. For now, since there is a single version 0, -/// simply validate if it's 0. -#[allow(unused)] -pub fn version_deserialize<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - let version: u8 = serde::de::Deserialize::deserialize(deserializer)?; - if version != 0 { - Err(serde::de::Error::custom("wrong format version, only 0 supported")) - } else { - Ok(version) - } -} - -// Default byte-oriented serialization for structs that need to be communicated. -// -// Note that we still manually implement these methods in each applicable type, -// instead of making these traits `pub` and asking users to import the traits. -// The reason is that ciphersuite traits would need to re-export these traits, -// parametrized with the ciphersuite, but trait aliases are not currently -// supported: - -pub trait Serialize { - /// Serialize the struct into a Vec. - fn serialize(&self) -> Result, Error>; -} - -pub trait Deserialize { - /// Deserialize the struct from a slice of bytes. - fn deserialize(bytes: &[u8]) -> Result - where - Self: core::marker::Sized; -} - -impl Serialize for T { - fn serialize(&self) -> Result, Error> { - postcard::to_allocvec(self).map_err(|_| Error::SerializationError) - } -} - -impl serde::Deserialize<'de>> Deserialize for T { - fn deserialize(bytes: &[u8]) -> Result { - postcard::from_bytes(bytes).map_err(|_| Error::DeserializationError) - } -} diff --git a/frost/src/signature.rs b/frost/src/signature.rs deleted file mode 100644 index 5a1f89bef..000000000 --- a/frost/src/signature.rs +++ /dev/null @@ -1,264 +0,0 @@ -//! Schnorr signatures over prime order groups (or subgroups) - -use core::fmt::Debug; - -use crate::{ - challenge::Challenge, identifier::Identifier, keys::VerifyingShare, - round1::GroupCommitmentShare, serialization::ScalarSerialization, Header, -}; - -use super::{ - error::Error, - traits::{Ciphersuite, Element, Field, Group, Scalar}, - util::{element_is_valid, scalar_is_valid}, -}; -use alloc::format; -use debugless_unwrap::DebuglessUnwrap; -use sp_std::{vec, vec::Vec}; - -/// A Schnorr signature over some prime order group (or subgroup). -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct Signature { - /// The commitment `R` to the signature nonce. - pub R: Element, - /// The response `z` to the challenge computed from the commitment `R`, the verifying key, and - /// the message. - pub z: Scalar, -} - -impl Signature -where - C: Ciphersuite, - C::Group: Group, - ::Field: Field, -{ - /// Create a new Signature. - pub fn new( - R: ::Element, - z: <::Field as Field>::Scalar, - ) -> Self { - Self { R, z } - } - - /// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature`. - pub fn deserialize(bytes: C::SignatureSerialization) -> Result { - // To compute the expected length of the encoded point, encode the generator - // and get its length. Note that we can't use the identity because it can be encoded - // shorter in some cases (e.g. P-256, which uses SEC1 encoding). - let generator = ::generator(); - let mut R_bytes = Vec::from(::serialize(&generator).as_ref()); - - let R_bytes_len = R_bytes.len(); - - R_bytes[..] - .copy_from_slice(bytes.as_ref().get(0..R_bytes_len).ok_or(Error::MalformedSignature)?); - - let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?; - - let one = <::Field as Field>::zero(); - let mut z_bytes = - Vec::from(<::Field as Field>::serialize(&one).as_ref()); - - let z_bytes_len = z_bytes.len(); - - // We extract the exact length of bytes we expect, not just the remaining bytes with - // `bytes[R_bytes_len..]` - z_bytes[..].copy_from_slice( - bytes - .as_ref() - .get(R_bytes_len..R_bytes_len + z_bytes_len) - .ok_or(Error::MalformedSignature)?, - ); - - let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?; - - Ok(Self { - R: ::deserialize(R_serialization).map_err(Error::Group)?, - z: <::Field>::deserialize(z_serialization).map_err(Error::Field)?, - }) - } - - /// Converts this signature to its [`Ciphersuite::SignatureSerialization`] in bytes. - pub fn serialize(&self) -> C::SignatureSerialization { - let mut bytes = vec![]; - - bytes.extend(::serialize(&self.R).as_ref()); - bytes.extend(<::Field>::serialize(&self.z).as_ref()); - - bytes.try_into().debugless_unwrap() - } - - /// Check if the signature as valid values. - pub fn is_valid(&self) -> bool { - element_is_valid::(&self.R) && scalar_is_valid::(&self.z) - } -} - -impl serde::Serialize for Signature -where - C: Ciphersuite, - C::Group: Group, - ::Field: Field, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serdect::slice::serialize_hex_lower_or_bin(&self.serialize().as_ref(), serializer) - } -} - -impl<'de, C> serde::Deserialize<'de> for Signature -where - C: Ciphersuite, - C::Group: Group, - ::Field: Field, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?; - let array = - bytes.try_into().map_err(|_| serde::de::Error::custom("invalid byte length"))?; - let identifier = Signature::deserialize(array) - .map_err(|err| serde::de::Error::custom(format!("{err}")))?; - Ok(identifier) - } -} - -impl sp_std::fmt::Debug for Signature { - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - f.debug_struct("Signature") - .field("R", &hex::encode(::serialize(&self.R).as_ref())) - .field("z", &hex::encode(<::Field>::serialize(&self.z).as_ref())) - .finish() - } -} - -// Used to help encoding a SignatureShare. Since it has a Scalar it can't -// be directly encoded with serde, so we use this struct to wrap the scalar. -#[derive(Clone, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ScalarSerialization")] -#[serde(into = "ScalarSerialization")] -struct SignatureShareHelper(Scalar); - -impl TryFrom> for SignatureShareHelper -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ScalarSerialization) -> Result { - <::Field>::deserialize(&value.0) - .map(|scalar| Self(scalar)) - .map_err(|e| e.into()) - } -} - -impl From> for ScalarSerialization -where - C: Ciphersuite, -{ - fn from(value: SignatureShareHelper) -> Self { - Self(<::Field>::serialize(&value.0)) - } -} - -/// A participant's signature share, which the coordinator will aggregate with all other signer's -/// shares into the joint signature. -#[derive(Clone, Copy, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(deny_unknown_fields)] -#[serde(try_from = "SignatureShareSerialization")] -#[serde(into = "SignatureShareSerialization")] -pub struct SignatureShare { - /// This participant's signature over the message. - pub share: Scalar, -} - -impl SignatureShare -where - C: Ciphersuite, -{ - /// Deserialize [`SignatureShare`] from bytes - pub fn deserialize( - bytes: <::Field as Field>::Serialization, - ) -> Result { - <::Field>::deserialize(&bytes) - .map(|scalar| Self { share: scalar }) - .map_err(|e| e.into()) - } - - /// Serialize [`SignatureShare`] to bytes - pub fn serialize(&self) -> <::Field as Field>::Serialization { - <::Field>::serialize(&self.share) - } - - /// Tests if a signature share issued by a participant is valid before - /// aggregating it into a final joint signature to publish. - /// - /// This is the final step of [`verify_signature_share`] from the spec. - /// - /// [`verify_signature_share`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-signature-share-verificatio - pub fn verify( - &self, - _identifier: Identifier, - group_commitment_share: &GroupCommitmentShare, - verifying_share: &VerifyingShare, - lambda_i: Scalar, - challenge: &Challenge, - ) -> Result<(), Error> { - if (::generator() * self.share) - != (group_commitment_share.0 + (verifying_share.0 * challenge.0 * lambda_i)) - { - return Err(Error::InvalidSignatureShare); - } - - Ok(()) - } - - /// Tests if the signature share is valid - pub fn is_valid(&self) -> bool { - scalar_is_valid::(&self.share) - } -} - -#[derive(serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(deny_unknown_fields)] -struct SignatureShareSerialization { - /// Serialization header - pub(crate) header: Header, - share: SignatureShareHelper, -} - -impl From> for SignatureShare -where - C: Ciphersuite, -{ - fn from(value: SignatureShareSerialization) -> Self { - Self { share: value.share.0 } - } -} - -impl From> for SignatureShareSerialization -where - C: Ciphersuite, -{ - fn from(value: SignatureShare) -> Self { - Self { header: Header::default(), share: SignatureShareHelper(value.share) } - } -} - -impl Debug for SignatureShare -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("SignatureShare") - .field("share", &hex::encode(self.serialize())) - .finish() - } -} diff --git a/frost/src/signing_key.rs b/frost/src/signing_key.rs deleted file mode 100644 index 758600f51..000000000 --- a/frost/src/signing_key.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Schnorr signature signing keys - -use crate::{ - error::Error, - traits::{Ciphersuite, Field, Group, Scalar}, - util::scalar_is_valid, - verifying_key::VerifyingKey, -}; - -#[cfg(feature = "std")] -use crate::{challenge, signature::Signature}; - -#[cfg(feature = "std")] -use crate::random_nonzero; -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; - -/// A signing key for a Schnorr signature on a FROST [`Ciphersuite::Group`]. -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct SigningKey -where - C: Ciphersuite, -{ - pub(crate) scalar: Scalar, -} - -impl SigningKey -where - C: Ciphersuite, -{ - /// Generate a new signing key. - #[cfg(feature = "std")] - pub fn new(rng: &mut R) -> SigningKey { - let scalar = random_nonzero::(rng); - - SigningKey { scalar } - } - - /// Deserialize from bytes - pub fn deserialize( - bytes: <::Field as Field>::Serialization, - ) -> Result, Error> { - let scalar = - <::Field as Field>::deserialize(&bytes).map_err(Error::from)?; - - if scalar == <::Field as Field>::zero() { - return Err(Error::MalformedSigningKey); - } - - Ok(Self { scalar }) - } - - /// Serialize `SigningKey` to bytes - pub fn serialize(&self) -> <::Field as Field>::Serialization { - <::Field as Field>::serialize(&self.scalar) - } - - /// Create a signature `msg` using this `SigningKey`. - #[cfg(feature = "std")] - pub fn sign(&self, mut rng: R, msg: &[u8]) -> Signature { - let k = random_nonzero::(&mut rng); - - let R = ::generator() * k; - - // Generate Schnorr challenge - let c = challenge::(&R, &VerifyingKey::::from(*self), msg); - - let z = k + (c.0 * self.scalar); - - Signature { R, z } - } - - /// Creates a SigningKey from a scalar. - pub fn from_scalar( - scalar: <<::Group as Group>::Field as Field>::Scalar, - ) -> Self { - Self { scalar } - } - - /// Return the underlying scalar. - pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { - self.scalar - } - - /// Check if the signing key is valid. - pub fn is_valid(&self) -> bool { - scalar_is_valid::(&self.scalar) - } -} - -impl core::fmt::Debug for SigningKey -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("SigningKey").field(&"").finish() - } -} - -impl From<&SigningKey> for VerifyingKey -where - C: Ciphersuite, -{ - fn from(signing_key: &SigningKey) -> Self { - VerifyingKey { element: C::Group::generator() * signing_key.scalar } - } -} - -impl From> for VerifyingKey -where - C: Ciphersuite, -{ - fn from(signing_key: SigningKey) -> Self { - VerifyingKey::::from(&signing_key) - } -} diff --git a/frost/src/traits.rs b/frost/src/traits.rs deleted file mode 100644 index 04b3418a0..000000000 --- a/frost/src/traits.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! Traits used to abstract Ciphersuites. - -use core::{ - fmt::Debug, - ops::{Add, Mul, Sub}, -}; - -use parity_scale_codec::{Decode, Encode}; -use sp_std::vec::Vec; - -#[cfg(feature = "std")] -use rand_core::{CryptoRng, RngCore}; - -use subtle::ConditionallyNegatable; - -use super::{ - error::{Error, FieldError, GroupError}, - signature::Signature, - verifying_key::VerifyingKey, -}; - -/// A prime order finite field GF(q) over which all scalar values for our prime order group can be -/// multiplied are defined. -/// -/// This trait does not have to be implemented for a finite field scalar itself, it can be a -/// pass-through, implemented for a type just for the ciphersuite, and calls through to another -/// implementation underneath, so that this trait does not have to be implemented for types you -/// don't own. -pub trait Field: Copy + Clone { - /// An element of the scalar field GF(p). - /// The Eq/PartialEq implementation MUST be constant-time. - type Scalar: Add - + ConditionallyNegatable - + Copy - + Clone - + Eq - + Mul - + PartialEq - + Sub - + Encode - + Decode; - - /// A unique byte array buf of fixed length N. - type Serialization: AsRef<[u8]> + Debug + TryFrom>; - - /// Returns the zero element of the field, the additive identity. - fn zero() -> Self::Scalar; - - /// Returns the one element of the field, the multiplicative identity. - fn one() -> Self::Scalar; - - /// Computes the multiplicative inverse of an element of the scalar field, failing if the - /// element is zero. - fn invert(scalar: &Self::Scalar) -> Result; - - /// Generate a random scalar from the entire space [0, l-1] - /// - /// - #[cfg(feature = "std")] - fn random(rng: &mut R) -> Self::Scalar; - - /// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of - /// fixed length Ne. - /// - /// - fn serialize(scalar: &Self::Scalar) -> Self::Serialization; - - /// A member function of a [`Field`] that maps a [`Scalar`] to a unique byte array buf of - /// fixed length Ne, in little-endian order. - /// - /// This is used internally. - fn little_endian_serialize(scalar: &Self::Scalar) -> Self::Serialization; - - /// A member function of a [`Field`] that attempts to map a byte array `buf` to a [`Scalar`]. - /// - /// Fails if the input is not a valid byte representation of an [`Scalar`] of the - /// [`Field`]. This function can raise an [`Error`] if deserialization fails. - /// - /// - fn deserialize(buf: &Self::Serialization) -> Result; -} - -/// An element of the [`Ciphersuite`] `C`'s [`Group`]'s scalar [`Field`]. -pub type Scalar = <<::Group as Group>::Field as Field>::Scalar; - -/// A prime-order group (or subgroup) that provides everything we need to create and verify Schnorr -/// signatures. -/// -/// This trait does not have to be implemented for the curve/element/point itself, it can be a -/// pass-through, implemented for a type just for the ciphersuite, and calls through to another -/// implementation underneath, so that this trait does not have to be implemented for types you -/// don't own. -pub trait Group: Copy + Clone + PartialEq { - /// A prime order finite field GF(q) over which all scalar values for our prime order group can - /// be multiplied are defined. - type Field: Field; - - /// An element of our group that we will be computing over. - type Element: Add - + ConditionallyNegatable - + Copy - + Clone - + Eq - + Mul<::Scalar, Output = Self::Element> - + PartialEq - + Sub - + Encode - + Decode; - - /// A unique byte array buf of fixed length N. - /// - /// Little-endian! - type Serialization: AsRef<[u8]> + Debug + TryFrom>; - - /// The order of the the quotient group when the prime order subgroup divides the order of the - /// full curve group. - /// - /// If using a prime order elliptic curve, the cofactor should be 1 in the scalar field. - fn cofactor() -> ::Scalar; - - /// Additive [identity] of the prime order group. - /// - /// [identity]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.2 - fn identity() -> Self::Element; - - /// The fixed generator element of the prime order group. - /// - /// The 'base' of ['ScalarBaseMult()'] from the spec. - /// - /// [`ScalarBaseMult()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#section-3.1-3.5 - fn generator() -> Self::Element; - - /// A member function of a group _G_ that maps an [`Element`] to a unique byte array buf of - /// fixed length Ne. - /// - /// - fn serialize(element: &Self::Element) -> Self::Serialization; - - /// A member function of a [`Group`] that attempts to map a byte array `buf` to an [`Element`]. - /// - /// Fails if the input is not a valid byte representation of an [`Element`] of the - /// [`Group`]. This function can raise an [`Error`] if deserialization fails or if the - /// resulting [`Element`] is the identity element of the group - /// - /// - fn deserialize(buf: &Self::Serialization) -> Result; - - /// The challenge bytes for a FROST ciphersuite. These may or may not match - /// the output from serialization like in the case of Taproot - fn challenge_bytes(element: &Self::Element) -> Vec { - Self::serialize(element).as_ref().to_vec() - } - - /// Determine if the elements y is odd or not. For now only applies - /// for bitcoin taproot - fn y_is_odd(_element: &Self::Element) -> subtle::Choice { - subtle::Choice::from(0u8) - } -} - -/// An element of the [`Ciphersuite`] `C`'s [`Group`]. -pub type Element = <::Group as Group>::Element; - -/// A [FROST ciphersuite] specifies the underlying prime-order group details and cryptographic hash -/// function. -/// -/// [FROST ciphersuite]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-ciphersuites -pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + Default { - /// The ciphersuite ID string. It should be equal to the contextString in - /// the spec. For new ciphersuites, this should be a string that identifies - /// the ciphersuite; it's recommended to use a similar format to the - /// ciphersuites in the FROST spec, e.g. "FROST-RISTRETTO255-SHA512-v1". - const ID: &'static str; - - /// The prime order group (or subgroup) that this ciphersuite operates over. - type Group: Group; - - /// A unique byte array of fixed length. - type HashOutput: AsRef<[u8]>; - - /// A unique byte array of fixed length that is the `Group::ElementSerialization` + - /// `Group::ScalarSerialization` - type SignatureSerialization: AsRef<[u8]> + TryFrom>; - - /// [H1] for a FROST ciphersuite. - /// - /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field. - /// - /// [H1]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function - fn H1(m: &[u8]) -> <::Field as Field>::Scalar; - - /// [H2] for a FROST ciphersuite. - /// - /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field. - /// - /// [H2]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function - fn H2(m: &[u8]) -> <::Field as Field>::Scalar; - - /// [H3] for a FROST ciphersuite. - /// - /// Maps arbitrary inputs to `Self::Scalar` elements of the prime-order group scalar field. - /// - /// [H3]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function - fn H3(m: &[u8]) -> <::Field as Field>::Scalar; - - /// [H4] for a FROST ciphersuite. - /// - /// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied. - /// - /// [H4]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-cryptographic-hash-function - fn H4(m: &[u8]) -> Self::HashOutput; - - /// [H5] for a FROST ciphersuite. - /// - /// Usually an an alias for the ciphersuite hash function _H_ with domain separation applied. - /// - /// [H5]: https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#cryptographic-hash - fn H5(m: &[u8]) -> Self::HashOutput; - - /// Hash function for a FROST ciphersuite, used for the DKG. - /// - /// The DKG it not part of the specification, thus this is optional. - /// It can return None if DKG is not supported by the Ciphersuite. This is - /// the default implementation. - /// - /// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar - /// field. - fn HDKG(_m: &[u8]) -> Option<<::Field as Field>::Scalar> { - None - } - - /// Hash function for a FROST ciphersuite, used for deriving identifiers from strings. - /// - /// This feature is not part of the specification and is just a convenient - /// way of creating identifiers. Therefore it can return None if this is not supported by the - /// Ciphersuite. This is the default implementation. - /// - /// Maps arbitrary inputs to non-zero `Self::Scalar` elements of the prime-order group scalar - /// field. - fn HID(_m: &[u8]) -> Option<<::Field as Field>::Scalar> { - None - } - - /// Verify a signature for this ciphersuite. The default implementation uses the "cofactored" - /// equation (it multiplies by the cofactor returned by [`Group::cofactor()`]). - /// - /// # Cryptographic Safety - /// - /// You may override this to provide a tailored implementation, but if the ciphersuite defines - /// it, it must also multiply by the cofactor to comply with the RFC. Note that batch - /// verification (see [`crate::batch::Verifier`]) also uses the default implementation - /// regardless whether a tailored implementation was provided. - fn verify_signature( - msg: &[u8], - signature: &Signature, - public_key: &VerifyingKey, - ) -> Result<(), Error> { - let c = super::challenge::(&signature.R, public_key, msg); - - public_key.verify_prehashed(c, signature) - } -} diff --git a/frost/src/util.rs b/frost/src/util.rs deleted file mode 100644 index c2151f2c9..000000000 --- a/frost/src/util.rs +++ /dev/null @@ -1,9 +0,0 @@ -use super::traits::{Ciphersuite, Element, Field, Group, Scalar}; - -pub fn scalar_is_valid(scalar: &Scalar) -> bool { - *scalar != <::Field as Field>::zero() -} - -pub fn element_is_valid(element: &Element) -> bool { - *element != C::Group::generator() && *element != C::Group::identity() -} diff --git a/frost/src/verifying_key.rs b/frost/src/verifying_key.rs deleted file mode 100644 index f7e57077c..000000000 --- a/frost/src/verifying_key.rs +++ /dev/null @@ -1,131 +0,0 @@ -use core::fmt::Debug; - -use super::{ - challenge::Challenge, - error::Error, - serialization::ElementSerialization, - signature::Signature, - traits::{Ciphersuite, Element, Group}, - util::element_is_valid, -}; - -use hex::FromHex; -use parity_scale_codec::{Decode, Encode}; -use sp_std::vec::Vec; - -/// A valid verifying key for Schnorr signatures over a FROST [`Ciphersuite::Group`]. -#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, serde::Serialize, serde::Deserialize)] -#[serde(bound = "C: Ciphersuite")] -#[serde(try_from = "ElementSerialization")] -#[serde(into = "ElementSerialization")] -pub struct VerifyingKey -where - C: Ciphersuite, -{ - pub element: Element, -} - -impl VerifyingKey -where - C: Ciphersuite, -{ - /// Create a new VerifyingKey from the given element. - #[allow(unused)] - pub fn new(element: ::Element) -> Self { - Self { element } - } - - /// Return the underlying element. - pub fn to_element(self) -> ::Element { - self.element - } - - /// Deserialize from bytes - pub fn deserialize( - bytes: ::Serialization, - ) -> Result, Error> { - ::deserialize(&bytes) - .map(|element| VerifyingKey { element }) - .map_err(Error::Group) - } - - /// Serialize `VerifyingKey` to bytes - pub fn serialize(&self) -> ::Serialization { - ::serialize(&self.element) - } - - /// Verify a purported `signature` with a pre-hashed [`Challenge`] made by this verification - /// key. - pub fn verify_prehashed( - &self, - challenge: Challenge, - signature: &Signature, - ) -> Result<(), Error> { - // Verify check is h * ( - z * B + R + c * A) == 0 - // h * ( z * B - c * A - R) == 0 - // - // where h is the cofactor - let zB = C::Group::generator() * signature.z; - let cA = self.element * challenge.0; - let check = (zB - cA - signature.R) * C::Group::cofactor(); - if check == C::Group::identity() { - Ok(()) - } else { - Err(Error::MalformedSignature) - } - } - - /// Verify a purported `signature` over `msg` made by this verification key. - pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> { - C::verify_signature(msg, signature, self) - } - - /// Check if the verifying key is valid. - pub fn is_valid(&self) -> bool { - element_is_valid::(&self.element) - } -} - -impl Debug for VerifyingKey -where - C: Ciphersuite, -{ - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - f.debug_tuple("VerifyingKey").field(&hex::encode(self.serialize())).finish() - } -} - -impl FromHex for VerifyingKey -where - C: Ciphersuite, -{ - type Error = &'static str; - - fn from_hex>(hex: T) -> Result { - let v: Vec = FromHex::from_hex(hex).map_err(|_| "invalid hex")?; - match v.try_into() { - Ok(bytes) => Self::deserialize(bytes).map_err(|_| "malformed verifying key encoding"), - Err(_) => Err("malformed verifying key encoding"), - } - } -} - -impl TryFrom> for VerifyingKey -where - C: Ciphersuite, -{ - type Error = Error; - - fn try_from(value: ElementSerialization) -> Result { - Self::deserialize(value.0) - } -} - -impl From> for ElementSerialization -where - C: Ciphersuite, -{ - fn from(value: VerifyingKey) -> Self { - Self(value.serialize()) - } -} diff --git a/precompiles/verify-schnorr-signatures/Cargo.toml b/precompiles/verify-schnorr-signatures/Cargo.toml index 94583326d..bc704d041 100644 --- a/precompiles/verify-schnorr-signatures/Cargo.toml +++ b/precompiles/verify-schnorr-signatures/Cargo.toml @@ -13,9 +13,7 @@ frost-ed25519 = { workspace = true, default-features = false } frost-ed448 = { workspace = true, default-features = false } frost-ristretto255 = { workspace = true, default-features = false } frost-secp256k1 = { workspace = true, default-features = false } -frost-taproot = { workspace = true, default-features = false } frost-p256 = { workspace = true, default-features = false } -frost-p384 = { workspace = true, default-features = false } # Substrate sp-core = { workspace = true } @@ -23,8 +21,6 @@ sp-std = { workspace = true } sp-io = { workspace = true } parity-scale-codec = { workspace = true, features = ["max-encoded-len"] } - - # Frontier fp-evm = { workspace = true } pallet-evm = { workspace = true } @@ -61,7 +57,5 @@ std = [ "frost-ed448/std", "frost-ristretto255/std", "frost-secp256k1/std", - "frost-taproot/std", "frost-p256/std", - "frost-p384/std", ] diff --git a/precompiles/verify-schnorr-signatures/src/lib.rs b/precompiles/verify-schnorr-signatures/src/lib.rs index 19b5636b7..d604aa76f 100644 --- a/precompiles/verify-schnorr-signatures/src/lib.rs +++ b/precompiles/verify-schnorr-signatures/src/lib.rs @@ -22,14 +22,12 @@ use sp_core::{sr25519, ConstU32}; use sp_io::{crypto::sr25519_verify, hashing::keccak_256}; use sp_std::{marker::PhantomData, prelude::*}; -use frost_core::{signature::Signature, verifying_key::VerifyingKey}; +use frost_core::{Signature, VerifyingKey}; use frost_ed25519::Ed25519Sha512; use frost_ed448::Ed448Shake256; use frost_p256::P256Sha256; -use frost_p384::P384Sha384; use frost_ristretto255::Ristretto255Sha512; use frost_secp256k1::Secp256K1Sha256; -use frost_taproot::Secp256K1Taproot; #[cfg(test)] mod mock; @@ -119,8 +117,8 @@ impl SchnorrSecp256k1Precompile { public_bytes.as_slice(), signature_bytes.as_slice(), &message, - [0u8; 33], - [0u8; 65] + &[0u8; 33], + &[0u8; 65] ); Ok(false) @@ -150,8 +148,8 @@ impl SchnorrEd25519Precompile { public_bytes.as_slice(), signature_bytes.as_slice(), &message, - [0u8; 32], - [0u8; 64] + &[0u8; 32], + &[0u8; 64] ); Ok(false) @@ -181,8 +179,8 @@ impl SchnorrEd448Precompile { public_bytes.as_slice(), signature_bytes.as_slice(), &message, - [0u8; 57], - [0u8; 114] + &[0u8; 57], + &[0u8; 114] ); Ok(false) @@ -212,39 +210,8 @@ impl SchnorrP256Precompile { public_bytes.as_slice(), signature_bytes.as_slice(), &message, - [0u8; 33], - [0u8; 65] - ); - - Ok(false) - } -} - -/// A precompile to verify SchnorrP384 signature -pub struct SchnorrP384Precompile(PhantomData); - -#[precompile_utils::precompile] -impl SchnorrP384Precompile { - #[precompile::public("verify(bytes,bytes,bytes)")] - #[precompile::view] - fn verify( - _handle: &mut impl PrecompileHandle, - public_bytes: BoundedBytes>, - signature_bytes: BoundedBytes>, - message: UnboundedBytes, - ) -> EvmResult { - // Parse arguments - let public_bytes: Vec = public_bytes.into(); - let signature_bytes: Vec = signature_bytes.into(); - let message: Vec = message.into(); - - verify_signature!( - P384Sha384, - public_bytes.as_slice(), - signature_bytes.as_slice(), - &message, - [0u8; 49], - [0u8; 97] + &[0u8; 33], + &[0u8; 65] ); Ok(false) @@ -274,39 +241,8 @@ impl SchnorrRistretto255Precompile { public_bytes.as_slice(), signature_bytes.as_slice(), &message, - [0u8; 32], - [0u8; 64] - ); - - Ok(false) - } -} - -/// A precompile to verify SchnorrTaproot signature -pub struct SchnorrTaprootPrecompile(PhantomData); - -#[precompile_utils::precompile] -impl SchnorrTaprootPrecompile { - #[precompile::public("verify(bytes,bytes,bytes)")] - #[precompile::view] - fn verify( - _handle: &mut impl PrecompileHandle, - public_bytes: BoundedBytes>, - signature_bytes: BoundedBytes>, - message: UnboundedBytes, - ) -> EvmResult { - // Parse arguments - let public_bytes: Vec = public_bytes.into(); - let signature_bytes: Vec = signature_bytes.into(); - let message: Vec = message.into(); - - verify_signature!( - Secp256K1Taproot, - public_bytes.as_slice(), - signature_bytes.as_slice(), - &message, - [0u8; 33], - [0u8; 65] + &[0u8; 32], + &[0u8; 64] ); Ok(false)