ADR 0017's step 1, on libsodium (sodium 4.1.0+1, whose build hook compiles the libsodium 1.0.22 it ships: a C compiler and make wherever it builds): - Argon2id (64 MiB, 3 passes, kept with the account and never below that) makes a master key, which libsodium's KDF splits into SRP's secret (x) and the key-encryption key. - The account's X25519 and Ed25519 key pairs, their private halves wrapped with XChaCha20-Poly1305 under the KEK and under the recovery key, bound to their public keys. - A 24-word BIP-39 recovery phrase (the English list, MIT) and the recovery key derived from it. - Data keys sealed to the account with crypto_box_seal, bound to their IDs; content in ADR 0002's envelope, encrypted with a data key and signed with Ed25519 (detached), bound to a context the caller names. - Secrets in libsodium's guarded memory where its API takes them. - Tests: libsodium's Argon2id known answers, BIP-39's English vectors, round trips and every refusal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
90 lines
3.4 KiB
Dart
90 lines
3.4 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:sodium/sodium_sumo.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
/// [hex] as bytes.
|
|
Uint8List _bytes(String hex) => Uint8List.fromList([
|
|
for (var i = 0; i < hex.length; i += 2)
|
|
int.parse(hex.substring(i, i + 2), radix: 16),
|
|
]);
|
|
|
|
/// [bytes] in lower-case hex.
|
|
String _hex(List<int> bytes) =>
|
|
bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
|
|
|
|
Future<void> main() async {
|
|
final sodium = await SodiumSumoInit.init();
|
|
|
|
// libsodium's own known answers for crypto_pwhash with Argon2id
|
|
// (libsodium 1.0.18, test/default/pwhash_argon2id.c's tv(), vectors 0
|
|
// and 6, and their lines 1 and 7 of pwhash_argon2id.exp): the password
|
|
// and salt as the test cuts them (the password to its length, the salt
|
|
// to 16 bytes), the output length, the passes and the memory.
|
|
const vectors =
|
|
<
|
|
({
|
|
String password,
|
|
int length,
|
|
String salt,
|
|
int outLen,
|
|
int opsLimit,
|
|
int memLimit,
|
|
String expected,
|
|
})
|
|
>[
|
|
(
|
|
password:
|
|
'a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffa'
|
|
'e065577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02'
|
|
'c08da232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de'
|
|
'97d6f58d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6',
|
|
length: 127,
|
|
salt: '5541fbc995d5c197ba290346d2c559de',
|
|
outLen: 155,
|
|
opsLimit: 5,
|
|
memLimit: 7256678,
|
|
expected:
|
|
'18acec5d6507739f203d1f5d9f1d862f7c2cdac4f19d2bdff64487e60d969e3c'
|
|
'ed615337b9eec6ac4461c6ca07f0939741e57c24d0005c7ea171a0ee1e734824'
|
|
'9d135b38f222e4dad7b9a033ed83f5ca27277393e316582033c74affe2566a2b'
|
|
'ea47f91f0fd9fe49ece7e1f79f3ad6e9b23e0277c8ecc4b313225748dd2a80f5'
|
|
'679534a0700e246a79a49b3f74eb89ec6205fe1eeb941c73b1fcf1',
|
|
),
|
|
(
|
|
password:
|
|
'b540beb016a5366524d4605156493f9874514a5aa58818cd0c6dfffaa9e90205'
|
|
'f17b',
|
|
length: 34,
|
|
salt: '44071f6d181561670bda728d43fb79b4',
|
|
outLen: 231,
|
|
opsLimit: 1,
|
|
memLimit: 1631659,
|
|
expected:
|
|
'7fb72409b0987f8190c3729710e98c3f80c5a8727d425fdcde7f3644d467fe97'
|
|
'3f5b5fee683bd3fce812cb9ae5e9921a2d06c2f1905e4e839692f2b934b682f1'
|
|
'1a2fe2b90482ea5dd234863516dba6f52dc0702d324ec77d860c2e181f84472b'
|
|
'd7104fedce071ffa93c5309494ad51623d214447a7b2b1462dc7d5d55a1f6fd5'
|
|
'b54ce024118d86f0c6489d16545aaa87b6689dad9f2fb47fda9894f8e12b87d9'
|
|
'78b483ccd4cc5fd9595cdc7a818452f915ce2f7df95ec12b1c72e3788d473441'
|
|
'd884f9748eb14703c21b45d82fd667b85f5b2d98c13303b3fe76285531a826b6'
|
|
'fc0fe8e3dddecf',
|
|
),
|
|
];
|
|
|
|
for (final (index, vector) in vectors.indexed) {
|
|
test("Argon2id gives libsodium's known answer $index", () {
|
|
final key = sodium.crypto.pwhash.callRaw(
|
|
outLen: vector.outLen,
|
|
password: Int8List.fromList(_bytes(vector.password)),
|
|
salt: _bytes(vector.salt),
|
|
opsLimit: vector.opsLimit,
|
|
memLimit: vector.memLimit,
|
|
alg: CryptoPwhashAlgorithm.argon2id13,
|
|
);
|
|
expect(_bytes(vector.password), hasLength(vector.length));
|
|
expect(_hex(key.extractBytes()), vector.expected);
|
|
key.dispose();
|
|
});
|
|
}
|
|
}
|