dcrypt_algorithms/ec/p192/
scalar.rs

1//! P-192 scalar arithmetic operations
2
3use crate::ec::p192::constants::P192_SCALAR_SIZE;
4use crate::error::{validate, Error, Result};
5use dcrypt_common::security::SecretBuffer;
6use dcrypt_params::traditional::ecdsa::NIST_P192;
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9/// P-192 scalar: integers mod n, where
10/// n = 0xFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF (curve order).
11#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug)]
12pub struct Scalar(SecretBuffer<P192_SCALAR_SIZE>);
13
14impl Scalar {
15    /// Create a scalar from raw bytes with reduction mod n.
16    /// Ensures result ∈ [1, n−1]. Errors if result = 0.
17    pub fn new(mut data: [u8; P192_SCALAR_SIZE]) -> Result<Self> {
18        Self::reduce_scalar_bytes(&mut data)?;
19        Ok(Scalar(SecretBuffer::new(data)))
20    }
21
22    /// Internal constructor without checking zero
23    fn from_bytes_unchecked(bytes: [u8; P192_SCALAR_SIZE]) -> Self {
24        Scalar(SecretBuffer::new(bytes))
25    }
26
27    /// Create from existing SecretBuffer (applies reduction & zero check)
28    pub fn from_secret_buffer(buffer: SecretBuffer<P192_SCALAR_SIZE>) -> Result<Self> {
29        let mut tmp = [0u8; P192_SCALAR_SIZE];
30        tmp.copy_from_slice(buffer.as_ref());
31        Self::new(tmp)
32    }
33
34    /// Access the underlying SecretBuffer
35    pub fn as_secret_buffer(&self) -> &SecretBuffer<P192_SCALAR_SIZE> {
36        &self.0
37    }
38
39    /// Serialize to big‐endian bytes
40    pub fn serialize(&self) -> [u8; P192_SCALAR_SIZE] {
41        let mut out = [0u8; P192_SCALAR_SIZE];
42        out.copy_from_slice(self.0.as_ref());
43        out
44    }
45
46    /// Deserialize from bytes (with validation)
47    pub fn deserialize(bytes: &[u8]) -> Result<Self> {
48        validate::length("P-192 Scalar", bytes.len(), P192_SCALAR_SIZE)?;
49        let mut tmp = [0u8; P192_SCALAR_SIZE];
50        tmp.copy_from_slice(bytes);
51        Self::new(tmp)
52    }
53
54    /// Is this scalar zero?
55    pub fn is_zero(&self) -> bool {
56        self.0.as_ref().iter().all(|&b| b == 0)
57    }
58
59    /// Convert big‐endian bytes → little‐endian 6 u32 limbs
60    #[inline(always)]
61    fn to_le_limbs(bytes_be: &[u8; 24]) -> [u32; 6] {
62        let mut limbs = [0u32; 6];
63        for (i, limb) in limbs.iter_mut().enumerate() {
64            let offset = (5 - i) * 4; // Start from the end for LE
65            *limb = u32::from_be_bytes([
66                bytes_be[offset],
67                bytes_be[offset + 1],
68                bytes_be[offset + 2],
69                bytes_be[offset + 3],
70            ]);
71        }
72        limbs
73    }
74
75    /// Convert little‐endian 6‐limb → big‐endian bytes
76    #[inline(always)]
77    fn limbs_to_be(limbs: &[u32; 6]) -> [u8; 24] {
78        let mut out = [0u8; 24];
79        for (i, &limb) in limbs.iter().enumerate() {
80            let b = limb.to_be_bytes();
81            let offset = (5 - i) * 4;
82            out[offset..offset + 4].copy_from_slice(&b);
83        }
84        out
85    }
86
87    /// Add two scalars mod n
88    pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
89        let a_limbs = Self::to_le_limbs(&self.serialize());
90        let b_limbs = Self::to_le_limbs(&other.serialize());
91        let mut r = [0u32; 6];
92        let mut carry: u64 = 0;
93        for ((&a_limb, &b_limb), r_limb) in a_limbs.iter().zip(b_limbs.iter()).zip(r.iter_mut()) {
94            let tmp = a_limb as u64 + b_limb as u64 + carry;
95            *r_limb = tmp as u32;
96            carry = tmp >> 32;
97        }
98        // If overflow OR r ≥ n, subtract n
99        if carry == 1 || Self::geq(&r, &Self::N_LIMBS) {
100            Self::sub_in_place(&mut r, &Self::N_LIMBS);
101        }
102        Ok(Self::from_bytes_unchecked(Self::limbs_to_be(&r)))
103    }
104
105    /// Subtract two scalars mod n
106    pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
107        let a_limbs = Self::to_le_limbs(&self.serialize());
108        let b_limbs = Self::to_le_limbs(&other.serialize());
109        let mut r = [0u32; 6];
110        let mut borrow: i64 = 0;
111        for ((&a_limb, &b_limb), r_limb) in a_limbs.iter().zip(b_limbs.iter()).zip(r.iter_mut()) {
112            let tmp = a_limb as i64 - b_limb as i64 - borrow;
113            if tmp < 0 {
114                *r_limb = (tmp + (1i64 << 32)) as u32;
115                borrow = 1;
116            } else {
117                *r_limb = tmp as u32;
118                borrow = 0;
119            }
120        }
121        if borrow == 1 {
122            // Add n back
123            let mut c: u64 = 0;
124            for (&n_limb, r_limb) in Self::N_LIMBS.iter().zip(r.iter_mut()) {
125                let tmp = *r_limb as u64 + n_limb as u64 + c;
126                *r_limb = tmp as u32;
127                c = tmp >> 32;
128            }
129        }
130        Ok(Self::from_bytes_unchecked(Self::limbs_to_be(&r)))
131    }
132
133    /// Multiply two scalars mod n (double‐and‐add)
134    pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
135        let mut acc = Self::from_bytes_unchecked([0u8; 24]);
136        let self_val = self.clone();
137
138        for &byte in other.serialize().iter() {
139            for i in (0..8).rev() {
140                acc = acc.add_mod_n(&acc)?; // Double
141                if ((byte >> i) & 1) == 1 {
142                    acc = acc.add_mod_n(&self_val)?; // Add
143                }
144            }
145        }
146        Ok(acc)
147    }
148
149    /// Compute inverse mod n via Fermat (n − 2)
150    pub fn inv_mod_n(&self) -> Result<Self> {
151        if self.is_zero() {
152            return Err(Error::param("P-192 Scalar", "Inverse of zero"));
153        }
154
155        // P-192 curve order n = FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831
156        // n-2 = FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D2282F
157        const N_MINUS_2: [u8; 24] = [
158            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xDE,
159            0xF8, 0x36, 0x14, 0x6B, 0xC9, 0xB1, 0xB4, 0xD2, 0x28, 0x2F,
160        ];
161
162        // Binary exponentiation
163        let mut result = {
164            let mut one = [0u8; 24];
165            one[23] = 1;
166            Scalar::from_bytes_unchecked(one)
167        };
168        let base = self.clone();
169
170        for &byte in N_MINUS_2.iter() {
171            for i in (0..8).rev() {
172                result = result.mul_mod_n(&result)?; // Square
173                if ((byte >> i) & 1) == 1 {
174                    result = result.mul_mod_n(&base)?; // Multiply
175                }
176            }
177        }
178
179        Ok(result)
180    }
181
182    /// Negate mod n: (n - self) if ≠ 0
183    pub fn negate(&self) -> Self {
184        if self.is_zero() {
185            return Self::from_bytes_unchecked([0u8; 24]);
186        }
187        let a_limbs = Self::to_le_limbs(&self.serialize());
188        let mut r = [0u32; 6];
189        let n = Self::N_LIMBS;
190        let mut borrow: i64 = 0;
191        for ((&n_limb, &a_limb), r_limb) in n.iter().zip(a_limbs.iter()).zip(r.iter_mut()) {
192            let tmp = n_limb as i64 - a_limb as i64 - borrow;
193            if tmp < 0 {
194                *r_limb = (tmp + (1i64 << 32)) as u32;
195                borrow = 1;
196            } else {
197                *r_limb = tmp as u32;
198                borrow = 0;
199            }
200        }
201        Self::from_bytes_unchecked(Self::limbs_to_be(&r))
202    }
203
204    /// Internal helper: reduce raw bytes mod n, ensure ≠ 0
205    fn reduce_scalar_bytes(bytes: &mut [u8; 24]) -> Result<()> {
206        let order = &NIST_P192.n;
207        // reject zero
208        if bytes.iter().all(|&b| b == 0) {
209            return Err(Error::param("P-192 Scalar", "Scalar cannot be zero"));
210        }
211        // compare bytes vs order big‐endian
212        let mut gt = 0u8;
213        let mut lt = 0u8;
214        for i in 0..24 {
215            let x = bytes[i];
216            let y = order[i];
217            gt |= ((x > y) as u8) & (!lt);
218            lt |= ((x < y) as u8) & (!gt);
219        }
220        // if ≥ order, subtract order
221        if gt == 1 || (lt == 0 && gt == 0) {
222            let mut borrow = 0u16;
223            for i in (0..24).rev() {
224                let v = (bytes[i] as i16) - (order[i] as i16) - (borrow as i16);
225                if v < 0 {
226                    bytes[i] = (v + 256) as u8;
227                    borrow = 1;
228                } else {
229                    bytes[i] = v as u8;
230                    borrow = 0;
231                }
232            }
233        }
234        // ensure not zero after reduction
235        if bytes.iter().all(|&b| b == 0) {
236            return Err(Error::param("P-192 Scalar", "Reduction resulted in zero"));
237        }
238        Ok(())
239    }
240
241    /// Compare two 6‐limb arrays: a ≥ b ?
242    #[inline(always)]
243    fn geq(a: &[u32; 6], b: &[u32; 6]) -> bool {
244        for i in (0..6).rev() {
245            if a[i] > b[i] {
246                return true;
247            }
248            if a[i] < b[i] {
249                return false;
250            }
251        }
252        true
253    }
254
255    /// Subtract b from a in‐place, ignoring final borrow
256    #[inline(always)]
257    fn sub_in_place(a: &mut [u32; 6], b: &[u32; 6]) {
258        let mut borrow = 0u64;
259        for (a_limb, &b_limb) in a.iter_mut().zip(b.iter()) {
260            let tmp = (*a_limb as u64)
261                .wrapping_sub(b_limb as u64)
262                .wrapping_sub(borrow);
263            *a_limb = tmp as u32;
264            borrow = (tmp >> 63) & 1;
265        }
266    }
267
268    /// Order n in little‐endian limbs
269    /// n = FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831
270    const N_LIMBS: [u32; 6] = [
271        0xB4D22831, // least significant 32 bits
272        0x146BC9B1, 0x99DEF836, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, // most significant
273    ];
274}