use ic_core::ct::Choice;
#[cfg(feature = "std")]
use super::arith::Field;
use super::point::Curve;
use super::point::Point;
#[cfg(feature = "std")]
use super::point::{AffinePoint, Projective};
const MAX_DIGITS: usize = 133;
const ENTRIES: usize = 8;
pub(super) struct Window<C: Curve>([Point<C>; ENTRIES]);
impl<C: Curve> Window<C> {
pub(super) fn new(base: &Point<C>) -> Self {
let mut entries = [Point::identity(); ENTRIES];
entries[0] = *base;
for i in 1..ENTRIES {
entries[i] = entries[i - 1].add(base);
}
Self(entries)
}
pub(super) fn select(&self, digit: i8) -> Point<C> {
let negative = Choice::from_u8((digit as u8) >> 7);
let magnitude = ((digit as i16 ^ (digit as i16 >> 7)) - (digit as i16 >> 7)) as u8;
let mut out = Point::identity();
for (i, entry) in self.0.iter().enumerate() {
let hit = Choice::from_u8(u8::from(magnitude == (i as u8 + 1)));
Point::cmov(&mut out, entry, hit);
}
out.conditional_negate(negative);
out
}
}
#[cfg(feature = "std")]
struct AffineWindow<C: Curve>([AffinePoint<C>; ENTRIES]);
#[cfg(feature = "std")]
impl<C: Curve> AffineWindow<C> {
fn new(base: &Point<C>) -> Option<Self> {
let window = Window::new(base);
let mut entries = [AffinePoint {
x: C::Field::ZERO,
y: C::Field::ZERO,
}; ENTRIES];
for (out, entry) in entries.iter_mut().zip(window.0.iter()) {
*out = entry.to_affine()?;
}
Some(Self(entries))
}
fn add_to(&self, acc: &Projective<C>, digit: i8) -> Projective<C> {
let negative = Choice::from_u8((digit as u8) >> 7);
let magnitude = ((digit as i16 ^ (digit as i16 >> 7)) - (digit as i16 >> 7)) as u8;
let mut x = self.0[0].x;
let mut y = self.0[0].y;
for (i, entry) in self.0.iter().enumerate() {
let hit = Choice::from_u8(u8::from(magnitude == (i as u8 + 1)));
C::Field::cmov(&mut x, &entry.x, hit);
C::Field::cmov(&mut y, &entry.y, hit);
}
let ny = y.neg();
C::Field::cmov(&mut y, &ny, negative);
let mut out = acc.add_affine(&x, &y);
Projective::cmov(&mut out, acc, Choice::from_u8(u8::from(magnitude == 0)));
out
}
}
#[cfg(feature = "std")]
pub struct Table<C: Curve> {
windows: std::vec::Vec<AffineWindow<C>>,
}
#[cfg(feature = "std")]
impl<C: Curve> Table<C> {
pub fn build() -> Self {
let used = C::SCALAR_BYTES + 1;
let mut windows = std::vec::Vec::with_capacity(used);
let mut base = Point::<C>::generator();
for i in 0..used {
if i > 0 {
for _ in 0..8 {
base = base.double();
}
}
match AffineWindow::new(&base) {
Some(window) => windows.push(window),
None => unreachable!("a multiple of the generator below its order is the identity"),
}
}
Self { windows }
}
pub fn mul(&self, scalar: &C::Scalar) -> Point<C> {
let bytes = scalar.to_bytes();
let digits = signed_digits(bytes.as_ref());
let n = bytes.as_ref().len() * 2 + 1;
debug_assert!(n.div_ceil(2) <= self.windows.len());
let mut acc = Projective::identity();
for i in (1..n).step_by(2) {
acc = self.windows[i / 2].add_to(&acc, digits[i]);
}
for _ in 0..4 {
acc = acc.double();
}
for i in (0..n).step_by(2) {
acc = self.windows[i / 2].add_to(&acc, digits[i]);
}
acc.to_jacobian()
}
}
pub(super) fn signed_digits(bytes: &[u8]) -> [i8; MAX_DIGITS] {
let mut nibbles = [0i8; MAX_DIGITS];
let n = bytes.len() * 2;
for (i, byte) in bytes.iter().rev().enumerate() {
nibbles[i * 2] = (byte & 0x0f) as i8;
nibbles[i * 2 + 1] = (byte >> 4) as i8;
}
for i in 0..n {
let carry = (nibbles[i] + 8) >> 4;
nibbles[i] -= carry << 4;
nibbles[i + 1] += carry;
}
debug_assert!(nibbles[n] == 0 || nibbles[n] == 1);
nibbles
}
pub trait HasGeneratorTable: Curve + Sized + 'static {
fn mul_generator(scalar: &Self::Scalar) -> super::point::Point<Self>;
}
macro_rules! generator_table_for {
($curve:ty) => {
impl $crate::nist::gentable::HasGeneratorTable for $curve {
#[cfg(feature = "std")]
fn mul_generator(
scalar: &<Self as $crate::nist::point::Curve>::Scalar,
) -> $crate::nist::point::Point<Self> {
static TABLE: std::sync::OnceLock<$crate::nist::gentable::Table<$curve>> =
std::sync::OnceLock::new();
TABLE
.get_or_init($crate::nist::gentable::Table::build)
.mul(scalar)
}
#[cfg(not(feature = "std"))]
fn mul_generator(
scalar: &<Self as $crate::nist::point::Curve>::Scalar,
) -> $crate::nist::point::Point<Self> {
$crate::nist::point::Point::<Self>::generator().mul_scalar(scalar)
}
}
};
}
pub(crate) use generator_table_for;