I've recently been working on a project that
involves encryption in web browser extensions, like take some text and encrypt
it using a password. Pretty standard, except how do you do that in JavaScript?
Due to my obsessive aversion to third party client dependencies, the decision
was clear: I must read the specifications to all
the relevant
cryptographic standards
and implement the algorithms myself in Javascript, from scratch.
It never dawned on me that in the half-decade since I last played with JS
crypto, perhaps things might have advanced past the previous status-quo of "roll
your own crypto library #yolo". I neglected to consider the possibility that all of
this functionality might be already baked into modern web browsers. Well turns
out it is. While I was putting the finishing touches on
my library (using a Web Worker
to make it multi-threaded), I stumbled onto documentation for the
Crypto.subtle API,
a.k.a. everything I had just built but way faster due to being compiled code
that takes advantage of native cryptographic CPU instructions. Oops.
I can't bring myself to straight-up delete this code, but in reality it should
never be used by anyone for any reason. Rather than try to make some sort of point
(this is pointless), I'll leave it here for those who click "Read More."
'use strict';
var Cryptoops = function() {
return this;
}
Cryptoops.DataUtils = {
stringToBytes: function(str) {
return Array.from(new TextEncoder("utf-8").encode(str))
},
bytesToString: function(uint8Array) {
return new TextDecoder("utf-8").decode(Uint8Array.from(uint8Array));
},
bytesToWords: function(uint8Array, reverseEndian) {
var words = [],
_toWord = this.bytesToWord;
if (reverseEndian)
for (var i=0; i<uint8Array.length; i+=4)
words.push(_toWord(
uint8Array[i+3],
uint8Array[i+2],
uint8Array[i+1],
uint8Array[i]
));
else
for (var i=0; i<uint8Array.length; i+=4)
words.push(_toWord(
uint8Array[i],
uint8Array[i+1],
uint8Array[i+2],
uint8Array[i+3]
));
return words;
},
bytesToWord: function() {
if (arguments.length == 4)
return ((arguments[0] & 255) << 24)
| ((arguments[1] & 255) << 16)
| ((arguments[2] & 255) << 8)
| (arguments[3] & 255);
var joined = 0;
for (var i = arguments.length-1; i >= 0; i--)
joined |= (arguments[i] & 255) << 8*(arguments.length-1-i);
return joined;
},
wordsToBytes: function(uint32Array) {
var byteArray = [],
_wordToBytes = this.wordToBytes;
for (var i=0; i<uint32Array.length; i++) {
var wordBytes = _wordToBytes(uint32Array[i]);
for (var j=0; j<wordBytes.length; j++) {
byteArray.push(wordBytes[j]);
}
}
return byteArray;
},
wordToBytes: function(word) {
return [
((word >>> 24) & 255),
((word >>> 16) & 255),
((word >>> 8) & 255),
(word & 255)
];
},
hexToBytes: function(hex) {
hex = hex.replace('0x', '');
if (hex.length % 2 == 1) hex = '0' + hex;
var bytes = [];
for (var i=0; i<hex.length; i+=2)
bytes.push(parseInt(hex.substr(i, 2),16));
return bytes;
},
bytesToHex: function(uint8Array) {
var hex = '';
for (var i=0; i<uint8Array.length; i++) {
hex += (uint8Array[i].toString(16).length == 1 ? '0' : '')
+ uint8Array[i].toString(16);
}
return hex;
},
};
Cryptoops.PaddingMode = function() {};
Cryptoops.PaddingMode.prototype.initialize = function(options) {
if (!options) options = {};
for (var attr in options) this[attr] = options[attr];
return this;
}
Cryptoops.PaddingMode.prototype.cipher = null;
Cryptoops.ZeroPadding = function(options) {
return this.initialize(options);
}
Cryptoops.ZeroPadding.prototype = Object.create(Cryptoops.PaddingMode.prototype);
Cryptoops.ZeroPadding.prototype.doPad = function(uint8Array) {
if ((uint8Array.length * 8) % this.cipher.getBlockSize() != 0)
for (var i=0; uint8Array.length % (this.cipher.getBlockSize() / 8) != 0; i++)
uint8Array.push(0);
return uint8Array;
}
Cryptoops.ZeroPadding.prototype.undoPad = function(uint8Array) {
return uint8Array;
}
Cryptoops.AnsiX923 = function(options) {
return this.initialize(options);
}
Cryptoops.AnsiX923.prototype = Object.create(Cryptoops.PaddingMode.prototype);
Cryptoops.AnsiX923.prototype.doPad = function(uint8Array) {
for (var i=0; i == 0 || uint8Array.length % (this.cipher.getBlockSize() / 8) != 0; i++)
uint8Array.push(0);
uint8Array.pop();
uint8Array.push(i);
return uint8Array;
}
Cryptoops.AnsiX923.prototype.undoPad = function(uint8Array) {
var padLength = uint8Array[uint8Array.length - 1];
uint8Array.splice(uint8Array.length - padLength);
return uint8Array;
}
Cryptoops.PKCS7 = function(options) {
return this.initialize(options);
}
Cryptoops.PKCS7.prototype = Object.create(Cryptoops.PaddingMode.prototype);
Cryptoops.PKCS7.prototype.doPad = function(uint8Array) {
var blockBytes = (this.cipher.getBlockSize() / 8),
padCount = uint8Array.length % blockBytes != 0 ? blockBytes - (uint8Array.length % blockBytes) : blockBytes;
for (var i=0; i == 0 || uint8Array.length % (this.cipher.getBlockSize() / 8) != 0; i++)
uint8Array.push(padCount);
return uint8Array;
}
Cryptoops.PKCS7.prototype.undoPad = function(uint8Array) {
var padLength = uint8Array[uint8Array.length - 1];
uint8Array.splice(uint8Array.length - padLength);
return uint8Array;
}
Cryptoops.BlockCipherMode = function() {};
Cryptoops.BlockCipherMode.prototype.initialize = function(options) {
if (!options) options = {};
for (var attr in options) this[attr] = options[attr];
return this;
}
Cryptoops.BlockCipherMode.prototype.cipher = null;
Cryptoops.BlockCipherMode.prototype.initIV = function() {
var iv = Cryptoops.DataUtils.bytesToWords(this.cipher.getIV());
if (iv.length != this.getWordsPerBlock())
throw new Error('Cryptoops.BlockCipherMode: Initial vector size must match block size!');
this.iv = iv;
}
Cryptoops.BlockCipherMode.prototype.getWordsPerBlock = function() {
return this.cipher.getBlockSize() / 32;
}
Cryptoops.BlockCipherMode.prototype.xorBlock = function(block1, block2) {
for (var i=0; i < block1.length; i++) block1[i] ^= block2[i];
return block1;
}
Cryptoops.ECB = function(options) {
this.initialize(options);
return this;
}
Cryptoops.ECB.prototype = Object.create(Cryptoops.BlockCipherMode.prototype);
Cryptoops.ECB.prototype.encryptBlocks = function(words) {
var ciphertext = [],
wordsPerBlock = this.getWordsPerBlock();
for (var i=0; i<words.length; i+=wordsPerBlock) {
var encryptedBlock = this.cipher.blockEncrypt(words.slice(i, i+wordsPerBlock));
for (var j=0; j<encryptedBlock.length; j++) {
ciphertext.push(encryptedBlock[j]);
}
}
return ciphertext;
}
Cryptoops.ECB.prototype.decryptBlocks = function(words) {
var plaintext = [],
wordsPerBlock = this.getWordsPerBlock();
for (var i=0; i<words.length; i+=wordsPerBlock)
{
var block = words.slice(i, i+wordsPerBlock),
decryptedBlock = this.cipher.blockDecrypt(words.slice(i, i+wordsPerBlock));
for (var j=0; j<decryptedBlock.length; j++) {
plaintext.push(decryptedBlock[j]);
}
}
return plaintext;
}
Cryptoops.CBC = function(options) {
this.initialize(options);
this.initIV();
return this;
}
Cryptoops.CBC.prototype = Object.create(Cryptoops.BlockCipherMode.prototype);
Cryptoops.CBC.prototype.iv = [];
Cryptoops.CBC.prototype.encryptBlocks = function(words) {
var ciphertext = [],
wordsPerBlock = this.getWordsPerBlock(),
_prevBlock = this.iv;
for (var i=0; i<words.length; i+=wordsPerBlock) {
var block = words.slice(i, i+wordsPerBlock),
xorBlock = this.xorBlock(block, _prevBlock),
encryptedBlock = this.cipher.blockEncrypt(xorBlock),
_prevBlock = encryptedBlock;
for (var j=0; j<encryptedBlock.length; j++) {
ciphertext.push(encryptedBlock[j]);
}
}
return ciphertext;
}
Cryptoops.CBC.prototype.decryptBlocks = function(words) {
var plaintext = [],
wordsPerBlock = this.getWordsPerBlock(),
_prevBlock = this.iv;
for (var i=0; i<words.length; i+=wordsPerBlock)
{
var block = words.slice(i, i+wordsPerBlock),
decryptedBlock = this.cipher.blockDecrypt(block),
xorDecryptedBlock = this.xorBlock(decryptedBlock, _prevBlock),
_prevBlock = block;
for (var j=0; j<xorDecryptedBlock.length; j++) {
plaintext.push(xorDecryptedBlock[j]);
}
}
return plaintext;
}
Cryptoops.CFB = function(options) {
this.initialize(options);
this.initIV();
return this;
}
Cryptoops.CFB.prototype = Object.create(Cryptoops.BlockCipherMode.prototype);
Cryptoops.CFB.prototype.iv = [];
Cryptoops.CFB.prototype.encryptBlocks = function(words) {
var ciphertext = [],
wordsPerBlock = this.getWordsPerBlock(),
_prevBlock = this.iv;
for (var i=0; i<words.length; i+=wordsPerBlock) {
var cipherBlock = this.cipher.blockEncrypt(_prevBlock),
block = words.slice(i, i+wordsPerBlock),
xorEncryptedBlock = this.xorBlock(block, cipherBlock),
_prevBlock = xorEncryptedBlock;
for (var j=0; j<xorEncryptedBlock.length; j++) {
ciphertext.push(xorEncryptedBlock[j]);
}
}
return ciphertext;
}
Cryptoops.CFB.prototype.decryptBlocks = function(words) {
var plaintext = [],
wordsPerBlock = this.getWordsPerBlock(),
_prevBlock = this.iv;
for (var i=0; i<words.length; i+=wordsPerBlock)
{
var cipherBlock = this.cipher.blockEncrypt(_prevBlock),
block = words.slice(i, i+wordsPerBlock),
xorDecryptedBlock = this.xorBlock(cipherBlock, block),
_prevBlock = block;
for (var j=0; j<xorDecryptedBlock.length; j++) {
plaintext.push(xorDecryptedBlock[j]);
}
}
return plaintext;
}
Cryptoops.BlockCipher = function() {};
Cryptoops.BlockCipher.prototype.initialize = function(options) {
if (!options) options = {};
for (var attr in options) this[attr] = options[attr];
if (!options.blockMode) this.blockMode = Cryptoops.CBC;
if (!options.padMode) this.padMode = Cryptoops.PKCS7;
if (options.passphrase) {
if (!options.passphraseSalt) this.generateRandomPassphraseSalt();
this.populateKeyAndIVFromPassphrase();
}
return this;
}
Cryptoops.BlockCipher.prototype.blockMode = null;
Cryptoops.BlockCipher.prototype.padMode = null;
Cryptoops.BlockCipher.prototype.iv = null;
Cryptoops.BlockCipher.prototype.passphrase = '';
Cryptoops.BlockCipher.prototype.passphraseSalt = null;
Cryptoops.BlockCipher.prototype.passphrasePBKDF2Iterations = 1;
Cryptoops.BlockCipher.prototype.debugMode = false;
Cryptoops.BlockCipher.prototype.generateRandomIV = function() {
var _ivTmp = new Uint8Array(this.getBlockSize() / 8),
iv = [];
window.crypto.getRandomValues(_ivTmp);
for (var i=0; i<_ivTmp.length; i++)
iv.push(_ivTmp[i]);
this.iv = iv;
}
Cryptoops.BlockCipher.prototype.generateRandomPassphraseSalt = function() {
var _passphraseSaltTmp = new Uint8Array(this.getBlockSize() / 8),
passphraseSalt = [];
window.crypto.getRandomValues(_passphraseSaltTmp);
for (var i=0; i<_passphraseSaltTmp.length; i++)
passphraseSalt.push(_passphraseSaltTmp[i]);
this.passphraseSalt = passphraseSalt;
}
Cryptoops.BlockCipher.prototype.getIV = function() {
return this.iv;
}
Cryptoops.BlockCipher.prototype.getPassphraseSalt = function() {
return this.passphraseSalt;
}
Cryptoops.BlockCipher.prototype.getPassphrasePBKDF2Iterations = function() {
return this.passphrasePBKDF2Iterations;
}
Cryptoops.BlockCipher.prototype.populateKeyAndIVFromPassphrase = function() {
var keyBytes = this.getKeyLength() / 8,
blockBytes = this.getBlockSize() / 8,
keyIV = new Cryptoops.PBKDF2({
keySize: (keyBytes + blockBytes),
iterations: this.getPassphrasePBKDF2Iterations()
})
.compute(this.passphrase, this.getPassphraseSalt());
this.iv = keyIV.splice(keyIV.length - blockBytes);
this.passphrase = '';
this.setKey(keyIV);
}
Cryptoops.BlockCipher.prototype.initCipherFromPassphraseThreaded = function(passphrase, iterations, hasher, callback) {
this.generateRandomPassphraseSalt();
}
Cryptoops.BlockCipher.prototype.debugWrite = function() {
if (this.debugMode) console.log.apply(console, arguments);
}
Cryptoops.BlockCipher.prototype.encrypt = function(plaintext) {
if (!Array.isArray(plaintext)) {
plaintext = Cryptoops.DataUtils.stringToBytes(plaintext);
}
var plaintext = new this.padMode({ cipher: this}).doPad(plaintext),
plaintext = Cryptoops.DataUtils.bytesToWords(plaintext),
operator = new this.blockMode({ cipher: this }),
ciphertext = operator.encryptBlocks(plaintext),
ciphertext = Cryptoops.DataUtils.wordsToBytes(ciphertext);
return ciphertext;
}
Cryptoops.BlockCipher.prototype.decrypt = function(ciphertext) {
var ciphertext = Cryptoops.DataUtils.bytesToWords(ciphertext),
operator = new this.blockMode({ cipher: this }),
plaintext = operator.decryptBlocks(ciphertext),
plaintext = Cryptoops.DataUtils.wordsToBytes(plaintext),
plaintext = new this.padMode({ cipher: this }).undoPad(plaintext);
return plaintext;
}
Cryptoops.PBKDF2 = function(options) {
if (!options) options = {};
if (options.keySize)
if (options.keySize % 4 != 0)
throw new Error('Cryptoops.PBKDF2: Key size must be a multiple of 4 bytes.');
else
this.keySize = options.keySize;
if (options.iterations)
this.iterations = options.iterations;
this.hasher = options.hasher ? options.hasher : Cryptoops.SHA1;
return this;
}
Cryptoops.PBKDF2.prototype.keySize = 32;
Cryptoops.PBKDF2.prototype.hasher = null;
Cryptoops.PBKDF2.prototype.iterations = 1;
Cryptoops.PBKDF2.prototype.compute = function(passphrase, salt, options) {
if (!salt) salt = [];
if (!options) options = {};
if (!options.returnFormat) options.returnFormat = 'bytes';
if (!Array.isArray(salt)) {
salt = Cryptoops.DataUtils.stringToBytes(salt);
}
var hmac = new Cryptoops.HMAC({ hasher: this.hasher, passphrase: passphrase }),
key = [],
keySize = this.keySize;
for (var i=1; key.length < keySize; i++) {
var block = hmac.hash(salt, {stream: true})
.hash(Cryptoops.DataUtils.wordToBytes(i), {returnFormat: 'words'});
var temp = block;
for (var j=1; j<this.iterations; j++) {
temp = hmac.hash(Cryptoops.DataUtils.wordsToBytes(temp), {returnFormat: 'words'});
for (var k=0; k<block.length; k++) {
block[k] ^= temp[k];
}
}
var blockBytes = Cryptoops.DataUtils.wordsToBytes(block);
for (var j=0; j<blockBytes.length; j++) {
key.push(blockBytes[j]);
}
}
key = key.slice(0, keySize);
switch (options.returnFormat) {
case 'hex':
return Cryptoops.DataUtils.bytesToHex(key);
case 'words':
return Cryptoops.DataUtils.bytesToWords(key);
case 'bytes':
default:
return key;
}
}
Cryptoops.HMAC = function(options) {
if (!options) options = {};
var hasher = options.hasher ? options.hasher : Cryptoops.SHA256;
if (!options.passphrase)
throw new Error('Cryptoops.HMAC: You must specify a passphrase for the HMAC class initialization function.');
this.hasherInstance = new hasher();
var blockBytes = this.hasherInstance.getBlockSize() / 8;
if (!Array.isArray(options.passphrase))
var key = Cryptoops.DataUtils.stringToBytes(options.passphrase)
else
var key = options.passphrase;
if (key.length > blockBytes) {
key = this.hasherInstance.hash(key, {returnFormat: 'bytes'});
this.hasherInstance.clear();
} else if (key.length < blockBytes) {
key = this.padKey(key, blockBytes, 0x00);
}
key = Cryptoops.DataUtils.bytesToWords(key);
var oPad = Cryptoops.DataUtils.bytesToWords(this.padKey([], blockBytes, 0x5c));
var iPad = Cryptoops.DataUtils.bytesToWords(this.padKey([], blockBytes, 0x36));
this.iKeyPad = Cryptoops.DataUtils.wordsToBytes(this.xorBlock(iPad, key));
this.oKeyPad = Cryptoops.DataUtils.wordsToBytes(this.xorBlock(oPad, key));
return this;
}
Cryptoops.HMAC.prototype.hasherInstance = null;
Cryptoops.HMAC.prototype.hasStreamedData = false;
Cryptoops.HMAC.prototype.iKeyPad = null;
Cryptoops.HMAC.prototype.oKeyPad = null;
Cryptoops.HMAC.prototype.reset = function() {
this.hasStreamedData = false;
this.hasherInstance.clear();
}
Cryptoops.HMAC.prototype.hash = function(data, options) {
if (!options) options = {};
if (!options.stream) options.stream = false;
if (!Array.isArray(data))
data = Cryptoops.DataUtils.stringToBytes(data);
if (this.hasStreamedData == false) {
var _tmpData = [],
_iKeyPad = this.iKeyPad;
for (var i=0; i<_iKeyPad.length; i++)
_tmpData.push(_iKeyPad[i]);
for (var i=0; i<data.length; i++)
_tmpData.push(data[i]);
data = _tmpData;
this.hasStreamedData = true;
}
this.hasherInstance.hash(data, {stream: true});
if (options.stream == false)
return this.finalize(options);
return this;
}
Cryptoops.HMAC.prototype.finalize = function(options) {
if (!options) options = {};
if (!options.returnFormat) options.returnFormat = 'hex';
var innerHash = this.hasherInstance.finalize({returnFormat: 'bytes'});
this.hasherInstance.clear();
var finalBytes = [],
_oKeyPad = this.oKeyPad;
for (var i=0; i<_oKeyPad.length; i++)
finalBytes.push(_oKeyPad[i]);
for (var i=0; i<innerHash.length; i++)
finalBytes.push(innerHash[i]);
var hmac = this.hasherInstance.hash(finalBytes, {returnFormat: options.returnFormat});
this.reset();
return hmac;
}
Cryptoops.HMAC.prototype.padKey = function(key, paddedLength, padValue) {
for (var i=0; key.length < paddedLength; i++) key.push(padValue);
return key;
}
Cryptoops.HMAC.prototype.xorBlock = function(block1, block2) {
for (var i=0; i < block1.length; i++) block1[i] ^= block2[i];
return block1;
}
Cryptoops.Hasher = function() {};
Cryptoops.Hasher.prototype.buffer = [];
Cryptoops.Hasher.prototype.h = [];
Cryptoops.Hasher.prototype.processedLength = 0;
Cryptoops.Hasher.prototype.reverseEndianWords = false;
Cryptoops.Hasher.prototype.resetState = function() {
this.buffer = [];
this.processedLength = 0;
}
Cryptoops.Hasher.prototype.pad = function() {
var finalLength = this.processedLength + (this.buffer.length * 8);
this.buffer.push(128);
for (var i=0; (this.buffer.length + 8) % 64 != 0; i++)
this.buffer.push(0);
var finalBinary = finalLength.toString(2);
for (var i=0; finalBinary.length % 64 != 0; i++)
finalBinary = '0' + finalBinary;
if (this.reverseEndianWords == false) {
var finalWord1 = Cryptoops.DataUtils.wordToBytes(parseInt(finalBinary.substr(0, 32), 2)),
finalWord2 = Cryptoops.DataUtils.wordToBytes(parseInt(finalBinary.substr(32), 2));
} else {
var finalWord1 = Cryptoops.DataUtils.wordToBytes(this.reverseWord(parseInt(finalBinary.substr(32), 2))),
finalWord2 = Cryptoops.DataUtils.wordToBytes(this.reverseWord(parseInt(finalBinary.substr(0, 32), 2)));
}
for (var i=0; i<finalWord1.length; i++) this.buffer.push(finalWord1[i]);
for (var i=0; i<finalWord2.length; i++) this.buffer.push(finalWord2[i]);
}
Cryptoops.Hasher.prototype.finalize = function(options) {
if (!options) options = {};
if (!options.returnFormat) options.returnFormat = 'hex';
this.pad();
this.hash([], {stream: true});
if (this.reverseEndianWords) {
this.h = [
this.reverseWord(this.h[0]),
this.reverseWord(this.h[1]),
this.reverseWord(this.h[2]),
this.reverseWord(this.h[3])
];
}
var output = this.h.slice(0);
this.clear();
switch (options.returnFormat) {
case 'hex':
return Cryptoops.DataUtils.bytesToHex(Cryptoops.DataUtils.wordsToBytes(output));
case 'bytes':
return Cryptoops.DataUtils.wordsToBytes(output);
case 'words':
return output;
}
}
Cryptoops.Hasher.prototype.getBlockSize = function() {
return this.blockSize;
}
Cryptoops.Hasher.prototype.getOutlen = function() {
return this.outlen;
}
Cryptoops.Hasher.prototype.reverseWord = function(word) {
return Cryptoops.DataUtils.bytesToWord(
word & 255,
(word >>> 8) & 255,
(word >>> 16) & 255,
(word >>> 24) & 255
);
}
Cryptoops.SHA256 = function() {
this.clear();
return this;
}
Cryptoops.SHA256.prototype = Object.create(Cryptoops.Hasher.prototype);
Cryptoops.SHA256.prototype.blockSize = 512;
Cryptoops.SHA256.prototype.outlen = 256;
Cryptoops.SHA256.prototype.k = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
Cryptoops.SHA256.prototype.clear = function() {
this.resetState();
this.h = [
0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19
];
}
Cryptoops.SHA256.prototype.hash = function(data, options) {
if (!options) options = {};
if (!options.stream) options.stream = false;
if (!Array.isArray(data))
data = Cryptoops.DataUtils.stringToBytes(data);
var _buffer = [],
realBuffer = this.buffer;
for (var i=0; i<realBuffer.length; i++)
_buffer.push(realBuffer[i]);
for (var i=0; i<data.length; i++)
_buffer.push(data[i]);
var _processedLength = this.processedLength,
k = this.k,
h = this.h;
for (var i=0; (i+64) <= _buffer.length; i += 64) {
var w = Cryptoops.DataUtils.bytesToWords(_buffer.slice(i, i+64)),
a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], _h = h[7];
for (var t = 0; t < 64; t++) {
if (t >= 16) {
w[t] = (
(((w[t-2] >>> 17) | (w[t-2] << 15)) ^ ((w[t-2] >>> 19) | (w[t-2] << 13)) ^ (w[t-2] >>> 10))
+ w[t-7]
+ (((w[t-15] >>> 7) | (w[t-15] << 25)) ^ ((w[t-15] >>> 18) | (w[t-15] << 14)) ^ (w[t-15] >>> 3))
+ w[t-16]
);
}
var temp1 = (
_h
+ (((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)))
+ ((e & f) ^ (~e & g))
+ k[t]
+ w[t]
) % Math.pow(2,32);
var temp2 = (
((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)))
+ ((a & b) ^ (a & c) ^ (b & c)
);
var _h = g, g = f, f = e, e = d + temp1, d = c, c = b, b = a, a = temp1 + temp2;
}
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
h[4] += e;
h[5] += f;
h[6] += g;
h[7] += _h;
_processedLength += 512;
}
this.buffer = _buffer.slice(i);
this.processedLength = _processedLength;
if (options.stream == false)
return this.finalize(options);
return this;
}
Cryptoops.SHA1 = function() {
this.clear();
return this;
}
Cryptoops.SHA1.prototype = Object.create(Cryptoops.Hasher.prototype);
Cryptoops.SHA1.prototype.blockSize = 512;
Cryptoops.SHA1.prototype.outlen = 160;
Cryptoops.SHA1.prototype.clear = function() {
this.resetState();
this.h = [
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
];
}
Cryptoops.SHA1.prototype.hash = function(data, options) {
if (!options) options = {};
if (!options.stream) options.stream = false;
if (!Array.isArray(data))
data = Cryptoops.DataUtils.stringToBytes(data);
var _buffer = [],
realBuffer = this.buffer;
for (var i=0; i<realBuffer.length; i++)
_buffer.push(realBuffer[i]);
for (var i=0; i<data.length; i++)
_buffer.push(data[i]);
var _processedLength = this.processedLength,
h = this.h;
for (var i=0; (i+64) <= _buffer.length; i += 64) {
var a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], w = Cryptoops.DataUtils.bytesToWords(_buffer.slice(i, i+64));
for (var t = 0; t < 80; t++)
{
if (t >= 16)
{
var _wt = w[t-3] ^ w[t-8] ^ w[t-14] ^ w[t-16];
w[t] = (_wt << 1) | (_wt >>> 31);
}
var temp = ((a << 5) | (a >>> 27)) + e + w[t];
if (t < 20)
temp += ((b & c) | (~b & d)) + 0x5A827999;
else if (t < 40)
temp += (b ^ c ^ d) + 0x6ed9eba1;
else if (t < 60)
temp += ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDC;
else
temp += (b ^ c ^ d) + 0xCA62C1D6;
var e = d, d = c, c = (b << 30) | (b >>> 2), b = a, a = temp;
}
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
h[4] += e;
_processedLength += 512;
}
this.buffer = _buffer.slice(i);
this.processedLength = _processedLength;
if (options.stream == false)
return this.finalize(options);
return this;
}
Cryptoops.AES = function(options) {
this.initialize(options);
if (this.key.length)
this.setKey(this.key);
return this;
}
Cryptoops.AES.prototype = Object.create(Cryptoops.BlockCipher.prototype);
Cryptoops.AES.prototype.key = [];
Cryptoops.AES.prototype.keyExpanded = [];
Cryptoops.AES.prototype.keyLength = 256;
Cryptoops.AES.prototype.blockSize = 128;
Cryptoops.AES.prototype.debugMode = false;
Cryptoops.AES.prototype.setKey = function(key) {
if (!Array.isArray(key))
key = Cryptoops.DataUtils.stringToBytes(key);
if (key.length != 16 && key.length != 24 && key.length != 32)
throw new Error('Cryptoops.AES: key must be 16, 24, or 32 bytes!');
this.key = key;
this.keyExpanded = [];
this.keyLength = key.length * 8;
return this;
}
Cryptoops.AES.prototype.getKey = function() { return this.key; }
Cryptoops.AES.prototype.getKeyLength = function() { return this.keyLength; }
Cryptoops.AES.prototype.getBlockSize = function() { return this.blockSize; }
Cryptoops.AES.prototype.blockEncrypt = function(state) {
this.debugWrite('Encrypting...');
var k = this.getExpandedKey();
state = this.addRoundKey(state, k.slice(0, 4));
for (var i=0; i<(this.key.length / 4) + 5; i++) {
state = this.addRoundKey(this.mixColumns(this.shiftRows(this.bytesSub(state))), k.slice((i+1)*4, ((i+1)*4)+4));
}
state = this.addRoundKey(this.shiftRows(this.bytesSub(state)), k.slice((i+1)*4, ((i+1)*4)+4));
return state;
}
Cryptoops.AES.prototype.blockDecrypt = function(state) {
this.debugWrite('Decrypting...');
var k = this.getExpandedKey(),
ki = k.length;
state = this.addRoundKey(state, k.slice(ki-4, ki));
for (var i = (this.key.length / 4) + 5, ki = ki - 4; i>0; i--, ki -= 4) {
state = this.inverseMixColumns(this.addRoundKey(this.inverseBytesSub(this.inverseShiftRows(state)), k.slice(ki-4, ki)));
}
state = this.addRoundKey(this.inverseBytesSub(this.inverseShiftRows(state)), k.slice(0, 4));
return state;
}
Cryptoops.AES.prototype.addRoundKey = function(state, roundKey) {
return [
state[0] ^ roundKey[0],
state[1] ^ roundKey[1],
state[2] ^ roundKey[2],
state[3] ^ roundKey[3]
];
}
Cryptoops.AES.prototype.mixColumns = function(state) {
var m2 = this.MULT_2, m3 = this.MULT_3, out = [];
for (var i = 0; i < 4; i++) {
var a0 = (state[i] >>> 24) & 255;
var a1 = (state[i] >>> 16) & 255;
var a2 = (state[i] >>> 8) & 255;
var a3 = (state[i] & 255);
out[i] = (( m2[a0] ^ m3[a1] ^ a2 ^ a3) << 24)
| (( a0 ^ m2[a1] ^ m3[a2] ^ a3) << 16)
| (( a0 ^ a1 ^ m2[a2] ^ m3[a3]) << 8)
| (( m3[a0] ^ a1 ^ a2 ^ m2[a3]) << 0)
}
return out;
}
Cryptoops.AES.prototype.inverseMixColumns = function(state) {
var m9 = this.MULT_9, mb = this.MULT_11, md = this.MULT_13, me = this.MULT_14, out = [];
for (var i = 0; i < 4; i++) {
var a0 = (state[i] >>> 24) & 255;
var a1 = (state[i] >>> 16) & 255;
var a2 = (state[i] >>> 8) & 255;
var a3 = (state[i] & 255);
out[i] = (( me[a0] ^ mb[a1] ^ md[a2] ^ m9[a3]) << 24)
| (( m9[a0] ^ me[a1] ^ mb[a2] ^ md[a3]) << 16)
| (( md[a0] ^ m9[a1] ^ me[a2] ^ mb[a3]) << 8)
| (( mb[a0] ^ md[a1] ^ m9[a2] ^ me[a3]) << 0)
}
return out;
}
Cryptoops.AES.prototype.shiftRows = function(state) {
return [
(state[3] & 255) | (((state[2] >>> 8) & 255) << 8) | (((state[1] >>> 16) & 255) << 16) | (((state[0] >>> 24) & 255) << 24),
(state[0] & 255) | (((state[3] >>> 8) & 255) << 8) | (((state[2] >>> 16) & 255) << 16) | (((state[1] >>> 24) & 255) << 24),
(state[1] & 255) | (((state[0] >>> 8) & 255) << 8) | (((state[3] >>> 16) & 255) << 16) | (((state[2] >>> 24) & 255) << 24),
(state[2] & 255) | (((state[1] >>> 8) & 255) << 8) | (((state[0] >>> 16) & 255) << 16) | (((state[3] >>> 24) & 255) << 24)
];
}
Cryptoops.AES.prototype.inverseShiftRows = function(state) {
return [
(state[1] & 255) | (((state[2] >>> 8) & 255) << 8) | (((state[3] >>> 16) & 255) << 16) | (((state[0] >>> 24) & 255) << 24),
(state[2] & 255) | (((state[3] >>> 8) & 255) << 8) | (((state[0] >>> 16) & 255) << 16) | (((state[1] >>> 24) & 255) << 24),
(state[3] & 255) | (((state[0] >>> 8) & 255) << 8) | (((state[1] >>> 16) & 255) << 16) | (((state[2] >>> 24) & 255) << 24),
(state[0] & 255) | (((state[1] >>> 8) & 255) << 8) | (((state[2] >>> 16) & 255) << 16) | (((state[3] >>> 24) & 255) << 24)
];
}
Cryptoops.AES.prototype.bytesSub = function(state) {
var s = this.S_E;
return [
(s[state[0] >>> 24] << 24) | (s[(state[0] >>> 16) & 255] << 16) | (s[(state[0] >>> 8) & 255] << 8) | s[state[0] & 255],
(s[state[1] >>> 24] << 24) | (s[(state[1] >>> 16) & 255] << 16) | (s[(state[1] >>> 8) & 255] << 8) | s[state[1] & 255],
(s[state[2] >>> 24] << 24) | (s[(state[2] >>> 16) & 255] << 16) | (s[(state[2] >>> 8) & 255] << 8) | s[state[2] & 255],
(s[state[3] >>> 24] << 24) | (s[(state[3] >>> 16) & 255] << 16) | (s[(state[3] >>> 8) & 255] << 8) | s[state[3] & 255]
];
}
Cryptoops.AES.prototype.inverseBytesSub = function(state) {
var s = this.S_D;
return [
(s[state[0] >>> 24] << 24) | (s[(state[0] >>> 16) & 255] << 16) | (s[(state[0] >>> 8) & 255] << 8) | s[state[0] & 255],
(s[state[1] >>> 24] << 24) | (s[(state[1] >>> 16) & 255] << 16) | (s[(state[1] >>> 8) & 255] << 8) | s[state[1] & 255],
(s[state[2] >>> 24] << 24) | (s[(state[2] >>> 16) & 255] << 16) | (s[(state[2] >>> 8) & 255] << 8) | s[state[2] & 255],
(s[state[3] >>> 24] << 24) | (s[(state[3] >>> 16) & 255] << 16) | (s[(state[3] >>> 8) & 255] << 8) | s[state[3] & 255]
];
}
Cryptoops.AES.prototype.getExpandedKey = function() {
if (this.keyExpanded.length)
return this.keyExpanded;
if (this.key.length != 32 && this.key.length != 24 && this.key.length != 16)
throw new Error('Cryptoops.AES: missing or invalid key!');
this.debugWrite('Generating key schedule...');
var w = [],
k = this.key,
n_k = (k.length / 4),
n_r = n_k + 6,
n_b = 4;
for (var i=0; i<n_k; i++) {
w[i] = Cryptoops.DataUtils.bytesToWord(k[4*i], k[4*i+1], k[4*i+2], k[4*i+3]);
}
for (i = i; i < n_b * (n_r + 1); i++) {
var temp = w[i-1];
if (i % n_k == 0)
temp = this.wordBytesSub(this.rotWord(temp)) ^ this.RCON[(i/n_k)-1];
else if (n_k > 6 && i % n_k == 4)
temp = this.wordBytesSub(temp);
w[i] = w[i-n_k] ^ temp;
}
this.keyExpanded = w;
return w;
}
Cryptoops.AES.prototype.wordBytesSub = function(word) {
var s = this.S_E;
return (s[word >>> 24] << 24) | (s[(word >>> 16) & 255] << 16) | (s[(word >>> 8) & 255] << 8) | s[word & 255];
}
Cryptoops.AES.prototype.rotWord = function(word) {
return (word << 8) | (word >>> 24);
}
Cryptoops.AES.prototype.debugDumpState = function(state) {
var hex = function(num) {
var str = (num).toString(16)
if (str.length != 2)
str = '0' + str;
return str;
};
var pad32 = function(num) {
var str = (num >>> 0).toString(16);
for (i=0; str.length % 8 != 0; i++)
var str = '0' + str;
return str;
};
var s = state;
this.debugWrite('----\n'
+ pad32(s[0]) + pad32(s[1]) + pad32(s[2]) + pad32(s[3]) + '\n'
+ hex((s[0]>>>24) & 255)+' '+hex((s[1]>>>24)&255)+' '+hex((s[2]>>>24)&255)+' '+hex((s[3]>>>24)&255)+'\n'
+ hex((s[0]>>>16) & 255)+' '+hex((s[1]>>>16)&255)+' '+hex((s[2]>>>16)&255)+' '+hex((s[3]>>>16)&255)+'\n'
+ hex((s[0]>>>8) & 255) +' '+hex((s[1]>>>8)&255) +' '+hex((s[2]>>>8)&255) +' '+hex((s[3]>>>8)&255)+'\n'
+ hex((s[0])&255) +' '+hex((s[1])&255) +' '+hex((s[2])&255) +' '+hex((s[3])&255)+'\n----'
);
}
Cryptoops.AES.prototype.RCON = [
0x01000000,
0x02000000,
0x04000000,
0x08000000,
0x10000000,
0x20000000,
0x40000000,
0x80000000,
0x1B000000,
0x36000000,
0x6C000000,
0xD8000000,
0xAB000000,
0x4D000000,
0x9A000000
];
Cryptoops.AES.prototype.S_E = [
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
];
Cryptoops.AES.prototype.S_D = [
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
];
Cryptoops.AES.prototype.MULT_2 = [
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
];
Cryptoops.AES.prototype.MULT_3 = [
0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
];
Cryptoops.AES.prototype.MULT_9 = [
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
];
Cryptoops.AES.prototype.MULT_11 = [
0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
];
Cryptoops.AES.prototype.MULT_13 = [
0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
];
Cryptoops.AES.prototype.MULT_14 = [
0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
];
Cryptoops.AES.prototype.test = function() {
var _doTest = function(key, plaintext, expected) {
var keyBin = Cryptoops.DataUtils.hexToBytes(key),
plainBin = Cryptoops.DataUtils.hexToBytes(plaintext);
this.setKey(keyBin);
this.debugWrite('------------------------------------------');
this.debugWrite('Test key: [binary] (length: '+keyBin.length+' bytes)');
this.debugWrite('Test key (hex): '+key);
this.debugWrite('Plaintext: [binary] (length: '+plainBin.length+' bytes)');
this.debugWrite('Plaintext (hex): '+plaintext);
this.debugWrite('Expecting ciphertext (hex): '+expected);
var ciphertext = this.encrypt(plainBin),
hex = Cryptoops.DataUtils.bytesToHex(ciphertext);
this.debugWrite('Ciphertext: [binary] (length: '+ciphertext.length+' bytes)');
this.debugWrite('Ciphertext (hex): '+hex);
if (hex != expected)
throw new Error('TEST FAILED: Invalid ciphertext! Expected: '+expected+', Got: ' + hex);
this.debugWrite('GOT EXPECTED CIPHERTEXT!');
var plaintext2 = this.decrypt(ciphertext),
hex2 = Cryptoops.DataUtils.bytesToHex(plaintext2);
this.debugWrite('Decrypted ciphertext: [binary] (length: '+plaintext2.length+' bytes)');
this.debugWrite('Decrypted ciphertext (hex): '+hex2);
if (hex2 != plaintext)
throw new Error('TEST FAILED: Invalid decrypted ciphertext! Expected: '+plaintext+', Got: ' + hex2);
this.debugWrite('SUCCESSFULLY DECRYPTED CIPHERTEXT!');
this.debugWrite('------------------------------------------');
return true;
}.bind(this);
this.debugWrite('AES-256 TEST');
_doTest(
'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
'00112233445566778899aabbccddeeff',
'8ea2b7ca516745bfeafc49904b496089'
);
this.debugWrite('AES-192 TEST');
_doTest(
'000102030405060708090a0b0c0d0e0f1011121314151617',
'00112233445566778899aabbccddeeff',
'dda97ca4864cdfe06eaf70a0ec0d7191'
);
this.debugWrite('AES-128 TEST');
_doTest(
'000102030405060708090a0b0c0d0e0f',
'00112233445566778899aabbccddeeff',
'69c4e0d86a7b0430d8cdb78070b4c55a'
);
return true;
}
Cryptoops.prototype.sanityTest = function() {
var _aes = new Cryptoops.AES({blockMode: Cryptoops.ECB, padMode: Cryptoops.ZeroPadding});
_aes.debugMode = true;
_aes.test();
console.log('SHA256 test: ');
var data = "This is my data to hash.";
var hash = new Cryptoops.SHA256().hash(data);
console.log('SHA256 hash: ', hash);
console.log('------------------------------------------');
console.log('PBKDF2 test: ');
var passphrase = 'passphrase',
salt = 'salt',
iterations = 10000,
byteLength = 32;
var start = new Date().getTime(),
rawKey = new Cryptoops.PBKDF2({
hasher: Cryptoops.SHA256,
keySize: byteLength,
iterations: iterations,
returnFormat: 'bytes'
}).compute(passphrase, salt),
key = Cryptoops.DataUtils.bytesToHex(rawKey),
end = new Date().getTime();
console.log('PBKDF2 key: ', key, rawKey, '; time in ms: ', (end - start));
console.log('------------------------------------------');
console.log('AES granular test: ');
var key = Cryptoops.DataUtils.hexToBytes('0123456712345678234567893456789a0123456712345678234567893456789a'),
iv = Cryptoops.DataUtils.hexToBytes('9876543210fedcba9876543210fedcba'),
blockMode = Cryptoops.CBC,
plaintext = 'Lorem ipsum.';
console.log('encrypting plaintext: ', plaintext);
console.log('plaintext bytes: ', Cryptoops.DataUtils.stringToBytes(plaintext));
var start = new Date().getTime();
var _aes = new Cryptoops.AES({
passphrase: 'cheers fuckface',
passphrasePBKDF2Iterations: 10101,
blockMode: blockMode
});
var salt = _aes.getPassphraseSalt();
console.log('passphraseSalt: ', salt);
console.log('key : ', _aes.key);
console.log('iv : ', _aes.iv);
var ciphertext = _aes.encrypt(plaintext);
var end = new Date().getTime();
console.log('raw ciphertext: ', ciphertext, '; time in ms: ', (end - start));
console.log('decrypting ciphertext...');
var _decAes = new Cryptoops.AES({
passphrase: 'cheers fuckface',
passphrasePBKDF2Iterations: 10101,
passphraseSalt: salt,
blockMode: blockMode
});
var plaintext = _decAes.decrypt(ciphertext);
console.log('raw plaintext: ', plaintext);
ciphertext = Cryptoops.DataUtils.bytesToHex(ciphertext);
console.log('converted ciphertext: ', ciphertext);
plaintext = Cryptoops.DataUtils.bytesToString(plaintext);
console.log('converted plaintext: ', plaintext);
return this.nativeSanityTestComparison(salt);
}
Cryptoops.prototype.nativeSanityTestComparison = async function(existingSalt) {
console.log('------------------------------------------');
console.log('Native PBKDF2 test: ');
var textEncoder = new TextEncoder('utf-8');
var passwordBytes = textEncoder.encode('passphrase');
var saltBytes = textEncoder.encode('salt');
console.log('passwordBytes: ', passwordBytes, '; saltBytes: ', saltBytes);
var start = new Date().getTime();
var importedKey = await window.crypto.subtle.importKey(
'raw',
passwordBytes,
'PBKDF2',
false,
['deriveBits']
);
var key = await window.crypto.subtle.deriveBits({
'name': 'PBKDF2',
'salt': saltBytes,
'iterations': 10000,
'hash': 'SHA-256'
}, importedKey, 32 * 8);
var end = new Date().getTime();
console.log('pbkdf2 native: ', new Uint8Array(key), '; time in ms: ', (end - start));
console.log('------------------------------------------');
console.log('Native AES granular test: ');
var textEncoder = new TextEncoder('utf-8');
var passwordBytes = textEncoder.encode('cheers fuckface');
var plaintext = textEncoder.encode('Lorem ipsum.');
var saltBytes = Uint8Array.from(existingSalt);
console.log('passwordBytes: ', passwordBytes, '; saltBytes: ', saltBytes);
var start = new Date().getTime();
var importedKey = await window.crypto.subtle.importKey(
'raw',
passwordBytes,
'PBKDF2',
false,
['deriveBits']
);
var keyIV = await window.crypto.subtle.deriveBits({
'name': 'PBKDF2',
'salt': saltBytes,
'iterations': 10101,
'hash': 'SHA-384'
}, importedKey, 384);
var key = keyIV.slice(0, 32);
var iv = keyIV.slice(32);
console.log('key: ', new Uint8Array(key));
console.log('iv: ', new Uint8Array(iv));
var symKey = await window.crypto.subtle.importKey(
'raw',
key,
'AES-CBC',
false,
['encrypt']
);
var encrypted = await window.crypto.subtle.encrypt({
name: 'AES-CBC',
iv: iv
}, symKey, plaintext);
var end = new Date().getTime();
console.log('encrypted: ', new Uint8Array(encrypted), '; time in ms: ', (end - start));
}
Put that in your pipe and smoke it.