Skip to content

Commit 3e06d95

Browse files
authored
test(security): Phase 4 audit — test coverage + 11 impl fixes (#986)
1 parent 6870c90 commit 3e06d95

22 files changed

Lines changed: 1788 additions & 219 deletions

example/src/hooks/useTestsList.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import '../tests/hkdf/hkdf_tests';
1919
import '../tests/hmac/hmac_tests';
2020
import '../tests/jose/jose';
2121
import '../tests/keys/create_keys';
22+
import '../tests/keys/cross_impl_verify';
2223
import '../tests/keys/ed_keyobject';
2324
import '../tests/keys/generate_key';
2425
import '../tests/keys/generate_keypair';

example/src/tests/argon2/argon2_tests.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { assert } from 'chai';
44

55
const SUITE = 'argon2';
66

7-
// RFC 9106 test vector for argon2id
7+
// RFC 9106 §5 official test vectors (32-byte tags). The (P, S, K, X, t, m,
8+
// p, T, v) tuple is identical across the three modes; only the variant and
9+
// resulting tag differ.
10+
//
11+
// https://www.rfc-editor.org/rfc/rfc9106#section-5
812
const RFC_PARAMS = {
913
message: Buffer.from(
1014
'0101010101010101010101010101010101010101010101010101010101010101',
@@ -20,10 +24,56 @@ const RFC_PARAMS = {
2024
version: 0x13,
2125
};
2226

23-
test(SUITE, 'argon2Sync: argon2id produces expected output', () => {
24-
const result = argon2Sync('argon2id', RFC_PARAMS);
25-
assert.isOk(result);
27+
const RFC_9106_TAGS = {
28+
argon2d: '512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb',
29+
argon2i: 'c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8',
30+
argon2id: '0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659',
31+
} as const;
32+
33+
test(SUITE, 'argon2Sync: argon2id matches RFC 9106 §5 KAT', () => {
34+
// Pass `version: 0x13` explicitly (matches RFC_PARAMS) so a future change
35+
// to the native binding's default version cannot silently break the KAT.
36+
const result = argon2Sync('argon2id', { ...RFC_PARAMS, version: 0x13 });
37+
assert.strictEqual(result.length, 32);
38+
assert.strictEqual(
39+
Buffer.from(result).toString('hex'),
40+
RFC_9106_TAGS.argon2id,
41+
);
42+
});
43+
44+
test(SUITE, 'argon2Sync: argon2i matches RFC 9106 §5 KAT', () => {
45+
const result = argon2Sync('argon2i', { ...RFC_PARAMS, version: 0x13 });
46+
assert.strictEqual(result.length, 32);
47+
assert.strictEqual(
48+
Buffer.from(result).toString('hex'),
49+
RFC_9106_TAGS.argon2i,
50+
);
51+
});
52+
53+
test(SUITE, 'argon2Sync: argon2d matches RFC 9106 §5 KAT', () => {
54+
const result = argon2Sync('argon2d', { ...RFC_PARAMS, version: 0x13 });
2655
assert.strictEqual(result.length, 32);
56+
assert.strictEqual(
57+
Buffer.from(result).toString('hex'),
58+
RFC_9106_TAGS.argon2d,
59+
);
60+
});
61+
62+
test(SUITE, 'argon2: async argon2id matches RFC 9106 §5 KAT', () => {
63+
return new Promise<void>((resolve, reject) => {
64+
argon2('argon2id', { ...RFC_PARAMS, version: 0x13 }, (err, result) => {
65+
try {
66+
assert.isNull(err);
67+
assert.strictEqual(
68+
Buffer.from(result).toString('hex'),
69+
RFC_9106_TAGS.argon2id,
70+
);
71+
resolve();
72+
} catch (e) {
73+
reject(e);
74+
}
75+
});
76+
});
2777
});
2878

2979
test(SUITE, 'argon2Sync: argon2i produces output', () => {

example/src/tests/blake3/blake3_tests.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,87 @@ test(SUITE, 'blake3 - empty context throws', () => {
339339
/context must be a non-empty string/,
340340
);
341341
});
342+
343+
// --- Phase 4.2: official BLAKE3 keyed_hash + derive_key KAT vectors ---
344+
//
345+
// Source: https://github.com/BLAKE3-team/BLAKE3/blob/master/test_vectors/test_vectors.json
346+
//
347+
// Each test_vectors.json case lists the same input bytes hashed in three
348+
// modes (hash, keyed_hash, derive_key). The pre-existing tests above only
349+
// exercised mode 1 (hash) against the official outputs — modes 2 and 3
350+
// produced different bytes per construction but were never pinned to the
351+
// published KAT outputs.
352+
//
353+
// The input is an N-byte prefix of the repeating sequence (0, 1, 2, ...,
354+
// 250, 0, 1, ...) per the file's `_comment` field. The keyed_hash mode key
355+
// is the 32-byte ASCII string `whats the Elvish word for friend`. The
356+
// derive_key mode context is the ASCII string
357+
// `BLAKE3 2019-12-27 16:29:52 test vectors context`. Implementations are
358+
// expected to produce extended output but match the first 32 bytes against
359+
// their default-length output — that's what we verify here.
360+
const BLAKE3_KAT_KEY = new TextEncoder().encode(
361+
'whats the Elvish word for friend',
362+
);
363+
// The KAT key is 32 ASCII bytes; assert at module load so a future Unicode
364+
// contamination of the source string can't silently shift every keyed_hash
365+
// expected output by 1+ bytes. BLAKE3 keys must be exactly 32 bytes
366+
// (`KEYED_HASH_KEY_LEN`) — anything else is a different MAC.
367+
if (BLAKE3_KAT_KEY.length !== 32) {
368+
throw new Error(
369+
`BLAKE3_KAT_KEY must be 32 bytes; got ${BLAKE3_KAT_KEY.length}`,
370+
);
371+
}
372+
const BLAKE3_KAT_CONTEXT = 'BLAKE3 2019-12-27 16:29:52 test vectors context';
373+
374+
const buildKatInput = (len: number): Uint8Array => {
375+
const buf = new Uint8Array(len);
376+
for (let i = 0; i < len; i++) buf[i] = i % 251;
377+
return buf;
378+
};
379+
380+
// First 32 bytes of each mode's extended output, taken verbatim from
381+
// test_vectors.json cases for input_len ∈ {0, 1, 8, 64}.
382+
const BLAKE3_KAT_CASES = [
383+
{
384+
input_len: 0,
385+
keyed_hash:
386+
'92b2b75604ed3c761f9d6f62392c8a9227ad0ea3f09573e783f1498a4ed60d26',
387+
derive_key:
388+
'2cc39783c223154fea8dfb7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d',
389+
},
390+
{
391+
input_len: 1,
392+
keyed_hash:
393+
'6d7878dfff2f485635d39013278ae14f1454b8c0a3a2d34bc1ab38228a80c95b',
394+
derive_key:
395+
'b3e2e340a117a499c6cf2398a19ee0d29cca2bb7404c73063382693bf66cb06c',
396+
},
397+
{
398+
input_len: 8,
399+
keyed_hash:
400+
'be2f5495c61cba1bb348a34948c004045e3bd4dae8f0fe82bf44d0da245a0600',
401+
derive_key:
402+
'2b166978cef14d9d438046c720519d8b1cad707e199746f1562d0c87fbd32940',
403+
},
404+
{
405+
input_len: 64,
406+
keyed_hash:
407+
'ba8ced36f327700d213f120b1a207a3b8c04330528586f414d09f2f7d9ccb7e6',
408+
derive_key:
409+
'a5c4a7053fa86b64746d4bb688d06ad1f02a18fce9afd3e818fefaa7126bf73e',
410+
},
411+
];
412+
413+
for (const kat of BLAKE3_KAT_CASES) {
414+
test(SUITE, `BLAKE3 KAT keyed_hash input_len=${kat.input_len}`, () => {
415+
const input = buildKatInput(kat.input_len);
416+
const result = blake3(input, { key: BLAKE3_KAT_KEY });
417+
expect(Buffer.from(result).toString('hex')).to.equal(kat.keyed_hash);
418+
});
419+
420+
test(SUITE, `BLAKE3 KAT derive_key input_len=${kat.input_len}`, () => {
421+
const input = buildKatInput(kat.input_len);
422+
const result = blake3(input, { context: BLAKE3_KAT_CONTEXT });
423+
expect(Buffer.from(result).toString('hex')).to.equal(kat.derive_key);
424+
});
425+
}

0 commit comments

Comments
 (0)