Skip to content

Commit

Permalink
Merge pull request #87 from CarlBeek/bls_h2c_v06
Browse files Browse the repository at this point in the history
Switches hash-to-field to new v06 hash system
  • Loading branch information
hwwhww authored May 5, 2020
2 parents 043b562 + 39d8878 commit 8981f54
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 75 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pip install py_ecc

## BLS Signatures

`py_ecc` implements the [IETF BLS draft standard v0](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00) with [hash-to-curve v5](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05) as per the inter-blockchain standardization agreement. The BLS standards specify [different ciphersuites](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00#section-4.2) which each have different functionality to accommodate various use cases. The following ciphersuites are availible from this library:
`py_ecc` implements the [IETF BLS draft standard v0](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00) with [hash-to-curve v6](https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06) as per the inter-blockchain standardization agreement. The BLS standards specify [different ciphersuites](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00#section-4.2) which each have different functionality to accommodate various use cases. The following ciphersuites are availible from this library:

- `G2Basic` also known as `BLS_SIG_BLS12381G2-SHA256-SSWU-RO-_NUL_`
- `G2MessageAugmentation` also known as `BLS_SIG_BLS12381G2-SHA256-SSWU-RO-_AUG_`
Expand Down
21 changes: 12 additions & 9 deletions py_ecc/bls/ciphersuites.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
big_endian_to_int,
ValidationError,
)
from hashlib import sha256

from py_ecc.fields import optimized_bls12_381_FQ12 as FQ12
from py_ecc.optimized_bls12_381 import (
Expand Down Expand Up @@ -44,6 +45,7 @@

class BaseG2Ciphersuite(abc.ABC):
DST = b''
xmd_hash_function = sha256

@staticmethod
def PrivToPub(privkey: int) -> BLSPubkey:
Expand All @@ -65,14 +67,15 @@ def KeyValidate(PK: BLSPubkey) -> bool:
return False
return True

@staticmethod
def _CoreSign(SK: int, message: bytes, DST: bytes) -> BLSSignature:
message_point = hash_to_G2(message, DST)
@classmethod
def _CoreSign(cls, SK: int, message: bytes, DST: bytes) -> BLSSignature:
message_point = hash_to_G2(message, DST, cls.xmd_hash_function)
signature_point = multiply(message_point, SK)
return G2_to_signature(signature_point)

@staticmethod
def _CoreVerify(PK: BLSPubkey, message: bytes, signature: BLSSignature, DST: bytes) -> bool:
@classmethod
def _CoreVerify(cls, PK: BLSPubkey, message: bytes,
signature: BLSSignature, DST: bytes) -> bool:
try:
signature_point = signature_to_G2(signature)
final_exponentiation = final_exponentiate(
Expand All @@ -81,7 +84,7 @@ def _CoreVerify(PK: BLSPubkey, message: bytes, signature: BLSSignature, DST: byt
G1,
final_exponentiate=False,
) * pairing(
hash_to_G2(message, DST),
hash_to_G2(message, DST, cls.xmd_hash_function),
neg(pubkey_to_G1(PK)),
final_exponentiate=False,
)
Expand All @@ -98,15 +101,15 @@ def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature:
accumulator = add(accumulator, signature_point)
return G2_to_signature(accumulator)

@staticmethod
def _CoreAggregateVerify(pairs: Sequence[Tuple[BLSPubkey, bytes]],
@classmethod
def _CoreAggregateVerify(cls, pairs: Sequence[Tuple[BLSPubkey, bytes]],
signature: BLSSignature, DST: bytes) -> bool:
try:
signature_point = signature_to_G2(signature)
accumulator = FQ12.one()
for pk, message in pairs:
pubkey_point = pubkey_to_G1(pk)
message_point = hash_to_G2(message, DST)
message_point = hash_to_G2(message, DST, cls.xmd_hash_function)
accumulator *= pairing(message_point, pubkey_point, final_exponentiate=False)
accumulator *= pairing(signature_point, neg(G1), final_exponentiate=False)
return final_exponentiate(accumulator) == FQ12.one()
Expand Down
8 changes: 7 additions & 1 deletion py_ecc/bls/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@
POW_2_382 = 2**382
POW_2_383 = 2**383

HASH_TO_G2_L = 64

# Store all the possible single bytes for faster access in hash-to-field
ALL_BYTES = tuple(bytes([i]) for i in range(256))

# Paramaters for hashing to the field as specified in:
# https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-8.7
HASH_TO_FIELD_L = 64
40 changes: 35 additions & 5 deletions py_ecc/bls/hash.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import math
import hashlib
import hmac
from typing import Union
import math
from typing import (
Union,
)
from hashlib import sha256
from _hashlib import HASH

from .constants import (
ALL_BYTES,
)


def hkdf_extract(salt: Union[bytes, bytearray], ikm: Union[bytes, bytearray]) -> bytes:
Expand All @@ -10,7 +17,7 @@ def hkdf_extract(salt: Union[bytes, bytearray], ikm: Union[bytes, bytearray]) ->
https://tools.ietf.org/html/rfc5869
"""
return hmac.new(salt, ikm, hashlib.sha256).digest()
return hmac.new(salt, ikm, sha256).digest()


def hkdf_expand(prk: Union[bytes, bytearray], info: Union[bytes, bytearray], length: int) -> bytes:
Expand All @@ -30,8 +37,31 @@ def hkdf_expand(prk: Union[bytes, bytearray], info: Union[bytes, bytearray], len
text = previous + info + bytes([i + 1])

# T(i + 1) = HMAC(T(i) || info || i)
previous = bytearray(hmac.new(prk, text, hashlib.sha256).digest())
previous = bytearray(hmac.new(prk, text, sha256).digest())
okm.extend(previous)

# Return first `length` bytes.
return okm[:length]


def xor(a: bytes, b: bytes) -> bytes:
return bytes(_a ^ _b for _a, _b in zip(a, b))


def expand_message_xmd(msg: bytes, DST: bytes, len_in_bytes: int, hash_function: HASH) -> bytes:
b_in_bytes = hash_function().digest_size
r_in_bytes = hash_function().block_size
if len(DST) > 255:
raise ValueError('DST must be <= 255 bytes')
ell = math.ceil(len_in_bytes / b_in_bytes)
if ell > 255:
raise ValueError('invalid len in bytes for hash function')
DST_prime = ALL_BYTES[len(DST)] + DST # Prepend the length if the DST as a single byte
Z_pad = b'\x00' * r_in_bytes
l_i_b_str = len_in_bytes.to_bytes(2, 'big')
b_0 = hash_function(Z_pad + msg + l_i_b_str + b'\x00' + DST_prime).digest()
b = [hash_function(b_0 + b'\x01' + DST_prime).digest()]
for i in range(2, ell + 1):
b.append(hash_function(xor(b_0, b[i - 2]) + ALL_BYTES[i] + DST_prime).digest())
pseudo_random_bytes = b''.join(b)
return pseudo_random_bytes[:len_in_bytes]
65 changes: 33 additions & 32 deletions py_ecc/bls/hash_to_curve.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,75 @@
from eth_utils import (
big_endian_to_int,
from typing import (
Tuple,
)
from _hashlib import HASH

from py_ecc.fields import (
optimized_bls12_381_FQ2 as FQ2,
)
from py_ecc.optimized_bls12_381 import (
add,
iso_map_G2,
field_modulus,
multiply_clear_cofactor_G2,
optimized_swu_G2,
)

from .constants import HASH_TO_G2_L
from .typing import (
G2Uncompressed,
)
from .hash import (
hkdf_expand,
hkdf_extract,
)
from .constants import HASH_TO_FIELD_L
from .hash import expand_message_xmd
from .typing import G2Uncompressed


# Hash to G2
def hash_to_G2(message: bytes, DST: bytes) -> G2Uncompressed:
def hash_to_G2(message: bytes, DST: bytes,
hash_function: HASH) -> G2Uncompressed:
"""
Convert a message to a point on G2 as defined here:
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-3
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-6.6.3
The idea is to first hash into FQ2 and then use SSWU to map the result into G2.
Contants and inputs follow the ciphersuite ``BLS12381G2-SHA256-SSWU-RO-`` defined here:
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-8.9.2
Contants and inputs follow the ciphersuite ``BLS12381G2_XMD:SHA-256_SSWU_RO_`` defined here:
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-8.7.2
"""
u0 = hash_to_base_FQ2(message, 0, DST)
u1 = hash_to_base_FQ2(message, 1, DST)
u0, u1 = hash_to_field_FQ2(message, 2, DST, hash_function)
q0 = map_to_curve_G2(u0)
q1 = map_to_curve_G2(u1)
r = add(q0, q1)
p = clear_cofactor_G2(r)
return p


def hash_to_base_FQ2(message: bytes, ctr: int, DST: bytes) -> FQ2:
def hash_to_field_FQ2(message: bytes, count: int,
DST: bytes, hash_function: HASH) -> Tuple[FQ2, ...]:
"""
Hash To Base for FQ2
Hash To Base Field for FQ2
Convert a message to a point in the finite field as defined here:
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-5
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2
"""
m_prime = hkdf_extract(DST, message + b'\x00')
info_pfx = b'H2C' + bytes([ctr])
e = []

# for i in (1, ..., m), where m is the extension degree of FQ2
for i in range(1, 3):
info = info_pfx + bytes([i])
t = hkdf_expand(m_prime, info, HASH_TO_G2_L)
e.append(big_endian_to_int(t))

return FQ2(e)
M = 2 # m is the extension degree of FQ2
len_in_bytes = count * M * HASH_TO_FIELD_L
pseudo_random_bytes = expand_message_xmd(message, DST, len_in_bytes, hash_function)
u = []
for i in range(0, count):
e = []
for j in range(0, M):
elem_offset = HASH_TO_FIELD_L * (j + i * M)
tv = pseudo_random_bytes[elem_offset: elem_offset + HASH_TO_FIELD_L]
e.append(int.from_bytes(tv, 'big') % field_modulus)
u.append(FQ2(e))
return tuple(u)


def map_to_curve_G2(u: FQ2) -> G2Uncompressed:
"""
Map To Curve for G2
First, convert FQ2 point to a point on the 3-Isogeny curve.
SWU Map: https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-6.6.2
SWU Map: https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-6.6.3
Second, map 3-Isogeny curve to BLS12-381-G2 curve.
3-Isogeny Map: https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#appendix-C.3
3-Isogeny Map: https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#appendix-C.3
"""
(x, y, z) = optimized_swu_G2(u)
return iso_map_G2(x, y, z)
Expand Down
4 changes: 2 additions & 2 deletions py_ecc/fields/optimized_field_elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def sgn0_be(self: T_FQ) -> int:
sgn0_be(x) = -1 when x > -x
Defined here:
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-4.1.1
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-4.1.1
"""
if self.n == 0:
return 1
Expand Down Expand Up @@ -384,7 +384,7 @@ def sgn0_be(self: T_FQP) -> int:
sgn0_be(x) = -1 when x > -x
Defined here:
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-4.1.1
https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-4.1.1
"""
sign = 0
for x_i in reversed(self.coeffs):
Expand Down
2 changes: 1 addition & 1 deletion py_ecc/optimized_bls12_381/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@

ISO_3_MAP_COEFFICIENTS = (ISO_3_X_NUMERATOR, ISO_3_X_DENOMINATOR, ISO_3_Y_NUMERATOR, ISO_3_Y_DENOMINATOR) # noqa: E501

# h_eff from https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-05#section-8.9.2
# h_eff from https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-8.7.2
H_EFF = 209869847837335686905080341498658477663839067235703451875306851526599783796572738804459333109033834234622528588876978987822447936461846631641690358257586228683615991308971558879306463436166481 # noqa: E501
41 changes: 17 additions & 24 deletions tests/bls/test_hash_to_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
big_endian_to_int,
)
import pytest
from hashlib import sha256

from py_ecc.bls.hash_to_curve import hash_to_G2
from py_ecc.bls.constants import (
Expand All @@ -31,7 +32,7 @@
iso_map_G2,
)

DST = b'BLS_SIG_BLS12381G2-SHA256-SSWU-RO_POP_' # TODO: Switch out test for valid DST
DST = b'BLS12381G2_XMD:SHA-256_SSWU_RO_TESTGEN'


@pytest.mark.parametrize(
Expand Down Expand Up @@ -59,35 +60,27 @@ def test_iso_map_G2(iso_x, iso_y, iso_z, g2_x, g2_y):
assert g2_y == result_y


# Tests taken from: https://github.com/cfrg/draft-irtf-cfrg-hash-to-curve/blob/master/draft-irtf-cfrg-hash-to-curve.md#bls12381g2_xmdsha-256_sswu_ro_
@pytest.mark.parametrize('H', [sha256])
@pytest.mark.parametrize(
'msg,x,y',
[
(b'msg',
FQ2([int('7896efdac56b0f6cbd8c78841676d63fc733b692628687bf25273aa8a107bd8cb53bbdb705b551e239dffe019abd4df', 16), int('bd557eda8d16ab2cb2e71cca4d7b343985064daad04734e07da5cdda26610b59cdc0810a25276467d24b315bf7860e0', 16)]),
FQ2([int('1bdb6290cae9f30f263dd40f014b9f4406c3fbbc5fea47e2ebd45e42332553961eb53a15c09e5e090d7a7122dc6657', 16), int('18370459c44e799af8ef31634a683e340e79c3a06f912594d287a443620933b47a2a3e5ce4470539eae50f6d49b8ebd6', 16)])),
(b'01234567890123456789012345678901',
FQ2([int('16b7456df1dfa411b8be80c503864b0795b0b9a7674c05c00e7bdee5a75cbdeec633e16a104406ea626ea6845f5d19b5', 16), int('12ae54eeb3b4dc113d7e80302e51456224087955910479929bf912d89177aa050376960002a96fc6541ac041957f4b93', 16)]),
FQ2([int('1632fe9d91a984f30a7d9b3bab6583974a2ca55933d96cba85f39ddd61ea0129274f75ad7de29473adf3db676dcdb6a3', 16), int('8d5d3b670fca3661122b0ca5929e48f293a5a5c1261050c46b6a08eac3f7d1f5075e2139a63f98e717ecc7c2e00d042', 16)])),
(b'',
FQ2([int('c38e18c9ca92ad387cbfa0e9bd62e53e4f938006097a092d5e9f2c6f3963d78969e7631bf8d6a8a9aad36bc82d763c1', 16), int('23ebc431b239ee7606aad7cd4eee60bb70df3e5072ede86946ffaddb0584e1fcfcee9484869f41e09ab4d64b9e4a72a', 16)]),
FQ2([int('735ae5ca4a2320d820e15501ee79c42ff58a6f40e8549eada554e07c94b34b6634b6034f8735a7e4ac01db81b00f58e', 16), int('1687b6a2fb9e542426d508d4a58846c0e3496ede2e12f57f3358110874ba0011e2107e0742eeb6707682d5ddf319b6f6', 16)])),
(b'abcdefghijklmnopqrstuvwxyz',
FQ2([int('db85d0c792c586c6efb4126e98a8a8788d28187a6432cbdd57444a8c937ce20e0fc0774477150d31bfff83a050b530e', 16), int('13505f5cbb1503c7b1206edd31364a467f5159d741cffe8f443f2282b4adfcf5f1450bd2fe6127991ff60955b3b40015', 16)]),
FQ2([int('1738e4903e5618fcba965861c73d7c7a7544fabc9762ccdf9842dbba30566ce33047c3ff714ce8a10323bcac0ee88479', 16), int('d0df337706a8b4c367ea189d9e213f47455399ddf734358695e84ad09630a724082ad22dda74e6cd41378dbb89b0ebd', 16)])),
(b'\xff' * 100,
FQ2([int('a2b9bb7afda6e1c3cb2340aa679ce00469a14c651becd30fa231c83ab82d1b92db074058c3673daaaa2a113f0c3ea56', 16), int('e7fcdf25cf4465f58de593bf6445ec1cd164de346a27ed46314dcbb35a830650f5bb4d8049878d9a84a34013fa4fb11', 16)]),
FQ2([int('14f909c9fb9fb14c0e7455ed5306edaad40e7c57cdd719f59730db6ae64161db1f1a8159db4d97700fba7547920fe1a2', 16), int('1702a797d33e0c7b3fac012da0ef1960e0f4551f23ffee3e12dc36ac4acdd6d78bee97bad76689b8e70dac80449a626c', 16)])),
(b'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
FQ2([int('7e2b8b61562339640addfda3202f5d657aa77c143bf5bceda818525ba6f984eba2648528928d6c9680f752dd88d91e3', 16), int('1663cd7231bd9708bebe0be61baecf2b89ebaa658150696f5be2dbe0e092ec698c931e8795ac6319f1c5fdda5d14136a', 16)]),
FQ2([int('33d40a6eb88c11c6018fcc00489bc4b9dd700c20d1bab21ad463c5ee63ce671d199020ba743828d450da050f0385680', 16), int('10336a533d1e3564da20bffe87ebc82121cfbfad2e36ecb950c5e12d8552bf932f5f5e846a50e9706b21b0db6585a777', 16)])),
(b'e46b320165eec91e6344fa10340d5b3208304d6cad29d0d5aed18466d1d9d80e',
FQ2([int('119a71e0d20489cf8c5f82c51e879e7b344e53307b53be650df7f3f04907b75b71fdafe26e8d4e14e603440b09efe6f3', 16), int('e4ea193377da29537e8fe6f6f631adff10afaef2ea1eb2107d30d97358c1a19975e0a8bb62650ff90447cf5b3719c1d', 16)]),
FQ2([int('1142f2e077cc4230ee3cf07565ee626141ea9b86a79a0422d7f0e84c281ca09c5bbbe95f21e51285618c81d6dfda943d', 16), int('15e4da056552343eb519b3087962521d112c7e307d731373e8f1c72415306ccbc3c14fc6d68d61d2feeda3ea2e7729f', 16)])),
FQ2([0x0d3b02ee071b12d1e79138c3900ca3da7b8021ac462fe6ed68080dc9a5f1c5de46b7fe171e8b3e4e7537e7746757aeca, 0x0d4733459fead6a1f30e5f92df08ecfd0db9bcd0f3e2f2de0f00c8f45e081420aa4392eade61eade57d7a68474672fc1]),
FQ2([0x09cc6f7b3074f0c82510e65d8fc58f6033e03ba7358005a13e2bbd7f429b080f29731ef08c3780c9e3c746578b96b05c, 0x0011531b8e08900a4f6f612e1e27432961419ce6a5ee3ec904a53588982d36ec4ea37be80b6cb7d986b38faec67dbe44])),
(b'abc',
FQ2([0x0b6d276d0bfbddde617a9ab4c175b07c9c4aecad2cdd6cc9ca541b61334a69c58680ef5692bbad03d2f572838df32b66, 0x139e9d78ff6d9d163f979d14a64c5e57f82f1ef7e42ece338b571a9e92c0666f0f6bf1a5fc21e2d32bcb6432eab7037c]),
FQ2([0x022f9ee5d596d06c5f2f735c3c5f743978f79fd57bf7d4291e221227f490d3f276066de9f9edc89c57e048ef4cf0ef72, 0x14dd23517516a80d1d840e34f51dfb76946c7670fca0f36ad8ec9bde4ea82dfae119a21b076519bcc1c00152989a4d45])),
(b'abcdef0123456789',
FQ2([0x0ded52c30aace28d3e9cc5c1b47861ae4dd4e9cd17622e0f5b9d584af0397cd0e3bae80d4ee2d9d4b18c390f63154dfd, 0x046701a03f361a0b8392ca387585f7ee6534dcec9450a035e39dc37387d5ca079b9557447f7d9cad0bd9671cb65ada02]),
FQ2([0x07a5cf56c5ea1d69ad59c0e80cc16c0c1b27f02840b396eb0ea320f70e87f705c6fa70cfeb9719b14badbb058bec5a4c, 0x0674d1f7c9e8e84d8d7a07b40231257571c43160fd566e8d24459d17ca52f6068e1b63aaae5359d8869d4abc66de66b6])),
(b'a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
FQ2([0x0161130ef4aa2f60f751e6b3dd48ac6e994d2d2613897c5dd26945bc72f33cc2977e1255c3f2dc0f1440d15a71c29b40, 0x06db1818f132a61f5fe86d315faa8de4653049ac9cf7fbbc6d9987e5864d82a0156259d56192109bafddd5c30b9f01f5]),
FQ2([0x00f7fab0fedc978b974a38a1755244727b8a4eb31073653fa949594645ad181880d20ff0c91c4375b7e451fe803c9847, 0x0964d550ee8752b6db99555ffcd442b4185267f31e3d57435ea73896a7a9fe952bd67f90fd75f4413212ac9640a7672c])),
]
)
def test_hash_to_G2(msg, x, y):
point = hash_to_G2(msg, DST)

def test_hash_to_G2(msg, x, y, H):
point = hash_to_G2(msg, DST, H)
assert is_on_curve(point, b2)

# Affine
Expand Down

0 comments on commit 8981f54

Please sign in to comment.