Просмотр исходного кода

sdk: Move ECVRF from research into the library.

parazyd 3 лет назад
Родитель
Сommit
59f3e514f8

+ 0 - 3
script/research/ecvrf/.gitignore

@@ -1,3 +0,0 @@
-Cargo.lock
-target/
-*.bin

+ 0 - 19
script/research/ecvrf/Cargo.toml

@@ -1,19 +0,0 @@
-[package]
-name = "ecvrf"
-version = "0.4.1"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-license = "AGPL-3.0-only"
-edition = "2021"
-
-[workspace]
-
-[dependencies]
-blake3 = "1.3.3"
-darkfi-sdk = {path = "../../../src/sdk"}
-darkfi = {path = "../../../", features = ["zk"]}
-lazy_static = "1.4.0"
-rand = "0.8.5"
-halo2_gadgets = "0.3.0"
-
-[patch.crates-io]
-halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v3"}

+ 0 - 114
script/research/ecvrf/src/main.rs

@@ -1,114 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-//! https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-04#section-5
-#![allow(non_snake_case)]
-
-use darkfi_sdk::{
-    crypto::{
-        constants::NullifierK,
-        pasta_prelude::{CurveExt, Field, Group},
-        util::mod_r_p,
-    },
-    pasta::{
-        group::{ff::FromUniformBytes, GroupEncoding},
-        pallas,
-    },
-};
-use halo2_gadgets::ecc::chip::FixedPoint;
-use lazy_static::lazy_static;
-use rand::rngs::OsRng;
-
-lazy_static! {
-    /// The `B` generator
-    static ref B: pallas::Affine = NullifierK.generator();
-}
-
-const VRF_DOMAIN: &str = "ECVRF";
-
-#[derive(Copy, Clone, Debug)]
-struct VrfProof {
-    gamma: pallas::Point,
-    c: blake3::Hash,
-    s: pallas::Scalar,
-}
-
-fn prove(x: pallas::Base, alpha_string: &[u8]) -> VrfProof {
-    let Y = *B * mod_r_p(x);
-
-    let pallas_hasher = pallas::Point::hash_to_curve(VRF_DOMAIN);
-    let H = pallas_hasher(&Y.to_bytes()) + pallas_hasher(alpha_string);
-
-    let gamma = H * mod_r_p(x);
-    let k = pallas::Scalar::random(&mut OsRng);
-
-    let mut hasher = blake3::Hasher::new();
-    hasher.update(&H.to_bytes());
-    hasher.update(&gamma.to_bytes());
-    hasher.update(&(*B * k).to_bytes());
-    hasher.update(&(H * k).to_bytes());
-    let c = hasher.finalize();
-
-    let mut c_scalar = [0u8; 64];
-    c_scalar[..blake3::OUT_LEN].copy_from_slice(c.as_bytes());
-    let c_scalar = pallas::Scalar::from_uniform_bytes(&c_scalar);
-
-    let s = k + c_scalar * mod_r_p(x);
-
-    VrfProof { gamma, c, s }
-}
-
-fn verify(Y: pallas::Point, proof: VrfProof, alpha_string: &[u8]) -> bool {
-    let pallas_hasher = pallas::Point::hash_to_curve(VRF_DOMAIN);
-    let H = pallas_hasher(&Y.to_bytes()) + pallas_hasher(alpha_string);
-
-    let mut c = [0u8; 64];
-    c[..blake3::OUT_LEN].copy_from_slice(proof.c.as_bytes());
-    let c_scalar = pallas::Scalar::from_uniform_bytes(&c);
-
-    let U = *B * proof.s - Y * c_scalar;
-    let V = H * proof.s - proof.gamma * c_scalar;
-
-    let mut hasher = blake3::Hasher::new();
-    hasher.update(&H.to_bytes());
-    hasher.update(&proof.gamma.to_bytes());
-    hasher.update(&U.to_bytes());
-    hasher.update(&V.to_bytes());
-
-    hasher.finalize() == proof.c
-}
-
-fn main() {
-    // VRF secret key
-    let secret_key = pallas::Base::random(&mut OsRng);
-    // VRF public key
-    let public_key = *B * mod_r_p(secret_key);
-    // VRF input
-    let input = [0xde, 0xad, 0xbe, 0xef];
-
-    let proof = prove(secret_key, &input);
-    assert!(verify(public_key, proof, &input));
-
-    // Forged public key
-    let forged_public_key = pallas::Point::random(&mut OsRng);
-    assert!(!verify(forged_public_key, proof, &input));
-
-    // Forged input
-    let forged_input = [0xde, 0xad, 0xba, 0xbe];
-    assert!(!verify(public_key, proof, &forged_input));
-}

+ 130 - 0
src/sdk/src/crypto/ecvrf.rs

