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

115 lines
4.0 KiB

Stage 7b: the author keys and Ed25519 signing ed25519_sign.dart signs as crypto_sign of TweetNaCl in its JavaScript port, with the SHA-512 of package:crypto: the field of curve25519.dart, whose arithmetic is private there, copied with the product as a loop, and modL over 64 limbs of 8 bits in a Float64List, with floor divisions in place of the shifts of TweetNaCl, exact on the VM and on the web. The secret scalar and the nonce never meet a BigInt or a branch; neither platform promises constant time, and the values are wiped as a best effort. authorkey.dart ports package authorkey of datekeys-go: AuthorKey with generate, fromSeed, publicKey, sign, clear and secret, and a toString that hides it; authorPublicString, parseAuthorPublic and parseAuthorSecret, also on the bytes of a Go string; marshalAuthorKey; encryptAuthorKey, scrypt with logN 16 through ScryptRecipient and ageEncrypt of stage 6a; and readAuthorKey, through the age reader with a maximum work factor of 16, whose lines are those of bufio.Scanner and strings.TrimSpace. Every error has the text of Go, with the sets of go_unicode.dart for the case of a string and the spaces of a line. A cleared key refuses every use, where Go would give the values of a key of zeros. tool/authorkey_go_vectors.go writes test/vectors/authorkey.json: the signatures of crypto/ed25519 over lines of sign.input (RFC 8032 tests 1, 2, 3 and 1024), TEST SHA(abc), seeded seeds and messages up to 1 MiB and other public keys; the scalars of math/big; and Generate, Encrypt, ParsePublic, ParseSecret and Read of authorkey with each text, while crypto/rand reads the keystream of SeededRandomSource. Dart writes the same bytes and gives the same texts in every case; authorkey.g.dart, a part of it, runs also in Node.js. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 days ago
// The author keys and the Ed25519 signing against Go, also compiled to
// JavaScript: the part of test/vectors/authorkey.json in authorkey.g.dart
// (see authorkey_support.dart), and the behaviour of a key that the vectors
// do not show: a cleared key, the hidden secret and keys of random seeds.
import 'dart:convert';
import 'dart:typed_data';
import 'package:datekeys/src/authorkey.dart';
import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/curve25519.dart' show ed25519Order, verifyStrict;
import 'package:datekeys/src/ed25519_sign.dart';
import 'package:datekeys/src/random.dart' show randomBytes;
import 'package:test/test.dart';
import 'authorkey_support.dart';
import 'vectors/authorkey.g.dart';
final Json vectors = jsonDecode(authorKeyJson) as Json;
BigInt le(List<int> b) {
var n = BigInt.zero;
for (var i = b.length - 1; i >= 0; i--) {
n = (n << 8) | BigInt.from(b[i]);
}
return n;
}
void main() {
signTests(listOf(vectors['sign']));
authorKeyTests(vectors);
test('the scalars modulo ℓ against BigInt, at random', () {
final r = seeded('scalars');
for (var i = 0; i < 200; i++) {
final x = randomBytes(r, 64);
// Some with long runs of 0xff or of zeros.
if (i % 3 == 1) x.fillRange(0, i % 64, 0xff);
if (i % 3 == 2) x.fillRange(i % 64, 64, 0);
expect(le(ed25519ReduceScalar(x)), le(x) % ed25519Order);
final a = randomBytes(r, 32), b = randomBytes(r, 32);
final c = randomBytes(r, 32);
expect(
le(ed25519MulAdd(a, b, c)),
(le(a) * le(b) + le(c)) % ed25519Order,
);
}
});
test('a key signs what verifyStrict accepts, and nothing else', () {
final r = seeded('sign and verify');
for (var i = 0; i < 16; i++) {
final key = AuthorKey.generate(r);
final msg = randomBytes(r, i * 7);
final sig = key.sign(msg);
expect(verifyStrict(key.publicKey, msg, sig), isTrue);
final other = [...msg, 0];
expect(verifyStrict(key.publicKey, other, sig), isFalse);
// The strings and the file give the same key back.
expect(parseAuthorPublic(key.publicString), key.publicKey);
expect(parseAuthorSecret(key.secret).publicKey, key.publicKey);
expect(readAuthorKey(marshalAuthorKey(key)).secret, key.secret);
}
});
test('a cleared key refuses every use', () {
final key = AuthorKey.fromSeed(List.filled(32, 7));
expect(key.isCleared, isFalse);
key.clear();
expect(key.isCleared, isTrue);
expect(() => key.publicKey, throwsStateError);
expect(() => key.sign(const [1]), throwsStateError);
expect(() => key.secret, throwsStateError);
expect(() => key.publicString, throwsStateError);
expect(() => marshalAuthorKey(key), throwsStateError);
expect(() => encryptAuthorKey(key, 'p'), throwsStateError);
expect('$key', 'DKAUTHOR-SECRET-KEY-1… (hidden)');
});
test('the secret never shows in an interpolation', () {
final key = AuthorKey.fromSeed(List.filled(32, 9));
expect('$key'.contains(key.secret.substring(21)), isFalse);
expect('${[key]}', '[DKAUTHOR-SECRET-KEY-1… (hidden)]');
});
test('a seed is copied, and wiping it leaves the key', () {
final seed = Uint8List.fromList(List.filled(32, 3));
final key = AuthorKey.fromSeed(seed);
final pub = key.publicKey;
seed.fillRange(0, 32, 0);
expect(key.publicKey, pub);
expect(key.publicKey, isNot(same(key.publicKey)));
});
test('the arguments of the scalar functions', () {
expect(() => ed25519ReduceScalar(Uint8List(63)), throwsArgumentError);
expect(
() => ed25519MulAdd(Uint8List(32), Uint8List(31), Uint8List(32)),
throwsArgumentError,
);
expect(() => ed25519PublicKey(Uint8List(31)), throwsArgumentError);
expect(
() => ed25519Sign(Uint8List(32), Uint8List(33), const []),
throwsArgumentError,
);
});
test('ℓ is the order of Go', () {
expect(
le(fromHex((vectors['scalars']! as Json)['order']! as String)),
ed25519Order,
);
});
}

Powered by TurnKey Linux.