Smash the singular Ciphersuite trait into multiple

This helps identify where the various functionalities are used, or rather, not
used. The `Ciphersuite` trait present in `patches/ciphersuite`, facilitating
the entire FCMP++ tree, only requires the markers _and_ canonical point
decoding. I've opened a PR to upstream such a trait into `group`
(https://github.com/zkcrypto/group/pull/68).

`WrappedGroup` is still justified for as long as `Group::generator` exists.
Moving `::generator()` to its own trait, on an independent structure (upstream)
would be massively appreciated. @tarcieri also wanted to update from
`fn generator()` to `const GENERATOR`, which would encourage further discussion
on https://github.com/zkcrypto/group/issues/32 and
https://github.com/zkcrypto/group/issues/45, which have been stagnant.

The `Id` trait is occasionally used yet really should be first off the chopping
block.

Finally, `WithPreferredHash` is only actually used around a third of the time,
which more than justifies it being a separate trait.

---

Updates `dalek_ff_group::Scalar` to directly re-export
`curve25519_dalek::Scalar`, as without issue. `dalek_ff_group::RistrettoPoint`
also could be replaced with an export of `curve25519_dalek::RistrettoPoint`,
yet the coordinator relies on how we implemented `Hash` on it for the hell of
it so it isn't worth it at this time. `dalek_ff_group::EdwardsPoint` can't be
replaced for an re-export of `curve25519_dalek::SubgroupPoint` as it doesn't
implement `zeroize`, `subtle` traits within a released, non-yanked version.
Relevance to https://github.com/serai-dex/serai/issues/201 and
https://github.com/dalek-cryptography/curve25519-dalek/issues/811#issuecomment-3247732746.

Also updates the `Ristretto` ciphersuite to prefer `Blake2b-512` over
`SHA2-512`. In order to maintain compliance with FROST's IETF standard,
`modular-frost` defines its own ciphersuite for Ristretto which still uses
`SHA2-512`.
This commit is contained in:
Luke Parker
2025-09-03 12:25:37 -04:00
parent 215e41fdb6
commit a141deaf36
124 changed files with 1003 additions and 1211 deletions

186
Cargo.lock generated
View File

@@ -1526,11 +1526,11 @@ dependencies = [
[[package]] [[package]]
name = "blake2" name = "blake2"
version = "0.11.0-rc.0" version = "0.11.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3d950855224a23299348898f8a2127860e1afea78df3e51deebb89d1cb2f8f" checksum = "1edac47499deef695d9431bf241c75ea29f4cf3dcb78d39e19b31515e4ad3b08"
dependencies = [ dependencies = [
"digest 0.11.0-rc.0", "digest 0.11.0-rc.1",
] ]
[[package]] [[package]]
@@ -1588,11 +1588,12 @@ dependencies = [
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.11.0-rc.4" version = "0.11.0-rc.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a229bfd78e4827c91b9b95784f69492c1b77c1ab75a45a8a037b139215086f94" checksum = "e9ef36a6fcdb072aa548f3da057640ec10859eb4e91ddf526ee648d50c76a949"
dependencies = [ dependencies = [
"hybrid-array", "hybrid-array",
"zeroize",
] ]
[[package]] [[package]]
@@ -1922,13 +1923,11 @@ dependencies = [
name = "ciphersuite" name = "ciphersuite"
version = "0.4.2" version = "0.4.2"
dependencies = [ dependencies = [
"digest 0.11.0-rc.0", "digest 0.11.0-rc.1",
"ff", "ff",
"ff-group-tests", "ff-group-tests",
"flexible-transcript",
"group", "group",
"hex", "hex",
"rand_core 0.6.4",
"std-shims", "std-shims",
"subtle", "subtle",
"zeroize", "zeroize",
@@ -1939,7 +1938,9 @@ name = "ciphersuite"
version = "0.4.99" version = "0.4.99"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"std-shims",
"zeroize",
] ]
[[package]] [[package]]
@@ -1952,7 +1953,7 @@ dependencies = [
"k256", "k256",
"p256", "p256",
"rand_core 0.6.4", "rand_core 0.6.4",
"sha2 0.11.0-rc.0", "sha2 0.11.0-rc.2",
"zeroize", "zeroize",
] ]
@@ -2345,9 +2346,9 @@ dependencies = [
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.2.0-rc.3" version = "0.2.0-rc.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a23fa214dea9efd4dacee5a5614646b30216ae0f05d4bb51bafb50e9da1c5be" checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6"
dependencies = [ dependencies = [
"hybrid-array", "hybrid-array",
] ]
@@ -2371,6 +2372,7 @@ dependencies = [
"cpufeatures", "cpufeatures",
"curve25519-dalek-derive", "curve25519-dalek-derive",
"digest 0.10.7", "digest 0.10.7",
"ff",
"fiat-crypto", "fiat-crypto",
"group", "group",
"rand_core 0.6.4", "rand_core 0.6.4",
@@ -2454,28 +2456,27 @@ dependencies = [
[[package]] [[package]]
name = "dalek-ff-group" name = "dalek-ff-group"
version = "0.4.6" version = "0.5.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.2",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"crypto-bigint 0.6.1",
"curve25519-dalek", "curve25519-dalek",
"digest 0.10.7",
"ff-group-tests", "ff-group-tests",
"hex", "hex",
"prime-field", "prime-field",
"rand_core 0.6.4", "rand_core 0.6.4",
"sha2 0.11.0-rc.0", "sha2 0.11.0-rc.2",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
[[package]] [[package]]
name = "dalek-ff-group" name = "dalek-ff-group"
version = "0.4.99" version = "0.5.99"
dependencies = [ dependencies = [
"crypto-bigint 0.5.5", "crypto-bigint 0.5.5",
"crypto-bigint 0.6.1", "crypto-bigint 0.6.1",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"prime-field", "prime-field",
] ]
@@ -2551,7 +2552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"syn 2.0.106", "syn 1.0.109",
] ]
[[package]] [[package]]
@@ -2714,13 +2715,14 @@ dependencies = [
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.11.0-rc.0" version = "0.11.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460dd7f37e4950526b54a5a6b1f41b6c8e763c58eb9a8fc8fc05ba5c2f44ca7b" checksum = "3a4aae35a0fcbe22ff1be50fe96df72002d5a4a6fb4aae9193cf2da0daa36da2"
dependencies = [ dependencies = [
"block-buffer 0.11.0-rc.4", "block-buffer 0.11.0-rc.5",
"crypto-common 0.2.0-rc.3", "crypto-common 0.2.0-rc.4",
"subtle", "subtle",
"zeroize",
] ]
[[package]] [[package]]
@@ -2801,10 +2803,10 @@ dependencies = [
name = "dkg-evrf" name = "dkg-evrf"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"ciphersuite-kp256", "ciphersuite-kp256",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg", "dkg",
"dkg-recovery", "dkg-recovery",
"ec-divisors", "ec-divisors",
@@ -2829,7 +2831,7 @@ name = "dkg-musig"
version = "0.6.0" version = "0.6.0"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg", "dkg",
"dkg-recovery", "dkg-recovery",
"multiexp", "multiexp",
@@ -2920,9 +2922,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]] [[package]]
name = "ec-divisors" name = "ec-divisors"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=a6f8797007e768488568b821435cf5006517a962#a6f8797007e768488568b821435cf5006517a962" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7216a2e84c7671c167c3d81eafe0d2b1f418f102#7216a2e84c7671c167c3d81eafe0d2b1f418f102"
dependencies = [ dependencies = [
"dalek-ff-group 0.4.99", "dalek-ff-group 0.5.99",
"ff", "ff",
"group", "group",
"rand_core 0.6.4", "rand_core 0.6.4",
@@ -3031,10 +3033,9 @@ dependencies = [
name = "embedwards25519" name = "embedwards25519"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group 0.4.6",
"ff-group-tests", "ff-group-tests",
"generalized-bulletproofs-ec-gadgets", "generalized-bulletproofs-ec-gadgets",
"hex", "hex",
@@ -3321,10 +3322,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
name = "flexible-transcript" name = "flexible-transcript"
version = "0.3.4" version = "0.3.4"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"digest 0.11.0-rc.0", "digest 0.11.0-rc.1",
"merlin", "merlin",
"sha2 0.11.0-rc.0", "sha2 0.11.0-rc.2",
"zeroize", "zeroize",
] ]
@@ -3544,7 +3545,6 @@ name = "frost-schnorrkel"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6",
"flexible-transcript", "flexible-transcript",
"group", "group",
"modular-frost", "modular-frost",
@@ -3771,7 +3771,7 @@ dependencies = [
[[package]] [[package]]
name = "generalized-bulletproofs" name = "generalized-bulletproofs"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=a6f8797007e768488568b821435cf5006517a962#a6f8797007e768488568b821435cf5006517a962" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7216a2e84c7671c167c3d81eafe0d2b1f418f102#7216a2e84c7671c167c3d81eafe0d2b1f418f102"
dependencies = [ dependencies = [
"blake2 0.10.6", "blake2 0.10.6",
"ciphersuite 0.4.99", "ciphersuite 0.4.99",
@@ -3786,7 +3786,7 @@ dependencies = [
[[package]] [[package]]
name = "generalized-bulletproofs-circuit-abstraction" name = "generalized-bulletproofs-circuit-abstraction"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=a6f8797007e768488568b821435cf5006517a962#a6f8797007e768488568b821435cf5006517a962" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7216a2e84c7671c167c3d81eafe0d2b1f418f102#7216a2e84c7671c167c3d81eafe0d2b1f418f102"
dependencies = [ dependencies = [
"ciphersuite 0.4.99", "ciphersuite 0.4.99",
"generalized-bulletproofs", "generalized-bulletproofs",
@@ -3797,7 +3797,7 @@ dependencies = [
[[package]] [[package]]
name = "generalized-bulletproofs-ec-gadgets" name = "generalized-bulletproofs-ec-gadgets"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=a6f8797007e768488568b821435cf5006517a962#a6f8797007e768488568b821435cf5006517a962" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7216a2e84c7671c167c3d81eafe0d2b1f418f102#7216a2e84c7671c167c3d81eafe0d2b1f418f102"
dependencies = [ dependencies = [
"ciphersuite 0.4.99", "ciphersuite 0.4.99",
"generalized-bulletproofs-circuit-abstraction", "generalized-bulletproofs-circuit-abstraction",
@@ -4221,8 +4221,9 @@ checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
[[package]] [[package]]
name = "hybrid-array" name = "hybrid-array"
version = "0.3.1" version = "0.4.0"
source = "git+https://github.com/kayabaNerve/hybrid-array?rev=8caa508976c93696a67f40734537c91be7cecd96#8caa508976c93696a67f40734537c91be7cecd96" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe39a812f039072707ce38020acbab2f769087952eddd9e2b890f37654b2349"
dependencies = [ dependencies = [
"typenum", "typenum",
] ]
@@ -6020,7 +6021,7 @@ dependencies = [
"hex", "hex",
"prime-field", "prime-field",
"rand_core 0.6.4", "rand_core 0.6.4",
"sha3 0.11.0-rc.0", "sha3 0.11.0-rc.2",
"zeroize", "zeroize",
] ]
@@ -6079,11 +6080,11 @@ dependencies = [
[[package]] [[package]]
name = "modular-frost" name = "modular-frost"
version = "0.10.1" version = "0.11.0"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"ciphersuite-kp256", "ciphersuite-kp256",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg", "dkg",
"dkg-dealer", "dkg-dealer",
"dkg-recovery", "dkg-recovery",
@@ -6105,7 +6106,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-address" name = "monero-address"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"monero-base58", "monero-base58",
@@ -6117,7 +6118,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-base58" name = "monero-base58"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"monero-primitives", "monero-primitives",
"std-shims", "std-shims",
@@ -6126,7 +6127,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-borromean" name = "monero-borromean"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"monero-generators", "monero-generators",
@@ -6139,7 +6140,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-bulletproofs" name = "monero-bulletproofs"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"monero-generators", "monero-generators",
@@ -6154,10 +6155,10 @@ dependencies = [
[[package]] [[package]]
name = "monero-clsag" name = "monero-clsag"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group 0.4.99", "dalek-ff-group 0.5.99",
"flexible-transcript", "flexible-transcript",
"group", "group",
"modular-frost", "modular-frost",
@@ -6175,11 +6176,11 @@ dependencies = [
[[package]] [[package]]
name = "monero-generators" name = "monero-generators"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"crypto-bigint 0.5.5", "crypto-bigint 0.5.5",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group 0.4.99", "dalek-ff-group 0.5.99",
"group", "group",
"monero-io", "monero-io",
"sha3 0.10.8", "sha3 0.10.8",
@@ -6190,16 +6191,17 @@ dependencies = [
[[package]] [[package]]
name = "monero-io" name = "monero-io"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"std-shims", "std-shims",
"zeroize",
] ]
[[package]] [[package]]
name = "monero-mlsag" name = "monero-mlsag"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"monero-generators", "monero-generators",
@@ -6213,7 +6215,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-oxide" name = "monero-oxide"
version = "0.1.4-alpha" version = "0.1.4-alpha"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"hex-literal", "hex-literal",
@@ -6231,7 +6233,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-primitives" name = "monero-primitives"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"monero-generators", "monero-generators",
@@ -6244,7 +6246,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-rpc" name = "monero-rpc"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"hex", "hex",
@@ -6260,7 +6262,7 @@ dependencies = [
[[package]] [[package]]
name = "monero-simple-request-rpc" name = "monero-simple-request-rpc"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"digest_auth", "digest_auth",
"hex", "hex",
@@ -6273,10 +6275,10 @@ dependencies = [
[[package]] [[package]]
name = "monero-wallet" name = "monero-wallet"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/monero-oxide/monero-oxide?rev=6966575e05fe09b77674c46984b21686ed9304ff#6966575e05fe09b77674c46984b21686ed9304ff" source = "git+https://github.com/monero-oxide/monero-oxide?rev=7f37cc8f770858aa1739e0f56dbe447db86f4ba6#7f37cc8f770858aa1739e0f56dbe447db86f4ba6"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group 0.4.99", "dalek-ff-group 0.5.99",
"flexible-transcript", "flexible-transcript",
"hex", "hex",
"modular-frost", "modular-frost",
@@ -6327,7 +6329,7 @@ dependencies = [
name = "multiexp" name = "multiexp"
version = "0.4.2" version = "0.4.2"
dependencies = [ dependencies = [
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"ff", "ff",
"group", "group",
"k256", "k256",
@@ -6688,7 +6690,7 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
dependencies = [ dependencies = [
"proc-macro-crate 3.2.0", "proc-macro-crate 1.3.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.106",
@@ -9465,7 +9467,8 @@ name = "schnorr-signatures"
version = "0.5.2" version = "0.5.2"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"digest 0.11.0-rc.1",
"flexible-transcript", "flexible-transcript",
"hex", "hex",
"multiexp", "multiexp",
@@ -9602,7 +9605,7 @@ dependencies = [
"k256", "k256",
"prime-field", "prime-field",
"rand_core 0.6.4", "rand_core 0.6.4",
"sha2 0.11.0-rc.0", "sha2 0.11.0-rc.2",
"short-weierstrass", "short-weierstrass",
"std-shims", "std-shims",
] ]
@@ -9746,11 +9749,11 @@ dependencies = [
"async-lock", "async-lock",
"bitcoin", "bitcoin",
"bitvec", "bitvec",
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"ciphersuite-kp256", "ciphersuite-kp256",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg-musig", "dkg-musig",
"dockertest", "dockertest",
"frame-system", "frame-system",
@@ -9808,20 +9811,18 @@ name = "serai-coordinator"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg-musig", "dkg-musig",
"env_logger", "env_logger",
"flexible-transcript",
"frost-schnorrkel", "frost-schnorrkel",
"hex", "hex",
"log", "log",
"modular-frost", "modular-frost",
"parity-scale-codec", "parity-scale-codec",
"rand_core 0.6.4", "rand_core 0.6.4",
"schnorr-signatures",
"schnorrkel", "schnorrkel",
"serai-client", "serai-client",
"serai-coordinator-libp2p-p2p", "serai-coordinator-libp2p-p2p",
@@ -9845,7 +9846,7 @@ name = "serai-coordinator-libp2p-p2p"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"futures-util", "futures-util",
"hex", "hex",
@@ -9899,10 +9900,10 @@ dependencies = [
name = "serai-coordinator-tributary" name = "serai-coordinator-tributary"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg", "dkg",
"log", "log",
"parity-scale-codec", "parity-scale-codec",
@@ -9922,7 +9923,7 @@ dependencies = [
name = "serai-cosign" name = "serai-cosign"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"log", "log",
"parity-scale-codec", "parity-scale-codec",
@@ -10166,7 +10167,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"env_logger", "env_logger",
"flexible-transcript", "flexible-transcript",
"hex", "hex",
@@ -10187,7 +10188,7 @@ name = "serai-message-queue-tests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dockertest", "dockertest",
"hex", "hex",
"rand_core 0.6.4", "rand_core 0.6.4",
@@ -10204,7 +10205,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg-evrf", "dkg-evrf",
"log", "log",
"modular-frost", "modular-frost",
@@ -10234,7 +10235,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bitcoin-serai", "bitcoin-serai",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg", "dkg",
"dkg-evrf", "dkg-evrf",
"embedwards25519", "embedwards25519",
@@ -10256,7 +10257,7 @@ dependencies = [
"ciphersuite-kp256", "ciphersuite-kp256",
"clap", "clap",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"embedwards25519", "embedwards25519",
"frame-benchmarking", "frame-benchmarking",
"futures-util", "futures-util",
@@ -10307,7 +10308,7 @@ name = "serai-orchestrator"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"embedwards25519", "embedwards25519",
"flexible-transcript", "flexible-transcript",
"hex", "hex",
@@ -10462,12 +10463,14 @@ dependencies = [
name = "serai-processor-key-gen" name = "serai-processor-key-gen"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dkg-evrf", "dkg-evrf",
"embedwards25519",
"flexible-transcript", "flexible-transcript",
"log", "log",
"modular-frost",
"parity-scale-codec", "parity-scale-codec",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
"rand_core 0.6.4", "rand_core 0.6.4",
@@ -10510,7 +10513,7 @@ dependencies = [
name = "serai-processor-scanner" name = "serai-processor-scanner"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"group", "group",
"hex", "hex",
@@ -10542,10 +10545,9 @@ dependencies = [
name = "serai-processor-signers" name = "serai-processor-signers"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6",
"frost-schnorrkel", "frost-schnorrkel",
"log", "log",
"modular-frost", "modular-frost",
@@ -10723,7 +10725,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg-musig", "dkg-musig",
"frame-support", "frame-support",
"frame-system", "frame-system",
@@ -10758,7 +10760,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"borsh", "borsh",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"dkg-musig", "dkg-musig",
"parity-scale-codec", "parity-scale-codec",
"scale-info", "scale-info",
@@ -10943,13 +10945,13 @@ dependencies = [
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.11.0-rc.0" version = "0.11.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa1d2e6b3cc4e43a8258a9a3b17aa5dfd2cc5186c7024bba8a64aa65b2c71a59" checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
"digest 0.11.0-rc.0", "digest 0.11.0-rc.1",
] ]
[[package]] [[package]]
@@ -10964,11 +10966,11 @@ dependencies = [
[[package]] [[package]]
name = "sha3" name = "sha3"
version = "0.11.0-rc.0" version = "0.11.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e6a92fd180fd205defdc0b78288ce847c7309d329fd6647a814567e67db50e" checksum = "b3c185ed8cff82204014bfaa7649b4c945ca565e03c0534eb33a8d2a01572932"
dependencies = [ dependencies = [
"digest 0.11.0-rc.0", "digest 0.11.0-rc.1",
"keccak 0.2.0-pre.0", "keccak 0.2.0-pre.0",
] ]
@@ -12609,9 +12611,9 @@ dependencies = [
name = "tributary-sdk" name = "tributary-sdk"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"blake2 0.11.0-rc.0", "blake2 0.11.0-rc.2",
"ciphersuite 0.4.2", "ciphersuite 0.4.2",
"dalek-ff-group 0.4.6", "dalek-ff-group 0.5.0",
"flexible-transcript", "flexible-transcript",
"futures-channel", "futures-channel",
"futures-util", "futures-util",

View File

@@ -228,9 +228,6 @@ directories-next = { path = "patches/directories-next" }
k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" } k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" } p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
# https://github.com/RustCrypto/hybrid-array/issues/131
hybrid-array = { git = "https://github.com/kayabaNerve/hybrid-array", rev = "8caa508976c93696a67f40734537c91be7cecd96" }
[workspace.lints.clippy] [workspace.lints.clippy]
unwrap_or_default = "allow" unwrap_or_default = "allow"
map_unwrap_or = "allow" map_unwrap_or = "allow"

View File

@@ -24,10 +24,8 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] }
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
schnorrkel = { version = "0.11", default-features = false, features = ["std"] } schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std", "recommended"] }
dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"] } dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"] }
ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std"] } ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std"] }
schnorr = { package = "schnorr-signatures", path = "../crypto/schnorr", default-features = false, features = ["std", "aggregate"] }
dkg = { package = "dkg-musig", path = "../crypto/dkg/musig", default-features = false, features = ["std"] } dkg = { package = "dkg-musig", path = "../crypto/dkg/musig", default-features = false, features = ["std"] }
frost = { package = "modular-frost", path = "../crypto/frost" } frost = { package = "modular-frost", path = "../crypto/frost" }
frost-schnorrkel = { path = "../crypto/schnorrkel" } frost-schnorrkel = { path = "../crypto/schnorrkel" }

View File

@@ -3,11 +3,10 @@ use std::{boxed::Box, collections::HashMap};
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, *};
use dalek_ff_group::Ristretto;
use dkg::{Participant, musig}; use dkg::{Participant, musig};
use frost_schnorrkel::{ use frost_schnorrkel::{
frost::{FrostError, sign::*}, frost::{curve::Ristretto, FrostError, sign::*},
Schnorrkel, Schnorrkel,
}; };
@@ -31,7 +30,7 @@ fn schnorrkel() -> Schnorrkel {
fn our_i( fn our_i(
set: &NewSetInformation, set: &NewSetInformation,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
data: &HashMap<Participant, Vec<u8>>, data: &HashMap<Participant, Vec<u8>>,
) -> Participant { ) -> Participant {
let public = SeraiAddress((Ristretto::generator() * key.deref()).to_bytes()); let public = SeraiAddress((Ristretto::generator() * key.deref()).to_bytes());
@@ -125,7 +124,7 @@ pub(crate) struct ConfirmDkgTask<CD: DbTrait, TD: DbTrait> {
set: NewSetInformation, set: NewSetInformation,
tributary_db: TD, tributary_db: TD,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
signer: Option<Signer>, signer: Option<Signer>,
} }
@@ -134,7 +133,7 @@ impl<CD: DbTrait, TD: DbTrait> ConfirmDkgTask<CD, TD> {
db: CD, db: CD,
set: NewSetInformation, set: NewSetInformation,
tributary_db: TD, tributary_db: TD,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
) -> Self { ) -> Self {
Self { db, set, tributary_db, key, signer: None } Self { db, set, tributary_db, key, signer: None }
} }
@@ -153,7 +152,7 @@ impl<CD: DbTrait, TD: DbTrait> ConfirmDkgTask<CD, TD> {
db: &mut CD, db: &mut CD,
set: ExternalValidatorSet, set: ExternalValidatorSet,
attempt: u32, attempt: u32,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
signer: &mut Option<Signer>, signer: &mut Option<Signer>,
) { ) {
// Perform the preprocess // Perform the preprocess

View File

@@ -7,7 +7,7 @@ use rand_core::{RngCore, OsRng};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{ use ciphersuite::{
group::{ff::PrimeField, GroupEncoding}, group::{ff::PrimeField, GroupEncoding},
Ciphersuite, *,
}; };
use borsh::BorshDeserialize; use borsh::BorshDeserialize;
@@ -352,7 +352,7 @@ async fn main() {
let mut key_bytes = [0; 32]; let mut key_bytes = [0; 32];
key_bytes.copy_from_slice(&key_vec); key_bytes.copy_from_slice(&key_vec);
key_vec.zeroize(); key_vec.zeroize();
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::from_repr(key_bytes).unwrap()); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::from_repr(key_bytes).unwrap());
key_bytes.zeroize(); key_bytes.zeroize();
key key
}; };
@@ -439,7 +439,7 @@ async fn main() {
EphemeralEventStream::new( EphemeralEventStream::new(
db.clone(), db.clone(),
serai.clone(), serai.clone(),
SeraiAddress((<Ristretto as Ciphersuite>::generator() * serai_key.deref()).to_bytes()), SeraiAddress((<Ristretto as WrappedGroup>::generator() * serai_key.deref()).to_bytes()),
) )
.continually_run(substrate_ephemeral_task_def, vec![substrate_task]), .continually_run(substrate_ephemeral_task_def, vec![substrate_task]),
); );

View File

@@ -3,7 +3,7 @@ use std::sync::Arc;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -23,7 +23,7 @@ use serai_coordinator_p2p::P2p;
use crate::{Db, KeySet}; use crate::{Db, KeySet};
pub(crate) struct SubstrateTask<P: P2p> { pub(crate) struct SubstrateTask<P: P2p> {
pub(crate) serai_key: Zeroizing<<Ristretto as Ciphersuite>::F>, pub(crate) serai_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
pub(crate) db: Db, pub(crate) db: Db,
pub(crate) message_queue: Arc<MessageQueue>, pub(crate) message_queue: Arc<MessageQueue>,
pub(crate) p2p: P, pub(crate) p2p: P,

View File

@@ -4,7 +4,7 @@ use std::sync::Arc;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use blake2::{digest::typenum::U32, Digest, Blake2s}; use blake2::{digest::typenum::U32, Digest, Blake2s};
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -160,7 +160,7 @@ impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan
#[must_use] #[must_use]
async fn add_signed_unsigned_transaction<TD: DbTrait, P: P2p>( async fn add_signed_unsigned_transaction<TD: DbTrait, P: P2p>(
tributary: &Tributary<TD, Transaction, P>, tributary: &Tributary<TD, Transaction, P>,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
mut tx: Transaction, mut tx: Transaction,
) -> bool { ) -> bool {
// If this is a signed transaction, sign it // If this is a signed transaction, sign it
@@ -213,7 +213,7 @@ async fn add_with_recognition_check<TD: DbTrait, P: P2p>(
set: ExternalValidatorSet, set: ExternalValidatorSet,
tributary_db: &mut TD, tributary_db: &mut TD,
tributary: &Tributary<TD, Transaction, P>, tributary: &Tributary<TD, Transaction, P>,
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
tx: Transaction, tx: Transaction,
) -> bool { ) -> bool {
let kind = tx.kind(); let kind = tx.kind();
@@ -252,7 +252,7 @@ pub(crate) struct AddTributaryTransactionsTask<CD: DbTrait, TD: DbTrait, P: P2p>
tributary_db: TD, tributary_db: TD,
tributary: Tributary<TD, Transaction, P>, tributary: Tributary<TD, Transaction, P>,
set: NewSetInformation, set: NewSetInformation,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
} }
impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for AddTributaryTransactionsTask<CD, TD, P> { impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for AddTributaryTransactionsTask<CD, TD, P> {
type Error = DoesNotError; type Error = DoesNotError;
@@ -382,7 +382,7 @@ pub(crate) struct SignSlashReportTask<CD: DbTrait, TD: DbTrait, P: P2p> {
tributary_db: TD, tributary_db: TD,
tributary: Tributary<TD, Transaction, P>, tributary: Tributary<TD, Transaction, P>,
set: NewSetInformation, set: NewSetInformation,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
} }
impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for SignSlashReportTask<CD, TD, P> { impl<CD: DbTrait, TD: DbTrait, P: P2p> ContinuallyRan for SignSlashReportTask<CD, TD, P> {
type Error = DoesNotError; type Error = DoesNotError;
@@ -470,7 +470,7 @@ pub(crate) async fn spawn_tributary<P: P2p>(
p2p: P, p2p: P,
p2p_add_tributary: &mpsc::UnboundedSender<(ExternalValidatorSet, Tributary<Db, Transaction, P>)>, p2p_add_tributary: &mpsc::UnboundedSender<(ExternalValidatorSet, Tributary<Db, Transaction, P>)>,
set: NewSetInformation, set: NewSetInformation,
serai_key: Zeroizing<<Ristretto as Ciphersuite>::F>, serai_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
) { ) {
// Don't spawn retired Tributaries // Don't spawn retired Tributaries
if crate::db::RetiredTributary::get(&db, set.set.network).map(|session| session.0) >= if crate::db::RetiredTributary::get(&db, set.set.network).map(|session| session.0) >=
@@ -490,7 +490,7 @@ pub(crate) async fn spawn_tributary<P: P2p>(
let mut tributary_validators = Vec::with_capacity(set.validators.len()); let mut tributary_validators = Vec::with_capacity(set.validators.len());
for (validator, weight) in set.validators.iter().copied() { for (validator, weight) in set.validators.iter().copied() {
let validator_key = <Ristretto as Ciphersuite>::read_G(&mut validator.0.as_slice()) let validator_key = <Ristretto as GroupIo>::read_G(&mut validator.0.as_slice())
.expect("Serai validator had an invalid public key"); .expect("Serai validator had an invalid public key");
let weight = u64::from(weight); let weight = u64::from(weight);
tributary_validators.push((validator_key, weight)); tributary_validators.push((validator_key, weight));

View File

@@ -1,7 +1,7 @@
use std::collections::{VecDeque, HashSet}; use std::collections::{VecDeque, HashSet};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, *};
use serai_db::{Get, DbTxn, Db}; use serai_db::{Get, DbTxn, Db};
@@ -21,7 +21,7 @@ pub(crate) struct Blockchain<D: Db, T: TransactionTrait> {
block_number: u64, block_number: u64,
tip: [u8; 32], tip: [u8; 32],
participants: HashSet<<Ristretto as Ciphersuite>::G>, participants: HashSet<<Ristretto as WrappedGroup>::G>,
provided: ProvidedTransactions<D, T>, provided: ProvidedTransactions<D, T>,
mempool: Mempool<D, T>, mempool: Mempool<D, T>,
@@ -56,7 +56,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
} }
fn next_nonce_key( fn next_nonce_key(
genesis: &[u8; 32], genesis: &[u8; 32],
signer: &<Ristretto as Ciphersuite>::G, signer: &<Ristretto as WrappedGroup>::G,
order: &[u8], order: &[u8],
) -> Vec<u8> { ) -> Vec<u8> {
D::key( D::key(
@@ -69,7 +69,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
pub(crate) fn new( pub(crate) fn new(
db: D, db: D,
genesis: [u8; 32], genesis: [u8; 32],
participants: &[<Ristretto as Ciphersuite>::G], participants: &[<Ristretto as WrappedGroup>::G],
) -> Self { ) -> Self {
let mut res = Self { let mut res = Self {
db: Some(db.clone()), db: Some(db.clone()),
@@ -196,7 +196,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
pub(crate) fn next_nonce( pub(crate) fn next_nonce(
&self, &self,
signer: &<Ristretto as Ciphersuite>::G, signer: &<Ristretto as WrappedGroup>::G,
order: &[u8], order: &[u8],
) -> Option<u32> { ) -> Option<u32> {
if let Some(next_nonce) = self.mempool.next_nonce_in_mempool(signer, order.to_vec()) { if let Some(next_nonce) = self.mempool.next_nonce_in_mempool(signer, order.to_vec()) {

View File

@@ -3,7 +3,7 @@ use std::{sync::Arc, io};
use zeroize::Zeroizing; use zeroize::Zeroizing;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use scale::Decode; use scale::Decode;
@@ -162,8 +162,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
db: D, db: D,
genesis: [u8; 32], genesis: [u8; 32],
start_time: u64, start_time: u64,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
validators: Vec<(<Ristretto as Ciphersuite>::G, u64)>, validators: Vec<(<Ristretto as WrappedGroup>::G, u64)>,
p2p: P, p2p: P,
) -> Option<Self> { ) -> Option<Self> {
log::info!("new Tributary with genesis {}", hex::encode(genesis)); log::info!("new Tributary with genesis {}", hex::encode(genesis));
@@ -235,7 +235,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
pub async fn next_nonce( pub async fn next_nonce(
&self, &self,
signer: &<Ristretto as Ciphersuite>::G, signer: &<Ristretto as WrappedGroup>::G,
order: &[u8], order: &[u8],
) -> Option<u32> { ) -> Option<u32> {
self.network.blockchain.read().await.next_nonce(signer, order) self.network.blockchain.read().await.next_nonce(signer, order)

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use serai_db::{DbTxn, Db}; use serai_db::{DbTxn, Db};
@@ -21,9 +21,9 @@ pub(crate) struct Mempool<D: Db, T: TransactionTrait> {
db: D, db: D,
genesis: [u8; 32], genesis: [u8; 32],
last_nonce_in_mempool: HashMap<(<Ristretto as Ciphersuite>::G, Vec<u8>), u32>, last_nonce_in_mempool: HashMap<(<Ristretto as WrappedGroup>::G, Vec<u8>), u32>,
txs: HashMap<[u8; 32], Transaction<T>>, txs: HashMap<[u8; 32], Transaction<T>>,
txs_per_signer: HashMap<<Ristretto as Ciphersuite>::G, u32>, txs_per_signer: HashMap<<Ristretto as WrappedGroup>::G, u32>,
} }
impl<D: Db, T: TransactionTrait> Mempool<D, T> { impl<D: Db, T: TransactionTrait> Mempool<D, T> {
@@ -107,7 +107,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
// Returns Ok(true) if new, Ok(false) if an already present unsigned, or the error. // Returns Ok(true) if new, Ok(false) if an already present unsigned, or the error.
pub(crate) fn add< pub(crate) fn add<
N: Network, N: Network,
F: FnOnce(<Ristretto as Ciphersuite>::G, Vec<u8>) -> Option<u32>, F: FnOnce(<Ristretto as WrappedGroup>::G, Vec<u8>) -> Option<u32>,
>( >(
&mut self, &mut self,
blockchain_next_nonce: F, blockchain_next_nonce: F,
@@ -179,7 +179,7 @@ impl<D: Db, T: TransactionTrait> Mempool<D, T> {
// Returns None if the mempool doesn't have a nonce tracked. // Returns None if the mempool doesn't have a nonce tracked.
pub(crate) fn next_nonce_in_mempool( pub(crate) fn next_nonce_in_mempool(
&self, &self,
signer: &<Ristretto as Ciphersuite>::G, signer: &<Ristretto as WrappedGroup>::G,
order: Vec<u8>, order: Vec<u8>,
) -> Option<u32> { ) -> Option<u32> {
self.last_nonce_in_mempool.get(&(*signer, order)).copied().map(|nonce| nonce + 1) self.last_nonce_in_mempool.get(&(*signer, order)).copied().map(|nonce| nonce + 1)

View File

@@ -10,11 +10,8 @@ use rand_chacha::ChaCha12Rng;
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, RecommendedTranscript};
use ciphersuite::{ use ciphersuite::{
group::{ group::{ff::PrimeField, GroupEncoding},
GroupEncoding, *,
ff::{Field, PrimeField},
},
Ciphersuite,
}; };
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use schnorr::{ use schnorr::{
@@ -51,24 +48,26 @@ fn challenge(
key: [u8; 32], key: [u8; 32],
nonce: &[u8], nonce: &[u8],
msg: &[u8], msg: &[u8],
) -> <Ristretto as Ciphersuite>::F { ) -> <Ristretto as WrappedGroup>::F {
let mut transcript = RecommendedTranscript::new(b"Tributary Chain Tendermint Message"); let mut transcript = RecommendedTranscript::new(b"Tributary Chain Tendermint Message");
transcript.append_message(b"genesis", genesis); transcript.append_message(b"genesis", genesis);
transcript.append_message(b"key", key); transcript.append_message(b"key", key);
transcript.append_message(b"nonce", nonce); transcript.append_message(b"nonce", nonce);
transcript.append_message(b"message", msg); transcript.append_message(b"message", msg);
<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(&transcript.challenge(b"schnorr").into()) <Ristretto as WrappedGroup>::F::from_bytes_mod_order_wide(
&transcript.challenge(b"schnorr").into(),
)
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Signer { pub struct Signer {
genesis: [u8; 32], genesis: [u8; 32],
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as WrappedGroup>::F>,
} }
impl Signer { impl Signer {
pub(crate) fn new(genesis: [u8; 32], key: Zeroizing<<Ristretto as Ciphersuite>::F>) -> Signer { pub(crate) fn new(genesis: [u8; 32], key: Zeroizing<<Ristretto as WrappedGroup>::F>) -> Signer {
Signer { genesis, key } Signer { genesis, key }
} }
} }
@@ -101,10 +100,10 @@ impl SignerTrait for Signer {
assert_eq!(nonce_ref, [0; 64].as_ref()); assert_eq!(nonce_ref, [0; 64].as_ref());
let nonce = let nonce =
Zeroizing::new(<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(&nonce_arr)); Zeroizing::new(<Ristretto as WrappedGroup>::F::from_bytes_mod_order_wide(&nonce_arr));
nonce_arr.zeroize(); nonce_arr.zeroize();
assert!(!bool::from(nonce.ct_eq(&<Ristretto as Ciphersuite>::F::ZERO))); assert!(!bool::from(nonce.ct_eq(&<Ristretto as WrappedGroup>::F::ZERO)));
let challenge = challenge( let challenge = challenge(
self.genesis, self.genesis,
@@ -133,7 +132,7 @@ pub struct Validators {
impl Validators { impl Validators {
pub(crate) fn new( pub(crate) fn new(
genesis: [u8; 32], genesis: [u8; 32],
validators: Vec<(<Ristretto as Ciphersuite>::G, u64)>, validators: Vec<(<Ristretto as WrappedGroup>::G, u64)>,
) -> Option<Validators> { ) -> Option<Validators> {
let mut total_weight = 0; let mut total_weight = 0;
let mut weights = HashMap::new(); let mut weights = HashMap::new();
@@ -220,7 +219,7 @@ impl SignatureScheme for Validators {
signers signers
.iter() .iter()
.zip(challenges) .zip(challenges)
.map(|(s, c)| (<Ristretto as Ciphersuite>::read_G(&mut s.as_slice()).unwrap(), c)) .map(|(s, c)| (<Ristretto as GroupIo>::read_G(&mut s.as_slice()).unwrap(), c))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.as_slice(), .as_slice(),
) )

View File

@@ -5,7 +5,7 @@ use scale::{Encode, Decode, IoReader};
use blake2::{Digest, Blake2s256}; use blake2::{Digest, Blake2s256};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use crate::{ use crate::{
transaction::{Transaction, TransactionKind, TransactionError}, transaction::{Transaction, TransactionKind, TransactionError},
@@ -50,7 +50,7 @@ impl Transaction for TendermintTx {
Blake2s256::digest(self.serialize()).into() Blake2s256::digest(self.serialize()).into()
} }
fn sig_hash(&self, _genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F { fn sig_hash(&self, _genesis: [u8; 32]) -> <Ristretto as WrappedGroup>::F {
match self { match self {
TendermintTx::SlashEvidence(_) => panic!("sig_hash called on slash evidence transaction"), TendermintTx::SlashEvidence(_) => panic!("sig_hash called on slash evidence transaction"),
} }

View File

@@ -3,10 +3,7 @@ use std::{sync::Arc, io, collections::HashMap, fmt::Debug};
use blake2::{Digest, Blake2s256}; use blake2::{Digest, Blake2s256};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{ use ciphersuite::{group::Group, *};
group::{ff::Field, Group},
Ciphersuite,
};
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
use serai_db::MemDb; use serai_db::MemDb;
@@ -32,11 +29,11 @@ impl NonceTransaction {
nonce, nonce,
distinguisher, distinguisher,
Signed { Signed {
signer: <Ristretto as Ciphersuite>::G::identity(), signer: <Ristretto as WrappedGroup>::G::identity(),
nonce, nonce,
signature: SchnorrSignature::<Ristretto> { signature: SchnorrSignature::<Ristretto> {
R: <Ristretto as Ciphersuite>::G::identity(), R: <Ristretto as WrappedGroup>::G::identity(),
s: <Ristretto as Ciphersuite>::F::ZERO, s: <Ristretto as WrappedGroup>::F::ZERO,
}, },
}, },
) )

View File

@@ -11,7 +11,7 @@ use rand::rngs::OsRng;
use blake2::{Digest, Blake2s256}; use blake2::{Digest, Blake2s256};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::Field, Ciphersuite}; use ciphersuite::*;
use serai_db::{DbTxn, Db, MemDb}; use serai_db::{DbTxn, Db, MemDb};
@@ -31,7 +31,7 @@ type N = TendermintNetwork<MemDb, SignedTransaction, DummyP2p>;
fn new_blockchain<T: TransactionTrait>( fn new_blockchain<T: TransactionTrait>(
genesis: [u8; 32], genesis: [u8; 32],
participants: &[<Ristretto as Ciphersuite>::G], participants: &[<Ristretto as WrappedGroup>::G],
) -> (MemDb, Blockchain<MemDb, T>) { ) -> (MemDb, Blockchain<MemDb, T>) {
let db = MemDb::new(); let db = MemDb::new();
let blockchain = Blockchain::new(db.clone(), genesis, participants); let blockchain = Blockchain::new(db.clone(), genesis, participants);
@@ -82,7 +82,7 @@ fn invalid_block() {
assert!(blockchain.verify_block::<N>(&block, &validators, false).is_err()); assert!(blockchain.verify_block::<N>(&block, &validators, false).is_err());
} }
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0); let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
// Not a participant // Not a participant
@@ -134,7 +134,7 @@ fn invalid_block() {
blockchain.verify_block::<N>(&block, &validators, false).unwrap(); blockchain.verify_block::<N>(&block, &validators, false).unwrap();
match &mut block.transactions[0] { match &mut block.transactions[0] {
Transaction::Application(tx) => { Transaction::Application(tx) => {
tx.1.signature.s += <Ristretto as Ciphersuite>::F::ONE; tx.1.signature.s += <Ristretto as WrappedGroup>::F::ONE;
} }
_ => panic!("non-signed tx found"), _ => panic!("non-signed tx found"),
} }
@@ -150,7 +150,7 @@ fn invalid_block() {
fn signed_transaction() { fn signed_transaction() {
let genesis = new_genesis(); let genesis = new_genesis();
let validators = Arc::new(Validators::new(genesis, vec![]).unwrap()); let validators = Arc::new(Validators::new(genesis, vec![]).unwrap());
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0); let tx = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0);
let signer = tx.1.signer; let signer = tx.1.signer;
@@ -339,7 +339,7 @@ fn provided_transaction() {
#[tokio::test] #[tokio::test]
async fn tendermint_evidence_tx() { async fn tendermint_evidence_tx() {
let genesis = new_genesis(); let genesis = new_genesis();
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let signer = Signer::new(genesis, key.clone()); let signer = Signer::new(genesis, key.clone());
let signer_id = Ristretto::generator() * key.deref(); let signer_id = Ristretto::generator() * key.deref();
let validators = Arc::new(Validators::new(genesis, vec![(signer_id, 1)]).unwrap()); let validators = Arc::new(Validators::new(genesis, vec![(signer_id, 1)]).unwrap());
@@ -379,7 +379,7 @@ async fn tendermint_evidence_tx() {
let mut mempool: Vec<Transaction<SignedTransaction>> = vec![]; let mut mempool: Vec<Transaction<SignedTransaction>> = vec![];
let mut signers = vec![]; let mut signers = vec![];
for _ in 0 .. 5 { for _ in 0 .. 5 {
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let signer = Signer::new(genesis, key.clone()); let signer = Signer::new(genesis, key.clone());
let signer_id = Ristretto::generator() * key.deref(); let signer_id = Ristretto::generator() * key.deref();
signers.push((signer_id, 1)); signers.push((signer_id, 1));
@@ -446,7 +446,7 @@ async fn block_tx_ordering() {
} }
let genesis = new_genesis(); let genesis = new_genesis();
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
// signer // signer
let signer = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0).1.signer; let signer = crate::tests::signed_transaction(&mut OsRng, genesis, &key, 0).1.signer;

View File

@@ -4,7 +4,7 @@ use zeroize::Zeroizing;
use rand::{RngCore, rngs::OsRng}; use rand::{RngCore, rngs::OsRng};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::Field, Ciphersuite}; use ciphersuite::*;
use tendermint::ext::Commit; use tendermint::ext::Commit;
@@ -33,7 +33,7 @@ async fn mempool_addition() {
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] }) Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
}; };
let unsigned_in_chain = |_: [u8; 32]| false; let unsigned_in_chain = |_: [u8; 32]| false;
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let first_tx = signed_transaction(&mut OsRng, genesis, &key, 0); let first_tx = signed_transaction(&mut OsRng, genesis, &key, 0);
let signer = first_tx.1.signer; let signer = first_tx.1.signer;
@@ -125,7 +125,7 @@ async fn mempool_addition() {
// If the mempool doesn't have a nonce for an account, it should successfully use the // If the mempool doesn't have a nonce for an account, it should successfully use the
// blockchain's // blockchain's
let second_key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let second_key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let tx = signed_transaction(&mut OsRng, genesis, &second_key, 2); let tx = signed_transaction(&mut OsRng, genesis, &second_key, 2);
let second_signer = tx.1.signer; let second_signer = tx.1.signer;
assert_eq!(mempool.next_nonce_in_mempool(&second_signer, vec![]), None); assert_eq!(mempool.next_nonce_in_mempool(&second_signer, vec![]), None);
@@ -165,7 +165,7 @@ fn too_many_mempool() {
Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] }) Some(Commit::<Arc<Validators>> { end_time: 0, validators: vec![], signature: vec![] })
}; };
let unsigned_in_chain = |_: [u8; 32]| false; let unsigned_in_chain = |_: [u8; 32]| false;
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
// We should be able to add transactions up to the limit // We should be able to add transactions up to the limit
for i in 0 .. ACCOUNT_MEMPOOL_LIMIT { for i in 0 .. ACCOUNT_MEMPOOL_LIMIT {

View File

@@ -7,10 +7,7 @@ use rand::{RngCore, CryptoRng, rngs::OsRng};
use blake2::{Digest, Blake2s256}; use blake2::{Digest, Blake2s256};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{ use ciphersuite::{group::Group, *};
group::{ff::Field, Group},
Ciphersuite,
};
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
use scale::Encode; use scale::Encode;
@@ -34,11 +31,11 @@ mod tendermint;
pub fn random_signed<R: RngCore + CryptoRng>(rng: &mut R) -> Signed { pub fn random_signed<R: RngCore + CryptoRng>(rng: &mut R) -> Signed {
Signed { Signed {
signer: <Ristretto as Ciphersuite>::G::random(&mut *rng), signer: <Ristretto as WrappedGroup>::G::random(&mut *rng),
nonce: u32::try_from(rng.next_u64() >> 32 >> 1).unwrap(), nonce: u32::try_from(rng.next_u64() >> 32 >> 1).unwrap(),
signature: SchnorrSignature::<Ristretto> { signature: SchnorrSignature::<Ristretto> {
R: <Ristretto as Ciphersuite>::G::random(&mut *rng), R: <Ristretto as WrappedGroup>::G::random(&mut *rng),
s: <Ristretto as Ciphersuite>::F::random(rng), s: <Ristretto as WrappedGroup>::F::random(rng),
}, },
} }
} }
@@ -137,18 +134,18 @@ impl Transaction for SignedTransaction {
pub fn signed_transaction<R: RngCore + CryptoRng>( pub fn signed_transaction<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
genesis: [u8; 32], genesis: [u8; 32],
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
nonce: u32, nonce: u32,
) -> SignedTransaction { ) -> SignedTransaction {
let mut data = vec![0; 512]; let mut data = vec![0; 512];
rng.fill_bytes(&mut data); rng.fill_bytes(&mut data);
let signer = <Ristretto as Ciphersuite>::generator() * **key; let signer = <Ristretto as WrappedGroup>::generator() * **key;
let mut tx = let mut tx =
SignedTransaction(data, Signed { signer, nonce, signature: random_signed(rng).signature }); SignedTransaction(data, Signed { signer, nonce, signature: random_signed(rng).signature });
let sig_nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(rng)); let sig_nonce = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(rng));
tx.1.signature.R = Ristretto::generator() * sig_nonce.deref(); tx.1.signature.R = Ristretto::generator() * sig_nonce.deref();
tx.1.signature = SchnorrSignature::sign(key, sig_nonce, tx.sig_hash(genesis)); tx.1.signature = SchnorrSignature::sign(key, sig_nonce, tx.sig_hash(genesis));
@@ -163,7 +160,7 @@ pub fn random_signed_transaction<R: RngCore + CryptoRng>(
let mut genesis = [0; 32]; let mut genesis = [0; 32];
rng.fill_bytes(&mut genesis); rng.fill_bytes(&mut genesis);
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut *rng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut *rng));
// Shift over an additional bit to ensure it won't overflow when incremented // Shift over an additional bit to ensure it won't overflow when incremented
let nonce = u32::try_from(rng.next_u64() >> 32 >> 1).unwrap(); let nonce = u32::try_from(rng.next_u64() >> 32 >> 1).unwrap();
@@ -180,12 +177,11 @@ pub async fn tendermint_meta() -> ([u8; 32], Signer, [u8; 32], Arc<Validators>)
// signer // signer
let genesis = new_genesis(); let genesis = new_genesis();
let signer = let signer =
Signer::new(genesis, Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng))); Signer::new(genesis, Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng)));
let validator_id = signer.validator_id().await.unwrap(); let validator_id = signer.validator_id().await.unwrap();
// schema // schema
let signer_pub = let signer_pub = <Ristretto as GroupIo>::read_G::<&[u8]>(&mut validator_id.as_slice()).unwrap();
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut validator_id.as_slice()).unwrap();
let validators = Arc::new(Validators::new(genesis, vec![(signer_pub, 1)]).unwrap()); let validators = Arc::new(Validators::new(genesis, vec![(signer_pub, 1)]).unwrap());
(genesis, signer, validator_id, validators) (genesis, signer, validator_id, validators)

View File

@@ -3,7 +3,7 @@ use rand::rngs::OsRng;
use blake2::{Digest, Blake2s256}; use blake2::{Digest, Blake2s256};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::Field, Ciphersuite}; use ciphersuite::*;
use crate::{ use crate::{
ReadWrite, ReadWrite,
@@ -69,7 +69,7 @@ fn signed_transaction() {
} }
{ {
let mut tx = tx.clone(); let mut tx = tx.clone();
tx.1.signature.s += <Ristretto as Ciphersuite>::F::ONE; tx.1.signature.s += <Ristretto as WrappedGroup>::F::ONE;
assert!(verify_transaction(&tx, genesis, &mut |_, _| Some(tx.1.nonce)).is_err()); assert!(verify_transaction(&tx, genesis, &mut |_, _| Some(tx.1.nonce)).is_err());
} }

View File

@@ -4,7 +4,7 @@ use zeroize::Zeroizing;
use rand::{RngCore, rngs::OsRng}; use rand::{RngCore, rngs::OsRng};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{Ciphersuite, group::ff::Field}; use ciphersuite::*;
use scale::Encode; use scale::Encode;
@@ -261,7 +261,7 @@ async fn conflicting_msgs_evidence_tx() {
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await; let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
let signer_2 = let signer_2 =
Signer::new(genesis, Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng))); Signer::new(genesis, Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng)));
let signed_id_2 = signer_2.validator_id().await.unwrap(); let signed_id_2 = signer_2.validator_id().await.unwrap();
let signed_2 = signed_from_data::<N>( let signed_2 = signed_from_data::<N>(
signer_2.into(), signer_2.into(),
@@ -278,10 +278,9 @@ async fn conflicting_msgs_evidence_tx() {
)); ));
// update schema so that we don't fail due to invalid signature // update schema so that we don't fail due to invalid signature
let signer_pub = let signer_pub = <Ristretto as GroupIo>::read_G::<&[u8]>(&mut signer_id.as_slice()).unwrap();
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut signer_id.as_slice()).unwrap();
let signer_pub_2 = let signer_pub_2 =
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut signed_id_2.as_slice()).unwrap(); <Ristretto as GroupIo>::read_G::<&[u8]>(&mut signed_id_2.as_slice()).unwrap();
let validators = let validators =
Arc::new(Validators::new(genesis, vec![(signer_pub, 1), (signer_pub_2, 1)]).unwrap()); Arc::new(Validators::new(genesis, vec![(signer_pub, 1), (signer_pub_2, 1)]).unwrap());

View File

@@ -8,7 +8,7 @@ use blake2::{Digest, Blake2b512};
use ciphersuite::{ use ciphersuite::{
group::{Group, GroupEncoding}, group::{Group, GroupEncoding},
Ciphersuite, *,
}; };
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
@@ -43,7 +43,7 @@ pub enum TransactionError {
/// Data for a signed transaction. /// Data for a signed transaction.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Signed { pub struct Signed {
pub signer: <Ristretto as Ciphersuite>::G, pub signer: <Ristretto as WrappedGroup>::G,
pub nonce: u32, pub nonce: u32,
pub signature: SchnorrSignature<Ristretto>, pub signature: SchnorrSignature<Ristretto>,
} }
@@ -160,10 +160,10 @@ pub trait Transaction: 'static + Send + Sync + Clone + Eq + Debug + ReadWrite {
/// Do not override this unless you know what you're doing. /// Do not override this unless you know what you're doing.
/// ///
/// Panics if called on non-signed transactions. /// Panics if called on non-signed transactions.
fn sig_hash(&self, genesis: [u8; 32]) -> <Ristretto as Ciphersuite>::F { fn sig_hash(&self, genesis: [u8; 32]) -> <Ristretto as WrappedGroup>::F {
match self.kind() { match self.kind() {
TransactionKind::Signed(order, Signed { signature, .. }) => { TransactionKind::Signed(order, Signed { signature, .. }) => {
<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide( <Ristretto as WrappedGroup>::F::from_bytes_mod_order_wide(
&Blake2b512::digest( &Blake2b512::digest(
[ [
b"Tributary Signed Transaction", b"Tributary Signed Transaction",
@@ -182,8 +182,8 @@ pub trait Transaction: 'static + Send + Sync + Clone + Eq + Debug + ReadWrite {
} }
} }
pub trait GAIN: FnMut(&<Ristretto as Ciphersuite>::G, &[u8]) -> Option<u32> {} pub trait GAIN: FnMut(&<Ristretto as WrappedGroup>::G, &[u8]) -> Option<u32> {}
impl<F: FnMut(&<Ristretto as Ciphersuite>::G, &[u8]) -> Option<u32>> GAIN for F {} impl<F: FnMut(&<Ristretto as WrappedGroup>::G, &[u8]) -> Option<u32>> GAIN for F {}
pub(crate) fn verify_transaction<F: GAIN, T: Transaction>( pub(crate) fn verify_transaction<F: GAIN, T: Transaction>(
tx: &T, tx: &T,

View File

@@ -6,8 +6,8 @@ use rand_core::{RngCore, CryptoRng};
use blake2::{digest::typenum::U32, Digest, Blake2b}; use blake2::{digest::typenum::U32, Digest, Blake2b};
use ciphersuite::{ use ciphersuite::{
group::{ff::Field, Group, GroupEncoding}, group::{Group, GroupEncoding},
Ciphersuite, *,
}; };
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
@@ -52,7 +52,7 @@ impl SigningProtocolRound {
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Signed { pub struct Signed {
/// The signer. /// The signer.
signer: <Ristretto as Ciphersuite>::G, signer: <Ristretto as WrappedGroup>::G,
/// The signature. /// The signature.
signature: SchnorrSignature<Ristretto>, signature: SchnorrSignature<Ristretto>,
} }
@@ -73,7 +73,7 @@ impl BorshDeserialize for Signed {
impl Signed { impl Signed {
/// Fetch the signer. /// Fetch the signer.
pub(crate) fn signer(&self) -> <Ristretto as Ciphersuite>::G { pub(crate) fn signer(&self) -> <Ristretto as WrappedGroup>::G {
self.signer self.signer
} }
@@ -86,10 +86,10 @@ impl Signed {
impl Default for Signed { impl Default for Signed {
fn default() -> Self { fn default() -> Self {
Self { Self {
signer: <Ristretto as Ciphersuite>::G::identity(), signer: <Ristretto as WrappedGroup>::G::identity(),
signature: SchnorrSignature { signature: SchnorrSignature {
R: <Ristretto as Ciphersuite>::G::identity(), R: <Ristretto as WrappedGroup>::G::identity(),
s: <Ristretto as Ciphersuite>::F::ZERO, s: <Ristretto as WrappedGroup>::F::ZERO,
}, },
} }
} }
@@ -356,7 +356,7 @@ impl Transaction {
&mut self, &mut self,
rng: &mut R, rng: &mut R,
genesis: [u8; 32], genesis: [u8; 32],
key: &Zeroizing<<Ristretto as Ciphersuite>::F>, key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
) { ) {
fn signed(tx: &mut Transaction) -> &mut Signed { fn signed(tx: &mut Transaction) -> &mut Signed {
#[allow(clippy::match_same_arms)] // This doesn't make semantic sense here #[allow(clippy::match_same_arms)] // This doesn't make semantic sense here
@@ -380,13 +380,13 @@ impl Transaction {
} }
// Decide the nonce to sign with // Decide the nonce to sign with
let sig_nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(rng)); let sig_nonce = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(rng));
{ {
// Set the signer and the nonce // Set the signer and the nonce
let signed = signed(self); let signed = signed(self);
signed.signer = Ristretto::generator() * key.deref(); signed.signer = Ristretto::generator() * key.deref();
signed.signature.R = <Ristretto as Ciphersuite>::generator() * sig_nonce.deref(); signed.signature.R = <Ristretto as WrappedGroup>::generator() * sig_nonce.deref();
} }
// Get the signature hash (which now includes `R || A` making it valid as the challenge) // Get the signature hash (which now includes `R || A` making it valid as the challenge)

View File

@@ -17,15 +17,12 @@ rustdoc-args = ["--cfg", "docsrs"]
workspace = true workspace = true
[dependencies] [dependencies]
std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-features = false, optional = true } std-shims = { path = "../../common/std-shims", version = "0.1.4", default-features = false, optional = true }
rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["derive"] } zeroize = { version = "^1.5", default-features = false, features = ["derive"] }
subtle = { version = "^2.4", default-features = false } subtle = { version = "^2.4", default-features = false }
digest = { version = "0.11.0-rc.0", default-features = false, features = ["block-api"] } digest = { version = "0.11.0-rc.1", default-features = false }
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false }
ff = { version = "0.13", default-features = false, features = ["bits"] } ff = { version = "0.13", default-features = false, features = ["bits"] }
group = { version = "0.13", default-features = false } group = { version = "0.13", default-features = false }
@@ -33,24 +30,18 @@ group = { version = "0.13", default-features = false }
[dev-dependencies] [dev-dependencies]
hex = { version = "0.4", default-features = false, features = ["std"] } hex = { version = "0.4", default-features = false, features = ["std"] }
rand_core = { version = "0.6", default-features = false, features = ["std"] }
ff-group-tests = { version = "0.13", path = "../ff-group-tests" } ff-group-tests = { version = "0.13", path = "../ff-group-tests" }
[features] [features]
alloc = ["std-shims", "digest/alloc", "ff/alloc"] alloc = ["std-shims", "zeroize/alloc", "digest/alloc", "ff/alloc"]
std = [ std = [
"alloc", "alloc",
"std-shims/std", "std-shims/std",
"rand_core/std",
"zeroize/std", "zeroize/std",
"subtle/std", "subtle/std",
"transcript/std",
"ff/std", "ff/std",
] ]

View File

@@ -21,7 +21,7 @@ rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["derive"] } zeroize = { version = "^1.5", default-features = false, features = ["derive"] }
sha2 = { version = "0.11.0-rc.0", default-features = false } sha2 = { version = "0.11.0-rc.2", default-features = false }
p256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"] } p256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"] }
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"] } k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits", "hash2curve"] }

View File

@@ -5,7 +5,7 @@ use zeroize::Zeroize;
use sha2::Sha512; use sha2::Sha512;
use ciphersuite::Ciphersuite; use ciphersuite::{WrappedGroup, Id, WithPreferredHash, GroupCanonicalEncoding};
pub use k256; pub use k256;
pub use p256; pub use p256;
@@ -18,17 +18,20 @@ macro_rules! kp_curve {
$Ciphersuite: ident, $Ciphersuite: ident,
$ID: literal $ID: literal
) => { ) => {
impl Ciphersuite for $Ciphersuite { impl WrappedGroup for $Ciphersuite {
type F = $lib::Scalar; type F = $lib::Scalar;
type G = $lib::ProjectivePoint; type G = $lib::ProjectivePoint;
type H = Sha512;
const ID: &'static [u8] = $ID;
fn generator() -> Self::G { fn generator() -> Self::G {
$lib::ProjectivePoint::GENERATOR $lib::ProjectivePoint::GENERATOR
} }
} }
impl Id for $Ciphersuite {
const ID: &'static [u8] = $ID;
}
impl WithPreferredHash for $Ciphersuite {
type H = Sha512;
}
impl GroupCanonicalEncoding for $Ciphersuite {}
}; };
} }

View File

@@ -9,22 +9,18 @@ use std_shims::prelude::*;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use std_shims::io::{self, Read}; use std_shims::io::{self, Read};
use rand_core::{RngCore, CryptoRng}; use subtle::{CtOption, ConstantTimeEq, ConditionallySelectable};
use zeroize::Zeroize; use zeroize::Zeroize;
use subtle::ConstantTimeEq;
pub use digest; pub use digest;
use digest::{array::ArraySize, block_api::BlockSizeUser, OutputSizeUser, Digest, HashMarker}; use digest::{array::ArraySize, OutputSizeUser, Digest, HashMarker};
use transcript::SecureDigest;
pub use group; pub use group;
use group::{ use group::{
ff::{Field, PrimeField, PrimeFieldBits}, ff::{PrimeField, PrimeFieldBits},
Group, GroupOps, Group, GroupOps,
prime::PrimeGroup, prime::PrimeGroup,
}; };
#[cfg(feature = "alloc")]
use group::GroupEncoding; use group::GroupEncoding;
pub trait FromUniformBytes<T> { pub trait FromUniformBytes<T> {
@@ -36,74 +32,118 @@ impl<const N: usize, F: group::ff::FromUniformBytes<N>> FromUniformBytes<[u8; N]
} }
} }
/// Unified trait defining a ciphersuite around an elliptic curve. /// A marker trait for fields which fleshes them out a bit more.
pub trait Ciphersuite: pub trait F: PrimeField + PrimeFieldBits + Zeroize {}
impl<Fi: PrimeField + PrimeFieldBits + Zeroize> F for Fi {}
/// A marker trait for groups which fleshes them out a bit more.
pub trait G:
Group + GroupOps + GroupEncoding + PrimeGroup + ConstantTimeEq + ConditionallySelectable + Zeroize
{
}
impl<
Gr: Group
+ GroupOps
+ GroupEncoding
+ PrimeGroup
+ ConstantTimeEq
+ ConditionallySelectable
+ Zeroize,
> G for Gr
{
}
/// A `Group` type which has been wrapped into the current type.
///
/// This avoids having to re-implement all of the `Group` traits on the wrapper.
// TODO: Remove these bounds
pub trait WrappedGroup:
'static + Send + Sync + Clone + Copy + PartialEq + Eq + Debug + Zeroize 'static + Send + Sync + Clone + Copy + PartialEq + Eq + Debug + Zeroize
{ {
/// Scalar field element type. /// Scalar field element type.
// This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses // This is available via `G::Scalar` yet `WG::G::Scalar` is ambiguous, forcing horrific accesses
type F: PrimeField type F: F;
+ PrimeFieldBits
+ Zeroize
+ FromUniformBytes<<<Self::H as OutputSizeUser>::OutputSize as ArraySize>::ArrayType<u8>>;
/// Group element type. /// Group element type.
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq; type G: Group<Scalar = Self::F> + G;
/// Hash algorithm used with this curve.
// Requires BlockSizeUser so it can be used within Hkdf which requires that.
type H: Send + Clone + BlockSizeUser + Digest + HashMarker + SecureDigest;
/// ID for this curve.
const ID: &'static [u8];
/// Generator for the group. /// Generator for the group.
// While group does provide this in its API, privacy coins may want to use a custom basepoint
fn generator() -> Self::G; fn generator() -> Self::G;
}
impl<Gr: G<Scalar: F>> WrappedGroup for Gr {
type F = <Gr as Group>::Scalar;
type G = Gr;
fn generator() -> Self::G {
<Self::G as Group>::generator()
}
}
/// An ID for an object.
pub trait Id {
// The ID.
const ID: &'static [u8];
}
/// A group with a preferred hash function.
pub trait WithPreferredHash:
WrappedGroup<
F: FromUniformBytes<<<Self::H as OutputSizeUser>::OutputSize as ArraySize>::ArrayType<u8>>,
>
{
type H: Send + Clone + Digest + HashMarker;
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hash_to_F(data: &[u8]) -> Self::F { fn hash_to_F(data: &[u8]) -> Self::F {
Self::F::from_uniform_bytes(&Self::H::digest(data).into()) Self::F::from_uniform_bytes(&Self::H::digest(data).into())
} }
}
/// Generate a random non-zero scalar. /// A group which always encodes points canonically and supports decoding points while checking
#[allow(non_snake_case)] /// they have a canonical encoding.
fn random_nonzero_F<R: RngCore + CryptoRng>(rng: &mut R) -> Self::F { pub trait GroupCanonicalEncoding: WrappedGroup {
let mut res; /// Decode a point from its canonical encoding.
while {
res = Self::F::random(&mut *rng);
res.ct_eq(&Self::F::ZERO).into()
} {}
res
}
/// Read a canonical scalar from something implementing std::io::Read.
#[cfg(feature = "alloc")]
#[allow(non_snake_case)]
fn read_F<R: Read>(reader: &mut R) -> io::Result<Self::F> {
let mut encoding = <Self::F as PrimeField>::Repr::default();
reader.read_exact(encoding.as_mut())?;
// ff mandates this is canonical
let res = Option::<Self::F>::from(Self::F::from_repr(encoding))
.ok_or_else(|| io::Error::other("non-canonical scalar"));
encoding.as_mut().zeroize();
res
}
/// Read a canonical point from something implementing std::io::Read.
/// ///
/// The provided implementation is safe so long as `GroupEncoding::to_bytes` always returns a /// Returns `None` if the point was invalid or not the encoding wasn't canonical.
/// canonical serialization. ///
/// If `<Self::G as GroupEncoding>::from_bytes` already only accepts canonical encodings, this
/// SHOULD be overriden with `<Self::G as GroupEncoding>::from_bytes(bytes)`.
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
let res = Self::G::from_bytes(bytes).unwrap_or(Self::generator());
// Safe due to the bound points are always encoded canonically
let canonical = res.to_bytes().as_ref().ct_eq(bytes.as_ref());
CtOption::new(res, canonical)
}
}
/// `std::io` extensions for `GroupCanonicalEncoding.`
#[cfg(feature = "alloc")]
#[allow(non_snake_case)]
pub trait GroupIo: GroupCanonicalEncoding {
/// Read a canonical field element from something implementing `std::io::Read`.
fn read_F<R: Read>(reader: &mut R) -> io::Result<Self::F> {
let mut bytes = <Self::F as PrimeField>::Repr::default();
reader.read_exact(bytes.as_mut())?;
// `ff` mandates this is canonical
let res = Option::<Self::F>::from(Self::F::from_repr(bytes))
.ok_or_else(|| io::Error::other("non-canonical scalar"));
bytes.as_mut().zeroize();
res
}
/// Read a canonical point from something implementing `std::io::Read`.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> { fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
let mut encoding = <Self::G as GroupEncoding>::Repr::default(); let mut bytes = <Self::G as GroupEncoding>::Repr::default();
reader.read_exact(encoding.as_mut())?; reader.read_exact(bytes.as_mut())?;
let point = Option::<Self::G>::from(Self::G::from_bytes(&encoding)) let res = Option::<Self::G>::from(Self::from_canonical_bytes(&bytes))
.ok_or_else(|| io::Error::other("invalid point"))?; .ok_or_else(|| io::Error::other("invalid point"))?;
if point.to_bytes().as_ref() != encoding.as_ref() { bytes.as_mut().zeroize();
Err(io::Error::other("non-canonical point"))?;
} Ok(res)
Ok(point)
} }
} }
impl<Gr: GroupCanonicalEncoding> GroupIo for Gr {}
/// Unified trait defining a ciphersuite around an elliptic curve.
pub trait Ciphersuite: Id + WithPreferredHash + GroupCanonicalEncoding {}
impl<C: Id + WithPreferredHash + GroupCanonicalEncoding> Ciphersuite for C {}

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "dalek-ff-group" name = "dalek-ff-group"
version = "0.4.6" version = "0.5.0"
description = "ff/group bindings around curve25519-dalek" description = "ff/group bindings around curve25519-dalek"
license = "MIT" license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group" repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group"
@@ -22,15 +22,13 @@ subtle = { version = "^2.4", default-features = false }
rand_core = { version = "0.6", default-features = false } rand_core = { version = "0.6", default-features = false }
digest = { version = "0.10", default-features = false } sha2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
sha2 = { version = "0.11.0-rc.0", default-features = false } blake2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
prime-field = { path = "../prime-field", default-features = false } prime-field = { path = "../prime-field", default-features = false }
ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false } ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false }
crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] } curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["zeroize", "digest", "group-bits", "precomputed-tables"] }
curve25519-dalek = { version = ">= 4.0, < 4.2", default-features = false, features = ["zeroize", "digest", "group", "precomputed-tables"] }
[dev-dependencies] [dev-dependencies]
hex = "0.4" hex = "0.4"
@@ -38,6 +36,6 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] }
ff-group-tests = { path = "../ff-group-tests" } ff-group-tests = { path = "../ff-group-tests" }
[features] [features]
alloc = ["zeroize/alloc", "digest/alloc", "prime-field/alloc", "ciphersuite/alloc", "crypto-bigint/alloc", "curve25519-dalek/alloc"] alloc = ["zeroize/alloc", "prime-field/alloc", "ciphersuite/alloc", "curve25519-dalek/alloc"]
std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "digest/std", "prime-field/std", "ciphersuite/std"] std = ["alloc", "zeroize/std", "subtle/std", "rand_core/std", "prime-field/std", "ciphersuite/std"]
default = ["std"] default = ["std"]

View File

@@ -1,49 +1,48 @@
use zeroize::Zeroize; use zeroize::Zeroize;
use sha2::Sha512; use sha2::Sha512;
use blake2::Blake2b512;
use ciphersuite::{group::Group, Ciphersuite}; use ::ciphersuite::{group::Group, *};
use crate::Scalar; use crate::*;
macro_rules! dalek_curve {
(
$feature: literal,
$Ciphersuite: ident,
$Point: ident,
$ID: literal
) => {
use crate::$Point;
impl Ciphersuite for $Ciphersuite {
type F = Scalar;
type G = $Point;
type H = Sha512;
const ID: &'static [u8] = $ID;
fn generator() -> Self::G {
$Point::generator()
}
}
};
}
/// Ciphersuite for Ristretto. /// Ciphersuite for Ristretto.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct Ristretto; pub struct Ristretto;
dalek_curve!("ristretto", Ristretto, RistrettoPoint, b"ristretto"); impl WrappedGroup for Ristretto {
#[test] type F = Scalar;
fn test_ristretto() { type G = RistrettoPoint;
ff_group_tests::group::test_prime_group_bits::<_, RistrettoPoint>(&mut rand_core::OsRng); fn generator() -> Self::G {
<RistrettoPoint as Group>::generator()
}
}
impl Id for Ristretto {
const ID: &[u8] = b"ristretto";
}
impl WithPreferredHash for Ristretto {
type H = Blake2b512;
}
impl GroupCanonicalEncoding for Ristretto {
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
Self::G::from_bytes(bytes)
}
} }
/// Ciphersuite for Ed25519, inspired by RFC-8032. /// Ciphersuite for Ed25519.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct Ed25519; pub struct Ed25519;
dalek_curve!("ed25519", Ed25519, EdwardsPoint, b"edwards25519"); impl WrappedGroup for Ed25519 {
#[test] type F = Scalar;
fn test_ed25519() { type G = EdwardsPoint;
ff_group_tests::group::test_prime_group_bits::<_, EdwardsPoint>(&mut rand_core::OsRng); fn generator() -> Self::G {
<EdwardsPoint as Group>::generator()
}
} }
impl Id for Ed25519 {
const ID: &[u8] = b"ed25519";
}
impl WithPreferredHash for Ed25519 {
type H = Sha512;
}
impl GroupCanonicalEncoding for Ed25519 {}

View File

@@ -7,7 +7,7 @@
use core::{ use core::{
borrow::Borrow, borrow::Borrow,
ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign}, ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
iter::{Iterator, Sum, Product}, iter::{Iterator, Sum},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
}; };
@@ -15,25 +15,16 @@ use zeroize::Zeroize;
use subtle::{ConstantTimeEq, ConditionallySelectable}; use subtle::{ConstantTimeEq, ConditionallySelectable};
use rand_core::RngCore; use rand_core::RngCore;
use digest::{consts::U64, Digest, HashMarker};
use subtle::{Choice, CtOption}; use subtle::{Choice, CtOption};
pub use curve25519_dalek as dalek; use curve25519_dalek::{
edwards::{EdwardsPoint as DEdwardsPoint, CompressedEdwardsY},
use dalek::{ ristretto::{RistrettoPoint as DRistrettoPoint, CompressedRistretto},
constants::{self, BASEPOINT_ORDER},
scalar::Scalar as DScalar,
edwards::{EdwardsPoint as DEdwardsPoint, EdwardsBasepointTable, CompressedEdwardsY},
ristretto::{RistrettoPoint as DRistrettoPoint, RistrettoBasepointTable, CompressedRistretto},
}; };
pub use constants::{ED25519_BASEPOINT_TABLE, RISTRETTO_BASEPOINT_TABLE}; pub use curve25519_dalek::Scalar;
use ::ciphersuite::group::{ use ::ciphersuite::group::{Group, GroupEncoding, prime::PrimeGroup};
ff::{Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes},
Group, GroupEncoding,
prime::PrimeGroup,
};
mod ciphersuite; mod ciphersuite;
pub use crate::ciphersuite::{Ed25519, Ristretto}; pub use crate::ciphersuite::{Ed25519, Ristretto};
@@ -97,7 +88,41 @@ macro_rules! constant_time {
} }
}; };
} }
pub(crate) use constant_time;
macro_rules! math_op_without_wrapping {
(
$Value: ident,
$Other: ident,
$Op: ident,
$op_fn: ident,
$Assign: ident,
$assign_fn: ident,
$function: expr
) => {
impl $Op<$Other> for $Value {
type Output = $Value;
fn $op_fn(self, other: $Other) -> Self::Output {
Self($function(self.0, other))
}
}
impl $Assign<$Other> for $Value {
fn $assign_fn(&mut self, other: $Other) {
self.0 = $function(self.0, other);
}
}
impl<'a> $Op<&'a $Other> for $Value {
type Output = $Value;
fn $op_fn(self, other: &'a $Other) -> Self::Output {
Self($function(self.0, other))
}
}
impl<'a> $Assign<&'a $Other> for $Value {
fn $assign_fn(&mut self, other: &'a $Other) {
self.0 = $function(self.0, other);
}
}
};
}
macro_rules! math_op { macro_rules! math_op {
( (
@@ -133,20 +158,12 @@ macro_rules! math_op {
} }
}; };
} }
pub(crate) use math_op;
macro_rules! math {
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
math_op!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
};
}
pub(crate) use math;
macro_rules! math_neg { macro_rules! math_neg {
($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => { ($Value: ident, $Factor: ident, $add: expr, $sub: expr, $mul: expr) => {
math!($Value, $Factor, $add, $sub, $mul); math_op!($Value, $Value, Add, add, AddAssign, add_assign, $add);
math_op!($Value, $Value, Sub, sub, SubAssign, sub_assign, $sub);
math_op_without_wrapping!($Value, $Factor, Mul, mul, MulAssign, mul_assign, $mul);
impl Neg for $Value { impl Neg for $Value {
type Output = Self; type Output = Self;
@@ -157,187 +174,6 @@ macro_rules! math_neg {
}; };
} }
/// Wrapper around the dalek Scalar type.
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Zeroize)]
pub struct Scalar(pub DScalar);
deref_borrow!(Scalar, DScalar);
constant_time!(Scalar, DScalar);
math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul);
macro_rules! from_wrapper {
($uint: ident) => {
impl From<$uint> for Scalar {
fn from(a: $uint) -> Scalar {
Scalar(DScalar::from(a))
}
}
};
}
from_wrapper!(u8);
from_wrapper!(u16);
from_wrapper!(u32);
from_wrapper!(u64);
from_wrapper!(u128);
impl Scalar {
pub fn pow(&self, other: Scalar) -> Scalar {
let mut table = [Scalar::ONE; 16];
table[1] = *self;
for i in 2 .. 16 {
table[i] = table[i - 1] * self;
}
let mut res = Scalar::ONE;
let mut bits = 0;
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
bits <<= 1;
let mut bit = u8_from_bool(&mut bit);
bits |= bit;
bit.zeroize();
if ((i + 1) % 4) == 0 {
if i != 3 {
for _ in 0 .. 4 {
res *= res;
}
}
let mut scale_by = Scalar::ONE;
#[allow(clippy::needless_range_loop)]
for i in 0 .. 16 {
#[allow(clippy::cast_possible_truncation)] // Safe since 0 .. 16
{
scale_by = <_>::conditional_select(&scale_by, &table[i], bits.ct_eq(&(i as u8)));
}
}
res *= scale_by;
bits = 0;
}
}
res
}
/// Perform wide reduction on a 64-byte array to create a Scalar without bias.
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
Self(DScalar::from_bytes_mod_order_wide(bytes))
}
/// Derive a Scalar without bias from a digest via wide reduction.
pub fn from_hash<D: Digest<OutputSize = U64> + HashMarker>(hash: D) -> Scalar {
let mut output = [0u8; 64];
output.copy_from_slice(&hash.finalize());
let res = Scalar(DScalar::from_bytes_mod_order_wide(&output));
output.zeroize();
res
}
}
impl Field for Scalar {
const ZERO: Scalar = Scalar(DScalar::ZERO);
const ONE: Scalar = Scalar(DScalar::ONE);
fn random(rng: impl RngCore) -> Self {
Self(<DScalar as Field>::random(rng))
}
fn square(&self) -> Self {
Self(self.0.square())
}
fn double(&self) -> Self {
Self(self.0.double())
}
fn invert(&self) -> CtOption<Self> {
<DScalar as Field>::invert(&self.0).map(Self)
}
fn sqrt(&self) -> CtOption<Self> {
self.0.sqrt().map(Self)
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
let (choice, res) = DScalar::sqrt_ratio(num, div);
(choice, Self(res))
}
}
impl PrimeField for Scalar {
type Repr = [u8; 32];
const MODULUS: &'static str = <DScalar as PrimeField>::MODULUS;
const NUM_BITS: u32 = <DScalar as PrimeField>::NUM_BITS;
const CAPACITY: u32 = <DScalar as PrimeField>::CAPACITY;
const TWO_INV: Scalar = Scalar(<DScalar as PrimeField>::TWO_INV);
const MULTIPLICATIVE_GENERATOR: Scalar =
Scalar(<DScalar as PrimeField>::MULTIPLICATIVE_GENERATOR);
const S: u32 = <DScalar as PrimeField>::S;
const ROOT_OF_UNITY: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY);
const ROOT_OF_UNITY_INV: Scalar = Scalar(<DScalar as PrimeField>::ROOT_OF_UNITY_INV);
const DELTA: Scalar = Scalar(<DScalar as PrimeField>::DELTA);
fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
<DScalar as PrimeField>::from_repr(bytes).map(Scalar)
}
fn to_repr(&self) -> [u8; 32] {
self.0.to_repr()
}
fn is_odd(&self) -> Choice {
self.0.is_odd()
}
fn from_u128(num: u128) -> Self {
Scalar(DScalar::from_u128(num))
}
}
impl PrimeFieldBits for Scalar {
type ReprBits = [u8; 32];
fn to_le_bits(&self) -> FieldBits<Self::ReprBits> {
self.to_repr().into()
}
fn char_le_bits() -> FieldBits<Self::ReprBits> {
BASEPOINT_ORDER.to_bytes().into()
}
}
impl FromUniformBytes<64> for Scalar {
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
Self::from_bytes_mod_order_wide(bytes)
}
}
impl Sum<Scalar> for Scalar {
fn sum<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
Self(DScalar::sum(iter))
}
}
impl<'a> Sum<&'a Scalar> for Scalar {
fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
Self(DScalar::sum(iter))
}
}
impl Product<Scalar> for Scalar {
fn product<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
Self(DScalar::product(iter))
}
}
impl<'a> Product<&'a Scalar> for Scalar {
fn product<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
Self(DScalar::product(iter))
}
}
macro_rules! dalek_group { macro_rules! dalek_group {
( (
$Point: ident, $Point: ident,
@@ -347,9 +183,6 @@ macro_rules! dalek_group {
$Table: ident, $Table: ident,
$DCompressed: ident, $DCompressed: ident,
$BASEPOINT_POINT: ident,
$BASEPOINT_TABLE: ident
) => { ) => {
/// Wrapper around the dalek Point type. /// Wrapper around the dalek Point type.
/// ///
@@ -363,9 +196,6 @@ macro_rules! dalek_group {
constant_time!($Point, $DPoint); constant_time!($Point, $DPoint);
math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul); math_neg!($Point, Scalar, $DPoint::add, $DPoint::sub, $DPoint::mul);
/// The basepoint for this curve.
pub const $BASEPOINT_POINT: $Point = $Point(constants::$BASEPOINT_POINT);
impl Sum<$Point> for $Point { impl Sum<$Point> for $Point {
fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point { fn sum<I: Iterator<Item = $Point>>(iter: I) -> $Point {
Self($DPoint::sum(iter)) Self($DPoint::sum(iter))
@@ -396,7 +226,7 @@ macro_rules! dalek_group {
Self($DPoint::identity()) Self($DPoint::identity())
} }
fn generator() -> Self { fn generator() -> Self {
$BASEPOINT_POINT Self(<$DPoint as Group>::generator())
} }
fn is_identity(&self) -> Choice { fn is_identity(&self) -> Choice {
self.0.ct_eq(&$DPoint::identity()) self.0.ct_eq(&$DPoint::identity())
@@ -430,13 +260,6 @@ macro_rules! dalek_group {
impl PrimeGroup for $Point {} impl PrimeGroup for $Point {}
impl Mul<Scalar> for &$Table {
type Output = $Point;
fn mul(self, b: Scalar) -> $Point {
$Point(&b.0 * self)
}
}
// Support being used as a key in a table // Support being used as a key in a table
// While it is expensive as a key, due to the field operations required, there's frequently // While it is expensive as a key, due to the field operations required, there's frequently
// use cases for public key -> value lookups // use cases for public key -> value lookups
@@ -456,24 +279,14 @@ dalek_group!(
|point: DEdwardsPoint| point.is_torsion_free(), |point: DEdwardsPoint| point.is_torsion_free(),
EdwardsBasepointTable, EdwardsBasepointTable,
CompressedEdwardsY, CompressedEdwardsY,
ED25519_BASEPOINT_POINT,
ED25519_BASEPOINT_TABLE
); );
impl EdwardsPoint {
pub fn mul_by_cofactor(&self) -> EdwardsPoint {
EdwardsPoint(self.0.mul_by_cofactor())
}
}
dalek_group!( dalek_group!(
RistrettoPoint, RistrettoPoint,
DRistrettoPoint, DRistrettoPoint,
|_| true, |_| true,
RistrettoBasepointTable, RistrettoBasepointTable,
CompressedRistretto, CompressedRistretto,
RISTRETTO_BASEPOINT_POINT,
RISTRETTO_BASEPOINT_TABLE
); );
#[test] #[test]

View File

@@ -10,12 +10,12 @@ use rand_core::{RngCore, CryptoRng};
use ciphersuite::{ use ciphersuite::{
group::ff::{Field, PrimeField}, group::ff::{Field, PrimeField},
Ciphersuite, GroupIo, Id,
}; };
pub use dkg::*; pub use dkg::*;
/// Create a key via a dealer key generation protocol. /// Create a key via a dealer key generation protocol.
pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>( pub fn key_gen<R: RngCore + CryptoRng, C: GroupIo + Id>(
rng: &mut R, rng: &mut R,
threshold: u16, threshold: u16,
participants: u16, participants: u16,

View File

@@ -31,13 +31,13 @@ ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features
multiexp = { path = "../../multiexp", version = "0.4", default-features = false } multiexp = { path = "../../multiexp", version = "0.4", default-features = false }
generic-array = { version = "1", default-features = false, features = ["alloc"] } generic-array = { version = "1", default-features = false, features = ["alloc"] }
blake2 = { version = "0.11.0-rc.0", default-features = false } blake2 = { version = "0.11.0-rc.2", default-features = false }
rand_chacha = { version = "0.3", default-features = false } rand_chacha = { version = "0.3", default-features = false }
generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false } generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false } ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
generalized-bulletproofs-circuit-abstraction = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false } generalized-bulletproofs-circuit-abstraction = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false } generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false }
dkg = { path = "..", default-features = false } dkg = { path = "..", default-features = false }
@@ -52,7 +52,7 @@ rand = { version = "0.8", default-features = false, features = ["std"] }
ciphersuite = { path = "../../ciphersuite", default-features = false, features = ["std"] } ciphersuite = { path = "../../ciphersuite", default-features = false, features = ["std"] }
embedwards25519 = { path = "../../embedwards25519", default-features = false, features = ["std"] } embedwards25519 = { path = "../../embedwards25519", default-features = false, features = ["std"] }
dalek-ff-group = { path = "../../dalek-ff-group", default-features = false, features = ["std"] } dalek-ff-group = { path = "../../dalek-ff-group", default-features = false, features = ["std"] }
generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", features = ["tests"] } generalized-bulletproofs = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", features = ["tests"] }
dkg-recovery = { path = "../recovery" } dkg-recovery = { path = "../recovery" }
[features] [features]
@@ -86,6 +86,5 @@ std = [
] ]
secp256k1 = ["ciphersuite-kp256", "secq256k1"] secp256k1 = ["ciphersuite-kp256", "secq256k1"]
ed25519 = ["dalek-ff-group", "embedwards25519"] ed25519 = ["dalek-ff-group", "embedwards25519"]
ristretto = ["dalek-ff-group", "embedwards25519"]
tests = ["rand_core/getrandom"] tests = ["rand_core/getrandom"]
default = ["std"] default = ["std"]

View File

@@ -17,7 +17,7 @@ type Blake2s256Keyed = Blake2sMac<U32>;
use ciphersuite::{ use ciphersuite::{
group::{ff::FromUniformBytes, GroupEncoding}, group::{ff::FromUniformBytes, GroupEncoding},
Ciphersuite, WrappedGroup, Id, GroupIo,
}; };
use ec_divisors::DivisorCurve; use ec_divisors::DivisorCurve;
@@ -27,10 +27,10 @@ use generalized_bulletproofs_ec_gadgets::*;
/// A pair of curves to perform the eVRF with. /// A pair of curves to perform the eVRF with.
pub trait Curves { pub trait Curves {
/// The towering curve, for which the resulting key is on. /// The towering curve, for which the resulting key is on.
type ToweringCurve: Ciphersuite<F: FromUniformBytes<64>>; type ToweringCurve: Id + GroupIo<F: FromUniformBytes<64>>;
/// The embedded curve which participants represent their public keys over. /// The embedded curve which participants represent their public keys over.
type EmbeddedCurve: Ciphersuite< type EmbeddedCurve: GroupIo<
G: DivisorCurve<FieldElement = <Self::ToweringCurve as Ciphersuite>::F>, G: DivisorCurve<FieldElement = <Self::ToweringCurve as WrappedGroup>::F>,
>; >;
/// The parameters to use the embedded curve with the discrete-log gadget. /// The parameters to use the embedded curve with the discrete-log gadget.
type EmbeddedCurveParameters: DiscreteLogParameters; type EmbeddedCurveParameters: DiscreteLogParameters;
@@ -49,14 +49,14 @@ impl<C: Curves> Generators<C> {
pub fn new(max_threshold: u16, max_participants: u16) -> Generators<C> { pub fn new(max_threshold: u16, max_participants: u16) -> Generators<C> {
let entropy = <Blake2s256Keyed as KeyInit>::new(&{ let entropy = <Blake2s256Keyed as KeyInit>::new(&{
let mut key = Array::<u8, <Blake2s256Keyed as KeySizeUser>::KeySize>::default(); let mut key = Array::<u8, <Blake2s256Keyed as KeySizeUser>::KeySize>::default();
let key_len = key.len().min(<C::ToweringCurve as Ciphersuite>::ID.len()); let key_len = key.len().min(<C::ToweringCurve as Id>::ID.len());
{ {
let key: &mut [u8] = key.as_mut(); let key: &mut [u8] = key.as_mut();
key[.. key_len].copy_from_slice(&<C::ToweringCurve as Ciphersuite>::ID[.. key_len]) key[.. key_len].copy_from_slice(&<C::ToweringCurve as Id>::ID[.. key_len])
} }
key key
}) })
.chain_update(<C::ToweringCurve as Ciphersuite>::generator().to_bytes()) .chain_update(<C::ToweringCurve as WrappedGroup>::generator().to_bytes())
.finalize() .finalize()
.into_bytes(); .into_bytes();
let mut rng = ChaCha20Rng::from_seed(entropy.into()); let mut rng = ChaCha20Rng::from_seed(entropy.into());
@@ -71,7 +71,8 @@ impl<C: Curves> Generators<C> {
h_bold.push(crate::sample_point::<C::ToweringCurve>(&mut rng)); h_bold.push(crate::sample_point::<C::ToweringCurve>(&mut rng));
} }
Self( Self(
BpGenerators::new(<C::ToweringCurve as Ciphersuite>::generator(), h, g_bold, h_bold).unwrap(), BpGenerators::new(<C::ToweringCurve as WrappedGroup>::generator(), h, g_bold, h_bold)
.unwrap(),
) )
} }
} }
@@ -95,13 +96,3 @@ impl Curves for Ed25519 {
type EmbeddedCurve = embedwards25519::Embedwards25519; type EmbeddedCurve = embedwards25519::Embedwards25519;
type EmbeddedCurveParameters = embedwards25519::Embedwards25519; type EmbeddedCurveParameters = embedwards25519::Embedwards25519;
} }
/// Ristretto, and an elliptic curve defined over its scalar field (embedwards25519).
#[cfg(any(test, feature = "ristretto"))]
pub struct Ristretto;
#[cfg(any(test, feature = "ristretto"))]
impl Curves for Ristretto {
type ToweringCurve = dalek_ff_group::Ristretto;
type EmbeddedCurve = embedwards25519::Embedwards25519;
type EmbeddedCurveParameters = embedwards25519::Embedwards25519;
}

View File

@@ -21,7 +21,7 @@ use ciphersuite::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
Group, GroupEncoding, Group, GroupEncoding,
}, },
Ciphersuite, WrappedGroup, GroupIo,
}; };
use multiexp::multiexp_vartime; use multiexp::multiexp_vartime;
@@ -49,7 +49,7 @@ mod tests;
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Participation<C: Curves> { pub struct Participation<C: Curves> {
proof: Vec<u8>, proof: Vec<u8>,
encrypted_secret_shares: HashMap<Participant, <C::ToweringCurve as Ciphersuite>::F>, encrypted_secret_shares: HashMap<Participant, <C::ToweringCurve as WrappedGroup>::F>,
} }
impl<C: Curves> Participation<C> { impl<C: Curves> Participation<C> {
@@ -79,7 +79,7 @@ impl<C: Curves> Participation<C> {
let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n)); let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n));
for i in Participant::iter().take(usize::from(n)) { for i in Participant::iter().take(usize::from(n)) {
encrypted_secret_shares.insert(i, <C::ToweringCurve as Ciphersuite>::read_F(reader)?); encrypted_secret_shares.insert(i, <C::ToweringCurve as GroupIo>::read_F(reader)?);
} }
Ok(Self { proof, encrypted_secret_shares }) Ok(Self { proof, encrypted_secret_shares })
@@ -151,14 +151,14 @@ pub enum VerifyResult<C: Curves> {
pub struct Dkg<C: Curves> { pub struct Dkg<C: Curves> {
t: u16, t: u16,
n: u16, n: u16,
evrf_public_keys: Vec<<C::EmbeddedCurve as Ciphersuite>::G>, evrf_public_keys: Vec<<C::EmbeddedCurve as WrappedGroup>::G>,
verification_shares: HashMap<Participant, <C::ToweringCurve as Ciphersuite>::G>, verification_shares: HashMap<Participant, <C::ToweringCurve as WrappedGroup>::G>,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
encrypted_secret_shares: HashMap< encrypted_secret_shares: HashMap<
Participant, Participant,
HashMap< HashMap<
Participant, Participant,
([<C::EmbeddedCurve as Ciphersuite>::G; 2], <C::ToweringCurve as Ciphersuite>::F), ([<C::EmbeddedCurve as WrappedGroup>::G; 2], <C::ToweringCurve as WrappedGroup>::F),
>, >,
>, >,
} }
@@ -167,7 +167,7 @@ impl<C: Curves> Dkg<C> {
// Form the initial transcript for the proofs. // Form the initial transcript for the proofs.
fn initial_transcript( fn initial_transcript(
invocation: [u8; 32], invocation: [u8; 32],
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G], evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
t: u16, t: u16,
) -> [u8; 32] { ) -> [u8; 32] {
let mut transcript = Blake2s256::new(); let mut transcript = Blake2s256::new();
@@ -188,8 +188,8 @@ impl<C: Curves> Dkg<C> {
generators: &Generators<C>, generators: &Generators<C>,
context: [u8; 32], context: [u8; 32],
t: u16, t: u16,
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G], evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>, evrf_private_key: &Zeroizing<<C::EmbeddedCurve as WrappedGroup>::F>,
) -> Result<Participation<C>, Error> { ) -> Result<Participation<C>, Error> {
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { let Ok(n) = u16::try_from(evrf_public_keys.len()) else {
Err(Error::TooManyParticipants { provided: evrf_public_keys.len() })? Err(Error::TooManyParticipants { provided: evrf_public_keys.len() })?
@@ -202,7 +202,8 @@ impl<C: Curves> Dkg<C> {
}; };
// This also ensures the private key is not 0, due to the prior check the identity point wasn't // This also ensures the private key is not 0, due to the prior check the identity point wasn't
// present // present
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref(); let evrf_public_key =
<C::EmbeddedCurve as WrappedGroup>::generator() * evrf_private_key.deref();
if !evrf_public_keys.contains(&evrf_public_key) { if !evrf_public_keys.contains(&evrf_public_key) {
Err(Error::NotAParticipant)?; Err(Error::NotAParticipant)?;
}; };
@@ -231,7 +232,7 @@ impl<C: Curves> Dkg<C> {
let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n)); let mut encrypted_secret_shares = HashMap::with_capacity(usize::from(n));
for (l, encryption_key) in Participant::iter().take(usize::from(n)).zip(encryption_keys) { for (l, encryption_key) in Participant::iter().take(usize::from(n)).zip(encryption_keys) {
let share = polynomial::<<C::ToweringCurve as Ciphersuite>::F>(&coefficients, l); let share = polynomial::<<C::ToweringCurve as WrappedGroup>::F>(&coefficients, l);
encrypted_secret_shares.insert(l, *share + *encryption_key); encrypted_secret_shares.insert(l, *share + *encryption_key);
} }
@@ -243,26 +244,26 @@ impl<C: Curves> Dkg<C> {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn verifiable_encryption_statements<C: Curves>( fn verifiable_encryption_statements<C: Curves>(
rng: &mut (impl RngCore + CryptoRng), rng: &mut (impl RngCore + CryptoRng),
coefficients: &[<C::ToweringCurve as Ciphersuite>::G], coefficients: &[<C::ToweringCurve as WrappedGroup>::G],
encryption_key_commitments: &[<C::ToweringCurve as Ciphersuite>::G], encryption_key_commitments: &[<C::ToweringCurve as WrappedGroup>::G],
encrypted_secret_shares: &HashMap<Participant, <C::ToweringCurve as Ciphersuite>::F>, encrypted_secret_shares: &HashMap<Participant, <C::ToweringCurve as WrappedGroup>::F>,
) -> ( ) -> (
<C::ToweringCurve as Ciphersuite>::F, <C::ToweringCurve as WrappedGroup>::F,
Vec<(<C::ToweringCurve as Ciphersuite>::F, <C::ToweringCurve as Ciphersuite>::G)>, Vec<(<C::ToweringCurve as WrappedGroup>::F, <C::ToweringCurve as WrappedGroup>::G)>,
) { ) {
let mut g_scalar = <C::ToweringCurve as Ciphersuite>::F::ZERO; let mut g_scalar = <C::ToweringCurve as WrappedGroup>::F::ZERO;
let mut pairs = Vec::with_capacity(coefficients.len() + encryption_key_commitments.len()); let mut pairs = Vec::with_capacity(coefficients.len() + encryption_key_commitments.len());
// Push on the commitments to the polynomial being secret-shared // Push on the commitments to the polynomial being secret-shared
for coefficient in coefficients { for coefficient in coefficients {
// This uses `0` as we'll add to it later, given its fixed position // This uses `0` as we'll add to it later, given its fixed position
pairs.push((<C::ToweringCurve as Ciphersuite>::F::ZERO, *coefficient)); pairs.push((<C::ToweringCurve as WrappedGroup>::F::ZERO, *coefficient));
} }
for (i, encrypted_secret_share) in encrypted_secret_shares { for (i, encrypted_secret_share) in encrypted_secret_shares {
let encryption_key_commitment = encryption_key_commitments[usize::from(u16::from(*i)) - 1]; let encryption_key_commitment = encryption_key_commitments[usize::from(u16::from(*i)) - 1];
let weight = <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng); let weight = <C::ToweringCurve as WrappedGroup>::F::random(&mut *rng);
/* /*
The encrypted secret share scaling `G`, minus the encryption key commitment, minus the The encrypted secret share scaling `G`, minus the encryption key commitment, minus the
@@ -274,7 +275,7 @@ fn verifiable_encryption_statements<C: Curves>(
pairs.push((weight, encryption_key_commitment)); pairs.push((weight, encryption_key_commitment));
// Calculate the commitment to the secret share via the commitments to the polynomial // Calculate the commitment to the secret share via the commitments to the polynomial
{ {
let i = <C::ToweringCurve as Ciphersuite>::F::from(u64::from(u16::from(*i))); let i = <C::ToweringCurve as WrappedGroup>::F::from(u64::from(u16::from(*i)));
(0 .. coefficients.len()).fold(weight, |exp, j| { (0 .. coefficients.len()).fold(weight, |exp, j| {
pairs[j].0 += exp; pairs[j].0 += exp;
exp * i exp * i
@@ -300,7 +301,7 @@ impl<C: Curves> Dkg<C> {
generators: &Generators<C>, generators: &Generators<C>,
context: [u8; 32], context: [u8; 32],
t: u16, t: u16,
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G], evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
participations: &HashMap<Participant, Participation<C>>, participations: &HashMap<Participant, Participation<C>>,
) -> Result<VerifyResult<C>, Error> { ) -> Result<VerifyResult<C>, Error> {
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { let Ok(n) = u16::try_from(evrf_public_keys.len()) else {
@@ -386,7 +387,7 @@ impl<C: Curves> Dkg<C> {
{ {
let mut share_verification_statements_actual = HashMap::with_capacity(valid.len()); let mut share_verification_statements_actual = HashMap::with_capacity(valid.len());
if !{ if !{
let mut g_scalar = <C::ToweringCurve as Ciphersuite>::F::ZERO; let mut g_scalar = <C::ToweringCurve as WrappedGroup>::F::ZERO;
let mut pairs = Vec::with_capacity(valid.len() * (usize::from(t) + evrf_public_keys.len())); let mut pairs = Vec::with_capacity(valid.len() * (usize::from(t) + evrf_public_keys.len()));
for (i, (encrypted_secret_shares, data)) in &valid { for (i, (encrypted_secret_shares, data)) in &valid {
let (this_g_scalar, mut these_pairs) = verifiable_encryption_statements::<C>( let (this_g_scalar, mut these_pairs) = verifiable_encryption_statements::<C>(
@@ -417,9 +418,11 @@ impl<C: Curves> Dkg<C> {
let sum_encrypted_secret_share = sum_encrypted_secret_shares let sum_encrypted_secret_share = sum_encrypted_secret_shares
.get(j) .get(j)
.copied() .copied()
.unwrap_or(<C::ToweringCurve as Ciphersuite>::F::ZERO); .unwrap_or(<C::ToweringCurve as WrappedGroup>::F::ZERO);
let sum_mask = let sum_mask = sum_masks
sum_masks.get(j).copied().unwrap_or(<C::ToweringCurve as Ciphersuite>::G::identity()); .get(j)
.copied()
.unwrap_or(<C::ToweringCurve as WrappedGroup>::G::identity());
sum_encrypted_secret_shares.insert(*j, sum_encrypted_secret_share + enc_share); sum_encrypted_secret_shares.insert(*j, sum_encrypted_secret_share + enc_share);
let j_index = usize::from(u16::from(*j)) - 1; let j_index = usize::from(u16::from(*j)) - 1;
@@ -487,7 +490,7 @@ impl<C: Curves> Dkg<C> {
for i in Participant::iter().take(usize::from(n)) { for i in Participant::iter().take(usize::from(n)) {
verification_shares.insert( verification_shares.insert(
i, i,
(<C::ToweringCurve as Ciphersuite>::generator() * sum_encrypted_secret_shares[&i]) - (<C::ToweringCurve as WrappedGroup>::generator() * sum_encrypted_secret_shares[&i]) -
sum_masks[&i], sum_masks[&i],
); );
} }
@@ -506,9 +509,10 @@ impl<C: Curves> Dkg<C> {
/// This will return _all_ keys belong to the participant. /// This will return _all_ keys belong to the participant.
pub fn keys( pub fn keys(
&self, &self,
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>, evrf_private_key: &Zeroizing<<C::EmbeddedCurve as WrappedGroup>::F>,
) -> Vec<ThresholdKeys<C::ToweringCurve>> { ) -> Vec<ThresholdKeys<C::ToweringCurve>> {
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref(); let evrf_public_key =
<C::EmbeddedCurve as WrappedGroup>::generator() * evrf_private_key.deref();
let mut is = Vec::with_capacity(1); let mut is = Vec::with_capacity(1);
for (i, evrf_key) in Participant::iter().zip(self.evrf_public_keys.iter()) { for (i, evrf_key) in Participant::iter().zip(self.evrf_public_keys.iter()) {
if *evrf_key == evrf_public_key { if *evrf_key == evrf_public_key {
@@ -518,14 +522,14 @@ impl<C: Curves> Dkg<C> {
let mut res = Vec::with_capacity(is.len()); let mut res = Vec::with_capacity(is.len());
for i in is { for i in is {
let mut secret_share = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO); let mut secret_share = Zeroizing::new(<C::ToweringCurve as WrappedGroup>::F::ZERO);
for shares in self.encrypted_secret_shares.values() { for shares in self.encrypted_secret_shares.values() {
let (ecdh_commitments, encrypted_secret_share) = shares[&i]; let (ecdh_commitments, encrypted_secret_share) = shares[&i];
let mut ecdh = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO); let mut ecdh = Zeroizing::new(<C::ToweringCurve as WrappedGroup>::F::ZERO);
for point in ecdh_commitments { for point in ecdh_commitments {
let (mut x, mut y) = let (mut x, mut y) =
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(point * evrf_private_key.deref()).unwrap(); <C::EmbeddedCurve as WrappedGroup>::G::to_xy(point * evrf_private_key.deref()).unwrap();
*ecdh += x; *ecdh += x;
x.zeroize(); x.zeroize();
y.zeroize(); y.zeroize();
@@ -534,7 +538,7 @@ impl<C: Curves> Dkg<C> {
} }
debug_assert_eq!( debug_assert_eq!(
self.verification_shares[&i], self.verification_shares[&i],
<C::ToweringCurve as Ciphersuite>::G::generator() * secret_share.deref() <C::ToweringCurve as WrappedGroup>::generator() * secret_share.deref()
); );
res.push( res.push(

View File

@@ -8,7 +8,7 @@ use zeroize::Zeroizing;
use rand_core::{RngCore, CryptoRng, SeedableRng}; use rand_core::{RngCore, CryptoRng, SeedableRng};
use rand_chacha::ChaCha20Rng; use rand_chacha::ChaCha20Rng;
use ciphersuite::{group::ff::Field, Ciphersuite}; use ciphersuite::{group::ff::Field, WrappedGroup};
use generalized_bulletproofs::{ use generalized_bulletproofs::{
Generators, BatchVerifier, PedersenCommitment, PedersenVectorCommitment, Generators, BatchVerifier, PedersenCommitment, PedersenVectorCommitment,
@@ -28,8 +28,8 @@ mod tape;
use tape::*; use tape::*;
type EmbeddedPoint<C> = ( type EmbeddedPoint<C> = (
<<<C as Curves>::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement, <<<C as Curves>::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::FieldElement,
<<<C as Curves>::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement, <<<C as Curves>::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::FieldElement,
); );
#[allow(non_snake_case)] #[allow(non_snake_case)]
@@ -37,14 +37,15 @@ struct Circuit<
'a, 'a,
C: Curves, C: Curves,
CG: Iterator< CG: Iterator<
Item = ChallengedGenerator<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>, Item = ChallengedGenerator<<C::ToweringCurve as WrappedGroup>::F, C::EmbeddedCurveParameters>,
>, >,
> { > {
curve_spec: &'a CurveSpec<<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement>, curve_spec: &'a CurveSpec<<<C::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::FieldElement>,
circuit: &'a mut BpCircuit<C::ToweringCurve>, circuit: &'a mut BpCircuit<C::ToweringCurve>,
challenge: DiscreteLogChallenge<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>, challenge:
DiscreteLogChallenge<<C::ToweringCurve as WrappedGroup>::F, C::EmbeddedCurveParameters>,
challenged_G: challenged_G:
ChallengedGenerator<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>, ChallengedGenerator<<C::ToweringCurve as WrappedGroup>::F, C::EmbeddedCurveParameters>,
challenged_generators: &'a mut CG, challenged_generators: &'a mut CG,
tape: Tape, tape: Tape,
pedersen_commitment_tape: PedersenCommitmentTape, pedersen_commitment_tape: PedersenCommitmentTape,
@@ -54,7 +55,7 @@ impl<
'a, 'a,
C: Curves, C: Curves,
CG: Iterator< CG: Iterator<
Item = ChallengedGenerator<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>, Item = ChallengedGenerator<<C::ToweringCurve as WrappedGroup>::F, C::EmbeddedCurveParameters>,
>, >,
> Circuit<'a, C, CG> > Circuit<'a, C, CG>
{ {
@@ -92,7 +93,7 @@ impl<
&self.challenge, &self.challenge,
&challenged_generator, &challenged_generator,
); );
lincomb = lincomb.term(<C::ToweringCurve as Ciphersuite>::F::ONE, point.x()); lincomb = lincomb.term(<C::ToweringCurve as WrappedGroup>::F::ONE, point.x());
} }
/* /*
Constrain the sum of the two `x` coordinates to be equal to the value committed to in a Constrain the sum of the two `x` coordinates to be equal to the value committed to in a
@@ -137,7 +138,7 @@ impl<
&self.challenge, &self.challenge,
&challenged_public_key, &challenged_public_key,
); );
lincomb = lincomb.term(<C::ToweringCurve as Ciphersuite>::F::ONE, point.x()); lincomb = lincomb.term(<C::ToweringCurve as WrappedGroup>::F::ONE, point.x());
debug_assert!(point_with_dlogs.next().is_none()); debug_assert!(point_with_dlogs.next().is_none());
} }
@@ -152,20 +153,20 @@ impl<
/// The result of proving. /// The result of proving.
pub(super) struct ProveResult<C: Curves> { pub(super) struct ProveResult<C: Curves> {
/// The coefficients for use in the DKG. /// The coefficients for use in the DKG.
pub(super) coefficients: Vec<Zeroizing<<C::ToweringCurve as Ciphersuite>::F>>, pub(super) coefficients: Vec<Zeroizing<<C::ToweringCurve as WrappedGroup>::F>>,
/// The masks to encrypt secret shares with. /// The masks to encrypt secret shares with.
pub(super) encryption_keys: Vec<Zeroizing<<C::ToweringCurve as Ciphersuite>::F>>, pub(super) encryption_keys: Vec<Zeroizing<<C::ToweringCurve as WrappedGroup>::F>>,
/// The proof itself. /// The proof itself.
pub(super) proof: Vec<u8>, pub(super) proof: Vec<u8>,
} }
pub(super) struct Verified<C: Curves> { pub(super) struct Verified<C: Curves> {
/// The commitments to the coefficients used within the DKG. /// The commitments to the coefficients used within the DKG.
pub(super) coefficients: Vec<<C::ToweringCurve as Ciphersuite>::G>, pub(super) coefficients: Vec<<C::ToweringCurve as WrappedGroup>::G>,
/// The ephemeral public keys to perform ECDHs with /// The ephemeral public keys to perform ECDHs with
pub(super) ecdh_commitments: Vec<[<C::EmbeddedCurve as Ciphersuite>::G; 2]>, pub(super) ecdh_commitments: Vec<[<C::EmbeddedCurve as WrappedGroup>::G; 2]>,
/// The commitments to the masks used to encrypt secret shares with. /// The commitments to the masks used to encrypt secret shares with.
pub(super) encryption_key_commitments: Vec<<C::ToweringCurve as Ciphersuite>::G>, pub(super) encryption_key_commitments: Vec<<C::ToweringCurve as WrappedGroup>::G>,
} }
impl<C: Curves> fmt::Debug for Verified<C> { impl<C: Curves> fmt::Debug for Verified<C> {
@@ -175,7 +176,7 @@ impl<C: Curves> fmt::Debug for Verified<C> {
} }
type GeneratorTable<C> = generalized_bulletproofs_ec_gadgets::GeneratorTable< type GeneratorTable<C> = generalized_bulletproofs_ec_gadgets::GeneratorTable<
<<<C as Curves>::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement, <<<C as Curves>::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::FieldElement,
<C as Curves>::EmbeddedCurveParameters, <C as Curves>::EmbeddedCurveParameters,
>; >;
@@ -219,7 +220,7 @@ impl<C: Curves> Proof<C> {
} }
fn circuit( fn circuit(
curve_spec: &CurveSpec<<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement>, curve_spec: &CurveSpec<<<C::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::FieldElement>,
evrf_public_key: EmbeddedPoint<C>, evrf_public_key: EmbeddedPoint<C>,
coefficients: usize, coefficients: usize,
ecdh_commitments: &[[EmbeddedPoint<C>; 2]], ecdh_commitments: &[[EmbeddedPoint<C>; 2]],
@@ -281,7 +282,7 @@ impl<C: Curves> Proof<C> {
fn sample_coefficients_evrf_points( fn sample_coefficients_evrf_points(
seed: [u8; 32], seed: [u8; 32],
coefficients: usize, coefficients: usize,
) -> Vec<<C::EmbeddedCurve as Ciphersuite>::G> { ) -> Vec<<C::EmbeddedCurve as WrappedGroup>::G> {
let mut rng = ChaCha20Rng::from_seed(seed); let mut rng = ChaCha20Rng::from_seed(seed);
let quantity = 2 * coefficients; let quantity = 2 * coefficients;
let mut res = Vec::with_capacity(quantity); let mut res = Vec::with_capacity(quantity);
@@ -293,28 +294,29 @@ impl<C: Curves> Proof<C> {
/// Create the required tables for the generators. /// Create the required tables for the generators.
fn generator_tables( fn generator_tables(
coefficients_evrf_points: &[<C::EmbeddedCurve as Ciphersuite>::G], coefficients_evrf_points: &[<C::EmbeddedCurve as WrappedGroup>::G],
participants: &[<<C as Curves>::EmbeddedCurve as Ciphersuite>::G], participants: &[<<C as Curves>::EmbeddedCurve as WrappedGroup>::G],
) -> Vec<GeneratorTable<C>> { ) -> Vec<GeneratorTable<C>> {
let curve_spec = CurveSpec { let curve_spec = CurveSpec {
a: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::a(), a: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G::a(),
b: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::b(), b: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G::b(),
}; };
let mut generator_tables = let mut generator_tables =
Vec::with_capacity(1 + coefficients_evrf_points.len() + participants.len()); Vec::with_capacity(1 + coefficients_evrf_points.len() + participants.len());
{ {
let (x, y) = let (x, y) = <C::EmbeddedCurve as WrappedGroup>::G::to_xy(
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(<C::EmbeddedCurve as Ciphersuite>::generator()) <C::EmbeddedCurve as WrappedGroup>::generator(),
.unwrap(); )
.unwrap();
generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y)); generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y));
} }
for generator in coefficients_evrf_points { for generator in coefficients_evrf_points {
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(*generator).unwrap(); let (x, y) = <C::EmbeddedCurve as WrappedGroup>::G::to_xy(*generator).unwrap();
generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y)); generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y));
} }
for generator in participants { for generator in participants {
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(*generator).unwrap(); let (x, y) = <C::EmbeddedCurve as WrappedGroup>::G::to_xy(*generator).unwrap();
generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y)); generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y));
} }
generator_tables generator_tables
@@ -325,12 +327,12 @@ impl<C: Curves> Proof<C> {
generators: &Generators<C::ToweringCurve>, generators: &Generators<C::ToweringCurve>,
transcript: [u8; 32], transcript: [u8; 32],
coefficients: usize, coefficients: usize,
participant_public_keys: &[<<C as Curves>::EmbeddedCurve as Ciphersuite>::G], participant_public_keys: &[<<C as Curves>::EmbeddedCurve as WrappedGroup>::G],
evrf_private_key: &Zeroizing<<<C as Curves>::EmbeddedCurve as Ciphersuite>::F>, evrf_private_key: &Zeroizing<<<C as Curves>::EmbeddedCurve as WrappedGroup>::F>,
) -> Result<ProveResult<C>, AcProveError> { ) -> Result<ProveResult<C>, AcProveError> {
let curve_spec = CurveSpec { let curve_spec = CurveSpec {
a: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::a(), a: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G::a(),
b: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::b(), b: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G::b(),
}; };
let coefficients_evrf_points = Self::sample_coefficients_evrf_points(transcript, coefficients); let coefficients_evrf_points = Self::sample_coefficients_evrf_points(transcript, coefficients);
@@ -340,7 +342,7 @@ impl<C: Curves> Proof<C> {
// Push a discrete logarithm onto the tape // Push a discrete logarithm onto the tape
let discrete_log = let discrete_log =
|vector_commitment_tape: &mut Vec<_>, |vector_commitment_tape: &mut Vec<_>,
dlog: &ScalarDecomposition<<<C as Curves>::EmbeddedCurve as Ciphersuite>::F>| { dlog: &ScalarDecomposition<<<C as Curves>::EmbeddedCurve as WrappedGroup>::F>| {
for coefficient in dlog.decomposition() { for coefficient in dlog.decomposition() {
vector_commitment_tape.push(<_>::from(*coefficient)); vector_commitment_tape.push(<_>::from(*coefficient));
} }
@@ -351,8 +353,8 @@ impl<C: Curves> Proof<C> {
// Returns the point for which the claim was made. // Returns the point for which the claim was made.
let discrete_log_claim = let discrete_log_claim =
|vector_commitment_tape: &mut Vec<_>, |vector_commitment_tape: &mut Vec<_>,
dlog: &ScalarDecomposition<<<C as Curves>::EmbeddedCurve as Ciphersuite>::F>, dlog: &ScalarDecomposition<<<C as Curves>::EmbeddedCurve as WrappedGroup>::F>,
generator: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G| { generator: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G| {
{ {
let divisor = let divisor =
Zeroizing::new(dlog.scalar_mul_divisor(generator).normalize_x_coefficient()); Zeroizing::new(dlog.scalar_mul_divisor(generator).normalize_x_coefficient());
@@ -368,12 +370,12 @@ impl<C: Curves> Proof<C> {
.y_coefficients .y_coefficients
.first() .first()
.copied() .copied()
.unwrap_or(<C::ToweringCurve as Ciphersuite>::F::ZERO), .unwrap_or(<C::ToweringCurve as WrappedGroup>::F::ZERO),
); );
} }
let dh = generator * dlog.scalar(); let dh = generator * dlog.scalar();
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(dh).unwrap(); let (x, y) = <C::EmbeddedCurve as WrappedGroup>::G::to_xy(dh).unwrap();
vector_commitment_tape.push(x); vector_commitment_tape.push(x);
vector_commitment_tape.push(y); vector_commitment_tape.push(y);
(dh, (x, y)) (dh, (x, y))
@@ -387,7 +389,7 @@ impl<C: Curves> Proof<C> {
let mut coefficients = Vec::with_capacity(coefficients); let mut coefficients = Vec::with_capacity(coefficients);
let evrf_public_key = { let evrf_public_key = {
let evrf_private_key = let evrf_private_key =
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(**evrf_private_key) ScalarDecomposition::<<C::EmbeddedCurve as WrappedGroup>::F>::new(**evrf_private_key)
.expect("eVRF private key was zero"); .expect("eVRF private key was zero");
discrete_log(&mut vector_commitment_tape, &evrf_private_key); discrete_log(&mut vector_commitment_tape, &evrf_private_key);
@@ -396,12 +398,12 @@ impl<C: Curves> Proof<C> {
let (_, evrf_public_key) = discrete_log_claim( let (_, evrf_public_key) = discrete_log_claim(
&mut vector_commitment_tape, &mut vector_commitment_tape,
&evrf_private_key, &evrf_private_key,
<<C as Curves>::EmbeddedCurve as Ciphersuite>::generator(), <<C as Curves>::EmbeddedCurve as WrappedGroup>::generator(),
); );
// Push the divisor for each point we use in the eVRF // Push the divisor for each point we use in the eVRF
for pair in coefficients_evrf_points.chunks(2) { for pair in coefficients_evrf_points.chunks(2) {
let mut coefficient = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO); let mut coefficient = Zeroizing::new(<C::ToweringCurve as WrappedGroup>::F::ZERO);
for point in pair { for point in pair {
let (_, (dh_x, _)) = let (_, (dh_x, _)) =
discrete_log_claim(&mut vector_commitment_tape, &evrf_private_key, *point); discrete_log_claim(&mut vector_commitment_tape, &evrf_private_key, *point);
@@ -418,15 +420,16 @@ impl<C: Curves> Proof<C> {
let mut ecdh_commitments = Vec::with_capacity(2 * participant_public_keys.len()); let mut ecdh_commitments = Vec::with_capacity(2 * participant_public_keys.len());
let mut ecdh_commitments_xy = Vec::with_capacity(participant_public_keys.len()); let mut ecdh_commitments_xy = Vec::with_capacity(participant_public_keys.len());
for participant_public_key in participant_public_keys { for participant_public_key in participant_public_keys {
let mut ecdh_commitments_xy_i = let mut ecdh_commitments_xy_i = [(
[(<C::ToweringCurve as Ciphersuite>::F::ZERO, <C::ToweringCurve as Ciphersuite>::F::ZERO); <C::ToweringCurve as WrappedGroup>::F::ZERO,
2]; <C::ToweringCurve as WrappedGroup>::F::ZERO,
let mut encryption_key = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO); ); 2];
let mut encryption_key = Zeroizing::new(<C::ToweringCurve as WrappedGroup>::F::ZERO);
for ecdh_commitments_xy_i_j_dest in &mut ecdh_commitments_xy_i { for ecdh_commitments_xy_i_j_dest in &mut ecdh_commitments_xy_i {
let mut ecdh_ephemeral_secret; let mut ecdh_ephemeral_secret;
loop { loop {
ecdh_ephemeral_secret = ecdh_ephemeral_secret =
Zeroizing::new(<C::EmbeddedCurve as Ciphersuite>::F::random(&mut *rng)); Zeroizing::new(<C::EmbeddedCurve as WrappedGroup>::F::random(&mut *rng));
// 0 would produce the identity, which isn't representable within the discrete-log proof. // 0 would produce the identity, which isn't representable within the discrete-log proof.
if bool::from(!ecdh_ephemeral_secret.is_zero()) { if bool::from(!ecdh_ephemeral_secret.is_zero()) {
break; break;
@@ -434,7 +437,7 @@ impl<C: Curves> Proof<C> {
} }
let ecdh_ephemeral_secret = let ecdh_ephemeral_secret =
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(*ecdh_ephemeral_secret) ScalarDecomposition::<<C::EmbeddedCurve as WrappedGroup>::F>::new(*ecdh_ephemeral_secret)
.expect("ECDH ephemeral secret zero"); .expect("ECDH ephemeral secret zero");
discrete_log(&mut vector_commitment_tape, &ecdh_ephemeral_secret); discrete_log(&mut vector_commitment_tape, &ecdh_ephemeral_secret);
@@ -442,7 +445,7 @@ impl<C: Curves> Proof<C> {
let (ecdh_commitment, ecdh_commitment_xy_i_j) = discrete_log_claim( let (ecdh_commitment, ecdh_commitment_xy_i_j) = discrete_log_claim(
&mut vector_commitment_tape, &mut vector_commitment_tape,
&ecdh_ephemeral_secret, &ecdh_ephemeral_secret,
<<C as Curves>::EmbeddedCurve as Ciphersuite>::generator(), <<C as Curves>::EmbeddedCurve as WrappedGroup>::generator(),
); );
ecdh_commitments.push(ecdh_commitment); ecdh_commitments.push(ecdh_commitment);
*ecdh_commitments_xy_i_j_dest = ecdh_commitment_xy_i_j; *ecdh_commitments_xy_i_j_dest = ecdh_commitment_xy_i_j;
@@ -470,7 +473,7 @@ impl<C: Curves> Proof<C> {
for chunk in vector_commitment_tape.chunks(generators_to_use) { for chunk in vector_commitment_tape.chunks(generators_to_use) {
vector_commitments.push(PedersenVectorCommitment { vector_commitments.push(PedersenVectorCommitment {
g_values: chunk.into(), g_values: chunk.into(),
mask: <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng), mask: <C::ToweringCurve as WrappedGroup>::F::random(&mut *rng),
}); });
} }
@@ -479,13 +482,13 @@ impl<C: Curves> Proof<C> {
for coefficient in &coefficients { for coefficient in &coefficients {
commitments.push(PedersenCommitment { commitments.push(PedersenCommitment {
value: **coefficient, value: **coefficient,
mask: <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng), mask: <C::ToweringCurve as WrappedGroup>::F::random(&mut *rng),
}); });
} }
for enc_mask in &encryption_keys { for enc_mask in &encryption_keys {
commitments.push(PedersenCommitment { commitments.push(PedersenCommitment {
value: **enc_mask, value: **enc_mask,
mask: <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng), mask: <C::ToweringCurve as WrappedGroup>::F::random(&mut *rng),
}); });
} }
@@ -536,13 +539,13 @@ impl<C: Curves> Proof<C> {
} }
// Prove the openings of the commitments were correct // Prove the openings of the commitments were correct
let mut x = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO); let mut x = Zeroizing::new(<C::ToweringCurve as WrappedGroup>::F::ZERO);
for commitment in commitments { for commitment in commitments {
*x += commitment.mask * transcript.challenge::<C::ToweringCurve>(); *x += commitment.mask * transcript.challenge::<C::ToweringCurve>();
} }
// Produce a Schnorr PoK for the weighted-sum of the Pedersen commitments' blinding factors // Produce a Schnorr PoK for the weighted-sum of the Pedersen commitments' blinding factors
let r = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::random(&mut *rng)); let r = Zeroizing::new(<C::ToweringCurve as WrappedGroup>::F::random(&mut *rng));
transcript.push_point(&(generators.h() * r.deref())); transcript.push_point(&(generators.h() * r.deref()));
let c = transcript.challenge::<C::ToweringCurve>(); let c = transcript.challenge::<C::ToweringCurve>();
transcript.push_scalar((c * x.deref()) + r.deref()); transcript.push_scalar((c * x.deref()) + r.deref());
@@ -557,14 +560,14 @@ impl<C: Curves> Proof<C> {
verifier: &mut BatchVerifier<C::ToweringCurve>, verifier: &mut BatchVerifier<C::ToweringCurve>,
transcript: [u8; 32], transcript: [u8; 32],
coefficients: usize, coefficients: usize,
participant_public_keys: &[<<C as Curves>::EmbeddedCurve as Ciphersuite>::G], participant_public_keys: &[<<C as Curves>::EmbeddedCurve as WrappedGroup>::G],
evrf_public_key: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G, evrf_public_key: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G,
proof: &[u8], proof: &[u8],
) -> Result<Verified<C>, ()> { ) -> Result<Verified<C>, ()> {
let (mut transcript, ecdh_commitments, pedersen_commitments) = { let (mut transcript, ecdh_commitments, pedersen_commitments) = {
let curve_spec = CurveSpec { let curve_spec = CurveSpec {
a: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::a(), a: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G::a(),
b: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::b(), b: <<C as Curves>::EmbeddedCurve as WrappedGroup>::G::b(),
}; };
let coefficients_evrf_points = let coefficients_evrf_points =
@@ -600,9 +603,9 @@ impl<C: Curves> Proof<C> {
ecdh_commitments.push(ecdh_commitments_i); ecdh_commitments.push(ecdh_commitments_i);
// This inherently bans using the identity point, as it won't have an affine representation // This inherently bans using the identity point, as it won't have an affine representation
ecdh_commitments_xy.push([ ecdh_commitments_xy.push([
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitments_i[0]) <<C::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::to_xy(ecdh_commitments_i[0])
.ok_or(())?, .ok_or(())?,
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitments_i[1]) <<C::EmbeddedCurve as WrappedGroup>::G as DivisorCurve>::to_xy(ecdh_commitments_i[1])
.ok_or(())?, .ok_or(())?,
]); ]);
} }
@@ -610,7 +613,7 @@ impl<C: Curves> Proof<C> {
let mut circuit = BpCircuit::verify(); let mut circuit = BpCircuit::verify();
Self::circuit( Self::circuit(
&curve_spec, &curve_spec,
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(evrf_public_key).ok_or(())?, <C::EmbeddedCurve as WrappedGroup>::G::to_xy(evrf_public_key).ok_or(())?,
coefficients, coefficients,
&ecdh_commitments_xy, &ecdh_commitments_xy,
&generator_tables.iter().collect::<Vec<_>>(), &generator_tables.iter().collect::<Vec<_>>(),

View File

@@ -4,11 +4,11 @@ use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use ciphersuite::{group::ff::Field, Ciphersuite}; use ciphersuite::{group::ff::Field, WrappedGroup};
use embedwards25519::Embedwards25519; use embedwards25519::Embedwards25519;
use dkg_recovery::recover_key; use dkg_recovery::recover_key;
use crate::{Participant, Curves, Generators, VerifyResult, Dkg, Ristretto}; use crate::{Participant, Curves, Generators, VerifyResult, Dkg, Ed25519};
mod proof; mod proof;
@@ -17,14 +17,14 @@ const PARTICIPANTS: u16 = 5;
#[test] #[test]
fn dkg() { fn dkg() {
let generators = Generators::<Ristretto>::new(THRESHOLD, PARTICIPANTS); let generators = Generators::<Ed25519>::new(THRESHOLD, PARTICIPANTS);
let context = [0; 32]; let context = [0; 32];
let mut priv_keys = vec![]; let mut priv_keys = vec![];
let mut pub_keys = vec![]; let mut pub_keys = vec![];
for i in 0 .. PARTICIPANTS { for i in 0 .. PARTICIPANTS {
let priv_key = <Embedwards25519 as Ciphersuite>::F::random(&mut OsRng); let priv_key = <Embedwards25519 as WrappedGroup>::F::random(&mut OsRng);
pub_keys.push(<Embedwards25519 as Ciphersuite>::generator() * priv_key); pub_keys.push(<Embedwards25519 as WrappedGroup>::generator() * priv_key);
priv_keys.push((Participant::new(1 + i).unwrap(), Zeroizing::new(priv_key))); priv_keys.push((Participant::new(1 + i).unwrap(), Zeroizing::new(priv_key)));
} }
@@ -34,27 +34,15 @@ fn dkg() {
for (i, priv_key) in priv_keys.iter().take(usize::from(THRESHOLD)) { for (i, priv_key) in priv_keys.iter().take(usize::from(THRESHOLD)) {
participations.insert( participations.insert(
*i, *i,
Dkg::<Ristretto>::participate( Dkg::<Ed25519>::participate(&mut OsRng, &generators, context, THRESHOLD, &pub_keys, priv_key)
&mut OsRng, .unwrap(),
&generators,
context,
THRESHOLD,
&pub_keys,
priv_key,
)
.unwrap(),
); );
} }
let VerifyResult::Valid(dkg) = Dkg::<Ristretto>::verify( let VerifyResult::Valid(dkg) =
&mut OsRng, Dkg::<Ed25519>::verify(&mut OsRng, &generators, context, THRESHOLD, &pub_keys, &participations)
&generators, .unwrap()
context, else {
THRESHOLD,
&pub_keys,
&participations,
)
.unwrap() else {
panic!("verify didn't return VerifyResult::Valid") panic!("verify didn't return VerifyResult::Valid")
}; };
@@ -80,7 +68,7 @@ fn dkg() {
// TODO: Test for all possible combinations of keys // TODO: Test for all possible combinations of keys
assert_eq!( assert_eq!(
<<Ristretto as Curves>::ToweringCurve as Ciphersuite>::generator() * <<Ed25519 as Curves>::ToweringCurve as WrappedGroup>::generator() *
*recover_key(&all_keys.values().cloned().collect::<Vec<_>>()).unwrap(), *recover_key(&all_keys.values().cloned().collect::<Vec<_>>()).unwrap(),
group_key.unwrap() group_key.unwrap()
); );

View File

@@ -6,13 +6,13 @@ use zeroize::Zeroizing;
use ciphersuite::{ use ciphersuite::{
group::{ff::Field, Group}, group::{ff::Field, Group},
Ciphersuite, WrappedGroup,
}; };
use generalized_bulletproofs::{Generators, tests::insecure_test_generators}; use generalized_bulletproofs::{Generators, tests::insecure_test_generators};
use crate::{ use crate::{
Curves, Ristretto, Curves, Ed25519,
proof::*, proof::*,
tests::{THRESHOLD, PARTICIPANTS}, tests::{THRESHOLD, PARTICIPANTS},
}; };
@@ -20,9 +20,9 @@ use crate::{
fn proof<C: Curves>() { fn proof<C: Curves>() {
let generators = insecure_test_generators(&mut OsRng, 2048).unwrap(); let generators = insecure_test_generators(&mut OsRng, 2048).unwrap();
let embedded_private_key = let embedded_private_key =
Zeroizing::new(<C::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng)); Zeroizing::new(<C::EmbeddedCurve as WrappedGroup>::F::random(&mut OsRng));
let ecdh_public_keys: [_; PARTICIPANTS as usize] = let ecdh_public_keys: [_; PARTICIPANTS as usize] =
core::array::from_fn(|_| <C::EmbeddedCurve as Ciphersuite>::G::random(&mut OsRng)); core::array::from_fn(|_| <C::EmbeddedCurve as WrappedGroup>::G::random(&mut OsRng));
let time = Instant::now(); let time = Instant::now();
let res = Proof::<C>::prove( let res = Proof::<C>::prove(
&mut OsRng, &mut OsRng,
@@ -54,5 +54,5 @@ fn proof<C: Curves>() {
#[test] #[test]
fn ristretto_proof() { fn ristretto_proof() {
proof::<Ristretto>(); proof::<Ed25519>();
} }

View File

@@ -5,7 +5,7 @@ use rand_core::{RngCore, CryptoRng};
use ciphersuite::{ use ciphersuite::{
group::{ff::PrimeField, Group, GroupEncoding}, group::{ff::PrimeField, Group, GroupEncoding},
Ciphersuite, GroupIo,
}; };
use dkg::Participant; use dkg::Participant;
@@ -13,7 +13,7 @@ use dkg::Participant;
/// Sample a random, unbiased point on the elliptic curve with an unknown discrete logarithm. /// Sample a random, unbiased point on the elliptic curve with an unknown discrete logarithm.
/// ///
/// This keeps it simple by using rejection sampling. /// This keeps it simple by using rejection sampling.
pub(crate) fn sample_point<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G { pub(crate) fn sample_point<C: GroupIo>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
let mut repr = <C::G as GroupEncoding>::Repr::default(); let mut repr = <C::G as GroupEncoding>::Repr::default();
loop { loop {
rng.fill_bytes(repr.as_mut()); rng.fill_bytes(repr.as_mut());

View File

@@ -4,7 +4,7 @@ use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::Field, Ciphersuite}; use ciphersuite::WrappedGroup;
use dkg_recovery::recover_key; use dkg_recovery::recover_key;
use crate::*; use crate::*;
@@ -17,21 +17,21 @@ pub fn test_musig() {
let mut keys = vec![]; let mut keys = vec![];
let mut pub_keys = vec![]; let mut pub_keys = vec![];
for _ in 0 .. PARTICIPANTS { for _ in 0 .. PARTICIPANTS {
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
pub_keys.push(<Ristretto as Ciphersuite>::generator() * *key); pub_keys.push(<Ristretto as WrappedGroup>::generator() * *key);
keys.push(key); keys.push(key);
} }
const CONTEXT: [u8; 32] = *b"MuSig Test "; const CONTEXT: [u8; 32] = *b"MuSig Test ";
// Empty signing set // Empty signing set
musig::<Ristretto>(CONTEXT, Zeroizing::new(<Ristretto as Ciphersuite>::F::ZERO), &[]) musig::<Ristretto>(CONTEXT, Zeroizing::new(<Ristretto as WrappedGroup>::F::ZERO), &[])
.unwrap_err(); .unwrap_err();
// Signing set we're not part of // Signing set we're not part of
musig::<Ristretto>( musig::<Ristretto>(
CONTEXT, CONTEXT,
Zeroizing::new(<Ristretto as Ciphersuite>::F::ZERO), Zeroizing::new(<Ristretto as WrappedGroup>::F::ZERO),
&[<Ristretto as Ciphersuite>::generator()], &[<Ristretto as WrappedGroup>::generator()],
) )
.unwrap_err(); .unwrap_err();
@@ -48,7 +48,7 @@ pub fn test_musig() {
verification_shares.insert( verification_shares.insert(
these_keys.params().i(), these_keys.params().i(),
<Ristretto as Ciphersuite>::generator() * **these_keys.original_secret_share(), <Ristretto as WrappedGroup>::generator() * **these_keys.original_secret_share(),
); );
assert_eq!(these_keys.group_key(), group_key); assert_eq!(these_keys.group_key(), group_key);
@@ -63,7 +63,7 @@ pub fn test_musig() {
} }
assert_eq!( assert_eq!(
<Ristretto as Ciphersuite>::generator() * <Ristretto as WrappedGroup>::generator() *
*recover_key(&created_keys.values().cloned().collect::<Vec<_>>()).unwrap(), *recover_key(&created_keys.values().cloned().collect::<Vec<_>>()).unwrap(),
group_key group_key
); );

View File

@@ -8,7 +8,7 @@ use alloc::vec::Vec;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use ciphersuite::Ciphersuite; use ciphersuite::{GroupIo, Id};
pub use dkg::*; pub use dkg::*;
@@ -34,7 +34,7 @@ pub enum RecoveryError {
} }
/// Recover a shared secret from a collection of `dkg::ThresholdKeys`. /// Recover a shared secret from a collection of `dkg::ThresholdKeys`.
pub fn recover_key<C: Ciphersuite>( pub fn recover_key<C: GroupIo + Id>(
keys: &[ThresholdKeys<C>], keys: &[ThresholdKeys<C>],
) -> Result<Zeroizing<C::F>, RecoveryError> { ) -> Result<Zeroizing<C::F>, RecoveryError> {
let included = keys.iter().map(|keys| keys.params().i()).collect::<Vec<_>>(); let included = keys.iter().map(|keys| keys.params().i()).collect::<Vec<_>>();

View File

@@ -17,7 +17,7 @@ use ciphersuite::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
GroupEncoding, GroupEncoding,
}, },
Ciphersuite, GroupIo, Id,
}; };
/// The ID of a participant, defined as a non-zero u16. /// The ID of a participant, defined as a non-zero u16.
@@ -268,7 +268,7 @@ impl<F: Zeroize + PrimeField> Interpolation<F> {
/// heap-allocated pointer to minimize copies on the stack (`ThresholdKeys`, the publicly exposed /// heap-allocated pointer to minimize copies on the stack (`ThresholdKeys`, the publicly exposed
/// type). /// type).
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
struct ThresholdCore<C: Ciphersuite> { struct ThresholdCore<C: GroupIo + Id> {
params: ThresholdParams, params: ThresholdParams,
group_key: C::G, group_key: C::G,
verification_shares: HashMap<Participant, C::G>, verification_shares: HashMap<Participant, C::G>,
@@ -276,7 +276,7 @@ struct ThresholdCore<C: Ciphersuite> {
secret_share: Zeroizing<C::F>, secret_share: Zeroizing<C::F>,
} }
impl<C: Ciphersuite> fmt::Debug for ThresholdCore<C> { impl<C: GroupIo + Id> fmt::Debug for ThresholdCore<C> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt fmt
.debug_struct("ThresholdCore") .debug_struct("ThresholdCore")
@@ -288,7 +288,7 @@ impl<C: Ciphersuite> fmt::Debug for ThresholdCore<C> {
} }
} }
impl<C: Ciphersuite> Zeroize for ThresholdCore<C> { impl<C: GroupIo + Id> Zeroize for ThresholdCore<C> {
fn zeroize(&mut self) { fn zeroize(&mut self) {
self.params.zeroize(); self.params.zeroize();
self.group_key.zeroize(); self.group_key.zeroize();
@@ -302,7 +302,7 @@ impl<C: Ciphersuite> Zeroize for ThresholdCore<C> {
/// Threshold keys usable for signing. /// Threshold keys usable for signing.
#[derive(Clone, Debug, Zeroize)] #[derive(Clone, Debug, Zeroize)]
pub struct ThresholdKeys<C: Ciphersuite> { pub struct ThresholdKeys<C: GroupIo + Id> {
// Core keys. // Core keys.
#[zeroize(skip)] #[zeroize(skip)]
core: Arc<Zeroizing<ThresholdCore<C>>>, core: Arc<Zeroizing<ThresholdCore<C>>>,
@@ -315,7 +315,7 @@ pub struct ThresholdKeys<C: Ciphersuite> {
/// View of keys, interpolated and with the expected linear combination taken for usage. /// View of keys, interpolated and with the expected linear combination taken for usage.
#[derive(Clone)] #[derive(Clone)]
pub struct ThresholdView<C: Ciphersuite> { pub struct ThresholdView<C: GroupIo + Id> {
interpolation: Interpolation<C::F>, interpolation: Interpolation<C::F>,
scalar: C::F, scalar: C::F,
offset: C::F, offset: C::F,
@@ -326,7 +326,7 @@ pub struct ThresholdView<C: Ciphersuite> {
verification_shares: HashMap<Participant, C::G>, verification_shares: HashMap<Participant, C::G>,
} }
impl<C: Ciphersuite> fmt::Debug for ThresholdView<C> { impl<C: GroupIo + Id> fmt::Debug for ThresholdView<C> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt fmt
.debug_struct("ThresholdView") .debug_struct("ThresholdView")
@@ -341,7 +341,7 @@ impl<C: Ciphersuite> fmt::Debug for ThresholdView<C> {
} }
} }
impl<C: Ciphersuite> Zeroize for ThresholdView<C> { impl<C: GroupIo + Id> Zeroize for ThresholdView<C> {
fn zeroize(&mut self) { fn zeroize(&mut self) {
self.scalar.zeroize(); self.scalar.zeroize();
self.offset.zeroize(); self.offset.zeroize();
@@ -357,7 +357,7 @@ impl<C: Ciphersuite> Zeroize for ThresholdView<C> {
} }
} }
impl<C: Ciphersuite> ThresholdKeys<C> { impl<C: GroupIo + Id> ThresholdKeys<C> {
/// Create a new set of ThresholdKeys. /// Create a new set of ThresholdKeys.
pub fn new( pub fn new(
params: ThresholdParams, params: ThresholdParams,
@@ -632,7 +632,7 @@ impl<C: Ciphersuite> ThresholdKeys<C> {
let mut verification_shares = HashMap::new(); let mut verification_shares = HashMap::new();
for l in (1 ..= n).map(Participant) { for l in (1 ..= n).map(Participant) {
verification_shares.insert(l, <C as Ciphersuite>::read_G(reader)?); verification_shares.insert(l, C::read_G(reader)?);
} }
ThresholdKeys::new( ThresholdKeys::new(
@@ -645,7 +645,7 @@ impl<C: Ciphersuite> ThresholdKeys<C> {
} }
} }
impl<C: Ciphersuite> ThresholdView<C> { impl<C: GroupIo + Id> ThresholdView<C> {
/// Return the scalar applied to this view. /// Return the scalar applied to this view.
pub fn scalar(&self) -> C::F { pub fn scalar(&self) -> C::F {
self.scalar self.scalar

View File

@@ -19,7 +19,7 @@ workspace = true
[dependencies] [dependencies]
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }
sha3 = { version = "0.11.0-rc.0", default-features = false } sha3 = { version = "0.11.0-rc.2", default-features = false }
crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] } crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] }
prime-field = { path = "../prime-field", default-features = false } prime-field = { path = "../prime-field", default-features = false }

View File

@@ -1,4 +1,4 @@
use zeroize::Zeroize; use prime_field::subtle::CtOption;
use sha3::{ use sha3::{
digest::{ digest::{
@@ -8,9 +8,9 @@ use sha3::{
Shake256, Shake256,
}; };
use ciphersuite::{group::Group, Ciphersuite}; use ciphersuite::{group::GroupEncoding, Id, WithPreferredHash, GroupCanonicalEncoding};
use crate::{Scalar, Point}; use crate::Point;
/// Shake256, fixed to a 114-byte output, as used by Ed448. /// Shake256, fixed to a 114-byte output, as used by Ed448.
#[derive(Clone, Default)] #[derive(Clone, Default)]
@@ -49,21 +49,14 @@ impl FixedOutput for Shake256_114 {
} }
impl HashMarker for Shake256_114 {} impl HashMarker for Shake256_114 {}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] impl Id for Point {
pub struct Ed448; const ID: &[u8] = b"ed448";
impl Ciphersuite for Ed448 { }
type F = Scalar; impl WithPreferredHash for Point {
type G = Point;
type H = Shake256_114; type H = Shake256_114;
}
const ID: &'static [u8] = b"ed448"; impl GroupCanonicalEncoding for Point {
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
fn generator() -> Self::G { Self::G::from_bytes(bytes)
Point::generator()
} }
} }
#[test]
fn test_ed448() {
ff_group_tests::group::test_prime_group_bits::<_, Point>(&mut rand_core::OsRng);
}

View File

@@ -29,7 +29,6 @@ mod point;
pub use point::Point; pub use point::Point;
mod ciphersuite; mod ciphersuite;
pub use crate::ciphersuite::Ed448;
pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 { pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
use core::hint::black_box; use core::hint::black_box;

View File

@@ -25,12 +25,11 @@ typenum = { version = "1", default-features = false }
prime-field = { path = "../prime-field", default-features = false } prime-field = { path = "../prime-field", default-features = false }
short-weierstrass = { path = "../short-weierstrass", default-features = false } short-weierstrass = { path = "../short-weierstrass", default-features = false }
curve25519-dalek = { version = "4", default-features = false, features = ["legacy_compatibility"] } curve25519-dalek = { version = "4", default-features = false, features = ["legacy_compatibility"] }
dalek-ff-group = { path = "../dalek-ff-group", version = "0.4", default-features = false }
blake2 = { version = "0.11.0-rc.0", default-features = false } blake2 = { version = "0.11.0-rc.2", default-features = false }
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false } ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false, optional = true } generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false, optional = true }
[dev-dependencies] [dev-dependencies]
hex = "0.4" hex = "0.4"

View File

@@ -5,17 +5,14 @@
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[allow(unused_imports)] #[allow(unused_imports)]
use std_shims::prelude::*; use std_shims::prelude::*;
#[cfg(feature = "alloc")]
use std_shims::io::{self, Read};
use prime_field::{subtle::Choice, zeroize::Zeroize}; use prime_field::{
use ciphersuite::group::{ subtle::{Choice, CtOption},
ff::{Field, PrimeField}, zeroize::Zeroize,
Group,
}; };
use ciphersuite::group::{ff::PrimeField, Group, GroupEncoding};
use curve25519_dalek::Scalar as DalekScalar; pub use curve25519_dalek::Scalar as FieldElement;
pub use dalek_ff_group::Scalar as FieldElement;
use short_weierstrass::{ShortWeierstrass, Affine, Projective}; use short_weierstrass::{ShortWeierstrass, Affine, Projective};
@@ -32,18 +29,18 @@ pub struct Embedwards25519;
#[allow(deprecated)] // No other way to construct arbitrary `FieldElement` at compile-time :/ #[allow(deprecated)] // No other way to construct arbitrary `FieldElement` at compile-time :/
impl ShortWeierstrass for Embedwards25519 { impl ShortWeierstrass for Embedwards25519 {
type FieldElement = FieldElement; type FieldElement = FieldElement;
const A: FieldElement = FieldElement(DalekScalar::from_bits(hex_literal::hex!( const A: FieldElement = FieldElement::from_bits(hex_literal::hex!(
"ead3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010" "ead3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"
))); ));
const B: FieldElement = FieldElement(DalekScalar::from_bits(hex_literal::hex!( const B: FieldElement = FieldElement::from_bits(hex_literal::hex!(
"5f07603a853f20370b682036210d463e64903a23ea669d07ca26cfc13f594209" "5f07603a853f20370b682036210d463e64903a23ea669d07ca26cfc13f594209"
))); ));
const PRIME_ORDER: bool = true; const PRIME_ORDER: bool = true;
const GENERATOR: Affine<Self> = Affine::from_xy_unchecked( const GENERATOR: Affine<Self> = Affine::from_xy_unchecked(
FieldElement::ONE, FieldElement::ONE,
FieldElement(DalekScalar::from_bits(hex_literal::hex!( FieldElement::from_bits(hex_literal::hex!(
"2e4118080a484a3dfbafe2199a0e36b7193581d676c0dadfa376b0265616020c" "2e4118080a484a3dfbafe2199a0e36b7193581d676c0dadfa376b0265616020c"
))), )),
); );
type Scalar = Scalar; type Scalar = Scalar;
@@ -80,30 +77,23 @@ impl ShortWeierstrass for Embedwards25519 {
pub type Point = Projective<Embedwards25519>; pub type Point = Projective<Embedwards25519>;
impl ciphersuite::Ciphersuite for Embedwards25519 { impl ciphersuite::WrappedGroup for Embedwards25519 {
type F = Scalar; type F = Scalar;
type G = Point; type G = Point;
type H = blake2::Blake2b512;
const ID: &'static [u8] = b"embedwards25519";
fn generator() -> Self::G { fn generator() -> Self::G {
Point::generator() <Point as Group>::generator()
} }
}
// We override the provided impl, which compares against the reserialization, because impl ciphersuite::Id for Embedwards25519 {
// we already require canonicity const ID: &[u8] = b"embedwards25519";
#[cfg(feature = "alloc")] }
#[allow(non_snake_case)] impl ciphersuite::WithPreferredHash for Embedwards25519 {
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> { type H = blake2::Blake2b512;
use ciphersuite::group::GroupEncoding; }
impl ciphersuite::GroupCanonicalEncoding for Embedwards25519 {
let mut encoding = <Self::G as GroupEncoding>::Repr::default(); fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
reader.read_exact(encoding.as_mut())?; Self::G::from_bytes(bytes)
let point = Option::<Self::G>::from(Self::G::from_bytes(&encoding))
.ok_or_else(|| io::Error::other("invalid point"))?;
Ok(point)
} }
} }
@@ -119,9 +109,8 @@ fn test_curve() {
#[test] #[test]
fn generator() { fn generator() {
use ciphersuite::group::{Group, GroupEncoding};
assert_eq!( assert_eq!(
Point::generator(), <Point as Group>::generator(),
Point::from_bytes(&hex_literal::hex!( Point::from_bytes(&hex_literal::hex!(
"0100000000000000000000000000000000000000000000000000000000000000" "0100000000000000000000000000000000000000000000000000000000000000"
)) ))
@@ -139,6 +128,5 @@ fn zero_x_is_off_curve() {
// Checks random won't infinitely loop // Checks random won't infinitely loop
#[test] #[test]
fn random() { fn random() {
use ciphersuite::group::Group;
Point::random(&mut rand_core::OsRng); Point::random(&mut rand_core::OsRng);
} }

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "modular-frost" name = "modular-frost"
version = "0.10.1" version = "0.11.0"
description = "Modular implementation of FROST over ff/group" description = "Modular implementation of FROST over ff/group"
license = "MIT" license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/frost" repository = "https://github.com/serai-dex/serai/tree/develop/crypto/frost"
@@ -29,7 +29,7 @@ hex = { version = "0.4", default-features = false, features = ["std"], optional
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["std", "recommended"] } transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, features = ["std", "recommended"] }
dalek-ff-group = { path = "../dalek-ff-group", version = "0.4", default-features = false, features = ["std"], optional = true } dalek-ff-group = { path = "../dalek-ff-group", version = "0.5", default-features = false, features = ["std"], optional = true }
minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, features = ["std"], optional = true } minimal-ed448 = { path = "../ed448", version = "0.4", default-features = false, features = ["std"], optional = true }
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["std"] } ciphersuite = { path = "../ciphersuite", version = "^0.4.1", default-features = false, features = ["std"] }

View File

@@ -1,24 +1,24 @@
use ciphersuite::{digest::Digest, FromUniformBytes, Ciphersuite}; use subtle::CtOption;
use zeroize::Zeroize;
use ciphersuite::{
digest::Digest, group::GroupEncoding, FromUniformBytes, WrappedGroup, WithPreferredHash,
};
use dalek_ff_group::Scalar; use dalek_ff_group::Scalar;
use crate::{curve::Curve, algorithm::Hram}; use crate::{curve::Curve, algorithm::Hram};
macro_rules! dalek_curve { macro_rules! dalek_curve {
( (
$feature: literal,
$Curve: ident, $Curve: ident,
$Hram: ident, $Hram: ident,
$CONTEXT: literal, $CONTEXT: literal,
$chal: literal $chal: literal
) => { ) => {
pub use dalek_ff_group::$Curve;
impl Curve for $Curve { impl Curve for $Curve {
const CONTEXT: &'static [u8] = $CONTEXT; const CONTEXT: &'static [u8] = $CONTEXT;
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F { fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
let mut digest = <Self as Ciphersuite>::H::new(); let mut digest = <Self as WithPreferredHash>::H::new();
digest.update(Self::CONTEXT); digest.update(Self::CONTEXT);
digest.update(dst); digest.update(dst);
digest.update(msg); digest.update(msg);
@@ -31,8 +31,12 @@ macro_rules! dalek_curve {
pub struct $Hram; pub struct $Hram;
impl Hram<$Curve> for $Hram { impl Hram<$Curve> for $Hram {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hram(R: &<$Curve as Ciphersuite>::G, A: &<$Curve as Ciphersuite>::G, m: &[u8]) -> Scalar { fn hram(
let mut hash = <$Curve as Ciphersuite>::H::new(); R: &<$Curve as WrappedGroup>::G,
A: &<$Curve as WrappedGroup>::G,
m: &[u8],
) -> Scalar {
let mut hash = <$Curve as WithPreferredHash>::H::new();
if $chal.len() != 0 { if $chal.len() != 0 {
hash.update($CONTEXT); hash.update($CONTEXT);
hash.update($chal); hash.update($chal);
@@ -46,8 +50,31 @@ macro_rules! dalek_curve {
}; };
} }
#[cfg(feature = "ristretto")] /*
dalek_curve!("ristretto", Ristretto, IetfRistrettoHram, b"FROST-RISTRETTO255-SHA512-v1", b"chal"); FROST defined Ristretto as using SHA2-512, while Blake2b512 is considered "preferred" by
`dalek-ff-group`. We define our own ciphersuite for it accordingly.
*/
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct Ristretto;
impl WrappedGroup for Ristretto {
type F = Scalar;
type G = dalek_ff_group::RistrettoPoint;
fn generator() -> Self::G {
dalek_ff_group::Ristretto::generator()
}
}
impl ciphersuite::Id for Ristretto {
const ID: &[u8] = b"FROST-RISTRETTO255";
}
impl WithPreferredHash for Ristretto {
type H = <Ed25519 as WithPreferredHash>::H;
}
impl ciphersuite::GroupCanonicalEncoding for Ristretto {
fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
dalek_ff_group::Ristretto::from_canonical_bytes(bytes)
}
}
dalek_curve!(Ristretto, IetfRistrettoHram, b"FROST-RISTRETTO255-SHA512-v1", b"chal");
#[cfg(feature = "ed25519")] pub use dalek_ff_group::Ed25519;
dalek_curve!("ed25519", Ed25519, IetfEd25519Hram, b"FROST-ED25519-SHA512-v1", b""); dalek_curve!(Ed25519, IetfEd25519Hram, b"FROST-ED25519-SHA512-v1", b"");

View File

@@ -1,6 +1,6 @@
pub use ciphersuite::{digest::Digest, group::GroupEncoding, FromUniformBytes, Ciphersuite}; pub use ciphersuite::{digest::Digest, group::GroupEncoding, FromUniformBytes, WithPreferredHash};
use minimal_ed448::{Scalar, Point}; use minimal_ed448::Scalar;
pub use minimal_ed448::Ed448; pub use minimal_ed448::Point as Ed448;
use crate::{curve::Curve, algorithm::Hram}; use crate::{curve::Curve, algorithm::Hram};
@@ -9,7 +9,7 @@ const CONTEXT: &[u8] = b"FROST-ED448-SHAKE256-v1";
impl Curve for Ed448 { impl Curve for Ed448 {
const CONTEXT: &'static [u8] = CONTEXT; const CONTEXT: &'static [u8] = CONTEXT;
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F { fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
let mut digest = <Self as Ciphersuite>::H::new(); let mut digest = <Self as WithPreferredHash>::H::new();
digest.update(Self::CONTEXT); digest.update(Self::CONTEXT);
digest.update(dst); digest.update(dst);
digest.update(msg); digest.update(msg);
@@ -22,8 +22,8 @@ impl Curve for Ed448 {
pub(crate) struct Ietf8032Ed448Hram; pub(crate) struct Ietf8032Ed448Hram;
impl Ietf8032Ed448Hram { impl Ietf8032Ed448Hram {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub(crate) fn hram(context: &[u8], R: &Point, A: &Point, m: &[u8]) -> Scalar { pub(crate) fn hram(context: &[u8], R: &Ed448, A: &Ed448, m: &[u8]) -> Scalar {
let mut digest = <Ed448 as Ciphersuite>::H::new(); let mut digest = <Ed448 as WithPreferredHash>::H::new();
digest.update(b"SigEd448"); digest.update(b"SigEd448");
digest.update([0, u8::try_from(context.len()).unwrap()]); digest.update([0, u8::try_from(context.len()).unwrap()]);
digest.update(context); digest.update(context);
@@ -39,7 +39,7 @@ impl Ietf8032Ed448Hram {
pub struct IetfEd448Hram; pub struct IetfEd448Hram;
impl Hram<Ed448> for IetfEd448Hram { impl Hram<Ed448> for IetfEd448Hram {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hram(R: &Point, A: &Point, m: &[u8]) -> Scalar { fn hram(R: &Ed448, A: &Ed448, m: &[u8]) -> Scalar {
Ietf8032Ed448Hram::hram(&[], R, A, m) Ietf8032Ed448Hram::hram(&[], R, A, m)
} }
} }

View File

@@ -7,7 +7,7 @@ use ciphersuite::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
GroupEncoding, GroupEncoding,
}, },
Ciphersuite, WrappedGroup,
}; };
use elliptic_curve::{ use elliptic_curve::{
@@ -20,7 +20,7 @@ use elliptic_curve::{
use crate::{curve::Curve, algorithm::Hram}; use crate::{curve::Curve, algorithm::Hram};
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hash_to_F<C: Ciphersuite<F: PrimeField<Repr = GenericArray<u8, U32>>>>( fn hash_to_F<C: WrappedGroup<F: PrimeField<Repr = GenericArray<u8, U32>>>>(
dst: &[u8], dst: &[u8],
msg: &[u8], msg: &[u8],
) -> C::F { ) -> C::F {
@@ -112,10 +112,10 @@ macro_rules! kp_curve {
impl Hram<$Curve> for $Hram { impl Hram<$Curve> for $Hram {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hram( fn hram(
R: &<$Curve as Ciphersuite>::G, R: &<$Curve as WrappedGroup>::G,
A: &<$Curve as Ciphersuite>::G, A: &<$Curve as WrappedGroup>::G,
m: &[u8], m: &[u8],
) -> <$Curve as Ciphersuite>::F { ) -> <$Curve as WrappedGroup>::F {
<$Curve as Curve>::hash_to_F( <$Curve as Curve>::hash_to_F(
b"chal", b"chal",
&[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat(), &[R.to_bytes().as_ref(), A.to_bytes().as_ref(), m].concat(),
@@ -132,7 +132,7 @@ kp_curve!("p256", P256, IetfP256Hram, b"FROST-P256-SHA256-v1");
kp_curve!("secp256k1", Secp256k1, IetfSecp256k1Hram, b"FROST-secp256k1-SHA256-v1"); kp_curve!("secp256k1", Secp256k1, IetfSecp256k1Hram, b"FROST-secp256k1-SHA256-v1");
#[cfg(test)] #[cfg(test)]
fn test_oversize_dst<C: Ciphersuite<F: PrimeField<Repr = GenericArray<u8, U32>>>>() { fn test_oversize_dst<C: WrappedGroup<F: PrimeField<Repr = GenericArray<u8, U32>>>>() {
use sha2::Digest; use sha2::Digest;
// The draft specifies DSTs >255 bytes should be hashed into a 32-byte DST // The draft specifies DSTs >255 bytes should be hashed into a 32-byte DST

View File

@@ -6,21 +6,16 @@ use rand_core::{RngCore, CryptoRng};
use zeroize::{Zeroize, Zeroizing}; use zeroize::{Zeroize, Zeroizing};
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
pub use ciphersuite::{ use ciphersuite::group::{
digest::Digest, ff::{Field, PrimeField},
group::{ Group,
ff::{Field, PrimeField},
Group,
},
Ciphersuite,
}; };
pub use ciphersuite::{digest::Digest, WrappedGroup, GroupIo, Ciphersuite};
#[cfg(any(feature = "ristretto", feature = "ed25519"))] #[cfg(any(feature = "ristretto", feature = "ed25519"))]
mod dalek; mod dalek;
#[cfg(feature = "ristretto")] #[cfg(any(feature = "ristretto", feature = "ed25519"))]
pub use dalek::{Ristretto, IetfRistrettoHram}; pub use dalek::*;
#[cfg(feature = "ed25519")]
pub use dalek::{Ed25519, IetfEd25519Hram};
#[cfg(any(feature = "secp256k1", feature = "p256"))] #[cfg(any(feature = "secp256k1", feature = "p256"))]
mod kp256; mod kp256;
@@ -38,11 +33,11 @@ pub(crate) use ed448::Ietf8032Ed448Hram;
/// FROST Ciphersuite. /// FROST Ciphersuite.
/// ///
/// This exclude the signing algorithm specific H2, making this solely the curve, its associated /// This excludes the signing algorithm specific H2, making this solely the curve, its associated
/// hash function, and the functions derived from it. /// hash function, and the functions derived from it.
pub trait Curve: Ciphersuite { pub trait Curve: GroupIo + Ciphersuite {
/// Context string for this curve. /// Context string for this curve.
const CONTEXT: &'static [u8]; const CONTEXT: &[u8];
/// Hash the given dst and data to a byte vector. Used to instantiate H4 and H5. /// Hash the given dst and data to a byte vector. Used to instantiate H4 and H5.
fn hash(dst: &[u8], data: &[u8]) -> impl AsRef<[u8]> { fn hash(dst: &[u8], data: &[u8]) -> impl AsRef<[u8]> {
@@ -121,7 +116,7 @@ pub trait Curve: Ciphersuite {
/// Read a point from a reader, rejecting identity. /// Read a point from a reader, rejecting identity.
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> { fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
let res = <Self as Ciphersuite>::read_G(reader)?; let res = <Self as GroupIo>::read_G(reader)?;
if res.is_identity().into() { if res.is_identity().into() {
Err(io::Error::other("identity point"))?; Err(io::Error::other("identity point"))?;
} }

View File

@@ -11,10 +11,9 @@ use zeroize::{Zeroize, Zeroizing};
use transcript::Transcript; use transcript::Transcript;
use ciphersuite::group::{ use ciphersuite::group::{ff::PrimeField, GroupEncoding};
ff::{Field, PrimeField}, #[cfg(any(test, feature = "tests"))]
GroupEncoding, use ciphersuite::group::ff::Field;
};
use multiexp::BatchVerifier; use multiexp::BatchVerifier;
use crate::{ use crate::{

View File

@@ -1,6 +1,6 @@
use rand_core::OsRng; use rand_core::OsRng;
use ciphersuite::Ciphersuite; use ciphersuite::GroupIo;
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use ciphersuite::Ciphersuite; use ciphersuite::{GroupIo, Id};
pub use dkg_recovery::recover_key; pub use dkg_recovery::recover_key;
use crate::{ use crate::{
@@ -28,7 +28,7 @@ pub const PARTICIPANTS: u16 = 5;
pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1; pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1;
/// Create a key, for testing purposes. /// Create a key, for testing purposes.
pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>( pub fn key_gen<R: RngCore + CryptoRng, C: GroupIo + Id>(
rng: &mut R, rng: &mut R,
) -> HashMap<Participant, ThresholdKeys<C>> { ) -> HashMap<Participant, ThresholdKeys<C>> {
let res = dkg_dealer::key_gen::<R, C>(rng, THRESHOLD, PARTICIPANTS).unwrap(); let res = dkg_dealer::key_gen::<R, C>(rng, THRESHOLD, PARTICIPANTS).unwrap();

View File

@@ -22,6 +22,7 @@ std-shims = { path = "../../common/std-shims", version = "^0.1.1", default-featu
rand_core = { version = "0.6", default-features = false } rand_core = { version = "0.6", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] } zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
digest = { version = "0.11.0-rc.1", default-features = false, features = ["block-api"] }
transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, optional = true } transcript = { package = "flexible-transcript", path = "../transcript", version = "^0.3.2", default-features = false, optional = true }

View File

@@ -5,74 +5,34 @@ use std_shims::{
use zeroize::Zeroize; use zeroize::Zeroize;
use transcript::{Transcript, SecureDigest, DigestTranscript}; use transcript::{Transcript, DigestTranscript};
use ciphersuite::{ use ciphersuite::{
group::{ group::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
Group, GroupEncoding, Group, GroupEncoding,
}, },
Ciphersuite, FromUniformBytes, GroupIo, WithPreferredHash,
}; };
use multiexp::multiexp_vartime; use multiexp::multiexp_vartime;
use crate::SchnorrSignature; use crate::SchnorrSignature;
// Returns a unbiased scalar weight to use on a signature in order to prevent malleability // Returns a unbiased scalar weight to use on a signature in order to prevent malleability
fn weight<D: Send + Clone + SecureDigest, F: PrimeField>(digest: &mut DigestTranscript<D>) -> F { fn weight<C: WithPreferredHash>(digest: &mut DigestTranscript<C::H>) -> C::F {
let mut bytes = digest.challenge(b"aggregation_weight"); let bytes = digest.challenge(b"aggregation_weight");
debug_assert_eq!(bytes.len() % 8, 0); C::F::from_uniform_bytes(&bytes.into())
// This should be guaranteed thanks to SecureDigest
debug_assert!(bytes.len() >= 32);
let mut res = F::ZERO;
let mut i = 0;
// Derive a scalar from enough bits of entropy that bias is < 2^128
// This can't be const due to its usage of a generic
// Also due to the usize::try_from, yet that could be replaced with an `as`
#[allow(non_snake_case)]
let BYTES: usize = usize::try_from((F::NUM_BITS + 128).div_ceil(8)).unwrap();
let mut remaining = BYTES;
// We load bits in as u64s
const WORD_LEN_IN_BITS: usize = 64;
const WORD_LEN_IN_BYTES: usize = WORD_LEN_IN_BITS / 8;
let mut first = true;
while i < remaining {
// Shift over the already loaded bits
if !first {
for _ in 0 .. WORD_LEN_IN_BITS {
res += res;
}
}
first = false;
// Add the next 64 bits
res += F::from(u64::from_be_bytes(bytes[i .. (i + WORD_LEN_IN_BYTES)].try_into().unwrap()));
i += WORD_LEN_IN_BYTES;
// If we've exhausted this challenge, get another
if i == bytes.len() {
bytes = digest.challenge(b"aggregation_weight_continued");
remaining -= i;
i = 0;
}
}
res
} }
/// Aggregate Schnorr signature as defined in <https://eprint.iacr.org/2021/350>. /// Aggregate Schnorr signature as defined in <https://eprint.iacr.org/2021/350>.
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct SchnorrAggregate<C: Ciphersuite> { pub struct SchnorrAggregate<C: GroupIo + WithPreferredHash> {
Rs: Vec<C::G>, Rs: Vec<C::G>,
s: C::F, s: C::F,
} }
impl<C: Ciphersuite> SchnorrAggregate<C> { impl<C: GroupIo + WithPreferredHash> SchnorrAggregate<C> {
/// Read a SchnorrAggregate from something implementing Read. /// Read a SchnorrAggregate from something implementing Read.
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> { pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
let mut len = [0; 4]; let mut len = [0; 4];
@@ -137,7 +97,7 @@ impl<C: Ciphersuite> SchnorrAggregate<C> {
let mut pairs = Vec::with_capacity((2 * keys_and_challenges.len()) + 1); let mut pairs = Vec::with_capacity((2 * keys_and_challenges.len()) + 1);
for (i, (key, challenge)) in keys_and_challenges.iter().enumerate() { for (i, (key, challenge)) in keys_and_challenges.iter().enumerate() {
let z = weight(&mut digest); let z = weight::<C>(&mut digest);
pairs.push((z, self.Rs[i])); pairs.push((z, self.Rs[i]));
pairs.push((z * challenge, *key)); pairs.push((z * challenge, *key));
} }
@@ -148,13 +108,22 @@ impl<C: Ciphersuite> SchnorrAggregate<C> {
/// A signature aggregator capable of consuming signatures in order to produce an aggregate. /// A signature aggregator capable of consuming signatures in order to produce an aggregate.
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, Debug, Zeroize)] #[derive(Clone, Debug)]
pub struct SchnorrAggregator<C: Ciphersuite> { pub struct SchnorrAggregator<C: GroupIo + WithPreferredHash> {
digest: DigestTranscript<C::H>, digest: DigestTranscript<C::H>,
sigs: Vec<SchnorrSignature<C>>, sigs: Vec<SchnorrSignature<C>>,
} }
impl<C: GroupIo + WithPreferredHash> Zeroize for SchnorrAggregator<C>
where
C::H: digest::block_api::BlockSizeUser,
{
fn zeroize(&mut self) {
self.digest.zeroize();
self.sigs.zeroize();
}
}
impl<C: Ciphersuite> SchnorrAggregator<C> { impl<C: GroupIo + WithPreferredHash> SchnorrAggregator<C> {
/// Create a new aggregator. /// Create a new aggregator.
/// ///
/// The DST used here must prevent a collision with whatever hash function produced the /// The DST used here must prevent a collision with whatever hash function produced the
@@ -180,7 +149,7 @@ impl<C: Ciphersuite> SchnorrAggregator<C> {
let mut aggregate = SchnorrAggregate { Rs: Vec::with_capacity(self.sigs.len()), s: C::F::ZERO }; let mut aggregate = SchnorrAggregate { Rs: Vec::with_capacity(self.sigs.len()), s: C::F::ZERO };
for i in 0 .. self.sigs.len() { for i in 0 .. self.sigs.len() {
aggregate.Rs.push(self.sigs[i].R); aggregate.Rs.push(self.sigs[i].R);
aggregate.s += self.sigs[i].s * weight::<_, C::F>(&mut self.digest); aggregate.s += self.sigs[i].s * weight::<C>(&mut self.digest);
} }
Some(aggregate) Some(aggregate)
} }

View File

@@ -20,7 +20,7 @@ use ciphersuite::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
Group, GroupEncoding, Group, GroupEncoding,
}, },
Ciphersuite, GroupIo,
}; };
use multiexp::{multiexp_vartime, BatchVerifier}; use multiexp::{multiexp_vartime, BatchVerifier};
@@ -33,20 +33,20 @@ mod tests;
/// A Schnorr signature of the form (R, s) where s = r + cx. /// A Schnorr signature of the form (R, s) where s = r + cx.
/// ///
/// These are intended to be strict. It is generic over Ciphersuite which is for PrimeGroups, /// These are intended to be strict. It is generic over `GroupIo` which is for `PrimeGroup`s,
/// and mandates canonical encodings in its read function. /// and mandates canonical encodings in its read function.
/// ///
/// RFC 8032 has an alternative verification formula, 8R = 8s - 8cX, which is intended to handle /// RFC 8032 has an alternative verification formula for Ed25519, `8R = 8s - 8cX`, which is
/// torsioned nonces/public keys. Due to this library's strict requirements, such signatures will /// intended to handle torsioned nonces/public keys. Due to this library's strict requirements,
/// not be verifiable with this library. /// such signatures will not be verifiable with this library.
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct SchnorrSignature<C: Ciphersuite> { pub struct SchnorrSignature<C: GroupIo> {
pub R: C::G, pub R: C::G,
pub s: C::F, pub s: C::F,
} }
impl<C: Ciphersuite> SchnorrSignature<C> { impl<C: GroupIo> SchnorrSignature<C> {
/// Read a SchnorrSignature from something implementing Read. /// Read a SchnorrSignature from something implementing Read.
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> { pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
Ok(SchnorrSignature { R: C::read_G(reader)?, s: C::read_F(reader)? }) Ok(SchnorrSignature { R: C::read_G(reader)?, s: C::read_F(reader)? })

View File

@@ -6,7 +6,7 @@ use rand_core::OsRng;
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use ciphersuite::{ use ciphersuite::{
group::{ff::Field, Group}, group::{ff::Field, Group},
Ciphersuite, GroupIo, WithPreferredHash,
}; };
use multiexp::BatchVerifier; use multiexp::BatchVerifier;
@@ -16,10 +16,10 @@ use crate::aggregate::{SchnorrAggregator, SchnorrAggregate};
mod rfc8032; mod rfc8032;
pub(crate) fn sign<C: Ciphersuite>() { pub(crate) fn sign<C: GroupIo>() {
let private_key = Zeroizing::new(C::random_nonzero_F(&mut OsRng)); let private_key = Zeroizing::new(C::F::random(&mut OsRng));
let nonce = Zeroizing::new(C::random_nonzero_F(&mut OsRng)); let nonce = Zeroizing::new(C::F::random(&mut OsRng));
let challenge = C::random_nonzero_F(&mut OsRng); // Doesn't bother to craft an HRAm let challenge = C::F::random(&mut OsRng); // Doesn't bother to craft an HRAm
assert!(SchnorrSignature::<C>::sign(&private_key, nonce, challenge) assert!(SchnorrSignature::<C>::sign(&private_key, nonce, challenge)
.verify(C::generator() * private_key.deref(), challenge)); .verify(C::generator() * private_key.deref(), challenge));
} }
@@ -27,22 +27,22 @@ pub(crate) fn sign<C: Ciphersuite>() {
// The above sign function verifies signing works // The above sign function verifies signing works
// This verifies invalid signatures don't pass, using zero signatures, which should effectively be // This verifies invalid signatures don't pass, using zero signatures, which should effectively be
// random // random
pub(crate) fn verify<C: Ciphersuite>() { pub(crate) fn verify<C: GroupIo>() {
assert!(!SchnorrSignature::<C> { R: C::G::identity(), s: C::F::ZERO } assert!(!SchnorrSignature::<C> { R: C::G::identity(), s: C::F::ZERO }
.verify(C::generator() * C::random_nonzero_F(&mut OsRng), C::random_nonzero_F(&mut OsRng))); .verify(C::generator() * C::F::random(&mut OsRng), C::F::random(&mut OsRng)));
} }
pub(crate) fn batch_verify<C: Ciphersuite>() { pub(crate) fn batch_verify<C: GroupIo>() {
// Create 5 signatures // Create 5 signatures
let mut keys = vec![]; let mut keys = vec![];
let mut challenges = vec![]; let mut challenges = vec![];
let mut sigs = vec![]; let mut sigs = vec![];
for i in 0 .. 5 { for i in 0 .. 5 {
keys.push(Zeroizing::new(C::random_nonzero_F(&mut OsRng))); keys.push(Zeroizing::new(C::F::random(&mut OsRng)));
challenges.push(C::random_nonzero_F(&mut OsRng)); challenges.push(C::F::random(&mut OsRng));
sigs.push(SchnorrSignature::<C>::sign( sigs.push(SchnorrSignature::<C>::sign(
&keys[i], &keys[i],
Zeroizing::new(C::random_nonzero_F(&mut OsRng)), Zeroizing::new(C::F::random(&mut OsRng)),
challenges[i], challenges[i],
)); ));
} }
@@ -78,7 +78,7 @@ pub(crate) fn batch_verify<C: Ciphersuite>() {
} }
#[cfg(feature = "aggregate")] #[cfg(feature = "aggregate")]
pub(crate) fn aggregate<C: Ciphersuite>() { pub(crate) fn aggregate<C: GroupIo + WithPreferredHash>() {
const DST: &[u8] = b"Schnorr Aggregator Test"; const DST: &[u8] = b"Schnorr Aggregator Test";
// Create 5 signatures // Create 5 signatures
@@ -86,14 +86,14 @@ pub(crate) fn aggregate<C: Ciphersuite>() {
let mut challenges = vec![]; let mut challenges = vec![];
let mut aggregator = SchnorrAggregator::<C>::new(DST); let mut aggregator = SchnorrAggregator::<C>::new(DST);
for i in 0 .. 5 { for i in 0 .. 5 {
keys.push(Zeroizing::new(C::random_nonzero_F(&mut OsRng))); keys.push(Zeroizing::new(C::F::random(&mut OsRng)));
// In practice, this MUST be a secure challenge binding to the nonce, key, and any message // In practice, this MUST be a secure challenge binding to the nonce, key, and any message
challenges.push(C::random_nonzero_F(&mut OsRng)); challenges.push(C::F::random(&mut OsRng));
aggregator.aggregate( aggregator.aggregate(
challenges[i], challenges[i],
SchnorrSignature::<C>::sign( SchnorrSignature::<C>::sign(
&keys[i], &keys[i],
Zeroizing::new(C::random_nonzero_F(&mut OsRng)), Zeroizing::new(C::F::random(&mut OsRng)),
challenges[i], challenges[i],
), ),
); );

View File

@@ -6,7 +6,7 @@
use sha2::{Digest, Sha512}; use sha2::{Digest, Sha512};
use dalek_ff_group::{Scalar, Ed25519}; use dalek_ff_group::{Scalar, Ed25519};
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, GroupIo};
use crate::SchnorrSignature; use crate::SchnorrSignature;

View File

@@ -24,10 +24,9 @@ transcript = { package = "flexible-transcript", path = "../transcript", version
group = "0.13" group = "0.13"
dalek-ff-group = { path = "../dalek-ff-group" }
ciphersuite = { path = "../ciphersuite", version = "^0.4.1", features = ["std"] } ciphersuite = { path = "../ciphersuite", version = "^0.4.1", features = ["std"] }
schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1" } schnorr = { package = "schnorr-signatures", path = "../schnorr", version = "^0.5.1" }
frost = { path = "../frost", package = "modular-frost", version = "^0.10.0", features = ["ristretto"] } frost = { path = "../frost", package = "modular-frost", version = "0.11.0", features = ["ristretto"] }
schnorrkel = { version = "0.11" } schnorrkel = { version = "0.11" }

View File

@@ -9,16 +9,16 @@ use zeroize::Zeroizing;
use transcript::{Transcript, MerlinTranscript}; use transcript::{Transcript, MerlinTranscript};
use dalek_ff_group::Ristretto;
use ciphersuite::{ use ciphersuite::{
group::{ff::PrimeField, GroupEncoding}, group::{ff::PrimeField, GroupEncoding},
Ciphersuite, WrappedGroup,
}; };
use schnorr::SchnorrSignature; use schnorr::SchnorrSignature;
use ::frost::{ use ::frost::{
Participant, ThresholdKeys, ThresholdView, FrostError, Participant, ThresholdKeys, ThresholdView, FrostError,
algorithm::{Hram, Algorithm, Schnorr}, algorithm::{Hram, Algorithm, Schnorr},
curve::Ristretto,
}; };
/// The [modular-frost](https://docs.rs/modular-frost) library. /// The [modular-frost](https://docs.rs/modular-frost) library.
@@ -28,8 +28,8 @@ pub mod frost {
use schnorrkel::{PublicKey, Signature, context::SigningTranscript, signing_context}; use schnorrkel::{PublicKey, Signature, context::SigningTranscript, signing_context};
type RistrettoPoint = <Ristretto as Ciphersuite>::G; type RistrettoPoint = <Ristretto as WrappedGroup>::G;
type Scalar = <Ristretto as Ciphersuite>::F; type Scalar = <Ristretto as WrappedGroup>::F;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -83,7 +83,7 @@ impl Algorithm<Ristretto> for Schnorrkel {
self.schnorr.transcript() self.schnorr.transcript()
} }
fn nonces(&self) -> Vec<Vec<<Ristretto as Ciphersuite>::G>> { fn nonces(&self) -> Vec<Vec<<Ristretto as WrappedGroup>::G>> {
self.schnorr.nonces() self.schnorr.nonces()
} }

View File

@@ -18,13 +18,13 @@ hex-literal = { version = "0.4", default-features = false }
std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true } std-shims = { version = "0.1", path = "../../common/std-shims", default-features = false, optional = true }
k256 = { version = "0.13", default-features = false, features = ["arithmetic"] } sha2 = { version = "0.11.0-rc.0", default-features = false }
k256 = { version = "0.13", default-features = false, features = ["arithmetic", "expose-field"] }
prime-field = { path = "../prime-field", default-features = false } prime-field = { path = "../prime-field", default-features = false }
short-weierstrass = { path = "../short-weierstrass", default-features = false } short-weierstrass = { path = "../short-weierstrass", default-features = false }
sha2 = { version = "0.11.0-rc.0", default-features = false }
ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false } ciphersuite = { path = "../ciphersuite", version = "0.4", default-features = false }
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false, optional = true } generalized-bulletproofs-ec-gadgets = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false, optional = true }
[dev-dependencies] [dev-dependencies]
hex = "0.4" hex = "0.4"

View File

@@ -5,17 +5,12 @@
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[allow(unused_imports)] #[allow(unused_imports)]
use std_shims::prelude::*; use std_shims::prelude::*;
#[cfg(feature = "alloc")]
use std_shims::io::{self, Read};
use sha2::{ use sha2::digest::array::{typenum::U33, Array};
digest::array::{typenum::U33, Array},
Sha512,
};
use k256::elliptic_curve::{ use k256::elliptic_curve::{
subtle::{Choice, ConstantTimeEq, ConditionallySelectable}, subtle::{Choice, CtOption, ConstantTimeEq, ConditionallySelectable},
zeroize::Zeroize, zeroize::Zeroize,
group::{ff::PrimeField, Group}, group::{ff::PrimeField, Group, GroupEncoding},
sec1::Tag, sec1::Tag,
}; };
@@ -109,32 +104,25 @@ impl ShortWeierstrass for Secq256k1 {
pub type Point = Projective<Secq256k1>; pub type Point = Projective<Secq256k1>;
impl ciphersuite::Ciphersuite for Secq256k1 { impl ciphersuite::WrappedGroup for Secq256k1 {
type F = Scalar; type F = Scalar;
type G = Point; type G = Point;
type H = Sha512;
const ID: &'static [u8] = b"secq256k1";
fn generator() -> Self::G { fn generator() -> Self::G {
Point::generator() <Point as Group>::generator()
} }
}
// We override the provided impl, which compares against the reserialization, because impl ciphersuite::Id for Secq256k1 {
// we already require canonicity const ID: &[u8] = b"secq256k1";
#[cfg(feature = "alloc")] }
#[allow(non_snake_case)] impl ciphersuite::GroupCanonicalEncoding for Secq256k1 {
fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> { fn from_canonical_bytes(bytes: &<Self::G as GroupEncoding>::Repr) -> CtOption<Self::G> {
use ciphersuite::group::GroupEncoding; Self::G::from_bytes(bytes)
let mut encoding = <Self::G as GroupEncoding>::Repr::default();
reader.read_exact(encoding.as_mut())?;
let point = Option::<Self::G>::from(Self::G::from_bytes(&encoding))
.ok_or_else(|| io::Error::other("invalid point"))?;
Ok(point)
} }
} }
impl ciphersuite::WithPreferredHash for Secq256k1 {
type H = sha2::Sha512;
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Secq256k1 { impl generalized_bulletproofs_ec_gadgets::DiscreteLogParameter for Secq256k1 {
@@ -150,7 +138,7 @@ fn test_curve() {
fn generator() { fn generator() {
use ciphersuite::group::GroupEncoding; use ciphersuite::group::GroupEncoding;
assert_eq!( assert_eq!(
Point::generator(), <Point as Group>::generator(),
Point::from_bytes(&Array(hex_literal::hex!( Point::from_bytes(&Array(hex_literal::hex!(
"020000000000000000000000000000000000000000000000000000000000000001" "020000000000000000000000000000000000000000000000000000000000000001"
))) )))

View File

@@ -21,7 +21,7 @@ rand_core = { version = "0.6", default-features = false }
ff = { version = "0.13", default-features = false, features = ["bits"] } ff = { version = "0.13", default-features = false, features = ["bits"] }
group = { version = "0.13", default-features = false } group = { version = "0.13", default-features = false }
ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "a6f8797007e768488568b821435cf5006517a962", default-features = false, optional = true } ec-divisors = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7216a2e84c7671c167c3d81eafe0d2b1f418f102", default-features = false, optional = true }
[features] [features]
alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc", "ec-divisors"] alloc = ["zeroize/alloc", "rand_core/alloc", "ff/alloc", "group/alloc", "ec-divisors"]

View File

@@ -413,7 +413,6 @@ impl<C: ShortWeierstrass<Scalar: PrimeFieldBits>> PrimeGroup for Projective<C> {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
mod alloc { mod alloc {
use core::borrow::Borrow;
use ff::{PrimeField, PrimeFieldBits}; use ff::{PrimeField, PrimeFieldBits};
use crate::{ShortWeierstrass, Affine, Projective}; use crate::{ShortWeierstrass, Affine, Projective};
@@ -421,7 +420,8 @@ mod alloc {
type FieldElement = C::FieldElement; type FieldElement = C::FieldElement;
type XyPoint = ec_divisors::Projective<Self>; type XyPoint = ec_divisors::Projective<Self>;
fn interpolator_for_scalar_mul() -> impl Borrow<ec_divisors::Interpolator<C::FieldElement>> { type BorrowedInterpolator = ec_divisors::Interpolator<C::FieldElement>;
fn interpolator_for_scalar_mul() -> Self::BorrowedInterpolator {
ec_divisors::Interpolator::new((<C::Scalar as PrimeField>::NUM_BITS as usize).div_ceil(2) + 2) ec_divisors::Interpolator::new((<C::Scalar as PrimeField>::NUM_BITS as usize).div_ceil(2) + 2)
} }

View File

@@ -19,14 +19,14 @@ workspace = true
[dependencies] [dependencies]
zeroize = { version = "^1.5", default-features = false } zeroize = { version = "^1.5", default-features = false }
digest = { version = "0.11.0-rc.0", default-features = false, features = ["block-api"] } digest = { version = "0.11.0-rc.1", default-features = false, features = ["block-api"] }
blake2 = { version = "0.11.0-rc.0", default-features = false, optional = true } blake2 = { version = "0.11.0-rc.2", default-features = false, optional = true }
merlin = { version = "3", default-features = false, optional = true } merlin = { version = "3", default-features = false, optional = true }
[dev-dependencies] [dev-dependencies]
sha2 = { version = "0.11.0-rc.0", default-features = false } sha2 = { version = "0.11.0-rc.2", default-features = false }
blake2 = { version = "0.11.0-rc.0", default-features = false } blake2 = { version = "0.11.0-rc.2", default-features = false }
[features] [features]
std = ["zeroize/std", "merlin?/std"] std = ["zeroize/std", "merlin?/std"]

View File

@@ -4,13 +4,7 @@
use zeroize::Zeroize; use zeroize::Zeroize;
use digest::{ use digest::{block_api::BlockSizeUser, Digest, Output, HashMarker};
typenum::{
consts::U32, marker_traits::NonZero, type_operators::IsGreaterOrEqual, operator_aliases::GrEq,
},
block_api::BlockSizeUser,
Digest, Output, HashMarker,
};
#[cfg(feature = "merlin")] #[cfg(feature = "merlin")]
mod merlin; mod merlin;
@@ -75,24 +69,11 @@ impl DigestTranscriptMember {
} }
} }
/// A trait defining cryptographic Digests with at least a 256-bit output size, assuming at least a
/// 128-bit level of security accordingly.
pub trait SecureDigest: Digest + HashMarker {}
impl<D: Digest + HashMarker> SecureDigest for D
where
// This just lets us perform the comparison
D::OutputSize: IsGreaterOrEqual<U32>,
// Perform the comparison and make sure it's true (not zero), meaning D::OutputSize is >= U32
// This should be U32 as it's length in bytes, not bits
GrEq<D::OutputSize, U32>: NonZero,
{
}
/// A simple transcript format constructed around the specified hash algorithm. /// A simple transcript format constructed around the specified hash algorithm.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DigestTranscript<D: Send + Clone + SecureDigest>(D); pub struct DigestTranscript<D: Send + Clone + Digest + HashMarker>(D);
impl<D: Send + Clone + SecureDigest> DigestTranscript<D> { impl<D: Send + Clone + Digest + HashMarker> DigestTranscript<D> {
fn append(&mut self, kind: DigestTranscriptMember, value: &[u8]) { fn append(&mut self, kind: DigestTranscriptMember, value: &[u8]) {
self.0.update([kind.as_u8()]); self.0.update([kind.as_u8()]);
// Assumes messages don't exceed 16 exabytes // Assumes messages don't exceed 16 exabytes
@@ -101,7 +82,7 @@ impl<D: Send + Clone + SecureDigest> DigestTranscript<D> {
} }
} }
impl<D: Send + Clone + SecureDigest> Transcript for DigestTranscript<D> { impl<D: Send + Clone + Digest + HashMarker> Transcript for DigestTranscript<D> {
type Challenge = Output<D>; type Challenge = Output<D>;
fn new(name: &'static [u8]) -> Self { fn new(name: &'static [u8]) -> Self {
@@ -140,7 +121,7 @@ impl<D: Send + Clone + SecureDigest> Transcript for DigestTranscript<D> {
// Digest doesn't implement Zeroize // Digest doesn't implement Zeroize
// Implement Zeroize for DigestTranscript by writing twice the block size to the digest in an // Implement Zeroize for DigestTranscript by writing twice the block size to the digest in an
// attempt to overwrite the internal hash state/any leftover bytes // attempt to overwrite the internal hash state/any leftover bytes
impl<D: Send + Clone + SecureDigest> Zeroize for DigestTranscript<D> impl<D: Send + Clone + Digest + HashMarker> Zeroize for DigestTranscript<D>
where where
D: BlockSizeUser, D: BlockSizeUser,
{ {
@@ -159,7 +140,7 @@ where
// These writes may be optimized out if they're never read // These writes may be optimized out if they're never read
// Attempt to get them marked as read // Attempt to get them marked as read
fn mark_read<D: Send + Clone + SecureDigest>(transcript: &DigestTranscript<D>) { fn mark_read<D: Send + Clone + Digest + HashMarker>(transcript: &DigestTranscript<D>) {
// Just get a challenge from the state // Just get a challenge from the state
let mut challenge = core::hint::black_box(transcript.0.clone().finalize()); let mut challenge = core::hint::black_box(transcript.0.clone().finalize());
challenge.as_mut().zeroize(); challenge.as_mut().zeroize();

View File

@@ -29,7 +29,7 @@ zeroize = { version = "1", default-features = false, features = ["std"] }
rand_core = { version = "0.6", default-features = false, features = ["std"] } rand_core = { version = "0.6", default-features = false, features = ["std"] }
# Cryptography # Cryptography
transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std", "recommended"] } transcript = { package = "flexible-transcript", path = "../crypto/transcript", default-features = false, features = ["std"] }
dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"] } dalek-ff-group = { path = "../crypto/dalek-ff-group", default-features = false, features = ["std"] }
ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std"] } ciphersuite = { path = "../crypto/ciphersuite", default-features = false, features = ["std"] }
schnorr-signatures = { path = "../crypto/schnorr", default-features = false, features = ["std"] } schnorr-signatures = { path = "../crypto/schnorr", default-features = false, features = ["std"] }

View File

@@ -4,10 +4,7 @@ use zeroize::{Zeroize, Zeroizing};
use rand_core::OsRng; use rand_core::OsRng;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{ use ciphersuite::{group::ff::PrimeField, WrappedGroup};
group::ff::{Field, PrimeField},
Ciphersuite,
};
use schnorr_signatures::SchnorrSignature; use schnorr_signatures::SchnorrSignature;
use tokio::{ use tokio::{
@@ -22,8 +19,8 @@ use crate::{Service, Metadata, QueuedMessage, MessageQueueRequest, message_chall
pub struct MessageQueue { pub struct MessageQueue {
pub service: Service, pub service: Service,
priv_key: Zeroizing<<Ristretto as Ciphersuite>::F>, priv_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
pub_key: <Ristretto as Ciphersuite>::G, pub_key: <Ristretto as WrappedGroup>::G,
url: String, url: String,
} }
@@ -31,7 +28,7 @@ impl MessageQueue {
pub fn new( pub fn new(
service: Service, service: Service,
mut url: String, mut url: String,
priv_key: Zeroizing<<Ristretto as Ciphersuite>::F>, priv_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
) -> MessageQueue { ) -> MessageQueue {
// Allow MESSAGE_QUEUE_RPC to either be a full URL or just a hostname // Allow MESSAGE_QUEUE_RPC to either be a full URL or just a hostname
// While we could stitch together multiple variables, our control over this service makes this // While we could stitch together multiple variables, our control over this service makes this
@@ -46,16 +43,16 @@ impl MessageQueue {
pub fn from_env(service: Service) -> MessageQueue { pub fn from_env(service: Service) -> MessageQueue {
let url = env::var("MESSAGE_QUEUE_RPC").expect("message-queue RPC wasn't specified"); let url = env::var("MESSAGE_QUEUE_RPC").expect("message-queue RPC wasn't specified");
let priv_key: Zeroizing<<Ristretto as Ciphersuite>::F> = { let priv_key: Zeroizing<<Ristretto as WrappedGroup>::F> = {
let key_str = let key_str =
Zeroizing::new(env::var("MESSAGE_QUEUE_KEY").expect("message-queue key wasn't specified")); Zeroizing::new(env::var("MESSAGE_QUEUE_KEY").expect("message-queue key wasn't specified"));
let key_bytes = Zeroizing::new( let key_bytes = Zeroizing::new(
hex::decode(&key_str).expect("invalid message-queue key specified (wasn't hex)"), hex::decode(&key_str).expect("invalid message-queue key specified (wasn't hex)"),
); );
let mut bytes = <<Ristretto as Ciphersuite>::F as PrimeField>::Repr::default(); let mut bytes = <<Ristretto as WrappedGroup>::F as PrimeField>::Repr::default();
bytes.copy_from_slice(&key_bytes); bytes.copy_from_slice(&key_bytes);
let key = Zeroizing::new( let key = Zeroizing::new(
Option::from(<<Ristretto as Ciphersuite>::F as PrimeField>::from_repr(bytes)) Option::from(<<Ristretto as WrappedGroup>::F as PrimeField>::from_repr(bytes))
.expect("invalid message-queue key specified"), .expect("invalid message-queue key specified"),
); );
bytes.zeroize(); bytes.zeroize();
@@ -79,7 +76,7 @@ impl MessageQueue {
} }
pub async fn queue(&self, metadata: Metadata, msg: Vec<u8>) -> Result<(), String> { pub async fn queue(&self, metadata: Metadata, msg: Vec<u8>) -> Result<(), String> {
let nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let nonce = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let nonce_pub = Ristretto::generator() * nonce.deref(); let nonce_pub = Ristretto::generator() * nonce.deref();
let sig = SchnorrSignature::<Ristretto>::sign( let sig = SchnorrSignature::<Ristretto>::sign(
&self.priv_key, &self.priv_key,
@@ -215,7 +212,7 @@ impl MessageQueue {
pub async fn ack(&self, from: Service, id: u64) { pub async fn ack(&self, from: Service, id: u64) {
// TODO: Should this use OsRng? Deterministic or deterministic + random may be better. // TODO: Should this use OsRng? Deterministic or deterministic + random may be better.
let nonce = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)); let nonce = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut OsRng));
let nonce_pub = Ristretto::generator() * nonce.deref(); let nonce_pub = Ristretto::generator() * nonce.deref();
let sig = SchnorrSignature::<Ristretto>::sign( let sig = SchnorrSignature::<Ristretto>::sign(
&self.priv_key, &self.priv_key,

View File

@@ -4,7 +4,7 @@ pub(crate) use std::{
}; };
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
pub(crate) use ciphersuite::{group::GroupEncoding, Ciphersuite}; pub(crate) use ciphersuite::{group::GroupEncoding, WrappedGroup, GroupCanonicalEncoding};
pub(crate) use schnorr_signatures::SchnorrSignature; pub(crate) use schnorr_signatures::SchnorrSignature;
pub(crate) use serai_primitives::ExternalNetworkId; pub(crate) use serai_primitives::ExternalNetworkId;
@@ -29,7 +29,7 @@ pub(crate) type Db = serai_db::RocksDB;
mod clippy { mod clippy {
use super::*; use super::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub(crate) static KEYS: Lazy<Arc<RwLock<HashMap<Service, <Ristretto as Ciphersuite>::G>>>> = pub(crate) static KEYS: Lazy<Arc<RwLock<HashMap<Service, <Ristretto as WrappedGroup>::G>>>> =
Lazy::new(|| Arc::new(RwLock::new(HashMap::new()))); Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
pub(crate) static QUEUES: Lazy<Arc<RwLock<HashMap<(Service, Service), RwLock<Queue<Db>>>>>> = pub(crate) static QUEUES: Lazy<Arc<RwLock<HashMap<(Service, Service), RwLock<Queue<Db>>>>>> =
Lazy::new(|| Arc::new(RwLock::new(HashMap::new()))); Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
@@ -189,9 +189,9 @@ async fn main() {
let read_key = |str| { let read_key = |str| {
let key = serai_env::var(str)?; let key = serai_env::var(str)?;
let mut repr = <<Ristretto as Ciphersuite>::G as GroupEncoding>::Repr::default(); let mut repr = <<Ristretto as WrappedGroup>::G as GroupEncoding>::Repr::default();
repr.as_mut().copy_from_slice(&hex::decode(key).unwrap()); repr.as_mut().copy_from_slice(&hex::decode(key).unwrap());
Some(<Ristretto as Ciphersuite>::G::from_bytes(&repr).unwrap()) Some(<Ristretto as GroupCanonicalEncoding>::from_canonical_bytes(&repr).unwrap())
}; };
let register_service = |service, key| { let register_service = |service, key| {

View File

@@ -1,6 +1,6 @@
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, DigestTranscript};
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, FromUniformBytes, WrappedGroup, WithPreferredHash};
use borsh::{BorshSerialize, BorshDeserialize}; use borsh::{BorshSerialize, BorshDeserialize};
@@ -36,13 +36,15 @@ pub enum MessageQueueRequest {
pub fn message_challenge( pub fn message_challenge(
from: Service, from: Service,
from_key: <Ristretto as Ciphersuite>::G, from_key: <Ristretto as WrappedGroup>::G,
to: Service, to: Service,
intent: &[u8], intent: &[u8],
msg: &[u8], msg: &[u8],
nonce: <Ristretto as Ciphersuite>::G, nonce: <Ristretto as WrappedGroup>::G,
) -> <Ristretto as Ciphersuite>::F { ) -> <Ristretto as WrappedGroup>::F {
let mut transcript = RecommendedTranscript::new(b"Serai Message Queue v0.1 Message"); let mut transcript = DigestTranscript::<<Ristretto as WithPreferredHash>::H>::new(
b"Serai Message Queue v0.1 Message",
);
transcript.domain_separate(b"metadata"); transcript.domain_separate(b"metadata");
transcript.append_message(b"from", borsh::to_vec(&from).unwrap()); transcript.append_message(b"from", borsh::to_vec(&from).unwrap());
transcript.append_message(b"from_key", from_key.to_bytes()); transcript.append_message(b"from_key", from_key.to_bytes());
@@ -52,17 +54,19 @@ pub fn message_challenge(
transcript.append_message(b"msg", msg); transcript.append_message(b"msg", msg);
transcript.domain_separate(b"signature"); transcript.domain_separate(b"signature");
transcript.append_message(b"nonce", nonce.to_bytes()); transcript.append_message(b"nonce", nonce.to_bytes());
<Ristretto as Ciphersuite>::hash_to_F(&transcript.challenge(b"challenge")) <Ristretto as WrappedGroup>::F::from_uniform_bytes(&transcript.challenge(b"challenge").into())
} }
pub fn ack_challenge( pub fn ack_challenge(
to: Service, to: Service,
to_key: <Ristretto as Ciphersuite>::G, to_key: <Ristretto as WrappedGroup>::G,
from: Service, from: Service,
id: u64, id: u64,
nonce: <Ristretto as Ciphersuite>::G, nonce: <Ristretto as WrappedGroup>::G,
) -> <Ristretto as Ciphersuite>::F { ) -> <Ristretto as WrappedGroup>::F {
let mut transcript = RecommendedTranscript::new(b"Serai Message Queue v0.1 Acknowledgement"); let mut transcript = DigestTranscript::<<Ristretto as WithPreferredHash>::H>::new(
b"Serai Message Queue v0.1 Acknowledgement",
);
transcript.domain_separate(b"metadata"); transcript.domain_separate(b"metadata");
transcript.append_message(b"to", borsh::to_vec(&to).unwrap()); transcript.append_message(b"to", borsh::to_vec(&to).unwrap());
transcript.append_message(b"to_key", to_key.to_bytes()); transcript.append_message(b"to_key", to_key.to_bytes());
@@ -71,5 +75,5 @@ pub fn ack_challenge(
transcript.append_message(b"id", id.to_le_bytes()); transcript.append_message(b"id", id.to_le_bytes());
transcript.domain_separate(b"signature"); transcript.domain_separate(b"signature");
transcript.append_message(b"nonce", nonce.to_bytes()); transcript.append_message(b"nonce", nonce.to_bytes());
<Ristretto as Ciphersuite>::hash_to_F(&transcript.challenge(b"challenge")) <Ristretto as WrappedGroup>::F::from_uniform_bytes(&transcript.challenge(b"challenge").into())
} }

View File

@@ -27,7 +27,7 @@ rand_core = { version = "0.6", default-features = false }
bitcoin = { version = "0.32", default-features = false } bitcoin = { version = "0.32", default-features = false }
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] } k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.10", default-features = false, features = ["secp256k1"], optional = true } frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.11", default-features = false, features = ["secp256k1"], optional = true }
hex = { version = "0.4", default-features = false, optional = true } hex = { version = "0.4", default-features = false, optional = true }
serde = { version = "1", default-features = false, features = ["derive"], optional = true } serde = { version = "1", default-features = false, features = ["derive"], optional = true }

View File

@@ -40,7 +40,7 @@ mod frost_crypto {
use k256::{elliptic_curve::ops::Reduce, U256, Scalar}; use k256::{elliptic_curve::ops::Reduce, U256, Scalar};
use frost::{ use frost::{
curve::{Ciphersuite, Secp256k1}, curve::{WrappedGroup, Secp256k1},
Participant, ThresholdKeys, ThresholdView, FrostError, Participant, ThresholdKeys, ThresholdView, FrostError,
algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr}, algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr},
}; };
@@ -128,10 +128,10 @@ mod frost_crypto {
fn sign_share( fn sign_share(
&mut self, &mut self,
params: &ThresholdView<Secp256k1>, params: &ThresholdView<Secp256k1>,
nonce_sums: &[Vec<<Secp256k1 as Ciphersuite>::G>], nonce_sums: &[Vec<<Secp256k1 as WrappedGroup>::G>],
nonces: Vec<Zeroizing<<Secp256k1 as Ciphersuite>::F>>, nonces: Vec<Zeroizing<<Secp256k1 as WrappedGroup>::F>>,
msg: &[u8], msg: &[u8],
) -> <Secp256k1 as Ciphersuite>::F { ) -> <Secp256k1 as WrappedGroup>::F {
self.0.sign_share(params, nonce_sums, nonces, msg) self.0.sign_share(params, nonce_sums, nonces, msg)
} }

View File

@@ -13,7 +13,7 @@ use k256::{
#[cfg(feature = "std")] #[cfg(feature = "std")]
use frost::{ use frost::{
curve::{Ciphersuite, Secp256k1}, curve::{WrappedGroup, GroupIo, Secp256k1},
ThresholdKeys, ThresholdKeys,
}; };
@@ -59,7 +59,7 @@ pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
would be unusable due to a check the script path hash is less than the order. That doesn't would be unusable due to a check the script path hash is less than the order. That doesn't
impact us as we don't want the script path to be usable. impact us as we don't want the script path to be usable.
*/ */
keys.offset(<Secp256k1 as Ciphersuite>::F::reduce(U256::from_be_bytes( keys.offset(<Secp256k1 as WrappedGroup>::F::reduce(U256::from_be_bytes(
*tweak_hash.to_raw_hash().as_ref(), *tweak_hash.to_raw_hash().as_ref(),
))) )))
}; };

View File

@@ -3,7 +3,7 @@ use std::path::Path;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::PrimeField, Ciphersuite}; use ciphersuite::{group::ff::PrimeField, WrappedGroup};
use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile}; use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
@@ -11,8 +11,8 @@ use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
pub fn coordinator( pub fn coordinator(
orchestration_path: &Path, orchestration_path: &Path,
network: Network, network: Network,
coordinator_key: Zeroizing<<Ristretto as Ciphersuite>::F>, coordinator_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
serai_key: &Zeroizing<<Ristretto as Ciphersuite>::F>, serai_key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
) { ) {
let db = network.db(); let db = network.db();
let longer_reattempts = if network == Network::Dev { "longer-reattempts" } else { "" }; let longer_reattempts = if network == Network::Dev { "longer-reattempts" } else { "" };

View File

@@ -24,7 +24,7 @@ use ciphersuite::{
ff::{Field, PrimeField}, ff::{Field, PrimeField},
GroupEncoding, GroupEncoding,
}, },
Ciphersuite, WrappedGroup,
}; };
use embedwards25519::Embedwards25519; use embedwards25519::Embedwards25519;
use secq256k1::Secq256k1; use secq256k1::Secq256k1;
@@ -221,8 +221,10 @@ fn orchestration_path(network: Network) -> PathBuf {
orchestration_path orchestration_path
} }
type InfrastructureKeys = type InfrastructureKeys = HashMap<
HashMap<&'static str, (Zeroizing<<Ristretto as Ciphersuite>::F>, <Ristretto as Ciphersuite>::G)>; &'static str,
(Zeroizing<<Ristretto as WrappedGroup>::F>, <Ristretto as WrappedGroup>::G),
>;
fn infrastructure_keys(network: Network) -> InfrastructureKeys { fn infrastructure_keys(network: Network) -> InfrastructureKeys {
// Generate entropy for the infrastructure keys // Generate entropy for the infrastructure keys
@@ -257,7 +259,7 @@ fn infrastructure_keys(network: Network) -> InfrastructureKeys {
let mut rng = ChaCha20Rng::from_seed(transcript.rng_seed(b"infrastructure_keys")); let mut rng = ChaCha20Rng::from_seed(transcript.rng_seed(b"infrastructure_keys"));
let mut key_pair = || { let mut key_pair = || {
let key = Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut rng)); let key = Zeroizing::new(<Ristretto as WrappedGroup>::F::random(&mut rng));
let public = Ristretto::generator() * key.deref(); let public = Ristretto::generator() * key.deref();
(key, public) (key, public)
}; };
@@ -307,12 +309,12 @@ fn embedded_curve_keys(network: Network) -> EmbeddedCurveKeys {
EmbeddedCurveKeys { EmbeddedCurveKeys {
embedwards25519: { embedwards25519: {
let key = Zeroizing::new(<Embedwards25519 as Ciphersuite>::F::random(&mut rng)); let key = Zeroizing::new(<Embedwards25519 as WrappedGroup>::F::random(&mut rng));
let pub_key = Embedwards25519::generator() * key.deref(); let pub_key = Embedwards25519::generator() * key.deref();
(Zeroizing::new(key.to_repr().as_ref().to_vec()), pub_key.to_bytes().to_vec()) (Zeroizing::new(key.to_repr().as_ref().to_vec()), pub_key.to_bytes().to_vec())
}, },
secq256k1: { secq256k1: {
let key = Zeroizing::new(<Secq256k1 as Ciphersuite>::F::random(&mut rng)); let key = Zeroizing::new(<Secq256k1 as WrappedGroup>::F::random(&mut rng));
let pub_key = Secq256k1::generator() * key.deref(); let pub_key = Secq256k1::generator() * key.deref();
(Zeroizing::new(key.to_repr().as_ref().to_vec()), pub_key.to_bytes().to_vec()) (Zeroizing::new(key.to_repr().as_ref().to_vec()), pub_key.to_bytes().to_vec())
}, },
@@ -381,9 +383,9 @@ fn dockerfiles(network: Network) {
.expect("couldn't read key for this network"), .expect("couldn't read key for this network"),
); );
let mut serai_key_repr = let mut serai_key_repr =
Zeroizing::new(<<Ristretto as Ciphersuite>::F as PrimeField>::Repr::default()); Zeroizing::new(<<Ristretto as WrappedGroup>::F as PrimeField>::Repr::default());
serai_key_repr.as_mut().copy_from_slice(serai_key.as_ref()); serai_key_repr.as_mut().copy_from_slice(serai_key.as_ref());
Zeroizing::new(<Ristretto as Ciphersuite>::F::from_repr(*serai_key_repr).unwrap()) Zeroizing::new(<Ristretto as WrappedGroup>::F::from_repr(*serai_key_repr).unwrap())
}; };
coordinator(&orchestration_path, network, coordinator_key.0, &serai_key); coordinator(&orchestration_path, network, coordinator_key.0, &serai_key);
@@ -399,7 +401,7 @@ fn key_gen(network: Network) {
return; return;
} }
let key = <Ristretto as Ciphersuite>::F::random(&mut OsRng); let key = <Ristretto as WrappedGroup>::F::random(&mut OsRng);
let _ = fs::create_dir_all(&serai_dir); let _ = fs::create_dir_all(&serai_dir);
fs::write(key_file, key.to_repr()).expect("couldn't write key"); fs::write(key_file, key.to_repr()).expect("couldn't write key");
@@ -407,7 +409,7 @@ fn key_gen(network: Network) {
// TODO: Move embedded curve key gen here, and print them // TODO: Move embedded curve key gen here, and print them
println!( println!(
"Public Key: {}", "Public Key: {}",
hex::encode((<Ristretto as Ciphersuite>::generator() * key).to_bytes()) hex::encode((<Ristretto as WrappedGroup>::generator() * key).to_bytes())
); );
} }

View File

@@ -1,17 +1,17 @@
use std::path::Path; use std::path::Path;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, WrappedGroup};
use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile}; use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
pub fn message_queue( pub fn message_queue(
orchestration_path: &Path, orchestration_path: &Path,
network: Network, network: Network,
coordinator_key: <Ristretto as Ciphersuite>::G, coordinator_key: <Ristretto as WrappedGroup>::G,
bitcoin_key: <Ristretto as Ciphersuite>::G, bitcoin_key: <Ristretto as WrappedGroup>::G,
ethereum_key: <Ristretto as Ciphersuite>::G, ethereum_key: <Ristretto as WrappedGroup>::G,
monero_key: <Ristretto as Ciphersuite>::G, monero_key: <Ristretto as WrappedGroup>::G,
) { ) {
let setup = mimalloc(Os::Debian).to_string() + let setup = mimalloc(Os::Debian).to_string() +
&build_serai_service("", network.release(), network.db(), "serai-message-queue"); &build_serai_service("", network.release(), network.db(), "serai-message-queue");

View File

@@ -3,7 +3,7 @@ use std::path::Path;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::PrimeField, Ciphersuite}; use ciphersuite::{group::ff::PrimeField, WrappedGroup};
use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile}; use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
@@ -12,8 +12,8 @@ pub fn processor(
orchestration_path: &Path, orchestration_path: &Path,
network: Network, network: Network,
coin: &'static str, coin: &'static str,
_coordinator_key: <Ristretto as Ciphersuite>::G, _coordinator_key: <Ristretto as WrappedGroup>::G,
processor_key: Zeroizing<<Ristretto as Ciphersuite>::F>, processor_key: Zeroizing<<Ristretto as WrappedGroup>::F>,
substrate_evrf_key: Zeroizing<Vec<u8>>, substrate_evrf_key: Zeroizing<Vec<u8>>,
network_evrf_key: Zeroizing<Vec<u8>>, network_evrf_key: Zeroizing<Vec<u8>>,
) { ) {

View File

@@ -2,14 +2,14 @@ use std::path::Path;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use dalek_ff_group::Ristretto; use dalek_ff_group::Ristretto;
use ciphersuite::{group::ff::PrimeField, Ciphersuite}; use ciphersuite::{group::ff::PrimeField, WrappedGroup};
use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile}; use crate::{Network, Os, mimalloc, os, build_serai_service, write_dockerfile};
pub fn serai( pub fn serai(
orchestration_path: &Path, orchestration_path: &Path,
network: Network, network: Network,
serai_key: &Zeroizing<<Ristretto as Ciphersuite>::F>, serai_key: &Zeroizing<<Ristretto as WrappedGroup>::F>,
) { ) {
// Always builds in release for performance reasons // Always builds in release for performance reasons
let setup = mimalloc(Os::Debian).to_string() + &build_serai_service("", true, "", "serai-node"); let setup = mimalloc(Os::Debian).to_string() + &build_serai_service("", true, "", "serai-node");

View File

@@ -16,13 +16,19 @@ rustdoc-args = ["--cfg", "docsrs"]
workspace = true workspace = true
[dependencies] [dependencies]
std-shims = { path = "../../common/std-shims", version = "0.1.4", default-features = false, optional = true }
zeroize = { version = "1", default-features = false }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false }
dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, optional = true } dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, optional = true }
[features] [features]
alloc = ["ciphersuite/alloc"] alloc = ["std-shims", "zeroize/alloc", "ciphersuite/alloc", "dalek-ff-group/alloc"]
std = [ std = [
"alloc", "alloc",
"std-shims/std",
"zeroize/std",
"ciphersuite/std", "ciphersuite/std",
"dalek-ff-group?/std", "dalek-ff-group?/std",
] ]

View File

@@ -1,5 +1,33 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
pub use ciphersuite::*; use std_shims::io;
use zeroize::Zeroize;
pub use ciphersuite::group;
use group::{*, ff::*, prime::PrimeGroup};
pub trait Ciphersuite: 'static + Send + Sync {
type F: PrimeField + PrimeFieldBits + Zeroize;
type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize;
#[cfg(feature = "alloc")]
#[allow(non_snake_case)]
fn read_F<R: io::Read>(reader: &mut R) -> io::Result<Self::F>;
#[cfg(feature = "alloc")]
#[allow(non_snake_case)]
fn read_G<R: io::Read>(reader: &mut R) -> io::Result<Self::G>;
}
impl<C: ciphersuite::GroupIo> Ciphersuite for C {
type F = <C as ciphersuite::WrappedGroup>::F;
type G = <C as ciphersuite::WrappedGroup>::G;
#[cfg(feature = "alloc")]
fn read_F<R: io::Read>(reader: &mut R) -> io::Result<Self::F> {
<C as ciphersuite::GroupIo>::read_F(reader)
}
#[cfg(feature = "alloc")]
fn read_G<R: io::Read>(reader: &mut R) -> io::Result<Self::G> {
<C as ciphersuite::GroupIo>::read_G(reader)
}
}
#[cfg(feature = "ed25519")] #[cfg(feature = "ed25519")]
pub use dalek_ff_group::Ed25519; pub use dalek_ff_group::Ed25519;

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "dalek-ff-group" name = "dalek-ff-group"
version = "0.4.99" version = "0.5.99"
description = "ff/group bindings around curve25519-dalek" description = "ff/group bindings around curve25519-dalek"
license = "MIT" license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group" repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group"

View File

@@ -24,7 +24,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] } borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ristretto"] } dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std"] }
serai-client = { path = "../../substrate/client", default-features = false } serai-client = { path = "../../substrate/client", default-features = false }
serai-cosign = { path = "../../coordinator/cosign" } serai-cosign = { path = "../../coordinator/cosign" }

View File

@@ -4,9 +4,9 @@ use zeroize::{Zeroize, Zeroizing};
use ciphersuite::{ use ciphersuite::{
group::{ff::PrimeField, GroupEncoding}, group::{ff::PrimeField, GroupEncoding},
Ciphersuite, *,
}; };
use dkg::{Curves, Ristretto}; use dkg::Curves;
use serai_client::validator_sets::primitives::Session; use serai_client::validator_sets::primitives::Session;
@@ -14,7 +14,7 @@ use serai_env as env;
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel}; use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};
use primitives::EncodableG; use primitives::EncodableG;
use ::key_gen::{KeyGenParams, KeyGen}; use ::key_gen::{Ristretto, KeyGenParams, KeyGen};
use scheduler::{SignableTransaction, TransactionFor}; use scheduler::{SignableTransaction, TransactionFor};
use scanner::{ScannerFeed, Scanner, KeyFor, Scheduler}; use scanner::{ScannerFeed, Scanner, KeyFor, Scheduler};
use signers::{TransactionPublisher, Signers}; use signers::{TransactionPublisher, Signers};
@@ -80,7 +80,7 @@ pub fn url() -> String {
} }
fn key_gen<K: KeyGenParams>() -> KeyGen<K> { fn key_gen<K: KeyGenParams>() -> KeyGen<K> {
fn read_key_from_env<C: Ciphersuite>(label: &'static str) -> Zeroizing<C::F> { fn read_key_from_env<C: WrappedGroup>(label: &'static str) -> Zeroizing<C::F> {
let key_hex = let key_hex =
Zeroizing::new(env::var(label).unwrap_or_else(|| panic!("{label} wasn't provided"))); Zeroizing::new(env::var(label).unwrap_or_else(|| panic!("{label} wasn't provided")));
let bytes = Zeroizing::new( let bytes = Zeroizing::new(

View File

@@ -1,4 +1,4 @@
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, *};
use dkg::{ThresholdKeys, Curves, Secp256k1}; use dkg::{ThresholdKeys, Curves, Secp256k1};
use crate::{primitives::x_coord_to_even_point, scan::scanner}; use crate::{primitives::x_coord_to_even_point, scan::scanner};
@@ -18,7 +18,7 @@ impl key_gen::KeyGenParams for KeyGenParams {
} }
fn encode_key( fn encode_key(
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G, key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G,
) -> Vec<u8> { ) -> Vec<u8> {
let key = key.to_bytes(); let key = key.to_bytes();
let key: &[u8] = key.as_ref(); let key: &[u8] = key.as_ref();
@@ -28,7 +28,7 @@ impl key_gen::KeyGenParams for KeyGenParams {
fn decode_key( fn decode_key(
key: &[u8], key: &[u8],
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G> { ) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G> {
x_coord_to_even_point(key) x_coord_to_even_point(key)
} }
} }

View File

@@ -1,7 +1,7 @@
use core::fmt; use core::fmt;
use std::collections::HashMap; use std::collections::HashMap;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use bitcoin_serai::bitcoin::block::{Header, Block as BBlock}; use bitcoin_serai::bitcoin::block::{Header, Block as BBlock};
@@ -35,7 +35,7 @@ impl<D: Db> fmt::Debug for Block<D> {
impl<D: Db> primitives::Block for Block<D> { impl<D: Db> primitives::Block for Block<D> {
type Header = BlockHeader; type Header = BlockHeader;
type Key = <Secp256k1 as Ciphersuite>::G; type Key = <Secp256k1 as WrappedGroup>::G;
type Address = Address; type Address = Address;
type Output = Output; type Output = Output;
type Eventuality = Eventuality; type Eventuality = Eventuality;

View File

@@ -1,4 +1,4 @@
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use bitcoin_serai::bitcoin::key::{Parity, XOnlyPublicKey}; use bitcoin_serai::bitcoin::key::{Parity, XOnlyPublicKey};
@@ -7,7 +7,7 @@ pub(crate) mod output;
pub(crate) mod transaction; pub(crate) mod transaction;
pub(crate) mod block; pub(crate) mod block;
pub(crate) fn x_coord_to_even_point(key: &[u8]) -> Option<<Secp256k1 as Ciphersuite>::G> { pub(crate) fn x_coord_to_even_point(key: &[u8]) -> Option<<Secp256k1 as WrappedGroup>::G> {
if key.len() != 32 { if key.len() != 32 {
None? None?
}; };

View File

@@ -1,6 +1,6 @@
use std::io; use std::io;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use bitcoin_serai::{ use bitcoin_serai::{
@@ -55,7 +55,7 @@ pub(crate) struct Output {
impl Output { impl Output {
pub(crate) fn new( pub(crate) fn new(
getter: &impl Get, getter: &impl Get,
key: <Secp256k1 as Ciphersuite>::G, key: <Secp256k1 as WrappedGroup>::G,
tx: &Transaction, tx: &Transaction,
output: WalletOutput, output: WalletOutput,
) -> Self { ) -> Self {
@@ -71,7 +71,7 @@ impl Output {
} }
pub(crate) fn new_with_presumed_origin( pub(crate) fn new_with_presumed_origin(
key: <Secp256k1 as Ciphersuite>::G, key: <Secp256k1 as WrappedGroup>::G,
tx: &Transaction, tx: &Transaction,
presumed_origin: Option<Address>, presumed_origin: Option<Address>,
output: WalletOutput, output: WalletOutput,
@@ -88,7 +88,7 @@ impl Output {
} }
} }
impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output { impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
type Id = OutputId; type Id = OutputId;
type TransactionId = [u8; 32]; type TransactionId = [u8; 32];
@@ -108,7 +108,7 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
res res
} }
fn key(&self) -> <Secp256k1 as Ciphersuite>::G { fn key(&self) -> <Secp256k1 as WrappedGroup>::G {
// We read the key from the script pubkey so we don't have to independently store it // We read the key from the script pubkey so we don't have to independently store it
let script = &self.output.output().script_pubkey; let script = &self.output.output().script_pubkey;
@@ -121,7 +121,7 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
.expect("last item in scanned v1 Taproot script wasn't a valid x-only public key"); .expect("last item in scanned v1 Taproot script wasn't a valid x-only public key");
// The output's key minus the output's offset is the root key // The output's key minus the output's offset is the root key
key - (<Secp256k1 as Ciphersuite>::G::GENERATOR * self.output.offset()) key - (<Secp256k1 as WrappedGroup>::G::GENERATOR * self.output.offset())
} }
fn presumed_origin(&self) -> Option<Address> { fn presumed_origin(&self) -> Option<Address> {

View File

@@ -1,6 +1,6 @@
use std::{sync::LazyLock, collections::HashMap}; use std::{sync::LazyLock, collections::HashMap};
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use bitcoin_serai::{ use bitcoin_serai::{
@@ -20,20 +20,20 @@ use primitives::OutputType;
use crate::hash_bytes; use crate::hash_bytes;
// TODO: Bitcoin HD derivation, instead of these bespoke labels? // TODO: Bitcoin HD derivation, instead of these bespoke labels?
static BRANCH_BASE_OFFSET: LazyLock<<Secp256k1 as Ciphersuite>::F> = static BRANCH_BASE_OFFSET: LazyLock<<Secp256k1 as WrappedGroup>::F> =
LazyLock::new(|| Secp256k1::hash_to_F(b"branch")); LazyLock::new(|| Secp256k1::hash_to_F(b"branch"));
static CHANGE_BASE_OFFSET: LazyLock<<Secp256k1 as Ciphersuite>::F> = static CHANGE_BASE_OFFSET: LazyLock<<Secp256k1 as WrappedGroup>::F> =
LazyLock::new(|| Secp256k1::hash_to_F(b"change")); LazyLock::new(|| Secp256k1::hash_to_F(b"change"));
static FORWARD_BASE_OFFSET: LazyLock<<Secp256k1 as Ciphersuite>::F> = static FORWARD_BASE_OFFSET: LazyLock<<Secp256k1 as WrappedGroup>::F> =
LazyLock::new(|| Secp256k1::hash_to_F(b"forward")); LazyLock::new(|| Secp256k1::hash_to_F(b"forward"));
// Unfortunately, we have per-key offsets as it's the root key plus the base offset may not be // Unfortunately, we have per-key offsets as it's the root key plus the base offset may not be
// even. While we could tweak the key until all derivations are even, that'd require significantly // even. While we could tweak the key until all derivations are even, that'd require significantly
// more tweaking. This algorithmic complexity is preferred. // more tweaking. This algorithmic complexity is preferred.
pub(crate) fn offsets_for_key( pub(crate) fn offsets_for_key(
key: <Secp256k1 as Ciphersuite>::G, key: <Secp256k1 as WrappedGroup>::G,
) -> HashMap<OutputType, <Secp256k1 as Ciphersuite>::F> { ) -> HashMap<OutputType, <Secp256k1 as WrappedGroup>::F> {
let mut offsets = HashMap::from([(OutputType::External, <Secp256k1 as Ciphersuite>::F::ZERO)]); let mut offsets = HashMap::from([(OutputType::External, <Secp256k1 as WrappedGroup>::F::ZERO)]);
// We create an actual Bitcoin scanner as upon adding an offset, it yields the tweaked offset // We create an actual Bitcoin scanner as upon adding an offset, it yields the tweaked offset
// actually used // actually used
@@ -50,7 +50,7 @@ pub(crate) fn offsets_for_key(
offsets offsets
} }
pub(crate) fn scanner(key: <Secp256k1 as Ciphersuite>::G) -> Scanner { pub(crate) fn scanner(key: <Secp256k1 as WrappedGroup>::G) -> Scanner {
let mut scanner = Scanner::new(key).unwrap(); let mut scanner = Scanner::new(key).unwrap();
for (_, offset) in offsets_for_key(key) { for (_, offset) in offsets_for_key(key) {
let tweaked_offset = scanner.register_offset(offset).unwrap(); let tweaked_offset = scanner.register_offset(offset).unwrap();

View File

@@ -1,6 +1,6 @@
use core::future::Future; use core::future::Future;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use bitcoin_serai::{ use bitcoin_serai::{
@@ -26,8 +26,8 @@ use crate::{
rpc::Rpc, rpc::Rpc,
}; };
fn address_from_serai_key(key: <Secp256k1 as Ciphersuite>::G, kind: OutputType) -> Address { fn address_from_serai_key(key: <Secp256k1 as WrappedGroup>::G, kind: OutputType) -> Address {
let offset = <Secp256k1 as Ciphersuite>::G::GENERATOR * offsets_for_key(key)[&kind]; let offset = <Secp256k1 as WrappedGroup>::G::GENERATOR * offsets_for_key(key)[&kind];
Address::new( Address::new(
p2tr_script_buf(key + offset) p2tr_script_buf(key + offset)
.expect("creating address from Serai key which wasn't properly tweaked"), .expect("creating address from Serai key which wasn't properly tweaked"),
@@ -72,7 +72,7 @@ fn signable_transaction<D: Db>(
*/ */
payments.push(( payments.push((
// The generator is even so this is valid // The generator is even so this is valid
p2tr_script_buf(<Secp256k1 as Ciphersuite>::G::GENERATOR).unwrap(), p2tr_script_buf(<Secp256k1 as WrappedGroup>::G::GENERATOR).unwrap(),
// This uses the minimum output value allowed, as defined as a constant in bitcoin-serai // This uses the minimum output value allowed, as defined as a constant in bitcoin-serai
// TODO: Add a test for this comparing to bitcoin's `minimal_non_dust` // TODO: Add a test for this comparing to bitcoin's `minimal_non_dust`
bitcoin_serai::wallet::DUST, bitcoin_serai::wallet::DUST,

View File

@@ -1,4 +1,4 @@
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dkg::{ThresholdKeys, Curves, Secp256k1}; use dkg::{ThresholdKeys, Curves, Secp256k1};
use ethereum_schnorr::PublicKey; use ethereum_schnorr::PublicKey;
@@ -13,19 +13,19 @@ impl key_gen::KeyGenParams for KeyGenParams {
keys: &mut ThresholdKeys<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve>, keys: &mut ThresholdKeys<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve>,
) { ) {
while PublicKey::new(keys.group_key()).is_none() { while PublicKey::new(keys.group_key()).is_none() {
*keys = keys.clone().offset(<<Secp256k1 as Curves>::ToweringCurve as Ciphersuite>::F::ONE); *keys = keys.clone().offset(<<Secp256k1 as Curves>::ToweringCurve as WrappedGroup>::F::ONE);
} }
} }
fn encode_key( fn encode_key(
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G, key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G,
) -> Vec<u8> { ) -> Vec<u8> {
PublicKey::new(key).unwrap().eth_repr().to_vec() PublicKey::new(key).unwrap().eth_repr().to_vec()
} }
fn decode_key( fn decode_key(
key: &[u8], key: &[u8],
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G> { ) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G> {
PublicKey::from_eth_repr(key.try_into().ok()?).map(|key| key.point()) PublicKey::from_eth_repr(key.try_into().ok()?).map(|key| key.point())
} }
} }

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use serai_client::networks::ethereum::Address; use serai_client::networks::ethereum::Address;
@@ -41,7 +41,7 @@ pub(crate) struct FullEpoch {
impl primitives::Block for FullEpoch { impl primitives::Block for FullEpoch {
type Header = Epoch; type Header = Epoch;
type Key = <Secp256k1 as Ciphersuite>::G; type Key = <Secp256k1 as WrappedGroup>::G;
type Address = Address; type Address = Address;
type Output = Output; type Output = Output;
type Eventuality = Eventuality; type Eventuality = Eventuality;

View File

@@ -2,7 +2,7 @@ use std::{io, collections::HashMap};
use rand_core::{RngCore, CryptoRng}; use rand_core::{RngCore, CryptoRng};
use ciphersuite::Ciphersuite; use ciphersuite::*;
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use frost::{ use frost::{
dkg::{Participant, ThresholdKeys}, dkg::{Participant, ThresholdKeys},
@@ -24,10 +24,10 @@ pub struct EthereumHram;
impl Hram<Secp256k1> for EthereumHram { impl Hram<Secp256k1> for EthereumHram {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn hram( fn hram(
R: &<Secp256k1 as Ciphersuite>::G, R: &<Secp256k1 as WrappedGroup>::G,
A: &<Secp256k1 as Ciphersuite>::G, A: &<Secp256k1 as WrappedGroup>::G,
m: &[u8], m: &[u8],
) -> <Secp256k1 as Ciphersuite>::F { ) -> <Secp256k1 as WrappedGroup>::F {
Signature::challenge(*R, &PublicKey::new(*A).unwrap(), m) Signature::challenge(*R, &PublicKey::new(*A).unwrap(), m)
} }
} }

View File

@@ -1,6 +1,6 @@
use std::io; use std::io;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, *};
use ciphersuite_kp256::Secp256k1; use ciphersuite_kp256::Secp256k1;
use alloy_core::primitives::U256; use alloy_core::primitives::U256;
@@ -61,10 +61,10 @@ impl AsMut<[u8]> for OutputId {
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) enum Output { pub(crate) enum Output {
Output { key: <Secp256k1 as Ciphersuite>::G, instruction: EthereumInInstruction }, Output { key: <Secp256k1 as WrappedGroup>::G, instruction: EthereumInInstruction },
Eventuality { key: <Secp256k1 as Ciphersuite>::G, nonce: u64 }, Eventuality { key: <Secp256k1 as WrappedGroup>::G, nonce: u64 },
} }
impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output { impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
type Id = OutputId; type Id = OutputId;
type TransactionId = [u8; 32]; type TransactionId = [u8; 32];
@@ -107,7 +107,7 @@ impl ReceivedOutput<<Secp256k1 as Ciphersuite>::G, Address> for Output {
} }
} }
fn key(&self) -> <Secp256k1 as Ciphersuite>::G { fn key(&self) -> <Secp256k1 as WrappedGroup>::G {
match self { match self {
Output::Output { key, .. } | Output::Eventuality { key, .. } => *key, Output::Output { key, .. } | Output::Eventuality { key, .. } => *key,
} }

View File

@@ -32,7 +32,9 @@ rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] } blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std"] } transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std"] }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] } ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ristretto"] } embedwards25519 = { path = "../../crypto/embedwards25519", default-features = false, features = ["std"] }
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std"] }
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["ristretto"] }
# Substrate # Substrate
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] } serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }

View File

@@ -3,7 +3,7 @@ use std::collections::HashMap;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, *};
use dkg::*; use dkg::*;
use serai_validator_sets_primitives::Session; use serai_validator_sets_primitives::Session;
@@ -11,15 +11,15 @@ use serai_validator_sets_primitives::Session;
use borsh::{BorshSerialize, BorshDeserialize}; use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn}; use serai_db::{Get, DbTxn};
use crate::KeyGenParams; use crate::{Ristretto, KeyGenParams};
pub(crate) struct Params<P: KeyGenParams> { pub(crate) struct Params<P: KeyGenParams> {
pub(crate) t: u16, pub(crate) t: u16,
pub(crate) n: u16, pub(crate) n: u16,
pub(crate) substrate_evrf_public_keys: pub(crate) substrate_evrf_public_keys:
Vec<<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::G>, Vec<<<Ristretto as Curves>::EmbeddedCurve as WrappedGroup>::G>,
pub(crate) network_evrf_public_keys: pub(crate) network_evrf_public_keys:
Vec<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::G>, Vec<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as WrappedGroup>::G>,
} }
#[derive(BorshSerialize, BorshDeserialize)] #[derive(BorshSerialize, BorshDeserialize)]
@@ -85,17 +85,16 @@ impl<P: KeyGenParams> KeyGenDb<P> {
.substrate_evrf_public_keys .substrate_evrf_public_keys
.into_iter() .into_iter()
.map(|key| { .map(|key| {
<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::read_G(&mut key.as_slice()) <<Ristretto as Curves>::EmbeddedCurve as GroupIo>::read_G(&mut key.as_slice()).unwrap()
.unwrap()
}) })
.collect(), .collect(),
network_evrf_public_keys: params network_evrf_public_keys: params
.network_evrf_public_keys .network_evrf_public_keys
.into_iter() .into_iter()
.map(|key| { .map(|key| {
<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::read_G::< <<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as GroupIo>::read_G::<&[u8]>(
&[u8], &mut key.as_ref(),
>(&mut key.as_ref()) )
.unwrap() .unwrap()
}) })
.collect(), .collect(),

View File

@@ -13,7 +13,7 @@ use blake2::{Digest, Blake2s256};
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, RecommendedTranscript};
use ciphersuite::{ use ciphersuite::{
group::{Group, GroupEncoding}, group::{Group, GroupEncoding},
Ciphersuite, *,
}; };
use dkg::*; use dkg::*;
@@ -28,6 +28,14 @@ use generators::generators;
mod db; mod db;
use db::{Params, Participations, KeyGenDb}; use db::{Params, Participations, KeyGenDb};
/// Ristretto, and an elliptic curve defined over its scalar field (embedwards25519).
pub struct Ristretto;
impl Curves for Ristretto {
type ToweringCurve = frost::curve::Ristretto;
type EmbeddedCurve = embedwards25519::Embedwards25519;
type EmbeddedCurveParameters = embedwards25519::Embedwards25519;
}
/// Parameters for a key generation. /// Parameters for a key generation.
pub trait KeyGenParams { pub trait KeyGenParams {
/// The ID for this instantiation. /// The ID for this instantiation.
@@ -49,7 +57,7 @@ pub trait KeyGenParams {
/// ///
/// A default implementation is provided which calls the traditional `to_bytes`. /// A default implementation is provided which calls the traditional `to_bytes`.
fn encode_key( fn encode_key(
key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G, key: <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G,
) -> Vec<u8> { ) -> Vec<u8> {
key.to_bytes().as_ref().to_vec() key.to_bytes().as_ref().to_vec()
} }
@@ -59,11 +67,10 @@ pub trait KeyGenParams {
/// A default implementation is provided which calls the traditional `from_bytes`. /// A default implementation is provided which calls the traditional `from_bytes`.
fn decode_key( fn decode_key(
mut key: &[u8], mut key: &[u8],
) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::G> { ) -> Option<<<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as WrappedGroup>::G> {
let res = <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as Ciphersuite>::read_G( let res =
&mut key, <<Self::ExternalNetworkCiphersuite as Curves>::ToweringCurve as GroupIo>::read_G(&mut key)
) .ok()?;
.ok()?;
if !key.is_empty() { if !key.is_empty() {
None?; None?;
} }
@@ -101,14 +108,14 @@ pub trait KeyGenParams {
*/ */
fn coerce_keys<C: 'static + Curves>( fn coerce_keys<C: 'static + Curves>(
key_bytes: &[impl AsRef<[u8]>], key_bytes: &[impl AsRef<[u8]>],
) -> (Vec<<C::EmbeddedCurve as Ciphersuite>::G>, Vec<Participant>) { ) -> (Vec<<C::EmbeddedCurve as WrappedGroup>::G>, Vec<Participant>) {
fn evrf_key<C: 'static + Curves>(key: &[u8]) -> Option<<C::EmbeddedCurve as Ciphersuite>::G> { fn evrf_key<C: 'static + Curves>(key: &[u8]) -> Option<<C::EmbeddedCurve as WrappedGroup>::G> {
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default(); let mut repr = <<C::EmbeddedCurve as WrappedGroup>::G as GroupEncoding>::Repr::default();
if repr.as_ref().len() != key.len() { if repr.as_ref().len() != key.len() {
None?; None?;
} }
repr.as_mut().copy_from_slice(key); repr.as_mut().copy_from_slice(key);
let point = Option::<<C::EmbeddedCurve as Ciphersuite>::G>::from(<_>::from_bytes(&repr))?; let point = Option::<<C::EmbeddedCurve as WrappedGroup>::G>::from(<_>::from_bytes(&repr))?;
if bool::from(point.is_identity()) { if bool::from(point.is_identity()) {
None?; None?;
} }
@@ -131,10 +138,10 @@ fn coerce_keys<C: 'static + Curves>(
// Generate a random key // Generate a random key
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(key).into()); let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(key).into());
loop { loop {
let mut repr = <<C::EmbeddedCurve as Ciphersuite>::G as GroupEncoding>::Repr::default(); let mut repr = <<C::EmbeddedCurve as WrappedGroup>::G as GroupEncoding>::Repr::default();
rng.fill_bytes(repr.as_mut()); rng.fill_bytes(repr.as_mut());
if let Some(key) = if let Some(key) =
Option::<<C::EmbeddedCurve as Ciphersuite>::G>::from(<_>::from_bytes(&repr)) Option::<<C::EmbeddedCurve as WrappedGroup>::G>::from(<_>::from_bytes(&repr))
{ {
break key; break key;
} }
@@ -149,18 +156,20 @@ fn coerce_keys<C: 'static + Curves>(
/// An instance of the Serai key generation protocol. /// An instance of the Serai key generation protocol.
#[derive(Debug)] #[derive(Debug)]
pub struct KeyGen<P: KeyGenParams> { pub struct KeyGen<P: KeyGenParams> {
substrate_evrf_private_key: Zeroizing<<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::F>, substrate_evrf_private_key: Zeroizing<<<Ristretto as Curves>::EmbeddedCurve as WrappedGroup>::F>,
network_evrf_private_key: network_evrf_private_key:
Zeroizing<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::F>, Zeroizing<<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as WrappedGroup>::F>,
} }
impl<P: KeyGenParams> KeyGen<P> { impl<P: KeyGenParams> KeyGen<P> {
/// Create a new key generation instance. /// Create a new key generation instance.
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new( pub fn new(
substrate_evrf_private_key: Zeroizing<<<Ristretto as Curves>::EmbeddedCurve as Ciphersuite>::F>, substrate_evrf_private_key: Zeroizing<
<<Ristretto as Curves>::EmbeddedCurve as WrappedGroup>::F,
>,
network_evrf_private_key: Zeroizing< network_evrf_private_key: Zeroizing<
<<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as Ciphersuite>::F, <<P::ExternalNetworkCiphersuite as Curves>::EmbeddedCurve as WrappedGroup>::F,
>, >,
) -> KeyGen<P> { ) -> KeyGen<P> {
KeyGen { substrate_evrf_private_key, network_evrf_private_key } KeyGen { substrate_evrf_private_key, network_evrf_private_key }
@@ -214,8 +223,8 @@ impl<P: KeyGenParams> KeyGen<P> {
fn participate<C: 'static + Curves>( fn participate<C: 'static + Curves>(
context: [u8; 32], context: [u8; 32],
threshold: u16, threshold: u16,
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G], evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>, evrf_private_key: &Zeroizing<<C::EmbeddedCurve as WrappedGroup>::F>,
output: &mut impl io::Write, output: &mut impl io::Write,
) { ) {
let participation = Dkg::<C>::participate( let participation = Dkg::<C>::participate(
@@ -411,7 +420,7 @@ impl<P: KeyGenParams> KeyGen<P> {
session: Session, session: Session,
true_if_substrate_false_if_network: bool, true_if_substrate_false_if_network: bool,
threshold: u16, threshold: u16,
evrf_public_keys: &[<C::EmbeddedCurve as Ciphersuite>::G], evrf_public_keys: &[<C::EmbeddedCurve as WrappedGroup>::G],
substrate_participations: &mut HashMap<Participant, Vec<u8>>, substrate_participations: &mut HashMap<Participant, Vec<u8>>,
network_participations: &mut HashMap<Participant, Vec<u8>>, network_participations: &mut HashMap<Participant, Vec<u8>>,
) -> Result<Dkg<C>, Vec<ProcessorMessage>> { ) -> Result<Dkg<C>, Vec<ProcessorMessage>> {

View File

@@ -29,8 +29,8 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = fals
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ed25519"] } dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ed25519"] }
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false } frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "6966575e05fe09b77674c46984b21686ed9304ff", default-features = false, features = ["std", "multisig"] } monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7f37cc8f770858aa1739e0f56dbe447db86f4ba6", default-features = false, features = ["std", "multisig"] }
monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "6966575e05fe09b77674c46984b21686ed9304ff", default-features = false } monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "7f37cc8f770858aa1739e0f56dbe447db86f4ba6", default-features = false }
serai-client = { path = "../../substrate/client", default-features = false, features = ["monero"] } serai-client = { path = "../../substrate/client", default-features = false, features = ["monero"] }

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::{ use monero_wallet::{
@@ -32,7 +32,7 @@ pub(crate) struct Block(pub(crate) MScannableBlock);
impl primitives::Block for Block { impl primitives::Block for Block {
type Header = BlockHeader; type Header = BlockHeader;
type Key = <Ed25519 as Ciphersuite>::G; type Key = <Ed25519 as WrappedGroup>::G;
type Address = Address; type Address = Address;
type Output = Output; type Output = Output;
type Eventuality = Eventuality; type Eventuality = Eventuality;

View File

@@ -1,6 +1,6 @@
use zeroize::Zeroizing; use zeroize::Zeroizing;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::{address::SubaddressIndex, ViewPairError, GuaranteedViewPair}; use monero_wallet::{address::SubaddressIndex, ViewPairError, GuaranteedViewPair};
@@ -28,8 +28,8 @@ pub(crate) const FORWARDED_SUBADDRESS: SubaddressIndex = match SubaddressIndex::
None => panic!("SubaddressIndex for FORWARDED_SUBADDRESS was None"), None => panic!("SubaddressIndex for FORWARDED_SUBADDRESS was None"),
}; };
pub(crate) fn view_pair(key: <Ed25519 as Ciphersuite>::G) -> GuaranteedViewPair { pub(crate) fn view_pair(key: <Ed25519 as WrappedGroup>::G) -> GuaranteedViewPair {
match GuaranteedViewPair::new(key.0, Zeroizing::new(*view_key::<Ed25519>(0))) { match GuaranteedViewPair::new(key.0, Zeroizing::new(view_key::<Ed25519>(0))) {
Ok(view_pair) => view_pair, Ok(view_pair) => view_pair,
Err(ViewPairError::TorsionedSpendKey) => { Err(ViewPairError::TorsionedSpendKey) => {
unreachable!("dalek_ff_group::EdwardsPoint had torsion") unreachable!("dalek_ff_group::EdwardsPoint had torsion")

View File

@@ -1,6 +1,6 @@
use std::io; use std::io;
use ciphersuite::{group::Group, Ciphersuite}; use ciphersuite::WrappedGroup;
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::WalletOutput; use monero_wallet::WalletOutput;
@@ -35,7 +35,7 @@ impl AsMut<[u8]> for OutputId {
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct Output(pub(crate) WalletOutput); pub(crate) struct Output(pub(crate) WalletOutput);
impl ReceivedOutput<<Ed25519 as Ciphersuite>::G, Address> for Output { impl ReceivedOutput<<Ed25519 as WrappedGroup>::G, Address> for Output {
type Id = OutputId; type Id = OutputId;
type TransactionId = [u8; 32]; type TransactionId = [u8; 32];
@@ -64,12 +64,12 @@ impl ReceivedOutput<<Ed25519 as Ciphersuite>::G, Address> for Output {
self.0.transaction() self.0.transaction()
} }
fn key(&self) -> <Ed25519 as Ciphersuite>::G { fn key(&self) -> <Ed25519 as WrappedGroup>::G {
// The spend key will be a key we generated, so it'll be in the prime-order subgroup // The spend key will be a key we generated, so it'll be in the prime-order subgroup
// The output's key is the spend key + (key_offset * G), so it's in the prime-order subgroup if // The output's key is the spend key + (key_offset * G), so it's in the prime-order subgroup if
// the spend key is // the spend key is
dalek_ff_group::EdwardsPoint( dalek_ff_group::EdwardsPoint(
self.0.key() - (*<Ed25519 as Ciphersuite>::G::generator() * self.0.key_offset()), self.0.key() - (*<Ed25519 as WrappedGroup>::generator() * self.0.key_offset()),
) )
} }

View File

@@ -4,7 +4,7 @@ use zeroize::Zeroizing;
use rand_core::SeedableRng; use rand_core::SeedableRng;
use rand_chacha::ChaCha20Rng; use rand_chacha::ChaCha20Rng;
use ciphersuite::Ciphersuite; use ciphersuite::*;
use dalek_ff_group::Ed25519; use dalek_ff_group::Ed25519;
use monero_wallet::rpc::{FeeRate, RpcError}; use monero_wallet::rpc::{FeeRate, RpcError};
@@ -33,7 +33,7 @@ use crate::{
rpc::Rpc, rpc::Rpc,
}; };
fn address_from_serai_key(key: <Ed25519 as Ciphersuite>::G, kind: OutputType) -> Address { fn address_from_serai_key(key: <Ed25519 as WrappedGroup>::G, kind: OutputType) -> Address {
view_pair(key) view_pair(key)
.address( .address(
Network::Mainnet, Network::Mainnet,
@@ -118,8 +118,8 @@ async fn signable_transaction(
MoneroAddress::new( MoneroAddress::new(
Network::Mainnet, Network::Mainnet,
AddressType::Legacy, AddressType::Legacy,
<Ed25519 as Ciphersuite>::generator().0, <Ed25519 as WrappedGroup>::generator().0,
<Ed25519 as Ciphersuite>::generator().0, <Ed25519 as WrappedGroup>::generator().0,
), ),
0, 0,
)); ));

View File

@@ -5,7 +5,7 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use std::io; use std::io;
use ciphersuite::{group::GroupEncoding, Ciphersuite}; use ciphersuite::{group::GroupEncoding, *};
use frost::{dkg::ThresholdKeys, sign::PreprocessMachine}; use frost::{dkg::ThresholdKeys, sign::PreprocessMachine};
use serai_db::DbTxn; use serai_db::DbTxn;

Some files were not shown because too many files have changed in this diff Show More