tecdsa_core/arith/
sbig.rs1use alloc::vec::Vec;
3use crypto_bigint::{
4 BoxedUint, Choice, ConcatenatingMul, NonZero, RandomBits, RandomMod, Resize,
5 ctutils::{CtEq, CtSelect},
6 rand_core::Rng,
7};
8use zeroize::Zeroize;
9
10use crate::error::{Error, Result};
11
12#[derive(Clone)]
14pub struct SBig {
15 neg: Choice,
16 mag: BoxedUint,
17}
18
19impl PartialEq for SBig {
20 fn eq(&self, other: &Self) -> bool {
21 bool::from(self.neg.ct_eq(&other.neg))
22 && resized(&self.mag, other.mag.bits_precision())
23 == resized(&other.mag, self.mag.bits_precision())
24 }
25}
26impl Eq for SBig {}
27
28impl core::fmt::Debug for SBig {
29 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
30 f.write_str("SBig(***)")
31 }
32}
33
34impl Zeroize for SBig {
35 fn zeroize(&mut self) {
36 self.mag.zeroize();
37 self.neg = Choice::FALSE;
38 }
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct SRange {
44 bits: u32,
45}
46
47impl SRange {
48 pub const fn pm_pow2(n: u32) -> Self {
50 Self { bits: n }
51 }
52 pub const fn bits(&self) -> u32 {
53 self.bits
54 }
55}
56
57impl SBig {
58 pub fn zero(bits: u32) -> Self {
59 Self {
60 neg: Choice::FALSE,
61 mag: BoxedUint::zero_with_precision(bits.max(1)),
62 }
63 }
64
65 pub fn from_uint(mag: BoxedUint) -> Self {
67 Self {
68 neg: Choice::FALSE,
69 mag,
70 }
71 }
72
73 pub fn from_i64(v: i64) -> Self {
74 let mag = BoxedUint::from(v.unsigned_abs());
75 Self {
76 neg: neg_flag(v),
77 mag,
78 }
79 }
80
81 pub fn is_neg(&self) -> Choice {
82 self.neg
83 }
84
85 pub fn mag(&self) -> &BoxedUint {
86 &self.mag
87 }
88
89 pub fn is_zero(&self) -> Choice {
90 self.mag.is_zero()
91 }
92
93 pub fn bits_precision(&self) -> u32 {
94 self.mag.bits_precision()
95 }
96
97 fn widen(&self, bits: u32) -> BoxedUint {
98 resized(&self.mag, bits)
99 }
100
101 fn from_parts(neg: Choice, mag: BoxedUint) -> Self {
102 let neg = neg.and(mag.is_zero().not());
104 Self { neg, mag }
105 }
106
107 pub fn negate(&self) -> Self {
108 Self::from_parts(self.neg.not(), self.mag.clone())
109 }
110
111 pub fn add(&self, rhs: &Self) -> Self {
112 let bits = self.mag.bits_precision().max(rhs.mag.bits_precision()) + 64;
113 let a = self.widen(bits);
114 let b = rhs.widen(bits);
115 let same = self.neg.ct_eq(&rhs.neg);
116 let sum = a.wrapping_add(&b);
117 let (d_ab, a_lt_b) = a.underflowing_sub(&b);
118 let d_ba = b.wrapping_sub(&a);
119 let diff = d_ab.ct_select(&d_ba, a_lt_b);
121 let diff_neg = self.neg.ct_select(&rhs.neg, a_lt_b);
122 let mag = diff.ct_select(&sum, same);
123 let neg = diff_neg.ct_select(&self.neg, same);
124 Self::from_parts(neg, mag)
125 }
126
127 pub fn sub(&self, rhs: &Self) -> Self {
128 self.add(&rhs.negate())
129 }
130
131 pub fn mul(&self, rhs: &Self) -> Self {
132 let mag = self.mag.concatenating_mul(&rhs.mag);
133 Self::from_parts(self.neg.ct_ne(&rhs.neg), mag)
134 }
135
136 pub fn rem_euclid(&self, m: &NonZero<BoxedUint>) -> BoxedUint {
138 let bits = self.mag.bits_precision().max(m.bits_precision());
139 let wm = Option::<NonZero<BoxedUint>>::from(resized(m.as_ref(), bits).to_nz())
140 .unwrap_or_else(|| m.clone());
141 let r = self.widen(bits).rem(&wm);
142 let neg_r = r.neg_mod(&wm);
143 let out = r.ct_select(&neg_r, self.neg);
144 out.resize(m.bits_precision())
145 }
146
147 pub fn in_range(&self, range: SRange) -> bool {
149 if range.bits == 0 {
150 return bool::from(self.is_zero());
151 }
152 let bound = BoxedUint::one_with_precision(range.bits.max(self.mag.bits_precision()) + 1)
153 .shl(range.bits - 1);
154 let m = self.widen(bound.bits_precision());
155 if bool::from(self.neg) {
157 m.cmp_vartime(&bound) == core::cmp::Ordering::Less
158 } else {
159 m.cmp_vartime(&bound) != core::cmp::Ordering::Greater
160 }
161 }
162
163 pub fn random_in<R: Rng + ?Sized>(rng: &mut R, range: SRange) -> Self {
165 let n = range.bits.max(1);
166 let prec = n + 64;
167 let u = match BoxedUint::try_random_bits_with_precision(rng, n, prec) {
168 Ok(u) => u,
169 Err(_) => BoxedUint::zero_with_precision(prec),
170 };
171 let shift = BoxedUint::one_with_precision(prec).shl(n - 1);
172 let one = BoxedUint::one_with_precision(prec);
173 let up = u.wrapping_add(&one);
175 let (d, under) = up.underflowing_sub(&shift);
176 let d_neg = shift.wrapping_sub(&up);
177 let mag = d.ct_select(&d_neg, under);
178 Self::from_parts(under, mag)
179 }
180
181 pub fn random_in_size<R: Rng + ?Sized>(rng: &mut R, size: &BoxedUint) -> Result<Self> {
184 let prec = size.bits_precision() + 64;
185 let size_w = resized(size, prec);
186 let nz = Option::<NonZero<BoxedUint>>::from(size_w.to_nz()).ok_or(Error::OutOfRange)?;
187 let u = BoxedUint::random_mod_vartime(rng, &nz);
188 let half = size_w.shr_vartime(1).ok_or(Error::OutOfRange)?;
189 Ok(Self::from_uint(u)
190 .add(&Self::from_i64(1))
191 .sub(&Self::from_uint(half)))
192 }
193
194 pub fn to_bytes(&self, range: SRange) -> Result<Vec<u8>> {
196 if !self.in_range(range) {
197 return Err(Error::OutOfRange);
198 }
199 let len = (range.bits.saturating_sub(1) as usize).div_ceil(8).max(1);
200 let be = self.mag.to_be_bytes();
201 let mut out = Vec::with_capacity(1 + len);
202 out.push(u8::from(bool::from(self.neg)));
203 if be.len() >= len {
204 out.extend_from_slice(&be[be.len() - len..]);
205 } else {
206 out.resize(1 + len - be.len(), 0);
207 out.extend_from_slice(&be);
208 }
209 Ok(out)
210 }
211
212 pub fn from_bytes(bytes: &[u8], range: SRange) -> Result<Self> {
213 let len = (range.bits.saturating_sub(1) as usize).div_ceil(8).max(1);
214 if bytes.len() != 1 + len {
215 return Err(Error::InvalidEncoding);
216 }
217 let neg = match bytes[0] {
218 0 => false,
219 1 => true,
220 _ => return Err(Error::InvalidEncoding),
221 };
222 let mag = BoxedUint::from_be_slice(&bytes[1..], (len as u32 * 8).max(64))
223 .map_err(|_| Error::InvalidEncoding)?;
224 if neg && bool::from(mag.is_zero()) {
225 return Err(Error::InvalidEncoding);
226 }
227 let v = Self {
228 neg: Choice::from_u8_lsb(u8::from(neg)),
229 mag,
230 };
231 if !v.in_range(range) {
232 return Err(Error::OutOfRange);
233 }
234 Ok(v)
235 }
236}
237
238fn neg_flag(v: i64) -> Choice {
239 Choice::from_u8_lsb(u8::from(v < 0))
240}
241
242pub(crate) fn resized(x: &BoxedUint, bits: u32) -> BoxedUint {
244 if x.bits_precision() >= bits {
245 x.clone()
246 } else {
247 Resize::resize(x.clone(), bits)
248 }
249}