-
Notifications
You must be signed in to change notification settings - Fork 55
Add WnafBase::multiscalar_mul
#82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,23 +150,42 @@ pub(crate) fn wnaf_form<S: AsRef<[u8]>>(wnaf: &mut Vec<i64>, c: S, window: usize | |
| /// | ||
| /// This function must be provided a `table` and `wnaf` that were constructed with | ||
| /// the same window size; otherwise, it may panic or produce invalid results. | ||
| #[inline] | ||
| pub(crate) fn wnaf_exp<G: Group>(table: &[G], wnaf: &[i64]) -> G { | ||
| let mut result = G::identity(); | ||
| wnaf_multi_exp(&[table], &[wnaf]) | ||
| } | ||
|
|
||
| /// Performs w-NAF multi-exponentiation using the interleaved window method, also known as | ||
| /// Straus' method. | ||
| /// | ||
| /// The key insight is that when computing this sum by means of additions and doublings, the | ||
| /// doublings can be shared by performing the additions within an inner loop. | ||
| /// | ||
| /// This function must be provided with `tables` and `wnafs` that were constructed with | ||
| /// the same window size; otherwise, it may panic or produce invalid results. | ||
| pub(crate) fn wnaf_multi_exp<G: Group>(tables: &[&[G]], wnafs: &[&[i64]]) -> G { | ||
| debug_assert_eq!(tables.len(), wnafs.len()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. promote to release assertion for mismatched lengths.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least for now it's not possible to trip this assertion as the equality is enforced at the type system level where I can promote it, but really it's belt-and-suspenders. |
||
| let window_size = wnafs.iter().map(|w| w.len()).max().unwrap_or(0); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit;
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would you suggest instead? |
||
|
|
||
| let mut result = G::identity(); | ||
| let mut found_one = false; | ||
|
|
||
| for n in wnaf.iter().rev() { | ||
| for i in (0..window_size).rev() { | ||
| // Only double once per iteration of the loop | ||
| if found_one { | ||
| result = result.double(); | ||
| } | ||
|
|
||
| if *n != 0 { | ||
| found_one = true; | ||
| for (&table, &wnaf) in tables.iter().zip(wnafs.iter()) { | ||
| let n = wnaf.get(i).copied().unwrap_or(0); | ||
| if n != 0 { | ||
| found_one = true; | ||
|
|
||
| if *n > 0 { | ||
| result += &table[(n / 2) as usize]; | ||
| } else { | ||
| result -= &table[((-n) / 2) as usize]; | ||
| if n > 0 { | ||
| result += &table[(n / 2) as usize]; | ||
| } else { | ||
| result -= &table[((-n) / 2) as usize]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -499,6 +518,21 @@ impl<G: Group, const WINDOW_SIZE: usize> WnafBase<G, WINDOW_SIZE> { | |
|
|
||
| WnafBase { table } | ||
| } | ||
|
|
||
| /// Perform a multiscalar multiplication. | ||
| pub fn multiscalar_mul<'a, I, J>(scalars: I, bases: J) -> G | ||
|
Comment on lines
+522
to
+523
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. referencing dalek (VartimeMultiscalarMul trait and corresponding impl in straus.rs), there's explicit timing language there. suggest a timing note that this runs in variable time and not be used with secret scalars (dalek frames it as operating on "public scalars"). maybe also rename to
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We tend to put vartime at the end of the method names BTW. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gotcha, suggestion was matching dalek's naming convention.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW we also use |
||
| where | ||
| I: Iterator<Item = &'a WnafScalar<G::Scalar, WINDOW_SIZE>>, | ||
| J: Iterator<Item = &'a Self>, | ||
|
Comment on lines
+524
to
+526
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit; dalek uses |
||
| { | ||
| let wnafs = scalars | ||
| .map(|scalar| scalar.wnaf.as_slice()) | ||
| .collect::<Vec<_>>(); | ||
|
|
||
| let tables = bases.map(|base| base.table.as_slice()).collect::<Vec<_>>(); | ||
|
|
||
| wnaf_multi_exp(tables.as_slice(), wnafs.as_slice()) | ||
| } | ||
| } | ||
|
|
||
| impl<G: Group, const WINDOW_SIZE: usize> Mul<&WnafScalar<G::Scalar, WINDOW_SIZE>> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK – traced the impl; extends wNAF to MSM via Straus' method:
(N * (doublings + adds))to(doublings + (N * adds)), where N is # LC terms. doubling savings from interleaving windows (and it stacks on existing addition savings that use signed digits). fine for small MSMs, but obviously for larger N a hand rolled Pippenger impl downstream.generated some of my own test vectors locally, but suggesting multiscalar coverage for
wnaf_multi_exp/multiscalar_mulin the diff.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wNAF implementations in both curve25519-dalek and libsecp256k1 provide both Straus and Pippenger and select between them at runtime, which would probably be ideal here as well, but that first needs a generic implementation of Pippenger