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_params::traditional::ecdsa::NIST_P256;
7use subtle::{Choice, ConditionallySelectable};
8use zeroize::{Zeroize, ZeroizeOnDrop};
9
10#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug)]
15pub struct Scalar(SecretBuffer<P256_SCALAR_SIZE>);
16
17impl Scalar {
18 pub fn new(data: [u8; P256_SCALAR_SIZE]) -> Result<Self> {
25 Self::validate_canonical_nonzero(&data)?;
26 Ok(Scalar(SecretBuffer::new(data)))
27 }
28
29 pub fn from_bytes_reduced(mut data: [u8; P256_SCALAR_SIZE]) -> Self {
34 Self::reduce_scalar_bytes_allow_zero(&mut data);
35 Self::from_bytes_unchecked(data)
36 }
37
38 fn from_bytes_unchecked(bytes: [u8; P256_SCALAR_SIZE]) -> Self {
43 Scalar(SecretBuffer::new(bytes))
44 }
45
46 pub fn from_secret_buffer(buffer: SecretBuffer<P256_SCALAR_SIZE>) -> Result<Self> {
51 let mut bytes = [0u8; P256_SCALAR_SIZE];
52 bytes.copy_from_slice(buffer.as_ref());
53
54 Self::validate_canonical_nonzero(&bytes)?;
55 Ok(Scalar(SecretBuffer::new(bytes)))
56 }
57
58 pub fn as_secret_buffer(&self) -> &SecretBuffer<P256_SCALAR_SIZE> {
60 &self.0
61 }
62
63 pub fn serialize(&self) -> [u8; P256_SCALAR_SIZE] {
68 let mut result = [0u8; P256_SCALAR_SIZE];
69 result.copy_from_slice(self.0.as_ref());
70 result
71 }
72
73 pub fn deserialize(bytes: &[u8]) -> Result<Self> {
78 validate::length("P-256 Scalar", bytes.len(), P256_SCALAR_SIZE)?;
79
80 let mut scalar_bytes = [0u8; P256_SCALAR_SIZE];
81 scalar_bytes.copy_from_slice(bytes);
82
83 Self::new(scalar_bytes)
84 }
85
86 pub fn is_zero(&self) -> bool {
91 self.0.as_ref().iter().all(|&b| b == 0)
92 }
93
94 #[inline(always)]
97 fn to_le_limbs(bytes_be: &[u8; 32]) -> [u32; 8] {
98 let mut limbs = [0u32; 8];
99
100 #[allow(clippy::needless_range_loop)] for i in 0..8 {
103 let start = 28 - i * 4; limbs[i] = u32::from_le_bytes([
105 bytes_be[start + 3],
106 bytes_be[start + 2],
107 bytes_be[start + 1],
108 bytes_be[start],
109 ]);
110 }
111 limbs
112 }
113
114 pub fn add_mod_n(&self, other: &Self) -> Result<Self> {
116 let self_limbs = Self::to_le_limbs(&self.serialize());
117 let other_limbs = Self::to_le_limbs(&other.serialize());
118
119 let mut r = [0u32; 8];
120 let mut carry = 0u64;
121
122 #[allow(clippy::needless_range_loop)] for i in 0..8 {
125 let tmp = self_limbs[i] as u64 + other_limbs[i] as u64 + carry;
126 r[i] = tmp as u32;
127 carry = tmp >> 32;
128 }
129
130 let unreduced = Self::from_bytes_unchecked(Self::limbs_to_be(&r));
131 let mut reduced = r;
132 let borrow = Self::sub_in_place(&mut reduced, &Self::N_LIMBS);
133 let need_reduce = Choice::from((carry as u8) | ((borrow ^ 1) as u8));
134
135 Ok(Self::conditional_select(
136 &unreduced,
137 &Self::from_bytes_unchecked(Self::limbs_to_be(&reduced)),
138 need_reduce,
139 ))
140 }
141
142 pub fn sub_mod_n(&self, other: &Self) -> Result<Self> {
144 let self_limbs = Self::to_le_limbs(&self.serialize());
145 let other_limbs = Self::to_le_limbs(&other.serialize());
146
147 let mut r = [0u32; 8];
148 let mut borrow = 0u64;
149
150 #[allow(clippy::needless_range_loop)] for i in 0..8 {
152 let tmp = (self_limbs[i] as u64)
153 .wrapping_sub(other_limbs[i] as u64)
154 .wrapping_sub(borrow);
155 r[i] = tmp as u32;
156 borrow = (tmp >> 63) & 1;
157 }
158
159 let unreduced = Self::from_bytes_unchecked(Self::limbs_to_be(&r));
160 let mut reduced = r;
161 let mut carry = 0u64;
162 #[allow(clippy::needless_range_loop)] for i in 0..8 {
164 let tmp = reduced[i] as u64 + Self::N_LIMBS[i] as u64 + carry;
165 reduced[i] = tmp as u32;
166 carry = tmp >> 32;
167 }
168
169 Ok(Self::conditional_select(
170 &unreduced,
171 &Self::from_bytes_unchecked(Self::limbs_to_be(&reduced)),
172 Choice::from(borrow as u8),
173 ))
174 }
175
176 pub fn mul_mod_n(&self, other: &Self) -> Result<Self> {
181 let mut acc = Self::from_bytes_unchecked([0u8; P256_SCALAR_SIZE]);
183
184 for byte in other.serialize() {
186 for i in (0..8).rev() {
187 acc = acc.add_mod_n(&acc)?;
190
191 let acc_plus_self = acc.add_mod_n(self)?;
192 let choice = Choice::from((byte >> i) & 1);
193 acc = Self::conditional_select(&acc, &acc_plus_self, choice);
194 }
195 }
196
197 Ok(acc)
198 }
199
200 pub fn inv_mod_n(&self) -> Result<Self> {
203 if self.is_zero() {
205 return Err(Error::param("P-256 Scalar", "Cannot invert zero scalar"));
206 }
207
208 let mut exp = NIST_P256.n; let mut borrow = 2u16;
212 for i in (0..P256_SCALAR_SIZE).rev() {
213 let v = exp[i] as i16 - (borrow as i16);
214 if v < 0 {
215 exp[i] = (v + 256) as u8;
216 borrow = 1;
217 } else {
218 exp[i] = v as u8;
219 borrow = 0;
220 }
221 }
222
223 let mut result = {
229 let mut one = [0u8; P256_SCALAR_SIZE];
230 one[P256_SCALAR_SIZE - 1] = 1;
231 Self::from_bytes_unchecked(one)
233 };
234 let base = self.clone();
235
236 for byte in exp {
237 for bit in (0..8).rev() {
238 result = result.mul_mod_n(&result)?;
240 if (byte >> bit) & 1 == 1 {
242 result = result.mul_mod_n(&base)?;
243 }
244 }
245 }
246
247 Ok(result)
248 }
249
250 pub fn negate(&self) -> Self {
255 if self.is_zero() {
257 return Self::from_bytes_unchecked([0u8; P256_SCALAR_SIZE]);
258 }
259
260 let n_limbs = Self::N_LIMBS;
262 let self_limbs = Self::to_le_limbs(&self.serialize());
263 let mut res = [0u32; 8];
264
265 let mut borrow = 0i64;
267 #[allow(clippy::needless_range_loop)] for i in 0..8 {
269 let tmp = n_limbs[i] as i64 - self_limbs[i] as i64 - borrow;
270 if tmp < 0 {
271 res[i] = (tmp + (1i64 << 32)) as u32;
272 borrow = 1;
273 } else {
274 res[i] = tmp as u32;
275 borrow = 0;
276 }
277 }
278
279 debug_assert_eq!(borrow, 0);
281
282 Self::from_bytes_unchecked(Self::limbs_to_be(&res))
283 }
284
285 fn reduce_scalar_bytes_allow_zero(bytes: &mut [u8; P256_SCALAR_SIZE]) {
295 let order = &NIST_P256.n;
296
297 let mut gt = 0u8; let mut lt = 0u8; for i in 0..P256_SCALAR_SIZE {
303 let x = bytes[i];
304 let y = order[i];
305 gt |= ((x > y) as u8) & (!lt);
306 lt |= ((x < y) as u8) & (!gt);
307 }
308 let ge = gt | ((!lt) & 1); if ge == 1 {
311 let mut borrow = 0u16;
313 let mut temp_bytes = *bytes;
314
315 for i in (0..P256_SCALAR_SIZE).rev() {
316 let diff = (temp_bytes[i] as i16) - (order[i] as i16) - (borrow as i16);
317 if diff < 0 {
318 temp_bytes[i] = (diff + 256) as u8;
319 borrow = 1;
320 } else {
321 temp_bytes[i] = diff as u8;
322 borrow = 0;
323 }
324 }
325
326 *bytes = temp_bytes;
327 }
328 }
329
330 fn validate_canonical_nonzero(bytes: &[u8; P256_SCALAR_SIZE]) -> Result<()> {
331 if bytes.iter().all(|&byte| byte == 0) {
332 return Err(Error::param("P-256 Scalar", "Scalar cannot be zero"));
333 }
334
335 let order = &NIST_P256.n;
336 for (&byte, &order_byte) in bytes.iter().zip(order) {
337 if byte < order_byte {
338 return Ok(());
339 }
340 if byte > order_byte {
341 return Err(Error::param(
342 "P-256 Scalar",
343 "Scalar must be less than the group order",
344 ));
345 }
346 }
347
348 Err(Error::param(
349 "P-256 Scalar",
350 "Scalar must be less than the group order",
351 ))
352 }
353
354 const N_LIMBS: [u32; 8] = [
356 0xFC63_2551,
357 0xF3B9_CAC2,
358 0xA717_9E84,
359 0xBCE6_FAAD,
360 0xFFFF_FFFF,
361 0xFFFF_FFFF,
362 0x0000_0000,
363 0xFFFF_FFFF,
364 ];
365
366 #[inline(always)]
367 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
368 let a_bytes = a.serialize();
369 let b_bytes = b.serialize();
370 let mut out = [0u8; P256_SCALAR_SIZE];
371 for i in 0..P256_SCALAR_SIZE {
372 out[i] = u8::conditional_select(&a_bytes[i], &b_bytes[i], choice);
373 }
374 Self::from_bytes_unchecked(out)
375 }
376
377 #[inline(always)]
379 fn geq(a: &[u32; 8], b: &[u32; 8]) -> bool {
380 for i in (0..8).rev() {
381 if a[i] > b[i] {
382 return true;
383 }
384 if a[i] < b[i] {
385 return false;
386 }
387 }
388 true }
390
391 #[inline(always)]
393 fn sub_in_place(a: &mut [u32; 8], b: &[u32; 8]) -> u64 {
394 let mut borrow = 0u64;
395 #[allow(clippy::needless_range_loop)] for i in 0..8 {
397 let tmp = (a[i] as u64).wrapping_sub(b[i] as u64).wrapping_sub(borrow);
398 a[i] = tmp as u32;
399 borrow = (tmp >> 63) & 1; }
401 borrow
402 }
403
404 #[inline(always)]
407 fn limbs_to_be(limbs: &[u32; 8]) -> [u8; 32] {
408 let mut out = [0u8; 32];
409 for (i, &w) in limbs.iter().enumerate() {
410 let be = w.to_le_bytes(); let start = 28 - i * 4;
412 out[start] = be[3];
413 out[start + 1] = be[2];
414 out[start + 2] = be[1];
415 out[start + 3] = be[0];
416 }
417 out
418 }
419}