-
Notifications
You must be signed in to change notification settings - Fork 4
RFC: Add enum ReprEndianness constant to PrimeField trait
#4
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
Open
tarcieri
wants to merge
1
commit into
zkcrypto:main
Choose a base branch
from
tarcieri:ff-prime-field-repr-endianness
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| - Feature Name: `ff_prime_field_repr_endianness` | ||
| - Start Date: 2026-06-04 | ||
| - RFC PR: [zkcrypto/rfcs#4](https://github.com/zkcrypto/rfcs/pull/4) | ||
| - Tracking Issue: [zkcrypto/group#78](https://github.com/zkcrypto/group/issues/78) | ||
|
|
||
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| Add an enum with variants for distinguishing little versus big endian to the `ff` crate, as well | ||
| as an associated constant value of this type to the `PrimeField` trait, in order to handle cases | ||
| where curves use a big endian rather than little endian scalar serialization. | ||
|
|
||
| # Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| Almost all the elliptic curve implementations maintained by the @RustCrypto organization use the | ||
| SEC1 serialization for field elements, which is big endian. | ||
|
|
||
| This primarily becomes a problem when trying to combine scalars using a big endian serialization | ||
| for `PrimeField::Repr` with the `group` crate's w-NAF implementation, which assumes a little endian | ||
| ordering despite the documentation for `PrimeField::to_repr` stipulating that "The endianness of the | ||
| byte representation is implementation-specific. Generic encodings of field elements should be | ||
| treated as opaque." | ||
|
|
||
| Downstream consumers of `PrimeField::Repr`, including the `group` crate, need to instead handle it | ||
| in an endian-aware manner. | ||
|
|
||
| # Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| Adds a new `enum` to the public API of `ff` with the following shape (e.g. inspired by the | ||
| `ff_derive` crate): | ||
|
|
||
| ```rust | ||
| #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] | ||
| pub enum ReprEndianness { | ||
| Big, | ||
| #[default] | ||
| Little, | ||
| } | ||
|
Comment on lines
+36
to
+40
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. I chose this name based on |
||
| ``` | ||
|
|
||
| An associated constant added to `PrimeField`, with a default of `ReprEndianness::Little` in order | ||
| to ensure the addition is non-breaking, can now be used to determine how to iterate over the bits | ||
| of `PrimeField::Repr`: | ||
|
|
||
| ```rust | ||
| pub trait PrimeField { | ||
| type Repr: ...; | ||
|
|
||
| const REPR_ENDIANNESS: ReprEndianness = ReprEndianness::Little; | ||
| } | ||
| ``` | ||
|
|
||
| Any downstream code consuming `PrimeField::Repr` in a non-opaque manner will need to consult this | ||
| constant when attempting to iterate over its bytes. | ||
|
|
||
| # Reference-level explanation | ||
| [reference-level-explanation]: #reference-level-explanation | ||
|
|
||
| The w-NAF implementation in `group` can now consult this constant when e.g. constructing | ||
| `LimbBuffer` in order to determine the order in which to iterate over the bytes of the serialized | ||
| scalar. | ||
|
|
||
| Nothing else in the API needs to change besides this: downstream users of the w-NAF implementation | ||
| will now have it automatically work when they use it in conjunction with an elliptic curve whose | ||
| scalars use a big endian serialization, once the curve implementation sets the constant to | ||
| `ReprEndianness::Big` when appropriate. | ||
|
|
||
| # Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| The main drawback of this proposed approach is it leaves the door open to potential misuse where | ||
| the associated `const` for `ReprEndianness` isn't checked/honored and code continues to assume that | ||
| `PrimeField::Repr` is always little endian. | ||
|
|
||
| It doesn't present a single API that makes it easy to iterate over the bits of a field element | ||
| irrespective of the endianness in which `Repr` is serialized, though such an API could potentially | ||
| be added in a followup. This proposal deliberately sidesteps defining such an API in order to keep | ||
| the scope and code surface as simple as possible. | ||
|
|
||
| # Rationale and alternatives | ||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||
|
|
||
| Without some way for curve implementations to signal the endianness of `PrimeField::Repr` it's not | ||
| possible to use curves with a big endian serialization thereof with generic code that needs to | ||
| iterate over the bits of a field element. | ||
|
|
||
| The main alternative would be to define an API that allows for iterating over the bits of a | ||
| serialized field element in least-to-most significant order, such as `PrimeField::to_le_bits`, | ||
| returning a type bounded by something like `(Into)Iterator<Item = bool>`, e.g. a new public iterator | ||
| type adapted from e.g. `LimbBuffer` that can be used generically over curves, a new associated | ||
| type, or potentially using RPIT such that the type can be completely opaque. | ||
|
|
||
| Such an API can still be added in a followup, and defined generically by having it automatically | ||
| consult the associated `const` for the endianness. | ||
|
|
||
| # Prior art | ||
| [prior-art]: #prior-art | ||
|
|
||
| Two places we see prior art for at least the `enum` portion of this proposal: | ||
|
|
||
| - `ff_derive`: has an `enum ReprEndianness`, unfortunately due to the nature of proc macro crates | ||
| it will always need to define its own `enum` separate from everything else. | ||
| - `primefield`: @RustCrypto's generic implementation of prime fields which implement the traits | ||
| from `ff` defines an `enum ByteOrder` with `LittleEndian` and `BigEndian` variants, but can switch | ||
| to the `enum` from `ff` when added to the public API. | ||
|
|
||
| # Unresolved questions | ||
| [unresolved-questions]: #unresolved-questions | ||
|
|
||
| The main unresolved question is how to present an API which can consult the associated `const` which | ||
| defines the `ReprEndianness` and from it provide consistent behavior to downstream users, such that | ||
| downstream code doesn't need to check it and can always e.g. iterate over the bits of a field | ||
| element in least-to-most significant order regardless of the field element serialization,. | ||
|
|
||
| # Future possibilities | ||
| [future-possibilities]: #future-possibilities | ||
|
|
||
| The main future possibility, as sketched out in the "Rationale and alternatives" section, is to | ||
| provide a higher-level API which abstracts over the endianness so downstream code doesn't need to | ||
| be aware of it at all. | ||
|
|
||
| This would be beneficial for preventing misuses where the endianness is incorectly assumed to always | ||
| be little endian. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 may have misunderstood the purpose of this section versus "Reference-level explanation" and could potentially swap them?