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.
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.
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.
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() |