UNPKG

pooja-docucomb-tink-crypto

Version:

A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.

136 lines (120 loc) 3.92 kB
/** * @license * Copyright 2022 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import {SecurityException} from '../../../exception/security_exception'; import {fromRawKey as hmacFromRawKey} from '../../../subtle/hmac'; import * as validators from '../../../subtle/validators'; import {HpkeKdf} from './hpke_kdf'; import * as hpkeUtil from './hpke_util'; /** * HKDF HPKE KDF variant. * @see https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1-3 */ export class HkdfHpkeKdf implements HpkeKdf { constructor(private readonly hashFunction: 'SHA-256'|'SHA-512') {} async labeledExtract({ikm, ikmLabel, suiteId, salt}: { ikm: Uint8Array, ikmLabel: string, suiteId: Uint8Array, salt?: Uint8Array }): Promise<Uint8Array> { return await this.extract( hpkeUtil.labelIkm({ikmLabel, ikm, suiteId}), salt); } async labeledExpand({prk, info, infoLabel, suiteId, length}: { prk: Uint8Array, info: Uint8Array, infoLabel: string, suiteId: Uint8Array, length: number }): Promise<Uint8Array> { return await this.expand( prk, hpkeUtil.labelInfo({infoLabel, info, suiteId, length}), length); } async extractAndExpand( {ikm, ikmLabel, info, infoLabel, suiteId, length, salt}: { ikm: Uint8Array, ikmLabel: string, info: Uint8Array, infoLabel: string, suiteId: Uint8Array, length: number, salt?: Uint8Array }): Promise<Uint8Array> { const prk: Uint8Array = await this.extract(hpkeUtil.labelIkm({ikmLabel, ikm, suiteId}), salt); return await this.expand( prk, hpkeUtil.labelInfo({infoLabel, info, suiteId, length}), length); } /** * Copied from `javascript/subtle/hkdf`'s `compute` function */ private async expand(prk: Uint8Array, info: Uint8Array, length: number): Promise<Uint8Array> { if (!Number.isInteger(length)) { throw new SecurityException('length must be an integer'); } if (length <= 0) { throw new SecurityException('length must be positive'); } const digestSize = this.getMacLength(); if (length > 255 * digestSize) { throw new SecurityException('length too large'); } validators.requireUint8Array(info); const hmac = await hmacFromRawKey(this.hashFunction, prk, digestSize); let ctr = 1; let pos = 0; let digest = new Uint8Array(0); const result = new Uint8Array(length); while (true) { const input = new Uint8Array(digest.length + info.length + 1); input.set(digest, 0); input.set(info, digest.length); input[input.length - 1] = ctr; digest = await hmac.computeMac(input); if (pos + digest.length < length) { result.set(digest, pos); pos += digest.length; ctr++; } else { result.set(digest.subarray(0, length - pos), pos); break; } } return result; } /** * Copied from `javascript/subtle/hkdf`'s `compute` function */ private async extract(ikm: Uint8Array, salt?: Uint8Array): Promise<Uint8Array> { validators.requireUint8Array(ikm); const digestSize = this.getMacLength(); if (!salt?.length) { salt = new Uint8Array(digestSize); } validators.requireUint8Array(salt); const hmac = await hmacFromRawKey(this.hashFunction, salt, digestSize); const prk = await hmac.computeMac(ikm); return prk; } getKdfId(): Uint8Array { switch (this.hashFunction) { case 'SHA-256': return hpkeUtil.HKDF_SHA256_KDF_ID; case 'SHA-512': return hpkeUtil.HKDF_SHA512_KDF_ID; } } getMacLength(): number { switch (this.hashFunction) { case 'SHA-256': return 32; case 'SHA-512': return 64; } } }