@hackbg/miscreant-esm
Version:
(ESM port) Misuse resistant symmetric encryption library providing AES-SIV (RFC 5297), AES-PMAC-SIV, and STREAM constructions
153 lines (152 loc) • 5.34 kB
JavaScript
"use strict";
var __awaiter = this && this.__awaiter || (function (thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P ? value : new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
});
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SIV = exports.MAX_ASSOCIATED_DATA = void 0;
const constant_time_1 = require("./internals/constant-time.dist.cjs");
const wipe_1 = require("./internals/wipe.dist.cjs");
const xor_1 = require("./internals/xor.dist.cjs");
const exceptions_1 = require("./exceptions.dist.cjs");
const block_1 = require("./internals/block.dist.cjs");
const cmac_1 = require("./mac/cmac.dist.cjs");
const pmac_1 = require("./mac/pmac.dist.cjs");
const webcrypto_1 = require("./providers/webcrypto.dist.cjs");
exports.MAX_ASSOCIATED_DATA = 126;
class SIV {
static importKey(keyData, alg, provider = new webcrypto_1.WebCryptoProvider()) {
return __awaiter(this, void 0, void 0, function* () {
if (keyData.length !== 32 && keyData.length !== 64) {
throw new Error(`AES-SIV: key must be 32 or 64-bytes (got ${keyData.length}`);
}
const macKey = keyData.subarray(0, keyData.length / 2 | 0);
const encKey = keyData.subarray(keyData.length / 2 | 0);
let mac;
switch (alg) {
case "AES-SIV":
mac = yield cmac_1.CMAC.importKey(provider, macKey);
break;
case "AES-CMAC-SIV":
mac = yield cmac_1.CMAC.importKey(provider, macKey);
break;
case "AES-PMAC-SIV":
mac = yield pmac_1.PMAC.importKey(provider, macKey);
break;
default:
throw new exceptions_1.NotImplementedError(`Miscreant: algorithm not supported: ${alg}`);
}
const ctr = yield provider.importCTRKey(encKey);
return new SIV(mac, ctr);
});
}
constructor(mac, ctr) {
this._mac = mac;
this._ctr = ctr;
this._tmp1 = new block_1.default();
this._tmp2 = new block_1.default();
}
seal(plaintext, associatedData) {
return __awaiter(this, void 0, void 0, function* () {
if (associatedData.length > exports.MAX_ASSOCIATED_DATA) {
throw new Error("AES-SIV: too many associated data items");
}
const resultLength = block_1.default.SIZE + plaintext.length;
const result = new Uint8Array(resultLength);
const iv = yield this._s2v(associatedData, plaintext);
result.set(iv);
zeroIVBits(iv);
result.set(yield this._ctr.encryptCtr(iv, plaintext), iv.length);
return result;
});
}
open(sealed, associatedData) {
return __awaiter(this, void 0, void 0, function* () {
if (associatedData.length > exports.MAX_ASSOCIATED_DATA) {
throw new Error("AES-SIV: too many associated data items");
}
if (sealed.length < block_1.default.SIZE) {
throw new exceptions_1.IntegrityError("AES-SIV: ciphertext is truncated");
}
const tag = sealed.subarray(0, block_1.default.SIZE);
const iv = this._tmp1.data;
iv.set(tag);
zeroIVBits(iv);
const result = yield this._ctr.encryptCtr(iv, sealed.subarray(block_1.default.SIZE));
const expectedTag = yield this._s2v(associatedData, result);
if (!(0, constant_time_1.equal)(expectedTag, tag)) {
(0, wipe_1.wipe)(result);
throw new exceptions_1.IntegrityError("AES-SIV: ciphertext verification failure!");
}
return result;
});
}
clear() {
this._tmp1.clear();
this._tmp2.clear();
this._ctr.clear();
this._mac.clear();
return this;
}
_s2v(associated_data, plaintext) {
return __awaiter(this, void 0, void 0, function* () {
this._mac.reset();
this._tmp1.clear();
yield this._mac.update(this._tmp1.data);
this._tmp2.clear();
this._tmp2.data.set(yield this._mac.finish());
this._mac.reset();
for (const ad of associated_data) {
yield this._mac.update(ad);
this._tmp1.clear();
this._tmp1.data.set(yield this._mac.finish());
this._mac.reset();
this._tmp2.dbl();
(0, xor_1.xor)(this._tmp2.data, this._tmp1.data);
}
this._tmp1.clear();
if (plaintext.length >= block_1.default.SIZE) {
const n = plaintext.length - block_1.default.SIZE;
this._tmp1.data.set(plaintext.subarray(n));
yield this._mac.update(plaintext.subarray(0, n));
} else {
this._tmp1.data.set(plaintext);
this._tmp1.data[plaintext.length] = 0x80;
this._tmp2.dbl();
}
(0, xor_1.xor)(this._tmp1.data, this._tmp2.data);
yield this._mac.update(this._tmp1.data);
return this._mac.finish();
});
}
}
exports.SIV = SIV;
function zeroIVBits(iv) {
iv[iv.length - 8] &= 0x7f;
iv[iv.length - 4] &= 0x7f;
}