clock_curve_math/extensible/generic_field.rs
1//! Generic field arithmetic implementation.
2//!
3//! This module provides generic field elements and operations that can work
4//! with different field parameters and moduli.
5
6use super::field_parameters::FieldParameters;
7use crate::bigint::BigInt;
8use crate::error::MathError;
9
10/// Generic field element with extensible parameters.
11///
12/// This struct provides a generic field element that can work with
13/// different moduli and limb configurations through the FieldParameters trait.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct GenericFieldElement<P: FieldParameters> {
16 /// The Montgomery representation of the field element
17 value: BigInt,
18 /// Phantom data to tie the element to its parameters
19 _params: core::marker::PhantomData<P>,
20}
21
22#[allow(dead_code)]
23impl<P: FieldParameters> GenericFieldElement<P> {
24 /// Create a new field element from a BigInt.
25 ///
26 /// The value is automatically converted to Montgomery form.
27 pub fn new(value: BigInt) -> Result<Self, MathError> {
28 // Validate the value is in range
29 if value.ct_cmp(&P::modulus()) != core::cmp::Ordering::Less {
30 return Err(MathError::InvalidInput);
31 }
32
33 // For simplicity in the extensible implementation, store values directly
34 // Full Montgomery optimization can be added later
35 Ok(Self {
36 value,
37 _params: core::marker::PhantomData,
38 })
39 }
40
41 /// Create a field element from raw Montgomery representation.
42 ///
43 /// This is unsafe because it doesn't validate the input.
44 pub unsafe fn from_montgomery_unchecked(value: BigInt) -> Self {
45 Self {
46 value,
47 _params: core::marker::PhantomData,
48 }
49 }
50
51 /// Create a field element from raw Montgomery representation with validation.
52 ///
53 /// This validates that the input is in the correct range [0, modulus).
54 pub fn from_montgomery_checked(value: BigInt) -> Result<Self, MathError> {
55 let modulus = P::modulus();
56 if value.ct_cmp(&BigInt::from_u64(0)) == core::cmp::Ordering::Less
57 || value.ct_cmp(&modulus) != core::cmp::Ordering::Less
58 {
59 return Err(MathError::InvalidFieldElement);
60 }
61 Ok(Self {
62 value,
63 _params: core::marker::PhantomData,
64 })
65 }
66
67 /// Get the Montgomery representation of this field element.
68 ///
69 /// Returns a reference to the internal Montgomery-form value.
70 /// Note: In this extensible implementation, values are stored directly
71 /// rather than in full Montgomery form for simplicity.
72 pub fn to_montgomery(&self) -> &BigInt {
73 &self.value
74 }
75
76 /// Convert this field element to regular (non-Montgomery) representation.
77 ///
78 /// Returns the field element value in standard form, not Montgomery form.
79 /// In this extensible implementation, values are stored directly,
80 /// so this simply returns a clone of the internal value.
81 pub fn to_regular(&self) -> BigInt {
82 // For simplicity, values are stored directly
83 self.value.clone()
84 }
85
86 /// Get the modulus for this field.
87 ///
88 /// Returns the prime modulus that defines this finite field.
89 /// This is a static method that delegates to the FieldParameters trait.
90 pub fn modulus() -> BigInt {
91 P::modulus()
92 }
93
94 /// Convert a value to Montgomery form (static version for extensible arithmetic).
95 ///
96 /// This function converts a regular integer value to its Montgomery representation.
97 /// The conversion is done using: value * R mod N = montgomery_mul(value, R²)
98 ///
99 /// # Arguments
100 /// * `value` - The value to convert to Montgomery form
101 /// * `modulus` - The field modulus N
102 /// * `r` - The Montgomery radix R (typically a power of 2)
103 /// * `r2` - R² mod N, the Montgomery constant
104 /// * `n_prime` - The Montgomery inverse of N modulo R
105 ///
106 /// # Returns
107 /// The value converted to Montgomery form
108 fn to_montgomery_static(
109 value: &BigInt,
110 modulus: &BigInt,
111 r: &BigInt,
112 r2: &BigInt,
113 n_prime: &BigInt,
114 ) -> BigInt {
115 // value * R mod N = montgomery_mul(value, R²)
116 Self::montgomery_mul_static(value, r2, modulus, r, n_prime)
117 }
118
119 /// Convert a value from Montgomery form to regular representation (static version).
120 ///
121 /// This function performs Montgomery reduction to convert a Montgomery-form
122 /// value back to its regular integer representation.
123 ///
124 /// # Arguments
125 /// * `value` - The Montgomery-form value to convert
126 /// * `modulus` - The field modulus N
127 /// * `r` - The Montgomery radix R (typically a power of 2)
128 /// * `n_prime` - The Montgomery inverse of N modulo R
129 ///
130 /// # Returns
131 /// The value converted from Montgomery form to regular representation
132 fn from_montgomery_static(
133 value: &BigInt,
134 modulus: &BigInt,
135 r: &BigInt,
136 n_prime: &BigInt,
137 ) -> BigInt {
138 // Montgomery reduction
139 let m = value.mul(n_prime).div_rem(r).1;
140 let u = value.add(&m.mul(modulus)).div_rem(r).1;
141
142 if u.ct_cmp(modulus) != core::cmp::Ordering::Less {
143 u.sub(modulus)
144 } else {
145 u
146 }
147 }
148
149 /// Perform Montgomery multiplication (static version for extensible arithmetic).
150 ///
151 /// Computes (a * b * R^(-1)) mod N using the Montgomery multiplication algorithm.
152 /// This is more efficient than regular modular multiplication for multiple operations.
153 ///
154 /// # Arguments
155 /// * `a` - First operand (in Montgomery form)
156 /// * `b` - Second operand (in Montgomery form)
157 /// * `modulus` - The field modulus N
158 /// * `r` - The Montgomery radix R (typically a power of 2)
159 /// * `n_prime` - The Montgomery inverse of N modulo R
160 ///
161 /// # Returns
162 /// The product (a * b * R^(-1)) mod N in Montgomery form
163 #[allow(clippy::many_single_char_names)]
164 fn montgomery_mul_static(
165 a: &BigInt,
166 b: &BigInt,
167 modulus: &BigInt,
168 r: &BigInt,
169 n_prime: &BigInt,
170 ) -> BigInt {
171 let t = a.mul(b);
172 let m = t.mul(n_prime).div_rem(r).1;
173 let u = t.add(&m.mul(modulus)).div_rem(r).1;
174
175 if u.ct_cmp(modulus) != core::cmp::Ordering::Less {
176 u.sub(modulus)
177 } else {
178 u
179 }
180 }
181
182 /// Perform Montgomery squaring (static version for extensible arithmetic).
183 ///
184 /// Computes (a * a * R^(-1)) mod N using the Montgomery multiplication algorithm.
185 /// This is optimized for squaring operations, which are common in cryptographic computations.
186 ///
187 /// # Arguments
188 /// * `a` - The operand to square (in Montgomery form)
189 /// * `modulus` - The field modulus N
190 /// * `r` - The Montgomery radix R (typically a power of 2)
191 /// * `n_prime` - The Montgomery inverse of N modulo R
192 ///
193 /// # Returns
194 /// The square (a * a * R^(-1)) mod N in Montgomery form
195 fn montgomery_square_static(
196 a: &BigInt,
197 modulus: &BigInt,
198 r: &BigInt,
199 n_prime: &BigInt,
200 ) -> BigInt {
201 Self::montgomery_mul_static(a, a, modulus, r, n_prime)
202 }
203
204 /// Perform Montgomery reduction (static version for extensible arithmetic).
205 ///
206 /// Reduces a value t modulo N using the Montgomery reduction algorithm.
207 /// This converts a value from the range [0, N*R) to [0, N) by computing (t * R^(-1)) mod N.
208 ///
209 /// # Arguments
210 /// * `t` - The value to reduce (typically a product in the range [0, N*R))
211 /// * `modulus` - The field modulus N
212 /// * `r` - The Montgomery radix R (typically a power of 2)
213 /// * `n_prime` - The Montgomery inverse of N modulo R
214 ///
215 /// # Returns
216 /// The reduced value (t * R^(-1)) mod N
217 fn montgomery_reduce_static(
218 t: &BigInt,
219 modulus: &BigInt,
220 r: &BigInt,
221 n_prime: &BigInt,
222 ) -> BigInt {
223 let m = t.mul(n_prime).div_rem(r).1;
224 let u = t.add(&m.mul(modulus)).div_rem(r).1;
225
226 if u.ct_cmp(modulus) != core::cmp::Ordering::Less {
227 u.sub(modulus)
228 } else {
229 u
230 }
231 }
232}
233
234/// Generic field arithmetic operations.
235///
236/// This trait defines the core arithmetic operations that can be performed
237/// on field elements in a finite field. All operations are performed modulo
238/// the field's prime modulus, ensuring results remain within the field.
239pub trait GenericFieldOps<P: FieldParameters> {
240 /// Add two field elements.
241 ///
242 /// Computes (self + rhs) mod modulus. The result is always a valid
243 /// field element in the range [0, modulus).
244 fn add(&self, rhs: &GenericFieldElement<P>) -> GenericFieldElement<P>;
245
246 /// Subtract two field elements.
247 ///
248 /// Computes (self - rhs) mod modulus. If the result would be negative,
249 /// the modulus is added to ensure the result is in [0, modulus).
250 fn sub(&self, rhs: &GenericFieldElement<P>) -> GenericFieldElement<P>;
251
252 /// Multiply two field elements.
253 ///
254 /// Computes (self * rhs) mod modulus. The result is always a valid
255 /// field element in the range [0, modulus).
256 fn mul(&self, rhs: &GenericFieldElement<P>) -> GenericFieldElement<P>;
257
258 /// Compute the multiplicative inverse.
259 ///
260 /// Finds the element x such that (self * x) ≡ 1 mod modulus.
261 /// Returns an error if the element is zero (which has no inverse)
262 /// or if the extended Euclidean algorithm fails.
263 fn inv(&self) -> Result<GenericFieldElement<P>, MathError>;
264
265 /// Compute the square of this field element.
266 ///
267 /// Computes (self * self) mod modulus. This is often more efficient
268 /// than general multiplication and is commonly used in exponentiation algorithms.
269 fn square(&self) -> GenericFieldElement<P>;
270
271 /// Check equality in constant time.
272 ///
273 /// Performs a constant-time comparison to prevent timing attacks.
274 /// Returns true if the two elements represent the same field value.
275 fn ct_eq(&self, rhs: &GenericFieldElement<P>) -> bool;
276
277 /// Check if this element is the additive identity (zero).
278 ///
279 /// Returns true if this element represents 0 in the field.
280 fn is_zero(&self) -> bool;
281}
282
283impl<P: FieldParameters> GenericFieldOps<P> for GenericFieldElement<P> {
284 /// Add two field elements.
285 ///
286 /// Performs modular addition: (self + rhs) mod modulus.
287 /// If the sum exceeds the modulus, it wraps around by subtracting the modulus once.
288 fn add(&self, rhs: &GenericFieldElement<P>) -> GenericFieldElement<P> {
289 let sum = self.value.add(&rhs.value);
290 let reduced = if sum.ct_cmp(&P::modulus()) != core::cmp::Ordering::Less {
291 sum.sub(&P::modulus())
292 } else {
293 sum
294 };
295
296 // The result is reduced modulo the modulus, so it's valid
297 Self::from_montgomery_checked(reduced)
298 .expect("Montgomery reduction should produce valid field element")
299 }
300
301 /// Subtract two field elements.
302 ///
303 /// Performs modular subtraction: (self - rhs) mod modulus.
304 /// If self < rhs, adds the modulus to the difference to ensure a positive result.
305 fn sub(&self, rhs: &GenericFieldElement<P>) -> GenericFieldElement<P> {
306 let diff = if self.value.ct_cmp(&rhs.value) != core::cmp::Ordering::Less {
307 self.value.sub(&rhs.value)
308 } else {
309 // Add modulus to handle underflow
310 let temp = self.value.add(&P::modulus());
311 temp.sub(&rhs.value)
312 };
313
314 // The result is in [0, modulus), so it's valid
315 Self::from_montgomery_checked(diff).unwrap()
316 }
317
318 fn mul(&self, rhs: &GenericFieldElement<P>) -> GenericFieldElement<P> {
319 let product = self.value.mul(&rhs.value);
320 let reduced = product.div_rem(&P::modulus()).1;
321
322 // The result is reduced modulo the modulus, so it's valid
323 Self::from_montgomery_checked(reduced)
324 .expect("Montgomery reduction should produce valid field element")
325 }
326
327 /// Compute the multiplicative inverse using the extended Euclidean algorithm.
328 ///
329 /// Finds x such that (self * x) ≡ 1 mod modulus.
330 /// Uses the extended Euclidean algorithm to compute the modular inverse.
331 /// Returns DivisionByZero error if the element is zero or if no inverse exists.
332 fn inv(&self) -> Result<GenericFieldElement<P>, MathError> {
333 if self.is_zero() {
334 return Err(MathError::DivisionByZero);
335 }
336
337 // For the extensible implementation, compute inverse using extended Euclidean algorithm
338 // This is simpler and works for any modulus
339 let modulus = P::modulus();
340 let mut t = BigInt::from_u64(0);
341 let mut new_t = BigInt::from_u64(1);
342 let mut r = modulus.clone();
343 let mut new_r = self.value.clone();
344
345 while new_r.ct_cmp(&BigInt::from_u64(0)) != core::cmp::Ordering::Equal {
346 let quotient = r.div_rem(&new_r).0;
347 let temp_t = t.clone();
348 t = new_t.clone();
349 new_t = temp_t.sub("ient.mul(&new_t));
350
351 let temp_r = r.clone();
352 r = new_r.clone();
353 new_r = temp_r.sub("ient.mul(&new_r));
354 }
355
356 if r.ct_cmp(&BigInt::from_u64(1)) != core::cmp::Ordering::Equal {
357 return Err(MathError::DivisionByZero); // No inverse exists
358 }
359
360 // Make result positive
361 if t.ct_cmp(&BigInt::from_u64(0)) == core::cmp::Ordering::Less {
362 t = t.add(&modulus);
363 }
364
365 // The result is a valid field element in [0, modulus)
366 Self::from_montgomery_checked(t)
367 }
368
369 /// Compute the square of this field element.
370 ///
371 /// Performs modular squaring: (self * self) mod modulus.
372 /// More efficient than general multiplication for exponentiation algorithms.
373 fn square(&self) -> GenericFieldElement<P> {
374 let squared = self.value.mul(&self.value);
375 let reduced = squared.div_rem(&P::modulus()).1;
376
377 // The result is reduced modulo the modulus, so it's valid
378 Self::from_montgomery_checked(reduced)
379 .expect("Montgomery reduction should produce valid field element")
380 }
381
382 /// Check equality in constant time.
383 ///
384 /// Performs a constant-time comparison of the underlying BigInt values
385 /// to prevent timing-based side-channel attacks.
386 fn ct_eq(&self, rhs: &GenericFieldElement<P>) -> bool {
387 // For BigInt comparison, we need to convert to bytes or use the cmp method
388 // This is a simplified version for now
389 self.value.ct_cmp(&rhs.value) == core::cmp::Ordering::Equal
390 }
391
392 /// Check if this element is the additive identity (zero).
393 ///
394 /// Returns true if this field element represents 0 in the finite field.
395 /// Converts to regular representation and compares with zero.
396 fn is_zero(&self) -> bool {
397 // Check if the regular representation is zero
398 self.to_regular().ct_cmp(&BigInt::from_u64(0)) == core::cmp::Ordering::Equal
399 }
400}