Skip to content
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

curve25519: Add arbitrary integer multiplication with MontgomeryPoint::mul_bits_be #555

Merged
merged 10 commits into from
Aug 28, 2023

Conversation

rozbb
Copy link
Contributor

@rozbb rozbb commented Jul 30, 2023

There is occasionally a need 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.

So we define MontgomeryPoint::mul_bits_be, gated behind hazmat, which takes a big-endian representation of an integer and multiplies the point by that integer.

cc @elichai

@rozbb rozbb requested a review from tarcieri July 30, 2023 14:43
Copy link
Contributor

@elichai elichai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK ed53307 reviewed the code and it seems correct, haven't tested it yet

@rozbb
Copy link
Contributor Author

rozbb commented Aug 2, 2023

@tarcieri ready for review. One thing I wanna make sure of personally is that this hasn't strayed too far from Alg 8 of CS17

Comment on lines 398 to 399
fn mul_bits_be(self, bits: impl Iterator<Item = bool>) -> MontgomeryPoint {
self.mul_bits_be(bits)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to be pub?

It looks like it calls itself and infinitely recurses? Is it supposed to call self._mul_bits_be?

If the intent is for this to be pub, I worry about people accidentally discovering it. Feature-gating on hazmat alone isn't a great deterrent since other dependencies can activate the feature. Putting it in the hazmat module would better prevent accidental discovery/misuse IMO (and free up the mul_bits_be name so you don't need a _-prefixed version)

At the very least, it could use a more stringent warning label.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good points. I agree it'd be best to put it in the hazmat module. Will fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acutally, this is a question: should this method be feature-gated? There's two main misuses I can think of:

  1. If someone used this method instead of mul_clamped. This could leak up to 3 secret bits. Not great.
  2. If someone used this method instead of Mul<Scalar> for MontgomeryPoint. This would be fine, actually. In the worst case, this is equivalent to doing the scalar mul. In the best case, this is more correct than the scalar mul, because the point being multiplied might not be in the prime-order subgroup, and so the scalar mul made no sense anyway.

So the question is if case (1) is likely and worth avoiding. I'm not sure. If someone wanted to multiply by some arbitrary bytestring today, they can already do Scalar::from_bytes_mod_order(bytes) * point. If the bytestring is uniform, then this is already equivalent to mul_bits_be with 50% probability.

My vote is to let this function be public, and let it have the documentation note I just added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a wild suggestion, how do people think about a RingElement type that is over the full curve's order? is this too much for that kind of feature?

Copy link
Contributor

@tarcieri tarcieri Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My vote is to let this function be public, and let it have the documentation note I just added.

@rozbb seems okay if it comes with a warning and steers people towards the other options unless they know what they're doing.

Edit: looks like you do already have a note about using mul_clamped, so that's fine

is this too much for that kind of feature?

@elichai might be overkill

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RingElement is overkill imo. We don't define a way to add or multiply two numbers mod 8p, so it'd be a pretty useless ring element. Integer multiplication is broad enough for this use case imo

/// Curve25519 uses _clamped multiplication_, explained
/// [here](https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/).
/// When in doubt, use [`Self::mul_clamped`].
pub fn mul_bits_be(self, bits: impl Iterator<Item = bool>) -> MontgomeryPoint {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we care that this doesn't enforce how many bits are passed? ie you can pass an empty iter and it will be equivalent to multiplying by zero, or you can pass a longer iterator and compute a "longer" multiplication

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it use ExactSizeIterator and assert len == 256? that would still allow multiplying by something bigger than the curve's order though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like how it's infallible now.

Perhaps it could have a debug_assert!? That wouldn't necessarily require ExactSizeIterator, it would just need a sort of postcondition check that it iterated over 256 bits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why limit the function at all? Multiplication by arbitrary integers is always well-defined. If you don't want to reduce then you're free not to

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants