use core::fmt::Debug; use std::io::{self, Read}; use rand_core::{RngCore, CryptoRng}; use zeroize::Zeroize; use subtle::ConstantTimeEq; use ff::{Field, PrimeField, PrimeFieldBits}; use group::{Group, GroupOps, GroupEncoding, prime::PrimeGroup}; #[cfg(any(test, feature = "dalek"))] mod dalek; #[cfg(any(test, feature = "ristretto"))] pub use dalek::{Ristretto, IetfRistrettoHram}; #[cfg(feature = "ed25519")] pub use dalek::{Ed25519, IetfEd25519Hram}; #[cfg(feature = "kp256")] mod kp256; #[cfg(feature = "secp256k1")] pub use kp256::{Secp256k1, IetfSecp256k1Hram}; #[cfg(feature = "p256")] pub use kp256::{P256, IetfP256Hram}; #[cfg(feature = "ed448")] mod ed448; #[cfg(feature = "ed448")] pub use ed448::{Ed448, Ietf8032Ed448Hram, IetfEd448Hram}; /// Unified trait to manage an elliptic curve. // This should be moved into its own crate if the need for generic cryptography over ff/group // continues, which is the exact reason ff/group exists (to provide a generic interface) // elliptic-curve exists, yet it doesn't really serve the same role, nor does it use &[u8]/Vec // 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 + Zeroize { /// Scalar field element type. // This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses type F: PrimeField + PrimeFieldBits + Zeroize; /// Group element type. type G: Group + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq; /// ID for this curve. const ID: &'static [u8]; /// Generator for the group. // While group does provide this in its API, privacy coins may want to use a custom basepoint fn generator() -> Self::G; /// Hash the given dst and data to a byte vector. Used to instantiate H4 and H5. fn hash_to_vec(dst: &[u8], data: &[u8]) -> Vec; /// Field element from hash. Used during key gen and by other crates under Serai as a general /// utility. Used to instantiate H1 and H3. #[allow(non_snake_case)] fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F; /// Hash the message for the binding factor. H4 from the IETF draft. fn hash_msg(msg: &[u8]) -> Vec { Self::hash_to_vec(b"msg", msg) } /// Hash the commitments for the binding factor. H5 from the IETF draft. fn hash_commitments(commitments: &[u8]) -> Vec { Self::hash_to_vec(b"com", commitments) } /// Hash the commitments and message to calculate the binding factor. H1 from the IETF draft. fn hash_binding_factor(binding: &[u8]) -> Self::F { Self::hash_to_F(b"rho", binding) } #[allow(non_snake_case)] fn random_F(rng: &mut R) -> Self::F { let mut res; while { res = Self::F::random(&mut *rng); res.ct_eq(&Self::F::zero()).into() } {} res } /// Securely generate a random nonce. H3 from the IETF draft. fn random_nonce(mut secret: Self::F, rng: &mut R) -> Self::F { let mut seed = vec![0; 32]; rng.fill_bytes(&mut seed); let mut repr = secret.to_repr(); secret.zeroize(); let mut res; while { seed.extend(repr.as_ref()); res = Self::hash_to_F(b"nonce", &seed); res.ct_eq(&Self::F::zero()).into() } { rng.fill_bytes(&mut seed); } for i in repr.as_mut() { i.zeroize(); } seed.zeroize(); res } #[allow(non_snake_case)] fn F_len() -> usize { ::Repr::default().as_ref().len() } #[allow(non_snake_case)] fn G_len() -> usize { ::Repr::default().as_ref().len() } #[allow(non_snake_case)] fn read_F(r: &mut R) -> io::Result { let mut encoding = ::Repr::default(); r.read_exact(encoding.as_mut())?; // ff mandates this is canonical let res = Option::::from(Self::F::from_repr(encoding)) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "non-canonical scalar")); for b in encoding.as_mut() { b.zeroize(); } res } #[allow(non_snake_case)] fn read_G(r: &mut R) -> io::Result { let mut encoding = ::Repr::default(); r.read_exact(encoding.as_mut())?; let point = Option::::from(Self::G::from_bytes(&encoding)) .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point"))?; // Ban the identity, per the FROST spec, and non-canonical points if (point.is_identity().into()) || (point.to_bytes().as_ref() != encoding.as_ref()) { Err(io::Error::new(io::ErrorKind::Other, "non-canonical or identity point"))?; } Ok(point) } }