fp/field_ops.rs
1//! Core trait that every field element in the tower must implement.
2
3use std::ops::{Add, Mul, Neg, Sub};
4use subtle::{Choice, ConditionallySelectable, CtOption};
5
6/// Trait for generating cryptographically secure random field elements.
7///
8/// Separated from [`FieldOps`] so that downstream code that doesn't
9/// need randomness is free of the `rand` dependency.
10pub trait FieldRandom: Sized {
11 /// Sample a uniformly random element using a cryptographic RNG.
12 fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self;
13}
14
15/// Core arithmetic interface for field elements used throughout the library.
16///
17/// This trait abstracts the operations needed by the prime-field layer,
18/// extension fields, and higher-level elliptic-curve code.
19///
20/// It combines:
21/// - basic ring-style operations (`+`, `-`, `*`, unary `-`);
22/// - distinguished constants `zero()` and `one()`;
23/// - predicates such as `is_zero()` and `is_one()`;
24/// - field-specific operations such as inversion, Frobenius, norm, trace,
25/// square roots, and Legendre-symbol-style square testing;
26/// - constant-time conditional selection via `subtle::ConditionallySelectable`.
27///
28/// The trait is intentionally separate from [`FieldRandom`], so downstream code
29/// that only needs deterministic arithmetic does not need to depend on `rand`.
30///
31/// Scalars used by exponentiation methods are encoded as little-endian `u64`
32/// limbs, matching the convention used elsewhere in the library.
33pub trait FieldOps:
34 Sized
35 + Clone
36 + PartialEq
37 + Eq
38 + Add<Output = Self>
39 + Sub<Output = Self>
40 + Mul<Output = Self>
41 + Neg<Output = Self>
42 + Default
43 + ConditionallySelectable
44{
45 /// Create the constant zero
46 fn zero() -> Self;
47
48 /// Create the constant one
49 fn one() -> Self;
50
51 /// Check if element is zero
52 fn is_zero(&self) -> Choice;
53
54 /// Check if element is one
55 fn is_one(&self) -> Choice;
56
57 /// Negate `self` to `-self`
58 fn negate(&self) -> Self;
59
60 /// Add `rhs` to `self`
61 fn add(&self, rhs: &Self) -> Self;
62
63 /// Sub `rhs` from `self`
64 fn sub(&self, rhs: &Self) -> Self;
65
66 /// Multipliy `self` by `rhs`
67 fn mul(&self, rhs: &Self) -> Self;
68
69 /// Square `self`
70 fn square(&self) -> Self;
71
72 /// Double `self`
73 fn double(&self) -> Self;
74
75 /// Invert `self`
76 fn invert(&self) -> CtOption<Self>;
77
78 /// Divide `self` by `rhs`
79 fn div(&self, rhs: &Self) -> CtOption<Self> {
80 rhs.invert().map(|inv| self.mul(&inv))
81 }
82
83 /// `self^exp` using square-and multiply (litte-endian bit order)
84 ///
85 /// It is constant time for fixed `exp`
86 ///
87 /// # Arguments
88 ///
89 /// * `&self` - Finite field element (type: self)
90 /// * `exp` - Exponent (type: &[u64])
91 ///
92 /// # Returns
93 ///
94 /// `&self^exp` (type: Self)
95 ///
96 /// # Why `<Self as FieldOps>::mul` instead of `result.mul(&base)`
97 ///
98 /// `FieldOps` requires `Mul<Output = Self>` as a supertrait, so `Self`
99 /// exposes **two** methods named `mul`:
100 ///
101 /// - `<Self as Mul>::mul(self, rhs: Self) -> Self` ← operator, takes by value
102 /// - `<Self as FieldOps>::mul(&self, rhs: &Self) -> Self` ← ours, takes by ref
103 ///
104 /// Writing `result.mul(&base)` triggers method resolution, which picks
105 /// `Mul::mul` (the operator) because it was declared first in the supertrait
106 /// list. `Mul::mul` expects `Self`, not `&Self` → E0308.
107 ///
108 /// Fully-qualified syntax `<Self as FieldOps>::mul(...)` bypasses method
109 /// resolution entirely and calls exactly the trait method we want.
110 fn pow_vartime(&self, exp: &[u64]) -> Self {
111 let mut result = Self::one();
112 let mut base = self.clone();
113
114 for &limb in exp {
115 let mut limb = limb;
116 for _ in 0..64 {
117 let mybit = limb & 1;
118 let tmp = <Self as FieldOps>::mul(&result, &base);
119 result = Self::conditional_select(&result, &tmp, (mybit as u8).into());
120 base = <Self as FieldOps>::square(&base);
121 limb >>= 1;
122 }
123 }
124 result
125 }
126
127 /// `self^pow` in constant time using a Montgomery ladder
128 ///
129 /// Uses a Montgomery ladder to compute `self^exp`
130 /// WARNING: Only constant time if the number of limbs of exp is
131 /// constant
132 ///
133 /// # Arguments
134 ///
135 /// * `&self` - Element of $\mathbb{F}_p$ (type: self)
136 /// * `exp` - Exponent (type: &[u64])
137 ///
138 /// # Returns
139 ///
140 /// The value `self^pow` (type: Self)
141 ///
142 /// # Todo
143 ///
144 /// Use `subtle` and `conditional_swap` to make true constant time
145 fn pow(&self, exp: &[u64]) -> Self {
146 let mut result = Self::one();
147 let mut base = self.clone();
148
149 for &limb in exp.iter().rev() {
150 let mut limb = limb.reverse_bits();
151 for _ in 0..64 {
152 let mybit = limb & 1;
153 Self::conditional_swap(&mut base, &mut result, (mybit as u8).into());
154 base = <Self as FieldOps>::mul(&result, &base);
155 result = <Self as FieldOps>::square(&result);
156 Self::conditional_swap(&mut base, &mut result, (mybit as u8).into());
157 limb >>= 1;
158 }
159 }
160 result
161 }
162
163 /// Compute `self^p` the frobenius acting on `self`
164 fn frobenius(&self) -> Self;
165
166 /// Compute `self^{p^k}` a power of the frobenius
167 fn frobenius_pow(&self, k: u32) -> Self {
168 let mut result = self.clone();
169 for _ in 0..k {
170 result = result.frobenius();
171 }
172 result
173 }
174
175 /// compute the norm of `self` down to $\mathbb{F}_p$ (as an
176 /// element of type `Self`)
177 fn norm(&self) -> Self;
178
179 /// compute the trace of `self` down to $\mathbb{F}_p$ (as an
180 /// element of type `Self`)
181 fn trace(&self) -> Self;
182
183 /// Returns a squareroot if it exists
184 ///
185 /// Returns a squareroof of `self` if it exists in the finite
186 /// field $\mathbb{F}_{p^M}$. The return type is `CtOption<Self>`
187 /// and it is not none if and only if the squareroot exists. This
188 /// is an implementation fo the Tonelli--Shanks algorithm in the
189 /// multiplicative group $\mathbb{F}_{p^M}^*$
190 ///
191 /// # Arguments
192 ///
193 /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: self)
194 ///
195 /// # Returns
196 ///
197 /// The square root of `self` (type: `CtOption<Self>`)
198 fn sqrt(&self) -> CtOption<Self>;
199
200 /// Computes the inverse and square root of `self`
201 ///
202 /// Computes simulaineously the inverse and square root of `self`.
203 ///
204 /// # Arguments
205 ///
206 /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: self)
207 ///
208 /// # Returns
209 ///
210 /// The inverse and square root of `self`. The former is none if
211 /// and only if nonzero and the latter is not none if and only if
212 /// there exists a squareroot in $\mathbb{F}_{p^M}$
213 /// (type: `(CtOption<Self>, CtOption<self>)`)
214 fn inverse_and_sqrt(&self) -> (CtOption<Self>, CtOption<Self>) {
215 (self.invert(), self.sqrt())
216 }
217
218 /// Computes the square root the inverse of `self`
219 ///
220 /// # Arguments
221 ///
222 /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: self)
223 ///
224 /// # Returns
225 ///
226 /// The square root of the inverse of `self`. The former is not
227 /// none if and only if it is both nonzero there exists a
228 /// squareroot in $\mathbb{F}_{p^M}$ (type: `CtOption<self>`)
229 fn inv_sqrt(&self) -> CtOption<Self> {
230 self.sqrt().and_then(|s| s.invert())
231 }
232
233 /// Computes the inverse of `self` and square root of `rhs`
234 ///
235 /// Computes simulaineously the inverse of `self` and square root
236 /// of `rhs`.
237 ///
238 /// # Arguments
239 ///
240 /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: self)
241 /// * `rhs` - Element of $\mathbb{F}_{p^M}$ (type: &Self)
242 ///
243 /// # Returns
244 ///
245 /// The inverse of `self` and square root fo `rhs`. Theq former is
246 /// none if and only if `self` is nonzero and the latter is not
247 /// none if and only if there exists a squareroot of `rhs` in $\mathbb{F}_{p^M}$
248 /// (type: `(CtOption<Self>, CtOption<self>)`)
249 fn invertme_sqrtother(&self, rhs: &Self) -> (CtOption<Self>, CtOption<Self>) {
250 (self.invert(), rhs.sqrt())
251 }
252
253 /// Computes the squareroot of a ratio `self/rhs`
254 ///
255 /// # Arguments
256 ///
257 /// * `&self` - Element of $\mathbb{F}_{p^M}$ (type: self)
258 /// * `rhs` - Element of $\mathbb{F}_{p^M}$ (type: &Self)
259 ///
260 /// # Returns
261 ///
262 /// The squareroot of the ratio `self/rhs` is not none if and only
263 /// if `rhs` is invertible and the ratio has an $\mathbb{F}_{p^M}$ squareroot
264 /// (type: `(CtOption<Self>, CtOption<self>))`
265 fn sqrt_ratio(&self, rhs: &Self) -> CtOption<Self> {
266 rhs.invert().and_then(|inv_rhs| self.mul(&inv_rhs).sqrt())
267 }
268
269 /// Computes the "Legendre symbol" i.e., if 0,1,-1 depending if
270 /// `self` is 0, a square or a nonsquare.
271 fn legendre(&self) -> i8;
272
273 /// Returns the characteristic of the field.
274 fn characteristic() -> Vec<u64>;
275
276 /// Returns the extension degree of the field.
277 fn degree() -> u32;
278
279 /// Convert u64 to the field.
280 fn from_u64(x: u64) -> Self;
281
282}
283/// Trait for field types that can be constructed from a field-specific
284/// representation.
285pub trait FieldFromRepr: FieldOps {
286 /// The representation type accepted by this field.
287 type Repr;
288
289 /// Constructs a field element from the given representation.
290 fn from_repr(x: Self::Repr) -> Self;
291}