Files
libhalo/core/src.ts/halo/jwe_util.ts

79 lines
2.4 KiB
TypeScript

import {Buffer} from 'buffer/index.js';
import crypto from 'crypto';
const subtle = crypto.webcrypto && crypto.webcrypto.subtle ? crypto.webcrypto.subtle : globalThis.crypto.subtle;
import {CompactEncrypt, decodeProtectedHeader, compactDecrypt} from 'jose';
class JWEUtil {
private sharedKeyObj: CryptoKey | null;
constructor() {
this.sharedKeyObj = null;
}
async generateKey() {
const sharedKey = crypto.randomBytes(16)
const sharedKeyEnc = sharedKey
.toString('base64')
.replaceAll('+', '-')
.replaceAll('/', '_')
.replaceAll('==', '');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
this.sharedKeyObj = await subtle.importKey("raw", sharedKey, "AES-GCM", true, [
"encrypt",
"decrypt",
]);
return sharedKeyEnc;
}
async loadKey(sharedKey: string) {
// automatically add "=" padding if it's not present
let padLen = (-sharedKey.length % 3) + 3;
if (padLen === 3) {
padLen = 0;
}
const fixedKeyStr = (sharedKey + "=".repeat(padLen))
.replaceAll('-', '+')
.replaceAll('_', '/');
const sharedKeyBuf = Buffer.from(fixedKeyStr, 'base64');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
this.sharedKeyObj = await subtle.importKey("raw", sharedKeyBuf, "AES-GCM", true, [
"encrypt",
"decrypt",
]);
}
async encrypt(data: unknown) {
if (this.sharedKeyObj === null) {
throw new Error("Key is not loaded nor was generated.");
}
return await new CompactEncrypt(
new TextEncoder().encode(JSON.stringify(data)))
.setProtectedHeader({alg: 'dir', enc: 'A128GCM'})
.encrypt(this.sharedKeyObj);
}
async decrypt(jwe: string) {
if (this.sharedKeyObj === null) {
throw new Error("Key is not loaded nor was generated.");
}
const hdr = decodeProtectedHeader(jwe);
if (Object.keys(hdr).length !== 2 || hdr.alg !== "dir" || hdr.enc !== "A128GCM") {
throw new Error("Unexpected type of JWE provided.");
}
const { plaintext, protectedHeader } = await compactDecrypt(jwe, this.sharedKeyObj);
return JSON.parse(new TextDecoder().decode(plaintext));
}
}
export {JWEUtil};