import { randomBytes } from 'node:crypto'; import { AES_GCM_KEY_BYTE_LENGTH } from '../consts/crypto/aesGcmKeyByteLength'; import { fromBase64Url } from './fromBase64Url'; const toBase64UrlLikeClient = (bytes: Buffer): string => { let base64 = bytes.toString('base64').split('+').join('-').split('/').join('_'); while (base64.endsWith('=')) { base64 = base64.slice(0, -1); } return base64; }; describe('fromBase64Url', () => { it('decodes unpadded base64url utf8 payloads', () => { expect(fromBase64Url('aGVsbG8')).toEqual(Buffer.from('hello', 'utf8')); }); it('decodes empty string to an empty buffer', () => { expect(fromBase64Url('')).toEqual(Buffer.alloc(0)); }); it('decodes base64url with - and _ instead of + and /', () => { const bytes = Buffer.from([0xfb, 0xff, 0xfe]); const url = bytes.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); expect(fromBase64Url(url)).toEqual(bytes); }); it('restores padding when the input omits trailing equals', () => { expect(fromBase64Url('AQIDBA')).toEqual(Buffer.from([0x01, 0x02, 0x03, 0x04])); }); it('decodes when padding characters are already present', () => { expect(fromBase64Url('YQ==')).toEqual(Buffer.from('a', 'utf8')); expect(fromBase64Url('YWI=')).toEqual(Buffer.from('ab', 'utf8')); }); it('matches Node base64url decoding for random 32-byte keys', () => { const dek = randomBytes(AES_GCM_KEY_BYTE_LENGTH); const secret = dek.toString('base64url'); expect(fromBase64Url(secret)).toEqual(dek); expect(fromBase64Url(secret)).toEqual(Buffer.from(secret, 'base64url')); }); describe('round-trip with client-style encoding', () => { it.each([0, 1, 2, 3, 4, 12, 16, 32, 64])('decodes %i-byte payloads', length => { const bytes = randomBytes(length); const encoded = toBase64UrlLikeClient(bytes); expect(fromBase64Url(encoded)).toEqual(bytes); }); it('decodes fixed-byte patterns used in crypto tests', () => { const iv = Buffer.alloc(12, 0x11); const dek = Buffer.alloc(32, 0x42); expect(fromBase64Url(toBase64UrlLikeClient(iv))).toEqual(iv); expect(fromBase64Url(toBase64UrlLikeClient(dek))).toEqual(dek); }); }); describe('agrees with Buffer.from(..., "base64url")', () => { const samples = [ 'aGVsbG8', toBase64UrlLikeClient(Buffer.from([0xff, 0xfe, 0xfd])), randomBytes(48).toString('base64url') ]; it.each(samples)('matches for encoded sample', encoded => { expect(fromBase64Url(encoded)).toEqual(Buffer.from(encoded, 'base64url')); }); }); it('decodes standard base64 alphabet when plus and slash are passed through', () => { const standard = Buffer.from('binary+/', 'utf8').toString('base64'); expect(fromBase64Url(standard)).toEqual(Buffer.from('binary+/', 'utf8')); }); });