plonky2/evm/src/cpu/kernel/asm/ecrecover.asm
2022-07-15 09:56:52 +02:00

206 lines
6.4 KiB
NASM

// ecrecover precompile.
global ecrecover:
JUMPDEST
// stack: hash, v, r, s, retdest
// Check if inputs are valid.
%ecrecover_input_check
// stack: isValid(v,r,s), hash, v, r, s, retdest
// Lift r to an elliptic curve point if possible.
SWAP2
// stack: v, hash, isValid(v,r,s), r, s, retdest
DUP4
// stack: r, v, hash, isValid(v,r,s), r, s, retdest
// Compute v-27 which gives the parity of the y-coordinate of the lifted point.
SWAP1
// stack: v, r, hash, isValid(v,r,s), r, s, retdest
PUSH 27
// stack: 27, v, r, hash, isValid(v,r,s), r, s, retdest
SWAP1
// stack: v, 27, r, hash, isValid(v,r,s), r, s, retdest
SUB
// stack: v - 27, r, hash, isValid(v,r,s), r, s, retdest
SWAP1
// stack: r, v - 27, hash, isValid(v,r,s), r, s, retdest
%secp_lift_x
// stack: y, sqrtOk, hash, isValid(v,r,s), r, s, retdest
// If inputs are invalid or lifting fails, abort.
SWAP3
// stack: isValid(v,r,s), sqrtOk, hash, y, r, s, retdest
AND
// stack: isValid(v,r,s) & sqrtOk, hash, y, r, s, retdest
%jumpi(ecrecover_valid_input)
// stack: hash, y, r, s, retdest
%pop4
// stack: retdest
%ecrecover_invalid_input
// ecrecover precompile.
// Assumption: Inputs are valid.
// Pseudo-code:
// let P = lift_x(r, recovery_id);
// let r_inv = r.inverse();
// let u1 = s * r_inv;
// let u2 = -hash * r_inv;
// return u1*P + u2*GENERATOR;
ecrecover_valid_input:
JUMPDEST
// stack: hash, y, r, s, retdest
// Compute u1 = s * r^(-1)
SWAP1
// stack: y, hash, r, s, retdest
DUP3
// stack: r, y, hash, x, s, retdest (r=x)
%inverse_secp_scalar
// stack: r^(-1), y, hash, x, s, retdest
DUP1
// stack: r^(-1), r^(-1), y, hash, x, s, retdest
SWAP5
// stack: s, r^(-1), y, hash, x, r^(-1), retdest
%mulmodn_secp_scalar
// stack: u1, y, hash, x, r^(-1), retdest
// Compute (X,Y) = u1 * (x,y)
PUSH ecrecover_with_first_point
// stack: ecrecover_with_first_point, u1, y, hash, x, r^(-1), retdest
SWAP1
// stack: u1, ecrecover_with_first_point, y, hash, x, r^(-1), retdest
SWAP2
// stack: y, ecrecover_with_first_point, u1, hash, x, r^(-1), retdest
SWAP1
// stack: ecrecover_with_first_point, y, u1, hash, x, r^(-1), retdest
SWAP3
// stack: hash, y, u1, ecrecover_with_first_point, x, r^(-1), retdest
SWAP4
// stack: x, y, u1, ecrecover_with_first_point, hash, r^(-1), retdest
%jump(ec_mul_valid_point_secp)
// ecrecover precompile.
// Assumption: (X,Y) = u1 * P. Result is (X,Y) + u2*GENERATOR
ecrecover_with_first_point:
JUMPDEST
// stack: X, Y, hash, r^(-1), retdest
%secp_scalar
// stack: p, X, Y, hash, r^(-1), retdest
SWAP1
// stack: X, p, Y, hash, r^(-1), retdest
SWAP4
// stack: r^(-1), p, Y, hash, X, retdest
SWAP2
// stack: Y, p, r^(-1), hash, X, retdest
SWAP3
// stack: hash, p, r^(-1), Y, X, retdest
// Compute u2 = -hash * r^(-1)
MOD
// stack: hash%p, r^(-1), Y, X, retdest
%secp_scalar
// stack: p, hash%p, r^(-1), Y, X, retdest
SUB
// stack: -hash, r^(-1), Y, X, retdest
%mulmodn_secp_scalar
// stack: u2, Y, X, retdest
// Compute u2 * GENERATOR and chain the call to `ec_mul` with a call to `ec_add` to compute PUBKEY = (X,Y) + u2 * GENERATOR,
// and a call to `final_hashing` to get the final result `SHA3(PUBKEY)[-20:]`.
PUSH final_hashing
// stack: final_hashing, u2, Y, X, retdest
SWAP3
// stack: X, u2, Y, final_hashing, retdest
PUSH ec_add_valid_points_secp
// stack: ec_add_valid_points_secp, X, u2, Y, final_hashing, retdest
SWAP1
// stack: X, ec_add_valid_points_secp, u2, Y, final_hashing, retdest
PUSH 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 // x-coordinate of generator
// stack: Gx, X, ec_add_valid_points_secp, u2, Y, final_hashing, retdest
SWAP1
// stack: X, Gx, ec_add_valid_points_secp, u2, Y, final_hashing, retdest
PUSH 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 // y-coordinate of generator
// stack: Gy, X, Gx, ec_add_valid_points_secp, u2, Y, final_hashing, retdest
SWAP1
// stack: X, Gy, Gx, ec_add_valid_points_secp, u2, Y, final_hashing, retdest
SWAP4
// stack: u2, Gy, Gx, ec_add_valid_points_secp, X, Y, final_hashing, retdest
SWAP2
// stack: Gx, Gy, u2, ec_add_valid_points_secp, X, Y, final_hashing, retdest
%jump(ec_mul_valid_point_secp)
// TODO
final_hashing:
JUMPDEST
PUSH 0xdeadbeef
JUMP
// Check if v, r, and s are in correct form.
// Returns r < N & r!=0 & s < N & s!=0 & (v==28 || v==27).
%macro ecrecover_input_check
// stack: hash, v, r, s, retdest
DUP2
// stack: v, hash, v, r, s, retdest
PUSH 27
// stack: 27, v, hash, v, r, s, retdest
EQ
// stack: v==27, hash, v, r, s, retdest
DUP3
// stack: v, v==27, hash, v, r, s, retdest
PUSH 28
// stack: 28, v, v==27, hash, v, r, s, retdest
EQ
// stack: v==28, v==27, hash, v, r, s, retdest
OR
// stack: (v==28 || v==27), hash, v, r, s, retdest
DUP5
// stack: s, (v==28 || v==27), hash, v, r, s, retdest
%secp_is_out_of_bounds
// stack: (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest
DUP5
// stack: r, (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest
%secp_is_out_of_bounds
// stack: (r >= N || r==0), (s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest
OR
// stack: (r >= N || r==0 || s >= N || s==0), (v==28 || v==27), hash, v, r, s, retdest
ISZERO
// stack: (r < N & r!=0 & s < N & s!=0), (v==28 || v==27), hash, v, r, s, retdest
AND
// stack: r < N & r!=0 & s < N & s!=0 & (v==28 || v==27), hash, v, r, s, retdest
%endmacro
%macro secp_is_out_of_bounds
// stack: x
DUP1
// stack: x, x
ISZERO
// stack: x==0, x
SWAP1
// stack: x, x==0
%secp_scalar
// stack: N, x, x==0
SWAP1
// stack: x, N, x==0
LT
// stack: x < N, x==0
ISZERO
// stack: x >= N, x==0
OR
// stack: x >= N || x==0
%endmacro
%macro secp_scalar
PUSH 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
%endmacro
// Return u256::MAX which is used to indicate the input was invalid.
%macro ecrecover_invalid_input
// stack: retdest
PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
// stack: u256::MAX, retdest
SWAP1
// stack: retdest, u256::MAX
JUMP
%endmacro