primeorder/tables/
basepoint.rs1#![allow(clippy::cast_possible_truncation, clippy::needless_range_loop)]
4
5#[cfg(not(any(feature = "critical-section", feature = "std")))]
6compile_error!("`basepoint-table` feature requires either `critical-section` or `std`");
7
8use super::LookupTable;
9use crate::{PrimeCurveParams, ProjectivePoint, Radix16Decomposition, Radix16Digits, Scalar};
10use core::ops::Deref;
11use elliptic_curve::{
12 FieldBytesSize, array::typenum::Unsigned, ff::PrimeField, group::Group,
13 subtle::ConditionallySelectable,
14};
15
16#[cfg(feature = "critical-section")]
17use once_cell::sync::Lazy as LazyLock;
18#[cfg(all(feature = "std", not(feature = "critical-section")))]
19use std::sync::LazyLock;
20
21#[derive(Debug)]
29pub struct BasepointTable<Point, const WINDOW_SIZE: usize> {
30 tables: LazyLock<[LookupTable<Point>; WINDOW_SIZE]>,
31}
32
33impl<Point, const WINDOW_SIZE: usize> BasepointTable<Point, WINDOW_SIZE>
34where
35 Point: ConditionallySelectable + Default + Group,
36{
37 pub const fn new() -> Self {
42 fn init_table<Point, const N: usize>() -> [LookupTable<Point>; N]
44 where
45 Point: ConditionallySelectable + Default + Group,
46 {
47 const {
49 assert!(
50 N as u32 == 1 + Point::Scalar::NUM_BITS.div_ceil(8),
51 "incorrectly sized basepoint table"
52 );
53 }
54
55 let mut generator = Point::generator();
56 let mut res = [LookupTable::<Point>::default(); N];
57
58 for i in 0..N {
59 res[i] = LookupTable::new(generator);
60
61 for _ in 0..8 {
64 generator = generator.double();
65 }
66 }
67
68 res
69 }
70
71 Self {
72 tables: LazyLock::new(init_table),
73 }
74 }
75}
76
77impl<C: PrimeCurveParams, const WINDOW_SIZE: usize>
78 BasepointTable<ProjectivePoint<C>, WINDOW_SIZE>
79{
80 pub fn mul(&self, k: &Scalar<C>) -> ProjectivePoint<C> {
83 let digits = Radix16Decomposition::<Radix16Digits<C>>::new(k);
84 let len = FieldBytesSize::<C>::USIZE;
85 let mut acc = self[len].select(digits[len * 2]);
86 let mut acc2 = ProjectivePoint::<C>::IDENTITY;
87 for i in (0..len).rev() {
88 acc2 += &self[i].select(digits[i * 2 + 1]);
89 acc += &self[i].select(digits[i * 2]);
90 }
91
92 for _ in 0..4 {
94 acc2 = acc2.double();
95 }
96
97 acc + acc2
98 }
99
100 pub fn mul_vartime(&self, k: &Scalar<C>) -> ProjectivePoint<C> {
110 let digits = Radix16Decomposition::<Radix16Digits<C>>::new(k);
111 let len = FieldBytesSize::<C>::USIZE;
112 let mut acc = self[len].select_vartime(digits[len * 2]);
113 let mut acc2 = ProjectivePoint::<C>::IDENTITY;
114 for i in (0..len).rev() {
115 acc2 += &self[i].select_vartime(digits[i * 2 + 1]);
116 acc += &self[i].select_vartime(digits[i * 2]);
117 }
118
119 for _ in 0..4 {
121 acc2 = acc2.double();
122 }
123
124 acc + acc2
125 }
126}
127
128impl<Point, const WINDOW_SIZE: usize> Default for BasepointTable<Point, WINDOW_SIZE>
129where
130 Point: ConditionallySelectable + Default + Group,
131{
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl<Point, const WINDOW_SIZE: usize> Deref for BasepointTable<Point, WINDOW_SIZE> {
138 type Target = [LookupTable<Point>; WINDOW_SIZE];
139
140 #[inline]
141 fn deref(&self) -> &[LookupTable<Point>; WINDOW_SIZE] {
142 &self.tables
143 }
144}