Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add a Math.inv function that inverse a number in Z/nZ #4839

Merged
merged 28 commits into from
Jan 24, 2024

Conversation

Amxx
Copy link
Collaborator

@Amxx Amxx commented Jan 17, 2024

This is a very common function use in finite fields such as the ones that power ECDSA curves.

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

@Amxx Amxx added this to the 5.1 milestone Jan 17, 2024
Copy link

changeset-bot bot commented Jan 17, 2024

🦋 Changeset detected

Latest commit: f683c96

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Amxx Amxx requested a review from ernestognw January 17, 2024 14:57
@Amxx Amxx added feature New contracts, functions, or helpers. Cryptography labels Jan 17, 2024
@Amxx
Copy link
Collaborator Author

Amxx commented Jan 17, 2024

Some ressources:

  • A solidity implementation that uses signed numbers instead of addmod and mulmod. Its solidity 0.5.0, and porting it nativelly in 0.8.0 causes overflows that showed up when doing fuzzing.
  • Noble's implementation in typescript: they use bigint so they don't have to do the modulo at every step. The gcd = 1 check comes from there.

All these are based on the Euclidean algorithm, and Bezout's identity. To find r such that a * r + p * k = 1 which is equivalent to saying a * r = 1 (mod p). This works when a and p are relative primes, which is always the case if p is prime, and which is verified by checking that the gcd of a and p is 1.

@ernestognw
Copy link
Member

ernestognw commented Jan 19, 2024

This is my current understanding:

  • Z is the set of natural numbers (i.e. {… , ⁻2, ⁻1, 0, 1, 2, …})
  • Z/nZ is the set of natural numbers numbers mod n (i.e. {0, 1, . . . , n − 1})

What we're providing with this function is the modular multiplicative inverse of an uint256 a in a field defined by n (it's uint256 p in the function implementation currently).

The multiplicative inverse of a is an integer x such that the product ax is congruent. This is that ax ≡ 1 (mod n), and the way to calculate such thing is using the Euclidean algorithm (runs in O(log2(n))) to find the greatest common divisor (gcd for short), and, (if gcd == 1), then the inverse x can be found from the Bezout's identity that states:

ax + ny = gcd = 1

ax + ny = 1

ax = 1 + (-y)n

ax ≡ 1 (mod n) # We've found `x`

If the gcd of a, n (i.e. gcd(a,n)) is 1, it means they're relative primes. So, for a field n to have multiplicative inverses for every value in the field, then n should be prime. This is because if n is prime, then the amount of relative primes < n is n - 1 (it excludes only the 0).

}

function testInvModP256(uint256 seed) public {
uint256 p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff; // prime
Copy link
Member

Choose a reason for hiding this comment

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

Wow now I get why the rumors of a backdoor in secp256r1, this is a weird number

ernestognw
ernestognw previously approved these changes Jan 23, 2024
Copy link
Member

@ernestognw ernestognw left a comment

Choose a reason for hiding this comment

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

LGTM

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Cryptography feature New contracts, functions, or helpers.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants