From 8279636382f72278b5a02c138b4dd924634cdf2c Mon Sep 17 00:00:00 2001 From: Nalin Date: Sat, 18 May 2024 22:03:39 -0700 Subject: [PATCH] P256.sol: support precompile as primary, use solidity as fallback (#43) * P256.sol: support precompile as primary, use solidity as fallback * P256.sol: add fallback test --- lcov.info | 661 ++++++++++++++++++++++++++---------------------- src/P256.sol | 20 +- test/P256.t.sol | 59 +++++ 3 files changed, 428 insertions(+), 312 deletions(-) diff --git a/lcov.info b/lcov.info index 95863d0..0796ed4 100644 --- a/lcov.info +++ b/lcov.info @@ -1,264 +1,276 @@ TN: SF:src/P256.sol -FN:10,P256.verifySignatureAllowMalleability -FNDA:6,P256.verifySignatureAllowMalleability -DA:17,6 -DA:17,6 -DA:17,6 -DA:18,6 -DA:18,6 -DA:18,6 -DA:19,6 -DA:19,6 -BRDA:19,0,0,- -BRDA:19,0,1,- -DA:21,6 -DA:21,6 -DA:21,6 -DA:21,6 -FN:28,P256.verifySignature +FN:13,P256.verifySignatureAllowMalleability +FNDA:9,P256.verifySignatureAllowMalleability +DA:20,9 +DA:20,9 +DA:20,9 +DA:22,9 +DA:22,9 +DA:22,9 +DA:23,9 +DA:23,9 +DA:23,9 +BRDA:23,0,0,8 +BRDA:23,0,1,1 +DA:27,1 +DA:27,1 +DA:27,1 +DA:27,1 +DA:30,8 +DA:30,8 +DA:30,8 +DA:33,8 +DA:33,8 +BRDA:33,1,0,- +BRDA:33,1,1,- +DA:35,8 +DA:35,8 +DA:35,8 +DA:35,8 +FN:42,P256.verifySignature FNDA:5,P256.verifySignature -DA:36,5 -DA:36,5 -BRDA:36,1,0,4 -BRDA:36,1,1,1 -DA:37,1 -DA:37,1 -DA:40,4 -DA:40,4 -DA:40,4 +DA:50,5 +DA:50,5 +BRDA:50,2,0,4 +BRDA:50,2,1,1 +DA:51,1 +DA:51,1 +DA:54,4 +DA:54,4 +DA:54,4 FNF:2 FNH:2 -LF:7 -LH:7 -BRF:4 -BRH:2 +LF:10 +LH:10 +BRF:6 +BRH:4 end_of_record TN: SF:src/P256Verifier.sol FN:26,P256Verifier. -FNDA:2573,P256Verifier. -DA:27,2573 -DA:27,2573 -BRDA:27,0,0,2572 +FNDA:2577,P256Verifier. +DA:27,2577 +DA:27,2577 +BRDA:27,0,0,2576 BRDA:27,0,1,1 DA:28,1 DA:28,1 DA:28,1 -DA:31,2572 -DA:31,2572 -DA:31,2572 -DA:32,2572 -DA:32,2572 -DA:32,2572 -DA:33,2572 -DA:33,2572 -DA:33,2572 -DA:34,2572 -DA:34,2572 -DA:34,2572 -DA:35,2572 -DA:35,2572 -DA:35,2572 -DA:37,2572 -DA:37,2572 -DA:37,2572 -DA:39,2572 -DA:39,2572 -DA:39,2572 +DA:31,2576 +DA:31,2576 +DA:31,2576 +DA:32,2576 +DA:32,2576 +DA:32,2576 +DA:33,2576 +DA:33,2576 +DA:33,2576 +DA:34,2576 +DA:34,2576 +DA:34,2576 +DA:35,2576 +DA:35,2576 +DA:35,2576 +DA:37,2576 +DA:37,2576 +DA:37,2576 +DA:39,2576 +DA:39,2576 +DA:39,2576 FN:70,P256Verifier.ecdsa_verify -FNDA:2572,P256Verifier.ecdsa_verify -DA:77,2572 -DA:77,2572 -DA:77,2572 -DA:77,2572 -DA:77,2572 -DA:77,2543 -DA:77,2339 -DA:77,2327 -BRDA:77,1,0,2275 +FNDA:2576,P256Verifier.ecdsa_verify +DA:77,2576 +DA:77,2576 +DA:77,2576 +DA:77,2576 +DA:77,2576 +DA:77,2547 +DA:77,2343 +DA:77,2331 +BRDA:77,1,0,2279 BRDA:77,1,1,297 DA:78,297 DA:78,297 -DA:81,2275 -DA:81,2275 -BRDA:81,2,0,2268 +DA:81,2279 +DA:81,2279 +BRDA:81,2,0,2272 BRDA:81,2,1,7 DA:82,7 DA:82,7 -DA:85,2268 -DA:85,2268 -DA:85,2268 -DA:87,2268 -DA:87,2268 -DA:87,2268 -DA:88,2268 -DA:88,2268 -DA:88,2268 -DA:90,2268 -DA:90,2268 -DA:90,2268 -DA:96,2268 -DA:96,2268 -DA:96,2268 -DA:96,2268 +DA:85,2272 +DA:85,2272 +DA:85,2272 +DA:87,2272 +DA:87,2272 +DA:87,2272 +DA:88,2272 +DA:88,2272 +DA:88,2272 +DA:90,2272 +DA:90,2272 +DA:90,2272 +DA:96,2272 +DA:96,2272 +DA:96,2272 +DA:96,2272 FN:103,P256Verifier.ecAff_isValidPubkey -FNDA:2275,P256Verifier.ecAff_isValidPubkey -DA:107,2275 -DA:107,2275 -DA:107,2275 -DA:107,2275 -DA:107,2274 -BRDA:107,3,0,2273 +FNDA:2279,P256Verifier.ecAff_isValidPubkey +DA:107,2279 +DA:107,2279 +DA:107,2279 +DA:107,2279 +DA:107,2278 +BRDA:107,3,0,2277 BRDA:107,3,1,1 DA:108,2 DA:108,2 -DA:111,2273 -DA:111,2273 -DA:111,2273 +DA:111,2277 +DA:111,2277 +DA:111,2277 FN:114,P256Verifier.ecAff_satisfiesCurveEqn -FNDA:2273,P256Verifier.ecAff_satisfiesCurveEqn -DA:118,2273 -DA:118,2273 -DA:118,2273 -DA:119,2273 -DA:119,2273 -DA:119,2273 -DA:120,2273 -DA:120,2273 -DA:122,2273 -DA:122,2273 -DA:122,2273 +FNDA:2277,P256Verifier.ecAff_satisfiesCurveEqn +DA:118,2277 +DA:118,2277 +DA:118,2277 +DA:119,2277 +DA:119,2277 +DA:119,2277 +DA:120,2277 +DA:120,2277 +DA:122,2277 +DA:122,2277 +DA:122,2277 FN:130,P256Verifier.ecZZ_mulmuladd -FNDA:2268,P256Verifier.ecZZ_mulmuladd -DA:136,2268 -DA:136,2268 -DA:137,2268 -DA:137,2268 -DA:138,2268 -DA:138,2268 -DA:139,2268 -DA:139,2268 -DA:140,2268 -DA:140,2268 -DA:142,2268 -DA:142,2268 -DA:142,2268 +FNDA:2272,P256Verifier.ecZZ_mulmuladd +DA:136,2272 +DA:136,2272 +DA:137,2272 +DA:137,2272 +DA:138,2272 +DA:138,2272 +DA:139,2272 +DA:139,2272 +DA:140,2272 +DA:140,2272 +DA:142,2272 +DA:142,2272 +DA:142,2272 DA:142,0 -BRDA:142,4,0,2268 +BRDA:142,4,0,2272 BRDA:142,4,1,- DA:142,0 -DA:145,2268 -DA:145,2268 -DA:147,2268 -DA:147,2268 -DA:148,2268 -DA:148,2268 -DA:151,3080 -DA:151,3080 -DA:152,3080 -DA:152,3080 -DA:153,3080 -DA:153,3080 -DA:154,3080 -DA:154,3080 -BRDA:154,5,0,812 -BRDA:154,5,1,2268 +DA:145,2272 +DA:145,2272 +DA:147,2272 +DA:147,2272 +DA:148,2272 +DA:148,2272 +DA:151,3088 +DA:151,3088 +DA:152,3088 +DA:152,3088 +DA:153,3088 +DA:153,3088 +DA:154,3088 +DA:154,3088 +BRDA:154,5,0,816 +BRDA:154,5,1,2272 DA:154,0 -DA:160,2268 -DA:160,2268 +DA:160,2272 +DA:160,2272 BRDA:160,6,0,836 -BRDA:160,6,1,1432 +BRDA:160,6,1,1436 DA:161,836 DA:161,836 -DA:162,1432 -DA:162,1432 -BRDA:162,7,0,709 -BRDA:162,7,1,723 -DA:163,709 -DA:163,709 -DA:164,723 -DA:164,723 -BRDA:164,8,0,723 -BRDA:164,8,1,723 -DA:165,723 -DA:165,723 -DA:168,2268 -DA:168,2268 -DA:169,2268 -DA:169,2268 -DA:170,579796 -DA:170,579796 -DA:171,577528 -DA:171,577528 -DA:173,577528 -DA:173,577528 -DA:174,577528 -DA:174,577528 -DA:176,577528 -DA:176,577528 -BRDA:176,9,0,147784 -BRDA:176,9,1,429744 -DA:177,147784 -DA:177,147784 -DA:178,429744 -DA:178,429744 -BRDA:178,10,0,146865 -BRDA:178,10,1,282879 -DA:179,146865 -DA:179,146865 -DA:180,282879 -DA:180,282879 -BRDA:180,11,0,139945 -BRDA:180,11,1,142934 -DA:181,139945 -DA:181,139945 -DA:183,142934 -DA:183,142934 -DA:186,429744 -DA:186,429744 -DA:189,2268 -DA:189,2268 -DA:189,2268 -DA:190,2268 -DA:190,2268 +DA:162,1436 +DA:162,1436 +BRDA:162,7,0,711 +BRDA:162,7,1,725 +DA:163,711 +DA:163,711 +DA:164,725 +DA:164,725 +BRDA:164,8,0,725 +BRDA:164,8,1,725 +DA:165,725 +DA:165,725 +DA:168,2272 +DA:168,2272 +DA:169,2272 +DA:169,2272 +DA:170,580816 +DA:170,580816 +DA:171,578544 +DA:171,578544 +DA:173,578544 +DA:173,578544 +DA:174,578544 +DA:174,578544 +DA:176,578544 +DA:176,578544 +BRDA:176,9,0,148056 +BRDA:176,9,1,430488 +DA:177,148056 +DA:177,148056 +DA:178,430488 +DA:178,430488 +BRDA:178,10,0,147107 +BRDA:178,10,1,283381 +DA:179,147107 +DA:179,147107 +DA:180,283381 +DA:180,283381 +BRDA:180,11,0,140187 +BRDA:180,11,1,143194 +DA:181,140187 +DA:181,140187 +DA:183,143194 +DA:183,143194 +DA:186,430488 +DA:186,430488 +DA:189,2272 +DA:189,2272 +DA:189,2272 +DA:190,2272 +DA:190,2272 FN:203,P256Verifier.compute_bitpair -FNDA:580608,P256Verifier.compute_bitpair -DA:204,580608 -DA:204,580608 +FNDA:581632,P256Verifier.compute_bitpair +DA:204,581632 +DA:204,581632 FN:211,P256Verifier.ecAff_add -FNDA:2268,P256Verifier.ecAff_add -DA:220,2268 -DA:220,2268 -DA:221,2268 -DA:221,2268 -DA:223,2268 -DA:223,2268 -BRDA:223,12,0,2268 +FNDA:2272,P256Verifier.ecAff_add +DA:220,2272 +DA:220,2272 +DA:221,2272 +DA:221,2272 +DA:223,2272 +DA:223,2272 +BRDA:223,12,0,2272 BRDA:223,12,1,- DA:223,0 -DA:224,2268 -DA:224,2268 -BRDA:224,13,0,2268 +DA:224,2272 +DA:224,2272 +BRDA:224,13,0,2272 BRDA:224,13,1,- DA:224,0 -DA:226,2268 -DA:226,2268 -DA:228,2268 -DA:228,2268 -DA:228,2268 +DA:226,2272 +DA:226,2272 +DA:228,2272 +DA:228,2272 +DA:228,2272 FN:235,P256Verifier.ecAff_IsInf -FNDA:436568,P256Verifier.ecAff_IsInf -DA:241,436568 -DA:241,436568 +FNDA:437324,P256Verifier.ecAff_IsInf +DA:241,437324 +DA:241,437324 FN:248,P256Verifier.ecZZ_IsInf -FNDA:1011808,P256Verifier.ecZZ_IsInf -DA:255,1011808 -DA:255,1011808 +FNDA:1013576,P256Verifier.ecZZ_IsInf +DA:255,1013576 +DA:255,1013576 FN:265,P256Verifier.ecZZ_dadd_affine -FNDA:432012,P256Verifier.ecZZ_dadd_affine -DA:273,432012 -DA:273,432012 +FNDA:432760,P256Verifier.ecZZ_dadd_affine +DA:273,432760 +DA:273,432760 BRDA:273,14,0,272 BRDA:273,14,1,1020 DA:274,1292 @@ -269,39 +281,39 @@ DA:274,1020 DA:274,1020 DA:275,272 DA:275,272 -DA:276,430720 -DA:276,430720 -BRDA:276,16,0,430712 +DA:276,431468 +DA:276,431468 +BRDA:276,16,0,431460 BRDA:276,16,1,8 DA:277,8 DA:277,8 -DA:280,430712 -DA:280,430712 -DA:280,430712 -DA:281,430712 -DA:281,430712 -DA:281,430712 -DA:283,430712 -DA:283,430712 -BRDA:283,17,0,430636 +DA:280,431460 +DA:280,431460 +DA:280,431460 +DA:281,431460 +DA:281,431460 +DA:281,431460 +DA:283,431460 +DA:283,431460 +BRDA:283,17,0,431384 BRDA:283,17,1,- -DA:285,430636 -DA:285,430636 -DA:285,430636 -DA:286,430636 -DA:286,430636 -DA:286,430636 -DA:287,430636 -DA:287,430636 -DA:288,430636 -DA:288,430636 -DA:289,430636 -DA:289,430636 -DA:289,430636 -DA:290,430636 -DA:290,430636 -DA:295,430636 -DA:295,430636 +DA:285,431384 +DA:285,431384 +DA:285,431384 +DA:286,431384 +DA:286,431384 +DA:286,431384 +DA:287,431384 +DA:287,431384 +DA:288,431384 +DA:288,431384 +DA:289,431384 +DA:289,431384 +DA:289,431384 +DA:290,431384 +DA:290,431384 +DA:295,431384 +DA:295,431384 DA:300,76 DA:300,76 BRDA:300,18,0,20 @@ -310,39 +322,39 @@ DA:304,20 DA:304,20 DA:307,56 DA:307,56 -DA:310,430712 -DA:310,430712 +DA:310,431460 +DA:310,431460 FN:318,P256Verifier.ecZZ_double_zz -FNDA:577528,P256Verifier.ecZZ_double_zz -DA:320,577528 -DA:320,577528 -BRDA:320,19,0,575440 +FNDA:578544,P256Verifier.ecZZ_double_zz +DA:320,578544 +DA:320,578544 +BRDA:320,19,0,576456 BRDA:320,19,1,2088 DA:320,2088 DA:320,2088 -DA:322,575440 -DA:322,575440 -DA:322,575440 -DA:323,575440 -DA:323,575440 -DA:323,575440 -DA:324,575440 -DA:324,575440 -DA:324,575440 -DA:325,575440 -DA:325,575440 -DA:325,575440 -DA:326,575440 -DA:326,575440 -DA:326,575440 -DA:328,575440 -DA:328,575440 -DA:329,575440 -DA:329,575440 -DA:330,575440 -DA:330,575440 -DA:331,575440 -DA:331,575440 +DA:322,576456 +DA:322,576456 +DA:322,576456 +DA:323,576456 +DA:323,576456 +DA:323,576456 +DA:324,576456 +DA:324,576456 +DA:324,576456 +DA:325,576456 +DA:325,576456 +DA:325,576456 +DA:326,576456 +DA:326,576456 +DA:326,576456 +DA:328,576456 +DA:328,576456 +DA:329,576456 +DA:329,576456 +DA:330,576456 +DA:330,576456 +DA:331,576456 +DA:331,576456 FN:339,P256Verifier.ecZZ_double_affine FNDA:20,P256Verifier.ecZZ_double_affine DA:341,20 @@ -369,28 +381,28 @@ DA:349,20 DA:350,20 DA:350,20 FN:358,P256Verifier.ecZZ_SetAff -FNDA:2268,P256Verifier.ecZZ_SetAff -DA:364,2268 -DA:364,2268 -BRDA:364,21,0,2252 +FNDA:2272,P256Verifier.ecZZ_SetAff +DA:364,2272 +DA:364,2272 +BRDA:364,21,0,2256 BRDA:364,21,1,16 DA:365,16 DA:365,16 DA:366,16 DA:366,16 -DA:369,2252 -DA:369,2252 -DA:369,2252 -DA:370,2252 -DA:370,2252 -DA:370,2252 -DA:371,2252 -DA:371,2252 -DA:371,2252 -DA:376,2252 -DA:376,2252 -DA:377,2252 -DA:377,2252 +DA:369,2256 +DA:369,2256 +DA:369,2256 +DA:370,2256 +DA:370,2256 +DA:370,2256 +DA:371,2256 +DA:371,2256 +DA:371,2256 +DA:376,2256 +DA:376,2256 +DA:377,2256 +DA:377,2256 FN:383,P256Verifier.ecZZ_PointAtInf FNDA:3164,P256Verifier.ecZZ_PointAtInf DA:384,3164 @@ -400,25 +412,25 @@ FNDA:16,P256Verifier.ecAffine_PointAtInf DA:391,16 DA:391,16 FN:397,P256Verifier.nModInv -FNDA:2268,P256Verifier.nModInv -DA:398,2268 -DA:398,2268 -DA:398,2268 +FNDA:2272,P256Verifier.nModInv +DA:398,2272 +DA:398,2272 +DA:398,2272 FN:404,P256Verifier.pModInv -FNDA:4520,P256Verifier.pModInv -DA:405,4520 -DA:405,4520 -DA:405,4520 +FNDA:4528,P256Verifier.pModInv +DA:405,4528 +DA:405,4528 +DA:405,4528 FN:414,P256Verifier.modInv -FNDA:6788,P256Verifier.modInv -DA:421,6788 -DA:421,6788 -DA:422,6788 -DA:422,6788 +FNDA:6800,P256Verifier.modInv +DA:421,6800 +DA:421,6800 +DA:422,6800 +DA:422,6800 BRDA:422,22,0,- BRDA:422,22,1,- -DA:423,6788 -DA:423,6788 +DA:423,6800 +DA:423,6800 FNF:18 FNH:18 LF:121 @@ -588,3 +600,34 @@ LH:14 BRF:8 BRH:8 end_of_record +TN: +SF:test/P256.t.sol +FN:10,FakePrecompile. +FNDA:2,FakePrecompile. +DA:11,2 +DA:11,2 +DA:11,2 +DA:12,2 +DA:12,2 +BRDA:12,0,0,- +BRDA:12,0,1,- +DA:14,2 +DA:14,2 +DA:14,2 +DA:15,2 +DA:15,2 +BRDA:15,1,0,1 +BRDA:15,1,1,1 +DA:16,1 +DA:16,1 +DA:16,1 +DA:18,1 +DA:18,1 +DA:18,1 +FNF:1 +FNH:1 +LF:6 +LH:6 +BRF:4 +BRH:2 +end_of_record diff --git a/src/P256.sol b/src/P256.sol index 4eb05d7..c7792ea 100644 --- a/src/P256.sol +++ b/src/P256.sol @@ -3,8 +3,11 @@ pragma solidity 0.8.21; /** * Helper library for external contracts to verify P256 signatures. + * Tries to use RIP-7212 precompile if available on the chain, and if not falls + * back to more expensive Solidity implementation. **/ library P256 { + address constant PRECOMPILE = address(0x100); address constant VERIFIER = 0xc2b78104907F722DABAc4C69f826a522B2754De4; function verifySignatureAllowMalleability( @@ -15,10 +18,21 @@ library P256 { uint256 y ) internal view returns (bool) { bytes memory args = abi.encode(message_hash, r, s, x, y); - (bool success, bytes memory ret) = VERIFIER.staticcall(args); - assert(success); // never reverts, always returns 0 or 1 - return abi.decode(ret, (uint256)) == 1; + (bool success, bytes memory ret) = PRECOMPILE.staticcall(args); + if (success && ret.length > 0) { + // RIP-7212 precompile returns 1 if signature is valid + // and nothing if signature is invalid, so those fall back to + // more expensive Solidity implementation. + return abi.decode(ret, (uint256)) == 1; + } + + (bool fallbackSuccess, bytes memory fallbackRet) = VERIFIER.staticcall( + args + ); + assert(fallbackSuccess); // never reverts, always returns 0 or 1 + + return abi.decode(fallbackRet, (uint256)) == 1; } /// P256 curve order n/2 for malleability check diff --git a/test/P256.t.sol b/test/P256.t.sol index f64820b..5308d1a 100644 --- a/test/P256.t.sol +++ b/test/P256.t.sol @@ -6,6 +6,20 @@ import {stdJson} from "forge-std/StdJson.sol"; import {P256} from "../src/P256.sol"; import {P256Verifier} from "../src/P256Verifier.sol"; +contract FakePrecompile { + fallback(bytes calldata input) external returns (bytes memory) { + (bool success, bytes memory ret) = P256.VERIFIER.staticcall(input); + assert(success); + + uint256 retUint = abi.decode(ret, (uint256)); + if (retUint == 1) { + return abi.encode(1); + } else { + return abi.encode(); + } + } +} + contract P256Test is Test { uint256[2] public pubKey; @@ -57,4 +71,49 @@ contract P256Test is Test { res = P256.verifySignature(hash, r, s, pubKey[0], pubKey[1]); assertEq(res, true); } + + function testPrecompileUsage() public { + uint256 r = 0x01655c1753db6b61a9717e4ccc5d6c4bf7681623dd54c2d6babc55125756661c; + uint256 s = 7033802732221576339889804108463427183539365869906989872244893535944704590394; + + bytes32 hash = 0x267f9ea080b54bbea2443dff8aa543604564329783b6a515c6663a691c555490; + + uint gasBefore = gasleft(); + bool res = P256.verifySignatureAllowMalleability( + hash, + r, + s, + pubKey[0], + pubKey[1] + ); + assertEq(res, true); + uint gasUsedFallback = gasBefore - gasleft(); + assert(gasUsedFallback > 100_000); // no precompile, used Solidity implementation + + vm.etch(P256.PRECOMPILE, type(FakePrecompile).runtimeCode); + + gasBefore = gasleft(); + res = P256.verifySignatureAllowMalleability( + hash, + r, + s, + pubKey[0], + pubKey[1] + ); + assertEq(res, true); + uint gasUsedPrecompile = gasBefore - gasleft(); + assert(gasUsedPrecompile < gasUsedFallback); // precompile, used precompile + + gasBefore = gasleft(); + res = P256.verifySignatureAllowMalleability( + hash, + r + 1, + s, + pubKey[0], + pubKey[1] + ); + assertEq(res, false); + uint gasUsedInvalidSignature = gasBefore - gasleft(); + assert(gasUsedInvalidSignature > gasUsedFallback); // invalid signature, used precompile and failed, so fall back to Solidity implementation and failed with nearly double gas + } }