mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
dkg-evrf crate
monero-oxide relies on ciphersuite, which is in-tree, yet we've made breaking changes since. This commit adds a patch so monero-oxide -> patches/ciphersuite -> crypto/ciphersuite, with patches/ciphersuite resolving the breaking changes.
This commit is contained in:
77
Cargo.lock
generated
77
Cargo.lock
generated
@@ -1916,25 +1916,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ciphersuite"
|
name = "ciphersuite"
|
||||||
version = "0.4.2"
|
version = "0.4.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2ae44ce6224d75ced726e5597265b8f334b9bf4767b8b42e058f2304005d8475"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ciphersuite 0.4.2",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"digest 0.10.7",
|
|
||||||
"elliptic-curve",
|
|
||||||
"ff",
|
|
||||||
"flexible-transcript",
|
|
||||||
"group",
|
|
||||||
"k256",
|
|
||||||
"minimal-ed448",
|
|
||||||
"p256",
|
|
||||||
"rand_core 0.6.4",
|
|
||||||
"sha2 0.10.9",
|
|
||||||
"sha3",
|
|
||||||
"std-shims",
|
|
||||||
"subtle",
|
|
||||||
"zeroize",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2759,8 +2744,10 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
|
"ciphersuite-kp256",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"dkg",
|
"dkg",
|
||||||
|
"dkg-recovery",
|
||||||
"ec-divisors",
|
"ec-divisors",
|
||||||
"embedwards25519",
|
"embedwards25519",
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
@@ -2773,6 +2760,7 @@ dependencies = [
|
|||||||
"rand_chacha 0.3.1",
|
"rand_chacha 0.3.1",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"secq256k1",
|
"secq256k1",
|
||||||
|
"std-shims",
|
||||||
"thiserror 2.0.16",
|
"thiserror 2.0.16",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -2873,7 +2861,7 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ec-divisors"
|
name = "ec-divisors"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
@@ -3545,10 +3533,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "full-chain-membership-proofs"
|
name = "full-chain-membership-proofs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"ec-divisors",
|
"ec-divisors",
|
||||||
"generalized-bulletproofs",
|
"generalized-bulletproofs",
|
||||||
"generalized-bulletproofs-circuit-abstraction",
|
"generalized-bulletproofs-circuit-abstraction",
|
||||||
@@ -3758,10 +3746,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "generalized-bulletproofs"
|
name = "generalized-bulletproofs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"multiexp",
|
"multiexp",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"std-shims",
|
"std-shims",
|
||||||
@@ -3771,9 +3759,9 @@ 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/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"generalized-bulletproofs",
|
"generalized-bulletproofs",
|
||||||
"std-shims",
|
"std-shims",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -3782,9 +3770,9 @@ 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/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"generalized-bulletproofs-circuit-abstraction",
|
"generalized-bulletproofs-circuit-abstraction",
|
||||||
"generic-array 1.2.0",
|
"generic-array 1.2.0",
|
||||||
"std-shims",
|
"std-shims",
|
||||||
@@ -4002,10 +3990,10 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "helioselene"
|
name = "helioselene"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"ec-divisors",
|
"ec-divisors",
|
||||||
@@ -6074,7 +6062,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-address"
|
name = "monero-address"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"monero-base58",
|
"monero-base58",
|
||||||
@@ -6086,7 +6074,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-base58"
|
name = "monero-base58"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"monero-primitives",
|
"monero-primitives",
|
||||||
"std-shims",
|
"std-shims",
|
||||||
@@ -6095,7 +6083,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-borromean"
|
name = "monero-borromean"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"monero-generators",
|
"monero-generators",
|
||||||
@@ -6108,7 +6096,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-bulletproofs"
|
name = "monero-bulletproofs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"monero-generators",
|
"monero-generators",
|
||||||
@@ -6123,7 +6111,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-clsag"
|
name = "monero-clsag"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
@@ -6144,10 +6132,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-fcmp-plus-plus"
|
name = "monero-fcmp-plus-plus"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"ec-divisors",
|
"ec-divisors",
|
||||||
@@ -6166,10 +6154,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-generators"
|
name = "monero-generators"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2",
|
"blake2",
|
||||||
"ciphersuite 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ciphersuite 0.4.99",
|
||||||
"crypto-bigint",
|
"crypto-bigint",
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
@@ -6186,7 +6174,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-io"
|
name = "monero-io"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"std-shims",
|
"std-shims",
|
||||||
@@ -6195,7 +6183,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-mlsag"
|
name = "monero-mlsag"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"monero-generators",
|
"monero-generators",
|
||||||
@@ -6209,7 +6197,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/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
@@ -6228,7 +6216,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-primitives"
|
name = "monero-primitives"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"monero-generators",
|
"monero-generators",
|
||||||
@@ -6241,7 +6229,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-rpc"
|
name = "monero-rpc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -6257,7 +6245,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/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest_auth",
|
"digest_auth",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -6270,7 +6258,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-wallet"
|
name = "monero-wallet"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e#b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e"
|
source = "git+https://github.com/kayabaNerve/monero-oxide?rev=54da48f27a05fa8656014942919da1dfbab4d8e3#54da48f27a05fa8656014942919da1dfbab4d8e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curve25519-dalek",
|
"curve25519-dalek",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
@@ -10198,6 +10186,7 @@ dependencies = [
|
|||||||
"ciphersuite 0.4.2",
|
"ciphersuite 0.4.2",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"dkg",
|
"dkg",
|
||||||
|
"dkg-evrf",
|
||||||
"embedwards25519",
|
"embedwards25519",
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
"minimal-ed448",
|
"minimal-ed448",
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ members = [
|
|||||||
"patches/option-ext",
|
"patches/option-ext",
|
||||||
"patches/directories-next",
|
"patches/directories-next",
|
||||||
|
|
||||||
|
# monero-oxide expects ciphersuite, yet the ciphersuite in-tree here has breaking changes
|
||||||
|
# This re-exports the in-tree ciphersuite _without_ changes breaking to monero-oxide
|
||||||
|
# Not included in workspace to prevent having two crates with the same name (an error)
|
||||||
|
# "patches/ciphersuite",
|
||||||
|
|
||||||
"common/std-shims",
|
"common/std-shims",
|
||||||
"common/zalloc",
|
"common/zalloc",
|
||||||
"common/patchable-async-sleep",
|
"common/patchable-async-sleep",
|
||||||
@@ -172,6 +177,7 @@ std-shims = { path = "common/std-shims" }
|
|||||||
simple-request = { path = "common/request" }
|
simple-request = { path = "common/request" }
|
||||||
multiexp = { path = "crypto/multiexp" }
|
multiexp = { path = "crypto/multiexp" }
|
||||||
flexible-transcript = { path = "crypto/transcript" }
|
flexible-transcript = { path = "crypto/transcript" }
|
||||||
|
ciphersuite = { path = "patches/ciphersuite" }
|
||||||
dalek-ff-group = { path = "crypto/dalek-ff-group" }
|
dalek-ff-group = { path = "crypto/dalek-ff-group" }
|
||||||
minimal-ed448 = { path = "crypto/ed448" }
|
minimal-ed448 = { path = "crypto/ed448" }
|
||||||
modular-frost = { path = "crypto/frost" }
|
modular-frost = { path = "crypto/frost" }
|
||||||
|
|||||||
@@ -23,31 +23,37 @@ 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"] }
|
||||||
|
|
||||||
|
std-shims = { version = "0.1", path = "../../../common/std-shims", default-features = false }
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
transcript = { package = "flexible-transcript", path = "../../transcript", version = "^0.3.2", default-features = false, features = ["recommended"] }
|
||||||
|
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false }
|
ciphersuite = { path = "../../ciphersuite", version = "^0.4.1", default-features = false, features = ["alloc"] }
|
||||||
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.10", default-features = false, features = ["std"] }
|
blake2 = { version = "0.10", default-features = false }
|
||||||
rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
rand_chacha = { version = "0.3", default-features = false }
|
||||||
generalized-bulletproofs = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
|
||||||
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
generalized-bulletproofs = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
generalized-bulletproofs-circuit-abstraction = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e" }
|
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e" }
|
generalized-bulletproofs-circuit-abstraction = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
|
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
|
|
||||||
dkg = { path = ".." }
|
dkg = { path = ".." }
|
||||||
|
|
||||||
|
ciphersuite-kp256 = { path = "../../ciphersuite/kp256", default-features = false, optional = true }
|
||||||
secq256k1 = { path = "../../evrf/secq256k1", optional = true }
|
secq256k1 = { path = "../../evrf/secq256k1", optional = true }
|
||||||
|
dalek-ff-group = { path = "../../dalek-ff-group", default-features = false, optional = true }
|
||||||
embedwards25519 = { path = "../../evrf/embedwards25519", optional = true }
|
embedwards25519 = { path = "../../evrf/embedwards25519", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||||
rand = { version = "0.8", default-features = false, features = ["std"] }
|
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 = "../../evrf/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/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", features = ["tests"] }
|
generalized-bulletproofs = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", features = ["tests"] }
|
||||||
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e" }
|
dkg-recovery = { path = "../recovery" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
@@ -55,14 +61,32 @@ std = [
|
|||||||
|
|
||||||
"rand_core/std",
|
"rand_core/std",
|
||||||
|
|
||||||
|
"zeroize/std",
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
"transcript/std",
|
"transcript/std",
|
||||||
|
|
||||||
"ciphersuite/std",
|
"ciphersuite/std",
|
||||||
"multiexp/std",
|
"multiexp/std",
|
||||||
"multiexp/batch",
|
"multiexp/batch",
|
||||||
|
|
||||||
|
"blake2/std",
|
||||||
|
"rand_chacha/std",
|
||||||
|
|
||||||
|
"generalized-bulletproofs/std",
|
||||||
|
"ec-divisors/std",
|
||||||
|
"generalized-bulletproofs-circuit-abstraction/std",
|
||||||
|
"generalized-bulletproofs-ec-gadgets/std",
|
||||||
|
|
||||||
|
"dkg/std",
|
||||||
|
|
||||||
|
"ciphersuite-kp256?/std",
|
||||||
|
"secq256k1?/std",
|
||||||
|
"dalek-ff-group?/std",
|
||||||
|
"embedwards25519?/std",
|
||||||
]
|
]
|
||||||
secp256k1 = ["secq256k1"]
|
secp256k1 = ["ciphersuite-kp256", "secq256k1"]
|
||||||
ed25519 = ["embedwards25519"]
|
ed25519 = ["dalek-ff-group", "embedwards25519"]
|
||||||
ristretto = ["embedwards25519"]
|
ristretto = ["dalek-ff-group", "embedwards25519"]
|
||||||
tests = ["rand_core/getrandom"]
|
tests = ["rand_core/getrandom"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
50
crypto/dkg/evrf/README.md
Normal file
50
crypto/dkg/evrf/README.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# eVRF DKG
|
||||||
|
|
||||||
|
The DKG from the [eVRF paper](https://eprint.iacr.org/2024/397), extended with
|
||||||
|
Verifiable Encryption premised on the same methodology present in the eVRF
|
||||||
|
paper.
|
||||||
|
|
||||||
|
The DDH-premised VRF is used, yet the different instantiation presented in
|
||||||
|
section 6.4 premised on elliptic curve divisors. The one-round threshold DKG
|
||||||
|
presented in section 4.2 is extended, with the following changes:
|
||||||
|
|
||||||
|
- Any threshold of `t` participants may complete the DKG. This allows an
|
||||||
|
adversary to bias the resulting key by choosing the set of participants, yet
|
||||||
|
offers a robust protocol. The caller is able to choose between robustness and
|
||||||
|
a lack of bias by completing the DKG with just `t` messages or by waiting for
|
||||||
|
all `n`. If the caller does opt for robustness, the caller must ensure
|
||||||
|
participants agree on the subset of participants who actually participated.
|
||||||
|
|
||||||
|
- Communication of shares was prior defined as simply sending the share to the
|
||||||
|
relevant participant, with no description of the channel. Now, a pair of
|
||||||
|
ECDHs are performed on the embedded curve occurs (between the sender and the
|
||||||
|
recipient's public key), whose `x` coordinates are summed for a random,
|
||||||
|
uniform value (as an eVRF would). This value is used as a mask to encrypt the
|
||||||
|
communicated secret share, with the zero-knowledge proof proving it's
|
||||||
|
well-formed. This removes the need for a complaint round from the protocol,
|
||||||
|
allowing it to truly complete (with all recipients holding valid shares) in
|
||||||
|
just one round.
|
||||||
|
|
||||||
|
For a gist of the verifiable encryption scheme, please see
|
||||||
|
https://gist.github.com/kayabaNerve/cfbde74b0660dfdf8dd55326d6ec33d7. Security
|
||||||
|
proofs are currently being worked on.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This library relies on an implementation of Bulletproofs and various
|
||||||
|
zero-knowledge gadgets. This library uses
|
||||||
|
[`generalized-bulletproofs`](https://docs.rs/generalized-bulletproofs),
|
||||||
|
[`generalized-bulletproofs-circuit-abstraction`](https://docs.rs/generalized-bulletproofs-circuit-abstraction),
|
||||||
|
and
|
||||||
|
[`generalized-bulletproofs-ec-gadgets`](https://docs.rs/generalized-bulletproofs-ec-gadgets)
|
||||||
|
from the Monero project's FCMP++ codebase. These libraries have received the
|
||||||
|
following audits in the past:
|
||||||
|
- https://github.com/kayabaNerve/monero-oxide/tree/fcmp++/audits/generalized-bulletproofs
|
||||||
|
- https://github.com/kayabaNerve/monero-oxide/tree/fcmp++/audits/fcmps
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This library supports being run in no-std contexts with `alloc` when the `std`
|
||||||
|
feature (on by default) is disabled. Due to the intensity of the ZK proofs,
|
||||||
|
this isn't recommended, yet may be justified when _verifying_ posted proofs are
|
||||||
|
correct.
|
||||||
109
crypto/dkg/evrf/src/curves.rs
Normal file
109
crypto/dkg/evrf/src/curves.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::vec::Vec;
|
||||||
|
|
||||||
|
use rand_core::SeedableRng;
|
||||||
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
|
||||||
|
use blake2::{
|
||||||
|
digest::{
|
||||||
|
generic_array::{typenum::U32, GenericArray},
|
||||||
|
crypto_common::KeySizeUser,
|
||||||
|
KeyInit, Mac,
|
||||||
|
},
|
||||||
|
Blake2sMac,
|
||||||
|
};
|
||||||
|
type Blake2s256Keyed = Blake2sMac<U32>;
|
||||||
|
|
||||||
|
use ciphersuite::{
|
||||||
|
group::{ff::FromUniformBytes, GroupEncoding},
|
||||||
|
Ciphersuite,
|
||||||
|
};
|
||||||
|
|
||||||
|
use ec_divisors::DivisorCurve;
|
||||||
|
use generalized_bulletproofs::Generators as BpGenerators;
|
||||||
|
use generalized_bulletproofs_ec_gadgets::*;
|
||||||
|
|
||||||
|
/// A pair of curves to perform the eVRF with.
|
||||||
|
pub trait Curves {
|
||||||
|
/// The towering curve, for which the resulting key is on.
|
||||||
|
type ToweringCurve: Ciphersuite<F: FromUniformBytes<64>>;
|
||||||
|
/// The embedded curve which participants represent their public keys over.
|
||||||
|
type EmbeddedCurve: Ciphersuite<
|
||||||
|
G: DivisorCurve<FieldElement = <Self::ToweringCurve as Ciphersuite>::F>,
|
||||||
|
>;
|
||||||
|
/// The parameters to use the embedded curve with the discrete-log gadget.
|
||||||
|
type EmbeddedCurveParameters: DiscreteLogParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generators for an eVRF DKG.
|
||||||
|
///
|
||||||
|
/// These should be kept within a static. They're non-trivial to generate.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Generators<C: Curves>(pub(crate) BpGenerators<C::ToweringCurve>);
|
||||||
|
|
||||||
|
impl<C: Curves> Generators<C> {
|
||||||
|
/// Create a new set of generators.
|
||||||
|
///
|
||||||
|
/// This is deterministic to the towering curve's (possibly truncated) ID and generator.
|
||||||
|
pub fn new(max_threshold: u16, max_participants: u16) -> Generators<C> {
|
||||||
|
let entropy = <Blake2s256Keyed as KeyInit>::new(&{
|
||||||
|
let mut key = GenericArray::<u8, <Blake2s256Keyed as KeySizeUser>::KeySize>::default();
|
||||||
|
let key_len = key.len().min(<C::ToweringCurve as Ciphersuite>::ID.len());
|
||||||
|
{
|
||||||
|
let key: &mut [u8] = key.as_mut();
|
||||||
|
key[.. key_len].copy_from_slice(&<C::ToweringCurve as Ciphersuite>::ID[.. key_len])
|
||||||
|
}
|
||||||
|
key
|
||||||
|
})
|
||||||
|
.chain_update(<C::ToweringCurve as Ciphersuite>::generator().to_bytes())
|
||||||
|
.finalize()
|
||||||
|
.into_bytes();
|
||||||
|
let mut rng = ChaCha20Rng::from_seed(entropy.into());
|
||||||
|
|
||||||
|
let h = crate::sample_point::<C::ToweringCurve>(&mut rng);
|
||||||
|
let generators =
|
||||||
|
crate::Proof::<C>::generators_to_use(max_threshold.into(), max_participants.into());
|
||||||
|
let mut g_bold = Vec::with_capacity(generators);
|
||||||
|
let mut h_bold = Vec::with_capacity(generators);
|
||||||
|
for _ in 0 .. generators {
|
||||||
|
g_bold.push(crate::sample_point::<C::ToweringCurve>(&mut rng));
|
||||||
|
h_bold.push(crate::sample_point::<C::ToweringCurve>(&mut rng));
|
||||||
|
}
|
||||||
|
Self(
|
||||||
|
BpGenerators::new(<C::ToweringCurve as Ciphersuite>::generator(), h, g_bold, h_bold).unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO
|
||||||
|
/// Secp256k1, and an elliptic curve defined over its scalar field (secq256k1).
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
|
pub struct Secp256k1;
|
||||||
|
#[cfg(feature = "secp256k1")]
|
||||||
|
impl Curves for Secp256k1 {
|
||||||
|
type ToweringCurve = ciphersuite_kp256::Secp256k1;
|
||||||
|
type EmbeddedCurve = secq256k1::Secq256k1;
|
||||||
|
type EmbeddedCurveParameters = secq256k1::Secq256k1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Ed25519, and an elliptic curve defined over its scalar field (embedwards25519).
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
pub struct Ed25519;
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
impl Curves for Ed25519 {
|
||||||
|
type ToweringCurve = dalek_ff_group::Ed25519;
|
||||||
|
type EmbeddedCurve = 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;
|
||||||
|
}
|
||||||
@@ -1,72 +1,12 @@
|
|||||||
/*
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
We implement a DKG using an eVRF, as detailed in the eVRF paper. For the eVRF itself, we do not
|
#![doc = include_str!("../README.md")]
|
||||||
use a Paillier-based construction, nor the detailed construction premised on a Bulletproof.
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
For reference, the detailed construction premised on a Bulletproof involves two curves, notated
|
|
||||||
here as `C` and `E`, where the scalar field of `C` is the field of `E`. Accordingly, Bulletproofs
|
|
||||||
over `C` can efficiently perform group operations of points of curve `E`. Each participant has a
|
|
||||||
private point (`P_i`) on curve `E` committed to over curve `C`. The eVRF selects a pair of
|
|
||||||
scalars `a, b`, where the participant proves in-Bulletproof the points `A_i, B_i` are
|
|
||||||
`a * P_i, b * P_i`. The eVRF proceeds to commit to `A_i.x + B_i.x` in a Pedersen Commitment.
|
|
||||||
|
|
||||||
Our eVRF uses
|
|
||||||
[Generalized Bulletproofs](
|
|
||||||
https://repo.getmonero.org/monero-project/ccs-proposals
|
|
||||||
/uploads/a9baa50c38c6312efc0fea5c6a188bb9/gbp.pdf
|
|
||||||
).
|
|
||||||
This allows us much larger witnesses without growing the reference string, and enables us to
|
|
||||||
efficiently sample challenges off in-circuit variables (via placing the variables in a vector
|
|
||||||
commitment, then challenging from a transcript of the commitments). We proceed to use
|
|
||||||
[elliptic curve divisors](
|
|
||||||
https://repo.getmonero.org/-/project/54/
|
|
||||||
uploads/eb1bf5b4d4855a3480c38abf895bd8e8/Veridise_Divisor_Proofs.pdf
|
|
||||||
)
|
|
||||||
(which require the ability to sample a challenge off in-circuit variables) to prove discrete
|
|
||||||
logarithms efficiently.
|
|
||||||
|
|
||||||
This is done via having a private scalar (`p_i`) on curve `E`, not a private point, and
|
|
||||||
publishing the public key for it (`P_i = p_i * G`, where `G` is a generator of `E`). The eVRF
|
|
||||||
samples two points with unknown discrete logarithms `A, B`, and the circuit proves a Pedersen
|
|
||||||
Commitment commits to `(p_i * A).x + (p_i * B).x`.
|
|
||||||
|
|
||||||
With the eVRF established, we now detail our other novel aspect. The eVRF paper expects secret
|
|
||||||
shares to be sent to the other parties yet does not detail a precise way to do so. If we
|
|
||||||
encrypted the secret shares with some stream cipher, each recipient would have to attest validity
|
|
||||||
or accuse the sender of impropriety. We want an encryption scheme where anyone can verify the
|
|
||||||
secret shares were encrypted properly, without additional info, efficiently.
|
|
||||||
|
|
||||||
Please note from the published commitments, it's possible to calculcate a commitment to the
|
|
||||||
secret share each party should receive (`V_i`).
|
|
||||||
|
|
||||||
We have the sender sample two scalars per recipient, denoted `x_i, y_i` (where `i` is the
|
|
||||||
recipient index). They perform the eVRF to prove a Pedersen Commitment commits to
|
|
||||||
`z_i = (x_i * P_i).x + (y_i * P_i).x` and `x_i, y_i` are the discrete logarithms of `X_i, Y_i`
|
|
||||||
over `G`. They then publish the encrypted share `s_i + z_i` and `X_i, Y_i`.
|
|
||||||
|
|
||||||
The recipient is able to decrypt the share via calculating
|
|
||||||
`s_i - ((p_i * X_i).x + (p_i * Y_i).x)`.
|
|
||||||
|
|
||||||
To verify the secret share, we have the `F` terms of the Pedersen Commitments revealed (where
|
|
||||||
`F, H` are generators of `C`, `F` is used for binding and `H` for blinding). This already needs
|
|
||||||
to be done for the eVRF outputs used within the DKG, in order to obtain thecommitments to the
|
|
||||||
coefficients. When we have the commitment `Z_i = ((p_i * A).x + (p_i * B).x) * F`, we simply
|
|
||||||
check `s_i * F = Z_i + V_i`.
|
|
||||||
|
|
||||||
In order to open the Pedersen Commitments to their `F` terms, we transcript the commitments and
|
|
||||||
the claimed openings, then assign random weights to each pair of `(commitment, opening). The
|
|
||||||
prover proves knowledge of the discrete logarithm of the sum weighted commitments, minus the sum
|
|
||||||
sum weighted openings, over `H`.
|
|
||||||
|
|
||||||
The benefit to this construction is that given an broadcast channel which is reliable and
|
|
||||||
ordered, only `t` messages must be broadcast from honest parties in order to create a `t`-of-`n`
|
|
||||||
multisig. If the encrypted secret shares were not verifiable, one would need at least `t + n`
|
|
||||||
messages to ensure every participant has a correct dealing and can participate in future
|
|
||||||
reconstructions of the secret. This would also require all `n` parties be online, whereas this is
|
|
||||||
robust to threshold `t`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::{
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::{
|
||||||
|
vec::Vec,
|
||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
collections::{HashSet, HashMap},
|
collections::{HashSet, HashMap},
|
||||||
};
|
};
|
||||||
@@ -85,14 +25,22 @@ use ciphersuite::{
|
|||||||
};
|
};
|
||||||
use multiexp::multiexp_vartime;
|
use multiexp::multiexp_vartime;
|
||||||
|
|
||||||
use generalized_bulletproofs::{Generators, arithmetic_circuit_proof::*};
|
use generalized_bulletproofs::arithmetic_circuit_proof::*;
|
||||||
use ec_divisors::DivisorCurve;
|
use ec_divisors::DivisorCurve;
|
||||||
|
|
||||||
use dkg::{Participant, ThresholdParams, Interpolation, ThresholdKeys};
|
pub use dkg::*;
|
||||||
|
|
||||||
pub(crate) mod proof;
|
mod utils;
|
||||||
|
pub(crate) use utils::*;
|
||||||
|
|
||||||
|
mod curves;
|
||||||
|
pub use curves::*;
|
||||||
|
|
||||||
|
mod proof;
|
||||||
use proof::*;
|
use proof::*;
|
||||||
pub use proof::{EvrfCurve, EvrfGenerators};
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
/// Participation in the DKG.
|
/// Participation in the DKG.
|
||||||
///
|
///
|
||||||
@@ -106,14 +54,20 @@ pub struct Participation<C: Ciphersuite> {
|
|||||||
|
|
||||||
impl<C: Ciphersuite> Participation<C> {
|
impl<C: Ciphersuite> Participation<C> {
|
||||||
pub fn read<R: Read>(reader: &mut R, n: u16) -> io::Result<Self> {
|
pub fn read<R: Read>(reader: &mut R, n: u16) -> io::Result<Self> {
|
||||||
|
// Ban <32-bit platforms, allowing us to assume `u32` -> `usize` works
|
||||||
|
const _NO_16_BIT_PLATFORMS: [(); (usize::BITS - u32::BITS) as usize] = [(); _];
|
||||||
|
|
||||||
// TODO: Replace `len` with some calculation deterministic to the params
|
// TODO: Replace `len` with some calculation deterministic to the params
|
||||||
let mut len = [0; 4];
|
let mut len = [0; 4];
|
||||||
reader.read_exact(&mut len)?;
|
reader.read_exact(&mut len)?;
|
||||||
let len = usize::try_from(u32::from_le_bytes(len)).expect("<32-bit platform?");
|
let len = usize::try_from(u32::from_le_bytes(len)).expect("<32-bit platform?");
|
||||||
|
|
||||||
// Don't allocate a buffer for the claimed length
|
/*
|
||||||
// Read chunks until we reach the claimed length
|
Don't allocate a buffer for the claimed length.
|
||||||
// This means if we were told to read GB, we must actually be sent GB before allocating as such
|
|
||||||
|
We read chunks of a fixed-length until we reach the claimed length, preventing an adversary
|
||||||
|
from forcing us to allocate GB unless the proof is actually GB long.
|
||||||
|
*/
|
||||||
const CHUNK_SIZE: usize = 1024;
|
const CHUNK_SIZE: usize = 1024;
|
||||||
let mut proof = Vec::with_capacity(len.min(CHUNK_SIZE));
|
let mut proof = Vec::with_capacity(len.min(CHUNK_SIZE));
|
||||||
while proof.len() < len {
|
while proof.len() < len {
|
||||||
@@ -124,7 +78,7 @@ impl<C: Ciphersuite> 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 (1 ..= n).map(Participant) {
|
for i in Participant::iter().take(usize::from(n)) {
|
||||||
encrypted_secret_shares.insert(i, C::read_F(reader)?);
|
encrypted_secret_shares.insert(i, C::read_F(reader)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,113 +88,82 @@ impl<C: Ciphersuite> Participation<C> {
|
|||||||
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
writer.write_all(&u32::try_from(self.proof.len()).unwrap().to_le_bytes())?;
|
writer.write_all(&u32::try_from(self.proof.len()).unwrap().to_le_bytes())?;
|
||||||
writer.write_all(&self.proof)?;
|
writer.write_all(&self.proof)?;
|
||||||
for i in (1 ..= u16::try_from(self.encrypted_secret_shares.len())
|
for i in Participant::iter().take(self.encrypted_secret_shares.len()) {
|
||||||
.expect("writing a Participation which has a n > u16::MAX"))
|
|
||||||
.map(Participant)
|
|
||||||
{
|
|
||||||
writer.write_all(self.encrypted_secret_shares[&i].to_repr().as_ref())?;
|
writer.write_all(self.encrypted_secret_shares[&i].to_repr().as_ref())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn polynomial<F: PrimeField + Zeroize>(
|
|
||||||
coefficients: &[Zeroizing<F>],
|
|
||||||
l: Participant,
|
|
||||||
) -> Zeroizing<F> {
|
|
||||||
let l = F::from(u64::from(u16::from(l)));
|
|
||||||
// This should never be reached since Participant is explicitly non-zero
|
|
||||||
assert!(l != F::ZERO, "zero participant passed to polynomial");
|
|
||||||
let mut share = Zeroizing::new(F::ZERO);
|
|
||||||
for (idx, coefficient) in coefficients.iter().rev().enumerate() {
|
|
||||||
*share += coefficient.deref();
|
|
||||||
if idx != (coefficients.len() - 1) {
|
|
||||||
*share *= l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
share
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
fn share_verification_statements<C: Ciphersuite>(
|
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
|
||||||
commitments: &[C::G],
|
|
||||||
n: u16,
|
|
||||||
encryption_commitments: &[C::G],
|
|
||||||
encrypted_secret_shares: &HashMap<Participant, C::F>,
|
|
||||||
) -> (C::F, Vec<(C::F, C::G)>) {
|
|
||||||
debug_assert_eq!(usize::from(n), encryption_commitments.len());
|
|
||||||
debug_assert_eq!(usize::from(n), encrypted_secret_shares.len());
|
|
||||||
|
|
||||||
let mut g_scalar = C::F::ZERO;
|
|
||||||
let mut pairs = Vec::with_capacity(commitments.len() + encryption_commitments.len());
|
|
||||||
for commitment in commitments {
|
|
||||||
pairs.push((C::F::ZERO, *commitment));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut weight;
|
|
||||||
for (i, enc_share) in encrypted_secret_shares {
|
|
||||||
let enc_commitment = encryption_commitments[usize::from(u16::from(*i)) - 1];
|
|
||||||
|
|
||||||
weight = C::F::random(&mut *rng);
|
|
||||||
|
|
||||||
// s_i F
|
|
||||||
g_scalar += weight * enc_share;
|
|
||||||
// - Z_i
|
|
||||||
let weight = -weight;
|
|
||||||
pairs.push((weight, enc_commitment));
|
|
||||||
// - V_i
|
|
||||||
{
|
|
||||||
let i = C::F::from(u64::from(u16::from(*i)));
|
|
||||||
// The first `commitments.len()` pairs are for the commitments
|
|
||||||
(0 .. commitments.len()).fold(weight, |exp, j| {
|
|
||||||
pairs[j].0 += exp;
|
|
||||||
exp * i
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(g_scalar, pairs)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors from the eVRF DKG.
|
/// Errors from the eVRF DKG.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
|
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
|
||||||
pub enum EvrfError {
|
pub enum Error {
|
||||||
#[error("n, the amount of participants, exceeded a u16")]
|
/// Too many participants were provided.
|
||||||
TooManyParticipants,
|
#[error("{provided} participants provided, exceeding the limit of u16::MAX")]
|
||||||
#[error("the threshold t wasn't in range 1 <= t <= n")]
|
TooManyParticipants {
|
||||||
InvalidThreshold,
|
/// The amount of provided participants.
|
||||||
|
provided: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The threshold exceeded the amount of participants.
|
||||||
|
#[error("invalid threshold (max {n}, got {t})")]
|
||||||
|
InvalidThreshold {
|
||||||
|
/// The specified threshold.
|
||||||
|
t: u16,
|
||||||
|
/// The specified total amount of participants.
|
||||||
|
n: u16,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A participant's public key was the identity point.
|
||||||
#[error("a public key was the identity point")]
|
#[error("a public key was the identity point")]
|
||||||
PublicKeyWasIdentity,
|
PublicKeyWasIdentity,
|
||||||
|
|
||||||
|
/// Participating in a DKG we aren't present in.
|
||||||
#[error("participating in a DKG we aren't a participant in")]
|
#[error("participating in a DKG we aren't a participant in")]
|
||||||
NotAParticipant,
|
NotAParticipant,
|
||||||
|
|
||||||
|
/// A participant which doesn't exist provided a participation.
|
||||||
#[error("a participant with an unrecognized ID participated")]
|
#[error("a participant with an unrecognized ID participated")]
|
||||||
NonExistentParticipant,
|
NonExistentParticipant,
|
||||||
#[error("the passed in generators did not have enough generators for this DKG")]
|
|
||||||
NotEnoughGenerators,
|
/// Insufficient amount of generators for this DKG.
|
||||||
|
#[error("the passed in generators ({provided}) weren't enough for this DKG (needed {required})")]
|
||||||
|
NotEnoughGenerators {
|
||||||
|
/// The amount of generators provided.
|
||||||
|
provided: usize,
|
||||||
|
/// The amount of generators required.
|
||||||
|
required: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of calling EvrfDkg::verify.
|
/// The result of calling `Dkg::verify`.
|
||||||
pub enum VerifyResult<C: EvrfCurve> {
|
pub enum VerifyResult<C: Curves> {
|
||||||
Valid(EvrfDkg<C>),
|
/// The DKG participations were valid.
|
||||||
|
Valid(Dkg<C>),
|
||||||
|
/// The DKG participants were invalid, identifying the faulty participants.
|
||||||
Invalid(Vec<Participant>),
|
Invalid(Vec<Participant>),
|
||||||
|
/// Not enough participations were provided, yet no provided participations were faulty.
|
||||||
NotEnoughParticipants,
|
NotEnoughParticipants,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Struct to perform/verify the DKG with.
|
/// Struct representing a DKG.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EvrfDkg<C: EvrfCurve> {
|
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 Ciphersuite>::G>,
|
||||||
group_key: C::G,
|
verification_shares: HashMap<Participant, <C::ToweringCurve as Ciphersuite>::G>,
|
||||||
verification_shares: HashMap<Participant, C::G>,
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
encrypted_secret_shares:
|
encrypted_secret_shares: HashMap<
|
||||||
HashMap<Participant, HashMap<Participant, ([<C::EmbeddedCurve as Ciphersuite>::G; 2], C::F)>>,
|
Participant,
|
||||||
|
HashMap<
|
||||||
|
Participant,
|
||||||
|
([<C::EmbeddedCurve as Ciphersuite>::G; 2], <C::ToweringCurve as Ciphersuite>::F),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: EvrfCurve> EvrfDkg<C> {
|
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],
|
||||||
@@ -260,40 +183,37 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
///
|
///
|
||||||
/// The context MUST be unique across invocations. Reuse of context will lead to sharing
|
/// The context MUST be unique across invocations. Reuse of context will lead to sharing
|
||||||
/// prior-shared secrets.
|
/// prior-shared secrets.
|
||||||
///
|
|
||||||
/// Public keys are not allowed to be the identity point. This will error if any are.
|
|
||||||
pub fn participate(
|
pub fn participate(
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
generators: &EvrfGenerators<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 Ciphersuite>::G],
|
||||||
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
evrf_private_key: &Zeroizing<<C::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
) -> Result<Participation<C>, EvrfError> {
|
) -> Result<Participation<C::ToweringCurve>, Error> {
|
||||||
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { Err(EvrfError::TooManyParticipants)? };
|
let Ok(n) = u16::try_from(evrf_public_keys.len()) else {
|
||||||
|
Err(Error::TooManyParticipants { provided: evrf_public_keys.len() })?
|
||||||
|
};
|
||||||
if (t == 0) || (t > n) {
|
if (t == 0) || (t > n) {
|
||||||
Err(EvrfError::InvalidThreshold)?;
|
Err(Error::InvalidThreshold { t, n })?;
|
||||||
}
|
}
|
||||||
if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) {
|
if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) {
|
||||||
Err(EvrfError::PublicKeyWasIdentity)?;
|
Err(Error::PublicKeyWasIdentity)?;
|
||||||
};
|
};
|
||||||
// This also checks the private key is not 0
|
// This also ensures the private key is not 0, due to the prior check the identity point wasn't
|
||||||
|
// present
|
||||||
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
||||||
if !evrf_public_keys.iter().any(|key| *key == evrf_public_key) {
|
if !evrf_public_keys.contains(&evrf_public_key) {
|
||||||
Err(EvrfError::NotAParticipant)?;
|
Err(Error::NotAParticipant)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
let transcript = Self::initial_transcript(context, evrf_public_keys, t);
|
let transcript = Self::initial_transcript(context, evrf_public_keys, t);
|
||||||
// Further bind to the participant index so each index gets unique generators
|
// Bind to the participant
|
||||||
// This allows reusing eVRF public keys as the prover
|
|
||||||
let mut per_proof_transcript = Blake2s256::new();
|
let mut per_proof_transcript = Blake2s256::new();
|
||||||
per_proof_transcript.update(transcript);
|
per_proof_transcript.update(transcript);
|
||||||
per_proof_transcript.update(evrf_public_key.to_bytes());
|
per_proof_transcript.update(evrf_public_key.to_bytes());
|
||||||
|
|
||||||
// The above transcript is expected to be binding to all arguments here
|
let ProveResult { coefficients, encryption_keys, proof } = match Proof::<C>::prove(
|
||||||
// The generators are constant to this ciphersuite's generator, and the parameters are
|
|
||||||
// transcripted
|
|
||||||
let EvrfProveResult { coefficients, encryption_masks, proof } = match Evrf::prove(
|
|
||||||
rng,
|
rng,
|
||||||
&generators.0,
|
&generators.0,
|
||||||
per_proof_transcript.finalize().into(),
|
per_proof_transcript.finalize().into(),
|
||||||
@@ -302,7 +222,10 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
evrf_private_key,
|
evrf_private_key,
|
||||||
) {
|
) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(AcError::NotEnoughGenerators) => Err(EvrfError::NotEnoughGenerators)?,
|
Err(AcError::NotEnoughGenerators) => Err(Error::NotEnoughGenerators {
|
||||||
|
provided: generators.0.g_bold_slice().len(),
|
||||||
|
required: Proof::<C>::generators_to_use(usize::from(t), evrf_public_keys.len()),
|
||||||
|
})?,
|
||||||
Err(
|
Err(
|
||||||
AcError::DifferingLrLengths |
|
AcError::DifferingLrLengths |
|
||||||
AcError::InconsistentAmountOfConstraints |
|
AcError::InconsistentAmountOfConstraints |
|
||||||
@@ -317,14 +240,62 @@ impl<C: EvrfCurve> EvrfDkg<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_mask) in (1 ..= n).map(Participant).zip(encryption_masks) {
|
for (l, encryption_key) in Participant::iter().take(usize::from(n)).zip(encryption_keys) {
|
||||||
let share = polynomial::<C::F>(&coefficients, l);
|
let share = polynomial::<<C::ToweringCurve as Ciphersuite>::F>(&coefficients, l);
|
||||||
encrypted_secret_shares.insert(l, *share + *encryption_mask);
|
encrypted_secret_shares.insert(l, *share + *encryption_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Participation { proof, encrypted_secret_shares })
|
Ok(Participation { proof, encrypted_secret_shares })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Batch-verifiable statements to verify encrypted secret shares.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
fn verifiable_encryption_statements<C: Curves>(
|
||||||
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
|
coefficients: &[<C::ToweringCurve as Ciphersuite>::G],
|
||||||
|
encryption_key_commitments: &[<C::ToweringCurve as Ciphersuite>::G],
|
||||||
|
encrypted_secret_shares: &HashMap<Participant, <C::ToweringCurve as Ciphersuite>::F>,
|
||||||
|
) -> (
|
||||||
|
<C::ToweringCurve as Ciphersuite>::F,
|
||||||
|
Vec<(<C::ToweringCurve as Ciphersuite>::F, <C::ToweringCurve as Ciphersuite>::G)>,
|
||||||
|
) {
|
||||||
|
let mut g_scalar = <C::ToweringCurve as Ciphersuite>::F::ZERO;
|
||||||
|
let mut pairs = Vec::with_capacity(coefficients.len() + encryption_key_commitments.len());
|
||||||
|
|
||||||
|
// Push on the commitments to the polynomial being secret-shared
|
||||||
|
for coefficient in coefficients {
|
||||||
|
// This uses `0` as we'll add to it later, given its fixed position
|
||||||
|
pairs.push((<C::ToweringCurve as Ciphersuite>::F::ZERO, *coefficient));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, encrypted_secret_share) in encrypted_secret_shares {
|
||||||
|
let encryption_key_commitment = encryption_key_commitments[usize::from(u16::from(*i)) - 1];
|
||||||
|
|
||||||
|
let weight = <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The encrypted secret share scaling `G`, minus the encryption key commitment, minus the
|
||||||
|
ommitment to the secret share, should equal the identity point.
|
||||||
|
|
||||||
|
We actually subtract the encrypted share to optimize the amount of negations we perform.
|
||||||
|
*/
|
||||||
|
g_scalar -= weight * encrypted_secret_share;
|
||||||
|
pairs.push((weight, encryption_key_commitment));
|
||||||
|
// 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)));
|
||||||
|
(0 .. coefficients.len()).fold(weight, |exp, j| {
|
||||||
|
pairs[j].0 += exp;
|
||||||
|
exp * i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(g_scalar, pairs)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Curves> Dkg<C> {
|
||||||
/// Check if a batch of `Participation`s are valid.
|
/// Check if a batch of `Participation`s are valid.
|
||||||
///
|
///
|
||||||
/// If any `Participation` is invalid, the list of all invalid participants will be returned.
|
/// If any `Participation` is invalid, the list of all invalid participants will be returned.
|
||||||
@@ -336,22 +307,24 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
/// participate.
|
/// participate.
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
generators: &EvrfGenerators<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 Ciphersuite>::G],
|
||||||
participations: &HashMap<Participant, Participation<C>>,
|
participations: &HashMap<Participant, Participation<C::ToweringCurve>>,
|
||||||
) -> Result<VerifyResult<C>, EvrfError> {
|
) -> Result<VerifyResult<C>, Error> {
|
||||||
let Ok(n) = u16::try_from(evrf_public_keys.len()) else { Err(EvrfError::TooManyParticipants)? };
|
let Ok(n) = u16::try_from(evrf_public_keys.len()) else {
|
||||||
|
Err(Error::TooManyParticipants { provided: evrf_public_keys.len() })?
|
||||||
|
};
|
||||||
if (t == 0) || (t > n) {
|
if (t == 0) || (t > n) {
|
||||||
Err(EvrfError::InvalidThreshold)?;
|
Err(Error::InvalidThreshold { t, n })?;
|
||||||
}
|
}
|
||||||
if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) {
|
if evrf_public_keys.iter().any(|key| bool::from(key.is_identity())) {
|
||||||
Err(EvrfError::PublicKeyWasIdentity)?;
|
Err(Error::PublicKeyWasIdentity)?;
|
||||||
};
|
};
|
||||||
for i in participations.keys() {
|
for i in participations.keys() {
|
||||||
if u16::from(*i) > n {
|
if u16::from(*i) > n {
|
||||||
Err(EvrfError::NonExistentParticipant)?;
|
Err(Error::NonExistentParticipant)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +333,7 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
|
|
||||||
let transcript = Self::initial_transcript(context, evrf_public_keys, t);
|
let transcript = Self::initial_transcript(context, evrf_public_keys, t);
|
||||||
|
|
||||||
let mut evrf_verifier = Generators::batch_verifier();
|
let mut evrf_verifier = generalized_bulletproofs::Generators::batch_verifier();
|
||||||
for (i, participation) in participations {
|
for (i, participation) in participations {
|
||||||
let evrf_public_key = evrf_public_keys[usize::from(u16::from(*i)) - 1];
|
let evrf_public_key = evrf_public_keys[usize::from(u16::from(*i)) - 1];
|
||||||
|
|
||||||
@@ -370,7 +343,7 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
|
|
||||||
// Clone the verifier so if this proof is faulty, it doesn't corrupt the verifier
|
// Clone the verifier so if this proof is faulty, it doesn't corrupt the verifier
|
||||||
let mut verifier_clone = evrf_verifier.clone();
|
let mut verifier_clone = evrf_verifier.clone();
|
||||||
let Ok(data) = Evrf::<C>::verify(
|
let Ok(data) = Proof::<C>::verify(
|
||||||
rng,
|
rng,
|
||||||
&generators.0,
|
&generators.0,
|
||||||
&mut verifier_clone,
|
&mut verifier_clone,
|
||||||
@@ -396,8 +369,8 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
if faulty.contains(i) {
|
if faulty.contains(i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut evrf_verifier = Generators::batch_verifier();
|
let mut evrf_verifier = generalized_bulletproofs::Generators::batch_verifier();
|
||||||
Evrf::<C>::verify(
|
Proof::<C>::verify(
|
||||||
rng,
|
rng,
|
||||||
&generators.0,
|
&generators.0,
|
||||||
&mut evrf_verifier,
|
&mut evrf_verifier,
|
||||||
@@ -423,17 +396,13 @@ impl<C: EvrfCurve> EvrfDkg<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::F::ZERO;
|
let mut g_scalar = <C::ToweringCurve as Ciphersuite>::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) = share_verification_statements::<C>(
|
let (this_g_scalar, mut these_pairs) = verifiable_encryption_statements::<C>(
|
||||||
&mut *rng,
|
&mut *rng,
|
||||||
&data.coefficients,
|
&data.coefficients,
|
||||||
evrf_public_keys
|
&data.encryption_key_commitments,
|
||||||
.len()
|
|
||||||
.try_into()
|
|
||||||
.expect("n prior checked to be <= u16::MAX couldn't be converted to a u16"),
|
|
||||||
&data.encryption_commitments,
|
|
||||||
encrypted_secret_shares,
|
encrypted_secret_shares,
|
||||||
);
|
);
|
||||||
// Queue this into our batch
|
// Queue this into our batch
|
||||||
@@ -452,18 +421,22 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
We calculcate verification shares as the sum of the encrypted scalars, minus their
|
We calculcate verification shares as the sum of the encrypted scalars, minus their
|
||||||
masks. This only does one scalar multiplication, and `1+t` point additions (with
|
masks. This only does one scalar multiplication, and `1+t` point additions (with
|
||||||
one negation), and is accordingly much cheaper than interpolating the commitments.
|
one negation), and is accordingly much cheaper than interpolating the commitments.
|
||||||
This is only possible because already interpolated the commitments to verify the
|
This is only possible because we already interpolated the commitments to verify the
|
||||||
encrypted secret share.
|
encrypted secret share.
|
||||||
*/
|
*/
|
||||||
let sum_encrypted_secret_share =
|
let sum_encrypted_secret_share = sum_encrypted_secret_shares
|
||||||
sum_encrypted_secret_shares.get(j).copied().unwrap_or(C::F::ZERO);
|
.get(j)
|
||||||
let sum_mask = sum_masks.get(j).copied().unwrap_or(C::G::identity());
|
.copied()
|
||||||
|
.unwrap_or(<C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
|
let sum_mask =
|
||||||
|
sum_masks.get(j).copied().unwrap_or(<C::ToweringCurve as Ciphersuite>::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;
|
||||||
sum_masks.insert(*j, sum_mask + data.encryption_commitments[j_index]);
|
sum_masks.insert(*j, sum_mask + data.encryption_key_commitments[j_index]);
|
||||||
|
|
||||||
formatted_encrypted_secret_shares.insert(*j, (data.ecdh_keys[j_index], *enc_share));
|
formatted_encrypted_secret_shares
|
||||||
|
.insert(*j, (data.ecdh_commitments[j_index], *enc_share));
|
||||||
}
|
}
|
||||||
all_encrypted_secret_shares.insert(*i, formatted_encrypted_secret_shares);
|
all_encrypted_secret_shares.insert(*i, formatted_encrypted_secret_shares);
|
||||||
}
|
}
|
||||||
@@ -517,69 +490,74 @@ impl<C: EvrfCurve> EvrfDkg<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we now have >= t participations, calculate the group key and verification shares
|
// If we now have >= t participations, output the result
|
||||||
|
|
||||||
// The group key is the sum of the zero coefficients
|
|
||||||
let group_key = valid.values().map(|(_, evrf_data)| evrf_data.coefficients[0]).sum::<C::G>();
|
|
||||||
|
|
||||||
// Calculate each user's verification share
|
// Calculate each user's verification share
|
||||||
let mut verification_shares = HashMap::with_capacity(usize::from(n));
|
let mut verification_shares = HashMap::with_capacity(usize::from(n));
|
||||||
for i in (1 ..= n).map(Participant) {
|
for i in Participant::iter().take(usize::from(n)) {
|
||||||
verification_shares
|
verification_shares.insert(
|
||||||
.insert(i, (C::generator() * sum_encrypted_secret_shares[&i]) - sum_masks[&i]);
|
i,
|
||||||
|
(<C::ToweringCurve as Ciphersuite>::generator() * sum_encrypted_secret_shares[&i]) -
|
||||||
|
sum_masks[&i],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(VerifyResult::Valid(EvrfDkg {
|
Ok(VerifyResult::Valid(Dkg {
|
||||||
t,
|
t,
|
||||||
n,
|
n,
|
||||||
evrf_public_keys: evrf_public_keys.to_vec(),
|
evrf_public_keys: evrf_public_keys.to_vec(),
|
||||||
group_key,
|
|
||||||
verification_shares,
|
verification_shares,
|
||||||
encrypted_secret_shares: all_encrypted_secret_shares,
|
encrypted_secret_shares: all_encrypted_secret_shares,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve keys from a successful DKG.
|
||||||
|
///
|
||||||
|
/// 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 Ciphersuite>::F>,
|
||||||
) -> Vec<ThresholdKeys<C>> {
|
) -> Vec<ThresholdKeys<C::ToweringCurve>> {
|
||||||
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
let evrf_public_key = <C::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref();
|
||||||
let mut is = Vec::with_capacity(1);
|
let mut is = Vec::with_capacity(1);
|
||||||
for (i, evrf_key) in self.evrf_public_keys.iter().enumerate() {
|
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 {
|
||||||
let i = u16::try_from(i).expect("n <= u16::MAX yet i > u16::MAX?");
|
|
||||||
let i = Participant(1 + i);
|
|
||||||
is.push(i);
|
is.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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::F::ZERO);
|
let mut secret_share = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
for shares in self.encrypted_secret_shares.values() {
|
for shares in self.encrypted_secret_shares.values() {
|
||||||
let (ecdh_keys, enc_share) = shares[&i];
|
let (ecdh_commitments, encrypted_secret_share) = shares[&i];
|
||||||
|
|
||||||
let mut ecdh = Zeroizing::new(C::F::ZERO);
|
let mut ecdh = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
for point in ecdh_keys {
|
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 Ciphersuite>::G::to_xy(point * evrf_private_key.deref()).unwrap();
|
||||||
*ecdh += x;
|
*ecdh += x;
|
||||||
x.zeroize();
|
x.zeroize();
|
||||||
y.zeroize();
|
y.zeroize();
|
||||||
}
|
}
|
||||||
*secret_share += enc_share - ecdh.deref();
|
*secret_share += encrypted_secret_share - ecdh.deref();
|
||||||
}
|
}
|
||||||
|
debug_assert_eq!(
|
||||||
|
self.verification_shares[&i],
|
||||||
|
<C::ToweringCurve as Ciphersuite>::G::generator() * secret_share.deref()
|
||||||
|
);
|
||||||
|
|
||||||
debug_assert_eq!(self.verification_shares[&i], C::generator() * secret_share.deref());
|
res.push(
|
||||||
|
ThresholdKeys::new(
|
||||||
res.push(ThresholdKeys::from(ThresholdCore {
|
ThresholdParams::new(self.t, self.n, i).unwrap(),
|
||||||
params: ThresholdParams::new(self.t, self.n, i).unwrap(),
|
Interpolation::Lagrange,
|
||||||
interpolation: Interpolation::Lagrange,
|
|
||||||
secret_share,
|
secret_share,
|
||||||
group_key: self.group_key,
|
self.verification_shares.clone(),
|
||||||
verification_shares: self.verification_shares.clone(),
|
)
|
||||||
}));
|
.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,690 +0,0 @@
|
|||||||
use core::{marker::PhantomData, ops::Deref, fmt};
|
|
||||||
|
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
|
||||||
use rand_chacha::ChaCha20Rng;
|
|
||||||
|
|
||||||
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
|
|
||||||
|
|
||||||
use blake2::{Digest, Blake2s256};
|
|
||||||
use ciphersuite::{
|
|
||||||
group::{ff::Field, Group, GroupEncoding},
|
|
||||||
Ciphersuite,
|
|
||||||
};
|
|
||||||
|
|
||||||
use generalized_bulletproofs::{
|
|
||||||
*,
|
|
||||||
transcript::{Transcript as ProverTranscript, VerifierTranscript},
|
|
||||||
arithmetic_circuit_proof::*,
|
|
||||||
};
|
|
||||||
use generalized_bulletproofs_circuit_abstraction::*;
|
|
||||||
|
|
||||||
use ec_divisors::{DivisorCurve, ScalarDecomposition};
|
|
||||||
use generalized_bulletproofs_ec_gadgets::*;
|
|
||||||
|
|
||||||
/// A pair of curves to perform the eVRF with.
|
|
||||||
pub trait EvrfCurve: Ciphersuite {
|
|
||||||
type EmbeddedCurve: Ciphersuite<G: DivisorCurve<FieldElement = <Self as Ciphersuite>::F>>;
|
|
||||||
type EmbeddedCurveParameters: DiscreteLogParameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "evrf-secp256k1")]
|
|
||||||
impl EvrfCurve for ciphersuite::Secp256k1 {
|
|
||||||
type EmbeddedCurve = secq256k1::Secq256k1;
|
|
||||||
type EmbeddedCurveParameters = secq256k1::Secq256k1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "evrf-ed25519")]
|
|
||||||
impl EvrfCurve for ciphersuite::Ed25519 {
|
|
||||||
type EmbeddedCurve = embedwards25519::Embedwards25519;
|
|
||||||
type EmbeddedCurveParameters = embedwards25519::Embedwards25519;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "evrf-ristretto")]
|
|
||||||
impl EvrfCurve for ciphersuite::Ristretto {
|
|
||||||
type EmbeddedCurve = embedwards25519::Embedwards25519;
|
|
||||||
type EmbeddedCurveParameters = embedwards25519::Embedwards25519;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_point<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
|
|
||||||
let mut repr = <C::G as GroupEncoding>::Repr::default();
|
|
||||||
loop {
|
|
||||||
rng.fill_bytes(repr.as_mut());
|
|
||||||
if let Ok(point) = C::read_G(&mut repr.as_ref()) {
|
|
||||||
if bool::from(!point.is_identity()) {
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generators for eVRF proof.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct EvrfGenerators<C: EvrfCurve>(pub(crate) Generators<C>);
|
|
||||||
|
|
||||||
impl<C: EvrfCurve> EvrfGenerators<C> {
|
|
||||||
/// Create a new set of generators.
|
|
||||||
pub fn new(max_threshold: u16, max_participants: u16) -> EvrfGenerators<C> {
|
|
||||||
let g = C::generator();
|
|
||||||
let mut rng = ChaCha20Rng::from_seed(Blake2s256::digest(g.to_bytes()).into());
|
|
||||||
let h = sample_point::<C>(&mut rng);
|
|
||||||
let (_, generators) =
|
|
||||||
Evrf::<C>::muls_and_generators_to_use(max_threshold.into(), max_participants.into());
|
|
||||||
let mut g_bold = vec![];
|
|
||||||
let mut h_bold = vec![];
|
|
||||||
for _ in 0 .. generators {
|
|
||||||
g_bold.push(sample_point::<C>(&mut rng));
|
|
||||||
h_bold.push(sample_point::<C>(&mut rng));
|
|
||||||
}
|
|
||||||
Self(Generators::new(g, h, g_bold, h_bold).unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The result of proving for an eVRF.
|
|
||||||
pub(crate) struct EvrfProveResult<C: Ciphersuite> {
|
|
||||||
/// The coefficients for use in the DKG.
|
|
||||||
pub(crate) coefficients: Vec<Zeroizing<C::F>>,
|
|
||||||
/// The masks to encrypt secret shares with.
|
|
||||||
pub(crate) encryption_masks: Vec<Zeroizing<C::F>>,
|
|
||||||
/// The proof itself.
|
|
||||||
pub(crate) proof: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The result of verifying an eVRF.
|
|
||||||
pub(crate) struct EvrfVerifyResult<C: EvrfCurve> {
|
|
||||||
/// The commitments to the coefficients for use in the DKG.
|
|
||||||
pub(crate) coefficients: Vec<C::G>,
|
|
||||||
/// The ephemeral public keys to perform ECDHs with
|
|
||||||
pub(crate) ecdh_keys: Vec<[<C::EmbeddedCurve as Ciphersuite>::G; 2]>,
|
|
||||||
/// The commitments to the masks used to encrypt secret shares with.
|
|
||||||
pub(crate) encryption_commitments: Vec<C::G>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: EvrfCurve> fmt::Debug for EvrfVerifyResult<C> {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
fmt.debug_struct("EvrfVerifyResult").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A struct to prove/verify eVRFs with.
|
|
||||||
pub(crate) struct Evrf<C: EvrfCurve>(PhantomData<C>);
|
|
||||||
impl<C: EvrfCurve> Evrf<C> {
|
|
||||||
// Sample uniform points (via rejection-sampling) on the embedded elliptic curve
|
|
||||||
fn transcript_to_points(
|
|
||||||
seed: [u8; 32],
|
|
||||||
coefficients: usize,
|
|
||||||
) -> Vec<<C::EmbeddedCurve as Ciphersuite>::G> {
|
|
||||||
// We need to do two Diffie-Hellman's per coefficient in order to achieve an unbiased result
|
|
||||||
let quantity = 2 * coefficients;
|
|
||||||
|
|
||||||
let mut rng = ChaCha20Rng::from_seed(seed);
|
|
||||||
let mut res = Vec::with_capacity(quantity);
|
|
||||||
for _ in 0 .. quantity {
|
|
||||||
res.push(sample_point::<C::EmbeddedCurve>(&mut rng));
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a Variable from a theoretical vector commitment tape
|
|
||||||
fn read_one_from_tape(generators_to_use: usize, start: &mut usize) -> Variable {
|
|
||||||
// Each commitment has twice as many variables as generators in use
|
|
||||||
let commitment = *start / generators_to_use;
|
|
||||||
// The index will be less than the amount of generators in use, as half are left and half are
|
|
||||||
// right
|
|
||||||
let index = *start % generators_to_use;
|
|
||||||
let res = Variable::CG { commitment, index };
|
|
||||||
*start += 1;
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a set of variables from a theoretical vector commitment tape
|
|
||||||
fn read_from_tape<N: ArrayLength>(
|
|
||||||
generators_to_use: usize,
|
|
||||||
start: &mut usize,
|
|
||||||
) -> GenericArray<Variable, N> {
|
|
||||||
let mut buf = Vec::with_capacity(N::USIZE);
|
|
||||||
for _ in 0 .. N::USIZE {
|
|
||||||
buf.push(Self::read_one_from_tape(generators_to_use, start));
|
|
||||||
}
|
|
||||||
GenericArray::from_slice(&buf).clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read `PointWithDlog`s, which share a discrete logarithm, from the theoretical vector
|
|
||||||
/// commitment tape.
|
|
||||||
fn point_with_dlogs(
|
|
||||||
start: &mut usize,
|
|
||||||
quantity: usize,
|
|
||||||
generators_to_use: usize,
|
|
||||||
) -> Vec<PointWithDlog<C::EmbeddedCurveParameters>> {
|
|
||||||
// We define a serialized tape of the discrete logarithm, then for each divisor/point, we push:
|
|
||||||
// zero, x**i, y x**i, y, x_coord, y_coord
|
|
||||||
// We then chunk that into vector commitments
|
|
||||||
// Here, we take the assumed layout and generate the expected `Variable`s for this layout
|
|
||||||
|
|
||||||
let dlog = Self::read_from_tape(generators_to_use, start);
|
|
||||||
|
|
||||||
let mut res = Vec::with_capacity(quantity);
|
|
||||||
let mut read_point_with_dlog = || {
|
|
||||||
let zero = Self::read_one_from_tape(generators_to_use, start);
|
|
||||||
let x_from_power_of_2 = Self::read_from_tape(generators_to_use, start);
|
|
||||||
let yx = Self::read_from_tape(generators_to_use, start);
|
|
||||||
let y = Self::read_one_from_tape(generators_to_use, start);
|
|
||||||
let divisor = Divisor { zero, x_from_power_of_2, yx, y };
|
|
||||||
|
|
||||||
let point = (
|
|
||||||
Self::read_one_from_tape(generators_to_use, start),
|
|
||||||
Self::read_one_from_tape(generators_to_use, start),
|
|
||||||
);
|
|
||||||
|
|
||||||
res.push(PointWithDlog { dlog: dlog.clone(), divisor, point });
|
|
||||||
};
|
|
||||||
|
|
||||||
for _ in 0 .. quantity {
|
|
||||||
read_point_with_dlog();
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn muls_and_generators_to_use(coefficients: usize, ecdhs: usize) -> (usize, usize) {
|
|
||||||
const MULS_PER_DH: usize = 7;
|
|
||||||
// 1 DH to prove the discrete logarithm corresponds to the eVRF public key
|
|
||||||
// 2 DHs per generated coefficient
|
|
||||||
// 2 DHs per generated ECDH
|
|
||||||
let expected_muls = MULS_PER_DH * (1 + (2 * coefficients) + (2 * 2 * ecdhs));
|
|
||||||
let generators_to_use = {
|
|
||||||
let mut padded_pow_of_2 = 1;
|
|
||||||
while padded_pow_of_2 < expected_muls {
|
|
||||||
padded_pow_of_2 <<= 1;
|
|
||||||
}
|
|
||||||
// This may as small as 16, which would create an excessive amount of vector commitments
|
|
||||||
// We set a floor of 2048 rows for bandwidth reasons
|
|
||||||
padded_pow_of_2.max(2048)
|
|
||||||
};
|
|
||||||
(expected_muls, generators_to_use)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn circuit(
|
|
||||||
curve_spec: &CurveSpec<C::F>,
|
|
||||||
evrf_public_key: (C::F, C::F),
|
|
||||||
coefficients: usize,
|
|
||||||
ecdh_commitments: &[[(C::F, C::F); 2]],
|
|
||||||
generator_tables: &[&GeneratorTable<C::F, C::EmbeddedCurveParameters>],
|
|
||||||
circuit: &mut Circuit<C>,
|
|
||||||
transcript: &mut impl Transcript,
|
|
||||||
) {
|
|
||||||
let (expected_muls, generators_to_use) =
|
|
||||||
Self::muls_and_generators_to_use(coefficients, ecdh_commitments.len());
|
|
||||||
let (challenge, challenged_generators) =
|
|
||||||
circuit.discrete_log_challenge(transcript, curve_spec, generator_tables);
|
|
||||||
debug_assert_eq!(challenged_generators.len(), 1 + (2 * coefficients) + ecdh_commitments.len());
|
|
||||||
|
|
||||||
// The generators tables/challenged generators are expected to have the following layouts
|
|
||||||
// G, coefficients * [A, B], ecdhs * [P]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let challenged_G = &challenged_generators[0];
|
|
||||||
|
|
||||||
// Execute the circuit for the coefficients
|
|
||||||
let mut tape_pos = 0;
|
|
||||||
{
|
|
||||||
let mut point_with_dlogs =
|
|
||||||
Self::point_with_dlogs(&mut tape_pos, 1 + (2 * coefficients), generators_to_use)
|
|
||||||
.into_iter();
|
|
||||||
|
|
||||||
// Verify the discrete logarithm is in the fact the discrete logarithm of the eVRF public key
|
|
||||||
let point = circuit.discrete_log(
|
|
||||||
curve_spec,
|
|
||||||
point_with_dlogs.next().unwrap(),
|
|
||||||
&challenge,
|
|
||||||
challenged_G,
|
|
||||||
);
|
|
||||||
circuit.equality(LinComb::from(point.x()), &LinComb::empty().constant(evrf_public_key.0));
|
|
||||||
circuit.equality(LinComb::from(point.y()), &LinComb::empty().constant(evrf_public_key.1));
|
|
||||||
|
|
||||||
// Verify the DLog claims against the sampled points
|
|
||||||
for (i, pair) in challenged_generators[1 ..].chunks(2).take(coefficients).enumerate() {
|
|
||||||
let mut lincomb = LinComb::empty();
|
|
||||||
debug_assert_eq!(pair.len(), 2);
|
|
||||||
for challenged_generator in pair {
|
|
||||||
let point = circuit.discrete_log(
|
|
||||||
curve_spec,
|
|
||||||
point_with_dlogs.next().unwrap(),
|
|
||||||
&challenge,
|
|
||||||
challenged_generator,
|
|
||||||
);
|
|
||||||
// For each point in this pair, add its x coordinate to a lincomb
|
|
||||||
lincomb = lincomb.term(C::F::ONE, point.x());
|
|
||||||
}
|
|
||||||
// Constrain the sum of the two x coordinates to be equal to the value in the Pedersen
|
|
||||||
// commitment
|
|
||||||
circuit.equality(lincomb, &LinComb::from(Variable::V(i)));
|
|
||||||
}
|
|
||||||
debug_assert!(point_with_dlogs.next().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now execute the circuit for the ECDHs
|
|
||||||
let mut challenged_generators = challenged_generators.iter().skip(1 + (2 * coefficients));
|
|
||||||
for (i, ecdh) in ecdh_commitments.iter().enumerate() {
|
|
||||||
let challenged_generator = challenged_generators.next().unwrap();
|
|
||||||
let mut lincomb = LinComb::empty();
|
|
||||||
for ecdh in ecdh {
|
|
||||||
let mut point_with_dlogs =
|
|
||||||
Self::point_with_dlogs(&mut tape_pos, 2, generators_to_use).into_iter();
|
|
||||||
|
|
||||||
// One proof of the ECDH secret * G for the commitment published
|
|
||||||
let point = circuit.discrete_log(
|
|
||||||
curve_spec,
|
|
||||||
point_with_dlogs.next().unwrap(),
|
|
||||||
&challenge,
|
|
||||||
challenged_G,
|
|
||||||
);
|
|
||||||
circuit.equality(LinComb::from(point.x()), &LinComb::empty().constant(ecdh.0));
|
|
||||||
circuit.equality(LinComb::from(point.y()), &LinComb::empty().constant(ecdh.1));
|
|
||||||
|
|
||||||
// One proof of the ECDH secret * P for the ECDH
|
|
||||||
let point = circuit.discrete_log(
|
|
||||||
curve_spec,
|
|
||||||
point_with_dlogs.next().unwrap(),
|
|
||||||
&challenge,
|
|
||||||
challenged_generator,
|
|
||||||
);
|
|
||||||
// For each point in this pair, add its x coordinate to a lincomb
|
|
||||||
lincomb = lincomb.term(C::F::ONE, point.x());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constrain the sum of the two x coordinates to be equal to the value in the Pedersen
|
|
||||||
// commitment
|
|
||||||
circuit.equality(lincomb, &LinComb::from(Variable::V(coefficients + i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert_eq!(expected_muls, circuit.muls());
|
|
||||||
debug_assert!(challenged_generators.next().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prove a point on an elliptic curve had its discrete logarithm generated via an eVRF.
|
|
||||||
pub(crate) fn prove(
|
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
|
||||||
generators: &Generators<C>,
|
|
||||||
transcript: [u8; 32],
|
|
||||||
coefficients: usize,
|
|
||||||
ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G],
|
|
||||||
evrf_private_key: &Zeroizing<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
|
|
||||||
) -> Result<EvrfProveResult<C>, AcError> {
|
|
||||||
let curve_spec = CurveSpec {
|
|
||||||
a: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::a(),
|
|
||||||
b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// A tape of the discrete logarithm, then [zero, x**i, y x**i, y, x_coord, y_coord]
|
|
||||||
let mut vector_commitment_tape = vec![];
|
|
||||||
|
|
||||||
let mut generator_tables = Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
|
|
||||||
|
|
||||||
// A function to calculate a divisor and push it onto the tape
|
|
||||||
// This defines a vec, divisor_points, outside of the fn to reuse its allocation
|
|
||||||
let mut divisor =
|
|
||||||
|vector_commitment_tape: &mut Vec<_>,
|
|
||||||
dlog: &ScalarDecomposition<<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::F>,
|
|
||||||
push_generator: bool,
|
|
||||||
generator: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G,
|
|
||||||
dh: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G| {
|
|
||||||
if push_generator {
|
|
||||||
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(generator).unwrap();
|
|
||||||
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut divisor = dlog.scalar_mul_divisor(generator).normalize_x_coefficient();
|
|
||||||
|
|
||||||
vector_commitment_tape.push(divisor.zero_coefficient);
|
|
||||||
|
|
||||||
for coefficient in divisor.x_coefficients.iter().skip(1) {
|
|
||||||
vector_commitment_tape.push(*coefficient);
|
|
||||||
}
|
|
||||||
for _ in divisor.x_coefficients.len() ..
|
|
||||||
<C::EmbeddedCurveParameters as DiscreteLogParameters>::XCoefficientsMinusOne::USIZE
|
|
||||||
{
|
|
||||||
vector_commitment_tape.push(<C as Ciphersuite>::F::ZERO);
|
|
||||||
}
|
|
||||||
|
|
||||||
for coefficient in divisor.yx_coefficients.first().unwrap_or(&vec![]) {
|
|
||||||
vector_commitment_tape.push(*coefficient);
|
|
||||||
}
|
|
||||||
for _ in divisor.yx_coefficients.first().unwrap_or(&vec![]).len() ..
|
|
||||||
<C::EmbeddedCurveParameters as DiscreteLogParameters>::YxCoefficients::USIZE
|
|
||||||
{
|
|
||||||
vector_commitment_tape.push(<C as Ciphersuite>::F::ZERO);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_commitment_tape
|
|
||||||
.push(divisor.y_coefficients.first().copied().unwrap_or(<C as Ciphersuite>::F::ZERO));
|
|
||||||
|
|
||||||
divisor.zeroize();
|
|
||||||
drop(divisor);
|
|
||||||
|
|
||||||
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(dh).unwrap();
|
|
||||||
vector_commitment_tape.push(x);
|
|
||||||
vector_commitment_tape.push(y);
|
|
||||||
|
|
||||||
(x, y)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Start with the coefficients
|
|
||||||
let evrf_public_key;
|
|
||||||
let mut actual_coefficients = Vec::with_capacity(coefficients);
|
|
||||||
{
|
|
||||||
// This is checked at a higher level
|
|
||||||
let dlog =
|
|
||||||
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(**evrf_private_key)
|
|
||||||
.expect("eVRF private key was zero");
|
|
||||||
let points = Self::transcript_to_points(transcript, coefficients);
|
|
||||||
|
|
||||||
// Start by pushing the discrete logarithm onto the tape
|
|
||||||
for coefficient in dlog.decomposition() {
|
|
||||||
vector_commitment_tape.push(<_>::from(*coefficient));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push a divisor for proving that we're using the correct scalar
|
|
||||||
evrf_public_key = divisor(
|
|
||||||
&mut vector_commitment_tape,
|
|
||||||
&dlog,
|
|
||||||
true,
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator(),
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * evrf_private_key.deref(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Push a divisor for each point we use in the eVRF
|
|
||||||
for pair in points.chunks(2) {
|
|
||||||
let mut res = Zeroizing::new(C::F::ZERO);
|
|
||||||
for point in pair {
|
|
||||||
let (dh_x, _) = divisor(
|
|
||||||
&mut vector_commitment_tape,
|
|
||||||
&dlog,
|
|
||||||
true,
|
|
||||||
*point,
|
|
||||||
*point * evrf_private_key.deref(),
|
|
||||||
);
|
|
||||||
*res += dh_x;
|
|
||||||
}
|
|
||||||
actual_coefficients.push(res);
|
|
||||||
}
|
|
||||||
debug_assert_eq!(actual_coefficients.len(), coefficients);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now do the ECDHs for the encryption
|
|
||||||
let mut encryption_masks = Vec::with_capacity(ecdh_public_keys.len());
|
|
||||||
let mut ecdh_commitments = Vec::with_capacity(2 * ecdh_public_keys.len());
|
|
||||||
let mut ecdh_commitments_xy = Vec::with_capacity(ecdh_public_keys.len());
|
|
||||||
for ecdh_public_key in ecdh_public_keys {
|
|
||||||
ecdh_commitments_xy.push([(C::F::ZERO, C::F::ZERO); 2]);
|
|
||||||
|
|
||||||
let mut res = Zeroizing::new(C::F::ZERO);
|
|
||||||
for j in 0 .. 2 {
|
|
||||||
let mut ecdh_private_key;
|
|
||||||
loop {
|
|
||||||
ecdh_private_key = <C::EmbeddedCurve as Ciphersuite>::F::random(&mut *rng);
|
|
||||||
// Generate a non-0 ECDH private key, as necessary to not produce an identity output
|
|
||||||
// Identity isn't representable with the divisors, hence the explicit effort
|
|
||||||
if bool::from(!ecdh_private_key.is_zero()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let dlog =
|
|
||||||
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(ecdh_private_key)
|
|
||||||
.expect("ECDH private key was zero");
|
|
||||||
let ecdh_commitment = <C::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key;
|
|
||||||
ecdh_commitments.push(ecdh_commitment);
|
|
||||||
ecdh_commitments_xy.last_mut().unwrap()[j] =
|
|
||||||
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitment).unwrap();
|
|
||||||
|
|
||||||
// Start by pushing the discrete logarithm onto the tape
|
|
||||||
for coefficient in dlog.decomposition() {
|
|
||||||
vector_commitment_tape.push(<_>::from(*coefficient));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push a divisor for proving that we're using the correct scalar for the commitment
|
|
||||||
divisor(
|
|
||||||
&mut vector_commitment_tape,
|
|
||||||
&dlog,
|
|
||||||
false,
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator(),
|
|
||||||
<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::generator() * ecdh_private_key,
|
|
||||||
);
|
|
||||||
// Push a divisor for the key we're performing the ECDH with
|
|
||||||
let (dh_x, _) = divisor(
|
|
||||||
&mut vector_commitment_tape,
|
|
||||||
&dlog,
|
|
||||||
j == 0,
|
|
||||||
*ecdh_public_key,
|
|
||||||
*ecdh_public_key * ecdh_private_key,
|
|
||||||
);
|
|
||||||
*res += dh_x;
|
|
||||||
|
|
||||||
ecdh_private_key.zeroize();
|
|
||||||
}
|
|
||||||
encryption_masks.push(res);
|
|
||||||
}
|
|
||||||
debug_assert_eq!(encryption_masks.len(), ecdh_public_keys.len());
|
|
||||||
|
|
||||||
// Now that we have the vector commitment tape, chunk it
|
|
||||||
let (_, generators_to_use) =
|
|
||||||
Self::muls_and_generators_to_use(coefficients, ecdh_public_keys.len());
|
|
||||||
|
|
||||||
let mut vector_commitments =
|
|
||||||
Vec::with_capacity(vector_commitment_tape.len().div_ceil(generators_to_use));
|
|
||||||
for chunk in vector_commitment_tape.chunks(generators_to_use) {
|
|
||||||
let g_values = chunk[.. generators_to_use.min(chunk.len())].to_vec().into();
|
|
||||||
vector_commitments.push(PedersenVectorCommitment { g_values, mask: C::F::random(&mut *rng) });
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_commitment_tape.zeroize();
|
|
||||||
drop(vector_commitment_tape);
|
|
||||||
|
|
||||||
let mut commitments = Vec::with_capacity(coefficients + ecdh_public_keys.len());
|
|
||||||
for coefficient in &actual_coefficients {
|
|
||||||
commitments.push(PedersenCommitment { value: **coefficient, mask: C::F::random(&mut *rng) });
|
|
||||||
}
|
|
||||||
for enc_mask in &encryption_masks {
|
|
||||||
commitments.push(PedersenCommitment { value: **enc_mask, mask: C::F::random(&mut *rng) });
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut transcript = ProverTranscript::new(transcript);
|
|
||||||
let commited_commitments = transcript.write_commitments(
|
|
||||||
vector_commitments
|
|
||||||
.iter()
|
|
||||||
.map(|commitment| {
|
|
||||||
commitment
|
|
||||||
.commit(generators.g_bold_slice(), generators.h())
|
|
||||||
.ok_or(AcError::NotEnoughGenerators)
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?,
|
|
||||||
commitments
|
|
||||||
.iter()
|
|
||||||
.map(|commitment| commitment.commit(generators.g(), generators.h()))
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
for ecdh_commitment in ecdh_commitments {
|
|
||||||
transcript.push_point(ecdh_commitment);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut circuit = Circuit::prove(vector_commitments, commitments.clone());
|
|
||||||
Self::circuit(
|
|
||||||
&curve_spec,
|
|
||||||
evrf_public_key,
|
|
||||||
coefficients,
|
|
||||||
&ecdh_commitments_xy,
|
|
||||||
&generator_tables.iter().collect::<Vec<_>>(),
|
|
||||||
&mut circuit,
|
|
||||||
&mut transcript,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (statement, Some(witness)) = circuit
|
|
||||||
.statement(
|
|
||||||
generators.reduce(generators_to_use).ok_or(AcError::NotEnoughGenerators)?,
|
|
||||||
commited_commitments,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
else {
|
|
||||||
panic!("proving yet wasn't yielded the witness");
|
|
||||||
};
|
|
||||||
statement.prove(&mut *rng, &mut transcript, witness).unwrap();
|
|
||||||
|
|
||||||
// Push the reveal onto the transcript
|
|
||||||
for commitment in &commitments {
|
|
||||||
transcript.push_point(generators.g() * commitment.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define a weight to aggregate the commitments with
|
|
||||||
let mut agg_weights = Vec::with_capacity(commitments.len());
|
|
||||||
agg_weights.push(C::F::ONE);
|
|
||||||
while agg_weights.len() < commitments.len() {
|
|
||||||
agg_weights.push(transcript.challenge::<C>());
|
|
||||||
}
|
|
||||||
let mut x = commitments
|
|
||||||
.iter()
|
|
||||||
.zip(&agg_weights)
|
|
||||||
.map(|(commitment, weight)| commitment.mask * *weight)
|
|
||||||
.sum::<C::F>();
|
|
||||||
|
|
||||||
// Do a Schnorr PoK for the randomness of the aggregated Pedersen commitment
|
|
||||||
let mut r = C::F::random(&mut *rng);
|
|
||||||
transcript.push_point(generators.h() * r);
|
|
||||||
let c = transcript.challenge::<C>();
|
|
||||||
transcript.push_scalar(r + (c * x));
|
|
||||||
r.zeroize();
|
|
||||||
x.zeroize();
|
|
||||||
|
|
||||||
Ok(EvrfProveResult {
|
|
||||||
coefficients: actual_coefficients,
|
|
||||||
encryption_masks,
|
|
||||||
proof: transcript.complete(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify an eVRF proof, returning the commitments output.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub(crate) fn verify(
|
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
|
||||||
generators: &Generators<C>,
|
|
||||||
verifier: &mut BatchVerifier<C>,
|
|
||||||
transcript: [u8; 32],
|
|
||||||
coefficients: usize,
|
|
||||||
ecdh_public_keys: &[<<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G],
|
|
||||||
evrf_public_key: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G,
|
|
||||||
proof: &[u8],
|
|
||||||
) -> Result<EvrfVerifyResult<C>, ()> {
|
|
||||||
let curve_spec = CurveSpec {
|
|
||||||
a: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::a(),
|
|
||||||
b: <<C as EvrfCurve>::EmbeddedCurve as Ciphersuite>::G::b(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut generator_tables = Vec::with_capacity(1 + (2 * coefficients) + ecdh_public_keys.len());
|
|
||||||
{
|
|
||||||
let (x, y) =
|
|
||||||
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(<C::EmbeddedCurve as Ciphersuite>::generator())
|
|
||||||
.unwrap();
|
|
||||||
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
|
|
||||||
}
|
|
||||||
let points = Self::transcript_to_points(transcript, coefficients);
|
|
||||||
for generator in points {
|
|
||||||
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(generator).unwrap();
|
|
||||||
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
|
|
||||||
}
|
|
||||||
for generator in ecdh_public_keys {
|
|
||||||
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(*generator).unwrap();
|
|
||||||
generator_tables.push(GeneratorTable::new(&curve_spec, x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (_, generators_to_use) =
|
|
||||||
Self::muls_and_generators_to_use(coefficients, ecdh_public_keys.len());
|
|
||||||
|
|
||||||
let mut transcript = VerifierTranscript::new(transcript, proof);
|
|
||||||
|
|
||||||
let dlog_len = <C::EmbeddedCurveParameters as DiscreteLogParameters>::ScalarBits::USIZE;
|
|
||||||
let divisor_len = 1 +
|
|
||||||
<C::EmbeddedCurveParameters as DiscreteLogParameters>::XCoefficientsMinusOne::USIZE +
|
|
||||||
<C::EmbeddedCurveParameters as DiscreteLogParameters>::YxCoefficients::USIZE +
|
|
||||||
1;
|
|
||||||
let dlog_proof_len = divisor_len + 2;
|
|
||||||
|
|
||||||
let coeffs_vc_variables = dlog_len + ((1 + (2 * coefficients)) * dlog_proof_len);
|
|
||||||
let ecdhs_vc_variables = ((2 * ecdh_public_keys.len()) * dlog_len) +
|
|
||||||
((2 * 2 * ecdh_public_keys.len()) * dlog_proof_len);
|
|
||||||
let vcs = (coeffs_vc_variables + ecdhs_vc_variables).div_ceil(generators_to_use);
|
|
||||||
|
|
||||||
let all_commitments =
|
|
||||||
transcript.read_commitments(vcs, coefficients + ecdh_public_keys.len()).map_err(|_| ())?;
|
|
||||||
let commitments = all_commitments.V().to_vec();
|
|
||||||
|
|
||||||
let mut ecdh_keys = Vec::with_capacity(ecdh_public_keys.len());
|
|
||||||
let mut ecdh_keys_xy = Vec::with_capacity(ecdh_public_keys.len());
|
|
||||||
for _ in 0 .. ecdh_public_keys.len() {
|
|
||||||
let ecdh_keys_i = [
|
|
||||||
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
|
||||||
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
|
||||||
];
|
|
||||||
ecdh_keys.push(ecdh_keys_i);
|
|
||||||
// This bans zero ECDH keys
|
|
||||||
ecdh_keys_xy.push([
|
|
||||||
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_keys_i[0]).ok_or(())?,
|
|
||||||
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_keys_i[1]).ok_or(())?,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut circuit = Circuit::verify();
|
|
||||||
Self::circuit(
|
|
||||||
&curve_spec,
|
|
||||||
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(evrf_public_key).ok_or(())?,
|
|
||||||
coefficients,
|
|
||||||
&ecdh_keys_xy,
|
|
||||||
&generator_tables.iter().collect::<Vec<_>>(),
|
|
||||||
&mut circuit,
|
|
||||||
&mut transcript,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (statement, None) =
|
|
||||||
circuit.statement(generators.reduce(generators_to_use).ok_or(())?, all_commitments).unwrap()
|
|
||||||
else {
|
|
||||||
panic!("verifying yet was yielded a witness");
|
|
||||||
};
|
|
||||||
|
|
||||||
statement.verify(rng, verifier, &mut transcript).map_err(|_| ())?;
|
|
||||||
|
|
||||||
// Read the openings for the commitments
|
|
||||||
let mut openings = Vec::with_capacity(commitments.len());
|
|
||||||
for _ in 0 .. commitments.len() {
|
|
||||||
openings.push(transcript.read_point::<C>().map_err(|_| ())?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the openings of the commitments
|
|
||||||
let mut agg_weights = Vec::with_capacity(commitments.len());
|
|
||||||
agg_weights.push(C::F::ONE);
|
|
||||||
while agg_weights.len() < commitments.len() {
|
|
||||||
agg_weights.push(transcript.challenge::<C>());
|
|
||||||
}
|
|
||||||
|
|
||||||
let sum_points =
|
|
||||||
openings.iter().zip(&agg_weights).map(|(point, weight)| *point * *weight).sum::<C::G>();
|
|
||||||
let sum_commitments =
|
|
||||||
commitments.into_iter().zip(agg_weights).map(|(point, weight)| point * weight).sum::<C::G>();
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let A = sum_commitments - sum_points;
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let R = transcript.read_point::<C>().map_err(|_| ())?;
|
|
||||||
let c = transcript.challenge::<C>();
|
|
||||||
let s = transcript.read_scalar::<C>().map_err(|_| ())?;
|
|
||||||
|
|
||||||
// Doesn't batch verify this as we can't access the internals of the GBP batch verifier
|
|
||||||
if (R + (A * c)) != (generators.h() * s) {
|
|
||||||
Err(())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !transcript.complete().is_empty() {
|
|
||||||
Err(())?
|
|
||||||
};
|
|
||||||
|
|
||||||
let encryption_commitments = openings[coefficients ..].to_vec();
|
|
||||||
let coefficients = openings[.. coefficients].to_vec();
|
|
||||||
Ok(EvrfVerifyResult { coefficients, ecdh_keys, encryption_commitments })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
684
crypto/dkg/evrf/src/proof/mod.rs
Normal file
684
crypto/dkg/evrf/src/proof/mod.rs
Normal file
@@ -0,0 +1,684 @@
|
|||||||
|
use core::{marker::PhantomData, ops::Deref, fmt};
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std_shims::prelude::*;
|
||||||
|
use std_shims::{vec, vec::Vec};
|
||||||
|
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
||||||
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
|
||||||
|
use ciphersuite::{group::ff::Field, Ciphersuite};
|
||||||
|
|
||||||
|
use generalized_bulletproofs::{
|
||||||
|
Generators, BatchVerifier, PedersenCommitment, PedersenVectorCommitment,
|
||||||
|
transcript::{Transcript as ProverTranscript, VerifierTranscript},
|
||||||
|
arithmetic_circuit_proof::*,
|
||||||
|
};
|
||||||
|
use generalized_bulletproofs_circuit_abstraction::{Transcript, Circuit as BpCircuit};
|
||||||
|
|
||||||
|
use ec_divisors::{DivisorCurve, ScalarDecomposition};
|
||||||
|
use generalized_bulletproofs_ec_gadgets::{
|
||||||
|
CurveSpec, DiscreteLogChallenge, ChallengedGenerator, EcDlogGadgets,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Curves;
|
||||||
|
|
||||||
|
mod tape;
|
||||||
|
use tape::*;
|
||||||
|
|
||||||
|
type EmbeddedPoint<C> = (
|
||||||
|
<<<C as Curves>::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement,
|
||||||
|
<<<C as Curves>::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct Circuit<
|
||||||
|
'a,
|
||||||
|
C: Curves,
|
||||||
|
CG: Iterator<
|
||||||
|
Item = ChallengedGenerator<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
curve_spec: &'a CurveSpec<<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement>,
|
||||||
|
circuit: &'a mut BpCircuit<C::ToweringCurve>,
|
||||||
|
challenge: DiscreteLogChallenge<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>,
|
||||||
|
challenged_G:
|
||||||
|
ChallengedGenerator<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>,
|
||||||
|
challenged_generators: &'a mut CG,
|
||||||
|
tape: Tape,
|
||||||
|
pedersen_commitment_tape: PedersenCommitmentTape,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
'a,
|
||||||
|
C: Curves,
|
||||||
|
CG: Iterator<
|
||||||
|
Item = ChallengedGenerator<<C::ToweringCurve as Ciphersuite>::F, C::EmbeddedCurveParameters>,
|
||||||
|
>,
|
||||||
|
> Circuit<'a, C, CG>
|
||||||
|
{
|
||||||
|
/// Generate coefficients for secret-sharing via an eVRF.
|
||||||
|
///
|
||||||
|
/// This follows the methodology of Protocol 5 from the
|
||||||
|
/// [eVRF paper](https://eprint.iacr.org/2024/397.pdf).
|
||||||
|
fn coefficients(&mut self, evrf_public_key: EmbeddedPoint<C>, coefficients: usize) {
|
||||||
|
/*
|
||||||
|
Read the opening of the prover's eVRF public key, along with all the proofs for the eVRF.
|
||||||
|
Each invocation of the eVRF requires performing _two_ Diffie-Hellmans against
|
||||||
|
uniformly-sampled points.
|
||||||
|
*/
|
||||||
|
let mut point_with_dlogs = self.tape.read_points_with_common_dlog::<C>(1 + (2 * coefficients));
|
||||||
|
|
||||||
|
// Assert this discrete logarithm opens the prover's public key
|
||||||
|
let point = self.circuit.discrete_log(
|
||||||
|
self.curve_spec,
|
||||||
|
point_with_dlogs.next().unwrap(),
|
||||||
|
&self.challenge,
|
||||||
|
&self.challenged_G,
|
||||||
|
);
|
||||||
|
self.circuit.equality(LinComb::from(point.x()), &LinComb::empty().constant(evrf_public_key.0));
|
||||||
|
self.circuit.equality(LinComb::from(point.y()), &LinComb::empty().constant(evrf_public_key.1));
|
||||||
|
|
||||||
|
// Verify the eVRF invocations
|
||||||
|
for _ in 0 .. coefficients {
|
||||||
|
let mut lincomb = LinComb::empty();
|
||||||
|
for challenged_generator in
|
||||||
|
[self.challenged_generators.next().unwrap(), self.challenged_generators.next().unwrap()]
|
||||||
|
{
|
||||||
|
let point = self.circuit.discrete_log(
|
||||||
|
self.curve_spec,
|
||||||
|
point_with_dlogs.next().unwrap(),
|
||||||
|
&self.challenge,
|
||||||
|
&challenged_generator,
|
||||||
|
);
|
||||||
|
lincomb = lincomb.term(<C::ToweringCurve as Ciphersuite>::F::ONE, point.x());
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Constrain the sum of the two `x` coordinates to be equal to the value committed to in a
|
||||||
|
Pedersen commitment
|
||||||
|
*/
|
||||||
|
self.circuit.equality(
|
||||||
|
lincomb,
|
||||||
|
&LinComb::from(self.pedersen_commitment_tape.allocate_pedersen_commitment()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
debug_assert!(point_with_dlogs.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sample an encryption key, proving it's correctly-formed and committed to within a Pedersen
|
||||||
|
/// commitment.
|
||||||
|
fn verifiable_encryption(&mut self, ecdh_commitments: &[EmbeddedPoint<C>; 2]) {
|
||||||
|
// Read the public key used for this encryption
|
||||||
|
let challenged_public_key = self.challenged_generators.next().unwrap();
|
||||||
|
// We perform two separate ECDHs, the sum of their `x` coordinates being our encryption key
|
||||||
|
let mut lincomb = LinComb::empty();
|
||||||
|
for ecdh_commitment in ecdh_commitments {
|
||||||
|
// We open the posted commitment to the ephemeral secret used, and the ECDH value
|
||||||
|
let mut point_with_dlogs = self.tape.read_points_with_common_dlog::<C>(2);
|
||||||
|
|
||||||
|
let point = self.circuit.discrete_log(
|
||||||
|
self.curve_spec,
|
||||||
|
point_with_dlogs.next().unwrap(),
|
||||||
|
&self.challenge,
|
||||||
|
&self.challenged_G,
|
||||||
|
);
|
||||||
|
// Ensure this equals the publicly posted commitment
|
||||||
|
self
|
||||||
|
.circuit
|
||||||
|
.equality(LinComb::from(point.x()), &LinComb::empty().constant(ecdh_commitment.0));
|
||||||
|
self
|
||||||
|
.circuit
|
||||||
|
.equality(LinComb::from(point.y()), &LinComb::empty().constant(ecdh_commitment.1));
|
||||||
|
|
||||||
|
let point = self.circuit.discrete_log(
|
||||||
|
self.curve_spec,
|
||||||
|
point_with_dlogs.next().unwrap(),
|
||||||
|
&self.challenge,
|
||||||
|
&challenged_public_key,
|
||||||
|
);
|
||||||
|
lincomb = lincomb.term(<C::ToweringCurve as Ciphersuite>::F::ONE, point.x());
|
||||||
|
debug_assert!(point_with_dlogs.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require the encryption mask be successfully commited to within a Pedersen commitment
|
||||||
|
self.circuit.equality(
|
||||||
|
lincomb,
|
||||||
|
&LinComb::from(self.pedersen_commitment_tape.allocate_pedersen_commitment()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The result of proving.
|
||||||
|
pub(super) struct ProveResult<C: Curves> {
|
||||||
|
/// The coefficients for use in the DKG.
|
||||||
|
pub(super) coefficients: Vec<Zeroizing<<C::ToweringCurve as Ciphersuite>::F>>,
|
||||||
|
/// The masks to encrypt secret shares with.
|
||||||
|
pub(super) encryption_keys: Vec<Zeroizing<<C::ToweringCurve as Ciphersuite>::F>>,
|
||||||
|
/// The proof itself.
|
||||||
|
pub(super) proof: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct Verified<C: Curves> {
|
||||||
|
/// The commitments to the coefficients used within the DKG.
|
||||||
|
pub(super) coefficients: Vec<<C::ToweringCurve as Ciphersuite>::G>,
|
||||||
|
/// The ephemeral public keys to perform ECDHs with
|
||||||
|
pub(super) ecdh_commitments: Vec<[<C::EmbeddedCurve as Ciphersuite>::G; 2]>,
|
||||||
|
/// The commitments to the masks used to encrypt secret shares with.
|
||||||
|
pub(super) encryption_key_commitments: Vec<<C::ToweringCurve as Ciphersuite>::G>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: Curves> fmt::Debug for Verified<C> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt.debug_struct("Verified").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneratorTable<C> = generalized_bulletproofs_ec_gadgets::GeneratorTable<
|
||||||
|
<<<C as Curves>::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement,
|
||||||
|
<C as Curves>::EmbeddedCurveParameters,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub(super) struct Proof<C>(PhantomData<C>);
|
||||||
|
impl<C: Curves> Proof<C> {
|
||||||
|
fn discrete_log_claims(coefficients: usize, participants: usize) -> usize {
|
||||||
|
/*
|
||||||
|
- 1 DLOG to prove the discrete logarithm corresponds to the eVRF public key
|
||||||
|
- 2 DLOGs per coefficient in the secret-sharing polynomial
|
||||||
|
- 2 DLOGs per each ECDH (one to open the commitment, one for the ECDH itself), with two ECDHs
|
||||||
|
for each participant (with the sum of their `x` coordinates being uniform and used as the
|
||||||
|
mask)
|
||||||
|
*/
|
||||||
|
const DLOGS_PER_COEFFICIENT: usize = 2;
|
||||||
|
const ECDHS_PER_PARTICIPANT: usize = 2;
|
||||||
|
const DLOGS_PER_ECDH: usize = 2;
|
||||||
|
const DLOGS_PER_PARTICIPANT: usize = ECDHS_PER_PARTICIPANT * DLOGS_PER_ECDH;
|
||||||
|
1 + (DLOGS_PER_COEFFICIENT * coefficients) + (DLOGS_PER_PARTICIPANT * participants)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expected_multiplications(coefficients: usize, participants: usize) -> usize {
|
||||||
|
const MULS_PER_DLOG: usize = 7;
|
||||||
|
MULS_PER_DLOG * Self::discrete_log_claims(coefficients, participants)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generators_to_use(coefficients: usize, participants: usize) -> usize {
|
||||||
|
/*
|
||||||
|
`expected_multiplications` may be as small as 16, which would create an excessive amount of
|
||||||
|
vector commitments (as a vector commitment can only commit to as many variables as we have
|
||||||
|
multiplications).
|
||||||
|
|
||||||
|
We require the actual amount of multiplications to be at least 2048 (even though that
|
||||||
|
that 'wastes' thousands of multiplications) to ensure the bandwidth usage remains reasonable.
|
||||||
|
*/
|
||||||
|
Self::expected_multiplications(coefficients, participants).next_power_of_two().max(2048)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variables_in_vector_commitments(coefficients: usize, participants: usize) -> usize {
|
||||||
|
Tape::variables_for_points_with_common_dlog::<C>(1 + (2 * coefficients)) +
|
||||||
|
(participants * 2 * Tape::variables_for_points_with_common_dlog::<C>(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn circuit(
|
||||||
|
curve_spec: &CurveSpec<<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::FieldElement>,
|
||||||
|
evrf_public_key: EmbeddedPoint<C>,
|
||||||
|
coefficients: usize,
|
||||||
|
ecdh_commitments: &[[EmbeddedPoint<C>; 2]],
|
||||||
|
generator_tables: &[&GeneratorTable<C>],
|
||||||
|
circuit: &mut BpCircuit<C::ToweringCurve>,
|
||||||
|
transcript: &mut impl Transcript,
|
||||||
|
) {
|
||||||
|
let participants = ecdh_commitments.len();
|
||||||
|
let generators_to_use = Self::generators_to_use(coefficients, participants);
|
||||||
|
|
||||||
|
// Sample the challenge for all the discrete-logarithm claims
|
||||||
|
let (challenge, challenged_generators) =
|
||||||
|
circuit.discrete_log_challenge(transcript, curve_spec, generator_tables);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The generator tables, and the challenged generators, will have the following layout:
|
||||||
|
- G
|
||||||
|
- Generators for the eVRFs used to sample the coefficients
|
||||||
|
- The participants' public keys, used for performing ECDHs with
|
||||||
|
*/
|
||||||
|
let mut challenged_generators = challenged_generators.into_iter();
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let challenged_G = challenged_generators.next().unwrap();
|
||||||
|
|
||||||
|
let tape = Tape::new(generators_to_use);
|
||||||
|
let pedersen_commitment_tape = PedersenCommitmentTape::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut circuit = Circuit::<C, _> {
|
||||||
|
curve_spec,
|
||||||
|
circuit,
|
||||||
|
challenge,
|
||||||
|
challenged_G,
|
||||||
|
challenged_generators: &mut challenged_generators,
|
||||||
|
tape,
|
||||||
|
pedersen_commitment_tape,
|
||||||
|
};
|
||||||
|
|
||||||
|
circuit.coefficients(evrf_public_key, coefficients);
|
||||||
|
|
||||||
|
// Now execute the circuit for the ECDHs
|
||||||
|
for ecdh_commitments in ecdh_commitments {
|
||||||
|
circuit.verifiable_encryption(ecdh_commitments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(
|
||||||
|
Self::expected_multiplications(coefficients, participants),
|
||||||
|
circuit.muls(),
|
||||||
|
"unexpected amount of multiplications actually used"
|
||||||
|
);
|
||||||
|
debug_assert!(
|
||||||
|
challenged_generators.next().is_none(),
|
||||||
|
"didn't consume all challenged generators"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sample the points for the eVRF invocations used for the coefficients.
|
||||||
|
fn sample_coefficients_evrf_points(
|
||||||
|
seed: [u8; 32],
|
||||||
|
coefficients: usize,
|
||||||
|
) -> Vec<<C::EmbeddedCurve as Ciphersuite>::G> {
|
||||||
|
let mut rng = ChaCha20Rng::from_seed(seed);
|
||||||
|
let quantity = 2 * coefficients;
|
||||||
|
let mut res = Vec::with_capacity(quantity);
|
||||||
|
for _ in 0 .. quantity {
|
||||||
|
res.push(crate::sample_point::<C::EmbeddedCurve>(&mut rng));
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create the required tables for the generators.
|
||||||
|
fn generator_tables(
|
||||||
|
coefficients_evrf_points: &[<C::EmbeddedCurve as Ciphersuite>::G],
|
||||||
|
participants: &[<<C as Curves>::EmbeddedCurve as Ciphersuite>::G],
|
||||||
|
) -> Vec<GeneratorTable<C>> {
|
||||||
|
let curve_spec = CurveSpec {
|
||||||
|
a: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::a(),
|
||||||
|
b: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::b(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut generator_tables =
|
||||||
|
Vec::with_capacity(1 + coefficients_evrf_points.len() + participants.len());
|
||||||
|
{
|
||||||
|
let (x, y) =
|
||||||
|
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(<C::EmbeddedCurve as Ciphersuite>::generator())
|
||||||
|
.unwrap();
|
||||||
|
generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y));
|
||||||
|
}
|
||||||
|
for generator in coefficients_evrf_points {
|
||||||
|
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(*generator).unwrap();
|
||||||
|
generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y));
|
||||||
|
}
|
||||||
|
for generator in participants {
|
||||||
|
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(*generator).unwrap();
|
||||||
|
generator_tables.push(GeneratorTable::<C>::new(&curve_spec, x, y));
|
||||||
|
}
|
||||||
|
generator_tables
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn prove(
|
||||||
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
|
generators: &Generators<C::ToweringCurve>,
|
||||||
|
transcript: [u8; 32],
|
||||||
|
coefficients: usize,
|
||||||
|
participant_public_keys: &[<<C as Curves>::EmbeddedCurve as Ciphersuite>::G],
|
||||||
|
evrf_private_key: &Zeroizing<<<C as Curves>::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
|
) -> Result<ProveResult<C>, AcError> {
|
||||||
|
let curve_spec = CurveSpec {
|
||||||
|
a: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::a(),
|
||||||
|
b: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::b(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let coefficients_evrf_points = Self::sample_coefficients_evrf_points(transcript, coefficients);
|
||||||
|
let generator_tables =
|
||||||
|
Self::generator_tables(&coefficients_evrf_points, participant_public_keys);
|
||||||
|
|
||||||
|
// Push a discrete logarithm onto the tape
|
||||||
|
let discrete_log =
|
||||||
|
|vector_commitment_tape: &mut Vec<_>,
|
||||||
|
dlog: &ScalarDecomposition<<<C as Curves>::EmbeddedCurve as Ciphersuite>::F>| {
|
||||||
|
for coefficient in dlog.decomposition() {
|
||||||
|
vector_commitment_tape.push(<_>::from(*coefficient));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Push a discrete-log claim onto the tape.
|
||||||
|
//
|
||||||
|
// Returns the point for which the claim was made.
|
||||||
|
let discrete_log_claim =
|
||||||
|
|vector_commitment_tape: &mut Vec<_>,
|
||||||
|
dlog: &ScalarDecomposition<<<C as Curves>::EmbeddedCurve as Ciphersuite>::F>,
|
||||||
|
generator: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G| {
|
||||||
|
{
|
||||||
|
let divisor =
|
||||||
|
Zeroizing::new(dlog.scalar_mul_divisor(generator).normalize_x_coefficient());
|
||||||
|
vector_commitment_tape.push(divisor.zero_coefficient);
|
||||||
|
for coefficient in divisor.x_coefficients.iter().skip(1) {
|
||||||
|
vector_commitment_tape.push(*coefficient);
|
||||||
|
}
|
||||||
|
for coefficient in divisor.yx_coefficients.first().unwrap_or(&vec![]) {
|
||||||
|
vector_commitment_tape.push(*coefficient);
|
||||||
|
}
|
||||||
|
vector_commitment_tape.push(
|
||||||
|
divisor
|
||||||
|
.y_coefficients
|
||||||
|
.first()
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(<C::ToweringCurve as Ciphersuite>::F::ZERO),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dh = generator * dlog.scalar();
|
||||||
|
let (x, y) = <C::EmbeddedCurve as Ciphersuite>::G::to_xy(dh).unwrap();
|
||||||
|
vector_commitment_tape.push(x);
|
||||||
|
vector_commitment_tape.push(y);
|
||||||
|
(dh, (x, y))
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut vector_commitment_tape = Zeroizing::new(Vec::with_capacity(
|
||||||
|
Self::variables_in_vector_commitments(coefficients, participant_public_keys.len()),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Handle the coefficients
|
||||||
|
let mut coefficients = Vec::with_capacity(coefficients);
|
||||||
|
let evrf_public_key = {
|
||||||
|
let evrf_private_key =
|
||||||
|
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(**evrf_private_key)
|
||||||
|
.expect("eVRF private key was zero");
|
||||||
|
|
||||||
|
discrete_log(&mut vector_commitment_tape, &evrf_private_key);
|
||||||
|
|
||||||
|
// Push the divisor for proving that we're using the correct scalar
|
||||||
|
let (_, evrf_public_key) = discrete_log_claim(
|
||||||
|
&mut vector_commitment_tape,
|
||||||
|
&evrf_private_key,
|
||||||
|
<<C as Curves>::EmbeddedCurve as Ciphersuite>::generator(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Push the divisor for each point we use in the eVRF
|
||||||
|
for pair in coefficients_evrf_points.chunks(2) {
|
||||||
|
let mut coefficient = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
|
for point in pair {
|
||||||
|
let (_, (dh_x, _)) =
|
||||||
|
discrete_log_claim(&mut vector_commitment_tape, &evrf_private_key, *point);
|
||||||
|
*coefficient += dh_x;
|
||||||
|
}
|
||||||
|
coefficients.push(coefficient);
|
||||||
|
}
|
||||||
|
|
||||||
|
evrf_public_key
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle the verifiable encryption
|
||||||
|
let mut encryption_keys = Vec::with_capacity(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());
|
||||||
|
for participant_public_key in participant_public_keys {
|
||||||
|
let mut ecdh_commitments_xy_i =
|
||||||
|
[(<C::ToweringCurve as Ciphersuite>::F::ZERO, <C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
|
2];
|
||||||
|
let mut encryption_key = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
|
for ecdh_commitments_xy_i_j_dest in &mut ecdh_commitments_xy_i {
|
||||||
|
let mut ecdh_ephemeral_secret;
|
||||||
|
loop {
|
||||||
|
ecdh_ephemeral_secret =
|
||||||
|
Zeroizing::new(<C::EmbeddedCurve as Ciphersuite>::F::random(&mut *rng));
|
||||||
|
// 0 would produce the identity, which isn't representable within the discrete-log proof.
|
||||||
|
if bool::from(!ecdh_ephemeral_secret.is_zero()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ecdh_ephemeral_secret =
|
||||||
|
ScalarDecomposition::<<C::EmbeddedCurve as Ciphersuite>::F>::new(*ecdh_ephemeral_secret)
|
||||||
|
.expect("ECDH ephemeral secret zero");
|
||||||
|
discrete_log(&mut vector_commitment_tape, &ecdh_ephemeral_secret);
|
||||||
|
|
||||||
|
// Push a divisor for proving that we're using the correct scalar for the commitment
|
||||||
|
let (ecdh_commitment, ecdh_commitment_xy_i_j) = discrete_log_claim(
|
||||||
|
&mut vector_commitment_tape,
|
||||||
|
&ecdh_ephemeral_secret,
|
||||||
|
<<C as Curves>::EmbeddedCurve as Ciphersuite>::generator(),
|
||||||
|
);
|
||||||
|
ecdh_commitments.push(ecdh_commitment);
|
||||||
|
*ecdh_commitments_xy_i_j_dest = ecdh_commitment_xy_i_j;
|
||||||
|
// Push a divisor for the key we're performing the ECDH with
|
||||||
|
let (_, (dh_x, _)) = discrete_log_claim(
|
||||||
|
&mut vector_commitment_tape,
|
||||||
|
&ecdh_ephemeral_secret,
|
||||||
|
*participant_public_key,
|
||||||
|
);
|
||||||
|
*encryption_key += dh_x;
|
||||||
|
}
|
||||||
|
ecdh_commitments_xy.push(ecdh_commitments_xy_i);
|
||||||
|
encryption_keys.push(encryption_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the vector commitment tape into vector commitments
|
||||||
|
let generators_to_use =
|
||||||
|
Self::generators_to_use(coefficients.len(), participant_public_keys.len());
|
||||||
|
debug_assert_eq!(
|
||||||
|
Self::variables_in_vector_commitments(coefficients.len(), participant_public_keys.len()),
|
||||||
|
vector_commitment_tape.len()
|
||||||
|
);
|
||||||
|
let mut vector_commitments =
|
||||||
|
Vec::with_capacity(vector_commitment_tape.len().div_ceil(generators_to_use));
|
||||||
|
for chunk in vector_commitment_tape.chunks(generators_to_use) {
|
||||||
|
vector_commitments.push(PedersenVectorCommitment {
|
||||||
|
g_values: chunk.to_vec().into(),
|
||||||
|
mask: <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the Pedersen commitments
|
||||||
|
let mut commitments = Vec::with_capacity(coefficients.len() + participant_public_keys.len());
|
||||||
|
for coefficient in &coefficients {
|
||||||
|
commitments.push(PedersenCommitment {
|
||||||
|
value: **coefficient,
|
||||||
|
mask: <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for enc_mask in &encryption_keys {
|
||||||
|
commitments.push(PedersenCommitment {
|
||||||
|
value: **enc_mask,
|
||||||
|
mask: <C::ToweringCurve as Ciphersuite>::F::random(&mut *rng),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut transcript = ProverTranscript::new(transcript);
|
||||||
|
let commited_commitments = transcript.write_commitments(
|
||||||
|
vector_commitments
|
||||||
|
.iter()
|
||||||
|
.map(|commitment| {
|
||||||
|
commitment
|
||||||
|
.commit(generators.g_bold_slice(), generators.h())
|
||||||
|
.ok_or(AcError::NotEnoughGenerators)
|
||||||
|
})
|
||||||
|
.collect::<Result<_, _>>()?,
|
||||||
|
commitments
|
||||||
|
.iter()
|
||||||
|
.map(|commitment| commitment.commit(generators.g(), generators.h()))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
for ecdh_commitment in ecdh_commitments {
|
||||||
|
transcript.push_point(&ecdh_commitment);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut circuit = BpCircuit::prove(vector_commitments, commitments.clone());
|
||||||
|
Self::circuit(
|
||||||
|
&curve_spec,
|
||||||
|
evrf_public_key,
|
||||||
|
coefficients.len(),
|
||||||
|
&ecdh_commitments_xy,
|
||||||
|
&generator_tables.iter().collect::<Vec<_>>(),
|
||||||
|
&mut circuit,
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (statement, Some(witness)) = circuit
|
||||||
|
.statement(
|
||||||
|
generators.reduce(generators_to_use).ok_or(AcError::NotEnoughGenerators)?,
|
||||||
|
commited_commitments,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("proving yet wasn't yielded the witness");
|
||||||
|
};
|
||||||
|
statement.prove(&mut *rng, &mut transcript, witness).unwrap();
|
||||||
|
|
||||||
|
// Push the reveal onto the transcript
|
||||||
|
for commitment in &commitments {
|
||||||
|
transcript.push_point(&(generators.g() * commitment.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prove the openings of the commitments were correct
|
||||||
|
let mut x = Zeroizing::new(<C::ToweringCurve as Ciphersuite>::F::ZERO);
|
||||||
|
for commitment in commitments {
|
||||||
|
*x += commitment.mask * transcript.challenge::<C::ToweringCurve>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
transcript.push_point(&(generators.h() * r.deref()));
|
||||||
|
let c = transcript.challenge::<C::ToweringCurve>();
|
||||||
|
transcript.push_scalar((c * x.deref()) + r.deref());
|
||||||
|
|
||||||
|
Ok(ProveResult { coefficients, encryption_keys, proof: transcript.complete() })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub(super) fn verify(
|
||||||
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
|
generators: &Generators<C::ToweringCurve>,
|
||||||
|
verifier: &mut BatchVerifier<C::ToweringCurve>,
|
||||||
|
transcript: [u8; 32],
|
||||||
|
coefficients: usize,
|
||||||
|
participant_public_keys: &[<<C as Curves>::EmbeddedCurve as Ciphersuite>::G],
|
||||||
|
evrf_public_key: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G,
|
||||||
|
proof: &[u8],
|
||||||
|
) -> Result<Verified<C>, ()> {
|
||||||
|
let (mut transcript, ecdh_commitments, pedersen_commitments) = {
|
||||||
|
let curve_spec = CurveSpec {
|
||||||
|
a: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::a(),
|
||||||
|
b: <<C as Curves>::EmbeddedCurve as Ciphersuite>::G::b(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let coefficients_evrf_points =
|
||||||
|
Self::sample_coefficients_evrf_points(transcript, coefficients);
|
||||||
|
let generator_tables =
|
||||||
|
Self::generator_tables(&coefficients_evrf_points, participant_public_keys);
|
||||||
|
|
||||||
|
let generators_to_use = Self::generators_to_use(coefficients, participant_public_keys.len());
|
||||||
|
|
||||||
|
let mut transcript = VerifierTranscript::new(transcript, proof);
|
||||||
|
|
||||||
|
let vector_commitments =
|
||||||
|
Self::variables_in_vector_commitments(coefficients, participant_public_keys.len())
|
||||||
|
.div_ceil(generators_to_use);
|
||||||
|
/*
|
||||||
|
One commitment is used to commit to each coefficient of the secret-sharing polynomial, and
|
||||||
|
one commitment is used to commit to each encryption key used to encrypt a secret share to
|
||||||
|
its recipient.
|
||||||
|
*/
|
||||||
|
let pedersen_commitments = coefficients + participant_public_keys.len();
|
||||||
|
let all_commitments =
|
||||||
|
transcript.read_commitments(vector_commitments, pedersen_commitments).map_err(|_| ())?;
|
||||||
|
let pedersen_commitments = all_commitments.V().to_vec();
|
||||||
|
|
||||||
|
// Read the commitments to the ephemeral secrets for the ECDHs
|
||||||
|
let mut ecdh_commitments = Vec::with_capacity(participant_public_keys.len());
|
||||||
|
let mut ecdh_commitments_xy = Vec::with_capacity(participant_public_keys.len());
|
||||||
|
for _ in 0 .. participant_public_keys.len() {
|
||||||
|
let ecdh_commitments_i = [
|
||||||
|
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
||||||
|
transcript.read_point::<C::EmbeddedCurve>().map_err(|_| ())?,
|
||||||
|
];
|
||||||
|
ecdh_commitments.push(ecdh_commitments_i);
|
||||||
|
// This inherently bans using the identity point, as it won't have an affine representation
|
||||||
|
ecdh_commitments_xy.push([
|
||||||
|
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitments_i[0])
|
||||||
|
.ok_or(())?,
|
||||||
|
<<C::EmbeddedCurve as Ciphersuite>::G as DivisorCurve>::to_xy(ecdh_commitments_i[1])
|
||||||
|
.ok_or(())?,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut circuit = BpCircuit::verify();
|
||||||
|
Self::circuit(
|
||||||
|
&curve_spec,
|
||||||
|
<C::EmbeddedCurve as Ciphersuite>::G::to_xy(evrf_public_key).ok_or(())?,
|
||||||
|
coefficients,
|
||||||
|
&ecdh_commitments_xy,
|
||||||
|
&generator_tables.iter().collect::<Vec<_>>(),
|
||||||
|
&mut circuit,
|
||||||
|
&mut transcript,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (statement, None) = circuit
|
||||||
|
.statement(generators.reduce(generators_to_use).ok_or(())?, all_commitments)
|
||||||
|
.unwrap()
|
||||||
|
else {
|
||||||
|
panic!("verifying yet was yielded a witness");
|
||||||
|
};
|
||||||
|
|
||||||
|
statement.verify(rng, verifier, &mut transcript).map_err(|_| ())?;
|
||||||
|
|
||||||
|
(transcript, ecdh_commitments, pedersen_commitments)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the openings for each of the Pedersen commitments
|
||||||
|
let mut openings = Vec::with_capacity(pedersen_commitments.len());
|
||||||
|
for _ in 0 .. pedersen_commitments.len() {
|
||||||
|
openings.push(transcript.read_point::<C::ToweringCurve>().map_err(|_| ())?);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Verify the openings of each of the Pedersen commitments.
|
||||||
|
|
||||||
|
We do this via verifying the prover knows an opening of their Pedersen commitment, minus the
|
||||||
|
claimed opening, over the blinding generator. For efficiency, we take a random combination of
|
||||||
|
all commitments/openings, solely requiring the prover know the single opening for the
|
||||||
|
combination.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
let (weighted_sum_commitments, weighted_sum_openings) = {
|
||||||
|
let mut weighted_sum_commitments = Vec::with_capacity(pedersen_commitments.len());
|
||||||
|
let mut weighted_sum_openings = Vec::with_capacity(pedersen_commitments.len());
|
||||||
|
for (pedersen_commitment, opening) in pedersen_commitments.iter().zip(&openings) {
|
||||||
|
let weight = transcript.challenge::<C::ToweringCurve>();
|
||||||
|
weighted_sum_commitments.push((weight, *pedersen_commitment));
|
||||||
|
weighted_sum_openings.push((weight, *opening));
|
||||||
|
}
|
||||||
|
(
|
||||||
|
multiexp::multiexp_vartime(&weighted_sum_commitments),
|
||||||
|
multiexp::multiexp_vartime(&weighted_sum_openings),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let A = weighted_sum_commitments - weighted_sum_openings;
|
||||||
|
|
||||||
|
// Schnorr signature
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let R = transcript.read_point::<C::ToweringCurve>().map_err(|_| ())?;
|
||||||
|
let c = transcript.challenge::<C::ToweringCurve>();
|
||||||
|
let s = transcript.read_scalar::<C::ToweringCurve>().map_err(|_| ())?;
|
||||||
|
|
||||||
|
// Doesn't batch verify this as we can't access the internals of the GBP batch verifier
|
||||||
|
if (R + (A * c)) != (generators.h() * s) {
|
||||||
|
Err(())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !transcript.complete().is_empty() {
|
||||||
|
Err(())?
|
||||||
|
};
|
||||||
|
|
||||||
|
let coefficients = openings[.. coefficients].to_vec();
|
||||||
|
let encryption_key_commitments = openings[coefficients.len() ..].to_vec();
|
||||||
|
Ok(Verified { coefficients, ecdh_commitments, encryption_key_commitments })
|
||||||
|
}
|
||||||
|
}
|
||||||
106
crypto/dkg/evrf/src/proof/tape.rs
Normal file
106
crypto/dkg/evrf/src/proof/tape.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use generic_array::{sequence::GenericSequence, ArrayLength, GenericArray};
|
||||||
|
|
||||||
|
use generalized_bulletproofs_circuit_abstraction::Variable;
|
||||||
|
use generalized_bulletproofs_ec_gadgets::{DiscreteLogParameters, Divisor, PointWithDlog};
|
||||||
|
|
||||||
|
use crate::Curves;
|
||||||
|
|
||||||
|
/*
|
||||||
|
For all variables we must commit to during the ZK proof, we place them on the 'tape'. The tape
|
||||||
|
is a linear representation of every single variable committed to by the proof, from which we can
|
||||||
|
read a collection of variables from/push a collection of variables onto. This offers an API
|
||||||
|
similar to reading/writing to a byte stream, despite working with variables in a ZK proof.
|
||||||
|
*/
|
||||||
|
pub(super) struct Tape {
|
||||||
|
generators: usize,
|
||||||
|
current_position: usize,
|
||||||
|
}
|
||||||
|
impl Tape {
|
||||||
|
// Construct a new tape.
|
||||||
|
pub(super) fn new(generators: usize) -> Self {
|
||||||
|
Self { generators, current_position: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a Variable from the tape.
|
||||||
|
fn read_one_from_tape(&mut self) -> Variable {
|
||||||
|
let commitment = self.current_position / self.generators;
|
||||||
|
let index = self.current_position % self.generators;
|
||||||
|
let res = Variable::CG { commitment, index };
|
||||||
|
self.current_position += 1;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a fixed-length array of variables from the tape.
|
||||||
|
fn read_from_tape<N: ArrayLength>(&mut self) -> GenericArray<Variable, N> {
|
||||||
|
GenericArray::<Variable, N>::generate(|_| self.read_one_from_tape())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read `PointWithDlog`s, which share a discrete logarithm, from the tape.
|
||||||
|
pub(super) fn read_points_with_common_dlog<C: Curves>(
|
||||||
|
&mut self,
|
||||||
|
quantity: usize,
|
||||||
|
) -> impl use<'_, C> + Iterator<Item = PointWithDlog<C::EmbeddedCurveParameters>> {
|
||||||
|
/*
|
||||||
|
The tape expects the format of:
|
||||||
|
- Discrete logarithm
|
||||||
|
- Divisor (zero coefficient, x coefficients, y x**i coefficients, y coefficient)
|
||||||
|
- Point (x, y)
|
||||||
|
Note the `x` coefficients are only from the power of two, and `i >= 1`.
|
||||||
|
*/
|
||||||
|
let dlog =
|
||||||
|
self.read_from_tape::<<C::EmbeddedCurveParameters as DiscreteLogParameters>::ScalarBits>();
|
||||||
|
|
||||||
|
struct PointIterator<'a, C: Curves>(
|
||||||
|
&'a mut Tape,
|
||||||
|
GenericArray<Variable, <C::EmbeddedCurveParameters as DiscreteLogParameters>::ScalarBits>,
|
||||||
|
PhantomData<C>,
|
||||||
|
);
|
||||||
|
impl<'a, C: Curves> Iterator for PointIterator<'a, C> {
|
||||||
|
type Item = PointWithDlog<C::EmbeddedCurveParameters>;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let divisor = {
|
||||||
|
let zero = self.0.read_one_from_tape();
|
||||||
|
let x_from_power_of_2 = self.0.read_from_tape();
|
||||||
|
let yx = self.0.read_from_tape();
|
||||||
|
let y = self.0.read_one_from_tape();
|
||||||
|
Divisor { zero, x_from_power_of_2, yx, y }
|
||||||
|
};
|
||||||
|
|
||||||
|
let point = (
|
||||||
|
// x coordinate
|
||||||
|
self.0.read_one_from_tape(),
|
||||||
|
// y coordinate
|
||||||
|
self.0.read_one_from_tape(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(PointWithDlog { dlog: self.1.clone(), divisor, point })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PointIterator(self, dlog, PhantomData::<C>).take(quantity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The amount of variables the points with a common discrete logarithm will use on the tape.
|
||||||
|
pub(super) fn variables_for_points_with_common_dlog<C: Curves>(quantity: usize) -> usize {
|
||||||
|
let mut dummy_tape = Tape::new(usize::MAX);
|
||||||
|
for _ in dummy_tape.read_points_with_common_dlog::<C>(quantity) {}
|
||||||
|
dummy_tape.current_position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct PedersenCommitmentTape {
|
||||||
|
pedersen_commitments: usize,
|
||||||
|
}
|
||||||
|
impl PedersenCommitmentTape {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self { pedersen_commitments: 0 }
|
||||||
|
}
|
||||||
|
/// Allocate a Pedersen commitment.
|
||||||
|
pub(super) fn allocate_pedersen_commitment(&mut self) -> Variable {
|
||||||
|
let res = Variable::V(self.pedersen_commitments);
|
||||||
|
self.pedersen_commitments += 1;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,26 +5,26 @@ use rand_core::OsRng;
|
|||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
|
||||||
use ciphersuite::{group::ff::Field, Ciphersuite};
|
use ciphersuite::{group::ff::Field, Ciphersuite};
|
||||||
|
use embedwards25519::Embedwards25519;
|
||||||
|
|
||||||
use crate::{
|
use dkg_recovery::recover_key;
|
||||||
Participant,
|
use crate::{Participant, Curves, Generators, VerifyResult, Dkg, Ristretto};
|
||||||
evrf::*,
|
|
||||||
tests::{THRESHOLD, PARTICIPANTS, recover_key},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod proof;
|
mod proof;
|
||||||
use proof::{Pallas, Vesta};
|
|
||||||
|
const THRESHOLD: u16 = 3;
|
||||||
|
const PARTICIPANTS: u16 = 5;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn evrf_dkg() {
|
fn dkg() {
|
||||||
let generators = EvrfGenerators::<Pallas>::new(THRESHOLD, PARTICIPANTS);
|
let generators = Generators::<Ristretto>::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 = <Vesta as Ciphersuite>::F::random(&mut OsRng);
|
let priv_key = <Embedwards25519 as Ciphersuite>::F::random(&mut OsRng);
|
||||||
pub_keys.push(<Vesta as Ciphersuite>::generator() * priv_key);
|
pub_keys.push(<Embedwards25519 as Ciphersuite>::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,7 +34,7 @@ fn evrf_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,
|
||||||
EvrfDkg::<Pallas>::participate(
|
Dkg::<Ristretto>::participate(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&generators,
|
&generators,
|
||||||
context,
|
context,
|
||||||
@@ -46,7 +46,7 @@ fn evrf_dkg() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let VerifyResult::Valid(dkg) = EvrfDkg::<Pallas>::verify(
|
let VerifyResult::Valid(dkg) = Dkg::<Ristretto>::verify(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&generators,
|
&generators,
|
||||||
context,
|
context,
|
||||||
@@ -67,13 +67,21 @@ fn evrf_dkg() {
|
|||||||
assert_eq!(keys.params().t(), THRESHOLD);
|
assert_eq!(keys.params().t(), THRESHOLD);
|
||||||
assert_eq!(keys.params().n(), PARTICIPANTS);
|
assert_eq!(keys.params().n(), PARTICIPANTS);
|
||||||
group_key = group_key.or(Some(keys.group_key()));
|
group_key = group_key.or(Some(keys.group_key()));
|
||||||
verification_shares = verification_shares.or(Some(keys.verification_shares()));
|
let these_verification_shares = Participant::iter()
|
||||||
|
.take(usize::from(PARTICIPANTS))
|
||||||
|
.map(|i| (i, keys.original_verification_share(i)))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
verification_shares = verification_shares.or(Some(these_verification_shares.clone()));
|
||||||
assert_eq!(Some(keys.group_key()), group_key);
|
assert_eq!(Some(keys.group_key()), group_key);
|
||||||
assert_eq!(Some(keys.verification_shares()), verification_shares);
|
assert_eq!(Some(these_verification_shares), verification_shares);
|
||||||
|
|
||||||
all_keys.insert(i, keys);
|
all_keys.insert(i, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test for all possible combinations of keys
|
// TODO: Test for all possible combinations of keys
|
||||||
assert_eq!(Pallas::generator() * recover_key(&all_keys), group_key.unwrap());
|
assert_eq!(
|
||||||
|
<<Ristretto as Curves>::ToweringCurve as Ciphersuite>::generator() *
|
||||||
|
*recover_key(&all_keys.values().cloned().collect::<Vec<_>>()).unwrap(),
|
||||||
|
group_key.unwrap()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,94 +2,49 @@ use std::time::Instant;
|
|||||||
|
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::Zeroizing;
|
||||||
use generic_array::typenum::{Sum, Diff, Quot, U, U1, U2};
|
|
||||||
use blake2::{Digest, Blake2b512};
|
|
||||||
|
|
||||||
use ciphersuite::{
|
use ciphersuite::{
|
||||||
group::{
|
group::{ff::Field, Group},
|
||||||
ff::{FromUniformBytes, Field, PrimeField},
|
Ciphersuite,
|
||||||
Group,
|
|
||||||
},
|
|
||||||
Ciphersuite, Secp256k1, Ed25519, Ristretto,
|
|
||||||
};
|
};
|
||||||
use pasta_curves::{Ep, Eq, Fp, Fq};
|
|
||||||
|
|
||||||
use generalized_bulletproofs::{Generators, tests::generators};
|
use generalized_bulletproofs::{Generators, tests::generators};
|
||||||
use generalized_bulletproofs_ec_gadgets::DiscreteLogParameters;
|
|
||||||
|
|
||||||
use crate::evrf::proof::*;
|
use crate::{
|
||||||
|
Curves, Ristretto,
|
||||||
|
proof::*,
|
||||||
|
tests::{THRESHOLD, PARTICIPANTS},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
fn proof<C: Curves>() {
|
||||||
pub(crate) struct Pallas;
|
|
||||||
impl Ciphersuite for Pallas {
|
|
||||||
type F = Fq;
|
|
||||||
type G = Ep;
|
|
||||||
type H = Blake2b512;
|
|
||||||
const ID: &'static [u8] = b"Pallas";
|
|
||||||
fn generator() -> Ep {
|
|
||||||
Ep::generator()
|
|
||||||
}
|
|
||||||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
|
||||||
// This naive concat may be insecure in a real world deployment
|
|
||||||
// This is solely test code so it's fine
|
|
||||||
Self::F::from_uniform_bytes(&Self::H::digest([dst, msg].concat()).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
|
||||||
pub(crate) struct Vesta;
|
|
||||||
impl Ciphersuite for Vesta {
|
|
||||||
type F = Fp;
|
|
||||||
type G = Eq;
|
|
||||||
type H = Blake2b512;
|
|
||||||
const ID: &'static [u8] = b"Vesta";
|
|
||||||
fn generator() -> Eq {
|
|
||||||
Eq::generator()
|
|
||||||
}
|
|
||||||
fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F {
|
|
||||||
// This naive concat may be insecure in a real world deployment
|
|
||||||
// This is solely test code so it's fine
|
|
||||||
Self::F::from_uniform_bytes(&Self::H::digest([dst, msg].concat()).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VestaParams;
|
|
||||||
impl DiscreteLogParameters for VestaParams {
|
|
||||||
type ScalarBits = U<{ <<Vesta as Ciphersuite>::F as PrimeField>::NUM_BITS as usize }>;
|
|
||||||
type XCoefficients = Quot<Sum<Self::ScalarBits, U1>, U2>;
|
|
||||||
type XCoefficientsMinusOne = Diff<Self::XCoefficients, U1>;
|
|
||||||
type YxCoefficients = Diff<Quot<Sum<Sum<Self::ScalarBits, U1>, U1>, U2>, U2>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EvrfCurve for Pallas {
|
|
||||||
type EmbeddedCurve = Vesta;
|
|
||||||
type EmbeddedCurveParameters = VestaParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evrf_proof_test<C: EvrfCurve>() {
|
|
||||||
let generators = generators(2048);
|
let generators = generators(2048);
|
||||||
let vesta_private_key = Zeroizing::new(<C::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng));
|
let embedded_private_key =
|
||||||
let ecdh_public_keys = [
|
Zeroizing::new(<C::EmbeddedCurve as Ciphersuite>::F::random(&mut OsRng));
|
||||||
<C::EmbeddedCurve as Ciphersuite>::G::random(&mut OsRng),
|
let ecdh_public_keys: [_; PARTICIPANTS as usize] =
|
||||||
<C::EmbeddedCurve as Ciphersuite>::G::random(&mut OsRng),
|
core::array::from_fn(|_| <C::EmbeddedCurve as Ciphersuite>::G::random(&mut OsRng));
|
||||||
];
|
|
||||||
let time = Instant::now();
|
let time = Instant::now();
|
||||||
let res =
|
let res = Proof::<C>::prove(
|
||||||
Evrf::<C>::prove(&mut OsRng, &generators, [0; 32], 1, &ecdh_public_keys, &vesta_private_key)
|
&mut OsRng,
|
||||||
|
&generators,
|
||||||
|
[0; 32],
|
||||||
|
THRESHOLD.into(),
|
||||||
|
&ecdh_public_keys,
|
||||||
|
&embedded_private_key,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("Proving time: {:?}", time.elapsed());
|
println!("Proving time: {:?}", time.elapsed());
|
||||||
|
|
||||||
let time = Instant::now();
|
let time = Instant::now();
|
||||||
let mut verifier = Generators::batch_verifier();
|
let mut verifier = Generators::batch_verifier();
|
||||||
Evrf::<C>::verify(
|
Proof::<C>::verify(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&generators,
|
&generators,
|
||||||
&mut verifier,
|
&mut verifier,
|
||||||
[0; 32],
|
[0; 32],
|
||||||
1,
|
THRESHOLD.into(),
|
||||||
&ecdh_public_keys,
|
&ecdh_public_keys,
|
||||||
C::EmbeddedCurve::generator() * *vesta_private_key,
|
C::EmbeddedCurve::generator() * *embedded_private_key,
|
||||||
&res.proof,
|
&res.proof,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -98,21 +53,6 @@ fn evrf_proof_test<C: EvrfCurve>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pallas_evrf_proof_test() {
|
fn ristretto_proof() {
|
||||||
evrf_proof_test::<Pallas>();
|
proof::<Ristretto>();
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn secp256k1_evrf_proof_test() {
|
|
||||||
evrf_proof_test::<Secp256k1>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ed25519_evrf_proof_test() {
|
|
||||||
evrf_proof_test::<Ed25519>();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ristretto_evrf_proof_test() {
|
|
||||||
evrf_proof_test::<Ristretto>();
|
|
||||||
}
|
}
|
||||||
|
|||||||
43
crypto/dkg/evrf/src/utils.rs
Normal file
43
crypto/dkg/evrf/src/utils.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
|
use ciphersuite::{
|
||||||
|
group::{ff::PrimeField, Group, GroupEncoding},
|
||||||
|
Ciphersuite,
|
||||||
|
};
|
||||||
|
|
||||||
|
use dkg::Participant;
|
||||||
|
|
||||||
|
/// Sample a random, unbiased point on the elliptic curve with an unknown discrete logarithm.
|
||||||
|
///
|
||||||
|
/// This keeps it simple by using rejection sampling.
|
||||||
|
pub(crate) fn sample_point<C: Ciphersuite>(rng: &mut (impl RngCore + CryptoRng)) -> C::G {
|
||||||
|
let mut repr = <C::G as GroupEncoding>::Repr::default();
|
||||||
|
loop {
|
||||||
|
rng.fill_bytes(repr.as_mut());
|
||||||
|
if let Ok(point) = C::read_G(&mut repr.as_ref()) {
|
||||||
|
if bool::from(!point.is_identity()) {
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn polynomial<F: PrimeField + Zeroize>(
|
||||||
|
coefficients: &[Zeroizing<F>],
|
||||||
|
l: Participant,
|
||||||
|
) -> Zeroizing<F> {
|
||||||
|
let l = F::from(u64::from(u16::from(l)));
|
||||||
|
// This should never be reached since Participant is explicitly non-zero
|
||||||
|
assert!(l != F::ZERO, "zero participant passed to polynomial");
|
||||||
|
let mut share = Zeroizing::new(F::ZERO);
|
||||||
|
for (idx, coefficient) in coefficients.iter().rev().enumerate() {
|
||||||
|
*share += coefficient.deref();
|
||||||
|
if idx != (coefficients.len() - 1) {
|
||||||
|
*share *= l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
share
|
||||||
|
}
|
||||||
@@ -39,6 +39,19 @@ impl Participant {
|
|||||||
pub const fn to_bytes(&self) -> [u8; 2] {
|
pub const fn to_bytes(&self) -> [u8; 2] {
|
||||||
self.0.to_le_bytes()
|
self.0.to_le_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an iterator over participant indexes.
|
||||||
|
pub fn iter() -> impl Iterator<Item = Participant> {
|
||||||
|
struct ParticipantIterator(u16);
|
||||||
|
impl Iterator for ParticipantIterator {
|
||||||
|
type Item = Participant;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0 = self.0.checked_add(1)?;
|
||||||
|
Some(Participant(self.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ParticipantIterator(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Participant> for u16 {
|
impl From<Participant> for u16 {
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ dalek-ff-group = { path = "../../dalek-ff-group", version = "0.4", default-featu
|
|||||||
|
|
||||||
blake2 = { version = "0.10", default-features = false }
|
blake2 = { version = "0.10", default-features = false }
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false }
|
ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false }
|
||||||
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ macro_rules! field {
|
|||||||
use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus};
|
use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus};
|
||||||
|
|
||||||
use ciphersuite::group::ff::{
|
use ciphersuite::group::ff::{
|
||||||
Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic,
|
Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes, helpers::sqrt_ratio_generic,
|
||||||
};
|
};
|
||||||
|
|
||||||
use $crate::backend::u8_from_bool;
|
use $crate::backend::u8_from_bool;
|
||||||
@@ -258,6 +258,12 @@ macro_rules! field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromUniformBytes<64> for $FieldName {
|
||||||
|
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
|
||||||
|
$FieldName(Residue::new(&reduce(U512::from_le_slice(bytes))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Sum<$FieldName> for $FieldName {
|
impl Sum<$FieldName> for $FieldName {
|
||||||
fn sum<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
fn sum<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
||||||
let mut res = $FieldName::ZERO;
|
let mut res = $FieldName::ZERO;
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ k256 = { version = "0.13", default-features = false, features = ["arithmetic"] }
|
|||||||
|
|
||||||
blake2 = { version = "0.10", default-features = false }
|
blake2 = { version = "0.10", default-features = false }
|
||||||
ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false }
|
ciphersuite = { path = "../../ciphersuite", version = "0.4", default-features = false }
|
||||||
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
generalized-bulletproofs-ec-gadgets = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ macro_rules! field {
|
|||||||
use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus};
|
use crypto_bigint::{Integer, NonZero, Encoding, impl_modulus};
|
||||||
|
|
||||||
use ciphersuite::group::ff::{
|
use ciphersuite::group::ff::{
|
||||||
Field, PrimeField, FieldBits, PrimeFieldBits, helpers::sqrt_ratio_generic,
|
Field, PrimeField, FieldBits, PrimeFieldBits, FromUniformBytes, helpers::sqrt_ratio_generic,
|
||||||
};
|
};
|
||||||
|
|
||||||
use $crate::backend::u8_from_bool;
|
use $crate::backend::u8_from_bool;
|
||||||
@@ -260,6 +260,12 @@ macro_rules! field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromUniformBytes<64> for $FieldName {
|
||||||
|
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
|
||||||
|
$FieldName(Residue::new(&reduce(U512::from_le_slice(bytes))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Sum<$FieldName> for $FieldName {
|
impl Sum<$FieldName> for $FieldName {
|
||||||
fn sum<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
fn sum<I: Iterator<Item = $FieldName>>(iter: I) -> $FieldName {
|
||||||
let mut res = $FieldName::ZERO;
|
let mut res = $FieldName::ZERO;
|
||||||
|
|||||||
30
patches/ciphersuite/Cargo.toml
Normal file
30
patches/ciphersuite/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "ciphersuite"
|
||||||
|
version = "0.4.99"
|
||||||
|
description = "Ciphersuites built around ff/group"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/ciphersuite"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = ["ciphersuite", "ff", "group"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.66"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false }
|
||||||
|
dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false, optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
alloc = ["ciphersuite/alloc"]
|
||||||
|
std = [
|
||||||
|
"ciphersuite/std",
|
||||||
|
"dalek-ff-group?/std",
|
||||||
|
]
|
||||||
|
ed25519 = ["dalek-ff-group"]
|
||||||
|
default = ["std"]
|
||||||
21
patches/ciphersuite/LICENSE
Normal file
21
patches/ciphersuite/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-2025 Luke Parker
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
4
patches/ciphersuite/README.md
Normal file
4
patches/ciphersuite/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Ciphersuite
|
||||||
|
|
||||||
|
Patch for the `crates.io` ciphersuite to use the in-tree ciphersuite, resolving
|
||||||
|
breaking changes made since.
|
||||||
5
patches/ciphersuite/src/lib.rs
Normal file
5
patches/ciphersuite/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
pub use ciphersuite::*;
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
use dalek_ff_group::Ed25519;
|
||||||
@@ -31,7 +31,7 @@ rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
|
|||||||
# Cryptography
|
# Cryptography
|
||||||
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
blake2 = { version = "0.10", default-features = false, features = ["std"] }
|
||||||
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std"] }
|
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std"] }
|
||||||
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
ec-divisors = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
|
ciphersuite = { path = "../../crypto/ciphersuite", 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"] }
|
||||||
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", "ristretto"] }
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, fea
|
|||||||
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/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false, features = ["std", "multisig"] }
|
monero-wallet = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false, features = ["std", "multisig"] }
|
||||||
monero-simple-request-rpc = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false }
|
monero-simple-request-rpc = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false }
|
||||||
|
|
||||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["monero"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["monero"] }
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ bitcoin = { version = "0.32", optional = true }
|
|||||||
|
|
||||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true }
|
dalek-ff-group = { path = "../../crypto/dalek-ff-group", optional = true }
|
||||||
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true }
|
ciphersuite = { path = "../../crypto/ciphersuite", version = "0.4", optional = true }
|
||||||
monero-address = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", version = "0.1.0", default-features = false, features = ["std"], optional = true }
|
monero-address = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", version = "0.1.0", default-features = false, features = ["std"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ serai-env = { path = "../../common/env" }
|
|||||||
|
|
||||||
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
|
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
|
||||||
bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, features = ["std", "hazmat"] }
|
bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, features = ["std", "hazmat"] }
|
||||||
monero-address = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "b6dd1a9ff7ac6b96eb7cb488a4501fd1f6f2dd1e", default-features = false, features = ["std"] }
|
monero-address = { git = "https://github.com/kayabaNerve/monero-oxide", rev = "54da48f27a05fa8656014942919da1dfbab4d8e3", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
substrate-build-script-utils = { git = "https://github.com/serai-dex/substrate" }
|
substrate-build-script-utils = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ secq256k1 = { path = "../../crypto/evrf/secq256k1", default-features = false }
|
|||||||
embedwards25519 = { path = "../../crypto/evrf/embedwards25519", default-features = false }
|
embedwards25519 = { path = "../../crypto/evrf/embedwards25519", default-features = false }
|
||||||
|
|
||||||
dkg = { path = "../../crypto/dkg", default-features = false }
|
dkg = { path = "../../crypto/dkg", default-features = false }
|
||||||
|
dkg-evrf = { path = "../../crypto/dkg/evrf", default-features = false }
|
||||||
# modular-frost = { path = "../../crypto/frost", default-features = false }
|
# modular-frost = { path = "../../crypto/frost", default-features = false }
|
||||||
# frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false }
|
# frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ pub use secq256k1;
|
|||||||
pub use embedwards25519;
|
pub use embedwards25519;
|
||||||
|
|
||||||
pub use dkg;
|
pub use dkg;
|
||||||
|
pub use dkg_evrf;
|
||||||
/*
|
/*
|
||||||
pub use modular_frost;
|
pub use modular_frost;
|
||||||
pub use frost_schnorrkel;
|
pub use frost_schnorrkel;
|
||||||
|
|||||||
Reference in New Issue
Block a user