-
Notifications
You must be signed in to change notification settings - Fork 198
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
Improved k256 point-scalar multiplication #82
Conversation
Codecov Report
@@ Coverage Diff @@
## master #82 +/- ##
==========================================
- Coverage 54.40% 52.68% -1.72%
==========================================
Files 16 17 +1
Lines 2998 3124 +126
==========================================
+ Hits 1631 1646 +15
- Misses 1367 1478 +111
Continue to review full report at Codecov.
|
My understanding is the libsecp256k1 C library (and its Rust wrapper) disable this optimization by-default out of concern for patents, notably https://patentimages.storage.googleapis.com/bb/75/9e/a4353a2158ea1a/US7995752.pdf (which looks like it might expire this September, but might also get renewed). While I think it's okay to include in the codebase, I think like libsecp256k1 it needs to be gated by a Cargo feature and disabled by-default such that it can only be used in jurisdictions where the patent does not apply, at least until such time as the patent expires. |
Hm, what exactly is protected by this patent? The whole usage of the endomorphism, or just the specific method of decomposing the scalar (as described in the paper of one of the patent holders, R. J. Lambert - which was published in 2001, so I don't know how this patent, filed in 2005, is even valid)? In the latter case, I would prefer not to use that anyway, since, as I mentioned earlier, it doesn't actually have a bound on its results. There is an alternative method, published in 2002 - would it be free of the patent? |
I’m certainly not a patent lawyer. All I know is the libsecp256k1 developers have been concerned about this particular patent and how it applies to endomorphism optimizations. |
Some more investigation results:
Now I'm not a patent lawyer myself, but I find it highly objectionable that a patent could be filed several years after the research is published, and cover not just a specific algorithm, but the general idea of doing a decomposition (otherwise
It does not lead to the breach of decomposition contract, since the code only calculates one part ( |
That's interesting. I do recall this claim from djb's patent research: https://cr.yp.to/patents/us/4200770.html
|
A variable time scalar mult which is faster than the constant time version is still useful for signature verification |
9a7add9
to
ff4016a
Compare
k256/src/arithmetic/scalar.rs
Outdated
@@ -65,6 +65,12 @@ impl Scalar { | |||
self.0.truncate_to_u32() | |||
} | |||
|
|||
/// Attempts to parse the given byte array as a scalar. | |||
/// Does not check the result for being in the correct range. | |||
pub const fn from_bytes_unchecked(bytes: &[u8; 32]) -> Self { |
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.
Is there a specific reason why this needs to be pub
as opposed to pub(crate)
?
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.
Not really, I didn't think about what should be exposed to the user. This method is useful for defining Scalar
constants, which I suppose may be needed sometimes? But if you want to preserve the current public API, this can be set to pub(crate)
for now.
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.
Given that this method can violate the invariant of what a Scalar
is supposed to contain, I'd suggest making it private for now.
I think ideally a pub const fn from_bytes(...)
method would be possible.
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.
Yes, I think from_bytes()
can be easily made into a const fn
(by using the existing unchecked
code). Probably can wait for another PR though.
pub fn mul_shift_var(&self, b: &Self, shift: usize) -> Self { | ||
debug_assert!(shift >= 256); | ||
|
||
fn ifelse(c: bool, x: u64, y: u64) -> u64 { if c {x} else {y} } |
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.
That's uhh... interesting 😉
#[cfg(feature = "arithmetic")] | ||
mod mul; |
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.
I feel like there's maybe a larger discussion to be had here about refactoring the arithmetic
module, but we can save that for another PR.
@fjarri About your doubts, @roconnor-blockstream wrote here a proof that 2^272 suffices: https://github.com/roconnor-blockstream/secp256k1/blob/7f4ba006e5c1495f7601b64d4466d8dcf69e15cf/endomorphism-proof.pdf Would love to hear your thoughts on this :) |
May fix issue #24, or at least come close to fixing it. On my machine, the multiplication speed without endomorphism is 104us, and with endomorphism 75us.
Current version uses a simple windowed multiplication.
libsecp256k1
has the following improvements on top of that:This is implemented, and enabled by
endomorphism-mul
. I have some doubts about the validity of the approximation used inlibsecp256k1
, see my comment inmul.rs
("@fjarri").I checked it out, and it performs the same as the one from Ristretto, with the latter being considerably simpler. So I used the Ristretto one. (It does have a hardcoded window size, but generalizing to other window sizes is trivial, if needed).
add_mixed()
later to add them to the (projective) accumulatorThis introduces non-constant-timeness wrt the point, and requires a check for the point not being infinite. Not for this PR.