2018-12-09 14:21:34 +00:00
# BLS signature verification
2018-11-27 16:08:43 +00:00
**Warning: This document is pending academic review and should not yet be considered secure.**
2018-12-09 14:21:34 +00:00
## Table of contents
<!-- TOC -->
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
- [BLS signature verification ](#bls-signature-verification )
- [Table of contents ](#table-of-contents )
- [Point representations ](#point-representations )
- [G1 points ](#g1-points )
- [G2 points ](#g2-points )
- [Helpers ](#helpers )
- [`hash_to_G2` ](#hash_to_g2 )
2018-12-09 14:43:13 +00:00
- [`modular_squareroot` ](#modular_squareroot )
2018-12-09 14:21:34 +00:00
- [Signature verification ](#signature-verification )
- [`bls_verify` ](#bls_verify )
- [`bls_verify_multiple` ](#bls_verify_multiple )
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
<!-- /TOC -->
2018-11-27 16:08:43 +00:00
2018-12-09 14:43:13 +00:00
## Curve
2018-12-09 14:21:34 +00:00
The BLS12-381 curve parameters are defined [here ](https://z.cash/blog/new-snark-curve ).
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
## Point representations
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
We represent points in the groups G1 and G2 following [zkcrypto/pairing ](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381 ). We denote by `q` the field modulus and by `i` the imaginary unit.
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
### G1 points
2018-11-27 16:08:43 +00:00
2018-12-10 10:34:36 +00:00
A point in G1 is represented as a 384-bit integer `z` decomposed as a 381-bit integer `x` and three 1-bit flags in the top bits:
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
* `x = z % 2**381`
* `a_flag = (z % 2**382) // 2**381`
* `b_flag = (z % 2**383) // 2**382`
* `c_flag = (z % 2**384) // 2**383`
2018-12-10 10:34:36 +00:00
Respecting bit ordering, `z` is decomposed as `(c_flag, b_flag, a_flag, x)` .
2018-12-09 14:21:34 +00:00
We require:
* `x < q`
* `c_flag == 1`
2018-12-10 04:44:44 +00:00
* if `b_flag == 1` then `a_flag == x == 0` and `z` represents the point at infinity
* if `b_flag == 0` then `z` represents the point `(x, y)` where `y` is the valid coordinate such that `(y * 2) // q == a_flag`
2018-12-09 14:21:34 +00:00
### G2 points
2018-12-10 04:44:44 +00:00
A point in G2 is represented as a pair of 384-bit integers `(z1, z2)` . We decompose `z1` as above into `x1` , `a_flag1` , `b_flag1` , `c_flag1` and `z2` into `x2` , `a_flag2` , `b_flag2` , `c_flag2` .
2018-12-09 14:21:34 +00:00
We require:
* `x1 < q` and `x2 < q`
* `a_flag2 == b_flag2 == c_flag2 == 0`
* `c_flag1 == 1`
2018-12-10 04:44:44 +00:00
* if `b_flag1 == 1` then `a_flag1 == x1 == x2 == 0` and `(z1, z2)` represents the point at infinity
* if `b_flag1 == 0` then `(z1, z2)` represents the point `(x1 * i + x2, y)` where `y` is the valid coordinate such that the imaginary part `y_im` of `y` satisfies `(y_im * 2) // q == a_flag1`
2018-12-09 14:21:34 +00:00
## Helpers
### `hash_to_G2`
2018-11-27 16:08:43 +00:00
```python
G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109
2018-12-10 10:34:36 +00:00
q = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
def hash_to_G2(message, domain):
2018-12-10 10:34:36 +00:00
x1 = int.from_bytes(hash(bytes8(domain) + b'\x01' + message), 'big')
x2 = int.from_bytes(hash(bytes8(domain) + b'\x02' + message), 'big')
2018-12-09 14:21:34 +00:00
x_coordinate = FQ2([x1, x2]) # x1 + x2 * i
2018-11-27 16:08:43 +00:00
while 1:
2018-12-09 14:30:48 +00:00
x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4, 4])
2018-12-09 14:43:13 +00:00
y_coordinate = modular_squareroot(x_cubed_plus_b2)
2018-12-09 14:21:34 +00:00
if y_coordinate is not None:
2018-11-27 16:08:43 +00:00
break
2018-12-09 14:21:34 +00:00
x_coordinate += FQ2([1, 0]) # Add one until we get a quadratic residue
2018-12-10 10:34:36 +00:00
assert is_on_G2((x_coordinate, y_coordinate))
return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor)
2018-11-27 16:08:43 +00:00
```
2018-12-09 14:43:13 +00:00
### `modular_squareroot`
2018-11-27 16:08:43 +00:00
2018-12-10 10:34:36 +00:00
`modular_squareroot(x)` returns a solution `y` to `y**2 % q == x` , and `None` if none exists. If there are two solutions the one with higher imaginary component is favored; if both solutions have equal imaginary component the one with higher real component is favored.
2018-12-10 04:44:44 +00:00
2018-11-27 16:08:43 +00:00
```python
2018-12-09 14:21:34 +00:00
qmod = q ** 2 - 1
2018-11-27 16:08:43 +00:00
eighth_roots_of_unity = [FQ2([1,1]) ** ((qmod * k) // 8) for k in range(8)]
2018-12-09 14:43:13 +00:00
def modular_squareroot(value):
candidate_squareroot = value ** ((qmod + 8) // 16)
check = candidate_squareroot ** 2 / value
2018-11-27 16:08:43 +00:00
if check in eighth_roots_of_unity[::2]:
2018-12-10 04:44:44 +00:00
x1 = candidate_squareroot / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2]
x2 = -x1
return x1 if (x1.coeffs[1].n, x1.coeffs[0].n) > (x2.coeffs[1].n, x2.coeffs[0].n) else x2
2018-11-27 16:08:43 +00:00
return None
```
2018-12-09 14:21:34 +00:00
## Signature verification
2018-12-09 14:30:48 +00:00
In the following `e` is the pairing function and `g` is the generator in G1.
2018-12-09 14:21:34 +00:00
### `bls_verify`
2018-12-09 14:43:13 +00:00
Let `bls_verify(pubkey: uint384, message: bytes32, signature: [uint384], domain: uint64) -> bool` :
2018-12-09 14:21:34 +00:00
* Verify that `pubkey` is a valid G1 point.
* Verify that `signature` is a valid G2 point.
2018-12-09 14:48:54 +00:00
* Verify that `e(pubkey, hash_to_G2(message, domain)) == e(g, signature)` .
2018-12-09 14:21:34 +00:00
### `bls_verify_multiple`
2018-12-09 14:49:16 +00:00
Let `bls_verify_multiple(pubkeys: [uint384], messages: [bytes32], signature: [uint384], domain: uint64) -> bool` :
2018-11-27 16:08:43 +00:00
2018-12-09 14:21:34 +00:00
* Verify that each `pubkey` in `pubkeys` is a valid G1 point.
* Verify that `signature` is a valid G2 point.
* Verify that `len(pubkeys)` equals `len(messages)` and denote the length `L` .
2018-12-09 14:43:13 +00:00
* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(g, signature)` .