Skip to content
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

Add EIP: Precompiled for secp256r1 Curve Support #7212

Merged
merged 12 commits into from
Jun 29, 2023
179 changes: 179 additions & 0 deletions EIPS/eip-7212.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
---
eip: 7212
title: Precompiled for secp256r1 Curve Support
description: Proposal to add precompiled contract that allows signature verifications in the “secp256r1” elliptic curve.
author: Ulaş Erdoğan (@ulerdogan), Doğan Alpaslan (@doganalpaslan)
discussions-to: https://ethereum-magicians.org/t/eip-7212-precompiled-for-secp256r1-curve-support/14789
status: Draft
type: Standards Track
category: Core
created: 2023-06-22
---

## Abstract

The proposal aims creating precompiled contract that allows signature verifications in the “secp256r1” elliptic curve by given parameters of message hash, `r` - `s` components of the signature, and `x` - `y` coordinates of the public key. So that, any EVM chain -principally Ethereum rollups- will be able to integrate this precompiled contract easily.

## Motivation

“secp256r1” elliptic curve is a standardized curve by NIST which has the same calculations by different input parameters with “secp256k1” elliptic curve used by the “ecrecover” precompiled contract. The cost of combined attacks and the security conditions are almost the same for both curves. Adding a precompiled contract which is similar to "ecrecover" can provide signature verifications using the “secp256r1” elliptic curve in the smart contracts and multi-faceted benefits can occur. One important factor is that this curve is widely used and supported in many modern devices such as Apple’s Secure Enclave, Webauthn, Android Keychain which proves the user adoption. Additionally, the introduction of this precompile could enable valuable features in the account abstraction which allows more efficient and flexible management of accounts by transaction signs in mobile devices.
Most of the modern devices and applications rely on the “secp256r1” elliptic curve. The addition of this precompiled contract enables the verification of device native transaction signing mechanisms. For example:

1. **Apple's Secure Enclave:** There is a separate “Trusted Execution Environment” in Apple hardware which can sign arbitrary messages and can only be accessed by biometric identification.
2. **Webauthn:** Web Authentication (WebAuthn) is a web standard published by the World Wide Web Consortium (W3C). WebAuthn aims to standardize an interface for authenticating users to web-based applications and services using public-key cryptography. It is being used by almost all of the modern web browsers.
3. **Android Keystore:** Android Keystore is an API that manages the private keys and signing methods. The private keys are not processed while using Keystore as the applications’ signing method. Also, it can be done in the “Trusted Execution Environment” in the microchip.
4. **Passkeys:** Passkeys is utilizing FIDO Alliance and W3C standards. It replaces passwords with cryptographic key-pairs which is also can be used for the elliptic curve cryptography.

Modern devices have these signing mechanisms that are designed to be more secure and they are able to sign transaction data, but none of the current wallets are utilizing these signing mechanisms. So, these secure signing methods can be enabled by the proposed precompiled contract to initiate the transactions natively from the devices and also, can be used for the key management. This proposal aims to reach maximum security and convenience for the key management.

## Specification

As of `FORK_TIMESTAMP` in the integrated EVM chain, add precompiled contract `P256VERIFY` for signature verifications in the “secp256r1” elliptic curve at address `PRECOMPILED_ADDRESS`.
ulerdogan marked this conversation as resolved.
Show resolved Hide resolved

### Elliptic Curve Information

“secp256r1” is a specific elliptic curve, also known as “P-256” and “prime256v1” curves. The curve is defined with the following equation and domain parameters:

```
# curve:
y^2 ≡ x^3 + ax + b

# p: specifies reduced elliptic group
0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff

# a: elliptic curve coefficient
0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc

# b: elliptic curve coefficient
0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b

# G: base point of the subgroup
(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5)

# n: order of the subgroup
0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551

# h: cofactor of the subgroup
0x1

```

### Elliptic Curve Signature Verification Steps

The signature verifying algorithm takes the signed message hash, the signature components provided by the “secp256r1” curve algorithm, and the public key derived from the signer private key. The verification can be done with the following steps:

