use crate::sm2::point::ProjectivePoint;
use spin::Once;
pub const WINDOW_BITS: usize = 4;
pub const WINDOW_SIZE: usize = 1 << WINDOW_BITS;
pub const NUM_WINDOWS: usize = 256 / WINDOW_BITS;
pub struct CombTable {
pub sub_tables: [[ProjectivePoint; WINDOW_SIZE]; NUM_WINDOWS],
}
static COMB_TABLE: Once<&'static CombTable> = Once::new();
pub fn comb_table() -> &'static CombTable {
COMB_TABLE.call_once(|| {
let table = build_comb_table();
alloc::boxed::Box::leak(table)
})
}
#[allow(clippy::large_stack_arrays)]
fn build_comb_table() -> alloc::boxed::Box<CombTable> {
use alloc::boxed::Box;
let g = ProjectivePoint::generator();
let identity = ProjectivePoint::identity();
let mut sub_tables: alloc::vec::Vec<[ProjectivePoint; WINDOW_SIZE]> =
alloc::vec::Vec::with_capacity(NUM_WINDOWS);
let mut row0 = [identity; WINDOW_SIZE];
row0[1] = g;
for j in 2..WINDOW_SIZE {
row0[j] = row0[j - 1].add(&g);
}
sub_tables.push(row0);
for i in 1..NUM_WINDOWS {
let prev = sub_tables[i - 1];
let mut row = [identity; WINDOW_SIZE];
for j in 0..WINDOW_SIZE {
let mut p = prev[j];
for _ in 0..WINDOW_BITS {
p = p.double();
}
row[j] = p;
}
sub_tables.push(row);
}
let arr: [[ProjectivePoint; WINDOW_SIZE]; NUM_WINDOWS] = sub_tables
.try_into()
.map_err(|_| ())
.expect("comb-table sub-table count must match NUM_WINDOWS");
Box::new(CombTable { sub_tables: arr })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sm2::curve::Fn;
use crate::sm2::scalar_mul::mul_var;
use crypto_bigint::U256;
use subtle::ConstantTimeEq;
#[test]
fn comb_table_entries_match_mul_var() {
let table = comb_table();
let seven = Fn::new(&U256::from_u64(7));
let expected = mul_var(&seven, &ProjectivePoint::generator());
assert!(
bool::from(table.sub_tables[0][7].ct_eq(&expected)),
"T[0][7] != 7·G"
);
let forty_eight = Fn::new(&U256::from_u64(48));
let expected = mul_var(&forty_eight, &ProjectivePoint::generator());
assert!(
bool::from(table.sub_tables[1][3].ct_eq(&expected)),
"T[1][3] != 48·G"
);
let scalar = U256::from_u64(15).shl_vartime(252);
let expected = mul_var(&Fn::new(&scalar), &ProjectivePoint::generator());
assert!(
bool::from(table.sub_tables[NUM_WINDOWS - 1][WINDOW_SIZE - 1].ct_eq(&expected)),
"T[63][15] != 15·2^252·G"
);
}
#[test]
fn comb_table_zero_columns_are_identity() {
let table = comb_table();
for i in 0..NUM_WINDOWS {
assert!(
bool::from(table.sub_tables[i][0].is_identity()),
"T[{i}][0] not identity"
);
}
}
}