UNPKG

@ethereumjs/evm

Version:
128 lines 5.26 kB
import { bytesToBigInt, bytesToHex, setLengthLeft } from '@ethereumjs/util'; import { p256 } from '@noble/curves/nist.js'; import { OOGResult } from "../evm.js"; import { getPrecompileName } from "./index.js"; import { gasLimitCheck } from "./util.js"; const P256VERIFY_GAS_COST = BigInt(6900); const SUCCESS_RETURN = new Uint8Array(32).fill(0).map((_, i) => (i === 31 ? 1 : 0)); // Curve parameters for secp256r1 const P256_N = BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551'); // Subgroup order const P256_P = BigInt('0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff'); // Base field modulus const P256_A = BigInt('0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc'); // Curve coefficient a const P256_B = BigInt('0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b'); // Curve coefficient b export function precompile100(opts) { const pName = getPrecompileName('100'); const gasUsed = P256VERIFY_GAS_COST; if (!gasLimitCheck(opts, gasUsed, pName)) { return OOGResult(opts.gasLimit); } const data = opts.data; // 1. Input length: Input MUST be exactly 160 bytes if (data.length !== 160) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: invalid input length ${data.length}, expected 160`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } const msgHash = data.subarray(0, 32); const r = data.subarray(32, 64); const s = data.subarray(64, 96); const qx = data.subarray(96, 128); const qy = data.subarray(128, 160); const rBigInt = bytesToBigInt(r); const sBigInt = bytesToBigInt(s); const qxBigInt = bytesToBigInt(qx); const qyBigInt = bytesToBigInt(qy); // 2. Signature component bounds: Both r and s MUST satisfy 0 < r < n and 0 < s < n if (!(rBigInt > BigInt(0) && rBigInt < P256_N && sBigInt > BigInt(0) && sBigInt < P256_N)) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: signature component out of bounds: r=${bytesToHex(r)}, s=${bytesToHex(s)}`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } // 3. Public key bounds: Both qx and qy MUST satisfy 0 ≤ qx < p and 0 ≤ qy < p if (!(qxBigInt >= BigInt(0) && qxBigInt < P256_P && qyBigInt >= BigInt(0) && qyBigInt < P256_P)) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: public key component out of bounds: qx=${bytesToHex(qx)}, qy=${bytesToHex(qy)}`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } // 4. Point validity: The point (qx, qy) MUST satisfy the curve equation qy^2 ≡ qx^3 + a*qx + b (mod p) const leftSide = (qyBigInt * qyBigInt) % P256_P; const rightSide = (qxBigInt * qxBigInt * qxBigInt + P256_A * qxBigInt + P256_B) % P256_P; if (leftSide !== rightSide) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: point not on curve`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } // 5. Point not at infinity: The point (qx, qy) MUST NOT be the point at infinity (represented as (0, 0)) if (qxBigInt === BigInt(0) && qyBigInt === BigInt(0)) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: public key is point at infinity`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } try { // Create public key point const publicKey = p256.Point.fromAffine({ x: qxBigInt, y: qyBigInt, }); // Create signature const rBytes = setLengthLeft(r, 32); const sBytes = setLengthLeft(s, 32); const signatureBytes = new Uint8Array(64); signatureBytes.set(rBytes, 0); signatureBytes.set(sBytes, 32); const signature = p256.Signature.fromBytes(signatureBytes).toBytes(); // Verify signature const isValid = p256.verify(signature, msgHash, publicKey.toBytes(false), { lowS: false, prehash: false, }); if (isValid) { if (opts._debug !== undefined) { opts._debug(`${pName} succeeded: signature verification passed`); } return { executionGasUsed: gasUsed, returnValue: SUCCESS_RETURN, }; } else { if (opts._debug !== undefined) { opts._debug(`${pName} failed: signature verification failed`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } } catch (error) { if (opts._debug !== undefined) { opts._debug(`${pName} failed: verification error: ${error}`); } return { executionGasUsed: gasUsed, returnValue: new Uint8Array(), }; } } //# sourceMappingURL=100-p256verify.js.map