dcrypt_algorithms/ec/p256/
scalar.rs1use crate::ec::p256::constants::P256_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_P256;
9
10#[derive(Clone, Debug)]
15pub struct Scalar(SecretBuffer<P256_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 pub fn new(data: [u8; P256_SCALAR_SIZE]) -> Result<Self> {
39 Self::from_secret_buffer(SecretBuffer::new(data))
40 }
41
42 pub fn from_bytes_reduced(data: [u8; P256_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 fn from_secret_buffer_unchecked(buffer: SecretBuffer<P256_SCALAR_SIZE>) -> Self {
57 Scalar(buffer)
58 }
59
60 pub fn from_secret_buffer(buffer: SecretBuffer<P256_SCALAR_SIZE>) -> Result<Self> {
65 Self::validate_canonical_nonzero(buffer.as_ref())?;
66 Ok(Self::from_secret_buffer_unchecked(buffer))
67 }
68
69 pub fn as_secret_buffer(&self) -> &SecretBuffer<P256_SCALAR_SIZE> {
71 &self.0
72 }
73
74 pub fn serialize(&self) -> SecretBuffer<P256_SCALAR_SIZE> {
80 self.0.clone()
81 }
82
83 pub fn deserialize(bytes: &[u8]) -> Result<Self> {
88 validate::length("P-256 Scalar", bytes.len(), P256_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 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 #[inline(always)]
110 fn to_le_limbs(bytes_be: &[u8]) -> Zeroizing<[u32; 8]> {
111 let mut limbs = Zeroizing::new([0u32; 8]);
112
113 #[allow(clippy::needless_range_loop)] for i in 0..8 {
116 let start = 28 - i * 4; limbs[i] = ((bytes_be[start] as u32) << 24)
118 | ((bytes_be[start + 1] as u32) << 16)
119 | ((bytes_be[start + 2] as u32) << 8)
120 | bytes_be[start + 3] as u32;
121 }
122 limbs
123 }
124
125 pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
127 let self_limbs = Self::to_le_limbs(self.0.as_ref());
128 let other_limbs = Self::to_le_limbs(other.0.as_ref());
129
130 let mut r = Zeroizing::new([0u32; 8]);
131 let mut carry = 0u64;
132
133 #[allow(clippy::needless_range_loop)] for i in 0..8 {
136 let tmp = self_limbs[i] as u64 + other_limbs[i] as u64 + carry;
137 r[i] = tmp as u32;
138 carry = tmp >> 32;
139 }
140
141 let unreduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
142 let borrow = Self::sub_in_place(&mut r, &Self::N_LIMBS);
143 let need_reduce = Choice::from((carry as u8) | ((borrow ^ 1) as u8));
144 let reduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
145
146 Ok(Self::conditional_select(&unreduced, &reduced, need_reduce))
147 }
148
149 pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
151 let self_limbs = Self::to_le_limbs(self.0.as_ref());
152 let other_limbs = Self::to_le_limbs(other.0.as_ref());
153
154 let mut r = Zeroizing::new([0u32; 8]);
155 let mut borrow = 0u64;
156
157 #[allow(clippy::needless_range_loop)] for i in 0..8 {
159 let tmp = (self_limbs[i] as u64)
160 .wrapping_sub(other_limbs[i] as u64)
161 .wrapping_sub(borrow);
162 r[i] = tmp as u32;
163 borrow = (tmp >> 63) & 1;
164 }
165
166 let unreduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
167 let mut carry = 0u64;
168 #[allow(clippy::needless_range_loop)] for i in 0..8 {
170 let tmp = r[i] as u64 + Self::N_LIMBS[i] as u64 + carry;
171 r[i] = tmp as u32;
172 carry = tmp >> 32;
173 }
174 let reduced = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&r));
175
176 Ok(Self::conditional_select(
177 &unreduced,
178 &reduced,
179 Choice::from(borrow as u8),
180 ))
181 }
182
183 pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
188 let mut acc = Self::zero();
190
191 for &byte in other.0.as_ref() {
193 for i in (0..8).rev() {
194 acc = acc.add_mod_n(&acc)?;
197
198 let acc_plus_self = acc.add_mod_n(self)?;
199 let choice = Choice::from((byte >> i) & 1);
200 acc = Self::conditional_select(&acc, &acc_plus_self, choice);
201 }
202 }
203
204 Ok(acc)
205 }
206
207 pub fn inv_mod_n(&self) -> Result<Self> {
210 if self.is_zero() {
212 return Err(Error::param("P-256 Scalar", "Cannot invert zero scalar"));
213 }
214
215 let mut exp = Zeroizing::new(NIST_P256.n); let mut borrow = 2u16;
219 for i in (0..P256_SCALAR_SIZE).rev() {
220 let v = exp[i] as i16 - (borrow as i16);
221 if v < 0 {
222 exp[i] = (v + 256) as u8;
223 borrow = 1;
224 } else {
225 exp[i] = v as u8;
226 borrow = 0;
227 }
228 }
229
230 let mut result = { Self::one() };
236 let base = self.clone();
237
238 for &byte in exp.iter() {
239 for bit in (0..8).rev() {
240 result = result.mul_mod_n(&result)?;
242 if (byte >> bit) & 1 == 1 {
244 result = result.mul_mod_n(&base)?;
245 }
246 }
247 }
248
249 Ok(result)
250 }
251
252 pub fn negate(&self) -> Self {
257 let self_limbs = Self::to_le_limbs(self.0.as_ref());
259 let mut res = Zeroizing::new([0u32; 8]);
260
261 let mut borrow = 0u64;
263 #[allow(clippy::needless_range_loop)] for i in 0..8 {
265 let tmp = (Self::N_LIMBS[i] as u64)
266 .wrapping_sub(self_limbs[i] as u64)
267 .wrapping_sub(borrow);
268 res[i] = tmp as u32;
269 borrow = (tmp >> 63) & 1;
270 }
271 let negated = Self::from_secret_buffer_unchecked(Self::limbs_to_secret_buffer(&res));
272 Self::conditional_select(&negated, &Self::zero(), Choice::from(self.is_zero() as u8))
273 }
274
275 fn reduce_scalar_bytes_allow_zero(bytes: &mut SecretBuffer<P256_SCALAR_SIZE>) {
285 let (candidate, borrow) = Self::subtract_order(bytes.as_ref());
286 let reduce = Choice::from(borrow ^ 1);
287 *bytes = Self::select_secret_buffer(bytes, &candidate, reduce);
288 }
289
290 #[inline(always)]
291 fn subtract_order(bytes: &[u8]) -> (SecretBuffer<P256_SCALAR_SIZE>, u8) {
292 let mut result = SecretBuffer::zeroed();
293 let mut borrow = 0u8;
294 for i in (0..P256_SCALAR_SIZE).rev() {
295 let (difference, borrow_order) = bytes[i].overflowing_sub(NIST_P256.n[i]);
296 let (difference, borrow_previous) = difference.overflowing_sub(borrow);
297 result[i] = difference;
298 borrow = (borrow_order | borrow_previous) as u8;
299 }
300 (result, borrow)
301 }
302
303 fn validate_canonical_nonzero(bytes: &[u8]) -> Result<()> {
304 let mut any = 0u8;
305 for &byte in bytes {
306 any |= byte;
307 }
308 if any == 0 {
309 return Err(Error::param("P-256 Scalar", "Scalar cannot be zero"));
310 }
311
312 let (_, borrow) = Self::subtract_order(bytes);
313 if borrow == 1 {
314 return Ok(());
315 }
316
317 Err(Error::param(
318 "P-256 Scalar",
319 "Scalar must be less than the group order",
320 ))
321 }
322
323 const N_LIMBS: [u32; 8] = [
325 0xFC63_2551,
326 0xF3B9_CAC2,
327 0xA717_9E84,
328 0xBCE6_FAAD,
329 0xFFFF_FFFF,
330 0xFFFF_FFFF,
331 0x0000_0000,
332 0xFFFF_FFFF,
333 ];
334
335 #[inline(never)]
336 fn select_secret_buffer(
337 a: &SecretBuffer<P256_SCALAR_SIZE>,
338 b: &SecretBuffer<P256_SCALAR_SIZE>,
339 choice: Choice,
340 ) -> SecretBuffer<P256_SCALAR_SIZE> {
341 let mut out = SecretBuffer::zeroed();
342 for i in 0..P256_SCALAR_SIZE {
343 out[i] = u8::conditional_select(&a[i], &b[i], choice);
344 }
345 out
346 }
347
348 #[inline(always)]
349 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
350 Self::from_secret_buffer_unchecked(Self::select_secret_buffer(&a.0, &b.0, choice))
351 }
352
353 fn zero() -> Self {
354 Self::from_secret_buffer_unchecked(SecretBuffer::zeroed())
355 }
356
357 fn one() -> Self {
358 let mut one = SecretBuffer::zeroed();
359 one[P256_SCALAR_SIZE - 1] = 1;
360 Self::from_secret_buffer_unchecked(one)
361 }
362
363 #[inline(always)]
365 fn sub_in_place(a: &mut [u32; 8], b: &[u32; 8]) -> u64 {
366 let mut borrow = 0u64;
367 #[allow(clippy::needless_range_loop)] for i in 0..8 {
369 let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
370 a[i] = tmp as u32;
371 borrow = (tmp >> 63) & 1; }
373 borrow
374 }
375
376 #[inline(always)]
379 fn limbs_to_secret_buffer(limbs: &[u32; 8]) -> SecretBuffer<P256_SCALAR_SIZE> {
380 let mut out = SecretBuffer::zeroed();
381 for (i, &w) in limbs.iter().enumerate() {
382 let start = 28 - i * 4;
383 out[start] = (w >> 24) as u8;
384 out[start + 1] = (w >> 16) as u8;
385 out[start + 2] = (w >> 8) as u8;
386 out[start + 3] = w as u8;
387 }
388 out
389 }
390}