diff --git a/examples/parse.ts b/examples/parse.ts new file mode 100644 index 0000000..32bff62 --- /dev/null +++ b/examples/parse.ts @@ -0,0 +1,18 @@ +import { CAT } from '../src'; + +async function main() { + const parser = new CAT({ + keys: { + Symmetric256: Buffer.from( + '403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d79569388', + 'hex' + ) + } + }); + const result = await parser.validate(process.argv[2], 'mac', { + issuer: 'eyevinn' + }); + console.log(result); +} + +main(); diff --git a/src/cat.ts b/src/cat.ts index f8fb5f8..b131489 100644 --- a/src/cat.ts +++ b/src/cat.ts @@ -15,6 +15,7 @@ import { CommonAccessTokenUri } from './catu'; import { CommonAccessTokenRenewal } from './catr'; import { CommonAccessTokenHeader } from './cath'; import { CommonAccessTokenIf } from './catif'; +import { toBase64, toHex } from './util'; export const claimsToLabels: { [key: string]: number } = { iss: 1, // 3 @@ -76,8 +77,8 @@ const claimTransform: { [key: string]: (value: string) => Buffer } = { }; const claimTransformReverse: { [key: string]: (value: Buffer) => string } = { - cti: (value: Buffer) => value.toString('hex'), - cattpk: (value: Buffer) => value.toString('hex') + cti: (value: Buffer) => toHex(value), + cattpk: (value: Buffer) => toHex(value) }; const claimTypeValidators: { @@ -492,7 +493,7 @@ export class CommonAccessToken { } get base64() { - return this.data?.toString('base64'); + return this.data ? toBase64(this.data) : undefined; } get keyId() { diff --git a/src/index.ts b/src/index.ts index b6ec269..af1eaea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { CommonAccessTokenFactory } from './cat'; import { KeyNotFoundError } from './errors'; +import { generateRandomHex, toBase64 } from './util'; export { CommonAccessToken } from './cat'; export { CommonAccessTokenRenewal } from './catr'; @@ -225,7 +226,7 @@ export class CAT { opts?: CatGenerateOptions ) { if (opts?.generateCwtId) { - claims['cti'] = crypto.randomBytes(16).toString('hex'); + claims['cti'] = generateRandomHex(16); } const cat = new CommonAccessToken(claims); if (opts && opts.type == 'mac') { @@ -239,7 +240,7 @@ export class CAT { if (!cat.raw) { throw new Error('Failed to MAC token'); } - return cat.raw.toString('base64'); + return toBase64(cat.raw); } } @@ -281,7 +282,7 @@ export class CAT { opts?: CatGenerateOptions ) { if (opts?.generateCwtId) { - dict['cti'] = crypto.randomBytes(16).toString('hex'); + dict['cti'] = generateRandomHex(16); } const cat = CommonAccessTokenFactory.fromDict(dict); if (opts && opts.type == 'mac') { @@ -295,7 +296,7 @@ export class CAT { if (!cat.raw) { throw new Error('Failed to MAC token'); } - return cat.raw.toString('base64'); + return toBase64(cat.raw); } } @@ -313,7 +314,7 @@ export class CAT { opts: CatRenewOptions ): Promise { const newClaims = cat.claims; - newClaims['cti'] = crypto.randomBytes(16).toString('hex'); + newClaims['cti'] = generateRandomHex(16); newClaims['iat'] = Math.floor(Date.now() / 1000); newClaims['iss'] = opts.issuer; newClaims['exp'] = newClaims['iat'] + (newClaims['catr'] as any)['expadd']; @@ -329,6 +330,6 @@ export class CAT { if (!newCat.raw) { throw new Error('Failed to MAC token'); } - return newCat.raw.toString('base64'); + return toBase64(newCat.raw); } } diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..202e242 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,26 @@ +import crypto from 'crypto'; + +/** + * Generate a random hex string of specified length + * @param bytes Number of random bytes to generate + * @returns Hex string + */ +export function generateRandomHex(bytes: number): string { + const randomBytes = new Uint8Array(bytes); + crypto.getRandomValues(randomBytes); + return Array.from(randomBytes) + .map((byte) => byte.toString(16).padStart(2, '0')) + .join(''); +} + +export function toBase64(input: Buffer): string { + const bytes = new Uint8Array(input); + return btoa(String.fromCharCode(...bytes)); +} + +export function toHex(input: Buffer): string { + const bytes = new Uint8Array(input); + return Array.from(bytes) + .map((byte) => byte.toString(16).padStart(2, '0')) + .join(''); +}