@@ -150,4 +150,137 @@ library BLS {
150
150
require (success, "MAP_FP2_TO_G2 failed " );
151
151
return abi.decode (output, (G2Point));
152
152
}
153
+
154
+ /// @notice Computes a point in G2 from a message
155
+ /// @dev Uses the eip-2537 precompiles
156
+ /// @param message Arbitrarylength byte string to be hashed
157
+ /// @return A point in G2
158
+ function hashToCurveG2 (bytes memory message ) internal view returns (G2Point memory ) {
159
+ // 1. u = hash_to_field(msg, 2)
160
+ Fp2[2 ] memory u = hashToFieldFp2 (message, bytes ("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_ " ));
161
+ // 2. Q0 = map_to_curve(u[0])
162
+ G2Point memory q0 = MapFp2ToG2 (u[0 ]);
163
+ // 3. Q1 = map_to_curve(u[1])
164
+ G2Point memory q1 = MapFp2ToG2 (u[1 ]);
165
+ // 4. R = Q0 + Q1
166
+ return G2Add (q0, q1);
167
+ }
168
+
169
+ /// @notice Computes a field point from a message
170
+ /// @dev Follows https://datatracker.ietf.org/doc/html/rfc9380#section-5.2
171
+ /// @param message Arbitrarylength byte string to be hashed
172
+ /// @param dst The domain separation tag
173
+ /// @return Two field points
174
+ function hashToFieldFp2 (bytes memory message , bytes memory dst ) private view returns (Fp2[2 ] memory ) {
175
+ // 1. len_in_bytes = count * m * L
176
+ // so always 2 * 2 * 64 = 256
177
+ uint16 lenInBytes = 256 ;
178
+ // 2. uniform_bytes = expand_message(msg, DST, len_in_bytes)
179
+ bytes32 [] memory pseudoRandomBytes = expandMsgXmd (message, dst, lenInBytes);
180
+ Fp2[2 ] memory u;
181
+ // No loop here saves 800 gas hardcoding offset an additional 300
182
+ // 3. for i in (0, ..., count - 1):
183
+ // 4. for j in (0, ..., m - 1):
184
+ // 5. elm_offset = L * (j + i * m)
185
+ // 6. tv = substr(uniform_bytes, elm_offset, HTF_L)
186
+ // uint8 HTF_L = 64;
187
+ // bytes memory tv = new bytes(64);
188
+ // 7. e_j = OS2IP(tv) mod p
189
+ // 8. u_i = (e_0, ..., e_(m - 1))
190
+ // tv = bytes.concat(pseudo_random_bytes[0], pseudo_random_bytes[1]);
191
+ u[0 ].c0 = _modfield (pseudoRandomBytes[0 ], pseudoRandomBytes[1 ]);
192
+ u[0 ].c1 = _modfield (pseudoRandomBytes[2 ], pseudoRandomBytes[3 ]);
193
+ u[1 ].c0 = _modfield (pseudoRandomBytes[4 ], pseudoRandomBytes[5 ]);
194
+ u[1 ].c1 = _modfield (pseudoRandomBytes[6 ], pseudoRandomBytes[7 ]);
195
+ // 9. return (u_0, ..., u_(count - 1))
196
+ return u;
197
+ }
198
+
199
+ /// @notice Computes a field point from a message
200
+ /// @dev Follows https://datatracker.ietf.org/doc/html/rfc9380#section-5.3
201
+ /// @dev bytes32[] because len_in_bytes is always a multiple of 32 in our case even 128
202
+ /// @param message Arbitrarylength byte string to be hashed
203
+ /// @param dst The domain separation tag of at most 255 bytes
204
+ /// @param lenInBytes The length of the requested output in bytes
205
+ /// @return A field point
206
+ function expandMsgXmd (bytes memory message , bytes memory dst , uint16 lenInBytes )
207
+ private
208
+ pure
209
+ returns (bytes32 [] memory )
210
+ {
211
+ // 1. ell = ceil(len_in_bytes / b_in_bytes)
212
+ // b_in_bytes seems to be 32 for sha256
213
+ // ceil the division
214
+ uint256 ell = (lenInBytes - 1 ) / 32 + 1 ;
215
+
216
+ // 2. ABORT if ell > 255 or len_in_bytes > 65535 or len(DST) > 255
217
+ require (ell <= 255 , "len_in_bytes too large for sha256 " );
218
+ // Not really needed because of parameter type
219
+ // require(lenInBytes <= 65535, "len_in_bytes too large");
220
+ // no length normalizing via hashing
221
+ require (dst.length <= 255 , "dst too long " );
222
+
223
+ bytes memory dstPrime = bytes .concat (dst, bytes1 (uint8 (dst.length )));
224
+
225
+ // 4. Z_pad = I2OSP(0, s_in_bytes)
226
+ // this should be sha256 blocksize so 64 bytes
227
+ bytes memory zPad = new bytes (64 );
228
+
229
+ // 5. l_i_b_str = I2OSP(len_in_bytes, 2)
230
+ // length in byte string?
231
+ bytes2 libStr = bytes2 (lenInBytes);
232
+
233
+ // 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime
234
+ bytes memory msgPrime = bytes .concat (zPad, message, libStr, hex "00 " , dstPrime);
235
+
236
+ // 7. b_0 = H(msg_prime)
237
+ bytes32 b_0 = sha256 (msgPrime);
238
+
239
+ bytes32 [] memory b = new bytes32 [](ell);
240
+
241
+ // 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
242
+ b[0 ] = sha256 (bytes .concat (b_0, hex "01 " , dstPrime));
243
+
244
+ // 9. for i in (2, ..., ell):
245
+ for (uint8 i = 2 ; i <= ell; i++ ) {
246
+ // 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
247
+ bytes memory tmp = abi.encodePacked (b_0 ^ b[i - 2 ], i, dstPrime);
248
+ b[i - 1 ] = sha256 (tmp);
249
+ }
250
+ // 11. uniform_bytes = b_1 || ... || b_ell
251
+ // 12. return substr(uniform_bytes, 0, len_in_bytes)
252
+ // Here we don't need the uniform_bytes because b is already properly formed
253
+ return b;
254
+ }
255
+
256
+ // passing two bytes32 instead of bytes memory saves approx 700 gas per call
257
+ // Computes the mod against the bls12-381 field modulus
258
+ function _modfield (bytes32 _b1 , bytes32 _b2 ) private view returns (Fp memory r ) {
259
+ (bool success , bytes memory output ) = address (0x5 ).staticcall (
260
+ abi.encode (
261
+ // arg[0] = base.length
262
+ 0x40 ,
263
+ // arg[1] = exp.length
264
+ 0x20 ,
265
+ // arg[2] = mod.length
266
+ 0x40 ,
267
+ // arg[3] = base.bits @ + 0x60
268
+ // places the first 32 bytes of _b1 and the last 32 bytes of _b2
269
+ _b1,
270
+ _b2,
271
+ // arg[4] = exp
272
+ // exponent always 1
273
+ 1 ,
274
+ // arg[5] = mod
275
+ // this field_modulus as hex 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787
276
+ // we add the 0 prefix so that the result will be exactly 64 bytes
277
+ // saves 300 gas per call instead of sending it along every time
278
+ // places the first 32 bytes and the last 32 bytes of the field modulus
279
+ 0x000000000000000000000000000000001a0111ea397fe69a4b1ba7b6434bacd7 , // arg[5] = mod
280
+ 0x64774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab //
281
+ )
282
+ );
283
+ require (success, "MODEXP failed " );
284
+ return abi.decode (output, (Fp));
285
+ }
153
286
}
0 commit comments