-
Notifications
You must be signed in to change notification settings - Fork 477
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
Make scalars always reduced #519
Conversation
…alars are now reduced mod l
…nd legacy_compatibility
I like the API and the direction. Preventing the clamped scalars from leaking as returned values seems like an elegant approach, while covering the important use cases. |
@jrose-signal would you mind taking a look at this and verifying it wouldn't break your libraries? |
@@ -342,6 +370,9 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a MontgomeryPoint { | |||
W: FieldElement::ONE, | |||
}; | |||
|
|||
// NOTE: The below swap-double-add routine skips the first iteration, i.e., it assumes the | |||
// MSB of `scalar` is 0. This is allowed, since it follows from Scalar invariant #1. | |||
|
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 noticed that this was the case because the following test fails.
let a_bytes = [0xff; 32];
let s1 = Scalar::from_bytes_mod_order(a_bytes);
let s2 = Scalar { bytes: a_bytes };
assert_eq!(
s1 * constants::X25519_BASEPOINT,
s2 * constants::X25519_BASEPOINT
);
That's kinda surprising. There's no NAF or any fancy arithmetic necessary for the Montgomery ladder. Anyways, neither here nor there, because it works for all scalars < 2^255, which is all we care about.
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 mean, that's expected for me. One scalar is reduced, the other is not, but Scalar { bytes: a_bytes }
would not be valid under Scalar
's (new) invariant and can't be constructed outside the crate due to field visibility.
sidebar: I'm curious if Scalar
could be made into a newtype for ScalarImpl
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.
Right, well it's only surprising because there's nothing inherent about Montgomery multiplication that even requires invariant 1 in the first place. In fact, if you change the below line to
let mut bits = core::iter::once(0).chain(scalar.bits_le().rev());
then these asserts actually pass.
What is ScalarImpl
?
Yes, this is fine for us (and I had someone more cryptographer than me confirm). I did find one place we were using |
…mpatibility to Makefile and docsrs flags
@jrose-signal thanks for the quick turnaround! We'll be propagating the API change to ed- and x- soon |
We currently use the (Specifically, this is needed for something similar to Moller04 https://www.bmoeller.de/pdf/pke-pseudo-esorics2004.pdf) |
@elichai would a dedicated API for this, e.g. |
Yep, that would definitely work |
What do you think about this function also overriding invariant #1? i.e. it will allow multiplying with the top bit on, so it can't assume the first bit is zero as in: https://docs.rs/curve25519-dalek/latest/src/curve25519_dalek/montgomery.rs.html#373-374 |
Perhaps it would need to be fallible and return an e.g. |
Why fallible? if it's bigger than |
Do you have a different solution in mind for it overriding the invariants? |
I think it'd be possible to just extend the iterator to all bits. I'd prefer to not copy the code, so I'll just modify this function and see if we get a performance regression |
…l_bits_be` (#555) There is occasionally [a need](#519 (comment)) to multiply a non-prime-order Montgomery point by an integer. There's currently no way to do this, since our only methods are multiplication by `Scalar` (doesn't make sense in the non-prime-order case), and `MontgomeryPoint::mul_base_clamped` clamps the integer before multiplying. This defines `MontgomeryPoint::mul_bits_be`, which takes a big-endian representation of an integer and multiplies the point by that integer. Its usage is not recommended by default, but it is also not so unsafe as to be gated behind a `hazmat` feature.
This makes unreduced
Scalar
unrepresentable by removingScalar::{from_bits, from_bytes_clamped}
. As a consequence, we can remove the additional reduction steps in scalar addition and subtraction. Benchmarks on my M1 Macbook Air show runtime diffs of -52% and -67% on scalar addition and subtraction, respectively.Since clamping is necessary elsewhere, this change exposes new methods
MontgomeryPoint::{mul_clamped, mul_base_clamped}
,EdwardsPoint::{mul_clamped, mul_base_clamped}
, andBasepointTable::mul_base_clamped
, which take byte sequences.This partially addresses #507 (scalar performance) and #514 (scalar arithmetic well-definedness).
The first snippet of #514 points out that it is possible to construct a clamped scalar
s
and a pointP
such thats * P
ands.reduce() * P
are not equal. This change makes this test case impossible to represent, since unreduced scalars are now unrepresentable.Upstream changes
dalek-cryptography/ed25519-dalek#293
dalek-cryptography/x25519-dalek#120