use alloc::{string::String, vec::Vec};
use crypto_bigint::{BoxedUint, Gcd, NonZero};
use k256::{
Scalar,
elliptic_curve::{bigint::U512, ops::Reduce},
};
use sha2::{Digest, Sha256};
use crate::{
arith::{
monty::{Modulus, Residue},
sbig::{SBig, SRange, resized},
},
error::{Error, Result},
};
const PREFIX: &str = "tecdsa-1ole/v1/";
#[derive(Clone)]
pub struct Transcript {
hasher: Sha256,
}
impl Transcript {
pub fn new(domain: &str) -> Self {
let mut t = Self {
hasher: Sha256::new(),
};
let full: String = [PREFIX, domain].concat();
t.hasher.update([0x00]);
t.hasher.update((full.len() as u32).to_be_bytes());
t.hasher.update(full.as_bytes());
t
}
pub fn append(&mut self, label: &str, bytes: &[u8]) -> &mut Self {
self.hasher.update([0x01]);
self.hasher.update((label.len() as u32).to_be_bytes());
self.hasher.update(label.as_bytes());
self.hasher.update((bytes.len() as u64).to_be_bytes());
self.hasher.update(bytes);
self
}
pub fn append_u64(&mut self, label: &str, v: u64) -> &mut Self {
self.append(label, &v.to_be_bytes())
}
pub fn append_uint(&mut self, label: &str, x: &BoxedUint, len: usize) -> Result<&mut Self> {
let bytes = uint_to_be(x, len)?;
Ok(self.append(label, &bytes))
}
pub fn append_residue(&mut self, label: &str, m: &Modulus, x: &Residue) -> Result<&mut Self> {
let _ = m;
let len = (x.params().modulus().bits() as usize).div_ceil(8);
self.append_uint(label, &x.retrieve(), len)
}
pub fn append_sbig(&mut self, label: &str, x: &SBig, range: SRange) -> Result<&mut Self> {
let bytes = x.to_bytes(range)?;
Ok(self.append(label, &bytes))
}
pub fn challenge(&self, label: &str) -> Challenge {
let mut h = self.hasher.clone();
h.update([0x02]);
h.update((label.len() as u32).to_be_bytes());
h.update(label.as_bytes());
let seed: [u8; 32] = h.finalize().into();
Challenge {
seed,
ctr: 0,
buf: Vec::new(),
pos: 0,
}
}
}
pub struct Challenge {
seed: [u8; 32],
ctr: u32,
buf: Vec<u8>,
pos: usize,
}
impl Challenge {
pub fn seed(&self) -> [u8; 32] {
self.seed
}
pub fn bytes(&mut self, n: usize) -> Vec<u8> {
let mut out = Vec::with_capacity(n);
while out.len() < n {
if self.pos == self.buf.len() {
let mut h = Sha256::new();
h.update(self.seed);
h.update(self.ctr.to_be_bytes());
self.ctr = self.ctr.wrapping_add(1);
self.buf = h.finalize().to_vec();
self.pos = 0;
}
let take = (n - out.len()).min(self.buf.len() - self.pos);
out.extend_from_slice(&self.buf[self.pos..self.pos + take]);
self.pos += take;
}
out
}
pub fn bits(&mut self, n: u32) -> BoxedUint {
let nbytes = (n as usize).div_ceil(8).max(1);
let mut b = self.bytes(nbytes);
let extra = nbytes as u32 * 8 - n;
if extra > 0 {
b[0] &= 0xff >> extra;
}
let prec = (nbytes as u32 * 8).next_multiple_of(64);
BoxedUint::from_be_slice(&b, prec).unwrap_or_else(|_| BoxedUint::zero_with_precision(prec))
}
pub fn pm_pow2(&mut self, ell: u32) -> SBig {
let u = SBig::from_uint(self.bits(ell));
let shift = SBig::from_uint(
BoxedUint::one_with_precision(ell.next_multiple_of(64) + 64).shl(ell.saturating_sub(1)),
);
u.sub(&shift).add(&SBig::from_i64(1))
}
pub fn small(&mut self, n: u32) -> u64 {
debug_assert!(n <= 64);
let b = self.bits(n);
b.as_words()[0]
}
pub fn scalar(&mut self) -> Scalar {
let b = self.bytes(64);
<Scalar as Reduce<U512>>::reduce(&U512::from_be_slice(&b))
}
pub fn unit_mod(&mut self, m: &Modulus) -> Result<Residue> {
let nbits = m.modulus().bits();
for _ in 0..64 {
let y = self.bits(nbits + 128);
let mm = resized(m.modulus().as_ref(), y.bits_precision());
let nz = Option::<NonZero<BoxedUint>>::from(mm.to_nz()).ok_or(Error::InvalidModulus)?;
let r = y.rem_vartime(&nz);
let g = r.gcd_vartime(&mm);
if bool::from(g.is_one()) {
return Ok(m.residue(&r));
}
}
Err(Error::OutOfRange)
}
pub fn qr_mod(&mut self, m: &Modulus) -> Result<Residue> {
let nbits = m.modulus().bits();
for _ in 0..64 {
let y = self.bits(nbits + 128);
let r = m.residue(&y);
let sq = r.square();
if !bool::from(sq.is_zero())
&& sq.retrieve() != BoxedUint::one_with_precision(sq.bits_precision())
{
return Ok(sq);
}
}
Err(Error::OutOfRange)
}
}
pub fn uint_to_be(x: &BoxedUint, len: usize) -> Result<Vec<u8>> {
let be = x.to_be_bytes();
if be.len() >= len {
let (hi, lo) = be.split_at(be.len() - len);
if hi.iter().any(|b| *b != 0) {
return Err(Error::OutOfRange);
}
Ok(lo.to_vec())
} else {
let mut out = alloc::vec![0u8; len - be.len()];
out.extend_from_slice(&be);
Ok(out)
}
}
pub fn uint_from_be(bytes: &[u8], bits: u32) -> Result<BoxedUint> {
let prec = bits.max(bytes.len() as u32 * 8).next_multiple_of(64);
BoxedUint::from_be_slice(&pad_left(bytes, prec as usize / 8), prec)
.map_err(|_| Error::InvalidEncoding)
}
fn pad_left(b: &[u8], len: usize) -> Vec<u8> {
let mut v = alloc::vec![0u8; len.saturating_sub(b.len())];
v.extend_from_slice(b);
v
}