diff options
author | Azul <azul@riseup.net> | 2017-03-23 14:12:30 +0100 |
---|---|---|
committer | Azul <azul@riseup.net> | 2017-03-23 14:12:30 +0100 |
commit | 372b15afed0b7477bd83062feaa7f24c4d40e38d (patch) | |
tree | 0e901532c839d7d0db922cb97d064df279090116 | |
parent | ac4cf4a1da4dcc697f1f5219f0073c5991a135e0 (diff) |
git subrepo clone https://leap.se/git/srp_js app/assets/javascripts/srp
subrepo:
subdir: "app/assets/javascripts/srp"
merged: "9e1a417"
upstream:
origin: "https://leap.se/git/srp_js"
branch: "master"
commit: "9e1a417"
git-subrepo:
version: "0.3.1"
origin: "https://github.com/ingydotnet/git-subrepo"
commit: "a7ee886"
30 files changed, 3808 insertions, 0 deletions
diff --git a/app/assets/javascripts/srp b/app/assets/javascripts/srp deleted file mode 160000 -Subproject 9e1a41733468d4a3f5102b04277b9cd7b52d0a4 diff --git a/app/assets/javascripts/srp/.gitignore b/app/assets/javascripts/srp/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/app/assets/javascripts/srp/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/app/assets/javascripts/srp/.gitrepo b/app/assets/javascripts/srp/.gitrepo new file mode 100644 index 0000000..89f521a --- /dev/null +++ b/app/assets/javascripts/srp/.gitrepo @@ -0,0 +1,11 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://leap.se/git/srp_js + branch = master + commit = 9e1a41733468d4a3f5102b04277b9cd7b52d0a45 + parent = ac4cf4a1da4dcc697f1f5219f0073c5991a135e0 + cmdver = 0.3.1 diff --git a/app/assets/javascripts/srp/.nvmrc b/app/assets/javascripts/srp/.nvmrc new file mode 100644 index 0000000..9212624 --- /dev/null +++ b/app/assets/javascripts/srp/.nvmrc @@ -0,0 +1 @@ +v0.12.7 diff --git a/app/assets/javascripts/srp/.travis.yml b/app/assets/javascripts/srp/.travis.yml new file mode 100644 index 0000000..587bd3e --- /dev/null +++ b/app/assets/javascripts/srp/.travis.yml @@ -0,0 +1 @@ +language: node_js diff --git a/app/assets/javascripts/srp/Readme.md b/app/assets/javascripts/srp/Readme.md new file mode 100644 index 0000000..5596b2f --- /dev/null +++ b/app/assets/javascripts/srp/Readme.md @@ -0,0 +1,20 @@ +originally imported to github from: https://code.google.com/p/srp-js/ + +License: [New BSD License](http://www.opensource.org/licenses/bsd-license.php) + +Many websites today require some form of authentication to access the site's full functionality. Unfortunately, many of these websites do not use secure authentication protocols. + +In some cases, websites will store user passwords in their database. If the database ever becomes compromised, an attacker could authenticate as any user he wanted. + +More security savvy websites will store user passwords in their database only after irreversible encryption has been applied. This prevents a leaked copy of the database from compromising user authentication. + +But even the more secure of these methods has one major drawback - the user's password is sent across the internet in plain text. Anyone capable of capturing packets, perhaps on a wireless network, is able to see a user's password when the user logs in. + +The Secure Remote Password protocol addresses this problem. First presented by T. Wu of Stanford in 1998, SRP has been used in some applications for over a decade. SRP addresses both of the issues previously mentioned: the server does not store information that could be used to login, and the client does not transmit the password in plain text. + +This project aims to provide a strong javascript implementation of SRP that will provide some peace of mind when using websites that do not use HTTPS. Due to the nature of HTTP, it is not invulnerable to man-in-the-middle attacks, but it should provide strong security against passive eavesdroppers, which are increasingly common in the age of wireless internet. + +[ruby-srp](https://github.com/leapcode/ruby-srp) contains client and server implementations in ruby that work with the current version of srp-js. It also ships an example using srp-js as the client. + +The [original repository](https://code.google.com/p/srp-js/) had code working with a django server. This was removed after the commit tagged 0.1.0 + diff --git a/app/assets/javascripts/srp/Version b/app/assets/javascripts/srp/Version new file mode 100644 index 0000000..1d0ba9e --- /dev/null +++ b/app/assets/javascripts/srp/Version @@ -0,0 +1 @@ +0.4.0 diff --git a/app/assets/javascripts/srp/index.js b/app/assets/javascripts/srp/index.js new file mode 100644 index 0000000..3366662 --- /dev/null +++ b/app/assets/javascripts/srp/index.js @@ -0,0 +1,3 @@ +//= require_tree ./lib +//= require ./src/srp +//= require_tree ./src diff --git a/app/assets/javascripts/srp/karma.conf.js b/app/assets/javascripts/srp/karma.conf.js new file mode 100644 index 0000000..6eec6d9 --- /dev/null +++ b/app/assets/javascripts/srp/karma.conf.js @@ -0,0 +1,81 @@ +// Karma configuration +// Generated on Tue Sep 15 2015 01:01:08 GMT+0200 (CEST) + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + // dependencies + 'lib/*.js', + 'node_modules/jquery/dist/jquery.min.js', + + // src + 'src/srp.js', + 'src/*.js', + 'src/jqueryRest.js', + + // devDependencies + 'node_modules/sinon/pkg/sinon.js', + 'node_modules/jasmine-jquery/lib/jasmine-jquery.js', + + // spec + 'spec/spec_helper.js', + 'spec/*.js' + ], + + + // list of files to exclude + exclude: [ + 'spec/lib/jasmine/*.js' + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }); +}; diff --git a/app/assets/javascripts/srp/lib/MD5.js b/app/assets/javascripts/srp/lib/MD5.js new file mode 100644 index 0000000..55cb8cc --- /dev/null +++ b/app/assets/javascripts/srp/lib/MD5.js @@ -0,0 +1,207 @@ +/** +* +* MD5 (Message-Digest Algorithm) +* http://www.webtoolkit.info/ +* +**/ + +var MD5 = function (string) { + + function RotateLeft(lValue, iShiftBits) { + return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits)); + } + + function AddUnsigned(lX,lY) { + var lX4,lY4,lX8,lY8,lResult; + lX8 = (lX & 0x80000000); + lY8 = (lY & 0x80000000); + lX4 = (lX & 0x40000000); + lY4 = (lY & 0x40000000); + lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF); + if (lX4 & lY4) { + return (lResult ^ 0x80000000 ^ lX8 ^ lY8); + } + if (lX4 | lY4) { + if (lResult & 0x40000000) { + return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); + } else { + return (lResult ^ 0x40000000 ^ lX8 ^ lY8); + } + } else { + return (lResult ^ lX8 ^ lY8); + } + } + + function F(x,y,z) { return (x & y) | ((~x) & z); } + function G(x,y,z) { return (x & z) | (y & (~z)); } + function H(x,y,z) { return (x ^ y ^ z); } + function I(x,y,z) { return (y ^ (x | (~z))); } + + function FF(a,b,c,d,x,s,ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); + return AddUnsigned(RotateLeft(a, s), b); + }; + + function GG(a,b,c,d,x,s,ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); + return AddUnsigned(RotateLeft(a, s), b); + }; + + function HH(a,b,c,d,x,s,ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); + return AddUnsigned(RotateLeft(a, s), b); + }; + + function II(a,b,c,d,x,s,ac) { + a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); + return AddUnsigned(RotateLeft(a, s), b); + }; + + function ConvertToWordArray(string) { + var lWordCount; + var lMessageLength = string.length; + var lNumberOfWords_temp1=lMessageLength + 8; + var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64; + var lNumberOfWords = (lNumberOfWords_temp2+1)*16; + var lWordArray=Array(lNumberOfWords-1); + var lBytePosition = 0; + var lByteCount = 0; + while ( lByteCount < lMessageLength ) { + lWordCount = (lByteCount-(lByteCount % 4))/4; + lBytePosition = (lByteCount % 4)*8; + lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition)); + lByteCount++; + } + lWordCount = (lByteCount-(lByteCount % 4))/4; + lBytePosition = (lByteCount % 4)*8; + lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition); + lWordArray[lNumberOfWords-2] = lMessageLength<<3; + lWordArray[lNumberOfWords-1] = lMessageLength>>>29; + return lWordArray; + }; + + function WordToHex(lValue) { + var WordToHexValue="",WordToHexValue_temp="",lByte,lCount; + for (lCount = 0;lCount<=3;lCount++) { + lByte = (lValue>>>(lCount*8)) & 255; + WordToHexValue_temp = "0" + lByte.toString(16); + WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2); + } + return WordToHexValue; + }; + + function Utf8Encode(string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }; + + var x=Array(); + var k,AA,BB,CC,DD,a,b,c,d; + var S11=7, S12=12, S13=17, S14=22; + var S21=5, S22=9 , S23=14, S24=20; + var S31=4, S32=11, S33=16, S34=23; + var S41=6, S42=10, S43=15, S44=21; + + string = Utf8Encode(string); + + x = ConvertToWordArray(string); + + a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; + + for (k=0;k<x.length;k+=16) { + AA=a; BB=b; CC=c; DD=d; + a=FF(a,b,c,d,x[k+0], S11,0xD76AA478); + d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756); + c=FF(c,d,a,b,x[k+2], S13,0x242070DB); + b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE); + a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF); + d=FF(d,a,b,c,x[k+5], S12,0x4787C62A); + c=FF(c,d,a,b,x[k+6], S13,0xA8304613); + b=FF(b,c,d,a,x[k+7], S14,0xFD469501); + a=FF(a,b,c,d,x[k+8], S11,0x698098D8); + d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF); + c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1); + b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE); + a=FF(a,b,c,d,x[k+12],S11,0x6B901122); + d=FF(d,a,b,c,x[k+13],S12,0xFD987193); + c=FF(c,d,a,b,x[k+14],S13,0xA679438E); + b=FF(b,c,d,a,x[k+15],S14,0x49B40821); + a=GG(a,b,c,d,x[k+1], S21,0xF61E2562); + d=GG(d,a,b,c,x[k+6], S22,0xC040B340); + c=GG(c,d,a,b,x[k+11],S23,0x265E5A51); + b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA); + a=GG(a,b,c,d,x[k+5], S21,0xD62F105D); + d=GG(d,a,b,c,x[k+10],S22,0x2441453); + c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681); + b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8); + a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6); + d=GG(d,a,b,c,x[k+14],S22,0xC33707D6); + c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87); + b=GG(b,c,d,a,x[k+8], S24,0x455A14ED); + a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905); + d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8); + c=GG(c,d,a,b,x[k+7], S23,0x676F02D9); + b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A); + a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942); + d=HH(d,a,b,c,x[k+8], S32,0x8771F681); + c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122); + b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C); + a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44); + d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9); + c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60); + b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70); + a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6); + d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA); + c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085); + b=HH(b,c,d,a,x[k+6], S34,0x4881D05); + a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039); + d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5); + c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8); + b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665); + a=II(a,b,c,d,x[k+0], S41,0xF4292244); + d=II(d,a,b,c,x[k+7], S42,0x432AFF97); + c=II(c,d,a,b,x[k+14],S43,0xAB9423A7); + b=II(b,c,d,a,x[k+5], S44,0xFC93A039); + a=II(a,b,c,d,x[k+12],S41,0x655B59C3); + d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92); + c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D); + b=II(b,c,d,a,x[k+1], S44,0x85845DD1); + a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F); + d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0); + c=II(c,d,a,b,x[k+6], S43,0xA3014314); + b=II(b,c,d,a,x[k+13],S44,0x4E0811A1); + a=II(a,b,c,d,x[k+4], S41,0xF7537E82); + d=II(d,a,b,c,x[k+11],S42,0xBD3AF235); + c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB); + b=II(b,c,d,a,x[k+9], S44,0xEB86D391); + a=AddUnsigned(a,AA); + b=AddUnsigned(b,BB); + c=AddUnsigned(c,CC); + d=AddUnsigned(d,DD); + } + + var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d); + + return temp.toLowerCase(); +} diff --git a/app/assets/javascripts/srp/lib/SHA1.js b/app/assets/javascripts/srp/lib/SHA1.js new file mode 100644 index 0000000..e772cbe --- /dev/null +++ b/app/assets/javascripts/srp/lib/SHA1.js @@ -0,0 +1,174 @@ +/** +* +* Secure Hash Algorithm (SHA1) +* http://www.webtoolkit.info/ +* +**/ + +function SHA1 (msg) { + + function rotate_left(n,s) { + var t4 = ( n<<s ) | (n>>>(32-s)); + return t4; + }; + + function lsb_hex(val) { + var str=""; + var i; + var vh; + var vl; + + for( i=0; i<=6; i+=2 ) { + vh = (val>>>(i*4+4))&0x0f; + vl = (val>>>(i*4))&0x0f; + str += vh.toString(16) + vl.toString(16); + } + return str; + }; + + function cvt_hex(val) { + var str=""; + var i; + var v; + + for( i=7; i>=0; i-- ) { + v = (val>>>(i*4))&0x0f; + str += v.toString(16); + } + return str; + }; + + + function Utf8Encode(string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }; + + var blockstart; + var i, j; + var W = new Array(80); + var H0 = 0x67452301; + var H1 = 0xEFCDAB89; + var H2 = 0x98BADCFE; + var H3 = 0x10325476; + var H4 = 0xC3D2E1F0; + var A, B, C, D, E; + var temp; + + msg = Utf8Encode(msg); + + var msg_len = msg.length; + + var word_array = new Array(); + for( i=0; i<msg_len-3; i+=4 ) { + j = msg.charCodeAt(i)<<24 | msg.charCodeAt(i+1)<<16 | + msg.charCodeAt(i+2)<<8 | msg.charCodeAt(i+3); + word_array.push( j ); + } + + switch( msg_len % 4 ) { + case 0: + i = 0x080000000; + break; + case 1: + i = msg.charCodeAt(msg_len-1)<<24 | 0x0800000; + break; + + case 2: + i = msg.charCodeAt(msg_len-2)<<24 | msg.charCodeAt(msg_len-1)<<16 | 0x08000; + break; + + case 3: + i = msg.charCodeAt(msg_len-3)<<24 | msg.charCodeAt(msg_len-2)<<16 | msg.charCodeAt(msg_len-1)<<8 | 0x80; + break; + } + + word_array.push( i ); + + while( (word_array.length % 16) != 14 ) word_array.push( 0 ); + + word_array.push( msg_len>>>29 ); + word_array.push( (msg_len<<3)&0x0ffffffff ); + + + for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) { + + for( i=0; i<16; i++ ) W[i] = word_array[blockstart+i]; + for( i=16; i<=79; i++ ) W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); + + A = H0; + B = H1; + C = H2; + D = H3; + E = H4; + + for( i= 0; i<=19; i++ ) { + temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B,30); + B = A; + A = temp; + } + + for( i=20; i<=39; i++ ) { + temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B,30); + B = A; + A = temp; + } + + for( i=40; i<=59; i++ ) { + temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B,30); + B = A; + A = temp; + } + + for( i=60; i<=79; i++ ) { + temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff; + E = D; + D = C; + C = rotate_left(B,30); + B = A; + A = temp; + } + + H0 = (H0 + A) & 0x0ffffffff; + H1 = (H1 + B) & 0x0ffffffff; + H2 = (H2 + C) & 0x0ffffffff; + H3 = (H3 + D) & 0x0ffffffff; + H4 = (H4 + E) & 0x0ffffffff; + + } + + var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4); + + return temp.toLowerCase(); + +} diff --git a/app/assets/javascripts/srp/lib/SHA256.js b/app/assets/javascripts/srp/lib/SHA256.js new file mode 100644 index 0000000..f47077b --- /dev/null +++ b/app/assets/javascripts/srp/lib/SHA256.js @@ -0,0 +1,100 @@ +/** +* +* Secure Hash Algorithm (SHA256) +* http://www.webtoolkit.info/ +* +* Original code by Angel Marin, Paul Johnston. +* +**/ + +function SHA256(s){ + + var chrsz = 8; + var hexcase = 0; + + function safe_add (x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + function S (X, n) { return ( X >>> n ) | (X << (32 - n)); } + function R (X, n) { return ( X >>> n ); } + function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); } + function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); } + function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); } + function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); } + function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); } + function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); } + + function core_sha256 (m, l) { + var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 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); + var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19); + var W = new Array(64); + var a, b, c, d, e, f, g, h, i, j; + var T1, T2; + + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for ( var i = 0; i<m.length; i+=16 ) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + + for ( var j = 0; j<64; j++) { + if (j < 16) W[j] = m[j + i]; + else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]); + + T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]); + T2 = safe_add(Sigma0256(a), Maj(a, b, c)); + + h = g; + g = f; + f = e; + e = safe_add(d, T1); + d = c; + c = b; + b = a; + a = safe_add(T1, T2); + } + + HASH[0] = safe_add(a, HASH[0]); + HASH[1] = safe_add(b, HASH[1]); + HASH[2] = safe_add(c, HASH[2]); + HASH[3] = safe_add(d, HASH[3]); + HASH[4] = safe_add(e, HASH[4]); + HASH[5] = safe_add(f, HASH[5]); + HASH[6] = safe_add(g, HASH[6]); + HASH[7] = safe_add(h, HASH[7]); + } + return HASH; + } + + function str2binb (str) { + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) { + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32); + } + return bin; + } + + function binb2hex (binarray) { + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; + } + + return binb2hex(core_sha256(str2binb(s), s.length * chrsz)); + +} diff --git a/app/assets/javascripts/srp/lib/aes.js b/app/assets/javascripts/srp/lib/aes.js new file mode 100644 index 0000000..5682618 --- /dev/null +++ b/app/assets/javascripts/srp/lib/aes.js @@ -0,0 +1,771 @@ +/* + * aes.js: implements AES - Advanced Encryption Standard + * from the SlowAES project, http://code.google.com/p/slowaes/ + * + * Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), + * Mark Percival ( http://mpercival.com ), + * + * Ported from C code written by Laurent Haan ( http://www.progressive-coding.com ) + * + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/ + */ + +var slowAES = { + /* + * START AES SECTION + */ + aes:{ + // structure of valid key sizes + keySize:{ + SIZE_128:16, + SIZE_192:24, + SIZE_256:32 + }, + + // Rijndael S-box + sbox:[ + 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 ], + + // Rijndael Inverted S-box + rsbox: + [ 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 ], + + /* rotate the word eight bits to the left */ + rotate:function(word) + { + var c = word[0]; + for (var i = 0; i < 3; i++) + word[i] = word[i+1]; + word[3] = c; + + return word; + }, + + // Rijndael Rcon + Rcon:[ + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, + 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, + 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, + 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, + 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, + 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, + 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, + 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, + 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, + 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, + 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, + 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, + 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, + 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, + 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, + 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb ], + + G2X: [ + 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 + ], + + G3X: [ + 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 + ], + + G9X: [ + 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 + ], + + GBX: [ + 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 + ], + + GDX: [ + 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 + ], + + GEX: [ + 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 + ], + + // Key Schedule Core + core:function(word,iteration) + { + /* rotate the 32-bit word 8 bits to the left */ + word = this.rotate(word); + /* apply S-Box substitution on all 4 parts of the 32-bit word */ + for (var i = 0; i < 4; ++i) + word[i] = this.sbox[word[i]]; + /* XOR the output of the rcon operation with i to the first part (leftmost) only */ + word[0] = word[0]^this.Rcon[iteration]; + return word; + }, + + /* Rijndael's key expansion + * expands an 128,192,256 key into an 176,208,240 bytes key + * + * expandedKey is a pointer to an char array of large enough size + * key is a pointer to a non-expanded key + */ + expandKey:function(key,size) + { + var expandedKeySize = (16*(this.numberOfRounds(size)+1)); + + /* current expanded keySize, in bytes */ + var currentSize = 0; + var rconIteration = 1; + var t = []; // temporary 4-byte variable + + var expandedKey = []; + for(var i = 0;i < expandedKeySize;i++) + expandedKey[i] = 0; + + /* set the 16,24,32 bytes of the expanded key to the input key */ + for (var j = 0; j < size; j++) + expandedKey[j] = key[j]; + currentSize += size; + + while (currentSize < expandedKeySize) + { + /* assign the previous 4 bytes to the temporary value t */ + for (var k = 0; k < 4; k++) + t[k] = expandedKey[(currentSize - 4) + k]; + + /* every 16,24,32 bytes we apply the core schedule to t + * and increment rconIteration afterwards + */ + if(currentSize % size == 0) + t = this.core(t, rconIteration++); + + /* For 256-bit keys, we add an extra sbox to the calculation */ + if(size == this.keySize.SIZE_256 && ((currentSize % size) == 16)) + for(var l = 0; l < 4; l++) + t[l] = this.sbox[t[l]]; + + /* We XOR t with the four-byte block 16,24,32 bytes before the new expanded key. + * This becomes the next four bytes in the expanded key. + */ + for(var m = 0; m < 4; m++) { + expandedKey[currentSize] = expandedKey[currentSize - size] ^ t[m]; + currentSize++; + } + } + return expandedKey; + }, + + // Adds (XORs) the round key to the state + addRoundKey:function(state,roundKey) + { + for (var i = 0; i < 16; i++) + state[i] ^= roundKey[i]; + return state; + }, + + // Creates a round key from the given expanded key and the + // position within the expanded key. + createRoundKey:function(expandedKey,roundKeyPointer) + { + var roundKey = []; + for (var i = 0; i < 4; i++) + for (var j = 0; j < 4; j++) + roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j]; + return roundKey; + }, + + /* substitute all the values from the state with the value in the SBox + * using the state value as index for the SBox + */ + subBytes:function(state,isInv) + { + for (var i = 0; i < 16; i++) + state[i] = isInv?this.rsbox[state[i]]:this.sbox[state[i]]; + return state; + }, + + /* iterate over the 4 rows and call shiftRow() with that row */ + shiftRows:function(state,isInv) + { + for (var i = 0; i < 4; i++) + state = this.shiftRow(state,i*4, i,isInv); + return state; + }, + + /* each iteration shifts the row to the left by 1 */ + shiftRow:function(state,statePointer,nbr,isInv) + { + for (var i = 0; i < nbr; i++) + { + if(isInv) + { + var tmp = state[statePointer + 3]; + for (var j = 3; j > 0; j--) + state[statePointer + j] = state[statePointer + j-1]; + state[statePointer] = tmp; + } + else + { + var tmp = state[statePointer]; + for (var j = 0; j < 3; j++) + state[statePointer + j] = state[statePointer + j+1]; + state[statePointer + 3] = tmp; + } + } + return state; + }, + + // galois multiplication of 8 bit characters a and b + galois_multiplication:function(a,b) + { + var p = 0; + for(var counter = 0; counter < 8; counter++) + { + if((b & 1) == 1) + p ^= a; + if(p > 0x100) p ^= 0x100; + var hi_bit_set = (a & 0x80); //keep p 8 bit + a <<= 1; + if(a > 0x100) a ^= 0x100; //keep a 8 bit + if(hi_bit_set == 0x80) + a ^= 0x1b; + if(a > 0x100) a ^= 0x100; //keep a 8 bit + b >>= 1; + if(b > 0x100) b ^= 0x100; //keep b 8 bit + } + return p; + }, + + // galois multipication of the 4x4 matrix + mixColumns:function(state,isInv) + { + var column = []; + /* iterate over the 4 columns */ + for (var i = 0; i < 4; i++) + { + /* construct one column by iterating over the 4 rows */ + for (var j = 0; j < 4; j++) + column[j] = state[(j*4)+i]; + /* apply the mixColumn on one column */ + column = this.mixColumn(column,isInv); + /* put the values back into the state */ + for (var k = 0; k < 4; k++) + state[(k*4)+i] = column[k]; + } + return state; + }, + + // galois multipication of 1 column of the 4x4 matrix + mixColumn:function(column,isInv) + { + var mult = []; + if(isInv) + mult = [14,9,13,11]; + else + mult = [2,1,1,3]; + var cpy = []; + for(var i = 0; i < 4; i++) + cpy[i] = column[i]; + + column[0] = this.galois_multiplication(cpy[0],mult[0]) ^ + this.galois_multiplication(cpy[3],mult[1]) ^ + this.galois_multiplication(cpy[2],mult[2]) ^ + this.galois_multiplication(cpy[1],mult[3]); + column[1] = this.galois_multiplication(cpy[1],mult[0]) ^ + this.galois_multiplication(cpy[0],mult[1]) ^ + this.galois_multiplication(cpy[3],mult[2]) ^ + this.galois_multiplication(cpy[2],mult[3]); + column[2] = this.galois_multiplication(cpy[2],mult[0]) ^ + this.galois_multiplication(cpy[1],mult[1]) ^ + this.galois_multiplication(cpy[0],mult[2]) ^ + this.galois_multiplication(cpy[3],mult[3]); + column[3] = this.galois_multiplication(cpy[3],mult[0]) ^ + this.galois_multiplication(cpy[2],mult[1]) ^ + this.galois_multiplication(cpy[1],mult[2]) ^ + this.galois_multiplication(cpy[0],mult[3]); + return column; + }, + + // applies the 4 operations of the forward round in sequence + round:function(state, roundKey) + { + state = this.subBytes(state,false); + state = this.shiftRows(state,false); + state = this.mixColumns(state,false); + state = this.addRoundKey(state, roundKey); + return state; + }, + + // applies the 4 operations of the inverse round in sequence + invRound:function(state,roundKey) + { + state = this.shiftRows(state,true); + state = this.subBytes(state,true); + state = this.addRoundKey(state, roundKey); + state = this.mixColumns(state,true); + return state; + }, + + /* + * Perform the initial operations, the standard round, and the final operations + * of the forward aes, creating a round key for each round + */ + main:function(state,expandedKey,nbrRounds) + { + state = this.addRoundKey(state, this.createRoundKey(expandedKey,0)); + for (var i = 1; i < nbrRounds; i++) + state = this.round(state, this.createRoundKey(expandedKey,16*i)); + state = this.subBytes(state,false); + state = this.shiftRows(state,false); + state = this.addRoundKey(state, this.createRoundKey(expandedKey,16*nbrRounds)); + return state; + }, + + /* + * Perform the initial operations, the standard round, and the final operations + * of the inverse aes, creating a round key for each round + */ + invMain:function(state, expandedKey, nbrRounds) + { + state = this.addRoundKey(state, this.createRoundKey(expandedKey,16*nbrRounds)); + for (var i = nbrRounds-1; i > 0; i--) + state = this.invRound(state, this.createRoundKey(expandedKey,16*i)); + state = this.shiftRows(state,true); + state = this.subBytes(state,true); + state = this.addRoundKey(state, this.createRoundKey(expandedKey,0)); + return state; + }, + + numberOfRounds:function(size) + { + var nbrRounds; + switch (size) /* set the number of rounds */ + { + case this.keySize.SIZE_128: + nbrRounds = 10; + break; + case this.keySize.SIZE_192: + nbrRounds = 12; + break; + case this.keySize.SIZE_256: + nbrRounds = 14; + break; + default: + return null; + break; + } + return nbrRounds; + }, + + // encrypts a 128 bit input block against the given key of size specified + encrypt:function(input,key,size) + { + var output = []; + var block = []; /* the 128 bit block to encode */ + var nbrRounds = this.numberOfRounds(size); + /* Set the block values, for the block: + * a0,0 a0,1 a0,2 a0,3 + * a1,0 a1,1 a1,2 a1,3 + * a2,0 a2,1 a2,2 a2,3 + * a3,0 a3,1 a3,2 a3,3 + * the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 + */ + for (var i = 0; i < 4; i++) /* iterate over the columns */ + for (var j = 0; j < 4; j++) /* iterate over the rows */ + block[(i+(j*4))] = input[(i*4)+j]; + + /* expand the key into an 176, 208, 240 bytes key */ + var expandedKey = this.expandKey(key, size); /* the expanded key */ + /* encrypt the block using the expandedKey */ + block = this.main(block, expandedKey, nbrRounds); + for (var k = 0; k < 4; k++) /* unmap the block again into the output */ + for (var l = 0; l < 4; l++) /* iterate over the rows */ + output[(k*4)+l] = block[(k+(l*4))]; + return output; + }, + + // decrypts a 128 bit input block against the given key of size specified + decrypt:function(input, key, size) + { + var output = []; + var block = []; /* the 128 bit block to decode */ + var nbrRounds = this.numberOfRounds(size); + /* Set the block values, for the block: + * a0,0 a0,1 a0,2 a0,3 + * a1,0 a1,1 a1,2 a1,3 + * a2,0 a2,1 a2,2 a2,3 + * a3,0 a3,1 a3,2 a3,3 + * the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 + */ + for (var i = 0; i < 4; i++) /* iterate over the columns */ + for (var j = 0; j < 4; j++) /* iterate over the rows */ + block[(i+(j*4))] = input[(i*4)+j]; + /* expand the key into an 176, 208, 240 bytes key */ + var expandedKey = this.expandKey(key, size); + /* decrypt the block using the expandedKey */ + block = this.invMain(block, expandedKey, nbrRounds); + for (var k = 0; k < 4; k++)/* unmap the block again into the output */ + for (var l = 0; l < 4; l++)/* iterate over the rows */ + output[(k*4)+l] = block[(k+(l*4))]; + return output; + } + }, + /* + * END AES SECTION + */ + + /* + * START MODE OF OPERATION SECTION + */ + //structure of supported modes of operation + modeOfOperation:{ + OFB:0, + CFB:1, + CBC:2 + }, + + // gets a properly padded block + getPaddedBlock: function(bytesIn,start,end,mode) + { + if(end - start > 16) + end = start + 16; + + var array = bytesIn.slice(start, end); + + if (mode == this.modeOfOperation.CBC) + { + var cpad = 16 - array.length; + while(array.length < 16) + { + array.push(cpad); + } + } + + return array; + }, + + /* + * Mode of Operation Encryption + * bytesIn - Input String as array of bytes + * mode - mode of type modeOfOperation + * key - a number array of length 'size' + * size - the bit length of the key + * iv - the 128 bit number array Initialization Vector + */ + encrypt: function (bytesIn, mode, key, size, iv) + { + if(key.length%size) + { + throw 'Key length does not match specified size.'; + } + if(iv.length%16) + { + throw 'iv length must be 128 bits.'; + } + // the AES input/output + var byteArray = []; + var input = []; + var output = []; + var ciphertext = []; + var cipherOut = []; + // char firstRound + var firstRound = true; + if (bytesIn !== null) + { + for (var j = 0;j < Math.ceil(bytesIn.length/16); j++) + { + var start = j*16; + var end = j*16+16; + if(j*16+16 > bytesIn.length) + end = bytesIn.length; + byteArray = this.getPaddedBlock(bytesIn,start,end,mode); + if (mode == this.modeOfOperation.CFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (var i = 0; i < 16; i++) + ciphertext[i] = byteArray[i] ^ output[i]; + for(var k = 0;k < end-start;k++) + cipherOut.push(ciphertext[k]); + input = ciphertext; + } + else if (mode == this.modeOfOperation.OFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (var i = 0; i < 16; i++) + ciphertext[i] = byteArray[i] ^ output[i]; + for(var k = 0;k < end-start;k++) + cipherOut.push(ciphertext[k]); + input = output; + } + else if (mode == this.modeOfOperation.CBC) + { + for (var i = 0; i < 16; i++) + input[i] = byteArray[i] ^ ((firstRound) ? iv[i] : ciphertext[i]); + firstRound = false; + ciphertext = this.aes.encrypt(input, key, size); + // always 16 bytes because of the padding for CBC + for(var k = 0;k < 16;k++) + cipherOut.push(ciphertext[k]); + } + } + } + return {mode:mode,originalsize:bytesIn.length,cipher:cipherOut}; + }, + + /* + * Mode of Operation Decryption + * cipherIn - Encrypted String as array of bytes + * originalsize - The unencrypted string length - required for CBC + * mode - mode of type modeOfOperation + * key - a number array of length 'size' + * size - the bit length of the key + * iv - the 128 bit number array Initialization Vector + */ + decrypt:function(cipherIn,originalsize,mode,key,size,iv) + { + if(key.length%size) + { + throw 'Key length does not match specified size.'; + return null; + } + if(iv.length%16) + { + throw 'iv length must be 128 bits.'; + } + // the AES input/output + var ciphertext = []; + var input = []; + var output = []; + var byteArray = []; + var bytesOut = []; + // char firstRound + var firstRound = true; + if (cipherIn !== null) + { + for (var j = 0;j < Math.ceil(cipherIn.length/16); j++) + { + var start = j*16; + var end = j*16+16; + if(j*16+16 > cipherIn.length) + end = cipherIn.length; + ciphertext = this.getPaddedBlock(cipherIn,start,end,mode); + if (mode == this.modeOfOperation.CFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (i = 0; i < 16; i++) + byteArray[i] = output[i] ^ ciphertext[i]; + for(var k = 0;k < end-start;k++) + bytesOut.push(byteArray[k]); + input = ciphertext; + } + else if (mode == this.modeOfOperation.OFB) + { + if (firstRound) + { + output = this.aes.encrypt(iv, key, size); + firstRound = false; + } + else + output = this.aes.encrypt(input, key, size); + for (i = 0; i < 16; i++) + byteArray[i] = output[i] ^ ciphertext[i]; + for(var k = 0;k < end-start;k++) + bytesOut.push(byteArray[k]); + input = output; + } + else if(mode == this.modeOfOperation.CBC) + { + output = this.aes.decrypt(ciphertext, key, size); + for (i = 0; i < 16; i++) + byteArray[i] = ((firstRound) ? iv[i] : input[i]) ^ output[i]; + firstRound = false; + if (originalsize < end) + for(var k = 0;k < originalsize-start;k++) + bytesOut.push(byteArray[k]); + else + for(var k = 0;k < end-start;k++) + bytesOut.push(byteArray[k]); + input = ciphertext; + } + } + } + return bytesOut; + } + /* + * END MODE OF OPERATION SECTION + */ +}; + diff --git a/app/assets/javascripts/srp/lib/cryptoHelpers.js b/app/assets/javascripts/srp/lib/cryptoHelpers.js new file mode 100644 index 0000000..a77ee42 --- /dev/null +++ b/app/assets/javascripts/srp/lib/cryptoHelpers.js @@ -0,0 +1,185 @@ +/* + * cryptoHelpers.js: implements AES - Advanced Encryption Standard + * from the SlowAES project, http://code.google.com/p/slowaes/ + * + * Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), + * Mark Percival ( http://mpercival.com ), + * Johan Sundstrom ( http://ecmanaut.blogspot.com ), + * John Resig ( http://ejohn.org ) + * + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/ + */ + + + +var cryptoHelpers = { + + // encodes a unicode string to UTF8 (8 bit characters are critical to AES functioning properly) + encode_utf8:function(s) + { + try{return unescape(encodeURIComponent(s));} + catch(e){throw 'error during utf8 encoding: cryptoHelpers.encode_utf8.';} + }, + + + // decodes a UTF8 string back to unicode + decode_utf8:function(s) + { + try{return decodeURIComponent(escape(s));} + catch(e){throw('error during utf8 decoding: cryptoHelpers.decode_utf8.');} + }, + + //convert a number array to a hex string + toHex:function() + { + var array = []; + if(arguments.length == 1 && arguments[0].constructor == Array) + array = arguments[0]; + else + array = arguments; + var ret = ''; + for(var i = 0;i < array.length;i++) + ret += (array[i] < 16 ? '0' : '') + array[i].toString(16); + return ret.toLowerCase(); + }, + + //convert a hex string to a number array + toNumbers:function(s) + { + var ret = []; + s.replace(/(..)/g,function(s){ + ret.push(parseInt(s,16)); + }); + return ret; + }, + + // get a random number in the range [min,max] + getRandom:function(min,max) + { + if(min === null) + min = 0; + if(max === null) + max = 1; + return Math.floor(Math.random()*(max+1)) + min; + }, + + generateSharedKey:function(len) + { + if(len === null) + len = 16; + var key = []; + for(var i = 0; i < len*2; i++) + key.push(this.getRandom(0,255)); + return key; + }, + + generatePrivateKey:function(s,size) + { + var sha = jsHash.sha2.arr_sha256(s); + return sha.slice(0,size); + }, + + convertStringToByteArray: function(s) + { + var byteArray = []; + for(var i = 0;i < s.length;i++) + { + byteArray.push(s.charCodeAt(i)); + } + return byteArray; + }, + + convertByteArrayToString: function(byteArray) + { + var s = ''; + for(var i = 0;i < byteArray.length;i++) + { + s += String.fromCharCode(byteArray[i]); + } + return s; + }, + + base64: { + // Takes a Nx16x1 byte array and converts it to Base64 + + chars: [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', + '=' // for decoding purposes + ], + + encode_line: function(flatArr){ + var b64 = ''; + + for (var i = 0; i < flatArr.length; i += 3){ + b64 += this.chars[flatArr[i] >> 2]; + b64 += this.chars[((flatArr[i] & 3) << 4) | (flatArr[i + 1] >> 4)]; + if (!(flatArr[i + 1] === null)){ + b64 += this.chars[((flatArr[i + 1] & 15) << 2) | (flatArr[i + 2] >> 6)]; + }else{ + b64 += '='; + } + if (!(flatArr[i + 2] === null)){ + b64 += this.chars[flatArr[i + 2] & 63]; + }else{ + b64 += '='; + } + } + return b64; + }, + + encode: function(flatArr) + { + var b64 = this.encode_line(flatArr); + // OpenSSL is super particular about line breaks + var broken_b64 = b64.slice(0, 64) + '\n'; + for (var i = 1; i < (Math.ceil(b64.length / 64)); i++) + { + broken_b64 += b64.slice(i * 64, i * 64 + 64) + (Math.ceil(b64.length / 64) == i + 1 ? '': '\n'); + } + return broken_b64; + }, + + decode: function(string) + { + string = string.replace(/[\r\n\t ]+/g, '') + '===='; // drop all whitespaces and pad with '=' (end of b64 marker) + var flatArr = []; + var c = []; + //var b = []; + for (var i = 0; true ; i = i + 4){ + c[0] = this.chars.indexOf(string.charAt(i)); + if(c[0] == 64){ + return flatArr; + } + c[1] = this.chars.indexOf(string.charAt(i + 1)); + c[2] = this.chars.indexOf(string.charAt(i + 2)); + c[3] = this.chars.indexOf(string.charAt(i + 3)); + + if( + (c[0] < 0) || // char1 is wrong + (c[1] < 0) || (c[1] == 64) || // char2 is wrong + (c[2] < 0) || // char3 is neither an valid char nor '=' + (c[3] < 0) // char4 is neither an valid char nor '=' + ){ + throw 'error during base64 decoding at pos '+i+': cryptoHelpers.base64.decode.'; + } + + flatArr.push((c[0] << 2) | (c[1] >> 4)); + if(c[2] >= 0 && c[2] < 64){ + flatArr.push(((c[1] & 15) << 4) | (c[2] >> 2)); + if(c[3] >= 0 && c[2] < 64){ + flatArr.push(((c[2] & 3) << 6) | c[3]); + } + } + } + } + } +}; + diff --git a/app/assets/javascripts/srp/lib/jsbn.js b/app/assets/javascripts/srp/lib/jsbn.js new file mode 100644 index 0000000..f557d12 --- /dev/null +++ b/app/assets/javascripts/srp/lib/jsbn.js @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<<dbits)-1); +BigInteger.prototype.DV = (1<<dbits); + +var BI_FP = 52; +BigInteger.prototype.FV = Math.pow(2,BI_FP); +BigInteger.prototype.F1 = BI_FP-dbits; +BigInteger.prototype.F2 = 2*dbits-BI_FP; + +// Digit conversions +var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; +var BI_RC = new Array(); +var rr,vv; +rr = "0".charCodeAt(0); +for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; +rr = "a".charCodeAt(0); +for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; +rr = "A".charCodeAt(0); +for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; + +function int2char(n) { return BI_RM.charAt(n); } +function intAt(s,i) { + var c = BI_RC[s.charCodeAt(i)]; + return (c==null)?-1:c; +} + +// (protected) copy this to r +function bnpCopyTo(r) { + for(var i = this.t-1; i >= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh; + this[this.t++] = (x>>(this.DB-sh)); + } + else + this[this.t-1] |= x<<sh; + sh += k; + if(sh >= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh; + } + this.clamp(); + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) clamp off excess high words +function bnpClamp() { + var c = this.s&this.DM; + while(this.t > 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1<<k)-1, d, m = false, r = "", i = this.t; + var p = this.DB-(i*this.DB)%k; + if(i-- > 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<<p)-1))<<(k-p); + d |= this[--i]>>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<<cbs)-1; + var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i; + for(i = this.t-1; i >= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<<bs; + } + for(i = ds-1; i >= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<<bs)-1; + r[0] = this[ds]>>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<<cbs; + r[i-ds] = this[i]>>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs; + r.t = this.t-ds; + r.clamp(); +} + +// (protected) r = this - a +function bnpSubTo(a,r) { + var i = 0, c = 0, m = Math.min(a.t,this.t); + while(i < m) { + c += this[i]-a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2; + var i = r.t, j = i-ys, t = (q==null)?nbi():q; + y.dlShiftTo(j,t); + if(r.compareTo(t) >= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1<<i)) > 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); diff --git a/app/assets/javascripts/srp/lib/jsbn2.js b/app/assets/javascripts/srp/lib/jsbn2.js new file mode 100644 index 0000000..b135844 --- /dev/null +++ b/app/assets/javascripts/srp/lib/jsbn2.js @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + +// Extended JavaScript BN functions, required for RSA private ops. + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0]; +} + +// (public) return value as byte +function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0; + this.fromString(x,256); + } +} + +// (public) convert to bigendian byte array +function bnToByteArray() { + var i = this.t, r = new Array(); + r[0] = this.s; + var p = this.DB-(i*this.DB)%8, d, k = 0; + if(i-- > 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<<p)-1))<<(8-p); + d |= this[--i]>>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<<n) +function bnpChangeBit(n,op) { + var r = BigInteger.ONE.shiftLeft(n); + this.bitwiseTo(r,op,r); + return r; +} + +// (public) this | (1<<n) +function bnSetBit(n) { return this.changeBit(n,op_or); } + +// (public) this & ~(1<<n) +function bnClearBit(n) { return this.changeBit(n,op_andnot); } + +// (public) this ^ (1<<n) +function bnFlipBit(n) { return this.changeBit(n,op_xor); } + +// (protected) r = this + a +function bnpAddTo(a,r) { + var i = 0, c = 0, m = Math.min(a.t,this.t); + while(i < m) { + c += this[i]+a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1; + g[1] = z.convert(this); + if(k > 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1<<i)) == 0) { + z.sqrTo(r,r2); t = r; r = r2; r2 = t; + if(--i < 0) { i = this.DB-1; --j; } + } + } + return z.revert(r); +} + +// (public) gcd(this,a) (HAC 14.54) +function bnGCD(a) { + var x = (this.s<0)?this.negate():this.clone(); + var y = (a.s<0)?a.negate():a.clone(); + if(x.compareTo(y) < 0) { var t = x; x = y; y = t; } + var i = x.getLowestSetBit(), g = y.getLowestSetBit(); + if(g < 0) return x; + if(i < g) g = i; + if(g > 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + a.fromInt(lowprimes[i]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + +// BigInteger interfaces not implemented in jsbn: + +// BigInteger(int signum, byte[] magnitude) +// double doubleValue() +// float floatValue() +// int hashCode() +// long longValue() +// static BigInteger valueOf(long val) diff --git a/app/assets/javascripts/srp/lib/prng4.js b/app/assets/javascripts/srp/lib/prng4.js new file mode 100644 index 0000000..ca3026d --- /dev/null +++ b/app/assets/javascripts/srp/lib/prng4.js @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + +// prng4.js - uses Arcfour as a PRNG + +function Arcfour() { + this.i = 0; + this.j = 0; + this.S = new Array(); +} + +// Initialize arcfour context from key, an array of ints, each from [0..255] +function ARC4init(key) { + var i, j, t; + for(i = 0; i < 256; ++i) + this.S[i] = i; + j = 0; + for(i = 0; i < 256; ++i) { + j = (j + this.S[i] + key[i % key.length]) & 255; + t = this.S[i]; + this.S[i] = this.S[j]; + this.S[j] = t; + } + this.i = 0; + this.j = 0; +} + +function ARC4next() { + var t; + this.i = (this.i + 1) & 255; + this.j = (this.j + this.S[this.i]) & 255; + t = this.S[this.i]; + this.S[this.i] = this.S[this.j]; + this.S[this.j] = t; + return this.S[(t + this.S[this.i]) & 255]; +} + +Arcfour.prototype.init = ARC4init; +Arcfour.prototype.next = ARC4next; + +// Plug in your RNG constructor here +function prng_newstate() { + return new Arcfour(); +} + +// Pool size must be a multiple of 4 and greater than 32. +// An array of bytes the size of the pool will be passed to init() +var rng_psize = 256; diff --git a/app/assets/javascripts/srp/lib/rng.js b/app/assets/javascripts/srp/lib/rng.js new file mode 100644 index 0000000..8d8de48 --- /dev/null +++ b/app/assets/javascripts/srp/lib/rng.js @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2003-2005 Tom Wu + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + +// Random number generator - requires a PRNG backend, e.g. prng4.js + +// For best results, put code like +// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'> +// in your main HTML document. + +var rng_state; +var rng_pool; +var rng_pptr; + +// Mix in a 32-bit integer into the pool +function rng_seed_int(x) { + rng_pool[rng_pptr++] ^= x & 255; + rng_pool[rng_pptr++] ^= (x >> 8) & 255; + rng_pool[rng_pptr++] ^= (x >> 16) & 255; + rng_pool[rng_pptr++] ^= (x >> 24) & 255; + if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; +} + +// Mix in the current time (w/milliseconds) into the pool +function rng_seed_time() { + rng_seed_int(new Date().getTime()); +} + +// Initialize the pool with junk if needed. +if(rng_pool == null) { + rng_pool = new Array(); + rng_pptr = 0; + var t; + if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { + // Extract entropy (256 bits) from NS4 RNG if available + var z = window.crypto.random(32); + for(t = 0; t < z.length; ++t) + rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; + } + while(rng_pptr < rng_psize) { // extract some randomness from Math.random() + t = Math.floor(65536 * Math.random()); + rng_pool[rng_pptr++] = t >>> 8; + rng_pool[rng_pptr++] = t & 255; + } + rng_pptr = 0; + rng_seed_time(); + //rng_seed_int(window.screenX); + //rng_seed_int(window.screenY); +} + +function rng_get_byte() { + if(rng_state == null) { + rng_seed_time(); + rng_state = prng_newstate(); + rng_state.init(rng_pool); + for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) + rng_pool[rng_pptr] = 0; + rng_pptr = 0; + //rng_pool = null; + } + // TODO: allow reseeding after first request + return rng_state.next(); +} + +function rng_get_bytes(ba) { + var i; + for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); +} + +function SecureRandom() {} + +SecureRandom.prototype.nextBytes = rng_get_bytes; diff --git a/app/assets/javascripts/srp/package.json b/app/assets/javascripts/srp/package.json new file mode 100644 index 0000000..611c187 --- /dev/null +++ b/app/assets/javascripts/srp/package.json @@ -0,0 +1,29 @@ +{ + "name": "srp_js", + "version": "0.4.0", + "description": "A secure remote password implementation for JavaScript", + "homepage": "https://github.com/leapcode/srp_js#readme", + "main": "index.js", + "devDependencies": { + "jasmine-core": "^2.3.4", + "jasmine-jquery": "^2.1.0", + "jquery": "^2.1.4", + "karma": "^0.13.9", + "karma-jasmine": "^0.3.6", + "karma-phantomjs-launcher": "^0.2.1", + "phantomjs": "^1.9.18", + "sinon": "^1.16.1" + }, + "scripts": { + "test": "node_modules/karma/bin/karma start --single-run" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/leapcode/srp_js.git" + }, + "author": "LEAP contributors", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/leapcode/srp_js/issues" + } +} diff --git a/app/assets/javascripts/srp/spec/account_spec.js b/app/assets/javascripts/srp/spec/account_spec.js new file mode 100644 index 0000000..4110778 --- /dev/null +++ b/app/assets/javascripts/srp/spec/account_spec.js @@ -0,0 +1,31 @@ +describe("Account", function() { + describe("without seeded values", function(){ + beforeEach(function() { + account = new srp.Account(); + }); + + it("fetches the password from the password field", function(){ + expect(account.password()).toBe("password"); + }); + + it("fetches the login from the login field", function(){ + expect(account.login()).toBe("testuser"); + }); + + }); + + describe("with seeded values", function(){ + beforeEach(function() { + account = new srp.Account("login", "secret"); + }); + + it("uses the seeded password", function(){ + expect(account.password()).toBe("secret"); + }); + + it("uses the seeded login", function(){ + expect(account.login()).toBe("login"); + }); + + }); +}); diff --git a/app/assets/javascripts/srp/spec/calculate_spec.js b/app/assets/javascripts/srp/spec/calculate_spec.js new file mode 100644 index 0000000..02bff1d --- /dev/null +++ b/app/assets/javascripts/srp/spec/calculate_spec.js @@ -0,0 +1,52 @@ +describe("Calculate", function() { + + beforeEach(function() { + calculate = new srp.Calculate(); + }); + + // login attempt with correct password that failed never the less: + var compare = { + username: "blues", + password: "justtest", + salt: "6a6ef9ce5cb998eb", + v: "a5da6d376d503e22d93385db0244c382d1413d9f721ad9866dfc5e895cf2a3331514ceec5f48aceab58b260651cc9ee1ba96d906f67a6b4a7414c82d1333607ebe96403ecc86050224dc4c17b1d30efdbb451a68d1b6a25cce10f0e844082329d3cb46e1c3d46298a0de2cd3b8c6acc1a80c206f0f10ec8cd3c050babdf338ba", + aa: "4decb8543891f5a744b1e9b5bc375a474bfe3c5417e1db176cefcc7ba915338a14f309f8e0a4c7641bc9c9b9bd2e91c4d1beda1772c30d0350c9ba44f7c5911dfe6bb593ac2a2b30f1f6e5ec8a656cb4947c1907cf62f8d7283cbe32eb44b02158b51091ae130afa6063bb28cdea9ae159d4f222571e146f8715bfa31af09868", + a: "d498c3d024ec17689b5320e33fc349a3f3f91320384155b3043fa410c90eab71", + bb: "dee64fd54daafc18b338c5783ade3ff4275dfee8c97008e2d9fb445880a2e1d452c822a35e8e3f012bc6facaa28022f8de3fb1d632667d635abde0afc0ca4ed06c9197ea88f379042b10bc7b7f816a1ec14fefe6e9adef4ab904315b3a3f36749f3f6d1083b0eb0029173770f8e9342b098298389ba49a88d4ea6b78a7f576a4", + s: "50973f6e8134f95bd04f54f522e6e57d957d0640f91f0a989ff775712b81d5856ae3bdd2aa9c5eda8019e9db18065519c99c33a62c7f12f98e7aed60b153feee9ab73ba1272b4d76aa002da8cd47c6da733c88a0e70d4c3d6752fd366d66efe40870d26fd5d1755883b9489721e1881376628bf6ef89902f35e5e7e31227e2f", + k: "dd93e648abfe2ac6c6d46e062ded60b31ec043e55ceca1946ec29508f4c68461", + m: "ccf0c492f715484dc8343e22cd5967c2c5d01de743c5f0a9c5cfd017db1804c" + }; + + it("calculates the proper A", function() { + expect(calculate.A(compare.a)).toBe(compare.aa); + }); + + it("prefixes A with 0 if needed", function() { + expect(calculate.A("3971782b")[0]).toBe("0"); + }); + + it("calculates the right x", function() { + x = calculate.X("testuser","password","7686acb8") + expect(x).toBe('84d6bb567ddf584b1d8c8728289644d45dbfbb02deedd05c0f64db96740f0398'); + }); + + it("calculates the right verifier", function() { + calculate_and_compare_verifier(compare); + }); + + it("calculates the right verifier with umlauts", function() { + with_umlauts = { + username: "test_joakcq", + password: "fs5uofäöìfvqynn", + salt: "eec1ff4c", + v: "551e82de8d61a6575a3da7fbede61f6f38164ed52eb64db031c1ec2316b474745d3ff24408bfcca3c50fc53283f2f975feebf1564d197051c834a56bf8bd804f3696d81e579915141f306242f133db210cbd11385afff01c355ca8446d92d8a54ff147ebb0e1cd3d5c78750a0488f1453473e9449a946c7c9298c167cc5adafc" + } + calculate_and_compare_verifier(with_umlauts); + }); + + function calculate_and_compare_verifier(values) { + x = calculate.X(values.username, values.password, values.salt) + expect(calculate.V(x)).toBe(values.v); + } +}); diff --git a/app/assets/javascripts/srp/spec/helper.js b/app/assets/javascripts/srp/spec/helper.js new file mode 100644 index 0000000..d1c699b --- /dev/null +++ b/app/assets/javascripts/srp/spec/helper.js @@ -0,0 +1,48 @@ +var specHelper = (function() { + // HELPERS + + function setupFakeXHR() { + this.xhr = sinon.useFakeXMLHttpRequest(); + var requests = this.requests = []; + this.xhr.onCreate = function (xhr) { + requests.push(xhr); + }; + this.expectRequest = expectRequest; + this.respondJSON = respondJSON; + this.respondXML = respondXML; + } + + // TODO: validate http verb + function expectRequest(url, content, verb) { + expect(this.requests.length).toBe(1); + expect(this.requests[0].url).toBe(url); + expect(decodeURI(this.requests[0].requestBody)).toBe(content); + if (verb) { + expect(this.requests[0].method).toBe(verb); + } + } + + function respondXML(content) { + var request = this.requests.pop(); + header = { "Content-Type": "application/xml;charset=utf-8" }; + body = '<?xml version="1.0" encoding="UTF-8"?>\n'; + body += content; + request.respond(200, header, body); + } + + function respondJSON(object, responseCode) { + var request = this.requests.pop(); + header = { "Content-Type": "application/json;charset=utf-8" }; + body = JSON.stringify(object); + request.respond(responseCode || 200, header, body); + } + + return { + setupFakeXHR: setupFakeXHR, + } + +})(); + +beforeEach(function () { + setFixtures("<form action='.' onsubmit='return register()'> <table> <tr><td>Username:</td><td><input type='text' id='srp_username' value='testuser' /></td></tr> <tr><td>Password:</td><td><input type='password' id='srp_password' value='password'/></td></tr> </table> <input type='submit'/> </form> "); +}); diff --git a/app/assets/javascripts/srp/spec/login_spec.js b/app/assets/javascripts/srp/spec/login_spec.js new file mode 100644 index 0000000..24c7f4f --- /dev/null +++ b/app/assets/javascripts/srp/spec/login_spec.js @@ -0,0 +1,113 @@ +describe("Login with srp var", function() { + + var fixtures = { + "failed_login": { + "username": "asre", + "password": "Started GET", + "salt": "ae631d2d5ed2c41d", + "verifier": "8abe157957f22cc3b0b004e964d8f4d036636b23c6489877db9a9f7e19f21b78df5b489171996dd4a57ab6714e31ed0f3187c930dd0b00654cab60aaf73d701cf71d3faed99da9cd37c0161c93f3e12c2627e286df9217bad7731d51c7558a7d07d9888808c5b62b275b07706cf2e3d0cdc628791c69975580f760c7bf28bae8", + "a": "eb9784d9", + "aa": "ab0109064a2da3c02c0cc6da028495d402affb814f4b40898c9c87922718bd03dbd41cf2fa0e23f4abd0f19722c3687b673177328ae4f74f48f7d8fafc30466652e97a2f8c438b471eb0ccbe66fb5bf0837ac7b2aa34bfc731714c3ce4fbb288abd59458e2e563391925a8b74b4179652839ea91da40a467702b1574728c9e22", + "bb": "ccc834b851d7d6e1aa86969705ecd53fd47c5e94c1e31f739db3534a73dee8eed362747d7b4c60ea9169352000dfe42ca8ae5d3b20bb8f40590106021e7a4cd398ca2df55cc209ad9732c8d6bd6c6acf8a27254dac3c74cbb326ee53a4519e6a630ccadebf1434f5e3d9bf99c7cd301255c94710445383808638394dd641aa27", + "s": "919418fb396e125dc8e881b01f3925029e8049e0f15032f601317a99489526fd46b8e8edb62962177b97efe2106a7da44b381e65a500ff1a86459683475b86b31fd81e73accc835a5e0da37b71ed68612c68fbe43a96b57bf3f5d560f71f37a3dbc7a2080c8a4dd7de1bb42cc6e1a21e66e3845f775cb4559ba9ac1faf551a39", + "k": "0aa8c328244c426c6165be08a1fa8b07e2949c1df577466b4815109221e2da6b", + "m": "8438a6e4f31334588b826ee92b7669dd8db59856c5934a9c659e1481bcdcae86", + "m2": "ec1fd1de67a08b981016272222f54f4b1c42768cb46cd3675fe6573fd60eb186" + }, + "py_srp": { + // these need to be the same as in the spec runner: + username: "testuser", + password: "password", + salt: '628365a0', + verifier: '4277ddfdd111cc6a4cd27af570172a93ff4dddd9441ad89ecd78b08504812819d85712fbb6d2b487798ea0e19eeb960ce129725286d1c891314c0620abce02ac0a37fac823d0858553aed30ba99622ec9c66cc937016b96e82ef9e3b5d06e1db707293459c0aa8e082b528fd236cda347c45d8b022a9d4f3701c696e0397332a', + // a valid auth attempt for the user / password given in the spec runner: + a: 'a5cccf937ea1bf72df5cf8099442552f5664da6780a75436d5a59bc77a8a9993', + aa: 'e67d222244564ccd2e37471f226b999a4e987f3d494c7d80e0d36169efd6c6c6d857a96924c25fc165e5e9b0212a31c30701ec376dc32e36be00bbcd6d2104789d368af984e26fc094374f90ee5746478f14cec45c7e131a3cbce15fe79e98894213dac4e63c3f73f644fe25aa8707bc58859dfd1b36972e4e34169db2622899', + // just for the sake of having a complete set of test vars: + b: '6aa5c88d1877af9907ccefad31083e1102a7121dc04706f681f66c8680fb7f05', + bb: 'd56a80aaafdf9f70598b5d1184f122f326a333fafd37ab76d6f7fba4a9c4ee59545be056335150bd64f04880bc8e76949469379fe9de17cf6f36f3ee11713d05f63050486bc73c545163169999ff01b55c0ca4e90d8856a6e3d3a6ffc70b70d993a5308a37a5c2399874344e083e72b3c9afa083d312dfe9096ea9a65023f135', + k: 'db6ec0bdab81742315861a828323ff492721bdcd114077a4124bc425e4bf328b', + m: '640e51d5ac5461591c31811221261f0e0eae7c08ce43c85e9556adbd94ed8c26', + m2: '49e48f8ac8c4da0e8a7374f73eeedbee2266e123d23fc1be1568523fc9c24b1e', + } + }; + + + describe("(Compatibility with py-srp)", function (){ + var A_, callback; + var data = fixtures.failed_login; + var old_pass, old_login, old_conf; + + + beforeEach(function() { + specHelper.setupFakeXHR.apply(this); + + calculate = new srp.Calculate(); + calculate.randomSalt = function() {return "4c78c3f8"}; + srp.session = new srp.Session(undefined, calculate); + + A_ = srp.session.calculateAndSetA(data.a) + old_login = $('#srp_username').val(); + old_conf = $('#srp_password_confirmation').val(); + old_pass = $('#srp_password').val(); + $('#srp_username').val(data.username); + $('#srp_password_confirmation').val(data.password); + $('#srp_password').val(data.password); + }); + + afterEach(function() { + $('#srp_username').val(old_login); + $('#srp_password_confirmation').val(old_conf); + $('#srp_password').val(old_pass); + this.xhr.restore(); + }); + + it("calculates the same A", function(){ + expect(A_).toBe(data.aa); + }); + + it("calculates the same key", function(){ + srp.session.calculations(data.salt, data.bb); + expect(srp.session.key()).toBe(data.k); + }); + + it("authenticates successfully", function(){ + srp.loggedIn = jasmine.createSpy(); + srp.login(); + + this.expectRequest('/1/sessions.json', 'login=' +data.username+ '&A=' +data.aa, 'POST'); + this.respondJSON({salt: data.salt, B: data.bb}); + this.expectRequest('/1/sessions/'+data.username+'.json', 'client_auth='+data.m, 'PUT'); + this.respondJSON({M2: data.m2}); + + expect(srp.loggedIn).toHaveBeenCalled(); + }); + + it("reports errors during handshake", function(){ + srp.error = jasmine.createSpy(); + var error = {login: "something went wrong on the server side"}; + srp.login(); + + this.expectRequest('/1/sessions.json', 'login=' +data.username+ '&A=' +data.aa, 'POST'); + this.respondJSON(error, 422); + //this.expectNoMoreRequests(); + + expect(srp.error).toHaveBeenCalledWith(error); + }); + + it("rejects B = 0", function(){ + srp.loggedIn = jasmine.createSpy(); + srp.error = jasmine.createSpy(); + srp.login(); + + this.expectRequest('/1/sessions.json', 'login=' +data.username+ '&A=' +data.aa, 'POST'); + this.respondJSON({salt: data.salt, B: 0}); + // aborting if B=0 + expect(this.requests).toEqual([]); + expect(srp.error).toHaveBeenCalledWith("Server send random number 0 - could not login."); + expect(srp.loggedIn).not.toHaveBeenCalled(); + }); + }); + + +}); diff --git a/app/assets/javascripts/srp/spec/session_spec.js b/app/assets/javascripts/srp/spec/session_spec.js new file mode 100644 index 0000000..b37d7b1 --- /dev/null +++ b/app/assets/javascripts/srp/spec/session_spec.js @@ -0,0 +1,108 @@ +describe("Session", function() { + + // data gathered from py-srp and ruby-srp + var old_compare = { + username: "UC6LTQ", + password: "PVSQ7DCEIR0B", + salt: "d6ed8dba", + v: "c86a8c04a4f71cb10bfe3fedb74bae545b9a20e0f3e95b6334fce1cb3384a296f75d774a3829ffd63f405f13f58ffbae415fd234b08b996c11e8618c17961defcebb1d244b388b75cf36882ee97182a900ebeaf7cffa0a83eed294f3a9449a06beb88954952759d2957b80ef851f4cc4fcaa6001fee4f00c273ecdd712d48371", + aa: "4decb8543891f5a744b1e9b5bc375a474bfe3c5417e1db176cefcc7ba915338a14f309f8e0a4c7641bc9c9b9bd2e91c4d1beda1772c30d0350c9ba44f7c5911dfe6bb593ac2a2b30f1f6e5ec8a656cb4947c1907cf62f8d7283cbe32eb44b02158b51091ae130afa6063bb28cdea9ae159d4f222571e146f8715bfa31af09868", + a: "d498c3d024ec17689b5320e33fc349a3f3f91320384155b3043fa410c90eab71", + bb: "5f5bedd1f95b6b0d6809614f162e49753acce6979e1041f4da5bfa91e1dadd2a5470270ed102a49c5f74fd42f2b61a8a1a43218159a22b31a7cbd4670679480e56d0e4e72a22c07e07102ff063045d0c3c96085dec1cc2959453e0299890bd95af76403cec6ec5f212667a75ae6f4a8327183d72c3ee85792ca43820fbccf244", + m: "bc30b8781e67a657e93d0a6cf7e7847fc60f79e2b0641e9c26b3522bc8f974cc" + } + + // login attempt with correct password that failed never the less: + var zero_prefixed_m = { + username: "blues", + password: "justtest", + salt: "6a6ef9ce5cb998eb", + v: "a5da6d376d503e22d93385db0244c382d1413d9f721ad9866dfc5e895cf2a3331514ceec5f48aceab58b260651cc9ee1ba96d906f67a6b4a7414c82d1333607ebe96403ecc86050224dc4c17b1d30efdbb451a68d1b6a25cce10f0e844082329d3cb46e1c3d46298a0de2cd3b8c6acc1a80c206f0f10ec8cd3c050babdf338ba", + aa: "4decb8543891f5a744b1e9b5bc375a474bfe3c5417e1db176cefcc7ba915338a14f309f8e0a4c7641bc9c9b9bd2e91c4d1beda1772c30d0350c9ba44f7c5911dfe6bb593ac2a2b30f1f6e5ec8a656cb4947c1907cf62f8d7283cbe32eb44b02158b51091ae130afa6063bb28cdea9ae159d4f222571e146f8715bfa31af09868", + a: "d498c3d024ec17689b5320e33fc349a3f3f91320384155b3043fa410c90eab71", + bb: "dee64fd54daafc18b338c5783ade3ff4275dfee8c97008e2d9fb445880a2e1d452c822a35e8e3f012bc6facaa28022f8de3fb1d632667d635abde0afc0ca4ed06c9197ea88f379042b10bc7b7f816a1ec14fefe6e9adef4ab904315b3a3f36749f3f6d1083b0eb0029173770f8e9342b098298389ba49a88d4ea6b78a7f576a4", + s: "050973f6e8134f95bd04f54f522e6e57d957d0640f91f0a989ff775712b81d5856ae3bdd2aa9c5eda8019e9db18065519c99c33a62c7f12f98e7aed60b153feee9ab73ba1272b4d76aa002da8cd47c6da733c88a0e70d4c3d6752fd366d66efe40870d26fd5d1755883b9489721e1881376628bf6ef89902f35e5e7e31227e2f", + k: "dd93e648abfe2ac6c6d46e062ded60b31ec043e55ceca1946ec29508f4c68461", + m: "0ccf0c492f715484dc8343e22cd5967c2c5d01de743c5f0a9c5cfd017db1804c" + }; + + var short_b = { + "username": "fwe", + "password": "eckout -b ne", + "salt": "67f5f4aaf82a2a86", + "verifier": "d0624d86b8ce793e8570d0a8e31df50bb5bd7c6bf56926b00b10125c541d663324018be5a9c9ec794e44e1be739270d0fa258af0e15c780d47ff889c881c7a6b22fd201265471953f2788f08b2f95709602b1a47207241432226bba224285c8ed706d0a47a49eb06c111dfdafe01fe6ac3ab98c9a4958a00a136d9c069bea065", + "a": "b82dbaac", + "aa": "6e0197741d4da91a97adb05c705dae37a778d44cab697afdbcfc2450a5ccbc96dae1f4144a8446b53bfda65bc4ae4bc04c81f41f17da3389a5477bd8c5799538fffda2d745a4aa0381297c904b474d0525c2d08b4f70f7d3f9c1c52a0e126fc3402e37ea82aed603fe76fa2d8827e1e5d80996260a8aba6dc53e5e57dd7bd6a4", + "bb": "c9ffd5cb17e29aedf08fb37f54af2f4b798ce8341d8d1f36fde589e76f8aa2541118125d419632eef1582fb4fe7d5df4e795c808b0b2f964f67927b73be6f7545f2d291b9b36ab3d4b9fd0eb506f22887706b94c36ff963af44050bd89043d85b6f75846244785624fd2afb91ee1b5706b5a6f453f057be14537faa8051be56", + "s": "ca95b0d1223f4180f9b664d7aab69325263ee8700c02cbb7b3e67f1b08f94e11397f03faf186559602f9948305c73a6b69eb31770421f9e69757a3e4235e61197eab703e8378a290d70c335f5b4a39af402d9c68512def102737c5e70182645f3a1b9e8dcfea6eb4407a2bfbe1d923b6a7322e1b058e2f551f584ab12b61bc2b", + "k": "2cc2a0641bfd142a9c34b038c61e64a2298d1fd07de10fae945ad9b1a6172d19", + "m": "c3e3096ed1553a7dad36d600cee4e2f43fa67e306ae9771fc045d4f1b092d5e6", + "m2": "13bae65005e54e6ccfc5c5d04e143c4ff1124972875be6860aa8a99ab179ebf3" + } + + var session; + + it("calculates the proper M even if that is 0 prefixed (INTEGRATION)", function() { + var compare = zero_prefixed_m; + account = new srp.Account(compare.username, compare.password); + session = new srp.Session(account); + session.calculateAndSetA(compare.a); + session.calculations(compare.salt, compare.bb); + expect(session.getS().toString(16)).toBe(compare.s); + expect(session.key()).toBe(compare.k); + expect(session.getM()).toBe(compare.m); + }); + + it("calculates the proper M from a smaller B (INTEGRATION)", function() { + // B has one less char than usual + var compare = short_b; + account = new srp.Account(compare.username, compare.password); + session = new srp.Session(account); + session.calculateAndSetA(compare.a); + session.calculations(compare.salt, compare.bb); + expect(session.getS().toString(16)).toBe(compare.s); + expect(session.key()).toBe(compare.k); + expect(session.getM()).toBe(compare.m); + }); + + + it("delegates login", function() { + var compare = zero_prefixed_m; + account = new srp.Account(compare.username, compare.password); + session = new srp.Session(account); + expect(session.login()).toBe(compare.username); + }); + + it('calculates secure user parameters for signup', function() { + var compare = short_b; + account = new srp.Account(compare.username, compare.password); + session = new srp.Session(account); + + var signupParams = session.signup(); + + expect(Object.keys(signupParams)).toEqual(['login', 'password_salt', 'password_verifier']); + }); + + it('calculates secure user parameters for update', function() { + var compare = short_b; + account = new srp.Account(compare.username, compare.password); + session = new srp.Session(account); + + var signupParams = session.update(); + + expect(Object.keys(signupParams)).toEqual(['login', 'password_salt', 'password_verifier']); + }); + + it("grabs extra signup parameters from account", function() { + account = jasmine.createSpyObj('account', ['login', 'password']); + account.loginParams = function() { + return { + "extraParam": "foobar" + } + } + session = new srp.Session(account); + + expect(session.signup().extraParam).toBe("foobar"); + }); + +}); diff --git a/app/assets/javascripts/srp/spec/signup_spec.js b/app/assets/javascripts/srp/spec/signup_spec.js new file mode 100644 index 0000000..89b0936 --- /dev/null +++ b/app/assets/javascripts/srp/spec/signup_spec.js @@ -0,0 +1,31 @@ +describe("Loading SRP", function() { + it("provides a signup function", function() { + expect(typeof srp.signup).toBe('function'); + }); + +}); + +describe("Signup with srp var", function() { + + beforeEach(function() { + specHelper.setupFakeXHR.apply(this); + calculate = new srp.Calculate(); + calculate.randomSalt = function() {return "4c78c3f8"}; + srp.session = new srp.Session(undefined, calculate); + }); + + afterEach(function() { + this.xhr.restore(); + }); + + it("identifies after successful registration (INTEGRATION)", function(){ + var callback = jasmine.createSpy(); + srp.signedUp = callback; + srp.signup(); + this.expectRequest('/1/users.json', "user[login]=testuser&user[password_salt]=4c78c3f8&user[password_verifier]=474c26aa42d11f20544a00f7bf9711c4b5cf7aab95ed448df82b95521b96668e7480b16efce81c861870302560ddf6604c67df54f1d04b99d5bb9d0f02c6051ada5dc9d594f0d4314e12f876cfca3dcd99fc9c98c2e6a5e04298b11061fb8549a22cde0564e91514080df79bca1c38c682214d65d590f66b3719f954b078b83c", 'POST') + this.respondJSON({password_salt: "4c78c3f8", login: "testuser", ok: "true"}); + expect(callback).toHaveBeenCalled(); + }); + +}); + diff --git a/app/assets/javascripts/srp/src/jqueryRest.js b/app/assets/javascripts/srp/src/jqueryRest.js new file mode 100644 index 0000000..0c58eb2 --- /dev/null +++ b/app/assets/javascripts/srp/src/jqueryRest.js @@ -0,0 +1,103 @@ +srp.remote = (function(){ + var jqueryRest = (function() { + + // TODO: Do we need to differentiate between PUT and POST? + function register(session) { + return $.post("/1/users.json", {user: session.signup() }); + } + + function update(session, token) { + return $.ajax({ + url: "/1/users/" + session.id() + ".json", + type: 'PUT', + headers: { Authorization: 'Token token="' + token + '"' }, + data: {user: session.update() } + }); + } + + function handshake(session) { + return $.post("/1/sessions.json", session.handshake()); + } + + function authenticate(session) { + return $.ajax({ + url: "/1/sessions/" + session.login() + ".json", + type: 'PUT', + data: {client_auth: session.getM()} + }); + } + + return { + register: register, + update: update, + handshake: handshake, + authenticate: authenticate + }; + }()); + + + function signup(){ + jqueryRest.register(srp.session) + .done(srp.signedUp) + .fail(error) + }; + + function update(submitEvent){ + var form = submitEvent.target; + var token = form.dataset.token; + jqueryRest.update(srp.session, token) + .done(srp.updated) + .fail(error) + }; + + function login(){ + jqueryRest.handshake(srp.session) + .done(receiveSalts) + .fail(error) + }; + + function receiveSalts(response){ + // B = 0 will make the algorithm always succeed + // -> refuse such a server answer + if(response.B === 0) { + srp.error("Server send random number 0 - could not login."); + } + else if(! response.salt || response.salt === 0) { + srp.error("Server failed to send salt - could not login."); + } + else + { + srp.session.calculations(response.salt, response.B); + jqueryRest.authenticate(srp.session) + .done(confirmAuthentication) + .fail(error); + } + }; + + // Receive M2 from the server and verify it + // If an error occurs, raise it as an alert. + function confirmAuthentication(response) + { + if (srp.session.validate(response.M2)) + srp.loggedIn(); + else + srp.error("Server key does not match"); + }; + + // The server will send error messages as json alongside + // the http error response. + function error(xhr, text, thrown) + { + if (xhr.responseText && xhr.responseText != "") + srp.error($.parseJSON(xhr.responseText)); + else + srp.error("Server did not respond."); + }; + + return { + signup: signup, + update: update, + login: login + } + +}()); diff --git a/app/assets/javascripts/srp/src/srp.js b/app/assets/javascripts/srp/src/srp.js new file mode 100644 index 0000000..efd50d2 --- /dev/null +++ b/app/assets/javascripts/srp/src/srp.js @@ -0,0 +1,24 @@ +var srp = (function(){ + + function signup() + { + srp.remote.signup(); + }; + + function login() + { + srp.remote.login(); + }; + + function update(submitEvent) + { + srp.remote.update(submitEvent); + }; + + return { + signup: signup, + update: update, + login: login + } +}()); + diff --git a/app/assets/javascripts/srp/src/srp_account.js b/app/assets/javascripts/srp/src/srp_account.js new file mode 100644 index 0000000..e949f12 --- /dev/null +++ b/app/assets/javascripts/srp/src/srp_account.js @@ -0,0 +1,17 @@ +srp.Account = function(login, password, id) { + + // Returns the user's identity + this.login = function() { + return login || document.getElementById("srp_username").value; + }; + + // Returns the password currently typed in + this.password = function() { + return password || document.getElementById("srp_password").value; + }; + + // The user's id + this.id = function() { + return id || document.getElementById("user_param").value; + }; +} diff --git a/app/assets/javascripts/srp/src/srp_calculate.js b/app/assets/javascripts/srp/src/srp_calculate.js new file mode 100644 index 0000000..e32def8 --- /dev/null +++ b/app/assets/javascripts/srp/src/srp_calculate.js @@ -0,0 +1,141 @@ +srp.Calculate = function() { + + // Variables used in the SRP protocol + var Nstr = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3"; + var N = new BigInteger(Nstr, 16); + var g = new BigInteger("2"); + var k = new BigInteger("bf66c44a428916cad64aa7c679f3fd897ad4c375e9bbb4cbf2f5de241d618ef0", 16); + var rng = new SecureRandom(); + + this.A = function(_a) { + a = new BigInteger(_a, 16); + return zeroPrefix(g.modPow(a, N).toString(16)); + }; + + // Calculates the X value + // x = H(s, H(I:p)) + this.X = function(login, password, salt) { + var salted = salt + this.hash(login + ":" + password) + return this.hashHex(salted); + }; + + this.V = this.A; + + // u = H(A,B) + this.U = function(A, B) { + return this.hashHex(A + B); + }; + + //S = (B - kg^x) ^ (a + ux) + this.S = function(_a, _A, _B, _x) { + var a = new BigInteger(_a, 16); + var x = new BigInteger(_x, 16); + var u = new BigInteger(this.U(_A, _B), 16); + var B = new BigInteger(_B, 16); + + var kgx = k.multiply(g.modPow(x, N)); + var aux = a.add(u.multiply(x)); + + return zeroPrefix(B.subtract(kgx).modPow(aux, N).toString(16)); + } + + this.K = function(_S) { + return this.hashHex(_S); + } + + this.nXorG = function() { + var hashN = this.hashHex(Nstr); + var hashG = this.hashHex(g.toString(16)); + return hexXor(hashN, hashG); + }; + + this.hashHex = function(hexString) { + return SHA256(hex2a(hexString)); + }; + + this.hash = function(string) { + return SHA256(utf8Encode(string)); + }; + + this.isInvalidEphemeral = function(a) { + return (g.modPow(a, N) == 0); + }; + + this.randomEphemeral = function() { + var a = new BigInteger(32, rng); + while(this.isInvalidEphemeral(a)) + { + a = new BigInteger(32, rng); + } + return zeroPrefix(a.toString(16)); + }; + + // some 16 byte random number + this.randomSalt = function() { + var salt = new BigInteger(64, rng); + return zeroPrefix(salt.toString(16)); + } + + // expose zeroPrefix for received values. + this.zeroPrefix = zeroPrefix; + + function hex2a(hex) { + var str = ''; + if(hex.length % 2) { + hex = "0" + hex; + } + for (var i = 0; i < hex.length; i += 2) + str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); + return str; + } + + + function zeroPrefix(hex) { + if (hex.length % 2) { + return "0" + hex; + } else { + return hex; + } + } + + + function removeLeading0(hex) { + if (hex[0] == "0") { + return hex.substr(1); + } else { + return hex; + } + } + + function hexXor(a, b) { + var str = ''; + for (var i = 0; i < a.length; i += 2) { + var xor = parseInt(a.substr(i, 2), 16) ^ parseInt(b.substr(i, 2), 16) + xor = xor.toString(16); + str += (xor.length == 1) ? ("0" + xor) : xor + } + return str; + } + + function utf8Encode(string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + var c = string.charCodeAt(n); + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + } + return utftext; + } +}; diff --git a/app/assets/javascripts/srp/src/srp_session.js b/app/assets/javascripts/srp/src/srp_session.js new file mode 100644 index 0000000..88f19d5 --- /dev/null +++ b/app/assets/javascripts/srp/src/srp_session.js @@ -0,0 +1,122 @@ +srp.Session = function(account, calculate) { + + // default for injected dependency + account = account || new srp.Account(); + calculate = calculate || new srp.Calculate(); + + var a = calculate.randomEphemeral(); + var A = calculate.A(a); + var S = null; + var K = null; + var M = null; + var M2 = null; + var authenticated = false; + + // *** Accessor methods *** + + // allows setting the random number A for testing + + this.calculateAndSetA = function(_a) { + a = _a; + A = calculate.A(_a); + return A; + }; + + this.update = function() { + var salt = calculate.randomSalt(); + var x = calculate.X(account.login(), account.password(), salt); + return { + login: account.login(), + password_salt: salt, + password_verifier: calculate.V(x) + }; + } + + this.signup = function() { + var loginParams = this.update(); + + if (account.loginParams) { + var extraParams = account.loginParams(); + for (var attr in extraParams) { + loginParams[attr] = extraParams[attr]; + } + } + + return loginParams; + }; + + this.handshake = function() { + return { + login: account.login(), + A: this.getA() + }; + }; + + this.getA = function() { + return A; + } + + // Delegate login & id so they can be used when talking to the remote + this.login = account.login; + this.id = account.id; + + // Calculate S, M, and M2 + // This is the client side of the SRP specification + this.calculations = function(salt, ephemeral) + { + //S -> C: s | B + var B = calculate.zeroPrefix(ephemeral); + salt = calculate.zeroPrefix(salt); + var x = calculate.X(account.login(), account.password(), salt); + S = calculate.S(a, A, B, x); + K = calculate.K(S); + + // M = H(H(N) xor H(g), H(I), s, A, B, K) + var xor = calculate.nXorG(); + var hash_i = calculate.hash(account.login()) + M = calculate.hashHex(xor + hash_i + salt + A + B + K); + //M2 = H(A, M, K) + M2 = calculate.hashHex(A + M + K); + }; + + + this.getS = function() { + return S; + } + + this.getM = function() { + return M; + } + + this.validate = function(serverM2) { + authenticated = (serverM2 && serverM2 == M2) + return authenticated; + } + + // If someone wants to use the session key for encrypting traffic, they can + // access the key with this function. + this.key = function() + { + if(K) { + return K; + } else { + this.onError("User has not been authenticated."); + } + }; + + // Encrypt plaintext using slowAES + this.encrypt = function(plaintext) + { + var key = cryptoHelpers.toNumbers(session.key()); + var byteMessage = cryptoHelpers.convertStringToByteArray(plaintext); + var iv = new Array(16); + rng.nextBytes(iv); + var paddedByteMessage = slowAES.getPaddedBlock(byteMessage, 0, byteMessage.length, slowAES.modeOfOperation.CFB); + var ciphertext = slowAES.encrypt(paddedByteMessage, slowAES.modeOfOperation.CFB, key, key.length, iv).cipher; + var retstring = cryptoHelpers.base64.encode(iv.concat(ciphertext)); + while(retstring.indexOf("+",0) > -1) + retstring = retstring.replace("+", "_"); + return retstring; + }; +}; + |