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_internal::constant_time::{Choice, ConditionallySelectable};
7use dcrypt_internal::zeroing::{Zeroize, ZeroizeOnDrop, Zeroizing};
8use dcrypt_params::traditional::ecdsa::NIST_P384;
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, Debug)]
15pub struct Scalar(SecretBuffer<P384_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; P384_SCALAR_SIZE]) -> Result<Self> {
37        Self::from_secret_buffer(SecretBuffer::new(data))
38    }
39
40    /// Interpret a 384-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; P384_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<P384_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<P384_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<P384_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<P384_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-384 operations.
87    pub fn deserialize(bytes: &[u8]) -> Result<Self> {
88        validate::length("P-384 Scalar", bytes.len(), P384_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 48-byte array → 12 little-endian u32 limbs
108    #[inline(always)]
109    fn to_le_limbs(bytes_be: &[u8]) -> Zeroizing<[u32; 12]> {
110        let mut limbs = Zeroizing::new([0u32; 12]);
111        for (i, limb) in limbs.iter_mut().enumerate() {
112            // MS limb first ⇒ start index counts back from the end
113            let start = 44 - i * 4;
114            *limb = ((bytes_be[start] as u32) << 24)
115                | ((bytes_be[start + 1] as u32) << 16)
116                | ((bytes_be[start + 2] as u32) << 8)
117                | bytes_be[start + 3] as u32;
118        }
119        limbs
120    }
121
122    /// Convert 12 little-endian limbs back to big-endian 48-byte array  
123    /// (inverse of `to_le_limbs`)
124    #[inline(always)]
125    fn limbs_to_secret_buffer(limbs: &[u32; 12]) -> SecretBuffer<P384_SCALAR_SIZE> {
126        let mut out = SecretBuffer::zeroed();
127        for (i, &w) in limbs.iter().enumerate() {
128            let start = 44 - i * 4;
129            out[start] = (w >> 24) as u8;
130            out[start + 1] = (w >> 16) as u8;
131            out[start + 2] = (w >> 8) as u8;
132            out[start + 3] = w as u8;
133        }
134        out
135    }
136
137    /// Add two scalars modulo the curve order n
138    pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
139        let a = Self::to_le_limbs(self.0.as_ref());
140        let b = Self::to_le_limbs(other.0.as_ref());
141
142        let mut r = Zeroizing::new([0u32; 12]);
143        let mut carry = 0u64;
144
145        // plain 384-bit addition
146        for i in 0..12 {
147            let tmp = a[i] as u64 + b[i] as u64 + carry;
148            r[i] = tmp as u32;
149            carry = tmp >> 32;
150        }
151
152        let unreduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
153        let borrow = Self::sub_in_place(&mut r, &Self::N_LIMBS);
154        let need_reduce = Choice::from((carry as u8) | ((borrow ^ 1) as u8));
155        let reduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
156
157        Ok(Self::conditional_select(&unreduced, &reduced, need_reduce))
158    }
159
160    /// Subtract two scalars modulo the curve order n
161    pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
162        let a = Self::to_le_limbs(self.0.as_ref());
163        let b = Self::to_le_limbs(other.0.as_ref());
164
165        let mut r = Zeroizing::new([0u32; 12]);
166        let mut borrow = 0u64;
167
168        for (i, r_limb) in r.iter_mut().enumerate() {
169            let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
170            *r_limb = tmp as u32;
171            borrow = (tmp >> 63) & 1;
172        }
173
174        let unreduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
175        let mut carry = 0u64;
176        for (i, r_limb) in r.iter_mut().enumerate() {
177            let tmp = *r_limb as u64 + Self::N_LIMBS[i] as u64 + carry;
178            *r_limb = tmp as u32;
179            carry = tmp >> 32;
180        }
181        let reduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
182
183        Ok(Self::conditional_select(
184            &unreduced,
185            &reduced,
186            Choice::from(borrow as u8),
187        ))
188    }
189
190    /// Multiply two scalars modulo the curve order n
191    ///
192    /// Uses constant-time double-and-add algorithm for correctness and security.
193    /// Processes bits from MSB to LSB to ensure correct powers of 2.
194    pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
195        // Start with zero (additive identity)
196        let mut acc = Self::zero();
197
198        // Process each bit from MSB to LSB
199        for &byte in other.0.as_ref() {
200            for i in (0..8).rev() {
201                // MSB first within each byte
202                // Double the accumulator: acc = acc * 2 (mod n)
203                acc = acc.add_mod_n(&acc)?;
204
205                let acc_plus_self = acc.add_mod_n(self)?;
206                let choice = Choice::from((byte >> i) & 1);
207                acc = Self::conditional_select(&acc, &acc_plus_self, choice);
208            }
209        }
210
211        Ok(acc)
212    }
213
214    /// Compute multiplicative inverse modulo n using Fermat's little theorem
215    pub fn inv_mod_n(&self) -> Result<Self> {
216        // Fast fail on zero - no multiplicative inverse exists
217        if self.is_zero() {
218            return Err(Error::param("P-384 Scalar", "Cannot invert zero scalar"));
219        }
220
221        // n-2 for P-384 in big-endian
222        // n = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973
223        // n-2 = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52971
224        const N_MINUS_2: [u8; 48] = [
225            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
226            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x63, 0x4D, 0x81,
227            0xF4, 0x37, 0x2D, 0xDF, 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC,
228            0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x71,
229        ];
230
231        let mut result = Self::one();
232        let base = self.clone();
233
234        for byte in N_MINUS_2 {
235            for bit in (0..8).rev() {
236                result = result.mul_mod_n(&result)?;
237                if (byte >> bit) & 1 == 1 {
238                    result = result.mul_mod_n(&base)?;
239                }
240            }
241        }
242
243        Ok(result)
244    }
245
246    /// Compute the additive inverse (negation) modulo n
247    ///
248    /// Returns -self mod n, which is equivalent to n - self when self != 0
249    /// Returns 0 when self is 0
250    pub fn negate(&self) -> Self {
251        // Compute n - self, then select zero for the zero input.
252        let self_limbs = Self::to_le_limbs(self.0.as_ref());
253        let mut res = Zeroizing::new([0u32; 12]);
254
255        // Subtract self from n
256        let mut borrow = 0u64;
257        for i in 0..12 {
258            let tmp = (Self::N_LIMBS[i] as u64)
259                .wrapping_sub(self_limbs[i] as u64)
260                .wrapping_sub(borrow);
261            res[i] = tmp as u32;
262            borrow = (tmp >> 63) & 1;
263        }
264        let negated = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&res));
265        Self::conditional_select(&negated, &Self::zero(), Choice::from(self.is_zero() as u8))
266    }
267
268    // Private helper methods
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-384 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-384 Scalar",
283                "Scalar must be less than the group order",
284            ));
285        }
286
287        Ok(())
288    }
289
290    /// Reduce an arbitrary 384-bit integer modulo the group order.
291    fn reduce_scalar_bytes_allow_zero(bytes: &mut SecretBuffer<P384_SCALAR_SIZE>) {
292        let (candidate, borrow) = Self::subtract_order(bytes.as_ref());
293        let reduce = Choice::from(borrow ^ 1);
294        *bytes = Self::select_secret_buffer(bytes, &candidate, reduce);
295    }
296
297    #[inline(always)]
298    fn subtract_order(bytes: &[u8]) -> (SecretBuffer<P384_SCALAR_SIZE>, u8) {
299        let mut result = SecretBuffer::zeroed();
300        let mut borrow = 0u8;
301        for i in (0..P384_SCALAR_SIZE).rev() {
302            let (difference, borrow_order) = bytes[i].overflowing_sub(NIST_P384.n[i]);
303            let (difference, borrow_previous) = difference.overflowing_sub(borrow);
304            result[i] = difference;
305            borrow = (borrow_order | borrow_previous) as u8;
306        }
307        (result, borrow)
308    }
309
310    // Helper constants
311    // The curve order n for P-384 in little-endian limbs
312    // n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973
313    const N_LIMBS: [u32; 12] = [
314        0xCCC5_2973,
315        0xECEC_196A,
316        0x48B0_A77A,
317        0x581A_0DB2,
318        0xF437_2DDF,
319        0xC763_4D81,
320        0xFFFF_FFFF,
321        0xFFFF_FFFF,
322        0xFFFF_FFFF,
323        0xFFFF_FFFF,
324        0xFFFF_FFFF,
325        0xFFFF_FFFF,
326    ];
327
328    #[inline(never)]
329    fn select_secret_buffer(
330        a: &SecretBuffer<P384_SCALAR_SIZE>,
331        b: &SecretBuffer<P384_SCALAR_SIZE>,
332        choice: Choice,
333    ) -> SecretBuffer<P384_SCALAR_SIZE> {
334        let mut out = SecretBuffer::zeroed();
335        for i in 0..P384_SCALAR_SIZE {
336            out[i] = u8::conditional_select(&a[i], &b[i], choice);
337        }
338        out
339    }
340
341    #[inline(always)]
342    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
343        Self::from_secret_buffer_unchecked(Self::select_secret_buffer(&a.0, &b.0, choice))
344    }
345
346    fn zero() -> Self {
347        Self::from_secret_buffer_unchecked(SecretBuffer::zeroed())
348    }
349
350    fn one() -> Self {
351        let mut one = SecretBuffer::zeroed();
352        one[P384_SCALAR_SIZE - 1] = 1;
353        Self::from_secret_buffer_unchecked(one)
354    }
355
356    /// a ← a − b   (little-endian limbs), ignores final borrow
357    #[inline(always)]
358    fn sub_in_place(a: &mut [u32; 12], b: &[u32; 12]) -> u64 {
359        let mut borrow = 0u64;
360        for i in 0..12 {
361            let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
362            a[i] = tmp as u32;
363            borrow = (tmp >> 63) & 1;
364        }
365        borrow
366    }
367}