@ethereumjs/evm
Version:
JavaScript Ethereum Virtual Machine (EVM) implementation
131 lines • 5.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.precompile100 = precompile100;
const util_1 = require("@ethereumjs/util");
const nist_js_1 = require("@noble/curves/nist.js");
const evm_ts_1 = require("../evm.js");
const index_ts_1 = require("./index.js");
const util_ts_1 = require("./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
function precompile100(opts) {
const pName = (0, index_ts_1.getPrecompileName)('100');
const gasUsed = P256VERIFY_GAS_COST;
if (!(0, util_ts_1.gasLimitCheck)(opts, gasUsed, pName)) {
return (0, evm_ts_1.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 = (0, util_1.bytesToBigInt)(r);
const sBigInt = (0, util_1.bytesToBigInt)(s);
const qxBigInt = (0, util_1.bytesToBigInt)(qx);
const qyBigInt = (0, util_1.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=${(0, util_1.bytesToHex)(r)}, s=${(0, util_1.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=${(0, util_1.bytesToHex)(qx)}, qy=${(0, util_1.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 = nist_js_1.p256.Point.fromAffine({
x: qxBigInt,
y: qyBigInt,
});
// Create signature
const rBytes = (0, util_1.setLengthLeft)(r, 32);
const sBytes = (0, util_1.setLengthLeft)(s, 32);
const signatureBytes = new Uint8Array(64);
signatureBytes.set(rBytes, 0);
signatureBytes.set(sBytes, 32);
const signature = nist_js_1.p256.Signature.fromBytes(signatureBytes).toBytes();
// Verify signature
const isValid = nist_js_1.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