# BLS Signatures in Solidity *Thank Onur, Jannik, and Mikerah for the review and the advice* published version https://ethresear.ch/t/bls-signatures-in-solidity/7919 [TOC] ## Who should read this? This article targets developers who want to perform BLS signature verification in Eth1 contracts. For readers interested in the BLS signature in Eth2, I highly recommend "BLS12-381 For The Rest Of Us"[^ben-bls] by Ben Edgington, which provides long but not too long answers to common questions. The article also assumes the readers heard of the terms like $G_1$, $G_2$, and pairings. "BLS12-381 For The Rest Of Us" is also a good source for understanding those. ## Introduction When we talk about the BLS signature, we are talking about the signature aggregation technique. The BLS here is the name of three authors Boneh, Lynn, and Shacham. [^bls] ## Which curve should I use? BLS signature aggregation works on pairing friendly curves. We have to choose which of the following curves to use. - alt_bn128: Barreto-Naehrig curve. [^bn] - BLS12-381: This BLS is Barreto, Lynn, and Scott. [^bls-curve] At the time of writing this document (2020-Aug), we can only use alt_bn128 curve in contracts. The alt_bn128 curve is specified in EIP-196[^eip196] (curve point addition and multiplication) and EIP-197[^eip197] (curve pairing) and introduced in Byzantium hardfork October 16, 2017[^byzantium-fork]. The cost is further reduced in EIP-1108[^eip1108], and was live on mainnet in Istanbul-fork[^istanbul-fork]. The BLS12-381 curve is specified in EIP-2537[^eip2537] and expected to go live in the Berlin hardfork[^eip2070]. The alt_bn128(BN256) is shown in 2016 to have less than 100-bits of security[^ietf]. To target 128-bits security use BLS12-381 whenever possible. We use alt_bn128 in the examples hereafter. ### alt_bn128, bn256, bn254, why so many names and so confusing? - Originally named alt_bn128 for targeting theoretic security at 128 bits. - It works on a 254 bit prime field, so people also call it bn254. - Some also call it bn256 for unknown reason. ## Notes on size of the elements | | alt_bn128 | BLS12-381 | Note | | ----------------------- | ------------------:| --------------------:| ------------------------------------ | | $F_q$ | 254 bits (32 bytes) | 381 bits (48 bytes) | has leading zero bits | | $F_{q^2}$ | 64 bytes | 96 bytes | | | $\Bbb G_1$ Uncompressed | 64 bytes | 96 bytes | has x and y coordinates as $F_q$ | | $\Bbb G_2$ Uncompressed | 128 bytes | 192 bytes | has x and y coordinates as $F_{q^2}$ | A curve point's y coordinate can be compressed to 1 bit and recovered by x. | | alt_bn128 | BLS12-381 | | --------------------- | ---------:| ---------:| | $\Bbb G_1$ compressed | 32 bytes | 48 bytes | | $\Bbb G_2$ compressed | 64 bytes | 96 bytes | ## Big Signatures or Big public keys? This is a decision you'll face in the project. Choose either: - Use $\Bbb G_2$ for signatures and messages, and $\Bbb G_1$ for public keys, or - Use $\Bbb G_2$ for public keys, and $\Bbb G_1$ for signatures and messages. $\Bbb G_2$ is 128 bytes (`uint256[4]` in Solidity) and $\Bbb G_1$ is 64 bytes (`uint256[2]` in Solidity). Also field/curve operations on $\Bbb G_2$ are more expensive. The option saves more gas in your project is better. We'll use the big public keys in the examples hereafter. ## BLS cheat sheet We have a curve. The points on the curve can form a subgroup. We define $\Bbb G_2$ to be the subgroup of points where the curve's x and y coordinates defined on $F_{q^2}$. And subgroup $\Bbb G_1$ with coordinates on $F_q$. ### Tips - **Field operations**: We are talking about arithmetics of field elements $F_q$ or $F_{q^2}$. $F_q$ can be added, subtracted, multiplied, and divided by other $F_q$. Same applies for $F_{q^2}$. - **Curve operations**: We are talking about operations of curve elements $\Bbb G_1$ or $\Bbb G_2$. An element in $\Bbb G_1$ can be added to another element in a geometrical way. An element can be added to itself multiple times, so we can define **multiplications** of an element in $\Bbb G_1$ with an integer in $Z_q$[^ecmul]. Same applies for $\Bbb G_2$. ### Pairing function $$ e:\Bbb G_1 \times \Bbb G_2 \to \Bbb G_T $$ $G_1$, $G_2$ are the generators of $\Bbb G_1$ and $\Bbb G_2$ respectively Pairing function has bilinear property, which means for $a, b \in Z_q$, $P \in \Bbb G_1$, and $Q \in \Bbb G_2$, we have: $$ e( a \times P, b \times Q) = e( ab \times P, Q) = e( P, ab \times Q) $$ ### Hash function It maps the message to an element of $\Bbb G_1$ $$ H_0: \mathcal{M} \to \Bbb G_1 $$ ### Key Generation Secret key: $$ \alpha \gets \Bbb Z_q $$ Public key: $$ h \gets \alpha \times G_2 \in \Bbb G_2 $$ ### Signing $$ \sigma \gets \alpha \times H_0(m) \in \Bbb G_1 $$ ### Verification $$ e(\sigma, G_2)\stackrel{?}{=} e(H_0(m), h) $$ ### Proof of why verification works $$ e(\sigma, G_2) \\ = e( \alpha \times H_0(m), G_2) \\ = e( H_0(m), \alpha \times G_2) \\ = e(H_0(m), h) $$ ## Contracts / Precompiles Developers usually work with a wrapped contract that has clear function names and function signatures. However, the core of the implementation could be confusing. Here we provide a simple walkthrough. ### Verify Single Below shows an example Solidity function that verifies a single signature. It is the small signature and big public key setup. A signature is a 64 bytes $\Bbb G_1$ group element, and its calldata is a length 2 array of uint256. On the other hand, a public key is a 128 bytes $\Bbb G_2$ group element, and its calldata is a length 4 array of uint256. EIP 197 defined a pairing precompile contract at address `0x8` and requires input to a multiple of 192. This assembly code calls the precompile contract at address `0x8` with inputs. ``` function verifySingle( uint256[2] memory signature, \\ small signature uint256[4] memory pubkey, \\ big public key: 96 bytes uint256[2] memory message ) internal view returns (bool) { uint256[12] memory input = [ signature[0], signature[1], nG2x1, nG2x0, nG2y1, nG2y0, message[0], message[1], pubkey[1], pubkey[0], pubkey[3], pubkey[2] ]; uint256[1] memory out; bool success; // solium-disable-next-line security/no-inline-assembly assembly { success := staticcall(sub(gas(), 2000), 8, input, 384, out, 0x20) switch success case 0 { invalid() } } require(success, ""); return out[0] != 0; } ``` We translate the above code into math formula. Where the `nG2` is the negative "curve" operation of the $G_2$ group generator. $$ e(\text{signature}, neg(G_2)) e(\text{message}, \text{pubkey}) \stackrel{?}{=} 1 $$ If the above formula is not straight forward, let's derive from the pairing check we usually see. The message here is the raw message hashed to $G_1$. $$ e(\text{message}, \text{pubkey}) \\ = e(\text{message}, \text{privkey} \times G_2 ) \\ = e(\text{privkey} \times \text{message}, G_2 ) \\ = e(\text{signature}, G_2) $$ $$ e(\text{message}, \text{pubkey}) \stackrel{?}{=} e(\text{signature}, G_2) $$ ### Hash to curve We can choose from Hash and pray approach or Fouque Tibouchi approach: - Hash and pray approach is easy to implement, but since it is non-constant time to run it has a security issue[^hash-and-pray-issue]. Each iteration is expensive in solidity and attacker can grind a message that's too expensive to check on chain. - Fouque Tibouchi is constant time, but more difficult to implement. #### Attempts to fix hash and pray Avg 30k gas for hash and pray https://github.com/thehubbleproject/RedditHubble/runs/1011657548#step:7:35 A sqrt iteration takes 14k gas due to the call to modexp precompile. Attempt to replace modexp with a series of modmul. optimized cost is 6.7k https://github.com/ChihChengLiang/modexp Kobi has a proposal that user provides outputs of modexp and onchain we use 1 modmul to verify. https://gist.github.com/kobigurk/b9142a4755691bb12df59fbe999c2a1f#file-bls_with_help-sol-L129-L154 ## Gas Consumption Post EIP-1108, k pairings cost `34 000 * k + 45 000` gas. So as the above example, to validate a single signature takes 2 pairings and thus 113000 gas. Validating n different messages takes n + 1 pairings, which costs `80 000 + 34000*n` gas. In comparison, ECDSA takes 3000 gas, see `ecrecover`[^ethgastable]. Here are cases to consider the aggregate signature: - When there's only one message, but many signatures to verify. The aggregate signature takes 2 pairings (113000 gas), and it wins ECDSA when you have 38 more signatures to verify. - When you need to store or log signatures on chain. Storing a word (32 bytes) costs 20000 or 5000 gas, and logging costs 8 gas per byte. The aggregate signature (48 bytes * 1 sig) wins ECDSA (65 bytes * n sigs) easily in this case. ## Packages to do BLS ### JavaScript/TypeScript https://github.com/kilic/evmbls ### Python https://github.com/ChihChengLiang/bls_solidity_python ## Cookbook ### Aggregations TODO - how to create private keys, public keys, and signatures (just listing the formulas should be enough, everyone can then use their favorite library to implement them). - test data for one cycle (a private key, a public key, a message, and a signature) - a note on encodings: The solidity code just uses the plain uints, but at least for BLS12-381 there are standardized encodings, aren't there? Not sure if it makes sense to go into details, but just mentioning that they exist with maybe a link could be helpful - public key aggregation (my problem basically): My understanding from our discussion yesterday is that it's not easily possible on-chain with bn128 and public keys from G2. I looked into the EIP fro BLS12-381 and they have a precompile for G2 additions, so with that it should be easy. [^ben-bls]:https://hackmd.io/@benjaminion/bls12-381 [^bls]:https://www.iacr.org/archive/asiacrypt2001/22480516.pdf [^bls-curve]:https://eprint.iacr.org/2002/088.pdf [^bn]:https://eprint.iacr.org/2005/133 [^eip196]:https://eips.ethereum.org/EIPS/eip-196 [^eip197]:https://eips.ethereum.org/EIPS/eip-197 [^eip2537]:https://eips.ethereum.org/EIPS/eip-2537 [^eip1108]:https://eips.ethereum.org/EIPS/eip-1108 [^eip2070]:https://eips.ethereum.org/EIPS/eip-2070 [^byzantium-fork]:https://blog.ethereum.org/2017/10/12/byzantium-hf-announcement/ [^istanbul-fork]:https://eips.ethereum.org/EIPS/eip-1679 [^ethgastable]:https://ethgastable.info/ [^ietf]:https://www.ietf.org/id/draft-irtf-cfrg-pairing-friendly-curves-07.html [^ecmul]:https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication [^hash-and-pray-issue]:https://github.com/thehubbleproject/RedditHubble/issues/171