ciphersuite/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2#![doc = include_str!("lib.md")]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5use core::fmt::Debug;
6#[cfg(any(feature = "alloc", feature = "std"))]
7#[allow(unused_imports)]
8use std_shims::prelude::*;
9#[cfg(any(feature = "alloc", feature = "std"))]
10use std_shims::io::{self, Read};
11
12use rand_core::{RngCore, CryptoRng};
13
14use zeroize::Zeroize;
15use subtle::ConstantTimeEq;
16
17use digest::{core_api::BlockSizeUser, Digest, HashMarker};
18use transcript::SecureDigest;
19
20pub use group;
21use group::{
22  ff::{Field, PrimeField, PrimeFieldBits},
23  Group, GroupOps,
24  prime::PrimeGroup,
25};
26#[cfg(any(feature = "alloc", feature = "std"))]
27use group::GroupEncoding;
28
29/// Unified trait defining a ciphersuite around an elliptic curve.
30pub trait Ciphersuite:
31  'static + Send + Sync + Clone + Copy + PartialEq + Eq + Debug + Zeroize
32{
33  /// Scalar field element type.
34  // This is available via G::Scalar yet `C::G::Scalar` is ambiguous, forcing horrific accesses
35  type F: PrimeField + PrimeFieldBits + Zeroize;
36  /// Group element type.
37  type G: Group<Scalar = Self::F> + GroupOps + PrimeGroup + Zeroize + ConstantTimeEq;
38  /// Hash algorithm used with this curve.
39  // Requires BlockSizeUser so it can be used within Hkdf which requires that.
40  type H: Send + Clone + BlockSizeUser + Digest + HashMarker + SecureDigest;
41
42  /// ID for this curve.
43  const ID: &'static [u8];
44
45  /// Generator for the group.
46  // While group does provide this in its API, privacy coins may want to use a custom basepoint
47  fn generator() -> Self::G;
48
49  /// Hash the provided domain-separation tag and message to a scalar. Ciphersuites MAY naively
50  /// prefix the tag to the message, enabling transpotion between the two. Accordingly, this
51  /// function should NOT be used in any scheme where one tag is a valid substring of another
52  /// UNLESS the specific Ciphersuite is verified to handle the DST securely.
53  ///
54  /// Verifying specific ciphersuites have secure tag handling is not recommended, due to it
55  /// breaking the intended modularity of ciphersuites. Instead, component-specific tags with
56  /// further purpose tags are recommended ("Schnorr-nonce", "Schnorr-chal").
57  #[allow(non_snake_case)]
58  fn hash_to_F(dst: &[u8], msg: &[u8]) -> Self::F;
59
60  /// Generate a random non-zero scalar.
61  #[allow(non_snake_case)]
62  fn random_nonzero_F<R: RngCore + CryptoRng>(rng: &mut R) -> Self::F {
63    let mut res;
64    while {
65      res = Self::F::random(&mut *rng);
66      res.ct_eq(&Self::F::ZERO).into()
67    } {}
68    res
69  }
70
71  /// Read a canonical scalar from something implementing std::io::Read.
72  #[cfg(any(feature = "alloc", feature = "std"))]
73  #[allow(non_snake_case)]
74  fn read_F<R: Read>(reader: &mut R) -> io::Result<Self::F> {
75    let mut encoding = <Self::F as PrimeField>::Repr::default();
76    reader.read_exact(encoding.as_mut())?;
77
78    // ff mandates this is canonical
79    let res = Option::<Self::F>::from(Self::F::from_repr(encoding))
80      .ok_or_else(|| io::Error::other("non-canonical scalar"));
81    encoding.as_mut().zeroize();
82    res
83  }
84
85  /// Read a canonical point from something implementing std::io::Read.
86  ///
87  /// The provided implementation is safe so long as `GroupEncoding::to_bytes` always returns a
88  /// canonical serialization.
89  #[cfg(any(feature = "alloc", feature = "std"))]
90  #[allow(non_snake_case)]
91  fn read_G<R: Read>(reader: &mut R) -> io::Result<Self::G> {
92    let mut encoding = <Self::G as GroupEncoding>::Repr::default();
93    reader.read_exact(encoding.as_mut())?;
94
95    let point = Option::<Self::G>::from(Self::G::from_bytes(&encoding))
96      .ok_or_else(|| io::Error::other("invalid point"))?;
97    if point.to_bytes().as_ref() != encoding.as_ref() {
98      Err(io::Error::other("non-canonical point"))?;
99    }
100    Ok(point)
101  }
102}