diff --git a/coins/monero/src/frost.rs b/coins/monero/src/frost.rs index e0cef7c4..0a276288 100644 --- a/coins/monero/src/frost.rs +++ b/coins/monero/src/frost.rs @@ -11,7 +11,7 @@ use curve25519_dalek::{ edwards::EdwardsPoint as DPoint }; -use ff::PrimeField; +use ff::{Field, PrimeField}; use group::Group; use transcript::{Transcript as TranscriptTrait, DigestTranscript}; @@ -59,6 +59,10 @@ impl Curve for Ed25519 { true } + fn random_nonce(_secret: Self::F, rng: &mut R) -> Self::F { + dfg::Scalar::random(rng) + } + // This will already be a keccak256 hash in the case of CLSAG signing, making it fine to simply // return as-is, yet this ensures it's fixed size (a security requirement) and unique regardless // of how it's called/what it's called with diff --git a/crypto/frost/src/lib.rs b/crypto/frost/src/lib.rs index a600466a..54abee1d 100644 --- a/crypto/frost/src/lib.rs +++ b/crypto/frost/src/lib.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use thiserror::Error; +use rand_core::{RngCore, CryptoRng}; + use ff::{Field, PrimeField}; use group::{Group, GroupOps}; @@ -32,7 +34,7 @@ pub enum CurveError { // It uses GenericArray which will hopefully be deprecated as Rust evolves and doesn't offer enough // advantages in the modern day to be worth the hassle -- Kayaba pub trait Curve: Clone + Copy + PartialEq + Eq + Debug { - /// Field element type + /// Scalar field element type // This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses type F: PrimeField; /// Group element type @@ -57,6 +59,9 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug { /// If little endian is used for the scalar field's Repr fn little_endian() -> bool; + /// Securely generate a random nonce. H4 from the IETF draft + fn random_nonce(secret: Self::F, rng: &mut R) -> Self::F; + /// Hash the message for the binding factor. H3 from the IETF draft // This doesn't actually need to be part of Curve as it does nothing with the curve // This also solely relates to FROST and with a proper Algorithm/HRAM, all projects using @@ -80,7 +85,7 @@ pub trait Curve: Clone + Copy + PartialEq + Eq + Debug { #[allow(non_snake_case)] fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F; - /// Constant size of a serialized field element + /// Constant size of a serialized scalar field element // The alternative way to grab this would be either serializing a junk element and getting its // length or doing a naive division of its BITS property by 8 and assuming a lack of padding #[allow(non_snake_case)] diff --git a/crypto/frost/src/sign.rs b/crypto/frost/src/sign.rs index ae33735b..5ccb139c 100644 --- a/crypto/frost/src/sign.rs +++ b/crypto/frost/src/sign.rs @@ -80,7 +80,10 @@ fn preprocess>( rng: &mut R, params: &mut Params, ) -> PreprocessPackage { - let nonces = [C::F::random(&mut *rng), C::F::random(&mut *rng)]; + let nonces = [ + C::random_nonce(params.view().secret_share(), &mut *rng), + C::random_nonce(params.view().secret_share(), &mut *rng) + ]; let commitments = [C::generator_table() * nonces[0], C::generator_table() * nonces[1]]; let mut serialized = C::G_to_bytes(&commitments[0]); serialized.extend(&C::G_to_bytes(&commitments[1])); diff --git a/crypto/frost/src/tests/literal/p256.rs b/crypto/frost/src/tests/literal/p256.rs index 75aa07bb..c36a046d 100644 --- a/crypto/frost/src/tests/literal/p256.rs +++ b/crypto/frost/src/tests/literal/p256.rs @@ -1,9 +1,9 @@ use core::convert::TryInto; -use rand::rngs::OsRng; +use rand::{RngCore, CryptoRng, rngs::OsRng}; use ff::{Field, PrimeField}; -use group::GroupEncoding; +use group::{Group, GroupEncoding}; use sha2::{digest::Update, Digest, Sha256}; @@ -102,6 +102,13 @@ impl Curve for P256 { false } + fn random_nonce(secret: Self::F, rng: &mut R) -> Self::F { + let mut seed = vec![0; 32]; + rng.fill_bytes(&mut seed); + seed.extend(&secret.to_repr()); + Self::hash_to_F(&[CONTEXT_STRING, b"nonce"].concat(), &seed) + } + fn hash_msg(msg: &[u8]) -> Vec { (&Sha256::new() .chain(CONTEXT_STRING) @@ -151,7 +158,7 @@ impl Curve for P256 { .map_err(|_| CurveError::InvalidLength(33, slice.len()))?; let point = ProjectivePoint::from_bytes(&bytes.into()); - if point.is_none().into() { + if point.is_none().into() || point.unwrap().is_identity().into() { Err(CurveError::InvalidPoint)?; }