ethereum.crypto.bls12_381

Byte-level interface to BLS12-381 curve operations.

All inputs and outputs are raw (unpadded) bytes.

InvalidInputError

Input bytes that are not a valid field element or curve point, or a point outside the prime-order subgroup where the operation needs one.

class InvalidInputError:

bytes_to_field_element

Decode a big-endian base field element, rejecting values outside the field.

def bytes_to_field_element(​raw: bytes​) -> int:
44
    <snip>
48
    value = int.from_bytes(raw, "big")
49
    if value >= FQ.field_modulus:
50
        raise InvalidInputError("field element >= field modulus")
51
    return value

bytes_to_fq2

Decode a 96-byte FP2 element, c0 first.

def bytes_to_fq2(​raw: bytes​) -> FQ2:
55
    <snip>
58
    if len(raw) != 96:
59
        raise ValueError("FP2 element must be 96 bytes")
60
61
    c0 = bytes_to_field_element(raw[:48])
62
    c1 = bytes_to_field_element(raw[48:])
63
    return FQ2((c0, c1))

bytes_to_g1

Decode a 96-byte raw G1 point to a py-ecc Point3D.

Validate that field elements are in range and the point is on the curve, and that it is in the prime-order subgroup if subgroup_check is set.

70
@lru_cache(maxsize=128)
def bytes_to_g1(​raw: bytes, ​​subgroup_check: bool​) -> Point3D[FQ]:
72
    <snip>
79
    if len(raw) != 96:
80
        raise ValueError("G1 point must be 96 bytes")
81
82
    x = bytes_to_field_element(raw[:48])
83
    y = bytes_to_field_element(raw[48:])
84
85
    z = 1
86
    if x == 0 and y == 0:
87
        z = 0
88
89
    point: Point3D[FQ] = (FQ(x), FQ(y), FQ(z))
90
    if not is_on_curve(point, b):
91
        raise InvalidInputError("G1 point is not on curve")
92
    if subgroup_check and not is_inf(bls12_multiply(point, curve_order)):
93
        raise InvalidInputError("Subgroup check failed for G1 point")
94
    return point

g1_to_bytes

Encode a py-ecc G1 point to 96 raw bytes.

def g1_to_bytes(​point: Point3D[FQ]​) -> bytes:
98
    <snip>
99
    x, y = normalize(point)
100
    return int(x).to_bytes(48, "big") + int(y).to_bytes(48, "big")

bytes_to_g2

Decode a 192-byte raw G2 point to a py-ecc Point3D.

Validate that field elements are in range and the point is on the curve, and that it is in the prime-order subgroup if subgroup_check is set.

107
@lru_cache(maxsize=128)
def bytes_to_g2(​raw: bytes, ​​subgroup_check: bool​) -> Point3D[FQ2]:
109
    <snip>
116
    if len(raw) != 192:
117
        raise ValueError("G2 point must be 192 bytes")
118
119
    x = bytes_to_fq2(raw[:96])
120
    y = bytes_to_fq2(raw[96:])
121
122
    z: FQ2
123
    if x == FQ2((0, 0)) and y == FQ2((0, 0)):
124
        z = FQ2((0, 0))
125
    else:
126
        z = FQ2((1, 0))
127
128
    point: Point3D[FQ2] = (x, y, z)
129
    if not is_on_curve(point, b2):
130
        raise InvalidInputError("G2 point is not on curve")
131
    if subgroup_check and not is_inf(bls12_multiply(point, curve_order)):
132
        raise InvalidInputError("Subgroup check failed for G2 point")
133
    return point

g2_to_bytes

Encode a py-ecc G2 point to 192 raw bytes.

def g2_to_bytes(​point: Point3D[FQ2]​) -> bytes:
137
    <snip>
138
    x, y = normalize(point)
139
    c0_x, c1_x = x.coeffs
140
    c0_y, c1_y = y.coeffs
141
    return (
142
        int(c0_x).to_bytes(48, "big")
143
        + int(c1_x).to_bytes(48, "big")
144
        + int(c0_y).to_bytes(48, "big")
145
        + int(c1_y).to_bytes(48, "big")
146
    )

