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
EIP-7212: precompile for secp256r1 curve #27540
Changes from all commits
fb89b61
a8c0a2e
c0554c0
6c7028a
d245194
066a31f
ec17e78
6bf9e70
f5b6d7e
2820903
1be1875
21f4932
98f10b0
7e0bc92
cec0b05
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ import ( | |
"github.com/ethereum/go-ethereum/crypto/bls12381" | ||
"github.com/ethereum/go-ethereum/crypto/bn256" | ||
"github.com/ethereum/go-ethereum/crypto/kzg4844" | ||
"github.com/ethereum/go-ethereum/crypto/secp256r1" | ||
"github.com/ethereum/go-ethereum/params" | ||
"golang.org/x/crypto/ripemd160" | ||
) | ||
|
@@ -121,6 +122,12 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ | |
common.BytesToAddress([]byte{18}): &bls12381MapG2{}, | ||
} | ||
|
||
// PrecompiledContractsP256Verify contains the precompiled Ethereum | ||
// contract specified in EIP-7212. This is exported for testing purposes. | ||
var PrecompiledContractsP256Verify = map[common.Address]PrecompiledContract{ | ||
common.BytesToAddress([]byte{19}): &p256Verify{}, | ||
} | ||
|
||
var ( | ||
PrecompiledAddressesCancun []common.Address | ||
PrecompiledAddressesBerlin []common.Address | ||
|
@@ -1135,3 +1142,37 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash { | |
|
||
return h | ||
} | ||
|
||
// P256VERIFY (secp256r1 signature verification) | ||
// implemented as a native contract | ||
type p256Verify struct{} | ||
|
||
// RequiredGas returns the gas required to execute the precompiled contract | ||
func (c *p256Verify) RequiredGas(input []byte) uint64 { | ||
return params.P256VerifyGas | ||
} | ||
|
||
// Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas | ||
func (c *p256Verify) Run(input []byte) ([]byte, error) { | ||
// Required input length is 160 bytes | ||
const p256VerifyInputLength = 160 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we put some constraints here to make sure the input provided should scope to 160 bytes only to avoid unnecessary execution? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume you mean to halt precompile execution in an exceptional state? The EIP doesn't specify this behavior so it shouldn't be added here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems clean for the precompile to always return 1 or 0. might be worth specifying that it return 0 if the input is not exactly 160 bytes. any other input length indicates an error in the calling contract. also, what happens if a nonzero cc @ulerdogan There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a note, we do not care about value transmitted to precompiles via call. None of the other precompiles check for it. |
||
// Check the input length | ||
if len(input) != p256VerifyInputLength { | ||
// Input length is invalid | ||
return nil, nil | ||
} | ||
|
||
// Extract the hash, r, s, x, y from the input | ||
hash := input[0:32] | ||
r, s := new(big.Int).SetBytes(input[32:64]), new(big.Int).SetBytes(input[64:96]) | ||
x, y := new(big.Int).SetBytes(input[96:128]), new(big.Int).SetBytes(input[128:160]) | ||
|
||
// Verify the secp256r1 signature | ||
if secp256r1.Verify(hash, r, s, x, y) { | ||
// Signature is valid | ||
return common.LeftPadBytes(common.Big1.Bytes(), 32), nil | ||
ulerdogan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
// Signature is invalid | ||
return nil, nil | ||
ulerdogan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[ | ||
{ | ||
"Input": "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", | ||
"Expected": "0000000000000000000000000000000000000000000000000000000000000001", | ||
"Gas": 3450, | ||
"Name": "CallP256Verify", | ||
"NoBenchmark": false | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package secp256r1 | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"math/big" | ||
) | ||
|
||
// Generates approptiate public key format from given coordinates | ||
func newPublicKey(x, y *big.Int) *ecdsa.PublicKey { | ||
// Check if the given coordinates are valid | ||
if x == nil || y == nil || !elliptic.P256().IsOnCurve(x, y) { | ||
return nil | ||
} | ||
|
||
// Check if the given coordinates are the reference point (infinity) | ||
if x.Sign() == 0 && y.Sign() == 0 { | ||
return nil | ||
} | ||
|
||
return &ecdsa.PublicKey{ | ||
Curve: elliptic.P256(), | ||
X: x, | ||
Y: y, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package secp256r1 | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"math/big" | ||
) | ||
|
||
// Verifies the given signature (r, s) for the given hash and public key (x, y). | ||
func Verify(hash []byte, r, s, x, y *big.Int) bool { | ||
// Create the public key format | ||
publicKey := newPublicKey(x, y) | ||
|
||
// Check if they are invalid public key coordinates | ||
if publicKey == nil { | ||
return false | ||
} | ||
|
||
// Verify the signature with the public key, | ||
// then return true if it's valid, false otherwise | ||
return ecdsa.Verify(publicKey, hash, r, s) | ||
} |
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.
The implementation address updated to
0x0b
due to the EIP specification in my branch with this commit, but have not synced into this branch as it's closed.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.
The implementation address updated to
0x100
again with this commit as the EIP moved into the RIPs repo and this range has been attached for the RIP precompiles.