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

// 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.