dcrypt_algorithms/ec/p521/scalar.rs
1//! P-521 scalar arithmetic operations
2
3use crate::ec::p521::constants::{p521_bytes_to_limbs, p521_limbs_to_bytes, P521_SCALAR_SIZE};
4use crate::ec::p521::field::FieldElement;
5use crate::error::{validate, Error, Result};
6use dcrypt_common::security::SecretBuffer;
7use dcrypt_params::traditional::ecdsa::NIST_P521;
8use zeroize::{Zeroize, ZeroizeOnDrop};
9
10/// P-521 scalar value for use in elliptic curve operations.
11/// Represents integers modulo the curve order n. Used for private keys
12/// and scalar multiplication. Automatically zeroized on drop for security.
13#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug)]
14pub struct Scalar(SecretBuffer<P521_SCALAR_SIZE>);
15
16impl Scalar {
17 /// Create a scalar from raw bytes with modular reduction.
18 /// Ensures the scalar is in the valid range [1, n-1] where n is the curve order.
19 /// Performs modular reduction if the input is >= n.
20 /// Returns an error if the result would be zero (invalid for cryptographic use).
21 pub fn new(mut data: [u8; P521_SCALAR_SIZE]) -> Result<Self> {
22 Self::reduce_scalar_bytes(&mut data)?;
23 Ok(Scalar(SecretBuffer::new(data)))
24 }
25
26 /// Internal constructor that allows zero values.
27 /// Used for intermediate arithmetic operations where zero is a valid result.
28 /// Should NOT be used for secret keys, nonces, or final signature components.
29 pub(super) fn from_bytes_unchecked(bytes: [u8; P521_SCALAR_SIZE]) -> Self {
30 Scalar(SecretBuffer::new(bytes))
31 }
32
33 /// Create a scalar from an existing SecretBuffer.
34 /// Performs the same validation and reduction as `new()` but starts
35 /// from a SecretBuffer instead of a raw byte array.
36 pub fn from_secret_buffer(buffer: SecretBuffer<P521_SCALAR_SIZE>) -> Result<Self> {
37 let mut bytes = [0u8; P521_SCALAR_SIZE];
38 bytes.copy_from_slice(buffer.as_ref());
39
40 Self::reduce_scalar_bytes(&mut bytes)?;
41 Ok(Scalar(SecretBuffer::new(bytes)))
42 }
43
44 /// Access the underlying SecretBuffer containing the scalar value
45 pub fn as_secret_buffer(&self) -> &SecretBuffer<P521_SCALAR_SIZE> {
46 &self.0
47 }
48
49 /// Serialize the scalar to a byte array.
50 /// Returns the scalar in big-endian byte representation.
51 /// The output is suitable for storage or transmission.
52 pub fn serialize(&self) -> [u8; P521_SCALAR_SIZE] {
53 let mut result = [0u8; P521_SCALAR_SIZE];
54 result.copy_from_slice(self.0.as_ref());
55 result
56 }
57
58 /// Deserialize a scalar from bytes with validation.
59 /// Parses bytes as a big-endian scalar value and ensures it's
60 /// in the valid range for P-521 operations.
61 pub fn deserialize(bytes: &[u8]) -> Result<Self> {
62 validate::length("P-521 Scalar", bytes.len(), P521_SCALAR_SIZE)?;
63
64 let mut scalar_bytes = [0u8; P521_SCALAR_SIZE];
65 scalar_bytes.copy_from_slice(bytes);
66
67 Self::new(scalar_bytes)
68 }
69
70 /// Check if the scalar represents zero.
71 /// Constant-time check to determine if the scalar is the
72 /// additive identity (which is invalid for most cryptographic operations).
73 pub fn is_zero(&self) -> bool {
74 self.0.as_ref().iter().all(|&b| b == 0)
75 }
76
77 /// Convert big-endian 66-byte array to 17 little-endian u32 limbs
78 #[inline(always)]
79 fn to_le_limbs(bytes_be: &[u8; 66]) -> [u32; 17] {
80 p521_bytes_to_limbs(bytes_be)
81 }
82
83 /// Convert 17 little-endian limbs back to big-endian 66-byte array
84 #[inline(always)]
85 fn limbs_to_be(limbs: &[u32; 17]) -> [u8; 66] {
86 p521_limbs_to_bytes(limbs)
87 }
88
89 /// Add two scalars modulo the curve order n
90 pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
91 let a = Self::to_le_limbs(&self.serialize());
92 let b = Self::to_le_limbs(&other.serialize());
93
94 let (mut r, carry) = FieldElement::adc_n(a, b);
95
96 // if overflowed OR r >= n ⇒ subtract n once
97 if carry == 1 || Self::geq(&r, &Self::N_LIMBS) {
98 Self::sub_in_place(&mut r, &Self::N_LIMBS);
99 }
100
101 Ok(Self::from_bytes_unchecked(Self::limbs_to_be(&r)))
102 }
103
104 /// Subtract two scalars modulo the curve order n
105 pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
106 let a = Self::to_le_limbs(&self.serialize());
107 let b = Self::to_le_limbs(&other.serialize());
108
109 let (mut r, borrow) = FieldElement::sbb_n(a, b);
110
111 // if negative ⇒ add n back
112 if borrow == 1 {
113 let (sum, _) = FieldElement::adc_n(r, Self::N_LIMBS);
114 r = sum;
115 }
116
117 Ok(Self::from_bytes_unchecked(Self::limbs_to_be(&r)))
118 }
119
120 /// Multiply two scalars modulo the curve order n.
121 /// Uses constant-time double-and-add algorithm for correctness and security.
122 /// Processes bits from MSB to LSB to ensure correct powers of 2.
123 pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
124 // Start with zero (additive identity)
125 let mut acc = Self::from_bytes_unchecked([0u8; P521_SCALAR_SIZE]);
126
127 // Process each bit from MSB to LSB
128 for byte in other.serialize() {
129 for i in (0..8).rev() {
130 // MSB first within each byte
131 // Double the accumulator: acc = acc * 2 (mod n)
132 acc = acc.add_mod_n(&acc)?;
133
134 // If bit is set, add self: acc = acc + self (mod n)
135 if (byte >> i) & 1 == 1 {
136 acc = acc.add_mod_n(self)?;
137 }
138 }
139 }
140
141 Ok(acc)
142 }
143
144 /// Compute multiplicative inverse modulo n using Fermat's little theorem
145 /// a^(-1) ≡ a^(n-2) (mod n). Left-to-right binary exponentiation.
146 pub fn inv_mod_n(&self) -> Result<Self> {
147 // zero has no inverse
148 if self.is_zero() {
149 return Err(Error::param("P-521 Scalar", "Cannot invert zero scalar"));
150 }
151
152 // Step 1: form exponent = n-2
153 let mut exp = NIST_P521.n; // big-endian [u8;66]
154 // subtract 2 with borrow
155 let mut borrow = 2u16;
156 for i in (0..P521_SCALAR_SIZE).rev() {
157 let v = exp[i] as i16 - (borrow as i16);
158 if v < 0 {
159 exp[i] = (v + 256) as u8;
160 borrow = 1;
161 } else {
162 exp[i] = v as u8;
163 borrow = 0;
164 }
165 }
166
167 // Step 2: binary exponentiation, left-to-right:
168 let mut result = {
169 let mut one = [0u8; P521_SCALAR_SIZE];
170 one[P521_SCALAR_SIZE - 1] = 1;
171 Self::from_bytes_unchecked(one)
172 };
173 let base = self.clone();
174
175 for byte in exp {
176 for bit in (0..8).rev() {
177 // square
178 result = result.mul_mod_n(&result)?;
179 // multiply if this exp-bit is 1
180 if (byte >> bit) & 1 == 1 {
181 result = result.mul_mod_n(&base)?;
182 }
183 }
184 }
185
186 Ok(result)
187 }
188
189 /// Compute the additive inverse (negation) modulo n
190 /// Returns -self mod n, which is equivalent to n - self when self != 0
191 /// Returns 0 when self is 0
192 pub fn negate(&self) -> Self {
193 // If self is zero, return zero
194 if self.is_zero() {
195 return Self::from_bytes_unchecked([0u8; P521_SCALAR_SIZE]);
196 }
197
198 // Otherwise compute n - self
199 let n_limbs = Self::N_LIMBS;
200 let self_limbs = Self::to_le_limbs(&self.serialize());
201 let mut res = [0u32; 17];
202
203 // Subtract self from n
204 let mut borrow = 0i64;
205 for i in 0..17 {
206 let tmp = n_limbs[i] as i64 - self_limbs[i] as i64 - borrow;
207 if tmp < 0 {
208 res[i] = (tmp + (1i64 << 32)) as u32;
209 borrow = 1;
210 } else {
211 res[i] = tmp as u32;
212 borrow = 0;
213 }
214 }
215
216 // No borrow should occur since self < n
217 debug_assert_eq!(borrow, 0);
218
219 Self::from_bytes_unchecked(Self::limbs_to_be(&res))
220 }
221
222 // Private helper methods
223
224 /// Reduce scalar modulo the curve order n using constant-time arithmetic.
225 /// The curve order n for P-521 is:
226 /// n = 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409
227 ///
228 /// Algorithm:
229 /// 1. Check if input is zero (invalid)
230 /// 2. Compare with curve order using constant-time comparison
231 /// 3. Conditionally subtract n if input >= n
232 /// 4. Verify result is still non-zero
233 ///
234 /// Constant-time "a ≥ b" test on 66-byte big-endian values
235 #[inline(always)]
236 fn ge_be(a: &[u8; P521_SCALAR_SIZE], b: &[u8; P521_SCALAR_SIZE]) -> bool {
237 let mut gt = 0u8;
238 let mut lt = 0u8;
239
240 for i in 0..P521_SCALAR_SIZE {
241 gt |= ((a[i] > b[i]) as u8) & (!lt);
242 lt |= ((a[i] < b[i]) as u8) & (!gt);
243 }
244 // true when a > b OR a == b
245 gt == 1 || (gt == 0 && lt == 0)
246 }
247
248 /* ---------- patched reducer ---------- */
249
250 fn reduce_scalar_bytes(bytes: &mut [u8; P521_SCALAR_SIZE]) -> Result<()> {
251 let order = &NIST_P521.n;
252
253 // reject zero
254 if bytes.iter().all(|&b| b == 0) {
255 return Err(Error::param("P-521 Scalar", "Scalar cannot be zero"));
256 }
257
258 // keep subtracting until bytes < order (constant-time loop)
259 while Self::ge_be(bytes, order) {
260 let mut borrow = 0u16;
261 for i in (0..P521_SCALAR_SIZE).rev() {
262 let diff = bytes[i] as i16 - order[i] as i16 - borrow as i16;
263 if diff < 0 {
264 bytes[i] = (diff + 256) as u8;
265 borrow = 1;
266 } else {
267 bytes[i] = diff as u8;
268 borrow = 0;
269 }
270 }
271 }
272 Ok(())
273 }
274
275 /// n (group order) in 17 little-endian 32-bit limbs
276 const N_LIMBS: [u32; 17] = [
277 0x9138_6409, // limb 0 – least-significant
278 0xBB6F_B71E, // limb 1
279 0x899C_47AE, // limb 2
280 0x3BB5_C9B8, // limb 3
281 0xF709_A5D0, // limb 4
282 0x7FCC_0148, // limb 5
283 0xBF2F_966B, // limb 6
284 0x5186_8783, // limb 7
285 0xFFFF_FFFA, // limb 8
286 0xFFFF_FFFF, // limb 9
287 0xFFFF_FFFF, // limb 10
288 0xFFFF_FFFF, // limb 11
289 0xFFFF_FFFF, // limb 12
290 0xFFFF_FFFF, // limb 13
291 0xFFFF_FFFF, // limb 14
292 0xFFFF_FFFF, // limb 15
293 0x0000_01FF, // limb 16 – most-significant 9 bits
294 ];
295
296 /// Compare two limb arrays for greater-than-or-equal
297 #[inline(always)]
298 fn geq(a: &[u32; 17], b: &[u32; 17]) -> bool {
299 for i in (0..17).rev() {
300 if a[i] > b[i] {
301 return true;
302 }
303 if a[i] < b[i] {
304 return false;
305 }
306 }
307 true // equal
308 }
309
310 /// Subtract b from a in-place
311 #[inline(always)]
312 fn sub_in_place(a: &mut [u32; 17], b: &[u32; 17]) {
313 let mut borrow = 0u64;
314 for i in 0..17 {
315 let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
316 a[i] = tmp as u32;
317 borrow = (tmp >> 63) & 1; // 1 if we wrapped
318 }
319 }
320}