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

Implement ff and group traits #473

Closed
wants to merge 5 commits into from
Closed

Conversation

str4d
Copy link
Contributor

@str4d str4d commented Dec 12, 2022

These are placed behind the new group feature flag.

@tarcieri tarcieri force-pushed the have-scalar-from-canonical-bytes-return-ctoption branch from c51ba24 to e665a4c Compare December 12, 2022 14:28
@str4d
Copy link
Contributor Author

str4d commented Dec 12, 2022

I'll rebase this.

@str4d str4d changed the base branch from have-scalar-from-canonical-bytes-return-ctoption to release/4.0 December 12, 2022 22:39
These are placed behind the new `ff` feature flag.
@str4d
Copy link
Contributor Author

str4d commented Dec 12, 2022

Rebased on release/4.0.

@daxpedda
Copy link

Cc #373.

@str4d
Copy link
Contributor Author

str4d commented Dec 16, 2022

I haven't added group trait impls in this PR because there are upcoming breaking changes to some of those traits that I'm hoping to make soon, and we can avoid needing to bring them in as part of a 5.0 if we wait until after that.

@tarcieri
Copy link
Contributor

tarcieri commented Dec 16, 2022

@str4d you could add ff and group to these SemVer exceptions, which allow for breaking upgrades around minor versioned releases of curve25519-dalek rather than major versions: https://github.com/dalek-cryptography/curve25519-dalek#public-api-semver-exemptions

@str4d
Copy link
Contributor Author

str4d commented Dec 16, 2022

If that's a strategy that the users of this crate (specifically the users of the ff trait impls) are happy with, then that's fine with me. It's definitely easier to set this up from the start, and doesn't affect anyone not enabling the ff feature. It does mean that cargo update doesn't work without specific care, so I'd like the README or somewhere else to give a bit of guidance on how crate users should handle these minor version bumps if they can't follow the update yet.

@tarcieri
Copy link
Contributor

@str4d if you had the full traits wired up, you could just have a group feature which pulls ff in as group::ff.

