UNPKG

@polygon-hermez/vm

Version:
106 lines (91 loc) 2.98 kB
// packages/vm/src/evm/precompiles/p256verify.ts import { setLengthLeft, setLengthRight, BN } from 'ethereumjs-util' import { PrecompileInput } from './types' import { OOGResult, ExecResult } from '../evm' const assert = require('assert') import { ec as EC } from 'elliptic'; // Initialize the P-256 curve (secp256r1) const ec = new EC('p256'); // 'p256' is equivalent to 'prime256v1' or 'secp256r1' function verifyP256Signature( pubKeyX: Buffer, pubKeyY: Buffer, r: Buffer, s: Buffer, msgHash: Buffer ): boolean { // Check that all inputs have the correct length (32 bytes each) if ( pubKeyX.length !== 32 || pubKeyY.length !== 32 || r.length !== 32 || s.length !== 32 || msgHash.length !== 32 ) { throw new Error('Invalid input lengths for P-256 signature verification'); } // Recreate the public key from the X and Y coordinates const key = ec.keyFromPublic( { x: pubKeyX.toString('hex'), y: pubKeyY.toString('hex'), }, 'hex' ); // Create the signature object const signature = { r: r.toString('hex'), s: s.toString('hex'), }; // Verify the signature return key.verify(msgHash.toString('hex'), signature); } /** * Precompiled contract for P256 signature verification (RIP-7212). * It takes the public key, message hash, and signature, and verifies if the signature is valid. * * Input: * - publicKey: 64 bytes (32 bytes for X coordinate, 32 bytes for Y coordinate) * - signature: 64 bytes (32 bytes for R, 32 bytes for S) * - message: 32 bytes (hash of the message) */ export default function (opts: PrecompileInput): ExecResult { assert(opts.data) // Gas cost is 3450 const gasUsed = new BN(opts._common.param('gasPrices', 'p256verify')) // Check if there is enough gas to run this precompile if (opts.gasLimit.lt(gasUsed)) { return OOGResult(opts.gasLimit) } // Ensure the data is the correct length for P256VERIFY // Input should be 128 bytes: 64 bytes for public key, 64 bytes for signature const data = setLengthRight(opts.data, 160) const msg = data.slice(0, 32) const r = data.slice(32, 64) const s = data.slice(64, 96) const pubKeyX = data.slice(96, 128) const pubKeyY = data.slice(128, 160) // Assuming a 32-byte message hash // Reduce counters opts._VM.vcm.computeFunctionCounters('preP256Verify', { pubKeyX: new BN(pubKeyX).toString('hex'), pubKeyY: new BN(pubKeyY).toString('hex'), r: new BN(r).toString('hex'), s: new BN(s).toString('hex'), }) // Verify the signature let isValid try { isValid = verifyP256Signature(pubKeyX, pubKeyY, r, s, msg) } catch (e: any) { // If verification fails, return an empty buffer return { gasUsed, returnValue: Buffer.alloc(0), } } // Return 1 for valid signature, 0 for invalid const returnValue = isValid ? setLengthLeft(Buffer.from([1]), 32) : Buffer.alloc(0) return { gasUsed, returnValue: returnValue, } }