#[cfg(feature = "std")]
use ic_core::ct::Choice;
#[cfg(feature = "std")]
use super::arith::Field;
use super::point::Curve;
#[cfg(feature = "std")]
use super::point::Point;
#[cfg(feature = "std")]
const MAX_DIGITS: usize = 133;
#[cfg(feature = "std")]
const ENTRIES: usize = 8;
#[cfg(feature = "std")]
struct Window<C: Curve>([Point<C>; ENTRIES]);
#[cfg(feature = "std")]
impl<C: Curve> Window<C> {
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)
}
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")]
pub struct Table<C: Curve> {
windows: std::vec::Vec<Window<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();
}
}
windows.push(Window::new(&base));
}
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 = Point::identity();
for i in (1..n).step_by(2) {
acc = acc.add(&self.windows[i / 2].select(digits[i]));
}
for _ in 0..4 {
acc = acc.double();
}
for i in (0..n).step_by(2) {
acc = acc.add(&self.windows[i / 2].select(digits[i]));
}
acc
}
}
#[cfg(feature = "std")]
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_export]
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> {
use $crate::nist::point::Curve as _;
$crate::nist::point::Point::<Self>::generator().mul_scalar(scalar)
}
}
};
}