|
22 | 22 | ./hash_to_curve/hash_to_curve,
|
23 | 23 | # For KZG point precompile
|
24 | 24 | ./ethereum_eip4844_kzg,
|
25 |
| - ./serialization/codecs_status_codes |
| 25 | + ./serialization/codecs_status_codes, |
| 26 | + # ECDSA for ECRecover |
| 27 | + ./eth_ecdsa_signatures |
26 | 28 |
|
27 | 29 | # For KZG point precompile
|
28 | 30 | export EthereumKZGContext, TrustedSetupFormat, TrustedSetupStatus, trusted_setup_load, trusted_setup_delete
|
|
48 | 50 | cttEVM_PointNotOnCurve
|
49 | 51 | cttEVM_PointNotInSubgroup
|
50 | 52 | cttEVM_VerificationFailure
|
| 53 | + cttEVM_MalformedSignature |
51 | 54 |
|
52 | 55 | func eth_evm_sha256*(r: var openArray[byte], inputs: openArray[byte]): CttEVMStatus {.libPrefix: prefix_ffi, meter.} =
|
53 | 56 | ## SHA256
|
@@ -1295,3 +1298,78 @@ func eth_evm_kzg_point_evaluation*(ctx: ptr EthereumKZGContext,
|
1295 | 1298 | r.toOpenArray(32, 64-1).marshal(Fr[BLS12_381].getModulus(), bigEndian)
|
1296 | 1299 |
|
1297 | 1300 | result = cttEVM_Success
|
| 1301 | + |
| 1302 | +import std / importutils # Alternatively make `r`, `s` visible or define setter or constructor |
| 1303 | +func eth_evm_ecrecover*(r: var openArray[byte], |
| 1304 | + input: openArray[byte]): CttEVMStatus {.libPrefix: prefix_ffi, meter.} = |
| 1305 | + ## Attempts to recover the public key, which was used to sign the given `data` |
| 1306 | + ## to obtain the given signature `sig`. |
| 1307 | + ## |
| 1308 | + ## If the signature is invalid, the result array `r` will contain the neutral |
| 1309 | + ## element of the curve. |
| 1310 | + ## |
| 1311 | + ## Inputs: |
| 1312 | + ## - `r`: Array of the recovered public key. An elliptic curve point in affine |
| 1313 | + ## coordinates (`EC_ShortW_Aff[Fp[Secp256k1], G1]`). |
| 1314 | + ## - `input`: The input data as an array of 128 bytes. The data is as follows: |
| 1315 | + ## - 32 byte: `keccak256` digest of the message that was signed |
| 1316 | + ## - 32 byte: `v`, decides if the even or odd coordinate in `R` was used |
| 1317 | + ## - 32 byte: `r` of the signature, scalar `Fr[Secp256k1]` |
| 1318 | + ## - 32 byte: `s` of the signature, scalar `Fr[Secp256k1]` |
| 1319 | + ## |
| 1320 | + ## Implementation follows Geth here: |
| 1321 | + ## https://github.com/ethereum/go-ethereum/blob/341647f1865dab437a690dc1424ba71495de2dd8/core/vm/contracts.go#L243-L272 |
| 1322 | + ## |
| 1323 | + ## and to a lesser extent the Ethereum Yellow Paper in appendix F: |
| 1324 | + ## https://ethereum.github.io/yellowpaper/paper.pdf |
| 1325 | + ## |
| 1326 | + ## Internal Geth implementation in: |
| 1327 | + ## https://github.com/ethereum/go-ethereum/blob/master/signer/core/signed_data.go#L292-L319 |
| 1328 | + if len(input) != 128: |
| 1329 | + return cttEVM_InvalidInputSize |
| 1330 | + |
| 1331 | + if len(r) != 32: |
| 1332 | + return cttEVM_InvalidOutputSize |
| 1333 | + |
| 1334 | + # 1. construct message hash as scalar in field `Fr[Secp256k1]` |
| 1335 | + var msgBI {.noinit.}: BigInt[256] |
| 1336 | + msgBI.unmarshal(input.toOpenArray(0, 32-1), bigEndian) |
| 1337 | + var msgHash {.noinit.}: Fr[Secp256k1] |
| 1338 | + msgHash.fromBig(msgBI) |
| 1339 | + |
| 1340 | + # 2. verify `v` data is valid |
| 1341 | + ## XXX: Or construct a `BigInt[256]` instead and compare? (or compare with uint64s?) |
| 1342 | + for i in 32 ..< 63: # first 31 bytes must be zero for a valid `v` |
| 1343 | + if input[i] != byte 0: |
| 1344 | + return cttEVM_MalformedSignature |
| 1345 | + let v = input[63] |
| 1346 | + if v notin [byte 0, 1, 27, 28]: |
| 1347 | + return cttEVM_MalformedSignature |
| 1348 | + # 2a. determine if even or odd `y` coordinate |
| 1349 | + let evenY = v in [byte 0, 27] # 0 / 27 indicates `y` to be even, 1 / 28 odd |
| 1350 | + |
| 1351 | + # 3. unmarshal signature data |
| 1352 | + var signature {.noinit.}: Signature |
| 1353 | + privateAccess(Signature) |
| 1354 | + var rSig {.noinit}, sSig {.noinit.}: BigInt[256] |
| 1355 | + rSig.unmarshal(input.toOpenArray(64, 96-1), bigEndian) |
| 1356 | + sSig.unmarshal(input.toOpenArray(96, 128-1), bigEndian) |
| 1357 | + signature.r = Fr[Secp256k1].fromBig(rSig) |
| 1358 | + signature.s = Fr[Secp256k1].fromBig(sSig) |
| 1359 | + |
| 1360 | + # 4. perform pubkey recovery |
| 1361 | + var pubKey {.noinit.}: PublicKey |
| 1362 | + pubKey.recoverPubkeyFromDigest(msgHash, signature, evenY) |
| 1363 | + |
| 1364 | + # 4. now calculate the Ethereum address of the public key (keccak256) |
| 1365 | + privateAccess(PublicKey) |
| 1366 | + var rawPubkey {.noinit.}: array[64, byte] # `[x, y]` coordinates of public key |
| 1367 | + rawPubkey.toOpenArray( 0, 32-1).marshal(pubKey.raw.x, bigEndian) |
| 1368 | + rawPubkey.toOpenArray(32, 64-1).marshal(pubKey.raw.y, bigEndian) |
| 1369 | + var dgst {.noinit.}: array[32, byte] # keccak256 digest |
| 1370 | + keccak256.hash(dgst, rawPubkey) |
| 1371 | + |
| 1372 | + # 5. and effectively truncate to last 20 bytes of digest |
| 1373 | + r.rawCopy(12, dgst, 12, 20) |
| 1374 | + |
| 1375 | + result = cttEVM_Success |
0 commit comments