Skip to main content

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