-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add BLS Elliptic Curve support in Cryptol #14
base: main
Are you sure you want to change the base?
Conversation
This will be needed for the `bls_modular_inverse` function.
These two functions are hardcoded to work for the BLS_MODULUS. There is currently no need to abstract them further. These functions match the implementations found in the Python specs: - https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/polynomial-commitments.md#bls_modular_inverse - https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/polynomial-commitments.md#div
See https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/polynomial-commitments.md#compute_powers Note that because of the way Cryptol's type system and comprehensions work, we need to have the type constrain of `n >= 2`, whereas the Python spec only constrains `n >= 0`.
We need to use a GCD algorithm because Cryptol cannot perform expoentiation with negative exponents. This means we have an error in the BlsHelpers::bls_modular_inverse function. I'll fix that next.
Have bls_modular_inverse and bls_div call the corresponding utility functions with the hardcoded BLS_MODULUS. This fixes the previous error in the bls_modular_inverse function. This error was uncaught because the unit tests were wrong. The unit tests have now been confirmed with the following site: https://planetcalc.com/3298/
I switched the API to use the Integer type for these utility functions because it simplifies the code. The BlsHelper functions that call these utility functions have been updated and now handle the conversion of types (Integral <=> Integer) internally. This also exposed an error in the modular_inverse when a negative number was passed. A new test has been added to make sure we don't regress.
These were redundant and missed during the previous commit redesign of types.
And add note to the BlsHelpers::bls_modular_inverse about the inability to use pow and why that code now looks different than the Python spec.
These are needed as the building blocks as we start to implement the rest of the Deneb Python spec.
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've only partly reviewed this but I'm going to give some feedback now since I think you started getting similar feedback in a separate channel.
I'm seeing some mixing of types that I think is introducing more potential bugs than you need. Some code uses bit vectors (integers mod 2^n
for the width n
), but they are actually holding integers mod p
for p =\= 2^n
, and also in other places they are converted to Integer
s.
Unless you are trying to do produce highly optimized implementations of these functions that minimize memory usage or something, I think you are better off using the Z
type to represent your numbers. I've flagged several overflow concerns; these will all be handled by Cryptol if you use Z
. The EC implementation in cryptol-specs uses that type if you would like to see it in practice.
spec/Common/Utils.cry
Outdated
pow : {n} (fin n, n >= 1) => [n] -> [n] -> [n] -> [n] | ||
pow base exp modulus = (base ^^ exp) % modulus |
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.
todo: I think you need to be more careful here to prevent overflow messing with your modular reductions. If the exponentiation overflows [n]
, it'll get reduce mod 2^n
, which might not be the same as your modulus
.
E.g. take n = 3
and (2, 3, 3). I think what you want is
I think the easiest solution will be something like use zext
to increase the width of the parameters, then do your computation, then truncate back to n
with confidence because the mod ensures that the result is no wider than n
bits.
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.
Note: I see this got removed eventually but here is a mod pow implementation using vectors in cryptol-specs.
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.
Yeah, this was wrong, and I determined I need a faster implementation anyway, which is provided with Z
.
Can I import from cryptol-specs
without having to pull in that source?
type BlsFieldSize = 256 | ||
type BlsFieldElement = [BlsFieldSize] |
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.
question: BLS field elements are in the integers mod group w.r.t. the BLS modulus, right? Did you consider using Cryptol's integer mod group Z
? I have a vague sense that it makes the SMT constraints a bit messier but it might simplify some of the code in here.
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 just learned about Z
. I had previously missed it in the manual.
/** | ||
* Compute the modular inverse of x | ||
* i.e. return y such that x * y % BLS_MODULUS == 1 | ||
* Precondition: x != 0 |
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.
todo: specifically, x != 0 mod BLS_MODULUS
.
spec/Spec/BlsHelpers.cry
Outdated
bls_div : BlsFieldElement -> BlsFieldElement -> BlsFieldElement | ||
bls_div x y = (x * (bls_modular_inverse y)) % BLS_MODULUS |
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.
todo: I'm worried about overflow here, too.
Here's an implementation of mod mul in cryptol specs, that uses an extension like I mentioned to do safe multiplication and mod.
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 think you're running into this because of a language difference between Python's happily expandable integers and Cryptol's extremely fixed-width ones, so there will be some discrepancy between the spec and your implementation.
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, you are 💯% correct. I'll fix these.
*/ | ||
compute_powers : {n} (fin n, width n <= 64, n >= 2) => BlsFieldElement -> [n]BlsFieldElement | ||
compute_powers x = powers where | ||
powers = [x] # [(powers@(i-1) * x) % BLS_MODULUS | i <- [1 .. n-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.
todo: I remain worried about overflow!! Can you make a single modmul
function somewhere and use it in all these places?
@@ -70,3 +80,15 @@ property test_bls_modular_inverse x expectedY = | |||
test_bls_div : BlsFieldElement -> BlsFieldElement -> BlsFieldElement -> Bit | |||
property test_bls_div x y expected = | |||
bls_div x y == expected | |||
|
|||
/** | |||
* Unit tests for `bls_modular_inverse`. |
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.
todo: I think this is implied for you but to make it explicit once: for each of my overflow comments, I suggest that you add a unit test that pokes at that upper bound, to make sure it's handled correctly.
/** | ||
* G1 elliptic curve point | ||
*/ | ||
type G1Point = | ||
{ xCoord : Bytes48 | ||
, yCoord : Bytes48 | ||
} |
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.
How do you represent the point at infinity? It doesn't have a set of affine coordinates but there are often conditionals in various algorithms that require checking for it. For the prime field elliptic curves I used an enum but that is not supported by SAW yet so might not be an option for this project.
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'm using (0, 0) for infinity --- I think that gets added in a later commit.
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.
As in my next PR.
x = toInteger point.xCoord | ||
y = toInteger point.yCoord | ||
modulus = toInteger Fp | ||
m = div (3 * x^^2) (2 * y) modulus |
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.
suggestion: consider renaming div
to div_mod
if you keep this function. modulus
is pretty clear but it's not immediately obvious why div has 3 parameters.
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 like this suggestion. I'm thinking though if I change it to use Z
then I don't need the extra parameter, so hopefully that is more clear. I only used the name div
because it matches the Python spec, but I prefer your suggestion, even as I go with using Z
.
Thanks for pointing out the |
Add the elliptic curve operations needed to continue to implement the Deneb Python spec BLS helper functions.
(Note for reviewers: I've made an effort to make this PR more easily reviewed on a per-commit basis. Hope this helps).