@peculiar/webcrypto
Version:
A WebCrypto Polyfill for NodeJS
1,281 lines (1,254 loc) • 89 kB
JavaScript
/*!
Copyright (c) Peculiar Ventures, LLC
*/
import * as core from 'webcrypto-core';
import { BufferSourceConverter as BufferSourceConverter$1 } from 'webcrypto-core';
export { CryptoKey } from 'webcrypto-core';
import { Buffer } from 'buffer';
import * as crypto from 'crypto';
import crypto__default from 'crypto';
import * as process from 'process';
import { __decorate } from 'tslib';
import { JsonProp, JsonPropTypes, JsonSerializer, JsonParser } from '@peculiar/json-schema';
import { Convert, BufferSourceConverter } from 'pvtsutils';
import { AsnParser, AsnSerializer } from '@peculiar/asn1-schema';
const JsonBase64UrlConverter = {
fromJSON: (value) => Buffer.from(Convert.FromBase64Url(value)),
toJSON: (value) => Convert.ToBase64Url(value),
};
class CryptoKey extends core.CryptoKey {
constructor() {
super(...arguments);
this.data = Buffer.alloc(0);
this.algorithm = { name: "" };
this.extractable = false;
this.type = "secret";
this.usages = [];
this.kty = "oct";
this.alg = "";
}
}
__decorate([
JsonProp({ name: "ext", type: JsonPropTypes.Boolean, optional: true })
], CryptoKey.prototype, "extractable", void 0);
__decorate([
JsonProp({ name: "key_ops", type: JsonPropTypes.String, repeated: true, optional: true })
], CryptoKey.prototype, "usages", void 0);
__decorate([
JsonProp({ type: JsonPropTypes.String })
], CryptoKey.prototype, "kty", void 0);
__decorate([
JsonProp({ type: JsonPropTypes.String, optional: true })
], CryptoKey.prototype, "alg", void 0);
class SymmetricKey extends CryptoKey {
constructor() {
super(...arguments);
this.kty = "oct";
this.type = "secret";
}
}
class AsymmetricKey extends CryptoKey {
}
class AesCryptoKey extends SymmetricKey {
get alg() {
switch (this.algorithm.name.toUpperCase()) {
case "AES-CBC":
return `A${this.algorithm.length}CBC`;
case "AES-CTR":
return `A${this.algorithm.length}CTR`;
case "AES-GCM":
return `A${this.algorithm.length}GCM`;
case "AES-KW":
return `A${this.algorithm.length}KW`;
case "AES-CMAC":
return `A${this.algorithm.length}CMAC`;
case "AES-ECB":
return `A${this.algorithm.length}ECB`;
default:
throw new core.AlgorithmError("Unsupported algorithm name");
}
}
set alg(value) {
}
}
__decorate([
JsonProp({ name: "k", converter: JsonBase64UrlConverter })
], AesCryptoKey.prototype, "data", void 0);
class AesCrypto {
static async generateKey(algorithm, extractable, keyUsages) {
const key = new AesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
key.data = crypto__default.randomBytes(algorithm.length >> 3);
return key;
}
static async exportKey(format, key) {
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'");
}
}
static async importKey(format, keyData, algorithm, extractable, keyUsages) {
let key;
switch (format.toLowerCase()) {
case "jwk":
key = JsonParser.fromJSON(keyData, { targetSchema: AesCryptoKey });
break;
case "raw":
key = new AesCryptoKey();
key.data = Buffer.from(keyData);
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;
switch (key.algorithm.length) {
case 128:
case 192:
case 256:
break;
default:
throw new core.OperationError("keyData: Is wrong key length");
}
return key;
}
static async encrypt(algorithm, key, data) {
switch (algorithm.name.toUpperCase()) {
case "AES-CBC":
return this.encryptAesCBC(algorithm, key, Buffer.from(data));
case "AES-CTR":
return this.encryptAesCTR(algorithm, key, Buffer.from(data));
case "AES-GCM":
return this.encryptAesGCM(algorithm, key, Buffer.from(data));
case "AES-KW":
return this.encryptAesKW(algorithm, key, Buffer.from(data));
case "AES-ECB":
return this.encryptAesECB(algorithm, key, Buffer.from(data));
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async decrypt(algorithm, key, data) {
if (!(key instanceof AesCryptoKey)) {
throw new Error("key: Is not AesCryptoKey");
}
switch (algorithm.name.toUpperCase()) {
case "AES-CBC":
return this.decryptAesCBC(algorithm, key, Buffer.from(data));
case "AES-CTR":
return this.decryptAesCTR(algorithm, key, Buffer.from(data));
case "AES-GCM":
return this.decryptAesGCM(algorithm, key, Buffer.from(data));
case "AES-KW":
return this.decryptAesKW(algorithm, key, Buffer.from(data));
case "AES-ECB":
return this.decryptAesECB(algorithm, key, Buffer.from(data));
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async encryptAesCBC(algorithm, key, data) {
const cipher = crypto__default.createCipheriv(`aes-${key.algorithm.length}-cbc`, key.data, new Uint8Array(algorithm.iv));
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
static async decryptAesCBC(algorithm, key, data) {
const decipher = crypto__default.createDecipheriv(`aes-${key.algorithm.length}-cbc`, key.data, new Uint8Array(algorithm.iv));
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
static async encryptAesCTR(algorithm, key, data) {
const cipher = crypto__default.createCipheriv(`aes-${key.algorithm.length}-ctr`, key.data, Buffer.from(algorithm.counter));
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
static async decryptAesCTR(algorithm, key, data) {
const decipher = crypto__default.createDecipheriv(`aes-${key.algorithm.length}-ctr`, key.data, new Uint8Array(algorithm.counter));
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
static async encryptAesGCM(algorithm, key, data) {
const cipher = crypto__default.createCipheriv(`aes-${key.algorithm.length}-gcm`, key.data, Buffer.from(algorithm.iv), {
authTagLength: (algorithm.tagLength || 128) >> 3,
});
if (algorithm.additionalData) {
cipher.setAAD(Buffer.from(algorithm.additionalData));
}
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final(), cipher.getAuthTag()]);
const res = new Uint8Array(enc).buffer;
return res;
}
static async decryptAesGCM(algorithm, key, data) {
const tagLength = (algorithm.tagLength || 128) >> 3;
const decipher = crypto__default.createDecipheriv(`aes-${key.algorithm.length}-gcm`, key.data, new Uint8Array(algorithm.iv), {
authTagLength: tagLength,
});
const enc = data.slice(0, data.length - tagLength);
const tag = data.slice(data.length - tagLength);
if (algorithm.additionalData) {
decipher.setAAD(Buffer.from(algorithm.additionalData));
}
decipher.setAuthTag(tag);
let dec = decipher.update(enc);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
static async encryptAesKW(algorithm, key, data) {
const cipher = crypto__default.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;
}
static async decryptAesKW(algorithm, key, data) {
const decipher = crypto__default.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;
}
static async encryptAesECB(algorithm, key, data) {
const cipher = crypto__default.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;
}
static async decryptAesECB(algorithm, key, data) {
const decipher = crypto__default.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;
}
}
AesCrypto.AES_KW_IV = Buffer.from("A6A6A6A6A6A6A6A6", "hex");
const keyStorage = new WeakMap();
function getCryptoKey(key) {
const res = keyStorage.get(key);
if (!res) {
throw new core.OperationError("Cannot get CryptoKey from secure storage");
}
return res;
}
function setCryptoKey(value) {
const key = core.CryptoKey.create(value.algorithm, value.type, value.extractable, value.usages);
Object.freeze(key);
keyStorage.set(key, value);
return key;
}
class AesCbcProvider extends core.AesCbcProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await AesCrypto.generateKey({
name: this.name,
length: algorithm.length,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onEncrypt(algorithm, key, data) {
return AesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return AesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onExportKey(format, key) {
return AesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const key = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
return setCryptoKey(key);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}
const zero = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
const rb = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 135]);
const blockSize = 16;
function bitShiftLeft(buffer) {
const shifted = Buffer.alloc(buffer.length);
const last = buffer.length - 1;
for (let index = 0; index < last; index++) {
shifted[index] = buffer[index] << 1;
if (buffer[index + 1] & 0x80) {
shifted[index] += 0x01;
}
}
shifted[last] = buffer[last] << 1;
return shifted;
}
function xor(a, b) {
const length = Math.min(a.length, b.length);
const output = Buffer.alloc(length);
for (let index = 0; index < length; index++) {
output[index] = a[index] ^ b[index];
}
return output;
}
function aes(key, message) {
const cipher = crypto.createCipheriv(`aes${key.length << 3}`, key, zero);
const result = cipher.update(message);
cipher.final();
return result;
}
function getMessageBlock(message, blockIndex) {
const block = Buffer.alloc(blockSize);
const start = blockIndex * blockSize;
const end = start + blockSize;
message.copy(block, 0, start, end);
return block;
}
function getPaddedMessageBlock(message, blockIndex) {
const block = Buffer.alloc(blockSize);
const start = blockIndex * blockSize;
const end = message.length;
block.fill(0);
message.copy(block, 0, start, end);
block[end - start] = 0x80;
return block;
}
function generateSubkeys(key) {
const l = aes(key, zero);
let subkey1 = bitShiftLeft(l);
if (l[0] & 0x80) {
subkey1 = xor(subkey1, rb);
}
let subkey2 = bitShiftLeft(subkey1);
if (subkey1[0] & 0x80) {
subkey2 = xor(subkey2, rb);
}
return { subkey1, subkey2 };
}
function aesCmac(key, message) {
const subkeys = generateSubkeys(key);
let blockCount = Math.ceil(message.length / blockSize);
let lastBlockCompleteFlag;
let lastBlock;
if (blockCount === 0) {
blockCount = 1;
lastBlockCompleteFlag = false;
}
else {
lastBlockCompleteFlag = (message.length % blockSize === 0);
}
const lastBlockIndex = blockCount - 1;
if (lastBlockCompleteFlag) {
lastBlock = xor(getMessageBlock(message, lastBlockIndex), subkeys.subkey1);
}
else {
lastBlock = xor(getPaddedMessageBlock(message, lastBlockIndex), subkeys.subkey2);
}
let x = zero;
let y;
for (let index = 0; index < lastBlockIndex; index++) {
y = xor(x, getMessageBlock(message, index));
x = aes(key, y);
}
y = xor(lastBlock, x);
return aes(key, y);
}
class AesCmacProvider extends core.AesCmacProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await AesCrypto.generateKey({
name: this.name,
length: algorithm.length,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onSign(algorithm, key, data) {
const result = aesCmac(getCryptoKey(key).data, Buffer.from(data));
return new Uint8Array(result).buffer;
}
async onVerify(algorithm, key, signature, data) {
const signature2 = await this.sign(algorithm, key, data);
return Buffer.from(signature).compare(Buffer.from(signature2)) === 0;
}
async onExportKey(format, key) {
return AesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
return setCryptoKey(res);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}
class AesCtrProvider extends core.AesCtrProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await AesCrypto.generateKey({
name: this.name,
length: algorithm.length,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onEncrypt(algorithm, key, data) {
return AesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return AesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onExportKey(format, key) {
return AesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
return setCryptoKey(res);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}
class AesGcmProvider extends core.AesGcmProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await AesCrypto.generateKey({
name: this.name,
length: algorithm.length,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onEncrypt(algorithm, key, data) {
return AesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return AesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onExportKey(format, key) {
return AesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
return setCryptoKey(res);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}
class AesKwProvider extends core.AesKwProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const res = await AesCrypto.generateKey({
name: this.name,
length: algorithm.length,
}, extractable, keyUsages);
return setCryptoKey(res);
}
async onExportKey(format, key) {
return AesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
return setCryptoKey(res);
}
async onEncrypt(algorithm, key, data) {
return AesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return AesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}
class AesEcbProvider extends core.AesEcbProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await AesCrypto.generateKey({
name: this.name,
length: algorithm.length,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onEncrypt(algorithm, key, data) {
return AesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return AesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onExportKey(format, key) {
return AesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const res = await AesCrypto.importKey(format, keyData, { name: algorithm.name }, extractable, keyUsages);
return setCryptoKey(res);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof AesCryptoKey)) {
throw new TypeError("key: Is not a AesCryptoKey");
}
}
}
class DesCryptoKey extends SymmetricKey {
get alg() {
switch (this.algorithm.name.toUpperCase()) {
case "DES-CBC":
return `DES-CBC`;
case "DES-EDE3-CBC":
return `3DES-CBC`;
default:
throw new core.AlgorithmError("Unsupported algorithm name");
}
}
set alg(value) {
}
}
__decorate([
JsonProp({ name: "k", converter: JsonBase64UrlConverter })
], DesCryptoKey.prototype, "data", void 0);
class DesCrypto {
static async generateKey(algorithm, extractable, keyUsages) {
const key = new DesCryptoKey();
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
key.data = crypto__default.randomBytes(algorithm.length >> 3);
return key;
}
static async exportKey(format, key) {
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'");
}
}
static async importKey(format, keyData, algorithm, extractable, keyUsages) {
let key;
switch (format.toLowerCase()) {
case "jwk":
key = JsonParser.fromJSON(keyData, { targetSchema: DesCryptoKey });
break;
case "raw":
key = new DesCryptoKey();
key.data = Buffer.from(keyData);
break;
default:
throw new core.OperationError("format: Must be 'jwk' or 'raw'");
}
key.algorithm = algorithm;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
static async encrypt(algorithm, key, data) {
switch (algorithm.name.toUpperCase()) {
case "DES-CBC":
return this.encryptDesCBC(algorithm, key, Buffer.from(data));
case "DES-EDE3-CBC":
return this.encryptDesEDE3CBC(algorithm, key, Buffer.from(data));
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async decrypt(algorithm, key, data) {
if (!(key instanceof DesCryptoKey)) {
throw new Error("key: Is not DesCryptoKey");
}
switch (algorithm.name.toUpperCase()) {
case "DES-CBC":
return this.decryptDesCBC(algorithm, key, Buffer.from(data));
case "DES-EDE3-CBC":
return this.decryptDesEDE3CBC(algorithm, key, Buffer.from(data));
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async encryptDesCBC(algorithm, key, data) {
const cipher = crypto__default.createCipheriv(`des-cbc`, key.data, new Uint8Array(algorithm.iv));
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
static async decryptDesCBC(algorithm, key, data) {
const decipher = crypto__default.createDecipheriv(`des-cbc`, key.data, new Uint8Array(algorithm.iv));
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
static async encryptDesEDE3CBC(algorithm, key, data) {
const cipher = crypto__default.createCipheriv(`des-ede3-cbc`, key.data, Buffer.from(algorithm.iv));
let enc = cipher.update(data);
enc = Buffer.concat([enc, cipher.final()]);
const res = new Uint8Array(enc).buffer;
return res;
}
static async decryptDesEDE3CBC(algorithm, key, data) {
const decipher = crypto__default.createDecipheriv(`des-ede3-cbc`, key.data, new Uint8Array(algorithm.iv));
let dec = decipher.update(data);
dec = Buffer.concat([dec, decipher.final()]);
return new Uint8Array(dec).buffer;
}
}
class DesCbcProvider extends core.DesProvider {
constructor() {
super(...arguments);
this.keySizeBits = 64;
this.ivSize = 8;
this.name = "DES-CBC";
}
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await DesCrypto.generateKey({
name: this.name,
length: this.keySizeBits,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onEncrypt(algorithm, key, data) {
return DesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return DesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onExportKey(format, key) {
return DesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const key = await DesCrypto.importKey(format, keyData, { name: this.name, length: this.keySizeBits }, extractable, keyUsages);
if (key.data.length !== (this.keySizeBits >> 3)) {
throw new core.OperationError("keyData: Wrong key size");
}
return setCryptoKey(key);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof DesCryptoKey)) {
throw new TypeError("key: Is not a DesCryptoKey");
}
}
}
class DesEde3CbcProvider extends core.DesProvider {
constructor() {
super(...arguments);
this.keySizeBits = 192;
this.ivSize = 8;
this.name = "DES-EDE3-CBC";
}
async onGenerateKey(algorithm, extractable, keyUsages) {
const key = await DesCrypto.generateKey({
name: this.name,
length: this.keySizeBits,
}, extractable, keyUsages);
return setCryptoKey(key);
}
async onEncrypt(algorithm, key, data) {
return DesCrypto.encrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onDecrypt(algorithm, key, data) {
return DesCrypto.decrypt(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onExportKey(format, key) {
return DesCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const key = await DesCrypto.importKey(format, keyData, { name: this.name, length: this.keySizeBits }, extractable, keyUsages);
if (key.data.length !== (this.keySizeBits >> 3)) {
throw new core.OperationError("keyData: Wrong key size");
}
return setCryptoKey(key);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
if (!(getCryptoKey(key) instanceof DesCryptoKey)) {
throw new TypeError("key: Is not a DesCryptoKey");
}
}
}
function getJwkAlgorithm(algorithm) {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP": {
const mdSize = /(\d+)$/.exec(algorithm.hash.name)[1];
return `RSA-OAEP${mdSize !== "1" ? `-${mdSize}` : ""}`;
}
case "RSASSA-PKCS1-V1_5":
return `RS${/(\d+)$/.exec(algorithm.hash.name)[1]}`;
case "RSA-PSS":
return `PS${/(\d+)$/.exec(algorithm.hash.name)[1]}`;
case "RSA-PKCS1":
return `RS1`;
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
class RsaPrivateKey extends AsymmetricKey {
constructor() {
super(...arguments);
this.type = "private";
}
getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PrivateKeyInfo);
return AsnParser.parse(keyInfo.privateKey, core.asn1.RsaPrivateKey);
}
toJSON() {
const key = this.getKey();
const json = {
kty: "RSA",
alg: getJwkAlgorithm(this.algorithm),
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, JsonSerializer.toJSON(key));
}
fromJSON(json) {
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.RsaPrivateKey });
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
keyInfo.privateKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
}
}
class RsaPublicKey extends AsymmetricKey {
constructor() {
super(...arguments);
this.type = "public";
}
getKey() {
const keyInfo = AsnParser.parse(this.data, core.asn1.PublicKeyInfo);
return AsnParser.parse(keyInfo.publicKey, core.asn1.RsaPublicKey);
}
toJSON() {
const key = this.getKey();
const json = {
kty: "RSA",
alg: getJwkAlgorithm(this.algorithm),
key_ops: this.usages,
ext: this.extractable,
};
return Object.assign(json, JsonSerializer.toJSON(key));
}
fromJSON(json) {
const key = JsonParser.fromJSON(json, { targetSchema: core.asn1.RsaPublicKey });
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
keyInfo.publicKey = AsnSerializer.serialize(key);
this.data = Buffer.from(AsnSerializer.serialize(keyInfo));
}
}
class RsaCrypto {
static async generateKey(algorithm, extractable, keyUsages) {
const privateKey = new RsaPrivateKey();
privateKey.algorithm = algorithm;
privateKey.extractable = extractable;
privateKey.usages = keyUsages.filter((usage) => this.privateKeyUsages.indexOf(usage) !== -1);
const publicKey = new RsaPublicKey();
publicKey.algorithm = algorithm;
publicKey.extractable = true;
publicKey.usages = keyUsages.filter((usage) => this.publicKeyUsages.indexOf(usage) !== -1);
const publicExponent = Buffer.concat([
Buffer.alloc(4 - algorithm.publicExponent.byteLength, 0),
Buffer.from(algorithm.publicExponent),
]).readInt32BE(0);
const keys = crypto__default.generateKeyPairSync("rsa", {
modulusLength: algorithm.modulusLength,
publicExponent,
publicKeyEncoding: {
format: "der",
type: "spki",
},
privateKeyEncoding: {
format: "der",
type: "pkcs8",
},
});
privateKey.data = keys.privateKey;
publicKey.data = keys.publicKey;
const res = {
privateKey,
publicKey,
};
return res;
}
static async exportKey(format, key) {
switch (format.toLowerCase()) {
case "jwk":
return JsonSerializer.toJSON(key);
case "pkcs8":
case "spki":
return new Uint8Array(key.data).buffer;
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
}
}
static async importKey(format, keyData, algorithm, extractable, keyUsages) {
switch (format.toLowerCase()) {
case "jwk": {
const jwk = keyData;
if (jwk.d) {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.RsaPrivateKey });
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
else {
const asnKey = JsonParser.fromJSON(keyData, { targetSchema: core.asn1.RsaPublicKey });
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
}
case "spki": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData), core.asn1.PublicKeyInfo);
const asnKey = AsnParser.parse(keyInfo.publicKey, core.asn1.RsaPublicKey);
return this.importPublicKey(asnKey, algorithm, extractable, keyUsages);
}
case "pkcs8": {
const keyInfo = AsnParser.parse(new Uint8Array(keyData), core.asn1.PrivateKeyInfo);
const asnKey = AsnParser.parse(keyInfo.privateKey, core.asn1.RsaPrivateKey);
return this.importPrivateKey(asnKey, algorithm, extractable, keyUsages);
}
default:
throw new core.OperationError("format: Must be 'jwk', 'pkcs8' or 'spki'");
}
}
static async sign(algorithm, key, data) {
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
case "RSASSA-PKCS1-V1_5":
return this.signRsa(algorithm, key, data);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async verify(algorithm, key, signature, data) {
switch (algorithm.name.toUpperCase()) {
case "RSA-PSS":
case "RSASSA-PKCS1-V1_5":
return this.verifySSA(algorithm, key, data, signature);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async encrypt(algorithm, key, data) {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP":
return this.encryptOAEP(algorithm, key, data);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static async decrypt(algorithm, key, data) {
switch (algorithm.name.toUpperCase()) {
case "RSA-OAEP":
return this.decryptOAEP(algorithm, key, data);
default:
throw new core.OperationError("algorithm: Is not recognized");
}
}
static importPrivateKey(asnKey, algorithm, extractable, keyUsages) {
const keyInfo = new core.asn1.PrivateKeyInfo();
keyInfo.privateKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.privateKeyAlgorithm.parameters = null;
keyInfo.privateKey = AsnSerializer.serialize(asnKey);
const key = new RsaPrivateKey();
key.data = Buffer.from(AsnSerializer.serialize(keyInfo));
key.algorithm = Object.assign({}, algorithm);
key.algorithm.publicExponent = new Uint8Array(asnKey.publicExponent);
key.algorithm.modulusLength = asnKey.modulus.byteLength << 3;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
static importPublicKey(asnKey, algorithm, extractable, keyUsages) {
const keyInfo = new core.asn1.PublicKeyInfo();
keyInfo.publicKeyAlgorithm.algorithm = "1.2.840.113549.1.1.1";
keyInfo.publicKeyAlgorithm.parameters = null;
keyInfo.publicKey = AsnSerializer.serialize(asnKey);
const key = new RsaPublicKey();
key.data = Buffer.from(AsnSerializer.serialize(keyInfo));
key.algorithm = Object.assign({}, algorithm);
key.algorithm.publicExponent = new Uint8Array(asnKey.publicExponent);
key.algorithm.modulusLength = asnKey.modulus.byteLength << 3;
key.extractable = extractable;
key.usages = keyUsages;
return key;
}
static getCryptoAlgorithm(alg) {
switch (alg.hash.name.toUpperCase()) {
case "SHA-1":
return "RSA-SHA1";
case "SHA-256":
return "RSA-SHA256";
case "SHA-384":
return "RSA-SHA384";
case "SHA-512":
return "RSA-SHA512";
case "SHA3-256":
return "RSA-SHA3-256";
case "SHA3-384":
return "RSA-SHA3-384";
case "SHA3-512":
return "RSA-SHA3-512";
default:
throw new core.OperationError("algorithm.hash: Is not recognized");
}
}
static signRsa(algorithm, key, data) {
const cryptoAlg = this.getCryptoAlgorithm(key.algorithm);
const signer = crypto__default.createSign(cryptoAlg);
signer.update(Buffer.from(data));
if (!key.pem) {
key.pem = `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`;
}
const options = {
key: key.pem,
};
if (algorithm.name.toUpperCase() === "RSA-PSS") {
options.padding = crypto__default.constants.RSA_PKCS1_PSS_PADDING;
options.saltLength = algorithm.saltLength;
}
const signature = signer.sign(options);
return new Uint8Array(signature).buffer;
}
static verifySSA(algorithm, key, data, signature) {
const cryptoAlg = this.getCryptoAlgorithm(key.algorithm);
const signer = crypto__default.createVerify(cryptoAlg);
signer.update(Buffer.from(data));
if (!key.pem) {
key.pem = `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`;
}
const options = {
key: key.pem,
};
if (algorithm.name.toUpperCase() === "RSA-PSS") {
options.padding = crypto__default.constants.RSA_PKCS1_PSS_PADDING;
options.saltLength = algorithm.saltLength;
}
const ok = signer.verify(options, signature);
return ok;
}
static encryptOAEP(algorithm, key, data) {
const options = {
key: `-----BEGIN PUBLIC KEY-----\n${key.data.toString("base64")}\n-----END PUBLIC KEY-----`,
padding: crypto__default.constants.RSA_PKCS1_OAEP_PADDING,
};
if (algorithm.label) ;
return new Uint8Array(crypto__default.publicEncrypt(options, data)).buffer;
}
static decryptOAEP(algorithm, key, data) {
const options = {
key: `-----BEGIN PRIVATE KEY-----\n${key.data.toString("base64")}\n-----END PRIVATE KEY-----`,
padding: crypto__default.constants.RSA_PKCS1_OAEP_PADDING,
};
if (algorithm.label) ;
return new Uint8Array(crypto__default.privateDecrypt(options, data)).buffer;
}
}
RsaCrypto.publicKeyUsages = ["verify", "encrypt", "wrapKey"];
RsaCrypto.privateKeyUsages = ["sign", "decrypt", "unwrapKey"];
class RsaSsaProvider extends core.RsaSsaProvider {
constructor() {
super(...arguments);
this.hashAlgorithms = [
"SHA-1", "SHA-256", "SHA-384", "SHA-512",
"shake128", "shake256",
"SHA3-256", "SHA3-384", "SHA3-512"
];
}
async onGenerateKey(algorithm, extractable, keyUsages) {
const keys = await RsaCrypto.generateKey({
...algorithm,
name: this.name,
}, extractable, keyUsages);
return {
privateKey: setCryptoKey(keys.privateKey),
publicKey: setCryptoKey(keys.publicKey),
};
}
async onSign(algorithm, key, data) {
return RsaCrypto.sign(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onVerify(algorithm, key, signature, data) {
return RsaCrypto.verify(algorithm, getCryptoKey(key), new Uint8Array(signature), new Uint8Array(data));
}
async onExportKey(format, key) {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const key = await RsaCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
return setCryptoKey(key);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
}
class RsaPssProvider extends core.RsaPssProvider {
constructor() {
super(...arguments);
this.hashAlgorithms = [
"SHA-1", "SHA-256", "SHA-384", "SHA-512",
"shake128", "shake256",
"SHA3-256", "SHA3-384", "SHA3-512"
];
}
async onGenerateKey(algorithm, extractable, keyUsages) {
const keys = await RsaCrypto.generateKey({
...algorithm,
name: this.name,
}, extractable, keyUsages);
return {
privateKey: setCryptoKey(keys.privateKey),
publicKey: setCryptoKey(keys.publicKey),
};
}
async onSign(algorithm, key, data) {
return RsaCrypto.sign(algorithm, getCryptoKey(key), new Uint8Array(data));
}
async onVerify(algorithm, key, signature, data) {
return RsaCrypto.verify(algorithm, getCryptoKey(key), new Uint8Array(signature), new Uint8Array(data));
}
async onExportKey(format, key) {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const key = await RsaCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
return setCryptoKey(key);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
}
class ShaCrypto {
static size(algorithm) {
switch (algorithm.name.toUpperCase()) {
case "SHA-1":
return 160;
case "SHA-256":
case "SHA3-256":
return 256;
case "SHA-384":
case "SHA3-384":
return 384;
case "SHA-512":
case "SHA3-512":
return 512;
default:
throw new Error("Unrecognized name");
}
}
static getAlgorithmName(algorithm) {
switch (algorithm.name.toUpperCase()) {
case "SHA-1":
return "sha1";
case "SHA-256":
return "sha256";
case "SHA-384":
return "sha384";
case "SHA-512":
return "sha512";
case "SHA3-256":
return "sha3-256";
case "SHA3-384":
return "sha3-384";
case "SHA3-512":
return "sha3-512";
default:
throw new Error("Unrecognized name");
}
}
static digest(algorithm, data) {
const hashAlg = this.getAlgorithmName(algorithm);
const hash = crypto__default.createHash(hashAlg)
.update(Buffer.from(data)).digest();
return new Uint8Array(hash).buffer;
}
}
class RsaOaepProvider extends core.RsaOaepProvider {
async onGenerateKey(algorithm, extractable, keyUsages) {
const keys = await RsaCrypto.generateKey({
...algorithm,
name: this.name,
}, extractable, keyUsages);
return {
privateKey: setCryptoKey(keys.privateKey),
publicKey: setCryptoKey(keys.publicKey),
};
}
async onEncrypt(algorithm, key, data) {
const internalKey = getCryptoKey(key);
const dataView = new Uint8Array(data);
const keySize = Math.ceil(internalKey.algorithm.modulusLength >> 3);
const hashSize = ShaCrypto.size(internalKey.algorithm.hash) >> 3;
const dataLength = dataView.byteLength;
const psLength = keySize - dataLength - 2 * hashSize - 2;
if (dataLength > keySize - 2 * hashSize - 2) {
throw new Error("Data too large");
}
const message = new Uint8Array(keySize);
const seed = message.subarray(1, hashSize + 1);
const dataBlock = message.subarray(hashSize + 1);
dataBlock.set(dataView, hashSize + psLength + 1);
const labelHash = crypto__default.createHash(internalKey.algorithm.hash.name.replace("-", ""))
.update(core.BufferSourceConverter.toUint8Array(algorithm.label || new Uint8Array(0)))
.digest();
dataBlock.set(labelHash, 0);
dataBlock[hashSize + psLength] = 1;
crypto__default.randomFillSync(seed);
const dataBlockMask = this.mgf1(internalKey.algorithm.hash, seed, dataBlock.length);
for (let i = 0; i < dataBlock.length; i++) {
dataBlock[i] ^= dataBlockMask[i];
}
const seedMask = this.mgf1(internalKey.algorithm.hash, dataBlock, seed.length);
for (let i = 0; i < seed.length; i++) {
seed[i] ^= seedMask[i];
}
if (!internalKey.pem) {
internalKey.pem = `-----BEGIN PUBLIC KEY-----\n${internalKey.data.toString("base64")}\n-----END PUBLIC KEY-----`;
}
const pkcs0 = crypto__default.publicEncrypt({
key: internalKey.pem,
padding: crypto__default.constants.RSA_NO_PADDING,
}, Buffer.from(message));
return new Uint8Array(pkcs0).buffer;
}
async onDecrypt(algorithm, key, data) {
const internalKey = getCryptoKey(key);
const keySize = Math.ceil(internalKey.algorithm.modulusLength >> 3);
const hashSize = ShaCrypto.size(internalKey.algorithm.hash) >> 3;
const dataLength = data.byteLength;
if (dataLength !== keySize) {
throw new Error("Bad data");
}
if (!internalKey.pem) {
internalKey.pem = `-----BEGIN PRIVATE KEY-----\n${internalKey.data.toString("base64")}\n-----END PRIVATE KEY-----`;
}
let pkcs0 = crypto__default.privateDecrypt({
key: internalKey.pem,
padding: crypto__default.constants.RSA_NO_PADDING,
}, Buffer.from(data));
const z = pkcs0[0];
const seed = pkcs0.subarray(1, hashSize + 1);
const dataBlock = pkcs0.subarray(hashSize + 1);
if (z !== 0) {
throw new Error("Decryption failed");
}
const seedMask = this.mgf1(internalKey.algorithm.hash, dataBlock, seed.length);
for (let i = 0; i < seed.length; i++) {
seed[i] ^= seedMask[i];
}
const dataBlockMask = this.mgf1(internalKey.algorithm.hash, seed, dataBlock.length);
for (let i = 0; i < dataBlock.length; i++) {
dataBlock[i] ^= dataBlockMask[i];
}
const labelHash = crypto__default.createHash(internalKey.algorithm.hash.name.replace("-", ""))
.update(core.BufferSourceConverter.toUint8Array(algorithm.label || new Uint8Array(0)))
.digest();
for (let i = 0; i < hashSize; i++) {
if (labelHash[i] !== dataBlock[i]) {
throw new Error("Decryption failed");
}
}
let psEnd = hashSize;
for (; psEnd < dataBlock.length; psEnd++) {
const psz = dataBlock[psEnd];
if (psz === 1) {
break;
}
if (psz !== 0) {
throw new Error("Decryption failed");
}
}
if (psEnd === dataBlock.length) {
throw new Error("Decryption failed");
}
pkcs0 = dataBlock.subarray(psEnd + 1);
return new Uint8Array(pkcs0).buffer;
}
async onExportKey(format, key) {
return RsaCrypto.exportKey(format, getCryptoKey(key));
}
async onImportKey(format, keyData, algorithm, extractable, keyUsages) {
const key = await RsaCrypto.importKey(format, keyData, { ...algorithm, name: this.name }, extractable, keyUsages);
return setCryptoKey(key);
}
checkCryptoKey(key, keyUsage) {
super.checkCryptoKey(key, keyUsage);
const internalKey = getCryptoKey(key);
if (!(internalKey instanceof RsaPrivateKey || internalKey instanceof RsaPublicKey)) {
throw new TypeError("key: Is not RSA CryptoKey");
}
}
mgf1(algorithm, seed, length = 0) {
const hashSize = ShaCrypto.size(algorithm) >> 3;
const mask = new Uint8Array(length);
const counter = new Uint8Array(4);
const chunks = Math.ceil(length / hashSize);
for (let i = 0; i < chunks; i++) {
counter[0] = i >>> 24;
counter[1] = (i >>> 16) & 255;
counter[2] = (i >>> 8) & 255;
counter[3] = i & 255;
const submask = mask.subarray(i * hashSize);
let chunk = crypto__default.createHash(algorithm.name.replace("-", ""))
.update(seed)
.update(counter)
.digest();
if (chunk.length > submask.length) {
chunk = chunk.subarray(0, submask.length);
}
submask.set(chunk);
}
return mask;
}
}
class RsaEsProvider extends core.ProviderCrypto {
constructor() {
super(...arguments);
this.name = "RSAES-PKCS1-v1_5";
this.usages = {
publicKey: ["encrypt", "wrapKey"],
privateKey: ["decrypt", "unwrapKey"],
};
}
async onGenerateKey(algorithm, extractable, keyUsages) {
const keys = await RsaCrypto.generateKey({
...algorithm,
name: this.name,
}, extractable, keyUsages);
return {
privateKey: setCryptoKey(keys.privateKey),
publicKey: setCryptoKey(keys.publicKey),
};
}
checkGenerateKeyParams(algorithm) {
this.checkRequiredProperty(algorithm, "publicExponent");
if (!(algorithm.publicExponent && algorithm.publicExponent instanceof Uint8Array)) {
throw new TypeError("publicExponent: Missing or not a Uint8Array");
}
const publicExponent = Convert.ToBa