diff --git a/evm/src/cpu/kernel/asm/basic_macros.asm b/evm/src/cpu/kernel/asm/basic_macros.asm index 376b661d..b70ce41f 100644 --- a/evm/src/cpu/kernel/asm/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/basic_macros.asm @@ -23,6 +23,11 @@ %pop2 %endmacro +%macro pop5 + %pop2 + %pop3 +%endmacro + // If pred is zero, yields z; otherwise, yields nz %macro select // stack: pred, nz, z diff --git a/evm/src/cpu/kernel/asm/ecrecover.asm b/evm/src/cpu/kernel/asm/ecrecover.asm index 519dc75e..09dc50e8 100644 --- a/evm/src/cpu/kernel/asm/ecrecover.asm +++ b/evm/src/cpu/kernel/asm/ecrecover.asm @@ -3,9 +3,21 @@ global ecrecover: // stack: hash, v, r, s, retdest %ecrecover_input_check // stack: isValid(v,r,s), hash, v, r, s, retdest + SWAP2 + // stack: v, hash, isValid(v,r,s), r, s, retdest + DUP4 + // stack: r, v, hash, isValid(v,r,s), r, s, retdest + %secp_lift_x + // stack: sqrtOk, x, y, hash, isValid(v,r,s), r, s, retdest + SWAP1 + // stack: x, sqrtOk, y, hash, isValid(v,r,s), r, s, retdest + SWAP4 + // stack: isValid(v,r,s), sqrtOk, y, hash, x, r, s, retdest + AND + // stack: isValid(v,r,s) & sqrtOk, y, hash, x, r, s, retdest %jumpi(ecrecover_valid_input) - // stack: hash, v, r, s, retdest - %pop4 + // stack: y, hash, x, r, s, retdest + %pop5 // stack: retdest %ecrecover_invalid_input @@ -17,12 +29,10 @@ global ecrecover: // return u1*P + u2*GENERATOR; ecrecover_valid_input: JUMPDEST - // stack: hash, v, r, s, retdest + // stack: y, hash, x, r, s, retdest SWAP1 - // stack: v, hash, r, s, retdest - DUP3 - // stack: r, v, hash, r, s, retdest - %secp_lift_x + // stack: hash, y, x, r, s, retdest + SWAP2 // stack: x, y, hash, r, s, retdest SWAP3 // stack: r, y, hash, x, s, retdest diff --git a/evm/src/cpu/kernel/asm/secp256k1/lift_x.asm b/evm/src/cpu/kernel/asm/secp256k1/lift_x.asm index 2578f6aa..57469239 100644 --- a/evm/src/cpu/kernel/asm/secp256k1/lift_x.asm +++ b/evm/src/cpu/kernel/asm/secp256k1/lift_x.asm @@ -8,36 +8,48 @@ // stack: 7, x^3, x, v %addmodn_secp // stack: x^3+7, x, v - %sqrt_secp - // stack: y, x, v DUP1 - // stack: y, y, x, v - PUSH 1 - // stack: 1, y, y, x, v - AND - // stack: 1 & y, y, x, v - PUSH 27 - // stack: 27, 1 & y, y, x, v - DUP5 - // stack: v, 27, 1 & y, y, x, v - SUB - // stack: v - 27, 1 & y, y, x, v - EQ - // stack: correctParity, y, x, v - DUP2 - // stack: y, correctParity, y, x, v - %secp_base - // stack: N, y, correctParity, y, x, v - SUB - // stack: N - y, correctParity, y, x, v + // stack: x^3+7, x^3+7, x, v + %sqrt_secp + // stack: y, x^3+7, x, v SWAP1 - // stack: correctParity, N - y, y, x, v - %select_bool - // stack: goody, x, v + // stack: x^3+7, y, x, v + DUP2 + // stack: y, x^3+7, y, x, v + %squaremodn_secp + // stack: y^2, x^3+7, y, x, v + EQ + // stack: sqrtOk, y, x, v + SWAP3 + // stack: v, y, x, sqrtOk + DUP2 + // stack: y, v, y, x, sqrtOk + PUSH 1 + // stack: 1, y, v, y, x, sqrtOk + AND + // stack: 1 & y, v, y, x, sqrtOk + PUSH 27 + // stack: 27, 1 & y, v, y, x, sqrtOk + SWAP1 + // stack: 1 & y, 27, v, y, x, sqrtOk SWAP2 - // stack: v, x, goody - POP - // stack: x, goody + // stack: v, 27, 1 & y, y, x, sqrtOk + SUB + // stack: v - 27, 1 & y, y, x, sqrtOk + EQ + // stack: correctParity, y, x, sqrtOk + DUP2 + // stack: y, correctParity, y, x, sqrtOk + %secp_base + // stack: N, y, correctParity, y, x, sqrtOk + SUB + // stack: N - y, correctParity, y, x, sqrtOk + SWAP1 + // stack: correctParity, N - y, y, x, sqrtOk + %select_bool + // stack: goody, x, sqrtOk + SWAP2 + // stack: sqrtOk, x, goody %endmacro %macro cubemodn_secp diff --git a/evm/src/cpu/kernel/tests/ecrecover.rs b/evm/src/cpu/kernel/tests/ecrecover.rs index 4a3a257b..47115317 100644 --- a/evm/src/cpu/kernel/tests/ecrecover.rs +++ b/evm/src/cpu/kernel/tests/ecrecover.rs @@ -1,6 +1,5 @@ -use anyhow::{ensure, Result}; +use anyhow::Result; use ethereum_types::U256; -use hex_literal::hex; use keccak_hash::keccak; use crate::cpu::kernel::aggregator::combined_kernel; @@ -28,7 +27,16 @@ fn test_valid_ecrecover( let initial_stack = u256ify([s, r, v, hash])?; let stack = run(&kernel.code, ecrecover, initial_stack); let got = pubkey_to_addr(stack[1], stack[0]); - assert_eq!(got, hex::decode(expected).unwrap()); + assert_eq!(got, hex::decode(&expected[2..]).unwrap()); + + Ok(()) +} + +fn test_invalid_ecrecover(hash: &str, v: &str, r: &str, s: &str, kernel: &Kernel) -> Result<()> { + let ecrecover = kernel.global_labels["ecrecover"]; + let initial_stack = u256ify(["0xdeadbeef", s, r, v, hash])?; + let stack = run(&kernel.code, ecrecover, initial_stack); + assert_eq!(stack, vec![U256::MAX]); Ok(()) } @@ -42,7 +50,7 @@ fn test_ecrecover() -> Result<()> { "0x1b", "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", "0x58351f48ce34bf134ee611fb5bf255a5733f0029561d345a7d46bfa344b60ac0", - "67f3c0Da351384838d7F7641AB0fCAcF853E1844", + "0x67f3c0Da351384838d7F7641AB0fCAcF853E1844", &kernel, )?; test_valid_ecrecover( @@ -50,17 +58,46 @@ fn test_ecrecover() -> Result<()> { "0x1c", "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", "0x58351f48ce34bf134ee611fb5bf255a5733f0029561d345a7d46bfa344b60ac0", - "aA58436DeABb64982a386B2De1A8015AA28fCCc0", + "0xaA58436DeABb64982a386B2De1A8015AA28fCCc0", + &kernel, + )?; + test_valid_ecrecover( + "0x0", + "0x1c", + "0x1", + "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "0x3344c6f6eeCA588be132142DB0a32C71ABFAAe7B", + &kernel, + )?; + + test_invalid_ecrecover( + "0x0", + "0x42", // v not in {27,28} + "0x1", + "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + &kernel, + )?; + test_invalid_ecrecover( + "0x0", + "0x42", + "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", + "0x0", // s=0 + &kernel, + )?; + test_invalid_ecrecover( + "0x0", + "0x42", + "0x0", // r=0 + "0xd667c5a20fa899b253924099e10ae92998626718585b8171eb98de468bbebc", + &kernel, + )?; + test_invalid_ecrecover( + "0x0", + "0x1c", + "0x3a18b21408d275dde53c0ea86f9c1982eca60193db0ce15008fa408d43024847", // r^3 + 7 isn't a square + "0x5db9745f44089305b2f2c980276e7025a594828d878e6e36dd2abd34ca6b9e3d", &kernel, )?; - // test_valid_ecrecover( - // "0x0", - // "0x1c", - // "0x3a18b21408d275dde53c0ea86f9c1982eca60193db0ce15008fa408d43024847", - // "0x5db9745f44089305b2f2c980276e7025a594828d878e6e36dd2abd34ca6b9e3d", - // "aA58436DeABb64982a386B2De1A8015AA28fCCc0", - // &kernel, - // )?; Ok(()) }