UNPKG

@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
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; } }