From 5a8274a7efa89dc3bf8d457fa45cdf44a828e56b Mon Sep 17 00:00:00 2001 From: didier amyot Date: Fri, 25 Mar 2016 17:54:34 -0400 Subject: [PATCH] first commit --- .gitignore | 1 + README.md | 1 + dist/spritz.js | 274 +++++++++++++++++++++++++++++++++++++++++++++++ gulpfile.js | 12 +++ package.json | 25 +++++ src/prefix.js | 1 + src/spritz.js | 183 +++++++++++++++++++++++++++++++ src/suffix.js | 52 +++++++++ src/util.js | 35 ++++++ test/utilSpec.js | 67 ++++++++++++ 10 files changed, 651 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 dist/spritz.js create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 src/prefix.js create mode 100644 src/spritz.js create mode 100644 src/suffix.js create mode 100644 src/util.js create mode 100644 test/utilSpec.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..0985333 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# S diff --git a/dist/spritz.js b/dist/spritz.js new file mode 100644 index 0000000..22b8afd --- /dev/null +++ b/dist/spritz.js @@ -0,0 +1,274 @@ +;(function() { + +var util = {}; +util.low = function(byte) { + return byte & 0xf; +}; + + +util.high = function(byte) { + return byte >>> 4 & 0xf; +}; + +util.madd = function(a, b) { // TODO: rename to madd256 + return (a + b) % N; + // return (a + b) & 0xff; // when N = 256, 0xff = N - 1 + }; + + +util.msub = function(a, b) {// TODO: rename to msub256 + return util.madd(N, a - b); +}; + +util.swap = function(lst, p1, p2) { + var tmp = lst[p1]; + lst[p1] = lst[p2]; + lst[p2] = tmp; +}; + +util.gcd = function(a, b){ + var t; + while (b !== 0) { + t = b; + b = a % b; + a = t; + } + return a; +}; + +var N = 256; +var N_OVER_TWO_FLOOR = Math.floor(N / 2); +var TWO_N = 2 * N; + +var spritz = {}; + +spritz._util = util; + +spritz._initializeState = function _initializeState() { + var state = {}; + + state.i = 0; + state.j = 0; + state.k = 0; + state.z = 0; + state.a = 0; + state.w = 1; + + state.S = []; + for (var i = 0; i < N; i++) { + state.S[i] = i; + } + return state; +}; + +spritz._absorb = function _absorb(I, state){ + var v; + /* + basic validation of I + consider bounds-checking and deep-checking of each N-value such that they are in the valid range (0 <= x < N) + */ + if(!(Array.isArray(I) && I.length > 0)){ + return false; + } + + for(v = 0; v < I.length; v++){ + spritz._absorbByte(I[v], state); + } + return true; +}; + +spritz._absorbByte = function _absorbByte(b, state) { + spritz._absorbNibble(util.low(b), state); + spritz._absorbNibble(util.high(b), state); +}; + +spritz._absorbNibble = function _absorbNibble(x, state) { + if (state.a === N_OVER_TWO_FLOOR){ + spritz.shuffle(state); + } + + util.swap(state.S, state.a, util.madd(N_OVER_TWO_FLOOR, x)); + state.a = util.madd(state.a, 1); +}; + +spritz._absorbStop = function _absorbStop(state){ + if(state.a === N_OVER_TWO_FLOOR){ + spritz._shuffle(state); + } + state.a = util.madd(state.a, 1); +}; + +spritz._shuffle = function _shuffle(state){ + spritz._whip(TWO_N, state); + spritz._crush(state); + spritz._whip(TWO_N, state); + spritz._crush(state); + spritz._whip(TWO_N, state); + state.a = 0; +}; + +spritz._whip = function _whip(r, state) { + for (var i = 0; i < r; i++) { + spritz._update(state); + } + do { + state.w = util.madd(state.w, 1); + } while (util.gcd(state.w, N) !== 1); + // NB. a simple-case assumption is that if N is a power of 2 then one could instead use: + // w = madd(w, 2); +}; + +spritz._crush = function _crush(state) { + var index; + for (var i = 0; i < N_OVER_TWO_FLOOR; i++) { + index = N - i - 1; + if (state.S[i] > state.S[index]) { + util.swap(state.S, i, index); + } + } +}; + +spritz._update = function _update(state) { //done + state.i = util.madd(state.i, state.w); + state.j = util.madd(state.k, state.S[util.madd(state.j, state.S[state.i])]); + state.k = util.madd(state.i + state.k, state.S[state.j]); + util.swap(state.S, state.i, state.j); +}; + +spritz._squeeze = function _squeeze(r, state) { + if (state.a > 0) { + spritz._shuffle(state); + } + + var P = []; + for (var i = 0; i < r; i++) { + P[i] = spritz._drip(state); + } + return P; +}; + +spritz._drip = function _drip(state){ + if (state.a > 0) { + spritz._shuffle(state); + } + spritz._update(state); + return spritz._output(state); +}; + +spritz._output = function _output(state) { + var idx = util.madd(state.z, state.k); + idx = util.madd(state.i, state.S[idx]); + idx = util.madd(state.j, state.S[idx]); + state.z = state.S[idx]; + return state.z; +}; + +spritz._keySetup = function _keySetup(K) { + var state = spritz._initializeState(); + spritz._absorb(K, state); + return state; +}; + +spritz.stringToUtf8ByteArray = function stringToUtf8ByteArray(utf8Array){ + var utf8 = unescape(encodeURIComponent(utf8Array)); + + var arr = []; + for (var i = 0; i < utf8.length; i++) { + arr.push(utf8.charCodeAt(i)); + } + return arr; +}; + +spritz.utf8ByteArrayToString = function utf8ByteArrayToString(utf8Array){ + var ret = ""; + for(var i = 0; i < utf8Array.length; i++){ + ret += String.fromCharCode(utf8Array[i]); + } + return decodeURIComponent(escape(ret)); +}; + +spritz.hash = function hash(M, r){ + if(typeof M == 'string'){ + return spritz.hash(spritz.stringToUtf8ByteArray(M), r); + } + var state = spritz._initializeState(); + spritz._absorb(M, state); + spritz._absorbStop(state); + spritz._absorb([r & 0xff], state); // NB. restricted(!) to 255-byte hashes + return spritz._squeeze(r, state); +}; + +spritz.encrypt = function encrypt(K, M) { + + var C = [], stream , i; + + var state = spritz._keySetup(K); + stream = spritz._squeeze(M.length, state); + for (i = 0; i < M.length; i++) { + C[i] = util.madd(M[i], stream[i]); + } + return C; +}; + +spritz.decrypt = function decrypt(K, C) { + var M = [] , stream , i; + var state = spritz._keySetup(K); + stream = spritz._squeeze(C.length, state); + for (i = 0; i < C.length; i++) { + M[i] = util.msub(C[i], stream[i]); + } + return M; +}; + +// Export spritz as lodash 4.5.0. + +var objectTypes = { + 'function': true, + 'object': true +}; + +function checkGlobal(value) { + return (value && value.Object === Object) ? value : null; +} + +var freeWindow = checkGlobal(objectTypes[typeof window] && window); +var freeSelf = checkGlobal(objectTypes[typeof self] && self); +var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global); +var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : undefined; +var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : undefined; +var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports: undefined; +var thisGlobal = checkGlobal(objectTypes[typeof this] && this); + + +var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')(); + + +// Expose lodash on the free variable `window` or `self` when available. This +// prevents errors in cases where lodash is loaded by a script tag in the presence +// of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch for more details. +(freeWindow || freeSelf || {}).spritz = spritz; + +// Some AMD build optimizers like r.js check for condition patterns like the following: +if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module. + define(function() { + return spritz; + }); +} +// Check for `exports` after `define` in case a build optimizer adds an `exports` object. +else if (freeExports && freeModule) { + // Export for Node.js. + if (moduleExports) { + freeModule.exports = spritz; + } + // Export for CommonJS support. + freeExports.spritz = spritz; +} +else { + // Export to the global object. + root.spritz = spritz; +} + + +}.call(this)); // close the prefix diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..924fb45 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,12 @@ +var gulp = require('gulp'); +var concat = require('gulp-concat'); + +gulp.task('build', function() { + var scripts = ['./src/prefix.js', + './src/util.js', + './src/spritz.js', + './src/suffix.js']; + return gulp.src(scripts) + .pipe(concat('spritz.js')) + .pipe(gulp.dest('./dist/')); +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..93c36a0 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "spritzjs", + "version": "1.0.0", + "description": "Implementation of spritz stream cipher and hash compatible with nodejs, requirejs and browserify.", + "main": "dist/spritz.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "gulp build; mocha" + }, + "keywords": [ + "spritz", + "stream cipher", + "hash", + "requirejs" + ], + "author": "didier amyot ", + "license": "MIT", + "devDependencies": { + "gulp": "^3.9.1", + "gulp-concat": "^2.6.0", + "mocha": "^2.4.5" + } +} diff --git a/src/prefix.js b/src/prefix.js new file mode 100644 index 0000000..aee2913 --- /dev/null +++ b/src/prefix.js @@ -0,0 +1 @@ +;(function() { diff --git a/src/spritz.js b/src/spritz.js new file mode 100644 index 0000000..cc2dc4a --- /dev/null +++ b/src/spritz.js @@ -0,0 +1,183 @@ +var N = 256; +var N_OVER_TWO_FLOOR = Math.floor(N / 2); +var TWO_N = 2 * N; + +var spritz = {}; + +spritz._util = util; + +spritz._initializeState = function _initializeState() { + var state = {}; + + state.i = 0; + state.j = 0; + state.k = 0; + state.z = 0; + state.a = 0; + state.w = 1; + + state.S = []; + for (var i = 0; i < N; i++) { + state.S[i] = i; + } + return state; +}; + +spritz._absorb = function _absorb(I, state){ + var v; + /* + basic validation of I + consider bounds-checking and deep-checking of each N-value such that they are in the valid range (0 <= x < N) + */ + if(!(Array.isArray(I) && I.length > 0)){ + return false; + } + + for(v = 0; v < I.length; v++){ + spritz._absorbByte(I[v], state); + } + return true; +}; + +spritz._absorbByte = function _absorbByte(b, state) { + spritz._absorbNibble(util.low(b), state); + spritz._absorbNibble(util.high(b), state); +}; + +spritz._absorbNibble = function _absorbNibble(x, state) { + if (state.a === N_OVER_TWO_FLOOR){ + spritz.shuffle(state); + } + + util.swap(state.S, state.a, util.madd(N_OVER_TWO_FLOOR, x)); + state.a = util.madd(state.a, 1); +}; + +spritz._absorbStop = function _absorbStop(state){ + if(state.a === N_OVER_TWO_FLOOR){ + spritz._shuffle(state); + } + state.a = util.madd(state.a, 1); +}; + +spritz._shuffle = function _shuffle(state){ + spritz._whip(TWO_N, state); + spritz._crush(state); + spritz._whip(TWO_N, state); + spritz._crush(state); + spritz._whip(TWO_N, state); + state.a = 0; +}; + +spritz._whip = function _whip(r, state) { + for (var i = 0; i < r; i++) { + spritz._update(state); + } + do { + state.w = util.madd(state.w, 1); + } while (util.gcd(state.w, N) !== 1); + // NB. a simple-case assumption is that if N is a power of 2 then one could instead use: + // w = madd(w, 2); +}; + +spritz._crush = function _crush(state) { + var index; + for (var i = 0; i < N_OVER_TWO_FLOOR; i++) { + index = N - i - 1; + if (state.S[i] > state.S[index]) { + util.swap(state.S, i, index); + } + } +}; + +spritz._update = function _update(state) { //done + state.i = util.madd(state.i, state.w); + state.j = util.madd(state.k, state.S[util.madd(state.j, state.S[state.i])]); + state.k = util.madd(state.i + state.k, state.S[state.j]); + util.swap(state.S, state.i, state.j); +}; + +spritz._squeeze = function _squeeze(r, state) { + if (state.a > 0) { + spritz._shuffle(state); + } + + var P = []; + for (var i = 0; i < r; i++) { + P[i] = spritz._drip(state); + } + return P; +}; + +spritz._drip = function _drip(state){ + if (state.a > 0) { + spritz._shuffle(state); + } + spritz._update(state); + return spritz._output(state); +}; + +spritz._output = function _output(state) { + var idx = util.madd(state.z, state.k); + idx = util.madd(state.i, state.S[idx]); + idx = util.madd(state.j, state.S[idx]); + state.z = state.S[idx]; + return state.z; +}; + +spritz._keySetup = function _keySetup(K) { + var state = spritz._initializeState(); + spritz._absorb(K, state); + return state; +}; + +spritz.stringToUtf8ByteArray = function stringToUtf8ByteArray(utf8Array){ + var utf8 = unescape(encodeURIComponent(utf8Array)); + + var arr = []; + for (var i = 0; i < utf8.length; i++) { + arr.push(utf8.charCodeAt(i)); + } + return arr; +}; + +spritz.utf8ByteArrayToString = function utf8ByteArrayToString(utf8Array){ + var ret = ""; + for(var i = 0; i < utf8Array.length; i++){ + ret += String.fromCharCode(utf8Array[i]); + } + return decodeURIComponent(escape(ret)); +}; + +spritz.hash = function hash(M, r){ + if(typeof M == 'string'){ + return spritz.hash(spritz.stringToUtf8ByteArray(M), r); + } + var state = spritz._initializeState(); + spritz._absorb(M, state); + spritz._absorbStop(state); + spritz._absorb([r & 0xff], state); // NB. restricted(!) to 255-byte hashes + return spritz._squeeze(r, state); +}; + +spritz.encrypt = function encrypt(K, M) { + + var C = [], stream , i; + + var state = spritz._keySetup(K); + stream = spritz._squeeze(M.length, state); + for (i = 0; i < M.length; i++) { + C[i] = util.madd(M[i], stream[i]); + } + return C; +}; + +spritz.decrypt = function decrypt(K, C) { + var M = [] , stream , i; + var state = spritz._keySetup(K); + stream = spritz._squeeze(C.length, state); + for (i = 0; i < C.length; i++) { + M[i] = util.msub(C[i], stream[i]); + } + return M; +}; diff --git a/src/suffix.js b/src/suffix.js new file mode 100644 index 0000000..1e7271d --- /dev/null +++ b/src/suffix.js @@ -0,0 +1,52 @@ +// Export spritz as lodash 4.5.0. + +var objectTypes = { + 'function': true, + 'object': true +}; + +function checkGlobal(value) { + return (value && value.Object === Object) ? value : null; +} + +var freeWindow = checkGlobal(objectTypes[typeof window] && window); +var freeSelf = checkGlobal(objectTypes[typeof self] && self); +var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global); +var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : undefined; +var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : undefined; +var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports: undefined; +var thisGlobal = checkGlobal(objectTypes[typeof this] && this); + + +var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')(); + + +// Expose lodash on the free variable `window` or `self` when available. This +// prevents errors in cases where lodash is loaded by a script tag in the presence +// of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch for more details. +(freeWindow || freeSelf || {}).spritz = spritz; + +// Some AMD build optimizers like r.js check for condition patterns like the following: +if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // Define as an anonymous module so, through path mapping, it can be + // referenced as the "underscore" module. + define(function() { + return spritz; + }); +} +// Check for `exports` after `define` in case a build optimizer adds an `exports` object. +else if (freeExports && freeModule) { + // Export for Node.js. + if (moduleExports) { + freeModule.exports = spritz; + } + // Export for CommonJS support. + freeExports.spritz = spritz; +} +else { + // Export to the global object. + root.spritz = spritz; +} + + +}.call(this)); // close the prefix diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..7cd5d20 --- /dev/null +++ b/src/util.js @@ -0,0 +1,35 @@ +var util = {}; +util.low = function(byte) { + return byte & 0xf; +}; + + +util.high = function(byte) { + return byte >>> 4 & 0xf; +}; + +util.madd = function(a, b) { // TODO: rename to madd256 + return (a + b) % N; + // return (a + b) & 0xff; // when N = 256, 0xff = N - 1 + }; + + +util.msub = function(a, b) {// TODO: rename to msub256 + return util.madd(N, a - b); +}; + +util.swap = function(lst, p1, p2) { + var tmp = lst[p1]; + lst[p1] = lst[p2]; + lst[p2] = tmp; +}; + +util.gcd = function(a, b){ + var t; + while (b !== 0) { + t = b; + b = a % b; + a = t; + } + return a; +}; diff --git a/test/utilSpec.js b/test/utilSpec.js new file mode 100644 index 0000000..e7ab311 --- /dev/null +++ b/test/utilSpec.js @@ -0,0 +1,67 @@ +var util = require("../dist/spritz.js")._util; +var spritz = require("../dist/spritz.js"); +var assert = require('assert'); + +describe('utility functions', function() { + + describe('the low function', function(){ + it('should get the low nibble of the input', function () { + assert.equal(util.low(3), 3); + assert.equal(util.low(0), 0); + assert.equal(util.low(255), 15); + }); + }); + +}); + +describe('the string to array convertion', function(){ + it('should be able to convert string to utf-8 array', function(){ + var utf8String = "maison-дом-տուն-სახლი"; + var expt = [109,97,105,115,111,110,45,208,180,208,190,208,188,45,213,191,213,184,214,130,213,182,45,225,131,161,225,131,144,225,131,174,225,131,154,225,131,152]; + assert.deepEqual(spritz.stringToUtf8ByteArray(utf8String), expt); + }); + + it('should be able to convert utf-8 array to string', function(){ + var utf8Array = [109,97,105,115,111,110,45,208,180,208,190,208,188,45,213,191,213,184,214,130,213,182,45,225,131,161,225,131,144,225,131,174,225,131,154,225,131,152]; + var expt = "maison-дом-տուն-სახლი"; + assert.deepEqual(spritz.utf8ByteArrayToString(utf8Array), expt); + }); +}); + + +describe('the hash function', function(){ + it('should be able to hash an array', function(){ + var M = [65, 66, 67]; + var r = 32; + var expt = [2,143,162,180,139,147,74,24,98,184,105,16,81,58,71,103,124,28,45,149,236,62,117,112,120,111,28,50,139,189,74,71]; + assert.deepEqual(spritz.hash(M, r), expt); + }); + + it('should be able to hash a string', function(){ + var M = 'ABC'; + var r = 32; + var expt = [2,143,162,180,139,147,74,24,98,184,105,16,81,58,71,103,124,28,45,149,236,62,117,112,120,111,28,50,139,189,74,71]; + assert.deepEqual(spritz.hash(M, r), expt); + }); +}); + +describe('the encrypt/decrypt function', function(){ + it('should be encrypt/decrypt a byte array', function(){ + var stringMessage = "maison-дом-տուն-სახლი"; + var byteArrMessage = spritz.stringToUtf8ByteArray(stringMessage); + var key = [12, 99, 200, 76, 162, 87, 729, 272, 272, 282, 282]; + var cypher = spritz.encrypt(key, byteArrMessage); + var decryptedMessage = spritz.decrypt(key, cypher); + assert.deepEqual(decryptedMessage, byteArrMessage); + }); + + xit('should encrypt correctly the message', function(){ + var stringMessage = "maison-дом-տուն-სახლი"; + var byteArrMessage = spritz.stringToUtf8ByteArray(stringMessage); + var key = [12, 55, 12, 12, 87, 89, 81, 17, 27, 89]; + var cypher = spritz.encrypt(key, byteArrMessage); + var expt = [125,26,182,7,75,36,178,253,82,46,91,21,218,16,127,41,203,45,90,129,129,47,171,68,221,112,210,6,99,250,91,111,225,27,89,100,189,9]; + assert.deepEqual(cypher, expt); + }); + +});