Skip to content

Commit

Permalink
feat(ethereum): ✨ recover address from message and signature
Browse files Browse the repository at this point in the history
  • Loading branch information
matteo-cristino authored and jaromil committed Oct 3, 2023
1 parent 9650cff commit ecfd2f9
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 11 deletions.
22 changes: 19 additions & 3 deletions src/lua/crypto_ethereum.lua
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,6 @@ function ETH.verifySignatureTransaction(pk, txSigned)
return ECDH.verify_hashed(pk, txHash, sig, #txHash)
end

local encode

-- This function defaults to sign_ecdh_deterministic if no sign_function is passed.
-- One may use the random ECDSA by specifying the "sign_function" parameter as "sign_ecdh".
function ETH.encodeSignedData(sk, message, sign_function)
Expand Down Expand Up @@ -380,6 +378,24 @@ function ETH.address_from_public_key(pk)
return H:process(pk:sub(2, #pk)):sub(13, 32)
end

function ETH.address_from_signature(signature, y_parity, hash)
local pk, valid

local x = INT.new(signature.r)
local p = ECDH.prime()
local n = ECDH.order()
local h = ECDH.cofactor() --h=1
repeat
pk, valid = ECDH.recovery(x:octet(), y_parity, hash, signature)
if h > 0 then -- do not add n last iteration
x = (x + n) % p
end
h = h-1
until (valid) or (h < 0)

return valid and ETH.address_from_public_key(pk)
end

local function encode_uint(val)
return BIG.new(val):fixed(32)
end
Expand All @@ -403,7 +419,7 @@ end

local encode_tuple

function encode(t, arg)
local function encode(t, arg)
local res
if type(t) == "string" then
if string.match(t, 'uint%d+$') or t == 'address' then
Expand Down
29 changes: 21 additions & 8 deletions src/lua/zencode_ethereum.lua
Original file line number Diff line number Diff line change
Expand Up @@ -437,22 +437,23 @@ When("create the ethereum signature of ''", function(object)
new_codec('ethereum signature')
end)

IfWhen("verify the '' has a ethereum signature in '' by ''", function(doc, sig, by)
local msg = have(doc)
local ethersMessage = O.from_string("\x19Ethereum Signed Message:\n") .. O.new(#msg) .. msg
local hmsg = keccak256(ethersMessage)
local function _prepare_msg_f(src)
local msg = have(src)
local ethers_message = O.from_string("\x19Ethereum Signed Message:\n") .. O.new(#msg) .. msg
local hashed_msg = keccak256(ethers_message)
return hashed_msg
end

IfWhen("verify the '' has a ethereum signature in '' by ''", function(doc, sig, by)
local hmsg = _prepare_msg_f(doc)
local signature = have(sig)
local address = have(by)
zencode_assert(ETH.verify_signature_from_address(signature, address, fif(signature.v:parity(), 0, 1), hmsg),
'The ethereum signature by '..by..' is not authentic')
end)

local function _verify_address_signature_array(add_sig, doc, fun)
local msg = have(doc)
local ethersMessage = O.from_string("\x19Ethereum Signed Message:\n") .. O.new(#msg) .. msg
local hmsg = keccak256(ethersMessage)

local hmsg = _prepare_msg_f(doc)
local address_signature, address_signature_codec = have(add_sig)
zencode_assert(address_signature_codec.schema, "The ethereum address signature pair array is not a schema")
zencode_assert(address_signature_codec.zentype == "a", "The ethereum address signature pair array is not an array")
Expand Down Expand Up @@ -504,3 +505,15 @@ When("use the ethereum transaction to run '' using ''", function(m, p)
zencode_assert(not tx.data or #tx.data == 0, "Cannot overwrite transaction data")
tx.data = transaction_data
end)

When("create the ethereum address from the ethereum signature '' of ''", function(sign, doc)
empty'ethereum address'
local hashed_msg = _prepare_msg_f(doc)
local signature = have(sign)
local res = ETH.address_from_signature(signature, fif(signature.v:parity(), 0, 1), hashed_msg)
if not res then
error("No valid address found related to signature :"..sign)
end
ACK.ethereum_address = res
new_codec('ethereum address', { zentype = 'e' , encoding = 'complex'})
end)
18 changes: 18 additions & 0 deletions test/zencode/ethereum.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1031,3 +1031,21 @@ EOF
save_output verification_result.out
assert_output '{"result_array":[{"address":"0x2B8070975AF995Ef7eb949AE28ee7706B9039504","status":"verified"},{"address":"0x3028806AC293B5aC9b863B685c73813626311DaD","status":"verified"},{"address":"0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a","status":"verified"},{"address":"0xe1C2F1ACb2758c4D88EDb84e0306A0a96682E62a","status":"not verified"}]}'
}

@test "create the ethereum address from the ethereum signature '' of ''" {
cat <<EOF | save_asset add_from_sig.data
{
"ethereum_signature": "0xed8f36c71989f8660e8f5d4adbfd8f1c0288cca90d3a5330b7bf735d71ab52fe7ba0a7827dc4ba707431f1c10babd389f658f8e208b89390a9be3c097579a2ff1b",
"message": "I love the Beatles, all but 3"
}
EOF
cat <<EOF | zexe add_from_sig.zen add_from_sig.data
Scenario ethereum
Given I have a 'ethereum_signature'
Given I have a 'string' named 'message'
When I create the ethereum address from the ethereum signature 'ethereum_signature' of 'message'
Then print the 'ethereum address'
EOF
save_output 'add_from_sig.json'
assert_output '{"ethereum_address":"0x2B8070975AF995Ef7eb949AE28ee7706B9039504"}'
}

0 comments on commit ecfd2f9

Please sign in to comment.