This repository has been archived on 2023-04-04. You can view files and clone it, but cannot push or open issues or pull requests.
webcrypto/src/mechs/aes/crypto.ts

335 lines
8.7 KiB
TypeScript

import { Buffer } from "buffer";
import crypto, { CipherGCM, DecipherGCM } from "crypto";
import { JsonParser, JsonSerializer } from "@peculiar/json-schema";
import * as core from "webcrypto-core";
import { AesCryptoKey } from "./key";
import { CryptoKey } from "../../keys";
export class AesCrypto {
public static AES_KW_IV = Buffer.from("A6A6A6A6A6A6A6A6", "hex");
public static async generateKey(
algorithm: AesKeyGenParams,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<AesCryptoKey> {
const key = new AesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
key.data = crypto.randomBytes(algorithm.length >> 3);
return key;
}
public static async exportKey(
format: string,
key: AesCryptoKey
): Promise<JsonWebKey | ArrayBuffer> {
if (!(key instanceof AesCryptoKey)) {
throw new Error("key: Is not AesCryptoKey");
}
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "raw":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
}
public static async importKey(
format: string,
keyData: JsonWebKey | ArrayBuffer,
algorithm: any,
extractable: boolean,
keyUsages: KeyUsage[]
): Promise<CryptoKey> {
let key: AesCryptoKey;
switch (format.toLowerCase()) {
case "jwk":
key = JsonParser.fromJSON(keyData, { targetSchema: AesCryptoKey });
break;
case "raw":
key = new AesCryptoKey();
key.data = Buffer.from(keyData as ArrayBuffer);
break;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
key.algorithm = algorithm;
key.algorithm.length = key.data.length << 3;
key.extractable = extractable;
key.usages = keyUsages;
// check key length
switch (key.algorithm.length) {
case 128:
case 192:
case 256:
break;
default:
throw new core.OperationError("keyData: Is wrong key length");
}
return key;
}
public static async encrypt(
algorithm: Algorithm,
key: AesCryptoKey,
data: Uint8Array
): Promise<ArrayBuffer> {
switch (algorithm.name.toUpperCase()) {
case "AES-CBC":
return this.encryptAesCBC(
algorithm as AesCbcParams,
key,
Buffer.from(data)
);
case "AES-CTR":
return this.encryptAesCTR(
algorithm as AesCtrParams,
key,
Buffer.from(data)
);
case "AES-GCM":
return this.encryptAesGCM(
algorithm as AesGcmParams,
key,
Buffer.from(data)
);
case "AES-KW":
return this.encryptAesKW(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
case "AES-ECB":
return this.encryptAesECB(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
public static async decrypt(
algorithm: Algorithm,
key: CryptoKey,
data: Uint8Array
): Promise<ArrayBuffer> {
if (!(key instanceof AesCryptoKey)) {
throw new Error("key: Is not AesCryptoKey");
}
switch (algorithm.name.toUpperCase()) {
case "AES-CBC":
return this.decryptAesCBC(
algorithm as AesCbcParams,
key,
Buffer.from(data)
);
case "AES-CTR":
return this.decryptAesCTR(
algorithm as AesCtrParams,
key,
Buffer.from(data)
);
case "AES-GCM":
return this.decryptAesGCM(
algorithm as AesGcmParams,
key,
Buffer.from(data)
);
case "AES-KW":
return this.decryptAesKW(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
case "AES-ECB":
return this.decryptAesECB(
algorithm as AesKeyAlgorithm,
key,
Buffer.from(data)
);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
public static async encryptAesCBC(
algorithm: AesCbcParams,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesCBC(
algorithm: AesCbcParams,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-cbc`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesCTR(
algorithm: AesCtrParams,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-ctr`,
key.data,
Buffer.from(algorithm.counter as ArrayBuffer)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesCTR(
algorithm: AesCtrParams,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-ctr`,
key.data,
new Uint8Array(algorithm.counter as ArrayBuffer)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesGCM(
algorithm: AesGcmParams,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-gcm`,
key.data,
Buffer.from(algorithm.iv as ArrayBuffer),
{
authTagLength: (algorithm.tagLength || 128) >> 3,
} as any
) as CipherGCM; // NodeJs d.ts doesn't support CipherGCMOptions for createCipheriv
if (algorithm.additionalData) {
cipher.setAAD(Buffer.from(algorithm.additionalData as ArrayBuffer));
}
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final(), cipher.getAuthTag()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesGCM(
algorithm: AesGcmParams,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-gcm`,
key.data,
new Uint8Array(algorithm.iv as ArrayBuffer)
) as DecipherGCM;
const tagLength = (algorithm.tagLength || 128) >> 3;
const enc = data.slice(0, data.length - tagLength);
const tag = data.slice(data.length - tagLength);
if (algorithm.additionalData) {
decipher.setAAD(Buffer.from(algorithm.additionalData as ArrayBuffer));
}
decipher.setAuthTag(tag);
let dec = decipher.update(enc);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesKW(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`id-aes${key.algorithm.length}-wrap`,
key.data,
this.AES_KW_IV
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
return new Uint8Array(enc).buffer;
}
public static async decryptAesKW(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`id-aes${key.algorithm.length}-wrap`,
key.data,
this.AES_KW_IV
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
public static async encryptAesECB(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const cipher = crypto.createCipheriv(
`aes-${key.algorithm.length}-ecb`,
key.data,
new Uint8Array(0)
);
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
public static async decryptAesECB(
algorithm: Algorithm,
key: AesCryptoKey,
data: Buffer
) {
const decipher = crypto.createDecipheriv(
`aes-${key.algorithm.length}-ecb`,
key.data,
new Uint8Array(0)
);
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
}