Skip to main content
Go to Latest
File
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license.// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license.// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license.
// deno-lint-ignore-file no-var no-inner-declarations
import { Buffer } from "../../../buffer.ts";import * as aes from "./aes.js";import Transform from "../cipher_base.js";import { GHASH } from "./ghash.js";import { xor } from "./xor.ts";import { incr32 } from "./incr32.js";
function xorTest(a, b) { var out = 0; if (a.length !== b.length) out++;
var len = Math.min(a.length, b.length); for (var i = 0; i < len; ++i) { out += a[i] ^ b[i]; }
return out;}
function calcIv(self, iv, ck) { if (iv.length === 12) { self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])]); return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])]); } var ghash = new GHASH(ck); var len = iv.length; var toPad = len % 16; ghash.update(iv); if (toPad) { toPad = 16 - toPad; ghash.update(Buffer.alloc(toPad, 0)); } ghash.update(Buffer.alloc(8, 0)); var ivBits = len * 8; var tail = Buffer.alloc(8); // Fixed from the original // https://github.com/crypto-browserify/browserify-aes/issues/58#issuecomment-451778917 tail.writeUIntBE(ivBits, 2, 6); ghash.update(tail); self._finID = ghash.state; var out = Buffer.from(self._finID); incr32(out); return out;}export function StreamCipher(mode, key, iv, decrypt) { Transform.call(this);
var h = Buffer.alloc(4, 0);
this._cipher = new aes.AES(key); var ck = this._cipher.encryptBlock(h); this._ghash = new GHASH(ck); iv = calcIv(this, iv, ck);
this._prev = Buffer.from(iv); this._cache = Buffer.allocUnsafe(0); this._secCache = Buffer.allocUnsafe(0); this._decrypt = decrypt; this._alen = 0; this._len = 0; this._mode = mode;
this._authTag = null; this._called = false;}
// StreamCipher inherts TransformStreamCipher.prototype = Object.create(Transform.prototype, { constructor: { value: StreamCipher, enumerable: false, writable: true, configurable: true, },});
StreamCipher.prototype._update = function (chunk) { if (!this._called && this._alen) { var rump = 16 - (this._alen % 16); if (rump < 16) { rump = Buffer.alloc(rump, 0); this._ghash.update(rump); } }
this._called = true; var out = this._mode.encrypt(this, chunk); if (this._decrypt) { this._ghash.update(chunk); } else { this._ghash.update(out); } this._len += chunk.length; return out;};
StreamCipher.prototype._final = function () { if (this._decrypt && !this._authTag) { throw new Error("Unsupported state or unable to authenticate data"); }
var tag = xor( this._ghash.final(this._alen * 8, this._len * 8), this._cipher.encryptBlock(this._finID), ); if (this._decrypt && xorTest(tag, this._authTag)) { throw new Error("Unsupported state or unable to authenticate data"); }
this._authTag = tag; this._cipher.scrub();};
StreamCipher.prototype.getAuthTag = function getAuthTag() { if (this._decrypt || !Buffer.isBuffer(this._authTag)) { throw new Error("Attempting to get auth tag in unsupported state"); }
return this._authTag;};
StreamCipher.prototype.setAuthTag = function setAuthTag(tag) { if (!this._decrypt) { throw new Error("Attempting to set auth tag in unsupported state"); }
this._authTag = tag;};
StreamCipher.prototype.setAAD = function setAAD(buf) { if (this._called) { throw new Error("Attempting to set AAD in unsupported state"); }
this._ghash.update(buf); this._alen += buf.length;};
export default StreamCipher;