You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dateKeys-dart/test/bls12381_test.dart

633 lines
23 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// The arithmetic of BLS12-381 by its properties, on the VM and compiled to
// JavaScript: the field layer against plain BigInt arithmetic, the tower,
// the constants against their definitions, the group laws and the encodings,
// the subgroup checks, the pairing and the hash to G1. They read no file:
// the few Go values they use are in bls12381_constants.dart. The vectors of Go
// are in bls12381_vectors_test.dart.
//
// On Node.js the BigInt of dart2js is about 20 times slower than on the VM,
// so the loops draw fewer cases there ([web]).
library;
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:datekeys/datekeys.dart' show fromHex, toHex;
import 'package:datekeys/src/bls12381_curve.dart';
import 'package:datekeys/src/bls12381_fp.dart';
import 'package:datekeys/src/bls12381_hash.dart';
import 'package:datekeys/src/bls12381_pairing.dart';
import 'package:datekeys/src/bls12381_tower.dart';
import 'package:test/test.dart';
import 'bls12381_constants.dart';
/// Whether the tests run compiled to JavaScript.
final bool web = identical(0, 0.0);
/// [vm] cases on the VM, [js] on the web.
int cases(int vm, int js) => web ? js : vm;
final BigInt p = fpModulus;
final BigInt r = groupOrder;
// A fixed-seed source of field elements, scalars and bytes.
final class Draw {
Draw(int seed) : _r = Random(seed);
final Random _r;
Uint8List bytes(int n) =>
Uint8List.fromList([for (var i = 0; i < n; i++) _r.nextInt(256)]);
BigInt big(int n) => BigInt.parse(toHex(bytes(n)), radix: 16);
Fp fp() => Fp.fromBytesReduced(bytes(64));
Fp2 fp2() => Fp2(fp(), fp());
Fp6 fp6() => Fp6(fp2(), fp2(), fp2());
Fp12 fp12() => Fp12(fp6(), fp6());
/// A scalar in 1..r-1.
BigInt scalar() => big(40) % (r - BigInt.one) + BigInt.one;
/// A point of E(Fp) from a random x: almost never in G1.
G1Point g1OnCurve() {
for (;;) {
final x = fp();
final y = (x.square() * x + G1Point.b).sqrt();
if (y != null) return G1Point.affine(x, y);
}
}
/// A point of E'(Fp2) from a random x: almost never in G2.
G2Point g2OnCurve() {
for (;;) {
final x = fp2();
final y = (x.square() * x + G2Point.b).sqrt();
if (y != null) return G2Point.affine(x, y);
}
}
}
BigInt big(Fp a) => a.toBigInt();
Fp fp(int v) => Fp(BigInt.from(v));
// The product in Fp2 by the definition u² = −1.
Fp2 mulFp2(Fp2 a, Fp2 b) =>
Fp2(a.c0 * b.c0 - a.c1 * b.c1, a.c0 * b.c1 + a.c1 * b.c0);
final Fp2 xi = Fp2(Fp.one, Fp.one);
// The product in Fp6 by the definition v³ = ξ, schoolbook.
Fp6 mulFp6(Fp6 a, Fp6 b) {
final c = List.filled(5, Fp2.zero);
final x = [a.c0, a.c1, a.c2];
final y = [b.c0, b.c1, b.c2];
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
c[i + j] = c[i + j] + mulFp2(x[i], y[j]);
}
}
return Fp6(c[0] + mulFp2(c[3], xi), c[1] + mulFp2(c[4], xi), c[2]);
}
final Fp6 v6 = Fp6(Fp2.zero, Fp2.one, Fp2.zero);
// The product in Fp12 by the definition w² = v, schoolbook.
Fp12 mulFp12(Fp12 a, Fp12 b) => Fp12(
mulFp6(a.c0, b.c0) + mulFp6(mulFp6(a.c1, b.c1), v6),
mulFp6(a.c0, b.c1) + mulFp6(a.c1, b.c0),
);
void main() {
group('Fp, the field layer', () {
test('p and r are those of spec §12.2, and p = 3 mod 4', () {
expect(
p.toRadixString(16),
'1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241ea'
'bfffeb153ffffb9feffffffffaaab',
);
expect(
r.toRadixString(16),
'73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001',
);
expect(p % BigInt.from(4), BigInt.from(3));
});
test('computes as BigInt arithmetic modulo p', () {
final d = Draw(1);
for (var i = 0; i < cases(200, 20); i++) {
final a = d.fp();
final b = d.fp();
final c = d.fp();
final e = d.fp();
expect(big(a + b), (big(a) + big(b)) % p);
expect(big(a - b), (big(a) - big(b)) % p);
expect(big(-a), (-big(a)) % p);
expect(big(a * b), (big(a) * big(b)) % p);
expect(big(a.square()), (big(a) * big(a)) % p);
expect(big(a.double()), (big(a) * BigInt.two) % p);
expect(
big(Fp.mulSub(a, b, c, e)),
(big(a) * big(b) - big(c) * big(e)) % p,
);
expect(
big(Fp.mulAdd(a, b, c, e)),
(big(a) * big(b) + big(c) * big(e)) % p,
);
expect((a * a.inverse()).isOne, isTrue);
expect(a.pow(BigInt.from(5)).equals(a * a * a * a * a), isTrue);
}
expect(Fp.zero.inverse().isZero, isTrue);
expect((-Fp.zero).isZero, isTrue);
expect((Fp.half + Fp.half).isOne, isTrue);
});
test('takes square roots exactly of the squares', () {
final d = Draw(2);
var squares = 0;
for (var i = 0; i < cases(60, 8); i++) {
final a = d.fp();
final legendre = big(a).modPow((p - BigInt.one) >> 1, p);
final s = a.sqrt();
expect(s != null, legendre == BigInt.one, reason: '$i');
if (s != null) {
expect(s.square().equals(a), isTrue);
squares++;
} else {
// The power is then a root of −a.
final (t, isSquare) = a.sqrtOrNegatedRoot();
expect(isSquare, isFalse);
expect(t.square().equals(-a), isTrue);
}
}
expect(squares, inInclusiveRange(1, cases(59, 7)));
expect(Fp.zero.sqrt()!.isZero, isTrue);
});
test('reads and writes 48 big-endian bytes, never reducing', () {
final d = Draw(3);
for (var i = 0; i < 20; i++) {
final a = d.fp();
expect(Fp.fromBytes(a.toBytes())!.equals(a), isTrue);
}
Uint8List be(BigInt v) => fromHex(v.toRadixString(16).padLeft(96, '0'));
expect(Fp.fromBytes(be(p - BigInt.one))!.toBigInt(), p - BigInt.one);
expect(Fp.fromBytes(be(p)), isNull);
expect(Fp.fromBytes(be(p + BigInt.one)), isNull);
expect(Fp.fromBytes(Uint8List(48)..fillRange(0, 48, 0xff)), isNull);
expect(() => Fp.fromBytes(Uint8List(47)), throwsArgumentError);
expect(Fp.fromBytesReduced(be(p + BigInt.two)).toBigInt(), BigInt.two);
expect(() => Fp(p), throwsArgumentError);
expect(() => Fp(-BigInt.one), throwsArgumentError);
});
test('the sign of spec §12.2 turns at (p − 1)/2', () {
final half = (p - BigInt.one) >> 1;
expect(Fp(half).isLexicographicallyLargest, isFalse);
expect(Fp(half + BigInt.one).isLexicographicallyLargest, isTrue);
expect(Fp.zero.isLexicographicallyLargest, isFalse);
expect(fp(3).isOdd, isTrue);
expect(fp(4).isOdd, isFalse);
});
});
group('the tower', () {
test('Fp2, Fp6 and Fp12 multiply as their definitions', () {
final d = Draw(4);
for (var i = 0; i < cases(20, 3); i++) {
final a2 = d.fp2();
final b2 = d.fp2();
expect((a2 * b2).equals(mulFp2(a2, b2)), isTrue);
expect(a2.square().equals(a2 * a2), isTrue);
expect((a2 * a2.inverse()).isOne, isTrue);
expect(a2.mulByNonResidue().equals(a2 * xi), isTrue);
final a6 = d.fp6();
final b6 = d.fp6();
expect((a6 * b6).equals(mulFp6(a6, b6)), isTrue);
expect(a6.square().equals(a6 * a6), isTrue);
expect((a6 * a6.inverse()).isOne, isTrue);
expect(a6.mulByNonResidue().equals(a6 * v6), isTrue);
final x0 = d.fp2();
final x1 = d.fp2();
expect(a6.mul01(x0, x1).equals(a6 * Fp6(x0, x1, Fp2.zero)), isTrue);
expect(a6.mul1(x1).equals(a6 * Fp6(Fp2.zero, x1, Fp2.zero)), isTrue);
final a12 = d.fp12();
final b12 = d.fp12();
expect((a12 * b12).equals(mulFp12(a12, b12)), isTrue);
expect(a12.square().equals(a12 * a12), isTrue);
expect((a12 * a12.inverse()).isOne, isTrue);
final x4 = d.fp2();
final sparse = Fp12(Fp6(x0, x1, Fp2.zero), Fp6(Fp2.zero, x4, Fp2.zero));
expect(a12.mul014(x0, x1, x4).equals(a12 * sparse), isTrue);
}
});
test('takes square roots in Fp2 exactly of the squares', () {
final d = Draw(5);
for (var i = 0; i < cases(30, 4); i++) {
final a = d.fp2();
final sq = a.square();
final s = sq.sqrt()!;
expect(s.square().equals(sq), isTrue);
// ξ = 1 + u is not a square: neither is ξ·a².
expect((sq * xi).sqrt(), isNull);
}
// Elements of Fp: a square, and a non-square whose root is in u·Fp.
final four = Fp2(fp(4), Fp.zero);
expect(four.sqrt()!.square().equals(four), isTrue);
final minusFour = Fp2(-fp(4), Fp.zero);
final root = minusFour.sqrt()!;
expect(root.c0.isZero, isTrue);
expect(root.square().equals(minusFour), isTrue);
expect(Fp2.zero.sqrt()!.isZero, isTrue);
});
test('the Frobenius coefficients are ξ^((p^k − 1)/6)', () {
for (var k = 1; k <= 3; k++) {
final e = (p.pow(k) - BigInt.one) ~/ BigInt.from(6);
expect(xi.pow(e).equals(frobeniusGammas[k - 1]), isTrue, reason: '$k');
}
});
test('the Frobenius map of Fp12 is a ring morphism of order 12', () {
final d = Draw(6);
final a = d.fp12();
final b = d.fp12();
for (var k = 1; k <= 3; k++) {
expect(
(a * b).frobenius(k).equals(a.frobenius(k) * b.frobenius(k)),
isTrue,
reason: '$k',
);
}
expect(a.frobenius(1).frobenius(1).equals(a.frobenius(2)), isTrue);
expect(a.frobenius(2).frobenius(1).equals(a.frobenius(3)), isTrue);
var x = a;
for (var i = 0; i < 12; i++) {
x = x.frobenius(1);
}
expect(x.equals(a), isTrue);
// On an element of Fp2, it is the conjugation.
final c = d.fp2();
final f = Fp12(Fp6(c, Fp2.zero, Fp2.zero), Fp6.zero);
expect(f.frobenius(1).c0.c0.equals(c.conjugate()), isTrue);
// And it is the p-th power: (a^p)·a^-p = 1 needs a^p, which the
// pairing vectors of Go check through the final exponentiation.
});
test('squares in the cyclotomic subgroup as in the field', () {
final d = Draw(7);
for (var i = 0; i < cases(5, 2); i++) {
// The easy part of the final exponentiation lands in the subgroup.
final f = d.fp12();
var g = f.conjugate() * f.inverse();
g = g.frobenius(2) * g;
expect(g.cyclotomicSquare().equals(g.square()), isTrue);
expect((g * g.conjugate()).isOne, isTrue);
}
});
test('GT is written c1 before c0 at every level', () {
final e = [for (var i = 1; i <= 12; i++) fp(i)];
final f = Fp12(
Fp6(Fp2(e[0], e[1]), Fp2(e[2], e[3]), Fp2(e[4], e[5])),
Fp6(Fp2(e[6], e[7]), Fp2(e[8], e[9]), Fp2(e[10], e[11])),
);
final b = f.toBytes();
expect(b, hasLength(576));
// c1 of Fp12: c2, c1, c0 of Fp6, each c1 then c0 of Fp2.
expect(
[for (var i = 0; i < 12; i++) b[48 * i + 47]],
[
12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, //
],
);
});
});
group('the curves', () {
test('the generators are those of Go, on their curves and in their '
'subgroups', () {
expect(toHex(G1Point.generator.toBytes()), generatorG1);
expect(toHex(G2Point.generator.toBytes()), generatorG2);
expect(
G1Point.decode(fromHex(generatorG1))!.equals(G1Point.generator),
isTrue,
);
expect(
G2Point.decode(fromHex(generatorG2))!.equals(G2Point.generator),
isTrue,
);
final (x1, y1) = G1Point.generator.toAffine()!;
final (x2, y2) = G2Point.generator.toAffine()!;
expect(G1Point.isOnCurve(x1, y1), isTrue);
expect(G2Point.isOnCurve(x2, y2), isTrue);
expect(G1Point.generator.multiply(r).isInfinity, isTrue);
expect(G2Point.generator.multiply(r).isInfinity, isTrue);
});
test('add, double and multiply as a group of order r', () {
final d = Draw(8);
for (var i = 0; i < cases(4, 1); i++) {
final a = d.scalar();
final b = d.scalar();
final g1 = G1Point.generator;
final g2 = G2Point.generator;
expect(
(g1.multiply(a) + g1.multiply(b)).equals(g1.multiply((a + b) % r)),
isTrue,
);
expect(
(g2.multiply(a) + g2.multiply(b)).equals(g2.multiply((a + b) % r)),
isTrue,
);
final p1 = g1.multiply(a);
final p2 = g2.multiply(b);
expect((p1 + p1).equals(p1.double()), isTrue);
expect((p2 + p2).equals(p2.double()), isTrue);
expect((p1 + -p1).isInfinity, isTrue);
expect((p2 + -p2).isInfinity, isTrue);
expect(p1.multiply(r + BigInt.one).equals(p1), isTrue);
expect(p2.multiply(BigInt.zero).isInfinity, isTrue);
final (ax, ay) = p1.toAffine()!;
expect(g1.addAffine(ax, ay).equals(g1 + p1), isTrue);
final (bx, by) = p2.toAffine()!;
expect(g2.addAffine(bx, by).equals(g2 + p2), isTrue);
}
expect(
(G1Point.infinity + G1Point.generator).equals(G1Point.generator),
isTrue,
);
expect(G1Point.infinity.double().isInfinity, isTrue);
expect(
() => G1Point.generator.multiply(-BigInt.one),
throwsArgumentError,
);
});
test('encode and decode the canonical encodings only', () {
final d = Draw(9);
final p1 = G1Point.generator.multiply(d.scalar());
final p2 = G2Point.generator.multiply(d.scalar());
for (final (group, b, point) in [
(BlsGroup.g1, p1.toBytes(), p1 as Object),
(BlsGroup.g2, p2.toBytes(), p2 as Object),
]) {
expect(checkCompressedPoint(group, b), PointVerdict.point);
final decoded = group == BlsGroup.g1
? G1Point.decode(b)
: G2Point.decode(b);
expect(
decoded is G1Point
? decoded.equals(point as G1Point)
: (decoded! as G2Point).equals(point as G2Point),
isTrue,
);
// The negation flips the sign bit only.
final neg = Uint8List.fromList(b)..[0] ^= 0x20;
final negated = group == BlsGroup.g1
? (-(point as G1Point)).toBytes()
: (-(point as G2Point)).toBytes();
expect(negated, neg);
// Not canonical: the compression flag cleared, the infinity flag
// set, another length, x + p in the first coordinate.
expect(
checkCompressedPoint(group, Uint8List.fromList(b)..[0] ^= 0x80),
PointVerdict.invalid,
);
expect(
checkCompressedPoint(group, Uint8List.fromList(b)..[0] |= 0x40),
PointVerdict.invalid,
);
expect(checkCompressedPoint(group, b.sublist(1)), PointVerdict.invalid);
expect(checkCompressedPoint(group, [...b, 0]), PointVerdict.invalid);
final raw = Uint8List.fromList(b)..[0] &= 0x1f;
final x = BigInt.parse(toHex(raw.sublist(0, 48)), radix: 16) + p;
if (x.bitLength <= 381) {
final plusP = fromHex(x.toRadixString(16).padLeft(96, '0'));
plusP[0] |= b[0] & 0xe0;
expect(
checkCompressedPoint(group, [...plusP, ...b.sublist(48)]),
PointVerdict.invalid,
);
}
}
for (final group in BlsGroup.values) {
final n = group.byteLength;
final infinity = Uint8List(n)..[0] = 0xc0;
expect(checkCompressedPoint(group, infinity), PointVerdict.identity);
expect(
checkCompressedPoint(group, Uint8List.fromList(infinity)..[0] = 0xe0),
PointVerdict.invalid,
);
expect(
checkCompressedPoint(
group,
Uint8List.fromList(infinity)..[n - 1] = 1,
),
PointVerdict.invalid,
);
expect(checkCompressedPoint(group, Uint8List(n)), PointVerdict.invalid);
}
expect(toHex(G1Point.infinity.toBytes()), 'c0${'00' * 47}');
expect(toHex(G2Point.infinity.toBytes()), 'c0${'00' * 95}');
});
test('reject the points of the curve outside the subgroup, torsion '
'added to a point of the subgroup included', () {
final d = Draw(10);
for (var i = 0; i < cases(3, 1); i++) {
final q1 = d.g1OnCurve();
expect(q1.isInSubgroup, isFalse);
expect(
checkCompressedPoint(BlsGroup.g1, q1.toBytes()),
PointVerdict.invalid,
);
// [r]·Q is a point of order dividing the cofactor.
final t1 = q1.multiply(r);
expect(t1.isInfinity, isFalse);
final s1 = G1Point.generator.multiply(d.scalar()) + t1;
expect(s1.isInSubgroup, isFalse);
expect(
checkCompressedPoint(BlsGroup.g1, s1.toBytes()),
PointVerdict.invalid,
);
final q2 = d.g2OnCurve();
expect(q2.isInSubgroup, isFalse);
expect(
checkCompressedPoint(BlsGroup.g2, q2.toBytes()),
PointVerdict.invalid,
);
final t2 = q2.multiply(r);
final s2 = G2Point.generator.multiply(d.scalar()) + t2;
expect(s2.isInSubgroup, isFalse);
expect(
checkCompressedPoint(BlsGroup.g2, s2.toBytes()),
PointVerdict.invalid,
);
}
});
test('the subgroup check of G2 by ψ decides as [r]·P = O, torsion of '
'every small order of the cofactor included', () {
final xi = Fp2(Fp.one, Fp.one);
expect(
xi.pow((p - BigInt.one) ~/ BigInt.from(3)).inverse().equals(psiX),
isTrue,
);
expect(
xi.pow((p - BigInt.one) ~/ BigInt.two).inverse().equals(psiY),
isTrue,
);
final g = G2Point.generator;
final absX = BigInt.parse('d201000000010000', radix: 16);
expect(g.psi().equals(-g.multiply(absX)), isTrue);
// The cofactor of G2 (cofactorG2 of kilic), and its prime factors
// below 2^21: 13², 23², 2713, 11953 and 262069.
final h2 = BigInt.parse(
'5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628'
'f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5',
radix: 16,
);
final small = [13, 23, 2713, 11953, 262069];
for (final f in small) {
expect(h2 % BigInt.from(f), BigInt.zero);
}
final d = Draw(13);
final points = <G2Point>[];
for (var i = 0; i < cases(3, 1); i++) {
final q = d.g2OnCurve();
// r·h2 is the order of E'(Fp2).
expect(q.multiply(r * h2).isInfinity, isTrue);
final s = g.multiply(d.scalar());
points.addAll([q, s, s + q.multiply(r)]);
// A point of each small order of the cofactor, and the point of the
// subgroup plus it.
for (final f in web ? small.take(2) : small) {
final t = q.multiply(r * h2 ~/ BigInt.from(f));
if (!t.isInfinity) points.addAll([t, s + t]);
}
}
var outside = 0;
for (final q in points) {
final naive = q.multiply(r).isInfinity;
expect(q.isInSubgroup, naive);
if (!naive) outside++;
}
expect(outside, greaterThanOrEqualTo(points.length * 2 ~/ 3));
});
});
group('the pairing', () {
test('e(G1, G2) gives the H2 of tlock_ibe.json', () {
// H2 of the IBE of tlock: SHA-256("IBE-H2" || GT), truncated.
final gt = pairing(G1Point.generator, G2Point.generator);
final h2 = sha256.convert([...'IBE-H2'.codeUnits, ...gt.toBytes()]);
expect(toHex(h2.bytes.sublist(0, 16)), h2OfGenerators);
});
test('is bilinear and non-degenerate', () {
final d = Draw(11);
final p1 = G1Point.generator.multiply(d.scalar());
final q2 = G2Point.generator.multiply(d.scalar());
final e = pairing(p1, q2);
expect(e.isOne, isFalse);
expect(pairing(p1.double(), q2).equals(e.square()), isTrue);
expect(pairing(p1, q2.double() + q2).equals(e.square() * e), isTrue);
expect(pairing(-p1, q2).equals(e.conjugate()), isTrue);
expect(pairing(G1Point.infinity, q2).isOne, isTrue);
expect(pairing(p1, G2Point.infinity).isOne, isTrue);
expect(pairingCheck([(p1, q2), (-p1, q2)]), isTrue);
expect(pairingCheck([(p1.double(), q2), (-p1, q2.double())]), isTrue);
expect(pairingCheck([(p1, q2), (p1, q2)]), isFalse);
expect(pairingCheck([(G1Point.infinity, q2)]), isTrue);
});
test('lands in the subgroup of order r of the cyclotomic subgroup', () {
final e = pairing(G1Point.generator, G2Point.generator);
var x = Fp12.one;
final bits = r.toRadixString(2);
for (var i = 0; i < bits.length; i++) {
x = x.cyclotomicSquare();
if (bits[i] == '1') x = x * e;
}
expect(x.isOne, isTrue);
});
test('runs the Miller loop over |x| = 0xd201000000010000', () {
expect(
BigInt.parse('1$millerLoopBits', radix: 2).toRadixString(16),
'd201000000010000',
);
});
});
group('the hash to G1', () {
test('maps to E′, which the isogeny maps to E, and lands in G1', () {
final d = Draw(12);
final a = Fp.hex(
'00144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac9893'
'6f8da0e0f97f5cf428082d584c1d',
);
final b = Fp.hex(
'12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23'
'215a316ceaa5d1cc48e98e172be0',
);
for (var i = 0; i < cases(10, 2); i++) {
final u = d.fp();
final (x, y) = mapToIsogenousCurve(u);
expect(y.square().equals((x.square() + a) * x + b), isTrue);
expect(y.isOdd, u.isOdd);
final (ix, iy) = isogenyMap(x, y);
expect(G1Point.isOnCurve(ix, iy), isTrue);
}
// The exceptional u = 0: x1 = B/(Z·A).
final (x0, _) = mapToIsogenousCurve(Fp.zero);
expect(x0.equals(b * (fp(11) * a).inverse()), isTrue);
expect(isogenyConstants.map((k) => k.length), [12, 11, 16, 16]);
expect(isogenyConstants[1].last.isOne, isTrue);
expect(isogenyConstants[3].last.isOne, isTrue);
});
test('hashes as Go, for the DST of Quicknet and of RFC 9380', () {
// The identity of round 1000: SHA-256 of its 8 big-endian bytes.
final id = sha256.convert([0, 0, 0, 0, 0, 0, 0x03, 0xe8]).bytes;
final p1 = hashToG1(id, quicknetDst);
expect(toHex(p1.toBytes()), hashOfRound1000);
expect(p1.isInSubgroup, isTrue);
final (x, y) = hashToG1(
'abc'.codeUnits,
'QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_',
).toAffine()!;
expect(
[toHex(x.toBytes()), toHex(y.toBytes())],
[hashOfAbcX, hashOfAbcY],
);
});
test('expand_message_xmd checks its lengths', () {
expect(expandMessageXmd([], 'DST'.codeUnits, 0), isEmpty);
expect(expandMessageXmd([], 'DST'.codeUnits, 8160), hasLength(8160));
expect(
() => expandMessageXmd([], 'DST'.codeUnits, 8161),
throwsArgumentError,
);
expect(
() => expandMessageXmd([], List.filled(256, 0x41), 32),
throwsArgumentError,
);
// The length is an input of b_0: no output is a prefix of a longer
// one.
final a = expandMessageXmd([1, 2], 'DST'.codeUnits, 32);
final b = expandMessageXmd([1, 2], 'DST'.codeUnits, 64);
expect(a, isNot(b.sublist(0, 32)));
});
});
}

Powered by TurnKey Linux.