I expect most users who care about the ff impls also want group (here's one anecdote) and it would make for one fewer knob.

Re: making cargo update work, it's fine if you use this sort of SemVer predicate:

curve25519-dalek = ">= 5, < 5.1"

...which will still allow for patch-level updates, though users would need to keep an eye out for minor version updates.

These ranges can also be expanded once it's been verified that a minor version bump doesn't affect functionality a given downstream dependent crate happens to be using, e.g.

curve25519-dalek = ">= 5.1, < 5.4"

@str4d
Copy link
Contributor Author

str4d commented Dec 18, 2022

@str4d if you had the full traits wired up, you could just have a group feature which pulls ff in as group::ff.

I expect most users who care about the ff impls also want group (here's #373 (comment)) and it would make for one fewer knob.

Given that ff would be optional, we get an implicit knob for it anyway (until MSRV is at least 1.60, at which point the implicit feature for an optional crate can be disabled). So we may as well have it behave correctly (i.e. have it enable rand_core).

...which will still allow for patch-level updates, though users would need to keep an eye out for minor version updates.

These ranges can also be expanded once it's been verified that a minor version bump doesn't affect functionality a given downstream dependent crate happens to be using

If I understand you correctly, the recommended workflow for using these "breaking changes in minor version bumps" feature flags is:

  • When you enable the feature flag, bound your crate's dependency to < X.{Y+1} where X.Y is the latest release.
  • When X.{Y+1}.0 is released, check that it works for you, and then cut a backwards-compatible point release of your crate with the bound increased to < X.{Y+2}.

It does require proactivity, but it seems fine as long as it is documented. So assuming my understanding is correct, I'm fine with adding group trait impls to this PR as well, or I can add them in a separate PR (as per above, we'll be keeping the ff feature flag in the near term).

@tarcieri
Copy link
Contributor

tarcieri commented Dec 18, 2022

Given that ff would be optional, we get an implicit knob for it anyway (until MSRV is at least 1.60, at which point the implicit feature for an optional crate can be disabled). So we may as well have it behave correctly (i.e. have it enable rand_core).

What you're suggesting would work just fine with namespaced features in 1.60 if you did something like this:

[dependencies]
ff = { version = "0.13", optional = true }
group = { version = "0.13", optional = true }
rand_core = { version = "0.6", optional = true }

[features]
ff = ["dep:ff", "rand_core"]
group = ["dep:group", "ff"]

However prior to 1.60 you can't rely on feature unification to activate other features in the same crate by way of transitive dependencies, i.e. if you just have this:

[dependencies]
ff = { version = "0.13", optional = true }
group = { version = "0.13", optional = true }
rand_core = { version = "0.6", optional = true }

Activating the ff feature will not automatically activate the rand_core feature, nor will activating the group feature automatically activate ff and rand_core features, even though ff has rand_core as a transitive dependency and group has ff as a transitive dependency.

@str4d
Copy link
Contributor Author

str4d commented Dec 18, 2022

I know, which is why the current PR uses the crate renaming trick to change the name of the implicit feature, so we can instead add an explicit feature that does have the correct setup. I was just arguing that we may as well preserve this because we're going to have an ff feature in the simple case. An alternative would be to use the renaming trick to "hide" the implicit feature and not add an explicit one, but that feels like busywork (as the renamed implicit feature is still an implicit feature).

@str4d
Copy link
Contributor Author

str4d commented Dec 19, 2022

Oh wait, I'm not tired now and see what you meant. Accessing ff as group::ff means we don't need an explicit dependency on ff, which means we don't have the implicit feature flag. Sure, that works fine for me, and we can always add the ff feature flag in a backwards-compatible way if users say they want it.

@str4d
Copy link
Contributor Author

str4d commented Dec 19, 2022

This PR now includes implementations of the group traits. A required crate::edwards::SubgroupPoint struct is introduced, to enable impl CofactorGroup for EdwardsPoint. I have not implemented group::Curve etc. because curve25519-dalek does not (currently) expose an affine representation of an Edwards point.

@str4d str4d changed the title Implement ff traits for Scalar Implement ff and group traits Dec 19, 2022
@str4d
Copy link
Contributor Author

str4d commented Dec 19, 2022

Force-pushed to fix test imports.

src/edwards.rs Outdated Show resolved Hide resolved
src/edwards.rs Outdated Show resolved Hide resolved
src/edwards.rs Outdated Show resolved Hide resolved
src/ristretto.rs Outdated Show resolved Hide resolved
@str4d
Copy link
Contributor Author

str4d commented Dec 20, 2022

Force-pushed to address most of @tarcieri's comments. I'll add a follow-up commit with the de-duplication of decompression.

Copy link
Contributor

@rozbb rozbb left a comment

Choose a reason for hiding this comment

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

Looks good, thank you! Left a few comments. Structurally, I think it also might be cleaner to put all the trait impls into a separate file. edwards.rs has almost 1/4 of its non-test code spent on impls.

@@ -260,6 +267,27 @@ impl CompressedRistretto {
///
/// - `None` if `self` was not the canonical encoding of a point.
pub fn decompress(&self) -> Option<RistrettoPoint> {
let (s_encoding_is_canonical, s_is_negative, s) = decompress::step_1(self);

if s_encoding_is_canonical.unwrap_u8() == 0u8 || s_is_negative.unwrap_u8() == 1u8 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing: unwrap_u8 -> bool::from

pub fn decompress(&self) -> Option<EdwardsPoint> {
let Y = FieldElement::from_bytes(self.as_bytes());
let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self);
if is_valid_y_coord.unwrap_u8() != 1u8 {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd use the bool::from impl here rather than unwrap_u8()

group::ff::helpers::sqrt_tonelli_shanks(
self,
[
0xcb02_4c63_4b9e_ba7d,
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this constant? Put a comment here

0, 0, 0,
],
};
const S: u32 = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

What is S?

Copy link
Contributor

Choose a reason for hiding this comment

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

FWIW these constants are documented in the trait definition itself:

https://docs.rs/ff/latest/ff/trait.PrimeField.html#associatedconstant.S

0xef, 0x8c, 0xb5, 0x06,
],
};
const DELTA: Self = Self {
Copy link
Contributor

Choose a reason for hiding this comment

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

What is Delta?

Copy link
Contributor

Choose a reason for hiding this comment

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

MULTIPLICATIVE_GENERATOR ^ (2^s) mod p, i.e. s squarings of MULTIPLICATIVE_GENERATOR

],
};
const S: u32 = 2;
const ROOT_OF_UNITY: Self = Self {
Copy link
Contributor

Choose a reason for hiding this comment

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

Which root of unity is this?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's MULTIPLICATIVE_GENERATOR ^ t mod p where t = (modulus - 1) >> s

}

const MODULUS: &'static str =
"0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed";
Copy link
Contributor

Choose a reason for hiding this comment

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

Should cite this and/or add a test for it in the tests. This constant occurs in the l() function on page 52 of RFC 8032.

// DELTA^{t} mod m == 1
assert_eq!(
Scalar::DELTA.pow(&[
0x9604_98c6_973d_74fb,
Copy link
Contributor

Choose a reason for hiding this comment

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

What is t?

Copy link
Contributor

Choose a reason for hiding this comment

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

t = (modulus - 1) >> s

@@ -65,6 +66,7 @@ packed_simd = { version = "0.3.4", package = "packed_simd_2", features = ["into_
[features]
default = ["alloc"]
alloc = ["zeroize/alloc"]
group = ["group_crate", "rand_core"]
Copy link
Contributor

Choose a reason for hiding this comment

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

With #485 landed this can now be:

Suggested change
group = ["group_crate", "rand_core"]
group = ["dep:group", "rand_core"]

@ycscaly
Copy link

ycscaly commented Mar 28, 2023

What is preventing the merge?

@rozbb
Copy link
Contributor

rozbb commented Mar 29, 2023

This feature is taking a back seat to the other things we need before 4.0. This can likely come in 4.1.

@ycscaly
Copy link

ycscaly commented Mar 29, 2023

This feature is taking a back seat to the other things we need before 4.0. This can likely come in 4.1.

Thanks, and is there any rough estimation on when 4.1 will come?

Also, is the code here already working or needs much more work?

@tarcieri
Copy link
Contributor

@str4d if you have some time, would you mind rebasing this?

I think @rozbb intends to cut a 4.0 release soon, and it'd be good to at least preflight this PR prior to a release to make sure it can be merged afterward without breaking changes.

@dconnolly
Copy link

@str4d if you have some time, would you mind rebasing this?

I think @rozbb intends to cut a 4.0 release soon, and it'd be good to at least preflight this PR prior to a release to make sure it can be merged afterward without breaking changes.

4.0 is out 🎉

@tarcieri
Copy link
Contributor

Would love to get this merged soon if possible. People are still asking me about it.

@pinkforest
Copy link
Contributor

Might just move this along:

tarcieri pushed a commit that referenced this pull request Aug 27, 2023
Originally authored by @str4d as #473
@rozbb
Copy link
Contributor

rozbb commented Aug 28, 2023

Superseded by #562

@rozbb rozbb closed this Aug 28, 2023
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.

7 participants