Skip to main content

dcrypt_algorithms/ec/p521/
scalar.rs

1//! P-521 scalar arithmetic operations
2
3use crate::ec::p521::constants::P521_SCALAR_SIZE;
4use crate::error::{validate, Error, Result};
5use dcrypt_common::security::SecretBuffer;
6use dcrypt_internal::constant_time::{Choice, ConditionallySelectable};
7use dcrypt_internal::zeroing::{Zeroize, ZeroizeOnDrop, Zeroizing};
8use dcrypt_params::traditional::ecdsa::NIST_P521;
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, Debug)]
14pub struct Scalar(SecretBuffer<P521_SCALAR_SIZE>);
15
16impl Zeroize for Scalar {
17    fn zeroize(&mut self) {
18        self.0.zeroize();
19    }
20}
21
22impl Drop for Scalar {
23    fn drop(&mut self) {
24        self.zeroize();
25    }
26}
27
28impl ZeroizeOnDrop for Scalar {}
29
30impl Scalar {
31    /// Create a canonical non-zero scalar from raw bytes.
32    ///
33    /// Private keys, nonces, and serialized signature components must be in
34    /// `1..n`. Non-canonical 528-bit encodings are rejected rather than reduced.
35    pub fn new(data: [u8; P521_SCALAR_SIZE]) -> Result<Self> {
36        Self::from_secret_buffer(SecretBuffer::new(data))
37    }
38
39    /// Interpret a 528-bit encoding modulo the group order, including zero.
40    ///
41    /// P-521 scalars use a 66-byte encoding. This constructor is intended for
42    /// standards-defined mathematical intermediates such as ECDSA hash and
43    /// x-coordinate reduction. Use [`Self::new`] for private scalars, nonces,
44    /// and serialized signature components, where zero is invalid.
45    pub fn from_bytes_reduced(data: [u8; P521_SCALAR_SIZE]) -> Self {
46        let mut protected = SecretBuffer::new(data);
47        Self::reduce_scalar_bytes_allow_zero(&mut protected);
48        Self::from_secret_buffer_unchecked(protected)
49    }
50
51    /// Internal constructor that allows zero values.
52    /// Used for intermediate arithmetic operations where zero is a valid result.
53    /// Should NOT be used for secret keys, nonces, or final signature components.
54    fn from_secret_buffer_unchecked(buffer: SecretBuffer<P521_SCALAR_SIZE>) -> Self {
55        Scalar(buffer)
56    }
57
58    /// Create a scalar from an existing SecretBuffer.
59    /// Performs the same canonical validation as `new()` but starts
60    /// from a SecretBuffer instead of a raw byte array.
61    pub fn from_secret_buffer(buffer: SecretBuffer<P521_SCALAR_SIZE>) -> Result<Self> {
62        Self::validate_canonical_nonzero(buffer.as_ref())?;
63        Ok(Self::from_secret_buffer_unchecked(buffer))
64    }
65
66    /// Access the underlying SecretBuffer containing the scalar value
67    pub fn as_secret_buffer(&self) -> &SecretBuffer<P521_SCALAR_SIZE> {
68        &self.0
69    }
70
71    /// Serialize the scalar to protected exact-size storage.
72    /// Returns the scalar in big-endian byte representation.
73    /// The output clears itself on drop. Callers that deliberately expose a
74    /// public signature component may copy from its borrowed slice.
75    pub fn serialize(&self) -> SecretBuffer<P521_SCALAR_SIZE> {
76        self.0.clone()
77    }
78
79    /// Deserialize a scalar from bytes with validation.
80    /// Parses bytes as a big-endian scalar value and ensures it's
81    /// in the valid range for P-521 operations.
82    pub fn deserialize(bytes: &[u8]) -> Result<Self> {
83        validate::length("P-521 Scalar", bytes.len(), P521_SCALAR_SIZE)?;
84
85        let mut protected = SecretBuffer::zeroed();
86        protected.as_mut().copy_from_slice(bytes);
87        Self::from_secret_buffer(protected)
88    }
89
90    /// Check if the scalar represents zero.
91    /// Constant-time check to determine if the scalar is the
92    /// additive identity (which is invalid for most cryptographic operations).
93    pub fn is_zero(&self) -> bool {
94        let mut any = 0u8;
95        for &byte in self.0.as_ref() {
96            any |= byte;
97        }
98        any == 0
99    }
100
101    /// Convert big-endian 66-byte array to 17 little-endian u32 limbs
102    #[inline(always)]
103    fn to_le_limbs(bytes_be: &[u8]) -> Zeroizing<[u32; 17]> {
104        let mut limbs = Zeroizing::new([0u32; 17]);
105        for i in 0..16 {
106            let offset = P521_SCALAR_SIZE - 4 - i * 4;
107            limbs[i] = ((bytes_be[offset] as u32) << 24)
108                | ((bytes_be[offset + 1] as u32) << 16)
109                | ((bytes_be[offset + 2] as u32) << 8)
110                | bytes_be[offset + 3] as u32;
111        }
112        limbs[16] = (((bytes_be[0] as u32) << 8) | bytes_be[1] as u32) & 0x1ff;
113        limbs
114    }
115
116    /// Convert 17 little-endian limbs back to big-endian 66-byte array
117    #[inline(always)]
118    fn limbs_to_secret_buffer(limbs: &[u32; 17]) -> SecretBuffer<P521_SCALAR_SIZE> {
119        let mut out = SecretBuffer::zeroed();
120        for (i, &limb) in limbs.iter().take(16).enumerate() {
121            let offset = P521_SCALAR_SIZE - 4 - i * 4;
122            out[offset] = (limb >> 24) as u8;
123            out[offset + 1] = (limb >> 16) as u8;
124            out[offset + 2] = (limb >> 8) as u8;
125            out[offset + 3] = limb as u8;
126        }
127        let most_significant = limbs[16] & 0x1ff;
128        out[0] = (most_significant >> 8) as u8;
129        out[1] = most_significant as u8;
130        out
131    }
132
133    /// Add two scalars modulo the curve order n
134    pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
135        let a = Self::to_le_limbs(self.0.as_ref());
136        let b = Self::to_le_limbs(other.0.as_ref());
137        let mut r = Zeroizing::new([0u32; 17]);
138        let mut carry = 0u64;
139        for i in 0..17 {
140            let sum = a[i] as u64 + b[i] as u64 + carry;
141            r[i] = sum as u32;
142            carry = sum >> 32;
143        }
144        let unreduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
145        let borrow = Self::sub_in_place(&mut r, &Self::N_LIMBS);
146        let need_reduce = Choice::from((carry as u8) | ((borrow ^ 1) as u8));
147        let reduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
148
149        Ok(Self::conditional_select(&unreduced, &reduced, need_reduce))
150    }
151
152    /// Subtract two scalars modulo the curve order n
153    pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
154        let a = Self::to_le_limbs(self.0.as_ref());
155        let b = Self::to_le_limbs(other.0.as_ref());
156        let mut r = Zeroizing::new([0u32; 17]);
157        let mut borrow = 0u64;
158        for i in 0..17 {
159            let difference = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
160            r[i] = difference as u32;
161            borrow = (difference >> 63) & 1;
162        }
163        let unreduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
164        let mut carry = 0u64;
165        for i in 0..17 {
166            let sum = r[i] as u64 + Self::N_LIMBS[i] as u64 + carry;
167            r[i] = sum as u32;
168            carry = sum >> 32;
169        }
170        let reduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
171
172        Ok(Self::conditional_select(
173            &unreduced,
174            &reduced,
175            Choice::from(borrow as u8),
176        ))
177    }
178
179    /// Multiply two scalars modulo the curve order n.
180    /// Uses constant-time double-and-add algorithm for correctness and security.
181    /// Processes bits from MSB to LSB to ensure correct powers of 2.
182    pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
183        // Start with zero (additive identity)
184        let mut acc = Self::zero();
185
186        // Process each bit from MSB to LSB
187        for &byte in other.0.as_ref() {
188            for i in (0..8).rev() {
189                // MSB first within each byte
190                // Double the accumulator: acc = acc * 2 (mod n)
191                acc = acc.add_mod_n(&acc)?;
192
193                let acc_plus_self = acc.add_mod_n(self)?;
194                let choice = Choice::from((byte >> i) & 1);
195                acc = Self::conditional_select(&acc, &acc_plus_self, choice);
196            }
197        }
198
199        Ok(acc)
200    }
201
202    /// Compute multiplicative inverse modulo n using Fermat's little theorem
203    /// a^(-1) ≡ a^(n-2) (mod n). Left-to-right binary exponentiation.
204    pub fn inv_mod_n(&self) -> Result<Self> {
205        // zero has no inverse
206        if self.is_zero() {
207            return Err(Error::param("P-521 Scalar", "Cannot invert zero scalar"));
208        }
209
210        // Step 1: form exponent = n-2
211        let mut exp = Zeroizing::new(NIST_P521.n); // public, fixed exponent
212                                                   // subtract 2 with borrow
213        let mut borrow = 2u16;
214        for i in (0..P521_SCALAR_SIZE).rev() {
215            let v = exp[i] as i16 - (borrow as i16);
216            if v < 0 {
217                exp[i] = (v + 256) as u8;
218                borrow = 1;
219            } else {
220                exp[i] = v as u8;
221                borrow = 0;
222            }
223        }
224
225        // Step 2: binary exponentiation, left-to-right:
226        let mut result = { Self::one() };
227        let base = self.clone();
228
229        for &byte in exp.iter() {
230            for bit in (0..8).rev() {
231                // square
232                result = result.mul_mod_n(&result)?;
233                // multiply if this exp-bit is 1
234                if (byte >> bit) & 1 == 1 {
235                    result = result.mul_mod_n(&base)?;
236                }
237            }
238        }
239
240        Ok(result)
241    }
242
243    /// Compute the additive inverse (negation) modulo n
244    /// Returns -self mod n, which is equivalent to n - self when self != 0
245    /// Returns 0 when self is 0
246    pub fn negate(&self) -> Self {
247        // Compute n - self, then select zero for the zero input.
248        let self_limbs = Self::to_le_limbs(self.0.as_ref());
249        let mut res = Zeroizing::new([0u32; 17]);
250
251        // Subtract self from n
252        let mut borrow = 0u64;
253        for i in 0..17 {
254            let tmp = (Self::N_LIMBS[i] as u64)
255                .wrapping_sub(self_limbs[i] as u64)
256                .wrapping_sub(borrow);
257            res[i] = tmp as u32;
258            borrow = (tmp >> 63) & 1;
259        }
260        let negated = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&res));
261        Self::conditional_select(&negated, &Self::zero(), Choice::from(self.is_zero() as u8))
262    }
263
264    // Private helper methods
265
266    /// Reduce scalar modulo the curve order n using constant-time arithmetic.
267    /// The curve order n for P-521 is:
268    /// n = 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409
269    ///
270    fn validate_canonical_nonzero(bytes: &[u8]) -> Result<()> {
271        let mut any = 0u8;
272        for &byte in bytes {
273            any |= byte;
274        }
275        if any == 0 {
276            return Err(Error::param("P-521 Scalar", "Scalar cannot be zero"));
277        }
278
279        let (_, borrow) = Self::subtract_order(bytes);
280        if borrow == 0 {
281            return Err(Error::param(
282                "P-521 Scalar",
283                "Scalar must be less than the group order",
284            ));
285        }
286
287        Ok(())
288    }
289
290    /// Reduce an arbitrary 528-bit encoding modulo the group order.
291    fn reduce_scalar_bytes_allow_zero(bytes: &mut SecretBuffer<P521_SCALAR_SIZE>) {
292        for _ in 0..128 {
293            let (candidate, borrow) = Self::subtract_order(bytes.as_ref());
294
295            let choice = Choice::from(borrow ^ 1);
296            *bytes = Self::select_secret_buffer(bytes, &candidate, choice);
297        }
298    }
299
300    #[inline(always)]
301    fn subtract_order(bytes: &[u8]) -> (SecretBuffer<P521_SCALAR_SIZE>, u8) {
302        let mut result = SecretBuffer::zeroed();
303        let mut borrow = 0u8;
304        for i in (0..P521_SCALAR_SIZE).rev() {
305            let (difference, borrow_order) = bytes[i].overflowing_sub(NIST_P521.n[i]);
306            let (difference, borrow_previous) = difference.overflowing_sub(borrow);
307            result[i] = difference;
308            borrow = (borrow_order | borrow_previous) as u8;
309        }
310        (result, borrow)
311    }
312
313    /// n (group order) in 17 little-endian 32-bit limbs
314    const N_LIMBS: [u32; 17] = [
315        0x9138_6409, // limb 0  – least-significant
316        0xBB6F_B71E, // limb 1
317        0x899C_47AE, // limb 2
318        0x3BB5_C9B8, // limb 3
319        0xF709_A5D0, // limb 4
320        0x7FCC_0148, // limb 5
321        0xBF2F_966B, // limb 6
322        0x5186_8783, // limb 7
323        0xFFFF_FFFA, // limb 8
324        0xFFFF_FFFF, // limb 9
325        0xFFFF_FFFF, // limb 10
326        0xFFFF_FFFF, // limb 11
327        0xFFFF_FFFF, // limb 12
328        0xFFFF_FFFF, // limb 13
329        0xFFFF_FFFF, // limb 14
330        0xFFFF_FFFF, // limb 15
331        0x0000_01FF, // limb 16 – most-significant 9 bits
332    ];
333
334    #[inline(never)]
335    fn select_secret_buffer(
336        a: &SecretBuffer<P521_SCALAR_SIZE>,
337        b: &SecretBuffer<P521_SCALAR_SIZE>,
338        choice: Choice,
339    ) -> SecretBuffer<P521_SCALAR_SIZE> {
340        let mut out = SecretBuffer::zeroed();
341        for i in 0..P521_SCALAR_SIZE {
342            out[i] = u8::conditional_select(&a[i], &b[i], choice);
343        }
344        out
345    }
346
347    #[inline(always)]
348    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
349        Self::from_secret_buffer_unchecked(Self::select_secret_buffer(&a.0, &b.0, choice))
350    }
351
352    fn zero() -> Self {
353        Self::from_secret_buffer_unchecked(SecretBuffer::zeroed())
354    }
355
356    fn one() -> Self {
357        let mut one = SecretBuffer::zeroed();
358        one[P521_SCALAR_SIZE - 1] = 1;
359        Self::from_secret_buffer_unchecked(one)
360    }
361
362    #[inline(always)]
363    fn sub_in_place(a: &mut [u32; 17], b: &[u32; 17]) -> u64 {
364        let mut borrow = 0u64;
365        for i in 0..17 {
366            let difference = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
367            a[i] = difference as u32;
368            borrow = (difference >> 63) & 1;
369        }
370        borrow
371    }
372}