-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBlsSignatures.sol
More file actions
162 lines (136 loc) · 5.34 KB
/
BlsSignatures.sol
File metadata and controls
162 lines (136 loc) · 5.34 KB
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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev A BLS G1 Point in affine form.
*/
struct AffinePointG1 {
bytes x;
bytes y;
}
/**
* @dev A BLS G2 Point in affine form.
*/
struct AffinePointG2 {
bytes x1;
bytes x2;
bytes y1;
bytes y2;
}
/**
* @dev A BLS G1 Point in serialized affine form, meant as input for precompile operations.
*/
struct OpPointG1 {
bytes xy;
}
/**
* @dev A BLS G2 Point in serialized affine form, meant as input for precompile operations.
*/
struct OpPointG2 {
bytes xxyy;
}
/**
* @author xevisalle
* @title BlsSignatures
* @dev Implementation of BLS signatures, instantiated with the parameters of
* the BLS12-381 curve.
*
* Ref: https://www.iacr.org/archive/asiacrypt2001/22480516.pdf
*/
library BlsSignatures {
// Amount of bytes set to zero used for converting affine points to operation form
bytes constant ZERO = new bytes(16);
// Field mod
bytes constant FIELD_MOD =
hex"1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
// The generator of G1
bytes constant G1_GEN =
hex"0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1";
// The generator of G2
bytes constant G2_GEN =
hex"00000000000000000000000000000000024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000000000000000000000000000000013e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e000000000000000000000000000000000ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801000000000000000000000000000000000606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be";
/**
* @dev Converts a point from affine to operation form
*/
function toOp(AffinePointG1 memory P) internal pure returns (OpPointG1 memory) {
return OpPointG1({xy: bytes.concat(ZERO, P.x, ZERO, P.y)});
}
/**
* @dev Converts a point from affine to G2 operation form
*/
function toOpG2(AffinePointG2 memory P) internal pure returns (OpPointG2 memory) {
return OpPointG2({xxyy: bytes.concat(ZERO, P.x1, ZERO, P.x2, ZERO, P.y1, ZERO, P.y2)});
}
/**
* @dev Hashes a given message and outputs a Keccak256 digest, with 2 bits truncated.
*/
function truncatedHash(bytes memory message) internal pure returns (bytes memory) {
return abi.encode(uint256(keccak256(message)) & (uint256(1) << (254)) - 1);
}
/**
* @dev Hashes a given message to the curve's G1, using the truncatedHash() method.
*/
function hashToCurve(bytes memory message) internal view returns (OpPointG1 memory) {
bytes memory hash = truncatedHash(message);
return mulG1Gen(hash);
}
/**
* @dev Adds two G1 points.
*/
function addG1(OpPointG1 memory P, OpPointG1 memory Q) internal view returns (OpPointG1 memory) {
(bool success, bytes memory sum) = address(0x0b).staticcall(bytes.concat(P.xy, Q.xy));
require(success, "BLS12-381 G1 addition failed.");
return OpPointG1({xy: sum});
}
/**
* @dev Multiplies a scalar by a G1 point.
*/
function mulG1(bytes memory x, OpPointG1 memory P) internal view returns (OpPointG1 memory) {
(bool success, bytes memory mul) = address(0x0c).staticcall(bytes.concat(P.xy, x));
require(success, "BLS12-381 G1 scalar multiplication failed.");
return OpPointG1({xy: mul});
}
/**
* @dev Multiplies a scalar by the G1 generator.
*/
function mulG1Gen(bytes memory x) internal view returns (OpPointG1 memory) {
(bool success, bytes memory mul) = address(0x0c).staticcall(bytes.concat(G1_GEN, x));
require(success, "BLS12-381 G1 scalar multiplication failed.");
return OpPointG1({xy: mul});
}
/**
* @dev Negates a G1 point.
*/
function negG1(AffinePointG1 memory P) public pure returns (AffinePointG1 memory) {
bytes memory res = new bytes(P.y.length);
uint256 borrow = 0;
for (uint256 i = P.y.length; i > 0; i--) {
uint256 ai = uint8(FIELD_MOD[i - 1]);
uint256 bi = uint8(P.y[i - 1]);
uint256 f;
if (ai >= bi + borrow) {
f = ai - bi - borrow;
borrow = 0;
} else {
f = 256 + ai - bi - borrow;
borrow = 1;
}
res[i - 1] = bytes1(uint8(f));
}
return AffinePointG1({x: P.x, y: res});
}
/**
* @dev Verifies a signature given the message and the public key.
*/
function verifySignature(OpPointG2 memory pk, bytes memory message, AffinePointG1 memory sig)
internal
view
returns (bool)
{
OpPointG1 memory hash = BlsSignatures.hashToCurve(message);
OpPointG1 memory sigNeg = toOp(negG1(sig));
(bool success, bytes memory pairing) =
address(0x0f).staticcall(bytes.concat(sigNeg.xy, G2_GEN, hash.xy, pk.xxyy));
require(success, "BLS12-381 pairing failed.");
return pairing[31] == bytes1(0x01);
}
}