P256.t.sol 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.20;
  3. import {Test} from "forge-std/Test.sol";
  4. import {P256} from "@openzeppelin/contracts/utils/cryptography/P256.sol";
  5. import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
  6. contract P256Test is Test {
  7. /// forge-config: default.fuzz.runs = 512
  8. function testVerify(bytes32 digest, uint256 seed) public {
  9. uint256 privateKey = _asPrivateKey(seed);
  10. (bytes32 x, bytes32 y) = P256PublicKey.getPublicKey(privateKey);
  11. (bytes32 r, bytes32 s) = vm.signP256(privateKey, digest);
  12. s = _ensureLowerS(s);
  13. assertTrue(P256.verify(digest, r, s, x, y));
  14. assertTrue(P256.verifySolidity(digest, r, s, x, y));
  15. }
  16. /// forge-config: default.fuzz.runs = 512
  17. function testRecover(bytes32 digest, uint256 seed) public {
  18. uint256 privateKey = _asPrivateKey(seed);
  19. (bytes32 x, bytes32 y) = P256PublicKey.getPublicKey(privateKey);
  20. (bytes32 r, bytes32 s) = vm.signP256(privateKey, digest);
  21. s = _ensureLowerS(s);
  22. (bytes32 qx0, bytes32 qy0) = P256.recovery(digest, 0, r, s);
  23. (bytes32 qx1, bytes32 qy1) = P256.recovery(digest, 1, r, s);
  24. assertTrue((qx0 == x && qy0 == y) || (qx1 == x && qy1 == y));
  25. }
  26. function _asPrivateKey(uint256 seed) private pure returns (uint256) {
  27. return bound(seed, 1, P256.N - 1);
  28. }
  29. function _ensureLowerS(bytes32 s) private pure returns (bytes32) {
  30. uint256 _s = uint256(s);
  31. unchecked {
  32. return _s > P256.N / 2 ? bytes32(P256.N - _s) : s;
  33. }
  34. }
  35. }
  36. /**
  37. * @dev Library to derive P256 public key from private key
  38. * Should be removed if Foundry adds this functionality
  39. * See https://github.com/foundry-rs/foundry/issues/7908
  40. */
  41. library P256PublicKey {
  42. function getPublicKey(uint256 privateKey) internal view returns (bytes32, bytes32) {
  43. (uint256 x, uint256 y, uint256 z) = _jMult(P256.GX, P256.GY, 1, privateKey);
  44. return _affineFromJacobian(x, y, z);
  45. }
  46. function _jMult(
  47. uint256 x,
  48. uint256 y,
  49. uint256 z,
  50. uint256 k
  51. ) private pure returns (uint256 rx, uint256 ry, uint256 rz) {
  52. unchecked {
  53. for (uint256 i = 0; i < 256; ++i) {
  54. if (rz > 0) {
  55. (rx, ry, rz) = _jDouble(rx, ry, rz);
  56. }
  57. if (k >> 255 > 0) {
  58. if (rz == 0) {
  59. (rx, ry, rz) = (x, y, z);
  60. } else {
  61. (rx, ry, rz) = _jAdd(rx, ry, rz, x, y, z);
  62. }
  63. }
  64. k <<= 1;
  65. }
  66. }
  67. }
  68. /// From P256.sol
  69. function _affineFromJacobian(uint256 jx, uint256 jy, uint256 jz) private view returns (bytes32 ax, bytes32 ay) {
  70. if (jz == 0) return (0, 0);
  71. uint256 zinv = Math.invModPrime(jz, P256.P);
  72. uint256 zzinv = mulmod(zinv, zinv, P256.P);
  73. uint256 zzzinv = mulmod(zzinv, zinv, P256.P);
  74. ax = bytes32(mulmod(jx, zzinv, P256.P));
  75. ay = bytes32(mulmod(jy, zzzinv, P256.P));
  76. }
  77. function _jDouble(uint256 x, uint256 y, uint256 z) private pure returns (uint256 rx, uint256 ry, uint256 rz) {
  78. uint256 p = P256.P;
  79. uint256 a = P256.A;
  80. assembly ("memory-safe") {
  81. let yy := mulmod(y, y, p)
  82. let zz := mulmod(z, z, p)
  83. let s := mulmod(4, mulmod(x, yy, p), p) // s = 4*x*y²
  84. let m := addmod(mulmod(3, mulmod(x, x, p), p), mulmod(a, mulmod(zz, zz, p), p), p) // m = 3*x²+a*z⁴
  85. let t := addmod(mulmod(m, m, p), sub(p, mulmod(2, s, p)), p) // t = m²-2*s
  86. // x' = t
  87. rx := t
  88. // y' = m*(s-t)-8*y⁴
  89. ry := addmod(mulmod(m, addmod(s, sub(p, t), p), p), sub(p, mulmod(8, mulmod(yy, yy, p), p)), p)
  90. // z' = 2*y*z
  91. rz := mulmod(2, mulmod(y, z, p), p)
  92. }
  93. }
  94. function _jAdd(
  95. uint256 x1,
  96. uint256 y1,
  97. uint256 z1,
  98. uint256 x2,
  99. uint256 y2,
  100. uint256 z2
  101. ) private pure returns (uint256 rx, uint256 ry, uint256 rz) {
  102. uint256 p = P256.P;
  103. assembly ("memory-safe") {
  104. let zz1 := mulmod(z1, z1, p) // zz1 = z1²
  105. let zz2 := mulmod(z2, z2, p) // zz2 = z2²
  106. let u1 := mulmod(x1, zz2, p) // u1 = x1*z2²
  107. let u2 := mulmod(x2, zz1, p) // u2 = x2*z1²
  108. let s1 := mulmod(y1, mulmod(zz2, z2, p), p) // s1 = y1*z2³
  109. let s2 := mulmod(y2, mulmod(zz1, z1, p), p) // s2 = y2*z1³
  110. let h := addmod(u2, sub(p, u1), p) // h = u2-u1
  111. let hh := mulmod(h, h, p) // h²
  112. let hhh := mulmod(h, hh, p) // h³
  113. let r := addmod(s2, sub(p, s1), p) // r = s2-s1
  114. // x' = r²-h³-2*u1*h²
  115. rx := addmod(addmod(mulmod(r, r, p), sub(p, hhh), p), sub(p, mulmod(2, mulmod(u1, hh, p), p)), p)
  116. // y' = r*(u1*h²-x')-s1*h³
  117. ry := addmod(mulmod(r, addmod(mulmod(u1, hh, p), sub(p, rx), p), p), sub(p, mulmod(s1, hhh, p)), p)
  118. // z' = h*z1*z2
  119. rz := mulmod(h, mulmod(z1, z2, p), p)
  120. }
  121. }
  122. }