@hackbg/miscreant-esm
Version:
(ESM port) Misuse resistant symmetric encryption library providing AES-SIV (RFC 5297), AES-PMAC-SIV, and STREAM constructions
141 lines (140 loc) • 4.62 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.PMAC = void 0;
const block_1 = require("../internals/block.dist.cjs");
const constant_time_1 = require("../internals/constant-time.dist.cjs");
const ctz_1 = require("../internals/ctz.dist.cjs");
const xor_1 = require("../internals/xor.dist.cjs");
const PRECOMPUTED_BLOCKS = 31;
class PMAC {
static importKey(provider, keyData) {
return __awaiter(this, void 0, void 0, function* () {
const cipher = yield provider.importBlockCipherKey(keyData);
const tmp = new block_1.default();
yield cipher.encryptBlock(tmp);
const l = new Array(PRECOMPUTED_BLOCKS);
for (let i = 0; i < PRECOMPUTED_BLOCKS; i++) {
l[i] = tmp.clone();
tmp.dbl();
}
const lInv = l[0].clone();
const lastBit = lInv.data[block_1.default.SIZE - 1] & 0x01;
for (let i = block_1.default.SIZE - 1; i > 0; i--) {
const carry = (0, constant_time_1.select)(lInv.data[i - 1] & 1, 0x80, 0);
lInv.data[i] = lInv.data[i] >>> 1 | carry;
}
lInv.data[0] >>>= 1;
lInv.data[0] ^= (0, constant_time_1.select)(lastBit, 0x80, 0);
lInv.data[block_1.default.SIZE - 1] ^= (0, constant_time_1.select)(lastBit, block_1.default.R >>> 1, 0);
return new PMAC(cipher, l, lInv);
});
}
constructor(cipher, l, lInv) {
this._finished = false;
this._cipher = cipher;
this._L = l;
this._LInv = lInv;
this._buffer = new block_1.default();
this._bufferPos = 0;
this._counter = 0;
this._offset = new block_1.default();
this._tag = new block_1.default();
}
reset() {
this._buffer.clear();
this._bufferPos = 0;
this._counter = 0;
this._offset.clear();
this._tag.clear();
this._finished = false;
return this;
}
clear() {
this.reset();
this._cipher.clear();
}
update(data) {
return __awaiter(this, void 0, void 0, function* () {
if (this._finished) {
throw new Error("pmac: already finished");
}
const left = block_1.default.SIZE - this._bufferPos;
let dataPos = 0;
let dataLength = data.length;
if (dataLength > left) {
this._buffer.data.set(data.slice(0, left), this._bufferPos);
dataPos += left;
dataLength -= left;
yield this._processBuffer();
}
while (dataLength > block_1.default.SIZE) {
this._buffer.data.set(data.slice(dataPos, dataPos + block_1.default.SIZE));
dataPos += block_1.default.SIZE;
dataLength -= block_1.default.SIZE;
yield this._processBuffer();
}
if (dataLength > 0) {
this._buffer.data.set(data.slice(dataPos, dataPos + dataLength), this._bufferPos);
this._bufferPos += dataLength;
}
return this;
});
}
finish() {
return __awaiter(this, void 0, void 0, function* () {
if (this._finished) {
throw new Error("pmac: already finished");
}
if (this._bufferPos === block_1.default.SIZE) {
(0, xor_1.xor)(this._tag.data, this._buffer.data);
(0, xor_1.xor)(this._tag.data, this._LInv.data);
} else {
(0, xor_1.xor)(this._tag.data, this._buffer.data.slice(0, this._bufferPos));
this._tag.data[this._bufferPos] ^= 0x80;
}
yield this._cipher.encryptBlock(this._tag);
this._finished = true;
return this._tag.clone().data;
});
}
_processBuffer() {
return __awaiter(this, void 0, void 0, function* () {
(0, xor_1.xor)(this._offset.data, this._L[(0, ctz_1.ctz)(this._counter + 1)].data);
(0, xor_1.xor)(this._buffer.data, this._offset.data);
this._counter++;
yield this._cipher.encryptBlock(this._buffer);
(0, xor_1.xor)(this._tag.data, this._buffer.data);
this._bufferPos = 0;
});
}
}
exports.PMAC = PMAC;