diff --git a/README.md b/README.md index b6f64e1..73d26f2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The secp256r1 elliptic curve, aka P256, is interesting because it's supported by ## Usage -**Address `0xea923BEe7108728eA2708af25e9981272193a555`** +**Address `0x86e49A916721C4542CD1378D43c9f5C7B501de81`** Available on any chain. If missing, see `deploy.sh`. @@ -19,7 +19,7 @@ bytes32 hash; // message hash uint256 r, s; // signature uint256 x, y; // public key -address verifier = 0xea923BEe7108728eA2708af25e9981272193a555; +address verifier = 0x86e49A916721C4542CD1378D43c9f5C7B501de81; bytes memory args = abi.encode(hash, r, s, x, y); (bool success, bytes memory ret) = verifier.staticcall(args); assert(success); // never reverts, always returns 0 or 1 diff --git a/broadcast/Deploy.s.sol/84531/run-latest.json b/broadcast/Deploy.s.sol/84531/run-latest.json index a176035..a32b638 100644 --- a/broadcast/Deploy.s.sol/84531/run-latest.json +++ b/broadcast/Deploy.s.sol/84531/run-latest.json @@ -1,20 +1,20 @@ { "transactions": [ { - "hash": "0x68290afa86c796a04d3da87cb7f20953925ebe836fa0ce2f58812cb0e29dece5", + "hash": "0x8b4eb4f5ce9c8ede905f3625391e1b963bd11db70607bb10bf9a1148e2f69b28", "transactionType": "CREATE2", "contractName": "P256Verifier", - "contractAddress": "0xea923BEe7108728eA2708af25e9981272193a555", + "contractAddress": "0x86e49A916721C4542CD1378D43c9f5C7B501de81", "function": null, "arguments": null, "transaction": { "type": "0x02", "from": "0x9a57d792cc04a7bceb5d1f8b1b7af5f8e5695e54", "to": "0x4e59b44847b379578588920ca78fbf26c0b4956c", - "gas": "0x11e558", + "gas": "0x12af6f", "value": "0x0", - "data": "0x00000000000000000000000000000000000000000000000000000000000000006080806040523461001657610e76908161001c8239f35b600080fdfe60e06040523461001a57610012366100c7565b602081519101f35b600080fd5b6040810190811067ffffffffffffffff82111761003b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60e0810190811067ffffffffffffffff82111761003b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761003b57604052565b60a08103610193578060201161001a57600060409180831161018f578060601161018f578060801161018f5760a01161018c57815182810181811067ffffffffffffffff82111761015f579061013291845260603581526080356020820152833560203584356101e1565b15610156575060ff6001915b5191166020820152602081526101538161001f565b90565b60ff909161013e565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80fd5b5080fd5b5060405160006020820152602081526101538161001f565b156101b257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9091821580156103b1575b80156103a9575b801561037f575b61037657835190602094610213868201938451906103db565b1561036b57604051958087019281845281604089015281606089015260808801527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f60a08801527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551968760c082015260c0815261028f8161006a565b600080948192519060055afa923d15610363573d9067ffffffffffffffff821161033657604051916102e8847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610086565b82523d818484013e5b828280518101031261018c5750916103138892610323979695940151936101ab565b519181818809940991519061054f565b929006149081610331575090565b905090565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526041600452fd5b6060906102f1565b505050505050600090565b50505050600090565b507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518110156101fa565b5080156101f3565b507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518310156101ec565b801580156104aa575b80156104a2575b8015610478575b610471577f5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b907fffffffff00000001000000000000000000000000ffffffffffffffffffffffff918282818080957fffffffff00000001000000000000000000000000fffffffffffffffffffffffc0991818180090908089180091490565b5050600090565b507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8210156103f2565b5081156103eb565b507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8110156103e4565b7f80000000000000000000000000000000000000000000000000000000000000008114610520577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9192608052600090600160a05260a05193600093811580610796575b610787576105879361057d84836107bb565b97919690976101ab565b60ff60c05260005b600060c051121561075e575b60a051810361070f575050507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5957f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296969594939291955b600060c051121561063557505050505050507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff61063060a051610d20565b920991565b9661064892939495969760a05191610af2565b9097929181928960a0528192819a6106656080518960c0516107a0565b61067060c0516104d4565b60c052806106895750505050505b9594939291956105f2565b969b506106e99693955091935091600181036106f657507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5937f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296936109b8565b979297919060a05261067e565b6002036107065786938a936109b8565b889388936109b8565b6002810361072957505050829581969594939291956105f2565b9197917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0161067e575095508495849661067e565b5061076e6080518560c0516107a0565b8061077a60c0516104d4565b60c0521561058f5761059b565b50505050505060009060a05190565b506080511561056b565b91906002600192841c831b16921c1681018091116105205790565b81156107de576107d7916107ce91610829565b92919091610cbb565b9192909190565b50507f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296907f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f590600190565b91908061087d57507f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29691507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5906001908190565b7fb01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a917fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808481600186090894817f94e82e0c1ed3bdb90743191a9c5bbf0d88fc827fd214cc5f0b5ec6ba27673d69816001840908938415610989575050808084800993840994818460010994828088600109957f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29609918784038481116105205784908180867fffffffff00000001000000000000000000000000fffffffffffffffffffffffd099181858009080897888503858111610520578580949281930994080908935b93929190565b935093505092156000146109a9576109a091610bf4565b91939092610983565b50506000806000926000610983565b9194959293909580156000146109f35750508315806109eb575b6109dc5793929190565b50600093508392508291508190565b5082156109d2565b85919294951580610aea575b610ada577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff968703918783116105205787838189850908938689038981116105205789908184840908928315610abf575050818880959493928180848196099b8c9485099b8c920999099609918784038481116105205784908180867fffffffff00000001000000000000000000000000fffffffffffffffffffffffd099181858009080897888503858111610520578580949281930994080908929190565b96509650505050909350156000146109a9576109a091610bf4565b9550509150915091906001908190565b5085156109ff565b939092821580610bec575b6109dc576001831480610be2575b610bcd577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff908185600209948280878009809709948380888a0998818080808680097fffffffff00000001000000000000000000000000fffffffffffffffffffffffc099280096003090884808a7fffffffff00000001000000000000000000000000fffffffffffffffffffffffd09818380090898898603918683116105205788870390878211610520578780969481809681950994089009089609930990565b5092610bda929150610bf4565b929391929091565b5060018114610b0b565b508015610afd565b9190918215610cae577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff90818460020991808084800980940991817fffffffff00000001000000000000000000000000fffffffffffffffffffffffc81808088860994800960030908958280837fffffffff00000001000000000000000000000000fffffffffffffffffffffffd098189800908968784039184831161052057858503928584116105205785809492819309940890090892565b5060009150819081908190565b90939192831580610d18575b610d0857610cd490610d20565b9381927fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8080958194098009900994099190565b5050915050600090600090600190565b508015610cc7565b6040516020908181019282845282604083015282606083015260808201527fffffffff00000001000000000000000000000000fffffffffffffffffffffffd60a08201527fffffffff00000001000000000000000000000000ffffffffffffffffffffffff60c082015260c08152610d978161006a565b600080938192519060055afa3d15610e38573d9067ffffffffffffffff8211610e0b5760405191610def847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610086565b82523d848484013e5b92828280518101031261018c5750015191565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b606090610df856fea26469706673582212200d343816486f53e797deda70da80bb5153203a89f3c77fc459e3f554071baee064736f6c63430008150033", - "nonce": "0x31", + "data": "0x00000000000000000000000000000000000000000000000000000000000000006080806040523461001657610e3e908161001c8239f35b600080fdfe60e06040523461001a57610012366100c7565b602081519101f35b600080fd5b6040810190811067ffffffffffffffff82111761003b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60e0810190811067ffffffffffffffff82111761003b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761003b57604052565b60a08103610193578060201161001a57600060409180831161018f578060601161018f578060801161018f5760a01161018c57815182810181811067ffffffffffffffff82111761015f579061013291845260603581526080356020820152833560203584356101e1565b15610156575060ff6001915b5191166020820152602081526101538161001f565b90565b60ff909161013e565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b80fd5b5080fd5b5060405160006020820152602081526101538161001f565b156101b257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9091821580156103b1575b80156103a9575b801561037f575b61037657835190602094610213868201938451906103db565b1561036b57604051958087019281845281604089015281606089015260808801527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f60a08801527fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551968760c082015260c0815261028f8161006a565b600080948192519060055afa923d15610363573d9067ffffffffffffffff821161033657604051916102e8847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610086565b82523d818484013e5b828280518101031261018c5750916103138892610323979695940151936101ab565b5191818188099409915190610509565b929006149081610331575090565b905090565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526041600452fd5b6060906102f1565b505050505050600090565b50505050600090565b507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518110156101fa565b5080156101f3565b507fffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325518310156101ec565b7fffffffff00000001000000000000000000000000ffffffffffffffffffffffff90818110801590610484575b8015610473575b61046b577f5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b8282818080957fffffffff00000001000000000000000000000000fffffffffffffffffffffffc0991818180090908089180091490565b505050600090565b508015801561040f5750821561040f565b5081831015610408565b7f800000000000000000000000000000000000000000000000000000000000000081146104da577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9192608052600090600160a05260a05193600093811580610750575b61074157610541936105378483610775565b97919690976101ab565b60ff60c05260005b600060c0511215610718575b60a05181036106c9575050507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5957f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296969594939291955b600060c05112156105ef57505050505050507fffffffff00000001000000000000000000000000ffffffffffffffffffffffff6105ea60a051610ce8565b920991565b9661060292939495969760a05191610aba565b9097929181928960a0528192819a61061f6080518960c05161075a565b61062a60c05161048e565b60c052806106435750505050505b9594939291956105ac565b969b506106a39693955091935091600181036106b057507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5937f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29693610980565b979297919060a052610638565b6002036106c05786938a93610980565b88938893610980565b600281036106e357505050829581969594939291956105ac565b9197917ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd016106385750955084958496610638565b506107286080518560c05161075a565b8061073460c05161048e565b60c0521561054957610555565b50505050505060009060a05190565b5060805115610525565b91906002600192841c831b16921c1681018091116104da5790565b8015806107e9575b61079e576107979161078e916107f1565b92919091610c83565b9192909190565b50507f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296907f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f590600190565b50811561077d565b91908061084557507f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29691507f4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5906001908190565b7fb01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a917fffffffff00000001000000000000000000000000ffffffffffffffffffffffff808481600186090894817f94e82e0c1ed3bdb90743191a9c5bbf0d88fc827fd214cc5f0b5ec6ba27673d69816001840908938415610951575050808084800993840994818460010994828088600109957f6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29609918784038481116104da5784908180867fffffffff00000001000000000000000000000000fffffffffffffffffffffffd0991818580090808978885038581116104da578580949281930994080908935b93929190565b935093505092156000146109715761096891610bbc565b9193909261094b565b5050600080600092600061094b565b9194959293909580156000146109bb5750508315806109b3575b6109a45793929190565b50600093508392508291508190565b50821561099a565b85919294951580610ab2575b610aa2577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff968703918783116104da5787838189850908938689038981116104da5789908184840908928315610a87575050818880959493928180848196099b8c9485099b8c920999099609918784038481116104da5784908180867fffffffff00000001000000000000000000000000fffffffffffffffffffffffd0991818580090808978885038581116104da578580949281930994080908929190565b96509650505050909350156000146109715761096891610bbc565b9550509150915091906001908190565b5085156109c7565b939092821580610bb4575b6109a4576001831480610baa575b610b95577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff908185600209948280878009809709948380888a0998818080808680097fffffffff00000001000000000000000000000000fffffffffffffffffffffffc099280096003090884808a7fffffffff00000001000000000000000000000000fffffffffffffffffffffffd09818380090898898603918683116104da57888703908782116104da578780969481809681950994089009089609930990565b5092610ba2929150610bbc565b929391929091565b5060018114610ad3565b508015610ac5565b9190918215610c76577fffffffff00000001000000000000000000000000ffffffffffffffffffffffff90818460020991808084800980940991817fffffffff00000001000000000000000000000000fffffffffffffffffffffffc81808088860994800960030908958280837fffffffff00000001000000000000000000000000fffffffffffffffffffffffd09818980090896878403918483116104da57858503928584116104da5785809492819309940890090892565b5060009150819081908190565b90939192831580610ce0575b610cd057610c9c90610ce8565b9381927fffffffff00000001000000000000000000000000ffffffffffffffffffffffff8080958194098009900994099190565b5050915050600090600090600190565b508015610c8f565b6040516020908181019282845282604083015282606083015260808201527fffffffff00000001000000000000000000000000fffffffffffffffffffffffd60a08201527fffffffff00000001000000000000000000000000ffffffffffffffffffffffff60c082015260c08152610d5f8161006a565b600080938192519060055afa3d15610e00573d9067ffffffffffffffff8211610dd35760405191610db7847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610086565b82523d848484013e5b92828280518101031261018c5750015191565b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b606090610dc056fea264697066735822122027580b7e2b29246317a7e6000f4d0ce0292b645fe0cec6bdf223d1ccdf9917e164736f6c63430008150033", + "nonce": "0x34", "accessList": [] }, "additionalContracts": [], @@ -23,27 +23,27 @@ ], "receipts": [ { - "transactionHash": "0x68290afa86c796a04d3da87cb7f20953925ebe836fa0ce2f58812cb0e29dece5", + "transactionHash": "0x8b4eb4f5ce9c8ede905f3625391e1b963bd11db70607bb10bf9a1148e2f69b28", "transactionIndex": "0x1", - "blockHash": "0x434302077b6da06c568545b51a034f71a40c822a5e561cfbf6c8a44e22bf3b81", - "blockNumber": "0x999a75", + "blockHash": "0xd3709d0694efd6b08eff1f07a23f2e3a3b80b2f745ae8af50ff0b51c9a1c6ff7", + "blockNumber": "0x9c3d57", "from": "0x9a57D792CC04a7bCEB5D1f8b1B7AF5F8e5695E54", "to": "0x4e59b44847b379578588920cA78FbF26c0B4956C", - "cumulativeGasUsed": "0xdabd5", - "gasUsed": "0xcf4d0", + "cumulativeGasUsed": "0xd80d3", + "gasUsed": "0xcc6ba", "contractAddress": null, "logs": [], "status": "0x1", "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "type": "0x2", - "effectiveGasPrice": "0xb2d05e57" + "effectiveGasPrice": "0xb2d05e32" } ], "libraries": [], "pending": [], "returns": {}, - "timestamp": 1695326719, + "timestamp": 1695672258, "chain": 84531, "multi": false, - "commit": "4887c97" + "commit": "e1406d3" } \ No newline at end of file diff --git a/lcov.info b/lcov.info index d8af40a..6cefa7a 100644 --- a/lcov.info +++ b/lcov.info @@ -34,13 +34,13 @@ DA:97,2262 FN:104,P256Verifier.ecAff_isOnCurve FNDA:2269,P256Verifier.ecAff_isOnCurve DA:108,2269 -BRDA:108,4,0,2265 -BRDA:108,4,1,4 -DA:109,4 -DA:112,2265 -DA:113,2265 -DA:114,2265 -DA:116,2265 +BRDA:108,4,0,2267 +BRDA:108,4,1,1 +DA:109,2 +DA:112,2267 +DA:113,2267 +DA:114,2267 +DA:116,2267 FN:124,P256Verifier.ecZZ_mulmuladd FNDA:2262,P256Verifier.ecZZ_mulmuladd DA:130,2262 @@ -114,8 +114,8 @@ BRDA:216,15,0,2262 BRDA:216,15,1,- DA:218,2262 DA:220,2262 -FN:227,P256Verifier.ecAff_IsZero -FNDA:4524,P256Verifier.ecAff_IsZero +FN:227,P256Verifier.ecAff_IsInf +FNDA:4524,P256Verifier.ecAff_IsInf DA:233,4524 FN:243,P256Verifier.ecZZ_dadd_affine FNDA:430879,P256Verifier.ecZZ_dadd_affine diff --git a/script/deploy.sh b/script/deploy.sh index 95d304d..d035897 100755 --- a/script/deploy.sh +++ b/script/deploy.sh @@ -3,7 +3,7 @@ forge script DeployScript --via-ir --optimizer-runs=999999 --rpc-url $RPC_URL --broadcast --private-key $PRIVATE_KEY # Update contract address -ADDR=0xea923BEe7108728eA2708af25e9981272193a555 +ADDR=0x86e49A916721C4542CD1378D43c9f5C7B501de81 # Verify to Etherscan forge verify-contract $ADDR P256Verifier --optimizer-runs=999999 --constructor-args "0x" --show-standard-json-input > script/etherscan.json @@ -13,4 +13,4 @@ forge verify-contract $ADDR P256Verifier --optimizer-runs=999999 --constructor-a # Finally, manually verify to Etherscan # Success -# https://goerli.basescan.org/address/0xea923BEe7108728eA2708af25e9981272193a555#code \ No newline at end of file +# https://goerli.basescan.org/address/0x86e49A916721C4542CD1378D43c9f5C7B501de81#code \ No newline at end of file diff --git a/script/etherscan.json b/script/etherscan.json index 2f83965..11f0ac1 100644 --- a/script/etherscan.json +++ b/script/etherscan.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "src/P256Verifier.sol": { - "content": "// SPDX-License-Identifier: MIT\n// Force a specific Solidity version for reproducibility.\npragma solidity 0.8.21;\n\n/**\n * This contract verifies P256 (secp256r1) signatures. It matches the exact\n * interface specified in the EIP-7212 precompile, allowing it to be used as a\n * fallback. It's based on Ledger's optimized implementation:\n * https://github.com/rdubois-crypto/FreshCryptoLib/tree/master/solidity\n **/\ncontract P256Verifier {\n /**\n * Precompiles don't use a function signature. The first byte of callldata\n * is the first byte of an input argument. In this case:\n *\n * input[ 0: 32] = signed data hash\n * input[ 32: 64] = signature r\n * input[ 64: 96] = signature s\n * input[ 96:128] = public key x\n * input[128:160] = public key y\n *\n * result[ 0: 32] = 0x00..00 (invalid) or 0x00..01 (valid)\n *\n * For details, see https://eips.ethereum.org/EIPS/eip-7212\n */\n fallback(bytes calldata input) external returns (bytes memory) {\n if (input.length != 160) {\n return abi.encodePacked(uint256(0));\n }\n\n bytes32 hash = bytes32(input[0:32]);\n uint256 r = uint256(bytes32(input[32:64]));\n uint256 s = uint256(bytes32(input[64:96]));\n uint256 x = uint256(bytes32(input[96:128]));\n uint256 y = uint256(bytes32(input[128:160]));\n\n uint256 ret = ecdsa_verify(hash, r, s, [x, y]) ? 1 : 0;\n\n return abi.encodePacked(ret);\n }\n\n // Parameters for the sec256r1 (P256) elliptic curve\n // Curve prime field modulus\n uint256 constant p =\n 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;\n // Short weierstrass first coefficient\n uint256 constant a = // The assumption a == -3 (mod p) is used throughout the codebase\n 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;\n // Short weierstrass second coefficient\n uint256 constant b =\n 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;\n // Generating point affine coordinates\n uint256 constant GX =\n 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;\n uint256 constant GY =\n 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;\n // Curve order (number of points)\n uint256 constant n =\n 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;\n // -2 mod p constant, used to speed up inversion and doubling (avoid negation)\n uint256 constant minus_2modp =\n 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;\n // -2 mod n constant, used to speed up inversion\n uint256 constant minus_2modn =\n 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;\n\n /**\n * @dev ECDSA verification given signature and public key.\n */\n function ecdsa_verify(\n bytes32 message_hash,\n uint256 r,\n uint256 s,\n uint256[2] memory pubKey\n ) private view returns (bool) {\n // Check r and s are in the scalar field\n if (r == 0 || r >= n || s == 0 || s >= n) {\n return false;\n }\n\n if (!ecAff_isOnCurve(pubKey[0], pubKey[1])) {\n return false;\n }\n\n (uint256 sInv, bool sInv_success) = nModInv(s);\n assert (sInv_success);\n\n uint256 scalar_u = mulmod(uint256(message_hash), sInv, n); // (h * s^-1) in scalar field\n uint256 scalar_v = mulmod(r, sInv, n); // (r * s^-1) in scalar field\n\n (uint256 r_x, bool mulmuladd_success) = ecZZ_mulmuladd(\n pubKey[0],\n pubKey[1],\n scalar_u,\n scalar_v\n );\n return r_x % n == r && mulmuladd_success;\n }\n\n /**\n * @dev Check if a point in affine coordinates is on the curve\n * Reject 0 point at infinity.\n */\n function ecAff_isOnCurve(\n uint256 x,\n uint256 y\n ) internal pure returns (bool) {\n if (0 == x || x >= p || 0 == y || y >= p) {\n return false;\n }\n\n uint256 LHS = mulmod(y, y, p); // y^2\n uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(a, x, p), p); // x^3 + a x\n RHS = addmod(RHS, b, p); // x^3 + a*x + b\n\n return LHS == RHS;\n }\n\n /**\n * @dev Computation of uG + vQ using Strauss-Shamir's trick, G basepoint, Q public key\n * returns tuple of (x coordinate of uG + vQ, boolean that is false if internal precompile staticcall fail)\n * Strauss-Shamir is described well in https://stackoverflow.com/a/50994362\n */\n function ecZZ_mulmuladd(\n uint256 QX,\n uint256 QY, // affine rep for input point Q\n uint256 scalar_u,\n uint256 scalar_v\n ) internal view returns (uint256 X, bool success) {\n uint256 zz = 1;\n uint256 zzz = 1;\n uint256 Y;\n uint256 HX;\n uint256 HY;\n bool add_success;\n\n if (scalar_u == 0 && scalar_v == 0) return (0, true);\n\n // H = g + Q\n (HX, HY, add_success) = ecAff_add(GX, GY, QX, QY);\n assert(add_success);\n\n int256 index = 255;\n uint256 bitpair;\n\n // Find the first bit index that's active in either scalar_u or scalar_v.\n while(index >= 0) {\n bitpair = compute_bitpair(uint256(index), scalar_u, scalar_v);\n index--;\n if (bitpair != 0) break;\n }\n\n // initialise (X, Y) depending on the first active bitpair.\n // invariant(bitpair != 0); // bitpair == 0 is only possible if u and v are 0.\n \n if (bitpair == 1) {\n (X, Y) = (GX, GY);\n } else if (bitpair == 2) {\n (X, Y) = (QX, QY);\n } else if (bitpair == 3) {\n (X, Y) = (HX, HY);\n }\n\n uint256 TX;\n uint256 TY;\n while(index >= 0) {\n (X, Y, zz, zzz) = ecZZ_double_zz(X, Y, zz, zzz);\n\n bitpair = compute_bitpair(uint256(index), scalar_u, scalar_v);\n index--;\n\n if (bitpair == 0) {\n continue;\n } else if (bitpair == 1) {\n (TX, TY) = (GX, GY);\n } else if (bitpair == 2) {\n (TX, TY) = (QX, QY);\n } else {\n (TX, TY) = (HX, HY);\n }\n\n (X, Y, zz, zzz) = ecZZ_dadd_affine(X, Y, zz, zzz, TX, TY);\n }\n\n uint256 zzInv;\n (zzInv, success) = pModInv(zz);\n X = mulmod(X, zzInv, p); // X/zz\n }\n\n /**\n * @dev Compute the bits at `index` of u and v and return\n * them as 2 bit concatenation.\n * todo: add example\n */\n function compute_bitpair(uint256 index, uint256 scalar_u, uint256 scalar_v) internal pure returns (uint256 ret) {\n ret = (((scalar_v >> index) & 1) << 1) + ((scalar_u >> index) & 1);\n }\n\n /**\n * @dev Add two elliptic curve points in affine coordinates\n * Assumes points are on the EC\n */\n function ecAff_add(\n uint256 x1,\n uint256 y1,\n uint256 x2,\n uint256 y2\n ) internal view returns (uint256, uint256, bool) {\n // invariant(ecAff_IsZero(x1, y1) || ecAff_isOnCurve(x1, y1));\n // invariant(ecAff_IsZero(x2, y2) || ecAff_isOnCurve(x2, y2));\n\n uint256 zz1;\n uint256 zzz1;\n\n if (ecAff_IsZero(x1, y1)) return (x2, y2, true);\n if (ecAff_IsZero(x2, y2)) return (x1, y1, true);\n\n (x1, y1, zz1, zzz1) = ecZZ_dadd_affine(x1, y1, 1, 1, x2, y2);\n\n return ecZZ_SetAff(x1, y1, zz1, zzz1);\n }\n\n /**\n * @dev Check if the curve is the zero curve in affine rep\n * Assumes point is on the EC or is the zero point.\n */\n function ecAff_IsZero(\n uint256,\n uint256 y\n ) internal pure returns (bool flag) {\n // invariant((x == 0 && y == 0) || ecAff_isOnCurve(x, y));\n\n return (y == 0);\n }\n\n /**\n * @dev Add a ZZ point to an affine point and return as ZZ rep\n * Uses madd-2008-s and mdbl-2008-s internally\n * https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-madd-2008-s\n * Matches https://github.com/supranational/blst/blob/9c87d4a09d6648e933c818118a4418349804ce7f/src/ec_ops.h#L705 closely\n * Handles points at infinity gracefully\n */\n function ecZZ_dadd_affine(\n uint256 x1,\n uint256 y1,\n uint256 zz1,\n uint256 zzz1,\n uint256 x2,\n uint256 y2\n ) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) {\n if (y2 == 0) { // (X2, Y2) is point at infinity\n if (zz1 == 0 && zzz1 == 0) return ecZZ_PointAtInf();\n return (x1, y1, zz1, zzz1);\n } else if (zz1 == 0 && zzz1 == 0) { // (X1, Y1) is point at infinity\n return (x2, y2, 1, 1);\n }\n\n uint256 comp_R = addmod(mulmod(y2, zzz1, p), p - y1, p); // R = S2 - y1 = y2*zzz1 - y1\n uint256 comp_P = addmod(mulmod(x2, zz1, p), p - x1, p); // P = U2 - x1 = x2*zz1 - x1\n\n if (comp_P != 0) { // X1 != X2\n // invariant(x1 != x2);\n uint256 comp_PP = mulmod(comp_P, comp_P, p); // PP = P^2\n uint256 comp_PPP = mulmod(comp_PP, comp_P, p); // PPP = P*PP\n zz3 = mulmod(zz1, comp_PP, p); //// ZZ3 = ZZ1*PP\n zzz3 = mulmod(zzz1, comp_PPP, p); //// ZZZ3 = ZZZ1*PPP\n uint256 comp_Q = mulmod(x1, comp_PP, p); // Q = X1*PP\n x3 = addmod(\n addmod(mulmod(comp_R, comp_R, p), p - comp_PPP, p), // (R^2) + (-PPP)\n mulmod(minus_2modp, comp_Q, p), // (-2)*(Q)\n p\n ); // R^2 - PPP - 2*Q\n y3 = addmod(\n mulmod(addmod(comp_Q, p - x3, p), comp_R, p), //(Q+(-x3))*R\n mulmod(p - y1, comp_PPP, p), // (-y1)*PPP\n p\n ); // R*(Q-x3) - y1*PPP\n } else if (comp_R == 0) { // X1 == X2 and Y1 == Y2\n // invariant(x1 == x2 && y1 == y2);\n\n // Must be affine because (X2, Y2) is affine.\n (x3, y3, zz3, zzz3) = ecZZ_double_affine(x2, y2);\n } else { // X1 == X2 and Y1 == -Y2\n // invariant(x1 == x2 && y1 == p - y2);\n (x3, y3, zz3, zzz3) = ecZZ_PointAtInf();\n }\n\n return (x3, y3, zz3, zzz3);\n }\n\n /**\n * @dev Double a ZZ point \n * Uses http://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1\n * Handles point at infinity gracefully\n */\n function ecZZ_double_zz(uint256 x1,\n uint256 y1, uint256 zz1, uint256 zzz1) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) {\n if (zz1 == 0 && zzz1 == 0) return ecZZ_PointAtInf();\n if (zz1 == 1 && zzz1 == 1) return ecZZ_double_affine(x1, y1);\n \n uint256 comp_U = mulmod(2, y1, p); // U = 2*Y1\n uint256 comp_V = mulmod(comp_U, comp_U, p); // V = U^2\n uint256 comp_W = mulmod(comp_U, comp_V, p); // W = U*V\n uint256 comp_S = mulmod(x1, comp_V, p); // S = X1*V\n uint256 comp_M = addmod(mulmod(3, mulmod(x1, x1, p), p), mulmod(a, mulmod(zz1, zz1, p), p), p); //M = 3*(X1)^2 + a*(zz1)^2\n \n x3 = addmod(mulmod(comp_M, comp_M, p), mulmod(minus_2modp, comp_S, p), p); // M^2 + (-2)*S\n y3 = addmod(mulmod(comp_M, addmod(comp_S, p - x3, p), p), mulmod(p - comp_W, y1, p), p); // M*(S+(-X3)) + (-W)*Y1\n zz3 = mulmod(comp_V, zz1, p); // V*ZZ1\n zzz3 = mulmod(comp_W, zzz1, p); // W*ZZZ1\n }\n\n /**\n * @dev Double an affine point and return as a ZZ point \n * Uses http://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-mdbl-2008-s-1\n * Handles point at infinity gracefully\n */\n function ecZZ_double_affine(uint256 x1,\n uint256 y1) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) {\n if (y1 == 0) return ecZZ_PointAtInf();\n\n uint256 comp_U = mulmod(2, y1, p); // U = 2*Y1\n zz3 = mulmod(comp_U, comp_U, p); // V = U^2 = zz3\n zzz3 = mulmod(comp_U, zz3, p); // W = U*V = zzz3\n uint256 comp_S = mulmod(x1, zz3, p); // S = X1*V\n uint256 comp_M = addmod(mulmod(3, mulmod(x1, x1, p), p), a, p); // M = 3*(X1)^2 + a\n \n x3 = addmod(mulmod(comp_M, comp_M, p), mulmod(minus_2modp, comp_S, p), p); // M^2 + (-2)*S\n y3 = addmod(mulmod(comp_M, addmod(comp_S, p - x3, p), p), mulmod(p - zzz3, y1, p), p); // M*(S+(-X3)) + (-W)*Y1\n }\n\n /**\n * @dev Convert from ZZ rep to affine rep\n * Assumes (zz)^(3/2) == zzz (i.e. zz == z^2 and zzz == z^3)\n * See https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html\n */\n function ecZZ_SetAff(\n uint256 x,\n uint256 y,\n uint256 zz,\n uint256 zzz\n ) internal view returns (uint256 x1, uint256 y1, bool success) {\n if(zz == 0 && zzz == 0) {\n (x1, y1) = ecAffine_PointAtInf();\n return (x1, y1, true);\n }\n\n (uint256 zzzInv, bool zzzInv_success) = pModInv(zzz); // 1 / zzz\n uint256 zInv = mulmod(zz, zzzInv, p); // 1 / z\n uint256 zzInv = mulmod(zInv, zInv, p); // 1 / zz\n\n // invariant(mulmod(FCL_pModInv(zInv), FCL_pModInv(zInv), p) == zz)\n // invariant(mulmod(mulmod(FCL_pModInv(zInv), FCL_pModInv(zInv), p), FCL_pModInv(zInv), p) == zzz)\n\n x1 = mulmod(x, zzInv, p); // X / zz\n y1 = mulmod(y, zzzInv, p); // y = Y / zzz\n success = zzzInv_success;\n }\n\n /**\n * @dev Point at infinity in ZZ rep\n */\n function ecZZ_PointAtInf() internal pure returns (uint256, uint256, uint256, uint256) {\n return (0, 0, 0, 0);\n }\n\n /**\n * @dev Point at infinity in affine rep\n */\n function ecAffine_PointAtInf() internal pure returns (uint256, uint256) {\n return (0, 0);\n }\n\n /**\n * @dev u^-1 mod n\n */\n function nModInv(uint256 u) internal view returns (uint256 result, bool success) {\n return modInv(u, n, minus_2modn);\n }\n\n /**\n * @dev u^-1 mod p\n */\n function pModInv(uint256 u) internal view returns (uint256 result, bool success) {\n return modInv(u, p, minus_2modp);\n }\n\n /**\n * @dev u^-1 mod f = u^(phi(f) - 1) mod f = u^(f-2) mod f for prime f\n * by Fermat's little theorem, compute u^(f-2) mod f using modexp precompile\n * Assume f != 0.\n */\n function modInv(uint256 u, uint256 f, uint256 minus_2modf) internal view returns (uint256 result, bool success) {\n // invariant(f != 0);\n // invariant(f prime);\n\n // This seems like a relatively standard way to use this precompile:\n // https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3298/files#diff-489d4519a087ca2c75be3315b673587abeca3b302f807643e97efa7de8cb35a5R427\n\n bytes memory ret;\n (success, ret) = (address(0x05).staticcall(abi.encode(32, 32, 32, u, minus_2modf, f)));\n result = abi.decode(ret, (uint256));\n }\n}\n" + "content": "// SPDX-License-Identifier: MIT\n// Force a specific Solidity version for reproducibility.\npragma solidity 0.8.21;\n\n/**\n * This contract verifies P256 (secp256r1) signatures. It matches the exact\n * interface specified in the EIP-7212 precompile, allowing it to be used as a\n * fallback. It's based on Ledger's optimized implementation:\n * https://github.com/rdubois-crypto/FreshCryptoLib/tree/master/solidity\n **/\ncontract P256Verifier {\n /**\n * Precompiles don't use a function signature. The first byte of callldata\n * is the first byte of an input argument. In this case:\n *\n * input[ 0: 32] = signed data hash\n * input[ 32: 64] = signature r\n * input[ 64: 96] = signature s\n * input[ 96:128] = public key x\n * input[128:160] = public key y\n *\n * result[ 0: 32] = 0x00..00 (invalid) or 0x00..01 (valid)\n *\n * For details, see https://eips.ethereum.org/EIPS/eip-7212\n */\n fallback(bytes calldata input) external returns (bytes memory) {\n if (input.length != 160) {\n return abi.encodePacked(uint256(0));\n }\n\n bytes32 hash = bytes32(input[0:32]);\n uint256 r = uint256(bytes32(input[32:64]));\n uint256 s = uint256(bytes32(input[64:96]));\n uint256 x = uint256(bytes32(input[96:128]));\n uint256 y = uint256(bytes32(input[128:160]));\n\n uint256 ret = ecdsa_verify(hash, r, s, [x, y]) ? 1 : 0;\n\n return abi.encodePacked(ret);\n }\n\n // Parameters for the sec256r1 (P256) elliptic curve\n // Curve prime field modulus\n uint256 constant p =\n 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;\n // Short weierstrass first coefficient\n uint256 constant a = // The assumption a == -3 (mod p) is used throughout the codebase\n 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC;\n // Short weierstrass second coefficient\n uint256 constant b =\n 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B;\n // Generating point affine coordinates\n uint256 constant GX =\n 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296;\n uint256 constant GY =\n 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5;\n // Curve order (number of points)\n uint256 constant n =\n 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551;\n // -2 mod p constant, used to speed up inversion and doubling (avoid negation)\n uint256 constant minus_2modp =\n 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFD;\n // -2 mod n constant, used to speed up inversion\n uint256 constant minus_2modn =\n 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F;\n\n /**\n * @dev ECDSA verification given signature and public key.\n */\n function ecdsa_verify(\n bytes32 message_hash,\n uint256 r,\n uint256 s,\n uint256[2] memory pubKey\n ) private view returns (bool) {\n // Check r and s are in the scalar field\n if (r == 0 || r >= n || s == 0 || s >= n) {\n return false;\n }\n\n if (!ecAff_isOnCurve(pubKey[0], pubKey[1])) {\n return false;\n }\n\n (uint256 sInv, bool sInv_success) = nModInv(s);\n assert (sInv_success);\n\n uint256 scalar_u = mulmod(uint256(message_hash), sInv, n); // (h * s^-1) in scalar field\n uint256 scalar_v = mulmod(r, sInv, n); // (r * s^-1) in scalar field\n\n (uint256 r_x, bool mulmuladd_success) = ecZZ_mulmuladd(\n pubKey[0],\n pubKey[1],\n scalar_u,\n scalar_v\n );\n return r_x % n == r && mulmuladd_success;\n }\n\n /**\n * @dev Check if a point in affine coordinates is on the curve\n * Reject 0 point at infinity.\n */\n function ecAff_isOnCurve(\n uint256 x,\n uint256 y\n ) internal pure returns (bool) {\n if (x >= p || y >= p || (x == 0 && y == 0)) {\n return false;\n }\n\n uint256 LHS = mulmod(y, y, p); // y^2\n uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(a, x, p), p); // x^3 + a x\n RHS = addmod(RHS, b, p); // x^3 + a*x + b\n\n return LHS == RHS;\n }\n\n /**\n * @dev Computation of uG + vQ using Strauss-Shamir's trick, G basepoint, Q public key\n * returns tuple of (x coordinate of uG + vQ, boolean that is false if internal precompile staticcall fail)\n * Strauss-Shamir is described well in https://stackoverflow.com/a/50994362\n */\n function ecZZ_mulmuladd(\n uint256 QX,\n uint256 QY, // affine rep for input point Q\n uint256 scalar_u,\n uint256 scalar_v\n ) internal view returns (uint256 X, bool success) {\n uint256 zz = 1;\n uint256 zzz = 1;\n uint256 Y;\n uint256 HX;\n uint256 HY;\n bool add_success;\n\n if (scalar_u == 0 && scalar_v == 0) return (0, true);\n\n // H = g + Q\n (HX, HY, add_success) = ecAff_add(GX, GY, QX, QY);\n assert(add_success);\n\n int256 index = 255;\n uint256 bitpair;\n\n // Find the first bit index that's active in either scalar_u or scalar_v.\n while(index >= 0) {\n bitpair = compute_bitpair(uint256(index), scalar_u, scalar_v);\n index--;\n if (bitpair != 0) break;\n }\n\n // initialise (X, Y) depending on the first active bitpair.\n // invariant(bitpair != 0); // bitpair == 0 is only possible if u and v are 0.\n \n if (bitpair == 1) {\n (X, Y) = (GX, GY);\n } else if (bitpair == 2) {\n (X, Y) = (QX, QY);\n } else if (bitpair == 3) {\n (X, Y) = (HX, HY);\n }\n\n uint256 TX;\n uint256 TY;\n while(index >= 0) {\n (X, Y, zz, zzz) = ecZZ_double_zz(X, Y, zz, zzz);\n\n bitpair = compute_bitpair(uint256(index), scalar_u, scalar_v);\n index--;\n\n if (bitpair == 0) {\n continue;\n } else if (bitpair == 1) {\n (TX, TY) = (GX, GY);\n } else if (bitpair == 2) {\n (TX, TY) = (QX, QY);\n } else {\n (TX, TY) = (HX, HY);\n }\n\n (X, Y, zz, zzz) = ecZZ_dadd_affine(X, Y, zz, zzz, TX, TY);\n }\n\n uint256 zzInv;\n (zzInv, success) = pModInv(zz);\n X = mulmod(X, zzInv, p); // X/zz\n }\n\n /**\n * @dev Compute the bits at `index` of u and v and return\n * them as 2 bit concatenation.\n * todo: add example\n */\n function compute_bitpair(uint256 index, uint256 scalar_u, uint256 scalar_v) internal pure returns (uint256 ret) {\n ret = (((scalar_v >> index) & 1) << 1) + ((scalar_u >> index) & 1);\n }\n\n /**\n * @dev Add two elliptic curve points in affine coordinates\n * Assumes points are on the EC\n */\n function ecAff_add(\n uint256 x1,\n uint256 y1,\n uint256 x2,\n uint256 y2\n ) internal view returns (uint256, uint256, bool) {\n // invariant(ecAff_IsZero(x1, y1) || ecAff_isOnCurve(x1, y1));\n // invariant(ecAff_IsZero(x2, y2) || ecAff_isOnCurve(x2, y2));\n\n uint256 zz1;\n uint256 zzz1;\n\n if (ecAff_IsInf(x1, y1)) return (x2, y2, true);\n if (ecAff_IsInf(x2, y2)) return (x1, y1, true);\n\n (x1, y1, zz1, zzz1) = ecZZ_dadd_affine(x1, y1, 1, 1, x2, y2);\n\n return ecZZ_SetAff(x1, y1, zz1, zzz1);\n }\n\n /**\n * @dev Check if a point is the infinity point in affine rep.\n * Assumes point is on the EC or is the point at infinity.\n */\n function ecAff_IsInf(\n uint256 x,\n uint256 y\n ) internal pure returns (bool flag) {\n // invariant((x == 0 && y == 0) || ecAff_isOnCurve(x, y));\n\n return (x == 0 && y == 0);\n }\n\n /**\n * @dev Add a ZZ point to an affine point and return as ZZ rep\n * Uses madd-2008-s and mdbl-2008-s internally\n * https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html#addition-madd-2008-s\n * Matches https://github.com/supranational/blst/blob/9c87d4a09d6648e933c818118a4418349804ce7f/src/ec_ops.h#L705 closely\n * Handles points at infinity gracefully\n */\n function ecZZ_dadd_affine(\n uint256 x1,\n uint256 y1,\n uint256 zz1,\n uint256 zzz1,\n uint256 x2,\n uint256 y2\n ) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) {\n if (y2 == 0) { // (X2, Y2) is point at infinity\n if (zz1 == 0 && zzz1 == 0) return ecZZ_PointAtInf();\n return (x1, y1, zz1, zzz1);\n } else if (zz1 == 0 && zzz1 == 0) { // (X1, Y1) is point at infinity\n return (x2, y2, 1, 1);\n }\n\n uint256 comp_R = addmod(mulmod(y2, zzz1, p), p - y1, p); // R = S2 - y1 = y2*zzz1 - y1\n uint256 comp_P = addmod(mulmod(x2, zz1, p), p - x1, p); // P = U2 - x1 = x2*zz1 - x1\n\n if (comp_P != 0) { // X1 != X2\n // invariant(x1 != x2);\n uint256 comp_PP = mulmod(comp_P, comp_P, p); // PP = P^2\n uint256 comp_PPP = mulmod(comp_PP, comp_P, p); // PPP = P*PP\n zz3 = mulmod(zz1, comp_PP, p); //// ZZ3 = ZZ1*PP\n zzz3 = mulmod(zzz1, comp_PPP, p); //// ZZZ3 = ZZZ1*PPP\n uint256 comp_Q = mulmod(x1, comp_PP, p); // Q = X1*PP\n x3 = addmod(\n addmod(mulmod(comp_R, comp_R, p), p - comp_PPP, p), // (R^2) + (-PPP)\n mulmod(minus_2modp, comp_Q, p), // (-2)*(Q)\n p\n ); // R^2 - PPP - 2*Q\n y3 = addmod(\n mulmod(addmod(comp_Q, p - x3, p), comp_R, p), //(Q+(-x3))*R\n mulmod(p - y1, comp_PPP, p), // (-y1)*PPP\n p\n ); // R*(Q-x3) - y1*PPP\n } else if (comp_R == 0) { // X1 == X2 and Y1 == Y2\n // invariant(x1 == x2 && y1 == y2);\n\n // Must be affine because (X2, Y2) is affine.\n (x3, y3, zz3, zzz3) = ecZZ_double_affine(x2, y2);\n } else { // X1 == X2 and Y1 == -Y2\n // invariant(x1 == x2 && y1 == p - y2);\n (x3, y3, zz3, zzz3) = ecZZ_PointAtInf();\n }\n\n return (x3, y3, zz3, zzz3);\n }\n\n /**\n * @dev Double a ZZ point \n * Uses http://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-dbl-2008-s-1\n * Handles point at infinity gracefully\n */\n function ecZZ_double_zz(uint256 x1,\n uint256 y1, uint256 zz1, uint256 zzz1) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) {\n if (zz1 == 0 && zzz1 == 0) return ecZZ_PointAtInf();\n if (zz1 == 1 && zzz1 == 1) return ecZZ_double_affine(x1, y1);\n \n uint256 comp_U = mulmod(2, y1, p); // U = 2*Y1\n uint256 comp_V = mulmod(comp_U, comp_U, p); // V = U^2\n uint256 comp_W = mulmod(comp_U, comp_V, p); // W = U*V\n uint256 comp_S = mulmod(x1, comp_V, p); // S = X1*V\n uint256 comp_M = addmod(mulmod(3, mulmod(x1, x1, p), p), mulmod(a, mulmod(zz1, zz1, p), p), p); //M = 3*(X1)^2 + a*(zz1)^2\n \n x3 = addmod(mulmod(comp_M, comp_M, p), mulmod(minus_2modp, comp_S, p), p); // M^2 + (-2)*S\n y3 = addmod(mulmod(comp_M, addmod(comp_S, p - x3, p), p), mulmod(p - comp_W, y1, p), p); // M*(S+(-X3)) + (-W)*Y1\n zz3 = mulmod(comp_V, zz1, p); // V*ZZ1\n zzz3 = mulmod(comp_W, zzz1, p); // W*ZZZ1\n }\n\n /**\n * @dev Double an affine point and return as a ZZ point \n * Uses http://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz.html#doubling-mdbl-2008-s-1\n * Handles point at infinity gracefully\n */\n function ecZZ_double_affine(uint256 x1,\n uint256 y1) internal pure returns (uint256 x3, uint256 y3, uint256 zz3, uint256 zzz3) {\n if (y1 == 0) return ecZZ_PointAtInf();\n\n uint256 comp_U = mulmod(2, y1, p); // U = 2*Y1\n zz3 = mulmod(comp_U, comp_U, p); // V = U^2 = zz3\n zzz3 = mulmod(comp_U, zz3, p); // W = U*V = zzz3\n uint256 comp_S = mulmod(x1, zz3, p); // S = X1*V\n uint256 comp_M = addmod(mulmod(3, mulmod(x1, x1, p), p), a, p); // M = 3*(X1)^2 + a\n \n x3 = addmod(mulmod(comp_M, comp_M, p), mulmod(minus_2modp, comp_S, p), p); // M^2 + (-2)*S\n y3 = addmod(mulmod(comp_M, addmod(comp_S, p - x3, p), p), mulmod(p - zzz3, y1, p), p); // M*(S+(-X3)) + (-W)*Y1\n }\n\n /**\n * @dev Convert from ZZ rep to affine rep\n * Assumes (zz)^(3/2) == zzz (i.e. zz == z^2 and zzz == z^3)\n * See https://hyperelliptic.org/EFD/g1p/auto-shortw-xyzz-3.html\n */\n function ecZZ_SetAff(\n uint256 x,\n uint256 y,\n uint256 zz,\n uint256 zzz\n ) internal view returns (uint256 x1, uint256 y1, bool success) {\n if(zz == 0 && zzz == 0) {\n (x1, y1) = ecAffine_PointAtInf();\n return (x1, y1, true);\n }\n\n (uint256 zzzInv, bool zzzInv_success) = pModInv(zzz); // 1 / zzz\n uint256 zInv = mulmod(zz, zzzInv, p); // 1 / z\n uint256 zzInv = mulmod(zInv, zInv, p); // 1 / zz\n\n // invariant(mulmod(FCL_pModInv(zInv), FCL_pModInv(zInv), p) == zz)\n // invariant(mulmod(mulmod(FCL_pModInv(zInv), FCL_pModInv(zInv), p), FCL_pModInv(zInv), p) == zzz)\n\n x1 = mulmod(x, zzInv, p); // X / zz\n y1 = mulmod(y, zzzInv, p); // y = Y / zzz\n success = zzzInv_success;\n }\n\n /**\n * @dev Point at infinity in ZZ rep\n */\n function ecZZ_PointAtInf() internal pure returns (uint256, uint256, uint256, uint256) {\n return (0, 0, 0, 0);\n }\n\n /**\n * @dev Point at infinity in affine rep\n */\n function ecAffine_PointAtInf() internal pure returns (uint256, uint256) {\n return (0, 0);\n }\n\n /**\n * @dev u^-1 mod n\n */\n function nModInv(uint256 u) internal view returns (uint256 result, bool success) {\n return modInv(u, n, minus_2modn);\n }\n\n /**\n * @dev u^-1 mod p\n */\n function pModInv(uint256 u) internal view returns (uint256 result, bool success) {\n return modInv(u, p, minus_2modp);\n }\n\n /**\n * @dev u^-1 mod f = u^(phi(f) - 1) mod f = u^(f-2) mod f for prime f\n * by Fermat's little theorem, compute u^(f-2) mod f using modexp precompile\n * Assume f != 0.\n */\n function modInv(uint256 u, uint256 f, uint256 minus_2modf) internal view returns (uint256 result, bool success) {\n // invariant(f != 0);\n // invariant(f prime);\n\n // This seems like a relatively standard way to use this precompile:\n // https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3298/files#diff-489d4519a087ca2c75be3315b673587abeca3b302f807643e97efa7de8cb35a5R427\n\n bytes memory ret;\n (success, ret) = (address(0x05).staticcall(abi.encode(32, 32, 32, u, minus_2modf, f)));\n result = abi.decode(ret, (uint256));\n }\n}\n" } }, "settings": { @@ -14,7 +14,6 @@ "enabled": true, "runs": 999999 }, - "viaIR": true, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", @@ -35,6 +34,7 @@ } }, "evmVersion": "paris", + "viaIR": true, "libraries": {} } } \ No newline at end of file diff --git a/src/P256Verifier.sol b/src/P256Verifier.sol index dfd9995..939f983 100644 --- a/src/P256Verifier.sol +++ b/src/P256Verifier.sol @@ -105,7 +105,7 @@ contract P256Verifier { uint256 x, uint256 y ) internal pure returns (bool) { - if (0 == x || x >= p || 0 == y || y >= p) { + if (x >= p || y >= p || (x == 0 && y == 0)) { return false; } @@ -212,8 +212,8 @@ contract P256Verifier { uint256 zz1; uint256 zzz1; - if (ecAff_IsZero(x1, y1)) return (x2, y2, true); - if (ecAff_IsZero(x2, y2)) return (x1, y1, true); + if (ecAff_IsInf(x1, y1)) return (x2, y2, true); + if (ecAff_IsInf(x2, y2)) return (x1, y1, true); (x1, y1, zz1, zzz1) = ecZZ_dadd_affine(x1, y1, 1, 1, x2, y2); @@ -221,16 +221,16 @@ contract P256Verifier { } /** - * @dev Check if the curve is the zero curve in affine rep - * Assumes point is on the EC or is the zero point. + * @dev Check if a point is the infinity point in affine rep. + * Assumes point is on the EC or is the point at infinity. */ - function ecAff_IsZero( - uint256, + function ecAff_IsInf( + uint256 x, uint256 y ) internal pure returns (bool flag) { // invariant((x == 0 && y == 0) || ecAff_isOnCurve(x, y)); - return (y == 0); + return (x == 0 && y == 0); } /** diff --git a/test/P256Verifier.t.sol b/test/P256Verifier.t.sol index 8118bd2..0147691 100644 --- a/test/P256Verifier.t.sol +++ b/test/P256Verifier.t.sol @@ -153,11 +153,10 @@ contract P256VerifierTest is Test { assertEq(result, false); assertLt(gasUsed, 1500); - // p-1 is in-bounds, takes more gas again. + // p-1 is in-bounds but point is not on curve. (x, y) = (p - 1, 1); (result, gasUsed) = evaluate(hash, r, s, x, y); console2.log("gasUsed ", gasUsed); assertEq(result, false); - assertGt(gasUsed, 1500); } }