## HMAC https://en.wikipedia.org/wiki/HMAC https://jwt.io/ ```js // await ( async function main() { // clear(); // debugger; /* | hash func | block size | | MD5 | 64 | insecure | SHA-1 | 64 | insecure | SHA-224 | 64 | | SHA-256 | 64 | | SHA-512/224 | 128 | | SHA-512/256 | 128 | | SHA-384 | 128 | | SHA-512 | 128 | | SHA3-224 | 144 | | SHA3-256 | 136 | | SHA3-384 | 104 | | SHA3-512 | 72 | */ /** * @param { ArrayBuffer } key * @param { ArrayBuffer } message * @param { enum( XXX"SHA-1", "SHA-256", "SHA-384", "SHA-512" ) } hash * @param { Number } block_size * @param { Number } output_size * @returns { ArrayBuffer } */ async function hmac( key, message, hash, block_size, _output_size ) { // console.assert( [ "SHA-256","SHA-384","SHA-512" ].includes( hash ) ); if( ![ "SHA-256","SHA-384","SHA-512" ].includes( hash ) ) { throw new Error( "invalid hash function" ); } const block_sized_key = await compute_block_sized_key( key, hash, block_size ); const o_key_pad = new Uint8Array( block_size ).fill( 0x5c ); for( let i=0; i<o_key_pad.length; i++ ) { o_key_pad[i] ^= block_sized_key[i]; } const i_key_pad = new Uint8Array( block_size ).fill( 0x36 ); for( let i=0; i<i_key_pad.length; i++ ) { i_key_pad[i] ^= block_sized_key[i]; } const payload_1 = new Uint8Array( i_key_pad.length + message.length ); payload_1.set( i_key_pad, 0 ); payload_1.set( message, i_key_pad.length ); const hash_1 = await crypto.subtle.digest( hash, payload_1 ); const _hash_1 = new Uint8Array( hash_1 ); const payload_2 = new Uint8Array( o_key_pad.length + _hash_1.length ); payload_2.set( o_key_pad, 0 ); payload_2.set( _hash_1, o_key_pad.length ); const hash_2 = await crypto.subtle.digest( hash, payload_2 ); return hash_2; } /** * @param { ArrayBuffer } key * @param { enum } hash * @param { Number } block_size * @returns { ArrayBuffer(block_size) } */ async function compute_block_sized_key( key, hash, block_size ) { let _key = key; if( block_size < _key.length ) { _key = await crypto.subtle.digest( hash, _key ); } if( _key.length < block_size ) { const padded_key = new Uint8Array( block_size ); padded_key.set( new Uint8Array( _key ), 0 ); _key = padded_key; } return _key; } const text_encoder = new TextEncoder(); const key = text_encoder.encode( "key" ); const message = text_encoder.encode( "The quick brown fox jumps over the lazy dog" ); const block_size = 64; const output_size = 32; // XXX unused console.log( "in", { key, message, block_size } ); const out = await hmac( key, message, "SHA-256", block_size, output_size ); console.log( "out", out ); console.log( [ ...new Uint8Array( out ) ].map( x => x.toString(16).padStart(2,'0') ).join('') ); "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8".length / 2; // 32 bytes out // } )(); ```