g1_add

Add two G1 points.

a and b_point are 96-byte G1 points. Return the 96-byte sum as a G1 point.

def g1_add(​a: bytes, ​​b_point: bytes​) -> bytes:
150
    <snip>
156
    p1 = bytes_to_g1(a)
157
    p2 = bytes_to_g1(b_point)
158
    return g1_to_bytes(bls12_add(p1, p2))

g2_add

Add two G2 points.

a and b_point are 192-byte G2 points. Return the 192-byte sum as a G2 point.

def g2_add(​a: bytes, ​​b_point: bytes​) -> bytes:
162
    <snip>
168
    p1 = bytes_to_g2(a)
169
    p2 = bytes_to_g2(b_point)
170
    return g2_to_bytes(bls12_add(p1, p2))

g1_msm

G1 multi-scalar multiplication with subgroup checks.

points is a sequence of 96-byte G1 points and scalars is a sequence of 32-byte big-endian scalars. Return the 96-byte result as a G1 point.

Raise InvalidInputError if a point is not on the curve or fails the subgroup check.

def g1_msm(​points: Sequence[bytes], ​​scalars: Sequence[bytes]​) -> bytes:
177
    <snip>
187
    result: Point3D[FQ] = Z1
188
    for raw_point, raw_scalar in zip(points, scalars, strict=True):
189
        point = bytes_to_g1(raw_point, subgroup_check=True)
190
191
        m = int.from_bytes(raw_scalar, "big")
192
        product = bls12_multiply(point, m)
193
        result = bls12_add(result, product)
194
195
    return g1_to_bytes(result)

g2_msm

G2 multi-scalar multiplication with subgroup checks.

points is a sequence of 192-byte G2 points and scalars is a sequence of 32-byte big-endian scalars. Return the 192-byte result as a G2 point.

Raise InvalidInputError if a point is not on the curve or fails the subgroup check.

def g2_msm(​points: Sequence[bytes], ​​scalars: Sequence[bytes]​) -> bytes:
202
    <snip>
212
    result: Point3D[FQ2] = Z2
213
    for raw_point, raw_scalar in zip(points, scalars, strict=True):
214
        point = bytes_to_g2(raw_point, subgroup_check=True)
215
216
        m = int.from_bytes(raw_scalar, "big")
217
        product = bls12_multiply(point, m)
218
        result = bls12_add(result, product)
219
220
    return g2_to_bytes(result)

map_fp_to_g1

Map a 48-byte field element to a 96-byte G1 point.

def map_fp_to_g1(​fp: bytes​) -> bytes:
224
    <snip>
227
    if len(fp) != 48:
228
        raise ValueError("field element must be 48 bytes")
229
230
    value = bytes_to_field_element(fp)
231
    g1_point = clear_cofactor_G1(map_to_curve_G1(FQ(value)))
232
233
    return g1_to_bytes(g1_point)

map_fp2_to_g2

Map a 96-byte FP2 element to a 192-byte G2 point.

def map_fp2_to_g2(​fp2: bytes​) -> bytes:
237
    <snip>
240
    g2_point = clear_cofactor_G2(map_to_curve_G2(bytes_to_fq2(fp2)))
241
242
    return g2_to_bytes(g2_point)

pairing_check

Check if the pairing of the given G1 and G2 points is the identity.

Perform on-curve and subgroup checks for each point. g1_points is a sequence of 96-byte G1 points and g2_points is a sequence of 192-byte G2 points.

Raise InvalidInputError if a point is not on the curve or fails the subgroup check.

def pairing_check(​g1_points: Sequence[bytes], ​​g2_points: Sequence[bytes]​) -> bool:
249
    <snip>
259
    result = FQ12.one()
260
    for raw_g1, raw_g2 in zip(g1_points, g2_points, strict=True):
261
        g1_point = bytes_to_g1(raw_g1, subgroup_check=True)
262
        g2_point = bytes_to_g2(raw_g2, subgroup_check=True)
263
264
        result *= pairing(g2_point, g1_point)
265
266
    return result == FQ12.one()