@hackbg/miscreant-esm
Version:
(ESM port) Misuse resistant symmetric encryption library providing AES-SIV (RFC 5297), AES-PMAC-SIV, and STREAM constructions
76 lines (75 loc) • 2.24 kB
JavaScript
import {AEAD} from "./aead.dist.mjs";
import {WebCryptoProvider} from "./providers/webcrypto.dist.mjs";
export const NONCE_SIZE = 8;
export const LAST_BLOCK_FLAG = 1;
export const COUNTER_MAX = 0xFFFFFFFF;
export class StreamEncryptor {
static async importKey(keyData, nonce, alg, provider = new WebCryptoProvider()) {
return new StreamEncryptor(await AEAD.importKey(keyData, alg, provider), nonce);
}
_aead;
_nonce_encoder;
constructor(aead, nonce) {
this._aead = aead;
this._nonce_encoder = new NonceEncoder(nonce);
}
async seal(plaintext, lastBlock = false, associatedData = new Uint8Array(0)) {
return this._aead.seal(plaintext, this._nonce_encoder.next(lastBlock), associatedData);
}
clear() {
this._aead.clear();
return this;
}
}
export class StreamDecryptor {
static async importKey(keyData, nonce, alg, provider = new WebCryptoProvider()) {
return new StreamDecryptor(await AEAD.importKey(keyData, alg, provider), nonce);
}
_aead;
_nonce_encoder;
constructor(aead, nonce) {
this._aead = aead;
this._nonce_encoder = new NonceEncoder(nonce);
}
async open(ciphertext, lastBlock = false, associatedData = new Uint8Array(0)) {
return this._aead.open(ciphertext, this._nonce_encoder.next(lastBlock), associatedData);
}
clear() {
this._aead.clear();
return this;
}
}
class NonceEncoder {
buffer;
view;
array;
counter;
finished;
constructor(noncePrefix) {
if (noncePrefix.length !== NONCE_SIZE) {
throw new Error(`STREAM: nonce must be 8-bits (got ${noncePrefix.length}`);
}
this.buffer = new ArrayBuffer(NONCE_SIZE + 4 + 1);
this.view = new DataView(this.buffer);
this.array = new Uint8Array(this.buffer);
this.array.set(noncePrefix);
this.counter = 0;
this.finished = false;
}
next(lastBlock) {
if (this.finished) {
throw new Error("STREAM: already finished");
}
this.view.setInt32(8, this.counter, false);
if (lastBlock) {
this.view.setInt8(12, LAST_BLOCK_FLAG);
this.finished = true;
} else {
this.counter += 1;
if (this.counter > COUNTER_MAX) {
throw new Error("STREAM counter overflowed");
}
}
return this.array;
}
}