@@ -339,3 +339,87 @@ test(SUITE, 'blake3 - empty context throws', () => {
339339 / c o n t e x t m u s t b e a n o n - e m p t y s t r i n g / ,
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