@@ -0,0 +1,130 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//! https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-04#section-5
+#![allow(non_snake_case)]
+
+use halo2_gadgets::ecc::chip::FixedPoint;
+use pasta_curves::{
+    arithmetic::CurveExt,
+    group::{
+        ff::{Field, FromUniformBytes},
+        GroupEncoding,
+    },
+    pallas,
+};
+use rand_core::{CryptoRng, RngCore};
+
+use super::{constants::NullifierK, util::mod_r_p, PublicKey, SecretKey};
+
+/// Prefix domain used for `hash_to_curve` calls
+const VRF_DOMAIN: &str = "DarkFi_ECVRF";
+
+/// VRF Proof
+///
+/// `gamma` is the deterministic randomness produced by the function.
+#[derive(Copy, Clone, Debug)]
+pub struct VrfProof {
+    /// Deterministic randomness produced by the VRF function
+    pub gamma: pallas::Point,
+    c: blake3::Hash,
+    s: pallas::Scalar,
+}
+
+impl VrfProof {
+    /// Execute the VRF function and create a proof given a `SecretKey`
+    /// a seed input `alpha_string`, and an RNG instance.
+    pub fn prove(x: SecretKey, alpha_string: &[u8], rng: &mut (impl CryptoRng + RngCore)) -> Self {
+        let Y = PublicKey::from_secret(x);
+
+        let mut message = vec![];
+        message.extend_from_slice(&Y.to_bytes());
+        message.extend_from_slice(alpha_string);
+        let H = pallas::Point::hash_to_curve(VRF_DOMAIN)(&message);
+
+        let gamma = H * mod_r_p(x.inner());
+        let k = pallas::Scalar::random(rng);
+
+        let mut hasher = blake3::Hasher::new();
+        hasher.update(&H.to_bytes());
+        hasher.update(&gamma.to_bytes());
+        // The paper's B generator we use is NullifierK as that's used for
+        // SecretKey -> PublicKey derivation.
+        hasher.update(&(NullifierK.generator() * k).to_bytes());
+        hasher.update(&(H * k).to_bytes());
+        let c = hasher.finalize();
+
+        let mut c_scalar = [0u8; 64];
+        c_scalar[..blake3::OUT_LEN].copy_from_slice(c.as_bytes());
+        let c_scalar = pallas::Scalar::from_uniform_bytes(&c_scalar);
+
+        let s = k + c_scalar * mod_r_p(x.inner());
+
+        Self { gamma, c, s }
+    }
+
+    /// Verify a `VrfProof` given a `Publickey` and a seed input `alpha_string`.
+    pub fn verify(&self, Y: PublicKey, alpha_string: &[u8]) -> bool {
+        let mut message = vec![];
+        message.extend_from_slice(&Y.to_bytes());
+        message.extend_from_slice(alpha_string);
+        let H = pallas::Point::hash_to_curve(VRF_DOMAIN)(&message);
+
+        let mut c = [0u8; 64];
+        c[..blake3::OUT_LEN].copy_from_slice(self.c.as_bytes());
+        let c_scalar = pallas::Scalar::from_uniform_bytes(&c);
+
+        let U = NullifierK.generator() * self.s - Y.inner() * c_scalar;
+        let V = H * self.s - self.gamma * c_scalar;
+
+        let mut hasher = blake3::Hasher::new();
+        hasher.update(&H.to_bytes());
+        hasher.update(&self.gamma.to_bytes());
+        hasher.update(&U.to_bytes());
+        hasher.update(&V.to_bytes());
+
+        hasher.finalize() == self.c
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use rand::rngs::OsRng;
+
+    #[test]
+    fn ecvrf() {
+        // VRF secret key
+        let secret_key = SecretKey::random(&mut OsRng);
+        // VRF public key
+        let public_key = PublicKey::from_secret(secret_key);
+        // VRF input
+        let input = [0xde, 0xad, 0xbe, 0xef];
+
+        let proof = VrfProof::prove(secret_key, &input, &mut OsRng);
+        assert!(proof.verify(public_key, &input));
+
+        // Forged public key
+        let forged_public_key = PublicKey::from_secret(SecretKey::random(&mut OsRng));
+        assert!(!proof.verify(forged_public_key, &input));
+
+        // Forged input
+        let forged_input = [0xde, 0xad, 0xba, 0xbe];
+        assert!(!proof.verify(public_key, &forged_input));
+    }
+}

+ 3 - 0
src/sdk/src/crypto/mod.rs

@@ -68,6 +68,9 @@ pub mod schnorr;
 /// MiMC VDF
 pub mod mimc_vdf;
 
+/// Elliptic curve VRF (Verifiable Random Function)
+pub mod ecvrf;
+
 /// Sparse Merkle Tree implementation
 pub mod smt;