Skip to main content

dcrypt_algorithms/ec/p224/
scalar.rs

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