Skip to main content

dcrypt_algorithms/ec/p384/
scalar.rs

1//! P-384 scalar arithmetic operations
2
3use crate::ec::p384::constants::P384_SCALAR_SIZE;
4use crate::error::{validate, Error, Result};
5use dcrypt_common::security::SecretBuffer;
6use dcrypt_params::traditional::ecdsa::NIST_P384;
7use subtle::{Choice, ConditionallySelectable};
8use zeroize::{Zeroize, ZeroizeOnDrop};
9
10/// P-384 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, Zeroize, ZeroizeOnDrop, Debug)]
15pub struct Scalar(SecretBuffer<P384_SCALAR_SIZE>);
16
17impl Scalar {
18    /// Create a scalar from raw bytes with modular reduction
19    ///
20    /// Ensures the scalar is in the valid range [1, n-1] where n is the curve order.
21    /// Performs modular reduction if the input is >= n.
22    /// Returns an error if the result would be zero (invalid for cryptographic use).
23    pub fn new(mut data: [u8; P384_SCALAR_SIZE]) -> Result<Self> {
24        Self::reduce_scalar_bytes(&mut data)?;
25        Ok(Scalar(SecretBuffer::new(data)))
26    }
27
28    /// Internal constructor that allows zero values
29    ///
30    /// Used for intermediate arithmetic operations where zero is a valid result.
31    /// Should NOT be used for secret keys, nonces, or final signature components.
32    fn from_bytes_unchecked(bytes: [u8; P384_SCALAR_SIZE]) -> Self {
33        Scalar(SecretBuffer::new(bytes))
34    }
35
36    /// Create a scalar from an existing SecretBuffer
37    ///
38    /// Performs the same validation and reduction as `new()` but starts
39    /// from a SecretBuffer instead of a raw byte array.
40    pub fn from_secret_buffer(buffer: SecretBuffer<P384_SCALAR_SIZE>) -> Result<Self> {
41        let mut bytes = [0u8; P384_SCALAR_SIZE];
42        bytes.copy_from_slice(buffer.as_ref());
43
44        Self::reduce_scalar_bytes(&mut bytes)?;
45        Ok(Scalar(SecretBuffer::new(bytes)))
46    }
47
48    /// Access the underlying SecretBuffer containing the scalar value
49    pub fn as_secret_buffer(&self) -> &SecretBuffer<P384_SCALAR_SIZE> {
50        &self.0
51    }
52
53    /// Serialize the scalar to a byte array
54    ///
55    /// Returns the scalar in big-endian byte representation.
56    /// The output is suitable for storage or transmission.
57    pub fn serialize(&self) -> [u8; P384_SCALAR_SIZE] {
58        let mut result = [0u8; P384_SCALAR_SIZE];
59        result.copy_from_slice(self.0.as_ref());
60        result
61    }
62
63    /// Deserialize a scalar from bytes with validation
64    ///
65    /// Parses bytes as a big-endian scalar value and ensures it's
66    /// in the valid range for P-384 operations.
67    pub fn deserialize(bytes: &[u8]) -> Result<Self> {
68        validate::length("P-384 Scalar", bytes.len(), P384_SCALAR_SIZE)?;
69
70        let mut scalar_bytes = [0u8; P384_SCALAR_SIZE];
71        scalar_bytes.copy_from_slice(bytes);
72
73        Self::new(scalar_bytes)
74    }
75
76    /// Check if the scalar represents zero
77    ///
78    /// Constant-time check to determine if the scalar is the
79    /// additive identity (which is invalid for most cryptographic operations).
80    pub fn is_zero(&self) -> bool {
81        self.0.as_ref().iter().all(|&b| b == 0)
82    }
83
84    /// Convert big-endian 48-byte array → 12 little-endian u32 limbs
85    #[inline(always)]
86    fn to_le_limbs(bytes_be: &[u8; 48]) -> [u32; 12] {
87        let mut limbs = [0u32; 12];
88        for (i, limb) in limbs.iter_mut().enumerate() {
89            // MS limb first ⇒ start index counts back from the end
90            let start = 44 - i * 4;
91            *limb = u32::from_le_bytes([
92                bytes_be[start + 3],
93                bytes_be[start + 2],
94                bytes_be[start + 1],
95                bytes_be[start],
96            ]);
97        }
98        limbs
99    }
100
101    /// Convert 12 little-endian limbs back to big-endian 48-byte array  
102    /// (inverse of `to_le_limbs`)
103    #[inline(always)]
104    fn limbs_to_be(limbs: &[u32; 12]) -> [u8; 48] {
105        let mut out = [0u8; 48];
106        for (i, &w) in limbs.iter().enumerate() {
107            let le = w.to_le_bytes();
108            let start = 44 - i * 4;
109            out[start] = le[3];
110            out[start + 1] = le[2];
111            out[start + 2] = le[1];
112            out[start + 3] = le[0];
113        }
114        out
115    }
116
117    /// Add two scalars modulo the curve order n
118    pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
119        let a = Self::to_le_limbs(&self.serialize());
120        let b = Self::to_le_limbs(&other.serialize());
121
122        let mut r = [0u32; 12];
123        let mut carry = 0u64;
124
125        // plain 384-bit addition
126        for i in 0..12 {
127            let tmp = a[i] as u64 + b[i] as u64 + carry;
128            r[i] = tmp as u32;
129            carry = tmp >> 32;
130        }
131
132        let unreduced = Self::from_bytes_unchecked(Self::limbs_to_be(&r));
133        let mut reduced = r;
134        let borrow = Self::sub_in_place(&mut reduced, &Self::N_LIMBS);
135        let need_reduce = Choice::from((carry as u8) | ((borrow ^ 1) as u8));
136
137        Ok(Self::conditional_select(
138            &unreduced,
139            &Self::from_bytes_unchecked(Self::limbs_to_be(&reduced)),
140            need_reduce,
141        ))
142    }
143
144    /// Subtract two scalars modulo the curve order n
145    pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
146        let a = Self::to_le_limbs(&self.serialize());
147        let b = Self::to_le_limbs(&other.serialize());
148
149        let mut r = [0u32; 12];
150        let mut borrow = 0u64;
151
152        for (i, r_limb) in r.iter_mut().enumerate() {
153            let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
154            *r_limb = tmp as u32;
155            borrow = (tmp >> 63) & 1;
156        }
157
158        let unreduced = Self::from_bytes_unchecked(Self::limbs_to_be(&r));
159        let mut reduced = r;
160        let mut carry = 0u64;
161        for (i, r_limb) in reduced.iter_mut().enumerate() {
162            let tmp = *r_limb as u64 + Self::N_LIMBS[i] as u64 + carry;
163            *r_limb = tmp as u32;
164            carry = tmp >> 32;
165        }
166
167        Ok(Self::conditional_select(
168            &unreduced,
169            &Self::from_bytes_unchecked(Self::limbs_to_be(&reduced)),
170            Choice::from(borrow as u8),
171        ))
172    }
173
174    /// Multiply two scalars modulo the curve order n
175    ///
176    /// Uses constant-time double-and-add algorithm for correctness and security.
177    /// Processes bits from MSB to LSB to ensure correct powers of 2.
178    pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
179        // Start with zero (additive identity)
180        let mut acc = Self::from_bytes_unchecked([0u8; P384_SCALAR_SIZE]);
181
182        // Process each bit from MSB to LSB
183        for byte in other.serialize() {
184            for i in (0..8).rev() {
185                // MSB first within each byte
186                // Double the accumulator: acc = acc * 2 (mod n)
187                acc = acc.add_mod_n(&acc)?;
188
189                let acc_plus_self = acc.add_mod_n(self)?;
190                let choice = Choice::from((byte >> i) & 1);
191                acc = Self::conditional_select(&acc, &acc_plus_self, choice);
192            }
193        }
194
195        Ok(acc)
196    }
197
198    /// Compute multiplicative inverse modulo n using Fermat's little theorem
199    pub fn inv_mod_n(&self) -> Result<Self> {
200        // Fast fail on zero - no multiplicative inverse exists
201        if self.is_zero() {
202            return Err(Error::param("P-384 Scalar", "Cannot invert zero scalar"));
203        }
204
205        // n-2 for P-384 in big-endian
206        // n = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973
207        // n-2 = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52971
208        const N_MINUS_2: [u8; 48] = [
209            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
210            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x63, 0x4D, 0x81,
211            0xF4, 0x37, 0x2D, 0xDF, 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC,
212            0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x71,
213        ];
214
215        let mut one_bytes = [0x00; 48];
216        one_bytes[47] = 0x01;
217        let mut result = Self::new(one_bytes)?;
218        let base = self.clone();
219
220        for byte in N_MINUS_2 {
221            for bit in (0..8).rev() {
222                result = result.mul_mod_n(&result)?;
223                if (byte >> bit) & 1 == 1 {
224                    result = result.mul_mod_n(&base)?;
225                }
226            }
227        }
228
229        Ok(result)
230    }
231
232    /// Compute the additive inverse (negation) modulo n
233    ///
234    /// Returns -self mod n, which is equivalent to n - self when self != 0
235    /// Returns 0 when self is 0
236    pub fn negate(&self) -> Self {
237        // If self is zero, return zero
238        if self.is_zero() {
239            return Self::from_bytes_unchecked([0u8; P384_SCALAR_SIZE]);
240        }
241
242        // Otherwise compute n - self
243        let n_limbs = Self::N_LIMBS;
244        let self_limbs = Self::to_le_limbs(&self.serialize());
245        let mut res = [0u32; 12];
246
247        // Subtract self from n
248        let mut borrow = 0i64;
249        for i in 0..12 {
250            let tmp = n_limbs[i] as i64 - self_limbs[i] as i64 - borrow;
251            if tmp < 0 {
252                res[i] = (tmp + (1i64 << 32)) as u32;
253                borrow = 1;
254            } else {
255                res[i] = tmp as u32;
256                borrow = 0;
257            }
258        }
259
260        // No borrow should occur since self < n
261        debug_assert_eq!(borrow, 0);
262
263        Self::from_bytes_unchecked(Self::limbs_to_be(&res))
264    }
265
266    // Private helper methods
267
268    /// Reduce scalar modulo the curve order n using constant-time arithmetic
269    ///
270    /// The curve order n for P-384 is:
271    /// n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973
272    ///
273    /// Algorithm:
274    /// 1. Check if input is zero (invalid)
275    /// 2. Compare with curve order using constant-time comparison
276    /// 3. Conditionally subtract n if input >= n
277    /// 4. Verify result is still non-zero
278    fn reduce_scalar_bytes(bytes: &mut [u8; P384_SCALAR_SIZE]) -> Result<()> {
279        let order = &NIST_P384.n;
280
281        // Reject zero scalars immediately
282        if bytes.iter().all(|&b| b == 0) {
283            return Err(Error::param("P-384 Scalar", "Scalar cannot be zero"));
284        }
285
286        // Constant-time comparison with curve order
287        // We want to check: is bytes >= order?
288        let mut gt = 0u8; // set if bytes > order
289        let mut lt = 0u8; // set if bytes < order
290
291        for i in 0..P384_SCALAR_SIZE {
292            let x = bytes[i];
293            let y = order[i];
294            gt |= ((x > y) as u8) & (!lt);
295            lt |= ((x < y) as u8) & (!gt);
296        }
297
298        if gt == 1 || (lt == 0 && gt == 0) {
299            // If scalar >= order, perform modular reduction
300            let mut borrow = 0u16;
301            let mut temp_bytes = *bytes;
302
303            for i in (0..P384_SCALAR_SIZE).rev() {
304                let diff = (temp_bytes[i] as i16) - (order[i] as i16) - (borrow as i16);
305                if diff < 0 {
306                    temp_bytes[i] = (diff + 256) as u8;
307                    borrow = 1;
308                } else {
309                    temp_bytes[i] = diff as u8;
310                    borrow = 0;
311                }
312            }
313
314            *bytes = temp_bytes;
315        }
316
317        // Check for zero after reduction
318        if bytes.iter().all(|&b| b == 0) {
319            return Err(Error::param(
320                "P-384 Scalar",
321                "Reduction resulted in zero scalar",
322            ));
323        }
324
325        Ok(())
326    }
327
328    // Helper constants
329    // The curve order n for P-384 in little-endian limbs
330    // n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973
331    const N_LIMBS: [u32; 12] = [
332        0xCCC5_2973,
333        0xECEC_196A,
334        0x48B0_A77A,
335        0x581A_0DB2,
336        0xF437_2DDF,
337        0xC763_4D81,
338        0xFFFF_FFFF,
339        0xFFFF_FFFF,
340        0xFFFF_FFFF,
341        0xFFFF_FFFF,
342        0xFFFF_FFFF,
343        0xFFFF_FFFF,
344    ];
345
346    #[inline(always)]
347    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
348        let a_bytes = a.serialize();
349        let b_bytes = b.serialize();
350        let mut out = [0u8; P384_SCALAR_SIZE];
351        for i in 0..P384_SCALAR_SIZE {
352            out[i] = u8::conditional_select(&a_bytes[i], &b_bytes[i], choice);
353        }
354        Self::from_bytes_unchecked(out)
355    }
356
357    /// constant-time compare:  a ≥ b ?
358    #[inline(always)]
359    fn geq(a: &[u32; 12], b: &[u32; 12]) -> bool {
360        for i in (0..12).rev() {
361            if a[i] > b[i] {
362                return true;
363            }
364            if a[i] < b[i] {
365                return false;
366            }
367        }
368        true // equal
369    }
370
371    /// a ← a − b   (little-endian limbs), ignores final borrow
372    #[inline(always)]
373    fn sub_in_place(a: &mut [u32; 12], b: &[u32; 12]) -> u64 {
374        let mut borrow = 0u64;
375        for i in 0..12 {
376            let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
377            a[i] = tmp as u32;
378            borrow = (tmp >> 63) & 1;
379        }
380        borrow
381    }
382}