-
Notifications
You must be signed in to change notification settings - Fork 20
/
EthereumUtils.sol
283 lines (235 loc) · 8.17 KB
/
EthereumUtils.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import {Sapphire} from "./Sapphire.sol";
struct SignatureRSV {
bytes32 r;
bytes32 s;
uint256 v;
}
library EthereumUtils {
uint256 internal constant K256_P =
0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f;
// (p+1)//4
uint256 internal constant K256_P_PLUS_1_OVER_4 =
0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c;
address internal constant PRECOMPILE_BIGMODEXP = address(0x5);
error expmod_Error();
function expmod(
uint256 base,
uint256 exponent,
uint256 modulus
) internal view returns (uint256 out) {
(bool success, bytes memory result) = PRECOMPILE_BIGMODEXP.staticcall(
abi.encodePacked(
uint256(0x20), // length of base
uint256(0x20), // length of exponent
uint256(0x20), // length of modulus
base,
exponent,
modulus
)
);
if (!success) revert expmod_Error();
out = uint256(bytes32(result));
}
error k256DeriveY_Invalid_Prefix_Error();
/**
* @notice Recover Y coordinate from X coordinate and sign bit.
* @param prefix 0x02 or 0x03 indicates sign bit of compressed point.
* @param x X coordinate.
*/
function k256DeriveY(uint8 prefix, uint256 x)
internal
view
returns (uint256 y)
{
if (prefix != 0x02 && prefix != 0x03)
revert k256DeriveY_Invalid_Prefix_Error();
// x^3 + ax + b, where a=0, b=7
y = addmod(mulmod(x, mulmod(x, x, K256_P), K256_P), 7, K256_P);
// find square root of quadratic residue
y = expmod(y, K256_P_PLUS_1_OVER_4, K256_P);
// negate y if indicated by sign bit
if ((y + prefix) % 2 != 0) {
y = K256_P - y;
}
}
error k256Decompress_Invalid_Length_Error();
/**
* @notice Decompress SEC P256 k1 point.
* @param pk 33 byte compressed public key.
* @return x X coordinate.
* @return y Y coordinate.
*/
function k256Decompress(bytes memory pk)
internal
view
returns (uint256 x, uint256 y)
{
if (pk.length != 33) revert k256Decompress_Invalid_Length_Error();
assembly {
// skip 32 byte length prefix, plus one byte sign prefix
x := mload(add(pk, 33))
}
y = k256DeriveY(uint8(pk[0]), x);
}
function k256PubkeyToEthereumAddress(bytes memory pubkey)
internal
view
returns (address)
{
(uint256 x, uint256 y) = k256Decompress(pubkey);
return toEthereumAddress(x, y);
}
/**
* @notice Convert SEC P256 k1 curve point to Ethereum address.
* @param x X coordinate.
* @param y Y coordinate.
* @custom:see https://gavwood.com/paper.pdf (pp. 212)
*/
function toEthereumAddress(uint256 x, uint256 y)
internal
pure
returns (address)
{
bytes32 digest = keccak256(abi.encodePacked(x, y));
return address(uint160((uint256(digest) << 96) >> 96));
}
error DER_Split_Error();
/**
* @notice Extracts the `r` and `s` parameters from a DER encoded ECDSA
* signature.
*
* The signature is an ASN1 encoded SEQUENCE of the variable length `r` and
* `s` INTEGERs.
*
* ```
* | 0x30 | len(z) | 0x02 | len(r) | r | 0x02 | len(s) | s | = hex value
* | 1 | 1 | 1 | 1 | 1-33 | 1 | 1 | 1-33 | = byte length
* ```
*
* If the highest bit of either `r` or `s` is set, it will be prefix padded
* with a zero byte. There is exponentially decreasing probability that
* either `r` or `s` will be below 32 bytes. There is a very high
* probability that either `r` or `s` will be 33 bytes. This function only
* works if either `r` or `s` are 256bits or lower.
*
* @param der DER encoded ECDSA signature
* @return rsv ECDSA R point X coordinate, and S scalar
* @custom:see https://bitcoin.stackexchange.com/questions/58853/how-do-you-figure-out-the-r-and-s-out-of-a-signature-using-python
*/
function splitDERSignature(bytes memory der)
internal
pure
returns (SignatureRSV memory rsv)
{
if (der.length < 8) revert DER_Split_Error();
if (der[0] != 0x30) revert DER_Split_Error();
if (der[2] != 0x02) revert DER_Split_Error();
uint256 zLen = uint8(der[1]);
uint256 rLen = uint8(der[3]);
if (rLen > 33) revert DER_Split_Error();
uint256 sOffset = 4 + rLen;
uint256 sLen = uint8(der[sOffset + 1]);
if (sLen > 33) revert DER_Split_Error();
if (der[sOffset] != 0x02) revert DER_Split_Error();
if (rLen + sLen + 4 != zLen) revert DER_Split_Error();
if (der.length != zLen + 2) revert DER_Split_Error();
sOffset += 2;
uint256 rOffset = 4;
if (rLen == 33) {
if (der[4] != 0x00) revert DER_Split_Error();
rOffset += 1;
rLen -= 1;
}
if (sLen == 33) {
if (der[sOffset] != 0x00) revert DER_Split_Error();
sOffset += 1;
sLen -= 1;
}
bytes32 r;
bytes32 s;
assembly {
r := mload(add(der, add(32, rOffset)))
s := mload(add(der, add(32, sOffset)))
}
// When length of either `r` or `s` is below 32 bytes
// the 32 byte `mload` will suffix it with unknown stuff
// shift right to remove the unknown stuff, prefixing with zeros instead
if (rLen < 32) {
r >>= 8 * (32 - rLen);
}
if (sLen < 32) {
s >>= 8 * (32 - sLen);
}
rsv.r = r;
rsv.s = s;
}
error recoverV_Error();
function recoverV(
address pubkeyAddr,
bytes32 digest,
SignatureRSV memory rsv
) internal pure {
rsv.v = 27;
if (ecrecover(digest, uint8(rsv.v), rsv.r, rsv.s) != pubkeyAddr) {
rsv.v = 28;
if (ecrecover(digest, uint8(rsv.v), rsv.r, rsv.s) != pubkeyAddr) {
revert recoverV_Error();
}
}
}
/**
* @notice Convert a Secp256k1PrehashedKeccak256 signature to one accepted
* by ecrecover.
* @param pubkey 33 byte compressed public key.
* @param digest 32 byte pre-hashed message digest.
* @param signature ASN.1 DER encoded signature, as returned from
* [`Sapphire.sign`](../Sapphire.sol/library.Sapphire.md#sign).
* @return pubkeyAddr 20 byte Ethereum address.
* @return rsv Ethereum EcDSA RSV signature values.
* @custom:see https://gavwood.com/paper.pdf (pp. 206)
*/
function toEthereumSignature(
bytes memory pubkey,
bytes32 digest,
bytes memory signature
) internal view returns (address pubkeyAddr, SignatureRSV memory rsv) {
pubkeyAddr = k256PubkeyToEthereumAddress(pubkey);
rsv = splitDERSignature(signature);
recoverV(pubkeyAddr, digest, rsv);
}
function sign(
address pubkeyAddr,
bytes32 secretKey,
bytes32 digest
) internal view returns (SignatureRSV memory rsv) {
bytes memory signature = Sapphire.sign(
Sapphire.SigningAlg.Secp256k1PrehashedKeccak256,
abi.encodePacked(secretKey),
abi.encodePacked(digest),
""
);
rsv = splitDERSignature(signature);
recoverV(pubkeyAddr, digest, rsv);
}
/**
* @notice Generate an Ethereum compatible SEC P256 k1 keypair and
* corresponding public address.
* @return pubkeyAddr Ethereum address.
* @return secretKey Secret key used for signing.
*/
function generateKeypair()
internal
view
returns (address pubkeyAddr, bytes32 secretKey)
{
bytes memory randSeed = Sapphire.randomBytes(32, "");
secretKey = bytes32(randSeed);
(bytes memory pk, ) = Sapphire.generateSigningKeyPair(
Sapphire.SigningAlg.Secp256k1PrehashedKeccak256,
randSeed
);
pubkeyAddr = k256PubkeyToEthereumAddress(pk);
}
}