```
# h (message hash)
# pubKey = (public key of the signer private key)

# Calculate the modular inverse of the signature proof:
s1 = s^(−1)(modn)

# Recover the random point used during the signing:
R' = (h * s1) * G + (r * s1) * pubKey

# Take from R' its x-coordinate:
r' = R'.x

# Calculate the signature validation result by comparing whether:
r' == r

```

### Required Checks in the Verification

Some requirements have to be checked to understand if the given signature components are valid:

- Verify that both `r` - `s` values are greater than 0 and less than the curve order.
- Verify that s is equal to or less than half of the order of the subgroup to prevent signature malleability.
- Verify that the point formed by (`x`, `y`) values is on the curve and both components are in between 0 and the `p` value of the curve.

### Precompiled Contract Specification

The `P256VERIFY` precompiled contract is proposed with the following input and outputs, which are big-endian values:

- **Input data:** 160 bytes of data including:
- 32 bytes of the signed data `hash`
- 32 bytes of the `r` component of the signature
- 32 bytes of the `s` component of the signature
- 32 bytes of the `x` coordinate of the public key
- 32 bytes of the `y` coordinate of the public key
- **Output data:** 32 bytes of result data and error
- If the signature verification process succeeds, it returns 1 in 32 bytes format.

### Precompiled Contract Gas Usage

