tecdsa_core/
transcript.rs1use alloc::{string::String, vec::Vec};
3use crypto_bigint::{BoxedUint, Gcd, NonZero};
4use k256::{
5 Scalar,
6 elliptic_curve::{bigint::U512, ops::Reduce},
7};
8use sha2::{Digest, Sha256};
9
10use crate::{
11 arith::{
12 monty::{Modulus, Residue},
13 sbig::{SBig, SRange, resized},
14 },
15 error::{Error, Result},
16};
17
18const PREFIX: &str = "tecdsa-1ole/v1/";
19
20#[derive(Clone)]
23pub struct Transcript {
24 hasher: Sha256,
25}
26
27impl Transcript {
28 pub fn new(domain: &str) -> Self {
30 let mut t = Self {
31 hasher: Sha256::new(),
32 };
33 let full: String = [PREFIX, domain].concat();
34 t.hasher.update([0x00]);
35 t.hasher.update((full.len() as u32).to_be_bytes());
36 t.hasher.update(full.as_bytes());
37 t
38 }
39
40 pub fn append(&mut self, label: &str, bytes: &[u8]) -> &mut Self {
42 self.hasher.update([0x01]);
43 self.hasher.update((label.len() as u32).to_be_bytes());
44 self.hasher.update(label.as_bytes());
45 self.hasher.update((bytes.len() as u64).to_be_bytes());
46 self.hasher.update(bytes);
47 self
48 }
49
50 pub fn append_u64(&mut self, label: &str, v: u64) -> &mut Self {
51 self.append(label, &v.to_be_bytes())
52 }
53
54 pub fn append_uint(&mut self, label: &str, x: &BoxedUint, len: usize) -> Result<&mut Self> {
56 let bytes = uint_to_be(x, len)?;
57 Ok(self.append(label, &bytes))
58 }
59
60 pub fn append_residue(&mut self, label: &str, m: &Modulus, x: &Residue) -> Result<&mut Self> {
62 let _ = m;
63 let len = (x.params().modulus().bits() as usize).div_ceil(8);
64 self.append_uint(label, &x.retrieve(), len)
65 }
66
67 pub fn append_sbig(&mut self, label: &str, x: &SBig, range: SRange) -> Result<&mut Self> {
69 let bytes = x.to_bytes(range)?;
70 Ok(self.append(label, &bytes))
71 }
72
73 pub fn challenge(&self, label: &str) -> Challenge {
75 let mut h = self.hasher.clone();
76 h.update([0x02]);
77 h.update((label.len() as u32).to_be_bytes());
78 h.update(label.as_bytes());
79 let seed: [u8; 32] = h.finalize().into();
80 Challenge {
81 seed,
82 ctr: 0,
83 buf: Vec::new(),
84 pos: 0,
85 }
86 }
87}
88
89pub struct Challenge {
91 seed: [u8; 32],
92 ctr: u32,
93 buf: Vec<u8>,
94 pos: usize,
95}
96
97impl Challenge {
98 pub fn seed(&self) -> [u8; 32] {
100 self.seed
101 }
102
103 pub fn bytes(&mut self, n: usize) -> Vec<u8> {
104 let mut out = Vec::with_capacity(n);
105 while out.len() < n {
106 if self.pos == self.buf.len() {
107 let mut h = Sha256::new();
108 h.update(self.seed);
109 h.update(self.ctr.to_be_bytes());
110 self.ctr = self.ctr.wrapping_add(1);
111 self.buf = h.finalize().to_vec();
112 self.pos = 0;
113 }
114 let take = (n - out.len()).min(self.buf.len() - self.pos);
115 out.extend_from_slice(&self.buf[self.pos..self.pos + take]);
116 self.pos += take;
117 }
118 out
119 }
120
121 pub fn bits(&mut self, n: u32) -> BoxedUint {
123 let nbytes = (n as usize).div_ceil(8).max(1);
124 let mut b = self.bytes(nbytes);
125 let extra = nbytes as u32 * 8 - n;
126 if extra > 0 {
127 b[0] &= 0xff >> extra;
128 }
129 let prec = (nbytes as u32 * 8).next_multiple_of(64);
130 BoxedUint::from_be_slice(&b, prec).unwrap_or_else(|_| BoxedUint::zero_with_precision(prec))
131 }
132
133 pub fn pm_pow2(&mut self, ell: u32) -> SBig {
135 let u = SBig::from_uint(self.bits(ell));
136 let shift = SBig::from_uint(
137 BoxedUint::one_with_precision(ell.next_multiple_of(64) + 64).shl(ell.saturating_sub(1)),
138 );
139 u.sub(&shift).add(&SBig::from_i64(1))
140 }
141
142 pub fn small(&mut self, n: u32) -> u64 {
144 debug_assert!(n <= 64);
145 let b = self.bits(n);
146 b.as_words()[0]
147 }
148
149 pub fn scalar(&mut self) -> Scalar {
151 let b = self.bytes(64);
152 <Scalar as Reduce<U512>>::reduce(&U512::from_be_slice(&b))
153 }
154
155 pub fn unit_mod(&mut self, m: &Modulus) -> Result<Residue> {
157 let nbits = m.modulus().bits();
158 for _ in 0..64 {
159 let y = self.bits(nbits + 128);
160 let mm = resized(m.modulus().as_ref(), y.bits_precision());
161 let nz = Option::<NonZero<BoxedUint>>::from(mm.to_nz()).ok_or(Error::InvalidModulus)?;
162 let r = y.rem_vartime(&nz);
163 let g = r.gcd_vartime(&mm);
164 if bool::from(g.is_one()) {
165 return Ok(m.residue(&r));
166 }
167 }
168 Err(Error::OutOfRange)
169 }
170
171 pub fn qr_mod(&mut self, m: &Modulus) -> Result<Residue> {
173 let nbits = m.modulus().bits();
174 for _ in 0..64 {
175 let y = self.bits(nbits + 128);
176 let r = m.residue(&y);
177 let sq = r.square();
178 if !bool::from(sq.is_zero())
179 && sq.retrieve() != BoxedUint::one_with_precision(sq.bits_precision())
180 {
181 return Ok(sq);
182 }
183 }
184 Err(Error::OutOfRange)
185 }
186}
187
188pub fn uint_to_be(x: &BoxedUint, len: usize) -> Result<Vec<u8>> {
190 let be = x.to_be_bytes();
191 if be.len() >= len {
192 let (hi, lo) = be.split_at(be.len() - len);
193 if hi.iter().any(|b| *b != 0) {
194 return Err(Error::OutOfRange);
195 }
196 Ok(lo.to_vec())
197 } else {
198 let mut out = alloc::vec![0u8; len - be.len()];
199 out.extend_from_slice(&be);
200 Ok(out)
201 }
202}
203
204pub fn uint_from_be(bytes: &[u8], bits: u32) -> Result<BoxedUint> {
206 let prec = bits.max(bytes.len() as u32 * 8).next_multiple_of(64);
207 BoxedUint::from_be_slice(&pad_left(bytes, prec as usize / 8), prec)
208 .map_err(|_| Error::InvalidEncoding)
209}
210
211fn pad_left(b: &[u8], len: usize) -> Vec<u8> {
212 let mut v = alloc::vec![0u8; len.saturating_sub(b.len())];
213 v.extend_from_slice(b);
214 v
215}