monero_ed25519/
scalar.rs

1use core::ops::DerefMut as _;
2
3#[allow(unused_imports)]
4use std_shims::prelude::*;
5use std_shims::io::{self, *};
6
7use subtle::{Choice, ConstantTimeEq, ConditionallySelectable};
8use zeroize::{Zeroize, Zeroizing};
9
10use rand_core::{RngCore, CryptoRng};
11
12use sha3::{Digest as _, Keccak256};
13
14use monero_io::*;
15
16/// A reduced scalar.
17#[derive(Clone, Copy, Eq, Debug, Zeroize)]
18pub struct Scalar([u8; 32]);
19
20impl ConstantTimeEq for Scalar {
21  fn ct_eq(&self, other: &Self) -> Choice {
22    self.0.ct_eq(&other.0)
23  }
24}
25impl PartialEq for Scalar {
26  /// This defers to `ConstantTimeEq::ct_eq`.
27  fn eq(&self, other: &Self) -> bool {
28    bool::from(self.ct_eq(other))
29  }
30}
31
32impl ConditionallySelectable for Scalar {
33  fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
34    let mut result = [0; 32];
35    #[allow(clippy::needless_range_loop)]
36    for i in 0 .. 32 {
37      result[i] = u8::conditional_select(&a.0[i], &b.0[i], choice);
38    }
39    Self(result)
40  }
41}
42
43impl Scalar {
44  /// The additive identity.
45  pub const ZERO: Self = Self([0; 32]);
46  /// The multiplicative identity.
47  #[rustfmt::skip]
48  pub const ONE: Self = Self([
49    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51  ]);
52  /// The multiplicative inverse of `8 \mod l`.
53  ///
54  /// `l` is defined as the largest prime factor in the amount of points on the Ed25519 elliptic
55  /// curve.
56  ///
57  /// This is useful as part of clearing terms belonging to a small-order subgroup from within a
58  /// point.
59  #[rustfmt::skip]
60  pub const INV_EIGHT: Self = Self([
61    121,  47, 220, 226,  41, 229,   6,  97, 208, 218,  28, 125, 179, 157, 211,   7,
62      0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   6,
63    ]);
64
65  /// Write a Scalar.
66  ///
67  /// This may run in variable time.
68  pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
69    w.write_all(&self.0)
70  }
71
72  /// Read a canonically-encoded scalar.
73  ///
74  /// Some scalars within the Monero protocol are not enforced to be canonically encoded. For such
75  /// scalars, they should be represented as `[u8; 32]` and later converted to scalars as relevant.
76  ///
77  /// This may run in variable time.
78  pub fn read<R: Read>(r: &mut R) -> io::Result<Scalar> {
79    let bytes = read_bytes(r)?;
80    Option::<curve25519_dalek::Scalar>::from(curve25519_dalek::Scalar::from_canonical_bytes(bytes))
81      .ok_or_else(|| io::Error::other("unreduced scalar"))?;
82    Ok(Self(bytes))
83  }
84
85  /// Create a `Scalar` from a `curve25519_dalek::Scalar`.
86  ///
87  /// This is not a public function as it is not part of our API commitment.
88  #[doc(hidden)]
89  pub fn from(scalar: curve25519_dalek::Scalar) -> Self {
90    Self(scalar.to_bytes())
91  }
92
93  /// Create a `curve25519_dalek::Scalar` from a `Scalar`.
94  ///
95  /// This is hidden as it is not part of our API commitment. No guarantees are made for it.
96  #[doc(hidden)]
97  pub fn into(self) -> curve25519_dalek::Scalar {
98    curve25519_dalek::Scalar::from_canonical_bytes(self.0)
99      .expect("`Scalar` instantiated with invalid contents")
100  }
101
102  /// Sample a uniform `Scalar` from an RNG.
103  ///
104  /// This is hidden as it is not part of our API commitment. No guarantees are made for it.
105  #[doc(hidden)]
106  pub fn random(rng: &mut (impl RngCore + CryptoRng)) -> Self {
107    let mut raw = Zeroizing::new([0; 64]);
108    rng.fill_bytes(raw.deref_mut());
109    Self(Zeroizing::new(curve25519_dalek::Scalar::from_bytes_mod_order_wide(&raw)).to_bytes())
110  }
111
112  /// Sample a scalar via hash function.
113  ///
114  /// The implementation of this is `keccak256(data) % l`, where `l` is the largest prime factor in
115  /// the amount of points on the Ed25519 elliptic curve. Notably, this is not a wide reduction.
116  ///
117  /// This function panics if it finds a Keccak-256 preimage for an encoding of a multiple of `l`.
118  pub fn hash(data: impl AsRef<[u8]>) -> Self {
119    let scalar =
120      curve25519_dalek::Scalar::from_bytes_mod_order(Keccak256::digest(data.as_ref()).into());
121
122    /*
123      Monero errors in this case to ensure its integrity, yet its of negligible probability to the
124      degree Monero's integrity will be compromised by _other_ methods much much sooner.
125
126      Accordingly, we don't propagate the error, and simply panic here. The end result is
127      effectively the same: We will not claim proofs which generate zero challenges are valid. We
128      just will panic, instead of flagging them as invalid.
129    */
130    assert!(
131      scalar != curve25519_dalek::Scalar::ZERO,
132      "keccak256(preimage) \\cong 0 \\mod l! Preimage: {:?}",
133      data.as_ref()
134    );
135
136    Self::from(scalar)
137  }
138}
139
140impl From<Scalar> for [u8; 32] {
141  fn from(scalar: Scalar) -> [u8; 32] {
142    scalar.0
143  }
144}