UltrafastSecp256k1 3.50.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
eth_signing.hpp
Go to the documentation of this file.
1#ifndef SECP256K1_COINS_ETH_SIGNING_HPP
2#define SECP256K1_COINS_ETH_SIGNING_HPP
3#pragma once
4
5// ============================================================================
6// Ethereum Signing Primitives
7// ============================================================================
8// EIP-191: Personal message signing ("\x19Ethereum Signed Message:\n" prefix)
9// EIP-155: Chain-ID replay protection (v = recid + 35 + 2*chainId)
10// ecrecover: Recover Ethereum address from (v, r, s) + message hash
11//
12// These are the core operations wallets & dApps use:
13// - MetaMask personal_sign / eth_sign
14// - Raw transaction signing (v,r,s with chain ID)
15// - Address recovery from signatures (ecrecover precompile 0x01)
16//
17// All signing uses CT layer (constant-time). Recovery uses fast layer.
18// ============================================================================
19
20#include <array>
21#include <cstdint>
22#include <cstddef>
23#include <utility>
24#include "secp256k1/scalar.hpp"
25#include "secp256k1/point.hpp"
27
28namespace secp256k1::coins {
29
30// -- EIP-191: Personal Message Hash -------------------------------------------
31
32// Compute EIP-191 personal message hash:
33// Keccak256("\x19Ethereum Signed Message:\n" + decimal_len(msg) + msg)
34// This is what MetaMask's personal_sign / eth_sign computes before signing.
35std::array<std::uint8_t, 32> eip191_hash(const std::uint8_t* msg, std::size_t msg_len);
36
37// -- EIP-155: Chain-ID Encoding -----------------------------------------------
38
39// Convert recovery ID (0-3) + chain ID to EIP-155 v value.
40// Legacy (pre-EIP155): v = 27 + recid
41// EIP-155: v = 35 + 2*chainId + recid
42inline std::uint64_t eip155_v(int recid, std::uint64_t chain_id) {
43 return 35 + 2 * chain_id + static_cast<std::uint64_t>(recid);
44}
45
46// Extract recovery ID from EIP-155 v value.
47// Legacy: recid = v - 27
48// EIP-155: recid = (v - 35) % 2 (works for v >= 35)
49inline int eip155_recid(std::uint64_t v) {
50 if (v < 27) {
51 return -1; // Invalid v value
52 }
53 if (v <= 28) {
54 return static_cast<int>(v - 27);
55 }
56 return static_cast<int>((v - 35) & 1);
57}
58
59// Extract chain ID from EIP-155 v value.
60// Returns 0 for legacy (v=27 or v=28).
61inline std::uint64_t eip155_chain_id(std::uint64_t v) {
62 if (v <= 28) return 0;
63 return (v - 35) / 2;
64}
65
66// -- Ethereum Sign (personal_sign) --------------------------------------------
67
69 std::array<std::uint8_t, 32> r;
70 std::array<std::uint8_t, 32> s;
71 std::uint64_t v; // EIP-155 v value (27/28 for legacy, 35+2*chainId+recid)
72};
73
74// Sign a raw message with EIP-191 prefix (personal_sign).
75// Hashes: Keccak256("\x19Ethereum Signed Message:\n" + len + msg)
76// then signs with ECDSA recovery. v = 27 + recid (legacy format).
77EthSignature eth_personal_sign(const std::uint8_t* msg, std::size_t msg_len,
78 const fast::Scalar& private_key);
79
80// Sign a pre-computed 32-byte hash with recovery.
81// v = 27 + recid (legacy) or 35 + 2*chainId + recid (EIP-155).
82EthSignature eth_sign_hash(const std::array<std::uint8_t, 32>& hash,
83 const fast::Scalar& private_key,
84 std::uint64_t chain_id = 0);
85
86// -- ecrecover: Recover Address from Signature --------------------------------
87
88// Recover Ethereum address (20 bytes) from signature + message hash.
89// This is the Ethereum ecrecover precompile (address 0x01).
90// Returns {address, ok}. ok=false if recovery fails.
91std::pair<std::array<std::uint8_t, 20>, bool>
92ecrecover(const std::array<std::uint8_t, 32>& msg_hash,
93 const std::array<std::uint8_t, 32>& r,
94 const std::array<std::uint8_t, 32>& s,
95 std::uint64_t v);
96
97// ecrecover from EthSignature struct
98std::pair<std::array<std::uint8_t, 20>, bool>
99ecrecover(const std::array<std::uint8_t, 32>& msg_hash,
100 const EthSignature& sig);
101
102// -- Verify: Check that signature was produced by address ---------------------
103
104// Verify that a personal_sign signature was produced by the given address.
105bool eth_personal_verify(const std::uint8_t* msg, std::size_t msg_len,
106 const EthSignature& sig,
107 const std::array<std::uint8_t, 20>& expected_addr);
108
109} // namespace secp256k1::coins
110
111#endif // SECP256K1_COINS_ETH_SIGNING_HPP
std::uint64_t eip155_v(int recid, std::uint64_t chain_id)
bool eth_personal_verify(const std::uint8_t *msg, std::size_t msg_len, const EthSignature &sig, const std::array< std::uint8_t, 20 > &expected_addr)
EthSignature eth_sign_hash(const std::array< std::uint8_t, 32 > &hash, const fast::Scalar &private_key, std::uint64_t chain_id=0)
std::pair< std::array< std::uint8_t, 20 >, bool > ecrecover(const std::array< std::uint8_t, 32 > &msg_hash, const std::array< std::uint8_t, 32 > &r, const std::array< std::uint8_t, 32 > &s, std::uint64_t v)
EthSignature eth_personal_sign(const std::uint8_t *msg, std::size_t msg_len, const fast::Scalar &private_key)
std::array< std::uint8_t, 32 > eip191_hash(const std::uint8_t *msg, std::size_t msg_len)
std::uint64_t eip155_chain_id(std::uint64_t v)
int eip155_recid(std::uint64_t v)
std::array< std::uint8_t, 32 > s
std::array< std::uint8_t, 32 > r