UNPKG

node-salsa20

Version:

pure javascript implemented salsa20 cipher

263 lines (205 loc) 7.83 kB
/* * A Salsa20 implementation in pure JavaScript for NodeJS * ====================================================== * Please view README.md in the same directory or at * <https://github.com/neoatlantis/node-salsa20/blob/master/salsa20.js> * for more information. */ (function(){ ////////////////////////////////////////////////////////////////////////////// const inputarray = require("./inputarray"); function determineDoubleRounds(options){ var doubleRounds = 10; if(Number.isInteger(options)){ console.warn( "node-salsa20.js: Deprecated specification of double-rounds. " + "For example, please use now `new Salsa20({ rounds: 20 })` or " + "`new Salsa20({ doubleRounds: 10 })` for Salsa20/20." ); doubleRounds = options; // backwards compability } else { if(options.doubleRounds !== undefined && options.rounds !== undefined){ throw Error("Either specify option.doubleRounds or option.rounds, not both."); } if(options.doubleRounds) doubleRounds = options.doubleRounds; if(options.rounds) doubleRounds = options.rounds / 2; } if(!Number.isInteger(doubleRounds) || doubleRounds <= 0){ throw Error("Invalid value of rounds specified."); } return doubleRounds; } function _Salsa20(options){ var self = this; if(undefined === options) options = {}; // Determine Salsa20 rounds. const doubleRounds = determineDoubleRounds(options); const CoreFunc = require("./core"); const coreFunc = new CoreFunc(doubleRounds); /* key expansion */ const keyExpandBuffer_u32 = new Uint32Array(16); /* key expansion for 8 words(32 bytes) key */ function _salsa20BufferFillKey8(nonce2, key8){ var input = keyExpandBuffer_u32, sigma = new Uint32Array( [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] ); input[0] = sigma[0]; input[1] = key8[0]; input[2] = key8[1]; input[3] = key8[2]; input[4] = key8[3]; input[5] = sigma[1]; input[6] = nonce2[0]; input[7] = nonce2[1]; input[10] = sigma[2]; input[11] = key8[4]; input[12] = key8[5]; input[13] = key8[6]; input[14] = key8[7]; input[15] = sigma[3]; }; function _salsa20ExpansionKey8(ret){ var input = keyExpandBuffer_u32; input[8] = counter[0]; input[9] = counter[1]; return coreFunc(input, ret); }; /* key expansion for 4 words key(16 bytes) */ function _salsa20BufferFillKey4(nonce2, key4){ var input = keyExpandBuffer_u32; tau = new Uint32Array( [0x61707865, 0x3120646e, 0x79622d36, 0x6b206574] ); input[0] = tau[0]; input[1] = key4[0]; input[2] = key4[1]; input[3] = key4[2]; input[4] = key4[3]; input[5] = tau[1]; input[6] = nonce2[0]; input[7] = nonce2[1]; input[10] = tau[2]; input[11] = key4[0]; input[12] = key4[1]; input[13] = key4[2]; input[14] = key4[3]; input[15] = tau[3]; }; function _salsa20ExpansionKey4(ret){ var input = keyExpandBuffer_u32; input[8] = counter[0]; input[9] = counter[1]; return coreFunc(input, ret); }; ////////////////////////////////////////////////////////////////////// var counter = new Uint32Array(2); var blockGenerator; function _counterReset(){counter[0] = 0; counter[1] = 0;}; function _counterInc(){ counter[0] += 1; if(0 == counter[0]) counter[1] += 1; }; function _initialize(nonceBuf, keyBuf){ var nonce = new Uint32Array(nonceBuf); if(32 == keyBuf.byteLength){ var key = new Uint32Array(keyBuf); _salsa20BufferFillKey8(nonce, key); blockGenerator = function(ret){ _salsa20ExpansionKey8(ret); _counterInc(); }; } else if(16 == keyBuf.byteLength){ var key = new Uint32Array(keyBuf); _salsa20BufferFillKey4(nonce, key); blockGenerator = function(ret){ _salsa20ExpansionKey4(ret); _counterInc(); }; } else throw new Error('invalid-key-length'); }; ////////////////////////////////////////////////////////////////////// function _xorBuf(dataBuf){ dataBuf = inputarray(dataBuf); var origLength = dataBuf.byteLength, blocksCount = Math.floor(origLength / 64) + 1, block = new Uint32Array(16); // holder of new generated block var stream = new Uint8Array(dataBuf), xorStream = new Uint8Array(stream.length + 64); var b=0, i, j; for(i=0; i<blocksCount; i++){ blockGenerator(block); for(j=0; j<16; j++){ xorStream[b++] = (block[j] >> 0) & 0xff; xorStream[b++] = (block[j] >> 8) & 0xff; xorStream[b++] = (block[j] >> 16) & 0xff; xorStream[b++] = (block[j] >> 24) & 0xff; }; }; for(i=0; i<origLength; i++) stream[i] ^= xorStream[i]; return stream.buffer; }; function _seek(u32_0, u32_1){ counter[0] = u32_0; counter[1] = u32_1; }; /* this.key: set up the key and initialize the inner buffer A single buffer is used as argument. It can be either 16 bytes or 32 bytes for a single key, or (backwards compatible) 24 / 40 bytes where the first 8 bytes will be used as a nonce. The latter way is deprecated, though. Doing so will result in a warning. */ function _finalizeAndBindMethods(){ self.encrypt = _xorBuf; self.decrypt = _xorBuf; self.seek = _seek; delete self.key; delete self.nonce; } this.key = function(bufKey){ bufKey = inputarray(bufKey); var nonceBuf = null; if(bufKey.byteLength == 24 || bufKey.byteLength == 40){ console.warn("node-salsa20.js: Deprecated usage in specifying nonce during key() call. Please use `salsa20().key().nonce()`."); nonceBuf = bufKey.slice(0, 8); bufKey = bufKey.slice(8); } if (!(bufKey.byteLength == 16 || bufKey.byteLength == 32)){ throw Error("node-salsa20.js: Invalid key length. Should be 16 or 32 bytes. Got " + bufKey.byteLength + " bytes."); } function setNonce(nonce){ nonce = inputarray(nonce); if(nonce.byteLength != 8 && nonce.byteLength != 16){ throw Error("node-salsa20.js: Invalid nonce length. Must be 8 or 16 bytes. Got " + nonce.byteLength + " bytes."); } _initialize(nonce.slice(0, 8), bufKey); if(nonce.byteLength == 16){ const counterVal = new Uint32Array(nonce.slice(8,16)); _seek(counterVal[0], counterVal[1]); } _finalizeAndBindMethods(); return self; } if(nonceBuf !== null){ setNonce(nonceBuf); } else { self.nonce = setNonce; } delete self.key; return self; }; if(true === options.testing){ console.debug("node-salsa20.js: Testing mode. Core function exposed."); self.core = function(inaBuf){ var ret = new Uint32Array(16); coreFunc(new Uint32Array(inaBuf), ret); return ret.buffer; }; }; return this; }; module.exports = _Salsa20; ////////////////////////////////////////////////////////////////////////////// })();