The use of signature verification cost by `P256VERIFY` is `3450` gas. Following reasons and calculations are provided in the [Rationale](#rationale) and [Test Cases](#test-cases) sections.

## Rationale

The "secp256r1" elliptic curve signatures consists of `v`, `r`, `s` components. Even if recovering the public key on the curve is possible, most of the applications are not generating `v` component of the signature and it causes an uncertainty of the result values. However, the signatures can be verified with only `r` - `s` values. In order to provide an exact and more compatible method, verification is preferred over recovery to propose in a precompiled.
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to clarify exactly what we gain from requiring x/y as inputs. It's not clear to me from the explanation here.

And requiring them as inputs requires substantial added call data.

Copy link

@dcposch dcposch Jun 23, 2023

Choose a reason for hiding this comment

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

If I understand correctly,

  • v is a sign bit
  • the P256 precompile is intended to support hardware signatures
  • most of the existing hardware signature mechanisms (Secure Enclave, Webauthn, etc) return only the (r,s) signature

It's easy to recover v -- you try the two possible values, and see which one returns your public key.

So the comparison is something like

v, r, s style

Pros

  • Matches ecrecover.
  • Less calldata. On the surface, 1 word = 32 bytes less, but actual savings should be a bit more, since the v parameter is mostly 0 bytes and therefore cheaper.

Cons

  • Users / libraries have to recover v. This is a bit awkward. Instead of just calling (hardware signing module), it has to call (hardware signing module) > (attempt verification with + v) > (attempt verification with - v). I am not a cryptographer... maybe there's a better way.

v, r, x, y style

Pros

  • Better matches the hardware interface. Users/libraries don't have to recover v, they just pass a pubkey (x,y) directly from the hardware enclave.

Copy link
Contributor

@xinbenlv xinbenlv Jun 23, 2023

Choose a reason for hiding this comment

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

FYI ERC-2098 by @ricmoo for might be relevant in the same spirit where user / library recovers v for secp256k1.

This is provided to you as a reference and you can gauge the preference by usage / adoption for designing the precompiles

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @dcposch for the explanation of the logic.

Yes, the mechanisms using the curve are returning only r and s, so the verification is possible with inputting public key coordinates x and y.

We need v value to recover public key without the x and y coordinates. The v value can be found by above-mentioned methods, but let me explain my design choice:

  • While we are making recovery with the ecrecover, we can reach the public address of the EOA accounts, so it can be directly used in the smart contracts. Unlikely, recovering the secp256r1 public key does not match any default stored types, and we still need to store the account public key.
  • Having to find the v value in the implementation part of the signature creates complexity on the application side. So, I didn't want to bring this complexity for the applications. Still, I would love to reassess and edit the EIP to implement recovery after discussion.


The signature values in `r` - `s` and the public key coordinates in the `x`- `y` provides direct computations in signing and verification part, so these formats are chose in the input data format which are 32 bytes.

The gas cost has proposed by comparing the performances of the `P256VERIFY` and the `ECRECOVER` which is implemented in the EVM at `0x01` address. It is seen that “secp256r1” signature verification by `P256VERIFY` is ~15% slower (elaborated in the [test cases](#test-cases) part) than “secp256k1” signature recovery by `ECRECOVER`, so `3450` gas is proposed by comparison which causes similar “mgas/op” values in both precompiles.

## Backwards Compatibility

No backward compatibility issues found as the precompiled contract will be added to `PRECOMPILED_ADDRESS`, one of the next address in the precompiled address set.

## Test Cases

Functional tests are applied for multiple cases in the [reference implementation](#reference-implementation) of `P256VERIFY` precompiled contract and they succeed. Benchmark tests are also applied for both `P256VERIFY` and `ECRECOVER` with some pre-calculated data and signatures in the “go-ethereum”s precompile testing structure to propose a meaningful gas cost for the “secp256r1” signature verifications by the precompiled contract implemented in the [reference implementation](#reference-implementation). The benchmark test results by example data in the assets can be checked:

- [P256Verify Benchmark Test Results](../assets/eip-7212/p256Verify_benchmark_test)
- [Ecrecover Benchmark Test Results](../assets/eip-7212/ecrecover_benchmark_test)

```
Copy link
Contributor

@jwasinger jwasinger Jun 23, 2023

Choose a reason for hiding this comment

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

IMO it's uneccessary to put system-specific benchmark results in the EIP itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, thanks for your comments! How do the benchmarks be displayed? I think that it's important to propose gas costs for the precompiled contract. I thought if I am comparing two results to propose gas cost in the same system would be beneficial. Should I include more systems benchmark results or completely remove it?

Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO The benchmark is being supplied as assets, I personally think it's a good supporting evidence for rationale like the gas cost choice. Hence I think it's good to be there. My two cents. Others might disagree.

# results of geth benchmark tests of
# ECRECOVER and P256VERIFY (reference implementation)
# by benchstat tool

goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/core/vm
│ compare_p256Verify │ compare_ecrecover │
│ sec/op │ sec/op │
PrecompiledP256Verify/p256Verify-Gas=3450-8 57.75µ ± 1%
PrecompiledEcrecover/-Gas=3000-8 50.48µ ± 1%
geomean 57.75µ 50.48µ

│ compare_p256Verify │ compare_ecrecover │
│ gas/op │ gas/op │
PrecompiledP256Verify/p256Verify-Gas=3450-8 3.450k ± 0%
PrecompiledEcrecover/-Gas=3000-8 3.000k ± 0%
geomean 3.450k 3.000k

│ compare_p256Verify │ compare_ecrecover │
│ mgas/s │ mgas/s │
PrecompiledP256Verify/p256Verify-Gas=3450-8 59.73 ± 1%
PrecompiledEcrecover/-Gas=3000-8 59.42 ± 1%
geomean 59.73 59.42

│ compare_p256Verify │ compare_ecrecover │
│ B/op │ B/op │
PrecompiledP256Verify/p256Verify-Gas=3450-8 1.523Ki ± 0%
PrecompiledEcrecover/-Gas=3000-8 800.0 ± 0%
geomean 1.523Ki 800.0

│ compare_p256Verify │ compare_ecrecover │
│ allocs/op │ allocs/op │
PrecompiledP256Verify/p256Verify-Gas=3450-8 33.00 ± 0%
PrecompiledEcrecover/-Gas=3000-8 7.000 ± 0%
geomean 33.00 7.000

```

## Reference Implementation

Implementation of the `P256VERIFY` precompiled contract is applied to go-ethereum client to create a reference. Also, an “secp256r1” package has already been included in the Besu Native library which is used by Besu client. Other client implementations are in the future roadmap.

## Security Considerations

The changes are not directly affecting the protocol security, it is related with the applications using `P256VERIFY` for the signature verifications. The “secp256r1” curve has been using in many other protocols and services and there is not any security issues in the past.


## Copyright

Copyright and related rights waived via CC0.
ulerdogan marked this conversation as resolved.
Show resolved Hide resolved
42 changes: 42 additions & 0 deletions assets/eip-7212/benchstat_compare_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/core/vm
│ compare_p256Verify │ compare_ecrecover │
│ sec/op │ sec/op vs base │
PrecompiledP256Verify/p256Verify-Gas=3450-8 57.75µ ± 1%
PrecompiledEcrecover/-Gas=3000-8 50.48µ ± 1%
geomean 57.75µ 50.48µ ? ¹ ²
¹ benchmark set differs from baseline; geomeans may not be comparable
² ratios must be >0 to compute geomean

│ compare_p256Verify │ compare_ecrecover │
│ gas/op │ gas/op vs base │
PrecompiledP256Verify/p256Verify-Gas=3450-8 3.450k ± 0%
PrecompiledEcrecover/-Gas=3000-8 3.000k ± 0%
geomean 3.450k 3.000k ? ¹ ²
¹ benchmark set differs from baseline; geomeans may not be comparable
² ratios must be >0 to compute geomean

│ compare_p256Verify │ compare_ecrecover │
│ mgas/s │ mgas/s vs base │
PrecompiledP256Verify/p256Verify-Gas=3450-8 59.73 ± 1%
PrecompiledEcrecover/-Gas=3000-8 59.42 ± 1%
geomean 59.73 59.42 ? ¹ ²
¹ benchmark set differs from baseline; geomeans may not be comparable
² ratios must be >0 to compute geomean

│ compare_p256Verify │ compare_ecrecover │
│ B/op │ B/op vs base │
PrecompiledP256Verify/p256Verify-Gas=3450-8 1.523Ki ± 0%
PrecompiledEcrecover/-Gas=3000-8 800.0 ± 0%
geomean 1.523Ki 800.0 ? ¹ ²
¹ benchmark set differs from baseline; geomeans may not be comparable
² ratios must be >0 to compute geomean

│ compare_p256Verify │ compare_ecrecover │
│ allocs/op │ allocs/op vs base │
PrecompiledP256Verify/p256Verify-Gas=3450-8 33.00 ± 0%
PrecompiledEcrecover/-Gas=3000-8 7.000 ± 0%
geomean 33.00 7.000 ? ¹ ²
¹ benchmark set differs from baseline; geomeans may not be comparable
² ratios must be >0 to compute geomean
15 changes: 15 additions & 0 deletions assets/eip-7212/ecrecover_benchmark_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/core/vm
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23295 50034 ns/op 3000 gas/op 59.95 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23734 50558 ns/op 3000 gas/op 59.33 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23823 50586 ns/op 3000 gas/op 59.30 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23913 50049 ns/op 3000 gas/op 59.94 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23721 50299 ns/op 3000 gas/op 59.64 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23760 51160 ns/op 3000 gas/op 58.63 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23151 50818 ns/op 3000 gas/op 59.03 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23744 53451 ns/op 3000 gas/op 56.12 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 22837 50315 ns/op 3000 gas/op 59.62 mgas/s 800 B/op 7 allocs/op
BenchmarkPrecompiledEcrecover/-Gas=3000-8 23823 50401 ns/op 3000 gas/op 59.52 mgas/s 800 B/op 7 allocs/op
PASS
ok github.com/ethereum/go-ethereum/core/vm 17.687s
15 changes: 15 additions & 0 deletions assets/eip-7212/p256Verify_benchmark_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/core/vm
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20770 57970 ns/op 3450 gas/op 59.51 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20899 57769 ns/op 3450 gas/op 59.71 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20780 57343 ns/op 3450 gas/op 60.16 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20870 57740 ns/op 3450 gas/op 59.74 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20911 57411 ns/op 3450 gas/op 60.09 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20874 58423 ns/op 3450 gas/op 59.04 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20736 57552 ns/op 3450 gas/op 59.94 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 19700 58235 ns/op 3450 gas/op 59.24 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20814 57681 ns/op 3450 gas/op 59.80 mgas/s 1560 B/op 33 allocs/op
BenchmarkPrecompiledP256Verify/p256Verify-Gas=3450-8 20736 58806 ns/op 3450 gas/op 58.66 mgas/s 1560 B/op 33 allocs/op
PASS
ok github.com/ethereum/go-ethereum/core/vm 18.491s