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
text/typescript
/**
* @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;
}
}
}