From 1b0b2f21cca29e12190cd929770bf16fff6d2dd6 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 9 Dec 2018 14:21:34 +0000 Subject: [PATCH 01/13] First pass cleanup of bls_verify.md Misc cleanups: * (typo) `highflag` => `highflag1` * (typo) `lowflag = x = 0` => `lowflag == x == 0` * Add structure and table of contents * Describe more notation in words (e.g. `i`) * Make sure flags are 1-bit * Clarify and polish presentation Example notation cleanups: * `G1` => `id_G1` (to avoid confusing with the group G1) * `field_modulus` => `q` (avoid using two names for same thing) * `BLSVerify` => `bls_verify` (respect notation for functions in main document) * `sig` => `signature` (avoid abbreviations as in main document) TODO: * Potentially describe `FQ2`, `b2`, `is_on_curve`, `multiply` in words * Make the naming changes around `bls_verify` in the main document * Fix any bugs introduced by the cleanup --- specs/bls_verify.md | 119 ++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 36 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index a84711cc1..b9f92ee53 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -1,64 +1,111 @@ -### BLS Verification +# BLS signature verification **Warning: This document is pending academic review and should not yet be considered secure.** -See https://z.cash/blog/new-snark-curve/ for BLS-12-381 parameters. `q` is the field modulus. +## Table of contents + -We represent coordinates as defined in https://github.com/zkcrypto/pairing/tree/master/src/bls12_381/. +- [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) + - [`modular_square_root`](#modular_square_root) + - [Signature verification](#signature-verification) + - [`bls_verify`](#bls_verify) + - [`bls_verify_multiple`](#bls_verify_multiple) -Specifically, a point in G1 as a 384-bit integer `z`, which we decompose into: + -* `x = z % 2**381` (must be `< q`) -* `highflag = z // 2**382` -* `lowflag = (z % 2**382) // 2**381` +The BLS12-381 curve parameters are defined [here](https://z.cash/blog/new-snark-curve). -If `highflag == 3`, the point is the point at infinity and we require `lowflag = x = 0`. Otherwise, we require `highflag == 2`, in which case the point is `(x, y)` where `y` is the valid coordinate such that `(y * 2) // q == lowflag`. +## Point representations -We represent a point in G2 as a pair of 384-bit integers `(z1, z2)` that are each decomposed into `x1`, `highflag1`, `lowflag1`, `x2`, `highflag2`, `lowflag2` as above, where `x1` and `x2` must both be `< q`. We require `lowflag2 == highflag2 == 0`. If `highflag1 == 3`, the point is the point at infinity and we require `lowflag1 == x1 == x2 == 0`. Otherwise, we require `highflag == 2`, in which case the point is `(x1 * i + x2, y)` where `y` is the valid coordinate such that the imaginary part of `y` satisfies `(y_im * 2) // q == lowflag1`. +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. -`BLSVerify(pubkey: uint384, msg: bytes32, sig: [uint384], domain: uint64)` is done as follows: +### G1 points -* Verify that `pubkey` is a valid G1 point and `sig` is a valid G2 point. -* Convert `msg` to a G2 point using `hash_to_G2` defined below. -* Do the pairing check: verify `e(pubkey, hash_to_G2(msg, domain)) == e(G1, sig)` (where `e` is the BLS pairing function) +A point in G1 is represented as a 384-bit integer `z` decomposed as a 381-bit integer and three 1-bit flags: -Here is the `hash_to_G2` definition: +* `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` + +We require: + +* `x < q` +* `c_flag == 1` +* if `b_flag == 1` then `a_flag == x == 0` and `z` is the point at infinity +* if `b_flag == 0` then `z` is the point `(x, y)` where `y` is the valid coordinate such that `(y * 2) // q == a_flag` + +### G2 points + +A point in G2 is represented as a pair of 384-bit integers `(z1, z2)`. We decompose `z1` and `z2` as above into `x1`, `a_flag1`, `b_flag1`, `c_flag1` and `x2`, `a_flag2`, `b_flag2`, `c_flag2`. + +We require: + +* `x1 < q` and `x2 < q` +* `a_flag2 == b_flag2 == c_flag2 == 0` +* `c_flag1 == 1` +* if `b_flag1 == 1` then `a_flag1 == x1 == x2 == 0` and `(z1, z2)` is the point at infinity +* if `b_flag1 == 0` then `(z1, z2)` is 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`. + +## Helpers + +### `hash_to_G2` ```python G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 -field_modulus = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -def hash_to_G2(m, domain): - x1 = hash(bytes8(domain) + b'\x01' + m) - x2 = hash(bytes8(domain) + b'\x02' + m) - x_coord = FQ2([x1, x2]) # x1 + x2 * i +def hash_to_G2(message, domain): + x1 = hash(bytes8(domain) + b'\x01' + message) + x2 = hash(bytes8(domain) + b'\x02' + message) + x_coordinate = FQ2([x1, x2]) # x1 + x2 * i while 1: - x_cubed_plus_b2 = x_coord ** 3 + FQ2([4,4]) - y_coord = mod_sqrt(x_cubed_plus_b2) - if y_coord is not None: + x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4,4]) + y_coordinate = modular_square_root(x_cubed_plus_b2) + if y_coordinate is not None: break - x_coord += FQ2([1, 0]) # Add one until we get a quadratic residue - assert is_on_curve((x_coord, y_coord)) - return multiply((x_coord, y_coord), G2_cofactor) + x_coordinate += FQ2([1, 0]) # Add one until we get a quadratic residue + assert is_on_curve((x_coordinate, y_coordinate)) + return multiply((x_coordinate, y_coordinate), G2_cofactor) ``` -Here is a sample implementation of `mod_sqrt`: +### `modular_square_root` ```python -qmod = field_modulus ** 2 - 1 +qmod = q ** 2 - 1 eighth_roots_of_unity = [FQ2([1,1]) ** ((qmod * k) // 8) for k in range(8)] -def mod_sqrt(val): - candidate_sqrt = val ** ((qmod + 8) // 16) - check = candidate_sqrt ** 2 / val +def modular_square_root(value): + candidate_square_root = value ** ((qmod + 8) // 16) + check = candidate_square_root ** 2 / value if check in eighth_roots_of_unity[::2]: - return candidate_sqrt / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2] + return candidate_square_root / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2] return None ``` -`BLSMultiVerify(pubkeys: [uint384], msgs: [bytes32], sig: [uint384], domain: uint64)` is done as follows: +## Signature verification -* Verify that each element of `pubkeys` is a valid G1 point and `sig` is a valid G2 point. -* Convert each element of `msg` to a G2 point using `hash_to_G2` defined above, using the specified `domain`. -* Check that the length of `pubkeys` and `msgs` is the same, call the length `L` -* Do the pairing check: verify `e(pubkeys[0], hash_to_G2(msgs[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(msgs[L-1], domain)) == e(G1, sig)` +In the following `e` is the pairing function and `id_G1` the identity in G1. + +### `bls_verify` + +`bls_verify(pubkey: uint384, message: bytes32, signature: [uint384], domain: uint64)` is done as follows: + +* Verify that `pubkey` is a valid G1 point. +* Verify that `signature` is a valid G2 point. +* Verify `e(pubkey, hash_to_G2(message, domain)) == e(id_G1, sig)`. + +### `bls_verify_multiple` + +`BLSMultiVerify(pubkeys: [uint384], messages: [bytes32], signature: [uint384], domain: uint64)` is done as follows: + +* 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`. +* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(id_G1, sig)`. From 24d3c9c33aa685860969c6d03d025d5cff61d234 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 9 Dec 2018 14:30:48 +0000 Subject: [PATCH 02/13] Update bls_verify.md --- specs/bls_verify.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index b9f92ee53..6b3497a73 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -66,7 +66,7 @@ def hash_to_G2(message, domain): x2 = hash(bytes8(domain) + b'\x02' + message) x_coordinate = FQ2([x1, x2]) # x1 + x2 * i while 1: - x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4,4]) + x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4, 4]) y_coordinate = modular_square_root(x_cubed_plus_b2) if y_coordinate is not None: break @@ -91,7 +91,7 @@ def modular_square_root(value): ## Signature verification -In the following `e` is the pairing function and `id_G1` the identity in G1. +In the following `e` is the pairing function and `g` is the generator in G1. ### `bls_verify` @@ -99,7 +99,7 @@ In the following `e` is the pairing function and `id_G1` the identity in G1. * Verify that `pubkey` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify `e(pubkey, hash_to_G2(message, domain)) == e(id_G1, sig)`. +* Verify `e(pubkey, hash_to_G2(message, domain)) == e(g, sig)`. ### `bls_verify_multiple` @@ -108,4 +108,4 @@ In the following `e` is the pairing function and `id_G1` the identity in G1. * 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`. -* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(id_G1, sig)`. +* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(g, sig)`. From 0b8fa12289f6ebdd38fe2c49a5be07b838e2f996 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 9 Dec 2018 14:43:13 +0000 Subject: [PATCH 03/13] Update bls_verify.md --- specs/bls_verify.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 6b3497a73..9b90b87f4 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -12,13 +12,15 @@ - [G2 points](#g2-points) - [Helpers](#helpers) - [`hash_to_G2`](#hash_to_g2) - - [`modular_square_root`](#modular_square_root) + - [`modular_squareroot`](#modular_squareroot) - [Signature verification](#signature-verification) - [`bls_verify`](#bls_verify) - [`bls_verify_multiple`](#bls_verify_multiple) +## Curve + The BLS12-381 curve parameters are defined [here](https://z.cash/blog/new-snark-curve). ## Point representations @@ -67,7 +69,7 @@ def hash_to_G2(message, domain): x_coordinate = FQ2([x1, x2]) # x1 + x2 * i while 1: x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4, 4]) - y_coordinate = modular_square_root(x_cubed_plus_b2) + y_coordinate = modular_squareroot(x_cubed_plus_b2) if y_coordinate is not None: break x_coordinate += FQ2([1, 0]) # Add one until we get a quadratic residue @@ -75,17 +77,17 @@ def hash_to_G2(message, domain): return multiply((x_coordinate, y_coordinate), G2_cofactor) ``` -### `modular_square_root` +### `modular_squareroot` ```python qmod = q ** 2 - 1 eighth_roots_of_unity = [FQ2([1,1]) ** ((qmod * k) // 8) for k in range(8)] -def modular_square_root(value): - candidate_square_root = value ** ((qmod + 8) // 16) - check = candidate_square_root ** 2 / value +def modular_squareroot(value): + candidate_squareroot = value ** ((qmod + 8) // 16) + check = candidate_squareroot ** 2 / value if check in eighth_roots_of_unity[::2]: - return candidate_square_root / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2] + return candidate_squareroot / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2] return None ``` @@ -95,17 +97,17 @@ In the following `e` is the pairing function and `g` is the generator in G1. ### `bls_verify` -`bls_verify(pubkey: uint384, message: bytes32, signature: [uint384], domain: uint64)` is done as follows: +Let `bls_verify(pubkey: uint384, message: bytes32, signature: [uint384], domain: uint64) -> bool`: * Verify that `pubkey` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify `e(pubkey, hash_to_G2(message, domain)) == e(g, sig)`. +* Verify `e(pubkey, hash_to_G2(message, domain)) == e(g, signature)`. ### `bls_verify_multiple` -`BLSMultiVerify(pubkeys: [uint384], messages: [bytes32], signature: [uint384], domain: uint64)` is done as follows: +Let `BLSMultiVerify(pubkeys: [uint384], messages: [bytes32], signature: [uint384], domain: uint64) -> bool`: * 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`. -* Verify that `e(pubkeys[0], hash_to_G2(messages[0], domain)) * ... * e(pubkeys[L-1], hash_to_G2(messages[L-1], domain)) == e(g, sig)`. +* 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)`. From 6c196fc41183a2779ae211f3c5bbd0fd623b81b9 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 9 Dec 2018 14:48:54 +0000 Subject: [PATCH 04/13] Update bls_verify.md --- specs/bls_verify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 9b90b87f4..858ba457c 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -101,7 +101,7 @@ Let `bls_verify(pubkey: uint384, message: bytes32, signature: [uint384], domain: * Verify that `pubkey` is a valid G1 point. * Verify that `signature` is a valid G2 point. -* Verify `e(pubkey, hash_to_G2(message, domain)) == e(g, signature)`. +* Verify that `e(pubkey, hash_to_G2(message, domain)) == e(g, signature)`. ### `bls_verify_multiple` From 6079f70ebd2096570d89ddb1ff963e308dcfae58 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 9 Dec 2018 14:49:16 +0000 Subject: [PATCH 05/13] Update bls_verify.md --- specs/bls_verify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 858ba457c..1e3f44aa7 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -105,7 +105,7 @@ Let `bls_verify(pubkey: uint384, message: bytes32, signature: [uint384], domain: ### `bls_verify_multiple` -Let `BLSMultiVerify(pubkeys: [uint384], messages: [bytes32], signature: [uint384], domain: uint64) -> bool`: +Let `bls_verify_multiple(pubkeys: [uint384], messages: [bytes32], signature: [uint384], domain: uint64) -> bool`: * Verify that each `pubkey` in `pubkeys` is a valid G1 point. * Verify that `signature` is a valid G2 point. From cd07ec126be07a19e7614ce936dca24a7b2b299c Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 9 Dec 2018 14:51:15 +0000 Subject: [PATCH 06/13] Update bls_verify.md --- specs/bls_verify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 1e3f44aa7..c28025b11 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -53,7 +53,7 @@ We require: * `a_flag2 == b_flag2 == c_flag2 == 0` * `c_flag1 == 1` * if `b_flag1 == 1` then `a_flag1 == x1 == x2 == 0` and `(z1, z2)` is the point at infinity -* if `b_flag1 == 0` then `(z1, z2)` is 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`. +* if `b_flag1 == 0` then `(z1, z2)` is 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` ## Helpers From d6cc826bb0eac45d47c6569b2a229835f1cfc749 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 9 Dec 2018 23:44:44 -0500 Subject: [PATCH 07/13] Some edits --- specs/bls_verify.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index c28025b11..7c1266787 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -40,20 +40,20 @@ We require: * `x < q` * `c_flag == 1` -* if `b_flag == 1` then `a_flag == x == 0` and `z` is the point at infinity -* if `b_flag == 0` then `z` is the point `(x, y)` where `y` is the valid coordinate such that `(y * 2) // q == a_flag` +* 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` ### G2 points -A point in G2 is represented as a pair of 384-bit integers `(z1, z2)`. We decompose `z1` and `z2` as above into `x1`, `a_flag1`, `b_flag1`, `c_flag1` and `x2`, `a_flag2`, `b_flag2`, `c_flag2`. +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`. We require: * `x1 < q` and `x2 < q` * `a_flag2 == b_flag2 == c_flag2 == 0` * `c_flag1 == 1` -* if `b_flag1 == 1` then `a_flag1 == x1 == x2 == 0` and `(z1, z2)` is the point at infinity -* if `b_flag1 == 0` then `(z1, z2)` is 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` +* 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` ## Helpers @@ -79,6 +79,8 @@ def hash_to_G2(message, domain): ### `modular_squareroot` +`modular_squareroot(x)` returns the value `y` such that `y**2 % field_modulus == x`, and `None` if this is not possible. In cases where there are two solutions, the value with higher imaginary component is favored; if both solutions have equal imaginary component the value with higher real component is favored. Here is an implementation. + ```python qmod = q ** 2 - 1 eighth_roots_of_unity = [FQ2([1,1]) ** ((qmod * k) // 8) for k in range(8)] @@ -87,7 +89,9 @@ def modular_squareroot(value): candidate_squareroot = value ** ((qmod + 8) // 16) check = candidate_squareroot ** 2 / value if check in eighth_roots_of_unity[::2]: - return candidate_squareroot / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2] + 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 return None ``` From 25cc1cf38221c5b7e1688cb330b612c9c9cee776 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 10 Dec 2018 10:34:36 +0000 Subject: [PATCH 08/13] Address issues from reviewers --- specs/bls_verify.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 7c1266787..e46699fb8 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -29,13 +29,15 @@ We represent points in the groups G1 and G2 following [zkcrypto/pairing](https:/ ### G1 points -A point in G1 is represented as a 384-bit integer `z` decomposed as a 381-bit integer and three 1-bit flags: +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: * `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` +Respecting bit ordering, `z` is decomposed as `(c_flag, b_flag, a_flag, x)`. + We require: * `x < q` @@ -61,11 +63,11 @@ We require: ```python G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 -q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 +q = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab def hash_to_G2(message, domain): - x1 = hash(bytes8(domain) + b'\x01' + message) - x2 = hash(bytes8(domain) + b'\x02' + message) + x1 = int.from_bytes(hash(bytes8(domain) + b'\x01' + message), 'big') + x2 = int.from_bytes(hash(bytes8(domain) + b'\x02' + message), 'big') x_coordinate = FQ2([x1, x2]) # x1 + x2 * i while 1: x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4, 4]) @@ -73,13 +75,13 @@ def hash_to_G2(message, domain): if y_coordinate is not None: break x_coordinate += FQ2([1, 0]) # Add one until we get a quadratic residue - assert is_on_curve((x_coordinate, y_coordinate)) - return multiply((x_coordinate, y_coordinate), G2_cofactor) + assert is_on_G2((x_coordinate, y_coordinate)) + return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor) ``` ### `modular_squareroot` -`modular_squareroot(x)` returns the value `y` such that `y**2 % field_modulus == x`, and `None` if this is not possible. In cases where there are two solutions, the value with higher imaginary component is favored; if both solutions have equal imaginary component the value with higher real component is favored. Here is an implementation. +`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. ```python qmod = q ** 2 - 1 From c782725aa1e24e8f08cd4001f4aeed90a4d59350 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 10 Dec 2018 10:43:42 +0000 Subject: [PATCH 09/13] Update bls_verify.md --- specs/bls_verify.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index e46699fb8..2711755c0 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -7,6 +7,7 @@ - [BLS signature verification](#bls-signature-verification) - [Table of contents](#table-of-contents) + - [Curve parameters](#curve-parameters) - [Point representations](#point-representations) - [G1 points](#g1-points) - [G2 points](#g2-points) @@ -19,7 +20,7 @@ -## Curve +## Curve parameters The BLS12-381 curve parameters are defined [here](https://z.cash/blog/new-snark-curve). From 411d347b6bc8f5e2f1b14e872c32e047e70ca7d3 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 10 Dec 2018 14:30:36 +0000 Subject: [PATCH 10/13] Update bls_verify.md --- specs/bls_verify.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 2711755c0..515910533 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -67,17 +67,18 @@ G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982 q = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab def hash_to_G2(message, domain): - x1 = int.from_bytes(hash(bytes8(domain) + b'\x01' + message), 'big') - x2 = int.from_bytes(hash(bytes8(domain) + b'\x02' + message), 'big') - x_coordinate = FQ2([x1, x2]) # x1 + x2 * i + # Initial candidate x coordinate + x_re = int.from_bytes(hash(bytes8(domain) + b'\x01' + message), 'big') + x_im = int.from_bytes(hash(bytes8(domain) + b'\x02' + message), 'big') + x_coordinate = FQ2([x_re, x_im]) # x = x_re + i * x_im + + # Test candidate y coordinates until a one is found while 1: - x_cubed_plus_b2 = x_coordinate ** 3 + FQ2([4, 4]) - y_coordinate = modular_squareroot(x_cubed_plus_b2) - if y_coordinate is not None: - break - x_coordinate += FQ2([1, 0]) # Add one until we get a quadratic residue - assert is_on_G2((x_coordinate, y_coordinate)) - return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor) + y_coordinate_squared = x_coordinate ** 3 + FQ2([4, 4]) # The curve is y^2 = x^3 + 4(i + 1) + y_coordinate = modular_squareroot(y_coordinate_squared) + if y_coordinate is not None: # Check if quadratic residue found + return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor) + x_coordinate += FQ2([1, 0]) # Add 1 and try again ``` ### `modular_squareroot` From 23f7e9db629165c70967f3e6a1b6fee6c1ebd823 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 11 Dec 2018 13:36:34 +0000 Subject: [PATCH 11/13] Rename FQ2 to Fq2 and specify the G1 generator --- specs/bls_verify.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 515910533..f27baf6ca 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -70,15 +70,15 @@ def hash_to_G2(message, domain): # Initial candidate x coordinate x_re = int.from_bytes(hash(bytes8(domain) + b'\x01' + message), 'big') x_im = int.from_bytes(hash(bytes8(domain) + b'\x02' + message), 'big') - x_coordinate = FQ2([x_re, x_im]) # x = x_re + i * x_im + x_coordinate = Fq2([x_re, x_im]) # x = x_re + i * x_im # Test candidate y coordinates until a one is found while 1: - y_coordinate_squared = x_coordinate ** 3 + FQ2([4, 4]) # The curve is y^2 = x^3 + 4(i + 1) + y_coordinate_squared = x_coordinate ** 3 + Fq2([4, 4]) # The curve is y^2 = x^3 + 4(i + 1) y_coordinate = modular_squareroot(y_coordinate_squared) if y_coordinate is not None: # Check if quadratic residue found return multiply_in_G2((x_coordinate, y_coordinate), G2_cofactor) - x_coordinate += FQ2([1, 0]) # Add 1 and try again + x_coordinate += Fq2([1, 0]) # Add 1 and try again ``` ### `modular_squareroot` @@ -87,7 +87,7 @@ def hash_to_G2(message, domain): ```python qmod = q ** 2 - 1 -eighth_roots_of_unity = [FQ2([1,1]) ** ((qmod * k) // 8) for k in range(8)] +eighth_roots_of_unity = [Fq2([1,1]) ** ((qmod * k) // 8) for k in range(8)] def modular_squareroot(value): candidate_squareroot = value ** ((qmod + 8) // 16) @@ -101,7 +101,13 @@ def modular_squareroot(value): ## Signature verification -In the following `e` is the pairing function and `g` is the generator in G1. +In the following `e` is the pairing function and `g` is the G1 generator with the following coordinates (see [here](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381#g1)): + +```python +g_x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 +g_y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 +g = Fq2(g_x, g_y) +``` ### `bls_verify` From a32af50513f092c5a36b90550abb5c495ca10e91 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 11 Dec 2018 13:56:40 +0000 Subject: [PATCH 12/13] Update bls_verify.md --- specs/bls_verify.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index f27baf6ca..12f8fe7bf 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -64,9 +64,9 @@ We require: ```python G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 -q = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +q = 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 -def hash_to_G2(message, domain): +def hash_to_G2(message: bytes32, domain: uint64) -> [uint384]: # Initial candidate x coordinate x_re = int.from_bytes(hash(bytes8(domain) + b'\x01' + message), 'big') x_im = int.from_bytes(hash(bytes8(domain) + b'\x02' + message), 'big') @@ -89,7 +89,7 @@ def hash_to_G2(message, domain): qmod = q ** 2 - 1 eighth_roots_of_unity = [Fq2([1,1]) ** ((qmod * k) // 8) for k in range(8)] -def modular_squareroot(value): +def modular_squareroot(value: int) -> int: candidate_squareroot = value ** ((qmod + 8) // 16) check = candidate_squareroot ** 2 / value if check in eighth_roots_of_unity[::2]: From d1c20b862bb13d3d433684f87efa21705e852769 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 11 Dec 2018 14:02:34 +0000 Subject: [PATCH 13/13] Update bls_verify.md --- specs/bls_verify.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/bls_verify.md b/specs/bls_verify.md index 12f8fe7bf..a6c41e555 100644 --- a/specs/bls_verify.md +++ b/specs/bls_verify.md @@ -86,11 +86,11 @@ def hash_to_G2(message: bytes32, domain: uint64) -> [uint384]: `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. ```python -qmod = q ** 2 - 1 -eighth_roots_of_unity = [Fq2([1,1]) ** ((qmod * k) // 8) for k in range(8)] +Fq2_order = q ** 2 - 1 +eighth_roots_of_unity = [Fq2([1,1]) ** ((Fq2_order * k) // 8) for k in range(8)] def modular_squareroot(value: int) -> int: - candidate_squareroot = value ** ((qmod + 8) // 16) + candidate_squareroot = value ** ((Fq2_order + 8) // 16) check = candidate_squareroot ** 2 / value if check in eighth_roots_of_unity[::2]: x1 = candidate_squareroot / eighth_roots_of_unity[eighth_roots_of_unity.index(check) // 2]