├── .gitignore ├── .travis.yml ├── browser.js ├── client.js ├── index.html ├── index.js ├── login.monopic ├── login.txt ├── package.json ├── readme.md ├── register.monopic ├── register.txt ├── server.js ├── test └── testrunner.js └── thinbus-original-client.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "io.js" 5 | - "node" 6 | - "lts/*" 7 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.thinbus = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Redistributions of source code must retain the above copyright notice, 67 | * this list of conditions, and the following disclaimer. 68 | * > Redistributions in binary form must reproduce the above copyright notice, 69 | * this list of conditions, and the following disclaimer in the documentation 70 | * or other materials provided with the distribution. 71 | * > Neither the name CryptoJS nor the names of its contributors may be used 72 | * to endorse or promote products derived from this software without specific 73 | * prior written permission. 74 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS," 75 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 76 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, 77 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 78 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 79 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 80 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 81 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 82 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 83 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 84 | * THE POSSIBILITY OF SUCH DAMAGE. 85 | */ 86 | 87 | var CryptoJS=CryptoJS||function(e,z){var m={},y=m.lib={},i=function(){},d=y.Base={extend:function(f){i.prototype=this;var g=new i;f&&g.mixIn(f);g.hasOwnProperty("init")||(g.init=function(){g.$super.init.apply(this,arguments)});g.init.prototype=g;g.$super=this;return g},create:function(){var f=this.extend();f.init.apply(f,arguments);return f},init:function(){},mixIn:function(f){for(var g in f){f.hasOwnProperty(g)&&(this[g]=f[g])}f.hasOwnProperty("toString")&&(this.toString=f.toString)},clone:function(){return this.init.prototype.extend(this)}},a=y.WordArray=d.extend({init:function(f,g){f=this.words=f||[];this.sigBytes=g!=z?g:4*f.length},toString:function(f){return(f||r).stringify(this)},concat:function(g){var k=this.words,j=g.words,f=this.sigBytes;g=g.sigBytes;this.clamp();if(f%4){for(var h=0;h>>2]|=(j[h>>>2]>>>24-8*(h%4)&255)<<24-8*((f+h)%4)}}else{if(65535>>2]=j[h>>>2]}}else{k.push.apply(k,j)}}this.sigBytes+=g;return this},clamp:function(){var f=this.words,g=this.sigBytes;f[g>>>2]&=4294967295<<32-8*(g%4);f.length=e.ceil(g/4)},clone:function(){var f=d.clone.call(this);f.words=this.words.slice(0);return f},random:function(f){for(var h=[],g=0;g>>2]>>>24-8*(f%4)&255;j.push((h>>>4).toString(16));j.push((h&15).toString(16))}return j.join("")},parse:function(g){for(var j=g.length,h=[],f=0;f>>3]|=parseInt(g.substr(f,2),16)<<24-4*(f%8)}return new a.init(h,j/2)}},c=p.Latin1={stringify:function(g){var j=g.words;g=g.sigBytes;for(var h=[],f=0;f>>2]>>>24-8*(f%4)&255))}return h.join("")},parse:function(g){for(var j=g.length,h=[],f=0;f>>2]|=(g.charCodeAt(f)&255)<<24-8*(f%4)}return new a.init(h,j)}},b=p.Utf8={stringify:function(f){try{return decodeURIComponent(escape(c.stringify(f)))}catch(g){throw Error("Malformed UTF-8 data")}},parse:function(f){return c.parse(unescape(encodeURIComponent(f)))}},n=y.BufferedBlockAlgorithm=d.extend({reset:function(){this._data=new a.init;this._nDataBytes=0},_append:function(f){"string"==typeof f&&(f=b.parse(f));this._data.concat(f);this._nDataBytes+=f.sigBytes},_process:function(j){var s=this._data,q=s.words,h=s.sigBytes,l=this.blockSize,k=h/(4*l),k=j?e.ceil(k):e.max((k|0)-this._minBufferSize,0);j=k*l;h=e.min(4*j,h);if(j){for(var g=0;gd;){var c;o:{c=z;for(var p=i.sqrt(c),r=2;r<=p;r++){if(!(c%r)){c=!1;break o}}c=!0}c&&(8>d&&(e[d]=y(i.pow(z,0.5))),b[d]=y(i.pow(z,1/3)),d++);z++}var o=[],n=n.SHA256=m.extend({_doReset:function(){this._hash=new A.init(e.slice(0))},_doProcessBlock:function(G,F){for(var H=this._hash.words,E=H[0],D=H[1],t=H[2],x=H[3],q=H[4],w=H[5],v=H[6],u=H[7],s=0;64>s;s++){if(16>s){o[s]=G[F+s]|0}else{var a=o[s-15],C=o[s-2];o[s]=((a<<25|a>>>7)^(a<<14|a>>>18)^a>>>3)+o[s-7]+((C<<15|C>>>17)^(C<<13|C>>>19)^C>>>10)+o[s-16]}a=u+((q<<26|q>>>6)^(q<<21|q>>>11)^(q<<7|q>>>25))+(q&w^~q&v)+b[s]+o[s];C=((E<<30|E>>>2)^(E<<19|E>>>13)^(E<<10|E>>>22))+(E&D^E&t^D&t);u=v;v=w;w=q;q=x+a|0;x=t;t=D;D=E;E=a+C|0}H[0]=H[0]+E|0;H[1]=H[1]+D|0;H[2]=H[2]+t|0;H[3]=H[3]+x|0;H[4]=H[4]+q|0;H[5]=H[5]+w|0;H[6]=H[6]+v|0;H[7]=H[7]+u|0},_doFinalize:function(){var g=this._data,j=g.words,f=8*this._nDataBytes,h=8*g.sigBytes;j[h>>>5]|=128<<24-h%32;j[(h+64>>>9<<4)+14]=i.floor(f/4294967296);j[(h+64>>>9<<4)+15]=f;g.sigBytes=4*j.length;this._process();return this._hash},clone:function(){var f=m.clone.call(this);f._hash=this._hash.clone();return f}});B.SHA256=m._createHelper(n);B.HmacSHA256=m._createHmacHelper(n)})(Math); 88 | var dbits;var canary=244837814094590;var j_lm=((canary&16777215)==15715070);function BigInteger(e,d,f){if(e!=null){if("number"==typeof e){this.fromNumber(e,d,f)}else{if(d==null&&"string"!=typeof e){this.fromString(e,256)}else{this.fromString(e,d)}}}}function nbi(){return new BigInteger(null)}function am1(f,a,b,e,h,g){while(--g>=0){var d=a*this[f++]+b[e]+h;h=Math.floor(d/67108864);b[e++]=d&67108863}return h}function am2(f,q,r,e,o,a){var k=q&32767,p=q>>15;while(--a>=0){var d=this[f]&32767;var g=this[f++]>>15;var b=p*d+g*k;d=k*d+((b&32767)<<15)+r[e]+(o&1073741823);o=(d>>>30)+(b>>>15)+p*g+(o>>>30);r[e++]=d&1073741823}return o}function am3(f,q,r,e,o,a){var k=q&16383,p=q>>14;while(--a>=0){var d=this[f]&16383;var g=this[f++]>>14;var b=p*d+g*k;d=k*d+((b&16383)<<14)+r[e]+o;o=(d>>28)+(b>>14)+p*g;r[e++]=d&268435455}return o}BigInteger.prototype.am=am3;dbits=28;BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--a){b[a]=this[a]}b.t=this.t;b.s=this.s}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0){this[0]=a}else{if(a<-1){this[0]=a+DV}else{this.t=0}}}function nbv(a){var b=nbi();b.fromInt(a);return b}function bnpFromString(h,c){var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==256){e=8}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{this.fromRadix(h,c);return}}}}}}this.t=0;this.s=0;var g=h.length,d=false,f=0;while(--g>=0){var a=(e==8)?h[g]&255:intAt(h,g);if(a<0){if(h.charAt(g)=="-"){d=true}continue}d=false;if(f==0){this[this.t++]=a}else{if(f+e>this.DB){this[this.t-1]|=(a&((1<<(this.DB-f))-1))<>(this.DB-f))}else{this[this.t-1]|=a<=this.DB){f-=this.DB}}if(e==8&&(h[0]&128)!=0){this.s=-1;if(f>0){this[this.t-1]|=((1<<(this.DB-f))-1)<0&&this[this.t-1]==a){--this.t}}function bnToString(c){if(this.s<0){return"-"+this.negate().toString(c)}var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{return this.toRadix(c)}}}}}var g=(1<0){if(j>j)>0){a=true;h=int2char(l)}while(f>=0){if(j>(j+=this.DB-e)}else{l=(this[f]>>(j-=e))&g;if(j<=0){j+=this.DB;--f}}if(l>0){a=true}if(a){h+=int2char(l)}}}return a?h:"0"}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return(this.s<0)?this.negate():this}function bnCompareTo(b){var d=this.s-b.s;if(d!=0){return d}var c=this.t;d=c-b.t;if(d!=0){return d}while(--c>=0){if((d=this[c]-b[c])!=0){return d}}return 0}function nbits(a){var c=1,b;if((b=a>>>16)!=0){a=b;c+=16}if((b=a>>8)!=0){a=b;c+=8}if((b=a>>4)!=0){a=b;c+=4}if((b=a>>2)!=0){a=b;c+=2}if((b=a>>1)!=0){a=b;c+=1}return c}function bnBitLength(){if(this.t<=0){return 0}return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM))}function bnpDLShiftTo(c,b){var a;for(a=this.t-1;a>=0;--a){b[a+c]=this[a]}for(a=c-1;a>=0;--a){b[a]=0}b.t=this.t+c;b.s=this.s}function bnpDRShiftTo(c,b){for(var a=c;a=0;--d){e[d+f+1]=(this[d]>>a)|h;h=(this[d]&g)<=0;--d){e[d]=0}e[f]=h;e.t=this.t+f+1;e.s=this.s;e.clamp()}function bnpRShiftTo(g,d){d.s=this.s;var e=Math.floor(g/this.DB);if(e>=this.t){d.t=0;return}var b=g%this.DB;var a=this.DB-b;var f=(1<>b;for(var c=e+1;c>b}if(b>0){d[this.t-e-1]|=(this.s&f)<>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g-=d.s}f.s=(g<0)?-1:0;if(g<-1){f[e++]=this.DV+g}else{if(g>0){f[e++]=g}}f.t=e;f.clamp()}function bnpMultiplyTo(c,e){var b=this.abs(),f=c.abs();var d=b.t;e.t=d+f.t;while(--d>=0){e[d]=0}for(d=0;d=0){d[b]=0}for(b=0;b=a.DV){d[b+a.t]-=a.DV;d[b+a.t+1]=1}}if(d.t>0){d[d.t-1]+=a.am(b,a[b],d,2*b,0,1)}d.s=0;d.clamp()}function bnpDivRemTo(n,h,g){var w=n.abs();if(w.t<=0){return}var k=this.abs();if(k.t0){w.lShiftTo(v,d);k.lShiftTo(v,g)}else{w.copyTo(d);k.copyTo(g)}var p=d.t;var b=d[p-1];if(b==0){return}var o=b*(1<1)?d[p-2]>>this.F2:0);var A=this.FV/o,z=(1<=0){g[g.t++]=1;g.subTo(f,g)}BigInteger.ONE.dlShiftTo(p,f);f.subTo(d,d);while(d.t=0){var c=(g[--u]==b)?this.DM:Math.floor(g[u]*A+(g[u-1]+x)*z);if((g[u]+=d.am(0,c,g,s,0,p))0){g.rShiftTo(v,g)}if(a<0){BigInteger.ZERO.subTo(g,g)}}function bnMod(b){var c=nbi();this.abs().divRemTo(b,null,c);if(this.s<0&&c.compareTo(BigInteger.ZERO)>0){b.subTo(c,c)}return c}function Classic(a){this.m=a}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0){return a.mod(this.m)}else{return a}}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1){return 0}var a=this[0];if((a&1)==0){return 0}var b=a&3;b=(b*(2-(a&15)*b))&15;b=(b*(2-(a&255)*b))&255;b=(b*(2-(((a&65535)*b)&65535)))&65535;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0){this.m.subTo(b,b)}return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}function montReduce(a){while(a.t<=this.mt2){a[a.t++]=0}for(var c=0;c>15)*this.mpl)&this.um)<<15))&a.DM;b=c+this.m.t;a[b]+=this.m.am(0,d,a,c,0,this.m.t);while(a[b]>=a.DV){a[b]-=a.DV;a[++b]++}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0}function bnpExp(h,j){if(h>4294967295||h<1){return BigInteger.ONE}var f=nbi(),a=nbi(),d=j.convert(this),c=nbits(h)-1;d.copyTo(f);while(--c>=0){j.sqrTo(f,a);if((h&(1<0){j.mulTo(a,d,f)}else{var b=f;f=a;a=b}}return j.revert(f)}function bnModPowInt(b,a){var c;if(b<256||a.isEven()){c=new Classic(a)}else{c=new Montgomery(a)}return this.exp(b,c)}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;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;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);function bnClone(){var a=nbi();this.copyTo(a);return a}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}}}return((this[1]&((1<<(32-this.DB))-1))<>24}function bnShortValue(){return(this.t==0)?this.s:(this[0]<<16)>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){if(this.s<0){return -1}else{if(this.t<=0||(this.t==1&&this[0]<=0)){return 0}else{return 1}}}function bnpToRadix(c){if(c==null){c=10}if(this.signum()==0||c<2||c>36){return"0"}var f=this.chunkSize(c);var e=Math.pow(c,f);var i=nbv(e),j=nbi(),h=nbi(),g="";this.divRemTo(i,j,h);while(j.signum()>0){g=(e+h.intValue()).toString(c).substr(1)+g;j.divRemTo(i,j,h)}return h.intValue().toString(c)+g}function bnpFromRadix(m,h){this.fromInt(0);if(h==null){h=10}var f=this.chunkSize(h);var g=Math.pow(h,f),e=false,a=0,l=0;for(var c=0;c=f){this.dMultiply(g);this.dAddOffset(l,0);a=0;l=0}}if(a>0){this.dMultiply(Math.pow(h,a));this.dAddOffset(l,0)}if(e){BigInteger.ZERO.subTo(this,this)}}function bnpFromNumber(f,e,h){if("number"==typeof e){if(f<2){this.fromInt(1)}else{this.fromNumber(f,h);if(!this.testBit(f-1)){this.bitwiseTo(BigInteger.ONE.shiftLeft(f-1),op_or,this)}if(this.isEven()){this.dAddOffset(1,0)}while(!this.isProbablePrime(e)){this.dAddOffset(2,0);if(this.bitLength()>f){this.subTo(BigInteger.ONE.shiftLeft(f-1),this)}}}}else{var d=new Array(),g=f&7;d.length=(f>>3)+1;e.nextBytes(d);if(g>0){d[0]&=((1<0){if(e>e)!=(this.s&this.DM)>>e){c[a++]=f|(this.s<<(this.DB-e))}while(b>=0){if(e<8){f=(this[b]&((1<>(e+=this.DB-8)}else{f=(this[b]>>(e-=8))&255;if(e<=0){e+=this.DB;--b}}if((f&128)!=0){f|=-256}if(a==0&&(this.s&128)!=(f&128)){++a}if(a>0||f!=this.s){c[a++]=f}}}return c}function bnEquals(b){return(this.compareTo(b)==0)}function bnMin(b){return(this.compareTo(b)<0)?this:b}function bnMax(b){return(this.compareTo(b)>0)?this:b}function bnpBitwiseTo(c,h,e){var d,g,b=Math.min(c.t,this.t);for(d=0;d>=16;b+=16}if((a&255)==0){a>>=8;b+=8}if((a&15)==0){a>>=4;b+=4}if((a&3)==0){a>>=2;b+=2}if((a&1)==0){++b}return b}function bnGetLowestSetBit(){for(var a=0;a=this.t){return(this.s!=0)}return((this[a]&(1<<(b%this.DB)))!=0)}function bnpChangeBit(c,b){var a=BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a,b,a);return a}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}function bnpAddTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g+=d.s}f.s=(g<0)?-1:0;if(g>0){f[e++]=g}else{if(g<-1){f[e++]=this.DV+g}}f.t=e;f.clamp()}function bnAdd(b){var c=nbi();this.addTo(b,c);return c}function bnSubtract(b){var c=nbi();this.subTo(b,c);return c}function bnMultiply(b){var c=nbi();this.multiplyTo(b,c);return c}function bnDivide(b){var c=nbi();this.divRemTo(b,c,null);return c}function bnRemainder(b){var c=nbi();this.divRemTo(b,null,c);return c}function bnDivideAndRemainder(b){var d=nbi(),c=nbi();this.divRemTo(b,d,c);return new Array(d,c)}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(b,a){while(this.t<=a){this[this.t++]=0}this[a]+=b;while(this[a]>=this.DV){this[a]-=this.DV;if(++a>=this.t){this[this.t++]=0}++this[a]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,c,b){a.multiplyTo(c,b)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp())}function bnpMultiplyLowerTo(b,f,e){var d=Math.min(this.t+b.t,f);e.s=0;e.t=d;while(d>0){e[--d]=0}var c;for(c=e.t-this.t;d=0){d[c]=0}for(c=Math.max(e-this.t,0);c2*this.m.t){return a.mod(this.m)}else{if(a.compareTo(this.m)<0){return a}else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}}function barrettRevert(a){return a}function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1){a.t=this.m.t+1;a.clamp()}this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(a.compareTo(this.r2)<0){a.dAddOffset(1,this.m.t+1)}a.subTo(this.r2,a);while(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Barrett.prototype.convert=barrettConvert;Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;function bnModPow(q,f){var o=q.bitLength(),h,b=nbv(1),v;if(o<=0){return b}else{if(o<18){h=1}else{if(o<48){h=3}else{if(o<144){h=4}else{if(o<768){h=5}else{h=6}}}}}if(o<8){v=new Classic(f)}else{if(f.isEven()){v=new Barrett(f)}else{v=new Montgomery(f)}}var p=new Array(),d=3,s=h-1,a=(1<1){var A=nbi();v.sqrTo(p[1],A);while(d<=a){p[d]=nbi();v.mulTo(A,p[d-2],p[d]);d+=2}}var l=q.t-1,x,u=true,c=nbi(),y;o=nbits(q[l])-1;while(l>=0){if(o>=s){x=(q[l]>>(o-s))&a}else{x=(q[l]&((1<<(o+1))-1))<<(s-o);if(l>0){x|=q[l-1]>>(this.DB+o-s)}}d=h;while((x&1)==0){x>>=1;--d}if((o-=d)<0){o+=this.DB;--l}if(u){p[x].copyTo(b);u=false}else{while(d>1){v.sqrTo(b,c);v.sqrTo(c,b);d-=2}if(d>0){v.sqrTo(b,c)}else{y=b;b=c;c=y}v.mulTo(c,p[x],b)}while(l>=0&&(q[l]&(1<0){b.rShiftTo(f,b);h.rShiftTo(f,h)}while(b.signum()>0){if((d=b.getLowestSetBit())>0){b.rShiftTo(d,b)}if((d=h.getLowestSetBit())>0){h.rShiftTo(d,h)}if(b.compareTo(h)>=0){b.subTo(h,b);b.rShiftTo(1,b)}else{h.subTo(b,h);h.rShiftTo(1,h)}}if(f>0){h.lShiftTo(f,h)}return h}function bnpModInt(e){if(e<=0){return 0}var c=this.DV%e,b=(this.s<0)?e-1:0;if(this.t>0){if(c==0){b=this[0]%e}else{for(var a=this.t-1;a>=0;--a){b=(c*b+this[a])%e}}}return b}function bnModInverse(f){var j=f.isEven();if((this.isEven()&&j)||f.signum()==0){return BigInteger.ZERO}var i=f.clone(),h=this.clone();var g=nbv(1),e=nbv(0),l=nbv(0),k=nbv(1);while(i.signum()!=0){while(i.isEven()){i.rShiftTo(1,i);if(j){if(!g.isEven()||!e.isEven()){g.addTo(this,g);e.subTo(f,e)}g.rShiftTo(1,g)}else{if(!e.isEven()){e.subTo(f,e)}}e.rShiftTo(1,e)}while(h.isEven()){h.rShiftTo(1,h);if(j){if(!l.isEven()||!k.isEven()){l.addTo(this,l);k.subTo(f,k)}l.rShiftTo(1,l)}else{if(!k.isEven()){k.subTo(f,k)}}k.rShiftTo(1,k)}if(i.compareTo(h)>=0){i.subTo(h,i);if(j){g.subTo(l,g)}e.subTo(k,e)}else{h.subTo(i,h);if(j){l.subTo(g,l)}k.subTo(e,k)}}if(h.compareTo(BigInteger.ONE)!=0){return BigInteger.ZERO}if(k.compareTo(f)>=0){return k.subtract(f)}if(k.signum()<0){k.addTo(f,k)}else{return k}if(k.signum()<0){return k.add(f)}else{return k}}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];function bnIsProbablePrime(e){var d,b=this.abs();if(b.t==1&&b[0]<=lowprimes[lowprimes.length-1]){for(d=0;d>1;if(f>lowprimes.length){f=lowprimes.length}var b=nbi();for(var e=0;e>>6)&31)|192);h.push(((c>>>0)&63)|128)}else{if((c&63488)!=55296){h.push(((c>>>12)&15)|224);h.push(((c>>>6)&63)|128);h.push(((c>>>0)&63)|128)}else{if(((c&64512)==55296)&&((a&64512)==56320)){d=((a&63)|((c&63)<<10))+65536;h.push(((d>>>18)&7)|240);h.push(((d>>>12)&63)|128);h.push(((d>>>6)&63)|128);h.push(((d>>>0)&63)|128);e++}else{}}}}if(h.length>3){g.push((h.shift()<<0)|(h.shift()<<8)|(h.shift()<<16)|(h.shift()<<24))}}return g};var isaac=(function(){var d=Array(256),e=0,i=0,c=0,a=Array(256),b=0;f(Math.random()*4294967295);function l(m,p){var o=(m&65535)+(p&65535);var n=(m>>>16)+(p>>>16)+(o>>>16);return(n<<16)|(o&65535)}function h(){e=i=c=0;for(var m=0;m<256;++m){d[m]=a[m]=0}b=0}function f(x){var w,v,u,t,r,p,o,n,m;w=v=u=t=r=p=o=n=2654435769;if(x&&typeof(x)==="string"){x=x.toIntArray()}if(x&&typeof(x)==="number"){x=[x]}if(x instanceof Array){h();for(m=0;m>>2;r=l(r,v);u=l(u,t);u^=t<<8;p=l(p,u);t=l(t,r);t^=r>>>16;o=l(o,t);r=l(r,p);r^=p<<10;n=l(n,r);p=l(p,o);p^=o>>>4;w=l(w,p);o=l(o,n);o^=n<<8;v=l(v,o);n=l(n,w);n^=w>>>9;u=l(u,n);w=l(w,v)}for(m=0;m<4;m++){q()}for(m=0;m<256;m+=8){if(x){w=l(w,a[m+0]);v=l(v,a[m+1]);u=l(u,a[m+2]);t=l(t,a[m+3]);r=l(r,a[m+4]);p=l(p,a[m+5]);o=l(o,a[m+6]);n=l(n,a[m+7])}q();d[m+0]=w;d[m+1]=v;d[m+2]=u;d[m+3]=t;d[m+4]=r;d[m+5]=p;d[m+6]=o;d[m+7]=n}if(x){for(m=0;m<256;m+=8){w=l(w,d[m+0]);v=l(v,d[m+1]);u=l(u,d[m+2]);t=l(t,d[m+3]);r=l(r,d[m+4]);p=l(p,d[m+5]);o=l(o,d[m+6]);n=l(n,d[m+7]);q();d[m+0]=w;d[m+1]=v;d[m+2]=u;d[m+3]=t;d[m+4]=r;d[m+5]=p;d[m+6]=o;d[m+7]=n}}k();b=256}function k(q){var o,m,p;q=(q&&typeof(q)==="number")?Math.abs(Math.floor(q)):1;while(q--){c=l(c,1);i=l(i,c);for(o=0;o<256;o++){switch(o&3){case 0:e^=e<<13;break;case 1:e^=e>>>6;break;case 2:e^=e<<2;break;case 3:e^=e>>>16;break}e=l(d[(o+128)&255],e);m=d[o];d[o]=p=l(d[(m>>>2)&255],l(e,i));a[o]=i=l(d[(p>>>10)&255],m)}}}function j(){if(!b--){k();b=255}return a[b]}function g(){return{a:e,b:i,c:c,m:d,r:a}}return{reset:h,seed:f,prng:k,rand:j,internals:g}})();isaac.random=function(){return 0.5+this.rand()*2.3283064365386963e-10}; 90 | var random16byteHex=(function(){function c(){if(typeof(window)!="undefined"&&window.crypto&&window.crypto.getRandomValues){return true}else{if(typeof(window)!="undefined"&&window.msCrypto&&window.msCrypto.getRandomValues){return true}else{return false}}}var e=c();function b(){if(e){return false}var i=+(new Date())+":"+Math.random();if(typeof(window)!="undefined"&&window.cookie){i+=document.cookie}var g=CryptoJS.SHA256||CryptoJS.SHA1;isaac.seed(g(i));return true}var a=b();function d(){var j=4;var n;if(e){var m=window.crypto||window.msCrypto;n=new Int32Array(j);m.getRandomValues(n)}else{var h=+(new Date());var l=h%50;isaac.prng(1+l);n=new Array();for(var k=0;k 154 | * 155 | * Computes x = H(s | H(I | ":" | P)) 156 | *

Uses string concatenation before hashing. 157 | *

Specification RFC 2945 158 | * 159 | * @param salt The salt 's'. Must not be null or empty. 160 | * @param identity The user identity/email 'I'. Must not be null or empty. 161 | * @param password The user password 'P'. Must not be null or empty 162 | * @return The resulting 'x' value as BigInteger. 163 | */ 164 | this.generateX = function(salt, identity, password) { 165 | this.check(salt, "salt"); 166 | this.check(identity, "identity"); 167 | this.check(password, "password"); 168 | //console.log("js salt:"+salt); 169 | //console.log("js i:"+identity); 170 | //console.log("js p:"+password); 171 | this.salt = salt; 172 | var hash1 = this.H(identity+':'+password); 173 | 174 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 175 | while (hash1.substring(0, 1) === '0') { 176 | //console.log("stripping leading zero from M1"); 177 | hash1 = hash1.substring(1); 178 | } 179 | 180 | //console.log("js hash1:"+hash1); 181 | //console.log("js salt:"+salt); 182 | var concat = (salt+hash1).toUpperCase(); 183 | //console.log("js concat:"+concat); 184 | var hash = this.H(concat); 185 | 186 | // Java BigInteger math will trim leading zeros so we do likewise 187 | while (hash.substring(0, 1) === '0') { 188 | //console.log("stripping leading zero from M1"); 189 | hash = hash.substring(1); 190 | } 191 | 192 | //console.log("js hash:"+hash) 193 | //console.log("js x before modN "+this.fromHex(hash)); 194 | this.x = this.fromHex(hash).mod(this.N()); 195 | return this.x; 196 | }; 197 | 198 | /** 199 | * Computes the session key S = (B - k * g^x) ^ (a + u * x) (mod N) 200 | * from client-side parameters. 201 | * 202 | *

Specification: RFC 5054 203 | * 204 | * @param N The prime parameter 'N'. Must not be {@code null}. 205 | * @param g The generator parameter 'g'. Must not be {@code null}. 206 | * @param k The SRP-6a multiplier 'k'. Must not be {@code null}. 207 | * @param x The 'x' value, see {@link #computeX}. Must not be 208 | * {@code null}. 209 | * @param u The random scrambling parameter 'u'. Must not be 210 | * {@code null}. 211 | * @param a The private client value 'a'. Must not be {@code null}. 212 | * @param B The public server value 'B'. Must note be {@code null}. 213 | * 214 | * @return The resulting session key 'S'. 215 | */ 216 | this.computeSessionKey = function(k, x, u, a, B) { 217 | this.check(k, "k"); 218 | this.check(x, "x"); 219 | this.check(u, "u"); 220 | this.check(a, "a"); 221 | this.check(B, "B"); 222 | 223 | var exp = u.multiply(x).add(a); 224 | var tmp = this.g().modPow(x, this.N()).multiply(k); 225 | return B.subtract(tmp).modPow(exp, this.N()); 226 | }; 227 | } 228 | 229 | // public helper 230 | SRP6JavascriptClientSession.prototype.toHex = function(n) { 231 | "use strict"; 232 | return n.toString(16); 233 | }; 234 | 235 | // public helper 236 | /* jshint ignore:start */ 237 | SRP6JavascriptClientSession.prototype.fromHex = function(s) { 238 | "use strict"; 239 | return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat 240 | }; 241 | /* jshint ignore:end */ 242 | 243 | // public helper to hide BigInteger from the linter 244 | /* jshint ignore:start */ 245 | SRP6JavascriptClientSession.prototype.BigInteger = function(string, radix) { 246 | "use strict"; 247 | return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat 248 | }; 249 | /* jshint ignore:end */ 250 | 251 | 252 | // public getter of the current workflow state. 253 | SRP6JavascriptClientSession.prototype.getState = function() { 254 | "use strict"; 255 | return this.state; 256 | }; 257 | 258 | /** 259 | * Gets the shared sessionkey 260 | * 261 | * @param hash Boolean With to return the large session key 'S' or 'K=H(S)' 262 | */ 263 | SRP6JavascriptClientSession.prototype.getSessionKey = function(hash) { 264 | "use strict"; 265 | if( this.S === null ) { 266 | return null; 267 | } 268 | this.SS = this.toHex(this.S); 269 | if(typeof hash !== 'undefined' && hash === false){ 270 | return this.SS; 271 | } else { 272 | if( this.K === null ) { 273 | this.K = this.H(this.SS); 274 | } 275 | return this.K; 276 | } 277 | }; 278 | 279 | // public getter 280 | SRP6JavascriptClientSession.prototype.getUserID = function() { 281 | "use strict"; 282 | return this.I; 283 | }; 284 | 285 | /* 286 | * Generates a new salt 's'. This takes the current time, a pure browser random value, and an optional server generated random, and hashes them all together. 287 | * This should ensure that the salt is unique to every use registration regardless of the quality of the browser random generation routine. 288 | * Note that this method is optional as you can choose to always generate the salt at the server and sent it to the browser as it is a public value. 289 | *

290 | * Always add a unique constraint to where you store this in your database to force that all users on the system have a unique salt. 291 | * 292 | * @param opionalServerSalt An optional server salt which is hashed into a locally generated random number. Can be left undefined when calling this function. 293 | * @return 's' Salt as a hex string of length driven by the bit size of the hash algorithm 'H'. 294 | */ 295 | SRP6JavascriptClientSession.prototype.generateRandomSalt = function(opionalServerSalt) { 296 | "use strict"; 297 | var s = null; 298 | 299 | /* jshint ignore:start */ 300 | s = random16byteHex.random(); 301 | /* jshint ignore:end */ 302 | 303 | // if you invoke without passing the string parameter the '+' operator uses 'undefined' so no nullpointer risk here 304 | var ss = this.H((new Date())+':'+opionalServerSalt+':'+s); 305 | return ss; 306 | }; 307 | 308 | /* 309 | * Generates a new verifier 'v' from the specified parameters. 310 | *

The verifier is computed as v = g^x (mod N). 311 | *

Specification RFC 2945 312 | * 313 | * @param salt The salt 's'. Must not be null or empty. 314 | * @param identity The user identity/email 'I'. Must not be null or empty. 315 | * @param password The user password 'P'. Must not be null or empty 316 | * @return The resulting verifier 'v' as a hex string 317 | */ 318 | SRP6JavascriptClientSession.prototype.generateVerifier = function(salt, identity, password) { 319 | "use strict"; 320 | //console.log("SRP6JavascriptClientSession.prototype.generateVerifier"); 321 | // no need to check the parameters as generateX will do this 322 | var x = this.generateX(salt, identity, password); 323 | //console.log("js x: "+this.toHex(x)); 324 | this.v = this.g().modPow(x, this.N()); 325 | //console.log("js v: "+this.toHex(this.v)); 326 | return this.toHex(this.v); 327 | }; 328 | 329 | /** 330 | * Records the identity 'I' and password 'P' of the authenticating user. 331 | * The session is incremented to {@link State#STEP_1}. 332 | *

Argument origin: 333 | *

    334 | *
  • From user: user identity 'I' and password 'P'. 335 | *
336 | * @param userID The identity 'I' of the authenticating user, UTF-8 337 | * encoded. Must not be {@code null} or empty. 338 | * @param password The user password 'P', UTF-8 encoded. Must not be 339 | * {@code null}. 340 | * @throws IllegalStateException If the method is invoked in a state 341 | * other than {@link State#INIT}. 342 | */ 343 | SRP6JavascriptClientSession.prototype.step1 = function(identity, password) { 344 | "use strict"; 345 | //console.log("SRP6JavascriptClientSession.prototype.step1"); 346 | //console.log("N: "+this.N()); 347 | //console.log("g: "+this.g()); 348 | //console.log("k: "+this.toHex(this.k)); 349 | this.check(identity, "identity"); 350 | this.check(password, "password"); 351 | this.I = identity; 352 | this.P = password; 353 | if( this.state !== this.INIT ) { 354 | throw new Error("IllegalStateException not in state INIT"); 355 | } 356 | this.state = this.STEP_1; 357 | }; 358 | 359 | /** 360 | * Computes the random scrambling parameter u = H(A | B) 361 | *

Specification RFC 2945 362 | * Will throw an error if 363 | * 364 | * @param A The public client value 'A'. Must not be {@code null}. 365 | * @param B The public server value 'B'. Must not be {@code null}. 366 | * 367 | * @return The resulting 'u' value. 368 | */ 369 | SRP6JavascriptClientSession.prototype.computeU = function(Astr, Bstr) { 370 | "use strict"; 371 | //console.log("SRP6JavascriptClientSession.prototype.computeU"); 372 | this.check(Astr, "Astr"); 373 | this.check(Bstr, "Bstr"); 374 | /* jshint ignore:start */ 375 | var output = this.H(Astr+Bstr); 376 | //console.log("js raw u:"+output); 377 | var u = new BigInteger(""+output,16); 378 | //console.log("js u:"+this.toHex(u)); 379 | if( BigInteger.ZERO.equals(u) ) { 380 | throw new Error("SRP6Exception bad shared public value 'u' as u==0"); 381 | } 382 | return u; 383 | /* jshint ignore:end */ 384 | }; 385 | 386 | SRP6JavascriptClientSession.prototype.random16byteHex = function() { 387 | "use strict"; 388 | 389 | var r1 = null; 390 | /* jshint ignore:start */ 391 | r1 = random16byteHex.random(); 392 | /* jshint ignore:end */ 393 | return r1; 394 | }; 395 | 396 | /** 397 | * Generate a random value in the range `[1,N)` using a minimum of 256 random bits. 398 | * 399 | * See specification RFC 5054. 400 | * This method users the best random numbers available. Just in case the random number 401 | * generate in the client web browser is totally buggy it also adds `H(I+":"+salt+":"+time())` 402 | * to the generated random number. 403 | * @param N The safe prime. 404 | */ 405 | SRP6JavascriptClientSession.prototype.randomA = function(N) { 406 | "use strict"; 407 | 408 | //console.log("N:"+N); 409 | 410 | // our ideal number of random bits to use for `a` as long as its bigger than 256 bits 411 | var hexLength = this.toHex(N).length; 412 | 413 | var ZERO = this.BigInteger("0", 10); 414 | var ONE = this.BigInteger("1", 10); 415 | 416 | var r = ZERO; 417 | 418 | // loop until we don't have a ZERO value. we would have to generate exactly N to loop so very rare. 419 | while(ZERO.equals(r)){ 420 | // in theory we get 256 bits of good random numbers here 421 | var rstr = this.random16byteHex() + this.random16byteHex(); 422 | 423 | //console.log("rstr:"+rstr); 424 | 425 | // add more random bytes until we are at least as large as N and ignore any overshoot 426 | while( rstr.length < hexLength ) { 427 | rstr = rstr + this.random16byteHex(); 428 | } 429 | 430 | //console.log("rstr:"+rstr); 431 | 432 | // we now have a random just at lest 256 bits but typically more bits than N for large N 433 | var rBi = this.BigInteger(rstr, 16); 434 | 435 | //console.log("rBi:"+rBi); 436 | 437 | // this hashes the time in ms such that we wont get repeated numbers for successive attempts 438 | // it also hashes the salt which can itself be salted by a server strong random which protects 439 | // against rainbow tables. it also hashes the user identity which is unique to each user 440 | // to protect against having simply no good random numbers anywhere 441 | var oneTimeBi = this.BigInteger(this.H(this.I+":"+this.salt+':'+(new Date()).getTime()), 16); 442 | 443 | //console.log("oneTimeBi:"+oneTimeBi); 444 | 445 | // here we add the "one time" hashed time number to our random number to the random number 446 | // this protected against a buggy browser random number generated generating a constant value 447 | // we mod(N) to wrap to the range [0,N) then loop if we get 0 to give [1,N) 448 | // mod(N) is broken due to buggy library code so we workaround with modPow(1,N) 449 | r = (oneTimeBi.add(rBi)).modPow(ONE, N); 450 | } 451 | 452 | //console.log("r:"+r); 453 | 454 | // the result will in the range [1,N) using more random bits than size N 455 | return r; 456 | }; 457 | 458 | /** 459 | * Receives the password salt 's' and public value 'B' from the server. 460 | * The SRP-6a crypto parameters are also set. The session is incremented 461 | * to {@link State#STEP_2}. 462 | *

Argument origin: 463 | *

    464 | *
  • From server: password salt 's', public value 'B'. 465 | *
  • Pre-agreed: crypto parameters prime 'N', 466 | * generator 'g' and hash function 'H'. 467 | *
468 | * @param s The password salt 's' as a hex string. Must not be {@code null}. 469 | * @param B The public server value 'B' as a hex string. Must not be {@code null}. 470 | * @param k k is H(N,g) with padding by the server. Must not be {@code null}. 471 | * @return The client credentials consisting of the client public key 472 | * 'A' and the client evidence message 'M1'. 473 | * @throws IllegalStateException If the method is invoked in a state 474 | * other than {@link State#STEP_1}. 475 | * @throws SRP6Exception If the public server value 'B' is invalid. 476 | */ 477 | SRP6JavascriptClientSession.prototype.step2 = function(s, BB) { 478 | "use strict"; 479 | 480 | //console.log("SRP6JavascriptClientSession.prototype.step2"); 481 | 482 | this.check(s, "s"); 483 | //console.log("s:" + s); 484 | this.check(BB, "BB"); 485 | //console.log("BB:" + BB); 486 | 487 | if( this.state !== this.STEP_1 ) { 488 | throw new Error("IllegalStateException not in state STEP_1"); 489 | } 490 | 491 | // this is checked when passed to computeSessionKey 492 | this.B = this.fromHex(BB); 493 | 494 | var ZERO = null; 495 | 496 | /* jshint ignore:start */ 497 | ZERO = BigInteger.ZERO; 498 | /* jshint ignore:end */ 499 | 500 | if (this.B.mod(this.N()).equals(ZERO)) { 501 | throw new Error("SRP6Exception bad server public value 'B' as B == 0 (mod N)"); 502 | } 503 | 504 | //console.log("k:" + this.k); 505 | 506 | // this is checked when passed to computeSessionKey 507 | var x = this.generateX(s, this.I, this.P); 508 | //console.log("x:" + x); 509 | 510 | // blank the password as there is no reason to keep it around in memory. 511 | this.P = null; 512 | 513 | //console.log("N:"+this.toHex(this.N).toString(16)); 514 | 515 | this.a = this.randomA(this.N); 516 | 517 | //console.log("a:" + this.toHex(this.a)); 518 | 519 | this.A = this.g().modPow(this.a, this.N()); 520 | //console.log("A:" + this.toHex(this.A)); 521 | this.check(this.A, "A"); 522 | 523 | this.u = this.computeU(this.A.toString(16),BB); 524 | //console.log("u:" + this.u); 525 | 526 | this.S = this.computeSessionKey(this.k, x, this.u, this.a, this.B); 527 | this.check(this.S, "S"); 528 | 529 | //console.log("jsU:" + this.toHex(this.u)); 530 | //console.log("jsS:" + this.toHex(this.S)); 531 | 532 | var AA = this.toHex(this.A); 533 | 534 | // console.log("cAA:"+AA); 535 | // console.log("cBB:"+BB); 536 | // console.log("cSS:"+this.toHex(this.S)); 537 | 538 | this.M1str = this.H(AA+BB+this.toHex(this.S)); 539 | this.check(this.M1str, "M1str"); 540 | 541 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 542 | while (this.M1str.substring(0, 1) === '0') { 543 | //console.log("stripping leading zero from M1"); 544 | this.M1str = this.M1str.substring(1); 545 | } 546 | 547 | //console.log("M1str:" + this.M1str); 548 | 549 | //console.log("js ABS:" + AA+BB+this.toHex(this.S)); 550 | //console.log("js A:" + AA); 551 | //console.log("js B:" + BB); 552 | //console.log("js v:" + this.v); 553 | //console.log("js u:" + this.u); 554 | //console.log("js A:" + this.A); 555 | //console.log("js b:" + this.B); 556 | //console.log("js S:" + this.S); 557 | //console.log("js S:" + this.toHex(this.S)); 558 | //console.log("js M1:" + this.M1str); 559 | 560 | this.state = this.STEP_2; 561 | return { A: AA, M1: this.M1str }; 562 | }; 563 | 564 | /** 565 | * Receives the server evidence message 'M1'. The session is incremented 566 | * to {@link State#STEP_3}. 567 | * 568 | *

Argument origin: 569 | *

    570 | *
  • From server: evidence message 'M2'. 571 | *
572 | * @param serverM2 The server evidence message 'M2' as string. Must not be {@code null}. 573 | * @throws IllegalStateException If the method is invoked in a state 574 | * other than {@link State#STEP_2}. 575 | * @throws SRP6Exception If the session has timed out or the 576 | * server evidence message 'M2' is 577 | * invalid. 578 | */ 579 | SRP6JavascriptClientSession.prototype.step3 = function(M2) { 580 | "use strict"; 581 | this.check(M2); 582 | //console.log("SRP6JavascriptClientSession.prototype.step3"); 583 | 584 | // Check current state 585 | if (this.state !== this.STEP_2) 586 | throw new Error("IllegalStateException State violation: Session must be in STEP_2 state"); 587 | 588 | //console.log("js A:" + this.toHex(this.A)); 589 | //console.log("jsM1:" + this.M1str); 590 | //console.log("js S:" + this.toHex(this.S)); 591 | 592 | var computedM2 = this.H(this.toHex(this.A)+this.M1str+this.toHex(this.S)); 593 | 594 | //console.log("jsServerM2:" + M2); 595 | //console.log("jsClientM2:" + computedM2); 596 | 597 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 598 | while (computedM2.substring(0, 1) === '0') { 599 | //console.log("stripping leading zero from computedM2"); 600 | computedM2 = computedM2.substring(1); 601 | } 602 | 603 | //console.log("server M2:"+M2+"\ncomputedM2:"+computedM2); 604 | if ( ""+computedM2 !== ""+M2) { 605 | throw new Error("SRP6Exception Bad server credentials"); 606 | } 607 | 608 | this.state = this.STEP_3; 609 | 610 | return true; 611 | }; 612 | 613 | /** 614 | * For the node.js world we optionally export a factory closure which takes SRP parameters and returns a SRP6JavascriptClientSession class using SHA256 and the parameters bound to it. 615 | * 616 | * @param {string} N_base10 Safe prime N as decimal string. 617 | * @param {string} g_base10 Generator g as decimal string. 618 | * @param {string} k_base16 Symmetry braking k as hexidecimal string. See https://bitbucket.org/simon_massey/thinbus-srp-js/overview 619 | */ 620 | /* jshint ignore:start */ 621 | if( typeof module !== 'undefined') 622 | if( typeof module.exports !== 'undefined' ) 623 | module.exports = function srpClientFactory (N_base10, g_base10, k_base16) { 624 | 625 | function SRP6JavascriptClientSessionSHA256(){ } 626 | 627 | SRP6JavascriptClientSessionSHA256.prototype = new SRP6JavascriptClientSession(); 628 | 629 | SRP6JavascriptClientSessionSHA256.prototype.N = function() { 630 | return new BigInteger(N_base10, 10); 631 | } 632 | 633 | SRP6JavascriptClientSessionSHA256.prototype.g = function() { 634 | return new BigInteger(g_base10, 10); 635 | } 636 | 637 | SRP6JavascriptClientSessionSHA256.prototype.H = function (x) { 638 | return CryptoJS.SHA256(x).toString().toLowerCase(); 639 | } 640 | 641 | SRP6JavascriptClientSessionSHA256.prototype.k = new BigInteger(k_base16, 16); 642 | 643 | // return the new session class 644 | return SRP6JavascriptClientSessionSHA256; 645 | } 646 | /* jshint ignore:end */ 647 | 648 | 649 | },{}]},{},[])("/thinbus-original-client.js") 650 | }); -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Thinbus Javascript Secure Remote Password (SRP) 3 | * Version 1.7.5 4 | * Copyright 2014-2017 Simon Massey 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | const SHA256 = require("crypto-js/sha256"); 8 | 9 | const BigInteger = require('jsbn').BigInteger; 10 | 11 | var randomStrings = require('random-strings'); 12 | 13 | /** 14 | * A factory closure which takes SRP parameters and returns a SRP6JavascriptClientSession class with the parameters bound to it. 15 | * 16 | * @param {string} N_base10 Safe prime N as decimal string. 17 | * @param {string} g_base10 Generator g as decimal string. 18 | * @param {string} k_base16 Symmetry braking k as hexidecimal string. See https://bitbucket.org/simon_massey/thinbus-srp-js/overview 19 | */ 20 | function srpClientFactory (N_base10, g_base10, k_base16) { 21 | 22 | 23 | function SRP6JavascriptClientSession() { 24 | "use strict"; 25 | 26 | /** 27 | * The session is initialised and ready to begin authentication 28 | * by proceeding to {@link #STEP_1}. 29 | */ 30 | this.INIT = 0; 31 | 32 | /** 33 | * The authenticating user has input their identity 'I' 34 | * (username) and password 'P'. The session is ready to proceed 35 | * to {@link #STEP_2}. 36 | */ 37 | this.STEP_1 = 1; 38 | 39 | /** 40 | * The user identity 'I' is submitted to the server which has 41 | * replied with the matching salt 's' and its public value 'B' 42 | * based on the user's password verifier 'v'. The session is 43 | * ready to proceed to {@link #STEP_3}. 44 | */ 45 | this.STEP_2 = 2; 46 | 47 | /** 48 | * The client public key 'A' and evidence message 'M1' are 49 | * submitted and the server has replied with own evidence 50 | * message 'M2'. The session is finished (authentication was 51 | * successful or failed). 52 | */ 53 | this.STEP_3 = 3; 54 | 55 | this.state = this.INIT; 56 | 57 | this.x = null; // salted hashed password 58 | this.v = null; // verifier 59 | this.I = null; // identity 60 | this.P = null; // password, nulled after use 61 | this.salt = null; // salt 62 | this.B = null; // server public key 63 | this.A = null; // client public key 64 | this.a = null; // client private key 65 | this.k = null; // constant computed by the server 66 | this.u = null; // blended public keys 67 | this.S = null; // shared secret key long form 68 | this.K = null; // shared secret hashed form 69 | this.M1str = null; // password proof 70 | 71 | // private 72 | this.check = function(v, name) { 73 | if( typeof v === 'undefined' || v === null || v === "" || v === "0" ) { 74 | throw new Error(name+" must not be null, empty or zero"); 75 | } 76 | }; 77 | 78 | /** private

79 | * 80 | * Computes x = H(s | H(I | ":" | P)) 81 | *

Uses string concatenation before hashing. 82 | *

Specification RFC 2945 83 | * 84 | * @param salt The salt 's'. Must not be null or empty. 85 | * @param identity The user identity/email 'I'. Must not be null or empty. 86 | * @param password The user password 'P'. Must not be null or empty 87 | * @return The resulting 'x' value as BigInteger. 88 | */ 89 | this.generateX = function(salt, identity, password) { 90 | this.check(salt, "salt"); 91 | this.check(identity, "identity"); 92 | this.check(password, "password"); 93 | //console.log("js salt:"+salt); 94 | //console.log("js i:"+identity); 95 | //console.log("js p:"+password); 96 | this.salt = salt; 97 | var hash1 = this.H(identity+':'+password); 98 | 99 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 100 | while (hash1.substring(0, 1) === '0') { 101 | //console.log("stripping leading zero from M1"); 102 | hash1 = hash1.substring(1); 103 | } 104 | 105 | //console.log("js hash1:"+hash1); 106 | //console.log("js salt:"+salt); 107 | var concat = (salt+hash1).toUpperCase(); 108 | //console.log("js concat:"+concat); 109 | var hash = this.H(concat); 110 | 111 | // Java BigInteger math will trim leading zeros so we do likewise 112 | while (hash.substring(0, 1) === '0') { 113 | //console.log("stripping leading zero from M1"); 114 | hash = hash.substring(1); 115 | } 116 | 117 | //console.log("js hash:"+hash) 118 | //console.log("js x before modN "+this.fromHex(hash)); 119 | this.x = this.fromHex(hash).mod(this.N()); 120 | return this.x; 121 | }; 122 | 123 | /** 124 | * Computes the session key S = (B - k * g^x) ^ (a + u * x) (mod N) 125 | * from client-side parameters. 126 | * 127 | *

Specification: RFC 5054 128 | * 129 | * @param N The prime parameter 'N'. Must not be {@code null}. 130 | * @param g The generator parameter 'g'. Must not be {@code null}. 131 | * @param k The SRP-6a multiplier 'k'. Must not be {@code null}. 132 | * @param x The 'x' value, see {@link #computeX}. Must not be 133 | * {@code null}. 134 | * @param u The random scrambling parameter 'u'. Must not be 135 | * {@code null}. 136 | * @param a The private client value 'a'. Must not be {@code null}. 137 | * @param B The public server value 'B'. Must note be {@code null}. 138 | * 139 | * @return The resulting session key 'S'. 140 | */ 141 | this.computeSessionKey = function(k, x, u, a, B) { 142 | this.check(k, "k"); 143 | this.check(x, "x"); 144 | this.check(u, "u"); 145 | this.check(a, "a"); 146 | this.check(B, "B"); 147 | 148 | var exp = u.multiply(x).add(a); 149 | var tmp = this.g().modPow(x, this.N()).multiply(k); 150 | return B.subtract(tmp).modPow(exp, this.N()); 151 | }; 152 | } 153 | 154 | // public helper 155 | SRP6JavascriptClientSession.prototype.toHex = function(n) { 156 | "use strict"; 157 | return n.toString(16); 158 | }; 159 | 160 | // public helper 161 | /* jshint ignore:start */ 162 | SRP6JavascriptClientSession.prototype.fromHex = function(s) { 163 | "use strict"; 164 | return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat 165 | }; 166 | /* jshint ignore:end */ 167 | 168 | // public helper to hide BigInteger from the linter 169 | /* jshint ignore:start */ 170 | SRP6JavascriptClientSession.prototype.BigInteger = function(string, radix) { 171 | "use strict"; 172 | return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat 173 | }; 174 | /* jshint ignore:end */ 175 | 176 | 177 | // public getter of the current workflow state. 178 | SRP6JavascriptClientSession.prototype.getState = function() { 179 | "use strict"; 180 | return this.state; 181 | }; 182 | 183 | /** 184 | * Gets the shared sessionkey 185 | * 186 | * @param hash Boolean With to return the large session key 'S' or 'K=H(S)' 187 | */ 188 | SRP6JavascriptClientSession.prototype.getSessionKey = function(hash) { 189 | "use strict"; 190 | if( this.S === null ) { 191 | return null; 192 | } 193 | this.SS = this.toHex(this.S); 194 | if(typeof hash !== 'undefined' && hash === false){ 195 | return this.SS; 196 | } else { 197 | if( this.K === null ) { 198 | this.K = this.H(this.SS); 199 | } 200 | return this.K; 201 | } 202 | }; 203 | 204 | // public getter 205 | SRP6JavascriptClientSession.prototype.getUserID = function() { 206 | "use strict"; 207 | return this.I; 208 | }; 209 | 210 | /* 211 | * Generates a new salt 's'. This takes the current time, a pure browser random value, and an optional server generated random, and hashes them all together. 212 | * This should ensure that the salt is unique to every use registration regardless of the quality of the browser random generation routine. 213 | * Note that this method is optional as you can choose to always generate the salt at the server and sent it to the browser as it is a public value. 214 | *

215 | * Always add a unique constraint to where you store this in your database to force that all users on the system have a unique salt. 216 | * 217 | * @param opionalServerSalt An optional server salt which is hashed into a locally generated random number. Can be left undefined when calling this function. 218 | * @return 's' Salt as a hex string of length driven by the bit size of the hash algorithm 'H'. 219 | */ 220 | SRP6JavascriptClientSession.prototype.generateRandomSalt = function(opionalServerSalt) { 221 | "use strict"; 222 | var s = null; 223 | 224 | /* jshint ignore:start */ 225 | s = randomStrings.hex(32); // 16 bytes 226 | /* jshint ignore:end */ 227 | 228 | // if you invoke without passing the string parameter the '+' operator uses 'undefined' so no nullpointer risk here 229 | var ss = this.H((new Date())+':'+opionalServerSalt+':'+s); 230 | return ss; 231 | }; 232 | 233 | /* 234 | * Generates a new verifier 'v' from the specified parameters. 235 | *

The verifier is computed as v = g^x (mod N). 236 | *

Specification RFC 2945 237 | * 238 | * @param salt The salt 's'. Must not be null or empty. 239 | * @param identity The user identity/email 'I'. Must not be null or empty. 240 | * @param password The user password 'P'. Must not be null or empty 241 | * @return The resulting verifier 'v' as a hex string 242 | */ 243 | SRP6JavascriptClientSession.prototype.generateVerifier = function(salt, identity, password) { 244 | "use strict"; 245 | //console.log("SRP6JavascriptClientSession.prototype.generateVerifier"); 246 | // no need to check the parameters as generateX will do this 247 | var x = this.generateX(salt, identity, password); 248 | //console.log("js x: "+this.toHex(x)); 249 | this.v = this.g().modPow(x, this.N()); 250 | //console.log("js v: "+this.toHex(this.v)); 251 | return this.toHex(this.v); 252 | }; 253 | 254 | /** 255 | * Records the identity 'I' and password 'P' of the authenticating user. 256 | * The session is incremented to {@link State#STEP_1}. 257 | *

Argument origin: 258 | *

    259 | *
  • From user: user identity 'I' and password 'P'. 260 | *
261 | * @param userID The identity 'I' of the authenticating user, UTF-8 262 | * encoded. Must not be {@code null} or empty. 263 | * @param password The user password 'P', UTF-8 encoded. Must not be 264 | * {@code null}. 265 | * @throws IllegalStateException If the method is invoked in a state 266 | * other than {@link State#INIT}. 267 | */ 268 | SRP6JavascriptClientSession.prototype.step1 = function(identity, password) { 269 | "use strict"; 270 | //console.log("SRP6JavascriptClientSession.prototype.step1"); 271 | //console.log("N: "+this.N()); 272 | //console.log("g: "+this.g()); 273 | //console.log("k: "+this.toHex(this.k)); 274 | this.check(identity, "identity"); 275 | this.check(password, "password"); 276 | this.I = identity; 277 | this.P = password; 278 | if( this.state !== this.INIT ) { 279 | throw new Error("IllegalStateException not in state INIT"); 280 | } 281 | this.state = this.STEP_1; 282 | }; 283 | 284 | /** 285 | * Computes the random scrambling parameter u = H(A | B) 286 | *

Specification RFC 2945 287 | * Will throw an error if 288 | * 289 | * @param A The public client value 'A'. Must not be {@code null}. 290 | * @param B The public server value 'B'. Must not be {@code null}. 291 | * 292 | * @return The resulting 'u' value. 293 | */ 294 | SRP6JavascriptClientSession.prototype.computeU = function(Astr, Bstr) { 295 | "use strict"; 296 | //console.log("SRP6JavascriptClientSession.prototype.computeU"); 297 | this.check(Astr, "Astr"); 298 | this.check(Bstr, "Bstr"); 299 | /* jshint ignore:start */ 300 | var output = this.H(Astr+Bstr); 301 | //console.log("js raw u:"+output); 302 | var u = new BigInteger(""+output,16); 303 | //console.log("js u:"+this.toHex(u)); 304 | if( BigInteger.ZERO.equals(u) ) { 305 | throw new Error("SRP6Exception bad shared public value 'u' as u==0"); 306 | } 307 | return u; 308 | /* jshint ignore:end */ 309 | }; 310 | 311 | SRP6JavascriptClientSession.prototype.random16byteHex = function() { 312 | "use strict"; 313 | 314 | var r1 = null; 315 | /* jshint ignore:start */ 316 | r1 = random16byteHex.random(); 317 | /* jshint ignore:end */ 318 | return r1; 319 | }; 320 | 321 | /** 322 | * Generate a random value in the range `[1,N)` using a minimum of 256 random bits. 323 | * 324 | * See specification RFC 5054. 325 | * This method users the best random numbers available. Just in case the random number 326 | * generate in the client web browser is totally buggy it also adds `H(I+":"+salt+":"+time())` 327 | * to the generated random number. 328 | * @param N The safe prime. 329 | */ 330 | SRP6JavascriptClientSession.prototype.randomA = function(N) { 331 | "use strict"; 332 | 333 | //console.log("N:"+N); 334 | 335 | // our ideal number of random bits to use for `a` as long as its bigger than 256 bits 336 | var hexLength = this.toHex(N).length; 337 | 338 | var ZERO = this.BigInteger("0", 10); 339 | var ONE = this.BigInteger("1", 10); 340 | 341 | var r = ZERO; 342 | 343 | // loop until we don't have a ZERO value. we would have to generate exactly N to loop so very rare. 344 | while(ZERO.equals(r)){ 345 | var rstr = randomStrings.hex(hexLength); 346 | 347 | //console.log("rstr:"+rstr); 348 | 349 | // we now have a random just at lest 256 bits but typically more bits than N for large N 350 | var rBi = this.BigInteger(rstr, 16); 351 | 352 | //console.log("rBi:"+rBi); 353 | 354 | // this hashes the time in ms such that we wont get repeated numbers for successive attempts 355 | // it also hashes the salt which can itself be salted by a server strong random which protects 356 | // against rainbow tables. it also hashes the user identity which is unique to each user 357 | // to protect against having simply no good random numbers anywhere 358 | var oneTimeBi = this.BigInteger(this.H(this.I+":"+this.salt+':'+(new Date()).getTime()), 16); 359 | 360 | //console.log("oneTimeBi:"+oneTimeBi); 361 | 362 | // here we add the "one time" hashed time number to our random number to the random number 363 | // this protected against a buggy browser random number generated generating a constant value 364 | // we mod(N) to wrap to the range [0,N) then loop if we get 0 to give [1,N) 365 | // mod(N) is broken due to buggy library code so we workaround with modPow(1,N) 366 | r = (oneTimeBi.add(rBi)).modPow(ONE, N); 367 | } 368 | 369 | //console.log("r:"+r); 370 | 371 | // the result will in the range [1,N) using more random bits than size N 372 | return r; 373 | }; 374 | 375 | /** 376 | * Receives the password salt 's' and public value 'B' from the server. 377 | * The SRP-6a crypto parameters are also set. The session is incremented 378 | * to {@link State#STEP_2}. 379 | *

Argument origin: 380 | *

    381 | *
  • From server: password salt 's', public value 'B'. 382 | *
  • Pre-agreed: crypto parameters prime 'N', 383 | * generator 'g' and hash function 'H'. 384 | *
385 | * @param s The password salt 's' as a hex string. Must not be {@code null}. 386 | * @param B The public server value 'B' as a hex string. Must not be {@code null}. 387 | * @param k k is H(N,g) with padding by the server. Must not be {@code null}. 388 | * @return The client credentials consisting of the client public key 389 | * 'A' and the client evidence message 'M1'. 390 | * @throws IllegalStateException If the method is invoked in a state 391 | * other than {@link State#STEP_1}. 392 | * @throws SRP6Exception If the public server value 'B' is invalid. 393 | */ 394 | SRP6JavascriptClientSession.prototype.step2 = function(s, BB) { 395 | "use strict"; 396 | 397 | //console.log("SRP6JavascriptClientSession.prototype.step2"); 398 | 399 | this.check(s, "s"); 400 | //console.log("s:" + s); 401 | this.check(BB, "BB"); 402 | //console.log("BB:" + BB); 403 | 404 | if( this.state !== this.STEP_1 ) { 405 | throw new Error("IllegalStateException not in state STEP_1"); 406 | } 407 | 408 | // this is checked when passed to computeSessionKey 409 | this.B = this.fromHex(BB); 410 | 411 | var ZERO = null; 412 | 413 | /* jshint ignore:start */ 414 | ZERO = BigInteger.ZERO; 415 | /* jshint ignore:end */ 416 | 417 | if (this.B.mod(this.N()).equals(ZERO)) { 418 | throw new Error("SRP6Exception bad server public value 'B' as B == 0 (mod N)"); 419 | } 420 | 421 | //console.log("k:" + this.k); 422 | 423 | // this is checked when passed to computeSessionKey 424 | var x = this.generateX(s, this.I, this.P); 425 | //console.log("x:" + x); 426 | 427 | // blank the password as there is no reason to keep it around in memory. 428 | this.P = null; 429 | 430 | //console.log("N:"+this.toHex(this.N).toString(16)); 431 | 432 | this.a = this.randomA(this.N); 433 | 434 | //console.log("a:" + this.toHex(this.a)); 435 | 436 | this.A = this.g().modPow(this.a, this.N()); 437 | //console.log("A:" + this.toHex(this.A)); 438 | this.check(this.A, "A"); 439 | 440 | this.u = this.computeU(this.A.toString(16),BB); 441 | //console.log("u:" + this.u); 442 | 443 | this.S = this.computeSessionKey(this.k, x, this.u, this.a, this.B); 444 | this.check(this.S, "S"); 445 | 446 | //console.log("jsU:" + this.toHex(this.u)); 447 | //console.log("jsS:" + this.toHex(this.S)); 448 | 449 | var AA = this.toHex(this.A); 450 | 451 | this.M1str = this.H(AA+BB+this.toHex(this.S)); 452 | this.check(this.M1str, "M1str"); 453 | 454 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 455 | while (this.M1str.substring(0, 1) === '0') { 456 | //console.log("stripping leading zero from M1"); 457 | this.M1str = this.M1str.substring(1); 458 | } 459 | 460 | //console.log("M1str:" + this.M1str); 461 | 462 | //console.log("js ABS:" + AA+BB+this.toHex(this.S)); 463 | //console.log("js A:" + AA); 464 | //console.log("js B:" + BB); 465 | //console.log("js v:" + this.v); 466 | //console.log("js u:" + this.u); 467 | //console.log("js A:" + this.A); 468 | //console.log("js b:" + this.B); 469 | //console.log("js S:" + this.S); 470 | //console.log("js S:" + this.toHex(this.S)); 471 | //console.log("js M1:" + this.M1str); 472 | 473 | this.state = this.STEP_2; 474 | return { A: AA, M1: this.M1str }; 475 | }; 476 | 477 | /** 478 | * Receives the server evidence message 'M1'. The session is incremented 479 | * to {@link State#STEP_3}. 480 | * 481 | *

Argument origin: 482 | *

    483 | *
  • From server: evidence message 'M2'. 484 | *
485 | * @param serverM2 The server evidence message 'M2' as string. Must not be {@code null}. 486 | * @throws IllegalStateException If the method is invoked in a state 487 | * other than {@link State#STEP_2}. 488 | * @throws SRP6Exception If the session has timed out or the 489 | * server evidence message 'M2' is 490 | * invalid. 491 | */ 492 | SRP6JavascriptClientSession.prototype.step3 = function(M2) { 493 | "use strict"; 494 | this.check(M2); 495 | //console.log("SRP6JavascriptClientSession.prototype.step3"); 496 | 497 | // Check current state 498 | if (this.state !== this.STEP_2) 499 | throw new Error("IllegalStateException State violation: Session must be in STEP_2 state"); 500 | 501 | //console.log("js A:" + this.toHex(this.A)); 502 | //console.log("jsM1:" + this.M1str); 503 | //console.log("js S:" + this.toHex(this.S)); 504 | 505 | var computedM2 = this.H(this.toHex(this.A)+this.M1str+this.toHex(this.S)); 506 | 507 | //console.log("jsServerM2:" + M2); 508 | //console.log("jsClientM2:" + computedM2); 509 | 510 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 511 | while (computedM2.substring(0, 1) === '0') { 512 | //console.log("stripping leading zero from computedM2"); 513 | computedM2 = computedM2.substring(1); 514 | } 515 | 516 | //console.log("server M2:"+M2+"\ncomputedM2:"+computedM2); 517 | if ( ""+computedM2 !== ""+M2) { 518 | throw new Error("SRP6Exception Bad server credentials"); 519 | } 520 | 521 | this.state = this.STEP_3; 522 | 523 | return true; 524 | }; 525 | 526 | 527 | function SRP6JavascriptClientSessionSHA256(){ } 528 | 529 | SRP6JavascriptClientSessionSHA256.prototype = new SRP6JavascriptClientSession(); 530 | 531 | SRP6JavascriptClientSessionSHA256.prototype.N = function() { 532 | return new BigInteger(N_base10, 10); 533 | } 534 | 535 | SRP6JavascriptClientSessionSHA256.prototype.g = function() { 536 | return new BigInteger(g_base10, 10); 537 | } 538 | 539 | SRP6JavascriptClientSessionSHA256.prototype.H = function (x) { 540 | return SHA256(x).toString().toLowerCase(); 541 | } 542 | 543 | SRP6JavascriptClientSessionSHA256.prototype.k = new BigInteger(k_base16, 16); 544 | 545 | // return the new session class 546 | return SRP6JavascriptClientSessionSHA256; 547 | 548 | } 549 | 550 | module.exports = srpClientFactory -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Thinbus Javascript Secure Remote Password (SRP) 3 | * Version 1.8.0 4 | * Copyright 2014-2017 Simon Massey 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | // see test/testrunner.js for usage 8 | module.exports = { 9 | // the original browser client session prototype using RFC 5054 2048bit constants 10 | SRP6JavascriptClientSessionSHA256: require('./browser.js'), 11 | // client session factory is for advanced usage to set your own large prime values 12 | clientSessionFactory: require('./client.js'), 13 | // server session factory is for advanded usage to set your own large prime values 14 | serverSessionFactory: require('./server.js') 15 | } -------------------------------------------------------------------------------- /login.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbo1905/thinbus-srp-npm/c87461c72d29b1b183cfd6f9134f599933f2cbc5/login.monopic -------------------------------------------------------------------------------- /login.txt: -------------------------------------------------------------------------------- 1 | ┌──────────────┐ ┌──────────────┐ 2 | │ Browser │ │ Web Server │ 3 | └──────────────┘ └──────────────┘ 4 | │ │ 5 | .─. ┌───┐ GET /login.html ┌───┐ 6 | ( ) email,passwd │ │◀────────────────────────────────│ │ 7 | `┬' ─────────────▶│ │ └───┘ .───────────. 8 | ────┼──── │ │ AJAX /challenge {email} │ ( Database ) 9 | │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▶───┐ (`───────────') 10 | ┌┴┐ │ │ ┌─┤ │◀──────────────(`───────────') 11 | │ │ │ │ step1(email,salt,verifier)│ │ │{salt,verifier}(`───────────') 12 | │ │ │ │ │ │ │ `───────────' 13 | ──┘ └── │ │ └▶│ │ 14 | │ │ {salt,B} │ │ store b .───────────. 15 | ┌─┤ │◀─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├──────────────▶( Cache ) 16 | step1(email,passwd)│ │ │ └─┬─┘ (`───────────') 17 | step2(salt,B)│ │ │ POST /auth {email,A,M1} ┌───┐ load b (`───────────') 18 | └▶│ ├─────────────────────────────────│ │◀──────────────(`───────────') 19 | └───┘ ┌─┤ │ `───────────' 20 | │ step2(A,M1)│ │ │ ┌───────────────────┐ 21 | │ │ │ │You have to retain │ 22 | ┌─┴─┐ {M2} └▶│ │ │the private "b" │ 23 | step3(M2)│ │◀────────────────────────────────┤ │ │which matches the │ 24 | └─┬─┘ REDIRECT /home.html OR └─┬─┘ │public challenge │ 25 | ┌──────────────────────┐ /login.html │"B". This can be in│ 26 | │step3 confirms a │ │ │ │the main DB or a │ 27 | │shared private key. A │ │cache. │ 28 | │mobile running │ │ │ └───────────────────┘ 29 | │embedded JavaScript │ ▼ ▼ 30 | │also confirms the │ 31 | │server knows the │ 32 | │verifier that the user│ 33 | │registered with. │ 34 | └──────────────────────┘ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thinbus-srp", 3 | "version": "1.8.0", 4 | "description": "Secure Remote Password SRP SRP6a implementation.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "node test/testrunner.js", 8 | "build": "browserify --standalone thinbus -r ./thinbus-original-client.js -o browser.js", 9 | "build-alt": "browserify --full-paths --standalone thinbus -r ./client.js:SRP6JavascriptClientSessionSHA256 -o alt-browser.js", 10 | "build-random-strings": "browserify --full-paths -r random-strings > random-strings-out.js", 11 | "build-nocrypto": "browserify -r random-strings > random-strings-out.js; browserify -r crypto-js/sha256 > sha256-out.js; browserify -r jsbn > jsbn-out.js; browserify --full-paths --no-bundle-external -x jsbn -x crypto-js/sha256 -r ./client.js -o browser-out.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/simbo1905/thinbus-srp-npm.git" 16 | }, 17 | "keywords": [ 18 | "SRP", 19 | "SRP6a", 20 | "zero-knowledge", 21 | "authentication", 22 | "password", 23 | "salt" 24 | ], 25 | "author": "Simon Massey (https://bitbucket.org/simon_massey/)", 26 | "license": "Apache-2.0", 27 | "homepage": "https://github.com/simbo1905/thinbus-srp-npm", 28 | "dependencies": { 29 | "crypto-js": "^3.1.9-1", 30 | "jsbn": "^1.1.0", 31 | "random-strings": "0.0.1" 32 | }, 33 | "devDependencies": { 34 | "browserify": "^14.5.0", 35 | "jasmine-node": "^1.14.5", 36 | "jsonfn": "^0.31.0", 37 | "unit.js": "^2.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Thinbus Javascript Secure Remote Password (SRP) 2 | 3 | [![Build Status](https://travis-ci.org/simbo1905/thinbus-srp-npm.svg?branch=master)](https://travis-ci.org/simbo1905/thinbus-srp-npm) 4 | [![npm version](https://badge.fury.io/js/thinbus-srp.svg)](https://badge.fury.io/js/thinbus-srp) 5 | 6 | This package provides a Javascript [Secure Remote Password](http://srp.stanford.edu/) [SRP-6a](http://srp.stanford.edu/doc.html#papers) implementation for web browsers to perform a zero-knowledge proof-of-password to a web server. There is a demo application [thinbus-srp-npm-tester](https://github.com/simbo1905/thinbus-srp-npm-tester) that uses this npm library. 7 | 8 | This library contains both client and server JavaScript code. The public API exposes the client and server modules as two seperate factory closures: 9 | 10 | ```JavaScript 11 | // RFC 5054 2048bit constants 12 | const rfc5054 = { 13 | N_base10: "21766174458617435773191008891802753781907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288819", 14 | g_base10: "2", 15 | k_base16: "5b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300" 16 | } 17 | 18 | // generate the client session class from the client session factory closure 19 | const SRP6JavascriptClientSession = require('thinbus-srp/client.js')(rfc5054.N_base10, rfc5054.g_base10, rfc5054.k_base16); 20 | 21 | // generate the server session class from the server session factory closure 22 | const SRP6JavascriptServerSession = require('thinbus-srp/server.js')(rfc5054.N_base10, rfc5054.g_base10, rfc5054.k_base16); 23 | 24 | // generate a light weight browser compatible client session class from the browser session factory closure 25 | const SRP6JavascriptServerSession = require('thinbus-srp/browser.js')(rfc5054.N_base10, rfc5054.g_base10, rfc5054.k_base16); 26 | ``` 27 | 28 | See `test\testrunner.js` and try out `npm test` for an example of seeing the client and server running through the full SRP6a protocol. 29 | 30 | In order to have a compatible and small (40K) browser version of the client this package also ships with the original [thinbus-srp-js](https://bitbucket.org/simon_massey/thinbus-srp-js) JavaScript code which `npm run-script build` converts into a browserify module within the file `browser.js`. See [this article](https://dontkry.com/posts/code/browserify-and-the-universal-module-definition.html) as an introduction to browserify. 31 | 32 | ## Using 33 | 34 | For the definitions of the values discussed below please refer to the [SRP design page](http://srp.stanford.edu/design.html). The following sequence diagram shows how to register a user with an SRP salt and verifier as demonstrated by the [Thinbus Demo Application](https://github.com/simbo1905/thinbus-srp-npm-tester). 35 | 36 | ![Thinbus SRP Register Diagram](http://simonmassey.bitbucket.io/thinbus/register.png "Thinbus SRP Register Diagram") 37 | 38 | In the diagram above the user is shown a standard registration form which includes both the username (e.g email) and password fields. 39 | They enter their email and password and click the register button. JavaScript then generates their random `salt` 40 | and with the salt, email and password generates an SRP `verififer`. Only the email, `salt` and the `verifier` are transmitted to 41 | the server and the generated values are saved into the database keyed by the email. 42 | 43 | **Note** Always use browser developer tools to inspect what you actually post to the server and only post the values shown 44 | in the sequence diagram as defined in the [SRP design page](http://srp.stanford.edu/design.html). It is a protocol 45 | violation and security bug if the raw password is accidently transmitted to the server even if it is ignored by the server. It is also a protocol violation and a security bug if you accidently transmit the verifier to the browser. 46 | 47 | The following sequence diagram shows how to login a registered user. 48 | 49 | ![Thinbus SRP Login Diagram](http://simonmassey.bitbucket.io/thinbus/login-cache.png "Thinbus SRP Login Diagram") 50 | 51 | In the diagram above the user is shown a standard login form. They enter their email and password and click the login button. 52 | JavaScript then makes an AJAX call using their email to load their `salt` and a one-time server challenge `B`. Then client creates 53 | a one-time client challenge `A` and uses all the information to compute a password proof `M1`. It then posts to the server 54 | the email, `A`, and `M1` as the users credentials. The server uses all the information (including a private part of the challenge to check the password proof). Only the email, 55 | client challenge `A` and the password proof `M1` are transmitted to the server. Note that the server needs to hold the private challenge state `b` that corresponds to the public challenge `B` sent to the client. It can store this private state in a time limited cache. 56 | 57 | There is an optional step `client.step3(M2)` where `M2` is the server's proof of a shared session key to the client. 58 | You can return `M2` from the server to check the browser has a matching shared secret if you wish to use that for further cryptography. If your web application is distributed as a native mobile application such that the client is running trusted JavaScript then the `M2` proof is an additional check of the authenticity of the server; it confirms to trusted client code that the server knows the verifier matching the user password. This check is also proof that the client and server both generated the same shared session key `S`. You can use `getSessionKey()` on the client and server to get `H(S)` a 256bit shared key that has not been transmitted over the network. This can be used for follow on cryptography such as [http-hmac-spec](https://github.com/acquia/http-hmac-spec/blob/2.0/README.md#spec) signing of restful API traffic. 59 | 60 | **Note** that you don't have to use AJAX for SRP. It is used in the examples to hide the fact that with SRP you need an additional round-trip to the server to generate a challenge using the users verifier. You can avoid using AJAX by splitting the username and password fields across two pages. The first page can send the username and the next page can have a hidden fields containing the user specific salt and the server challenge `B`. This simply replaces the AJAX trip with an explicit page load. 61 | 62 | **Note** as per RFC 2945 the user ID (usually their email) is concatenated to their password when generating the verifier. This means that if a user changes *either* their email address or their password you need to generate a new verifier and replace the old one in the database. 63 | 64 | **Note** always use browser developer tools to inspect what you actually post to the server and only post the values shown 65 | in the sequence diagram as defined in the [SRP design page](http://srp.stanford.edu/design.html). It is a protocol violation 66 | and a security bug to accidently transmit to the server anything else even if it is ignored by the server. 67 | 68 | **Note** the JavaScript client object (typically `SRP6JavascriptClientSessionSHA256`) must be destroyed after each login attempt. 69 | The object is intended to be a temporary object and should be deleted to erase all traces of the password. You must also destroy 70 | the password form field the user typed their password into. The normal way to achieve destroying any traces of the password is to unload 71 | the login page after every login attempt. This is trivial to do by reloading the login page upon authentication failure or by loading a main landing page upon successful login. 72 | 73 | **Note** that the server has to remember the private ephemeral key `b` that matches the public ephemeral key `B` sent as a one-time server challenge to the user. 74 | This requires storing `b` either in the database, the server session or a server cache for the short duration of the login protocol. 75 | You cannot pass this value back to the server from the client without compromising security. 76 | The server should not use any values transmitted from the client other than those shown in the sequence diagram and 77 | named in the [SRP design page](http://srp.stanford.edu/design.html). 78 | 79 | **Note** if you want to use the shared session key for follow-on cryptography you should use `client.getSessionKey()` to retrieved the 80 | session key from the thinbus object and destroy the thinbus object as discussed above. The typical way to do this is to put the session key into browser local session storage. Then you can unload the login page then load a main landing page that collects the session key 81 | from storage. You can for example use the shared key for [http-hmac-spec](https://github.com/acquia/http-hmac-spec/blob/2.0/README.md#spec) signing for restful API traffic. 82 | 83 | ## Creating A Custom Large Safe Prime 84 | 85 | The Java version of Thinbus has a command line tool and instructions how to use openssl to create safe prime see https://bitbucket.org/simon_massey/thinbus-srp-js/overview 86 | 87 | ## Recommendations 88 | 89 | * Use Thinbus SRP over HTTPS. Configure your webserver to mark session cookies as secure to prevent accidental use of HTTP. Configure [HSTS](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security#HSTS_mechanism_overview) to force HTTPS with your service. If your customers use a company supplied computer going via a corporate web proxy then HTTPS may be [decrypted and monitored](http://security.stackexchange.com/questions/63304/how-can-my-employer-be-a-man-in-the-middle-when-i-connect-to-gmail). HTTPS may be compromised due to things like [bad certs in the wild](http://nakedsecurity.sophos.com/2013/12/09/serious-security-google-finds-fake-but-trusted-ssl-certificates-for-its-domains-made-in-france/). HTTPS may be compromised by bugs or misconfigurations such as [Heartbleed](http://en.wikipedia.org/wiki/Heartbleed). HTTPS alone cannot protected against leaking passwords into error messages in your webserver logs. SRP over HTTPS is much safer than either used alone. 90 | * Add a javascript password strength meter and only allow users to register a verifier for a strong password. The best cryptography in the world won't protect your users if they use "password" as their password. 91 | * Check that the user isn't using a password that is in the [HiBP](https://haveibeenpwned.com/Passwords) database. HiBP lets you query using the first few characters of the hash of the users passwords. You can check for an exact match of the hash in the results and refuse to let the user use a password that is in the public database of leaked passwords. 92 | * Use a custom large safe prime number `N` using the instructions above. **Tip:** Check on the browsers and hardware you are targeting that the math runs fast enough for a good user experience for your chosen bit length. 93 | * Make the salt column in the database `not null` and add a uniqueness constraint. 94 | * Use symmetric AES encryption with a key only visible at the webserver to encrypt the verifier `v` value within the database. This protects against off-site database backups being used in an offline dictionary attack against `v`. 95 | * You can prevent privileged accounts from logging in using legacy browsers by checking `random16byteHex.isWebCryptoAPI()` when fetching the user salt; simply abort the protocol for privileged accounts when secure random numbers are not available at the browser. If you allow the use of browsers that don't have the `WebCryptoAPI` secure random number APIs then the fallback random generator hashes `window.cookie` as part of the generator seed. Consider adding a secure random cookie to help seed the fallback generator. 96 | * Don't include any JS files [or any CSS files](http://stackoverflow.com/a/3613162/329496) from external sites onto your login page. 97 | * Count the number of failed password attempts and present the user with a CAPTCHA after a dozen attempts. This slows down scripted online dictionary attacks. Consider suspending the account (possibly temporarily) after a large number of contiguous failed attempts to defeat someone carefully researching a user then trying to guess their likely password. 98 | 99 | ## Miscellaneous 100 | 101 | The name Thinbus is a play on the name of the SRP Java library Nimbus. Thinbus NPM (this repo) is tested against Thinbus JavaSciprt taken from the Java version, which in turn is testing against Nimbus, which gives higher confidence in its correctneess. Nimbus has had a lot of eyes look at it over the years and was carefully check against other Java SRP library code and the example code provided by the inventor of SRP. 102 | 103 | Thinbus aims to support different server languages. By providing server versions tested against Thinbus JavaScript which is tested against many servers we can collectively all have greater confidence that all the server versions are correct: 104 | 105 | 1. [thinbus-srp-js](https://bitbucket.org/simon_massey/thinbus-srp-js) The Java version which is compatible with the JavaScript version. At a future release I may delete the JavaScript from that repo and make this npm vesion the canonical one. It also includes a Java SRP client that you can use for server-to-server authentication or to generate temporary passwords to email to users. 106 | 1. [thinbus-srp-spring-demo](https://bitbucket.org/simon_massey/thinbus-srp-spring-demo/overview) A Spring MVC application which uses the Thinbus JavaScript library to create accounts and login users with Spring Security. This has both authentication and authorisation. 107 | 1. [thinbus-php](https://bitbucket.org/simon_massey/thinbus-php/overview) Uses the Thinbus Javascript library to do SRP authentication to PHP server code. It also includes a PHP SRP client that you can use for server-to-server authentication or to generating temporary passwords to email to users. 108 | 1. [pysrp_thinbus](https://github.com/SthPhoenix/pysrp_thinbus) is a fork of [pysrp](https://github.com/cocagne/pysrp) which is compatible with Thinbus so that you can use Python on the server. 109 | 110 | ## License 111 | 112 | ``` 113 | Copyright 2014-2017 Simon Massey 114 | 115 | Licensed under the Apache License, Version 2.0 (the "License"); 116 | you may not use this file except in compliance with the License. 117 | You may obtain a copy of the License at 118 | 119 | http://www.apache.org/licenses/LICENSE-2.0 120 | 121 | Unless required by applicable law or agreed to in writing, software 122 | distributed under the License is distributed on an "AS IS" BASIS, 123 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 124 | See the License for the specific language governing permissions and 125 | limitations under the License. 126 | ``` 127 | 128 | -------------------------------------------------------------------------------- /register.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simbo1905/thinbus-srp-npm/c87461c72d29b1b183cfd6f9134f599933f2cbc5/register.monopic -------------------------------------------------------------------------------- /register.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | ┌──────────────┐ ┌──────────────┐ 4 | │ Browser │ │ Web Server │ 5 | └──────────────┘ └──────┬───────┘ 6 | │ 7 | │ 8 | .─. ┌─┴─┐ GET /register.html ┌───┐ 9 | ( ) │ │◀────────────────────────────────│ │ 10 | `┬' │ │ └───┘ 11 | ────┼──── │ │ │ 12 | │ email,passwd │ │ 13 | ┌┴┐ ─────────────▶ ├──┐ │ 14 | │ │ │ │ │ generateSalt() 15 | │ │ │ │ │ generateVerifier(email,passwd) │ 16 | ──┘ └── │ │◀─┘ 17 | │ │ │ 18 | │ │ 19 | │ │ │ 20 | │ │ POST {email,salt,verifier} ┌───┐ 21 | │ ├────────────────────────────────▶│ │ 22 | │ │ └───┘ 23 | └───┘ │ 24 | │ 25 | │ 26 | │ 27 | ▼ ▼ -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Thinbus Javascript Secure Remote Password (SRP) 3 | * Version 1.8.0 4 | * Copyright 2014-2017 Simon Massey 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | const SHA256 = require("crypto-js/sha256"); 8 | 9 | const BigInteger = require('jsbn').BigInteger; 10 | 11 | var randomStrings = require('random-strings'); 12 | 13 | /** 14 | * A factory closure which takes SRP parameters and returns a SRP6JavascriptServerSession class with with the parameters bound to it. 15 | * 16 | * @param {string} N_base10 Safe prime N as decimal string. 17 | * @param {string} g_base10 Generator g as decimal string. 18 | * @param {string} k_base16 Symetry braking k as hexidecimal string. See https://bitbucket.org/simon_massey/thinbus-srp-js/overview 19 | */ 20 | function srpServerFactory (N_base10, g_base10, k_base16) { 21 | 22 | function SRP6JavascriptServerSession() { 23 | "use strict"; 24 | 25 | /** 26 | * The session is initialised and ready to begin authentication 27 | * by proceeding to {@link #STEP_1}. 28 | */ 29 | this.INIT = 0; 30 | 31 | /** 32 | * The authenticating user has input their identity 'I' 33 | * (username) and password 'P'. The session is ready to proceed 34 | * to {@link #STEP_2}. 35 | */ 36 | this.STEP_1 = 1; 37 | 38 | /** 39 | * The user identity 'I' is submitted to the server which has 40 | * replied with the matching salt 's' and its public value 'B' 41 | * based on the user's password verifier 'v'. The session is 42 | * ready to proceed to {@link #STEP_3}. 43 | */ 44 | this.STEP_2 = 2; 45 | 46 | /** 47 | * The client public key 'A' and evidence message 'M1' are 48 | * submitted and the server has replied with own evidence 49 | * message 'M2'. The session is finished (authentication was 50 | * successful or failed). 51 | */ 52 | this.STEP_3 = 3; 53 | 54 | this.state = this.INIT; 55 | 56 | this.v = null; // verifier 57 | this.I = null; // identity (used as informational not for any crypto) 58 | this.salt = null; // salt 59 | this.b = null; // server private key 60 | this.B = null; // server public key 61 | this.k = null; // constant computed by the server 62 | this.S = null; // shared secret key long form 63 | this.K = null; // shared secret hashed form 64 | 65 | // private 66 | this.check = function(v, name) { 67 | if( typeof v === 'undefined' || v === null || v === "" || v === "0" ) { 68 | throw new Error(name+" must not be null, empty or zero"); 69 | } 70 | }; 71 | } 72 | 73 | SRP6JavascriptServerSession.prototype.toPrivateStoreState = function() { 74 | "use strict"; 75 | return {I: this.I, v: this.toHex(this.v), s: this.toHex(this.salt), b: this.toHex(this.b)}; 76 | } 77 | 78 | SRP6JavascriptServerSession.prototype.fromPrivateStoreState = function(obj) { 79 | "use strict"; 80 | //return {I: this.I, v: this.toHex(this.v), s: this.toHex(this.salt), b: this.toHex(this.b)}; 81 | this.I = obj.I; 82 | this.v = this.fromHex(obj.v); 83 | this.salt = this.fromHex(obj.salt); 84 | this.b = this.fromHex(obj.b); 85 | this.B = this.g.modPow(this.b, this.N).add(this.v.multiply(this.k)).mod(this.N); 86 | this.state = this.STEP_1; 87 | return; 88 | } 89 | 90 | // public helper 91 | SRP6JavascriptServerSession.prototype.toHex = function(n) { 92 | "use strict"; 93 | return n.toString(16); 94 | }; 95 | 96 | // public helper 97 | /* jshint ignore:start */ 98 | SRP6JavascriptServerSession.prototype.fromHex = function(s) { 99 | "use strict"; 100 | return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat 101 | }; 102 | /* jshint ignore:end */ 103 | 104 | // public helper to hide BigInteger from the linter 105 | /* jshint ignore:start */ 106 | SRP6JavascriptServerSession.prototype.BigInteger = function(string, radix) { 107 | "use strict"; 108 | return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat 109 | }; 110 | /* jshint ignore:end */ 111 | 112 | 113 | // public getter of the current workflow state. 114 | SRP6JavascriptServerSession.prototype.getState = function() { 115 | "use strict"; 116 | return this.state; 117 | }; 118 | 119 | /** 120 | * Gets the shared sessionkey 121 | * 122 | * @param hash Boolean With to return the large session key 'S' or 'K=H(S)' 123 | */ 124 | SRP6JavascriptServerSession.prototype.getSessionKey = function(hash) { 125 | "use strict"; 126 | if( this.S === null ) { 127 | return null; 128 | } 129 | this.SS = this.toHex(this.S); 130 | if(typeof hash !== 'undefined' && hash === false){ 131 | return this.SS; 132 | } else { 133 | if( this.K === null ) { 134 | this.K = this.H(this.SS); 135 | } 136 | return this.K; 137 | } 138 | }; 139 | 140 | // public getter 141 | SRP6JavascriptServerSession.prototype.getUserID = function() { 142 | "use strict"; 143 | return this.I; 144 | }; 145 | 146 | /** 147 | * Increments this SRP-6a authentication session to 148 | * {@link State#STEP_1}. 149 | * 150 | *

Argument origin: 151 | * 152 | *

    153 | *
  • From client: user identity 'I'. 154 | *
  • From server database: matching salt 's' and password verifier 155 | * 'v' values. 156 | *
157 | * 158 | * @param userID The identity 'I' of the authenticating user. Must not 159 | * be {@code null} or empty. 160 | * @param s The password salt 's'. Must not be {@code null}. 161 | * @param v The password verifier 'v'. Must not be {@code null}. 162 | * 163 | * @return The server public value 'B'. 164 | * 165 | * @throws IllegalStateException If the mehod is invoked in a state 166 | * other than {@link State#INIT}. 167 | */ 168 | SRP6JavascriptServerSession.prototype.step1 = function(identity, salt, verifier) { 169 | "use strict"; 170 | //console.log("SRP6JavascriptServerSession.prototype.step1"); 171 | //console.log("N: "+this.N); 172 | //console.log("g: "+this.g); 173 | //console.log("k: "+this.toHex(this.k)); 174 | 175 | if( this.state !== this.INIT) { 176 | throw new Error("IllegalStateException not in state INIT"); 177 | } 178 | 179 | this.check(identity, "identity"); 180 | this.check(salt, "salt"); 181 | this.check(verifier, "verifier"); 182 | this.I = identity; 183 | this.v = this.fromHex(verifier); 184 | this.salt = this.fromHex(salt); 185 | 186 | this.state = this.STEP_1; 187 | this.b = this.randomB(); 188 | //console.log("b: "+this.b); 189 | this.B = this.g.modPow(this.b, this.N).add(this.v.multiply(this.k)).mod(this.N); 190 | //console.log("B: "+this.B); 191 | this.state = this.STEP_1; 192 | return this.toHex(this.B); 193 | }; 194 | 195 | /** 196 | * Computes the random scrambling parameter u = H(A | B) 197 | *

Specification RFC 2945 198 | * Will throw an error if 199 | * 200 | * @param A The public client value 'A'. Must not be {@code null}. 201 | * @param B The public server value 'B'. Must not be {@code null}. 202 | * 203 | * @return The resulting 'u' value. 204 | */ 205 | SRP6JavascriptServerSession.prototype.computeU = function(Astr, Bstr) { 206 | "use strict"; 207 | //console.log("SRP6JavascriptServerSession.prototype.computeU"); 208 | this.check(Astr, "Astr"); 209 | this.check(Bstr, "Bstr"); 210 | /* jshint ignore:start */ 211 | var output = this.H(Astr+Bstr); 212 | //console.log("js raw u:"+output); 213 | var u = new BigInteger(""+output,16); 214 | //console.log("js u:"+this.toHex(u)); 215 | if( BigInteger.ZERO.equals(u) ) { 216 | throw new Error("SRP6Exception bad shared public value 'u' as u==0"); 217 | } 218 | return u; 219 | /* jshint ignore:end */ 220 | }; 221 | 222 | SRP6JavascriptServerSession.prototype.random16byteHex = function() { 223 | "use strict"; 224 | 225 | var r1 = null; 226 | /* jshint ignore:start */ 227 | r1 = random16byteHex.random(); 228 | /* jshint ignore:end */ 229 | return r1; 230 | }; 231 | 232 | /** 233 | * Generate a random value in the range `[1,N)` using a minimum of 256 random bits. 234 | * 235 | * See specification RFC 5054. 236 | * This method users the best random numbers available. Just in case the random number 237 | * generate in the client web browser is totally buggy it also adds `H(I+":"+salt+":"+time())` 238 | * to the generated random number. 239 | */ 240 | SRP6JavascriptServerSession.prototype.randomB = function() { 241 | "use strict"; 242 | 243 | // our ideal number of random bits to use for `a` as long as its bigger than 256 bits 244 | var hexLength = this.toHex(this.N).length; 245 | 246 | var ZERO = this.BigInteger("0", 10); 247 | var ONE = this.BigInteger("1", 10); 248 | 249 | var r = ZERO; 250 | 251 | // loop until we don't have a ZERO value. we would have to generate exactly N to loop so very rare. 252 | while(ZERO.equals(r)){ 253 | var rstr = randomStrings.hex(hexLength); 254 | 255 | //console.log("rstr:"+rstr); 256 | 257 | // we now have a random just at lest 256 bits but typically more bits than N for large N 258 | var rBi = this.BigInteger(rstr, 16); 259 | 260 | //console.log("rBi:"+rBi); 261 | 262 | // this hashes the time in ms such that we wont get repeated numbers for successive attempts 263 | // it also hashes the salt which can itself be salted by a server strong random which protects 264 | // against rainbow tables. it also hashes the user identity which is unique to each user 265 | // to protect against having simply no good random numbers anywhere 266 | var oneTimeBi = this.BigInteger(this.H(this.I+":"+this.salt+':'+(new Date()).getTime()), 16); 267 | 268 | //console.log("oneTimeBi:"+oneTimeBi); 269 | 270 | // here we add the "one time" hashed time number to our random number to the random number 271 | // this protected against a buggy browser random number generated generating a constant value 272 | // we mod(N) to wrap to the range [0,N) then loop if we get 0 to give [1,N) 273 | // mod(N) is broken due to buggy library code so we workaround with modPow(1,N) 274 | r = (oneTimeBi.add(rBi)).modPow(ONE, this.N); 275 | } 276 | 277 | //console.log("r:"+r); 278 | 279 | // the result will in the range [1,N) using more random bits than size N 280 | return r; 281 | }; 282 | 283 | /** 284 | * Increments this SRP-6a authentication session to 285 | * {@link State#STEP_2}. 286 | * 287 | *

Argument origin: 288 | * 289 | *

    290 | *
  • From client: public value 'A' and evidence message 'M1'. 291 | *
292 | * 293 | * @param A The client public value. Must not be {@code null}. 294 | * @param M1 The client evidence message. Must not be {@code null}. 295 | * 296 | * @return The server evidence message 'M2'. 297 | * 298 | * @throws SRP6Exception If the session has timed out, the client public 299 | * value 'A' is invalid or the user credentials 300 | * are invalid. 301 | * 302 | * @throws IllegalStateException If the method is invoked in a state 303 | * other than {@link State#STEP_1}. 304 | */ 305 | SRP6JavascriptServerSession.prototype.step2 = function(Astr, M1client) { 306 | "use strict"; 307 | 308 | if( this.state !== this.STEP_1 ) { 309 | throw new Error("IllegalStateException not in state STEP_1"); 310 | } 311 | 312 | this.check(Astr, "A"); 313 | this.check(M1client, "M1"); 314 | 315 | var A = this.fromHex(Astr); 316 | 317 | var Bstr = this.toHex(this.B); 318 | 319 | var u = this.computeU(Astr, Bstr); 320 | 321 | this.S = this.v.modPow(u, this.N).multiply(A).modPow(this.b, this.N); 322 | 323 | // console.log("sAA:"+Astr); 324 | // console.log("sBB:"+Bstr); 325 | // console.log("sSS:"+this.toHex(this.S)); 326 | 327 | var M1str = this.H(Astr+Bstr+this.toHex(this.S)); 328 | 329 | this.check(M1str, "M1str"); 330 | 331 | // Java BigInteger math will trim leading zeros so we must do likewise to get a match across languages 332 | while (M1str.substring(0, 1) === '0') { 333 | //console.log("stripping leading zero from M1"); 334 | M1str = M1str.substring(1); 335 | } 336 | 337 | // console.log("M1client:"+M1client); 338 | // console.log("M1str :"+M1str); 339 | 340 | if( M1client !== M1str ){ 341 | throw "Bad client credentials"; 342 | } 343 | 344 | var M2 = this.H(this.toHex(A)+M1str+this.toHex(this.S)); 345 | 346 | // Java BigInteger math will trim leading zeros so we must do likewise to get a match across languages 347 | while (M2.substring(0, 1) === '0') { 348 | //console.log("stripping leading zero from computedM2"); 349 | M2 = M2.substring(1); 350 | } 351 | 352 | this.state = this.STEP_2; 353 | 354 | return M2; 355 | }; 356 | 357 | function SRP6JavascriptServerSessionSHA256(){ } 358 | 359 | SRP6JavascriptServerSessionSHA256.prototype = new SRP6JavascriptServerSession(); 360 | 361 | SRP6JavascriptServerSessionSHA256.prototype.N = new BigInteger(N_base10, 10); 362 | 363 | SRP6JavascriptServerSessionSHA256.prototype.g = new BigInteger(g_base10, 10); 364 | 365 | SRP6JavascriptServerSessionSHA256.prototype.H = function (x) { 366 | return SHA256(x).toString().toLowerCase(); 367 | } 368 | 369 | SRP6JavascriptServerSessionSHA256.prototype.k = new BigInteger(k_base16, 16); 370 | 371 | // return the new session class 372 | return SRP6JavascriptServerSessionSHA256; 373 | } 374 | 375 | module.exports = srpServerFactory 376 | -------------------------------------------------------------------------------- /test/testrunner.js: -------------------------------------------------------------------------------- 1 | 2 | // RFC 5054 2048bit constants 3 | var rfc5054 = { 4 | N_base10: "21766174458617435773191008891802753781907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288819", 5 | g_base10: "2", 6 | k_base16: "5b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300" 7 | } 8 | 9 | // generate the client session class from the client session factory using the safe prime constants 10 | const SRP6JavascriptClientSession = require('../client.js')(rfc5054.N_base10, rfc5054.g_base10, rfc5054.k_base16); 11 | 12 | // generate the server session class from the server session factory using the safe prime constants 13 | const SRP6JavascriptServerSession = require('../server.js')(rfc5054.N_base10, rfc5054.g_base10, rfc5054.k_base16); 14 | 15 | // ---------------------------------------------------------------------------- 16 | // CLIENT REGISTRATION FLOW 17 | // https://simonmassey.bitbucket.io/thinbus/register.png 18 | // Note as per RFC 2945 the user ID (usually their email) is concatenated to 19 | // their password when generating the verifier. This means that if a user 20 | // changes either their email address or their password you need to generate 21 | // a new verifier and replace the old one in the database. 22 | // ┌──────────────┐ ┌──────────────┐ 23 | // │ Browser │ │ Web Server │ 24 | // └──────────────┘ └──────┬───────┘ 25 | // │ 26 | // │ 27 | // .─. ┌─┴─┐ GET /register.html ┌───┐ 28 | // ( ) │ │◀────────────────────────────────│ │ 29 | // `┬' │ │ └───┘ 30 | // ────┼──── │ │ │ 31 | // │ email,passwd │ │ 32 | // ┌┴┐ ─────────────▶ ├──┐ │ 33 | // │ │ │ │ │ generateSalt() 34 | // │ │ │ │ │ generateVerifier(email,passwd) │ 35 | // ──┘ └── │ │◀─┘ 36 | // │ │ │ 37 | // │ │ 38 | // │ │ │ 39 | // │ │ POST {email,salt,verifier} ┌───┐ 40 | // │ ├────────────────────────────────▶│ │ 41 | // │ │ └───┘ 42 | // └───┘ │ 43 | // │ 44 | 45 | // instantiate a client session 46 | const client = new SRP6JavascriptClientSession(); 47 | 48 | // generate a random salt that should be stored with the user verifier 49 | const salt = client.generateRandomSalt(); 50 | 51 | const username = "tom@arcot.com"; 52 | const password = "password1234"; 53 | 54 | // generate the users password verifier that should be stored with their salt. 55 | const verifier = client.generateVerifier(salt, username, password); 56 | 57 | // ┌──────────────┐ ┌──────────────┐ 58 | // │ Browser │ │ Web Server │ 59 | // └──────────────┘ └──────────────┘ 60 | // │ │ 61 | // .─. ┌───┐ GET /login.html ┌───┐ 62 | // ( ) email,passwd │ │◀────────────────────────────────│ │ 63 | // `┬' ─────────────▶│ │ └───┘ .───────────. 64 | // ────┼──── │ │ AJAX /challenge {email} │ ( Database ) 65 | // │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▶───┐ (`───────────') 66 | // ┌┴┐ │ │ ┌─┤ │◀──────────────(`───────────') 67 | // │ │ │ │ step1(email,salt,verifier)│ │ │{salt,verifier}(`───────────') 68 | // │ │ │ │ │ │ │ `───────────' 69 | // ──┘ └── │ │ └▶│ │ 70 | // │ │ {salt,B} │ │ store b .───────────. 71 | // ┌─┤ │◀─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├──────────────▶( Cache ) 72 | // step1(email,passwd)│ │ │ └─┬─┘ (`───────────') 73 | // step2(salt,B)│ │ │ POST /auth {email,A,M1} ┌───┐ load b (`───────────') 74 | // └▶│ ├─────────────────────────────────│ │◀──────────────(`───────────') 75 | // └───┘ ┌─┤ │ `───────────' 76 | // │ step2(A,M1)│ │ │ ┌───────────────────┐ 77 | // │ │ │ │You have to retain │ 78 | // ┌─┴─┐ {M2} └▶│ │ │the private "b" │ 79 | // step3(M2)│ │◀────────────────────────────────┤ │ │which matches the │ 80 | // └─┬─┘ REDIRECT /home.html OR └─┬─┘ │public challenge │ 81 | // ┌──────────────────────┐ /login.html │"B". This can be in│ 82 | // │step3 confirms a │ │ │ │the main DB or a │ 83 | // │shared private key. A │ │cache. │ 84 | // │mobile running │ │ │ └───────────────────┘ 85 | // │embedded JavaScript │ ▼ ▼ 86 | // │also confirms the │ 87 | // │server knows the │ 88 | // │verifier that the user│ 89 | // │registered with. │ 90 | // └──────────────────────┘ 91 | 92 | // client starts with the username and password. 93 | client.step1(username, password); 94 | 95 | // server generates B and b, sends B to client and b to a cache 96 | var serverWillDie = new SRP6JavascriptServerSession(); 97 | const B = serverWillDie.step1(username, salt, verifier); 98 | const privateState = serverWillDie.toPrivateStoreState(); 99 | const cacheJson = JSON.stringify(privateState); 100 | // store the dbJson in a temporary cache or the main DB and await client to respond to challenge B. 101 | // return B and salt to the client. 102 | 103 | // client creates a password proof from the salt, challenge and the username and password provided at step1. this generates `A` the cliehnt public ephemeral number and `M1` the hash of `M1` of a shared session key derived from both `A` and `B`. You post `A` and `M1` to the server (e.g. seperated by a colon) instead of a password. 104 | var credentials = client.step2(salt, B); 105 | 106 | // we now need to load the challenge data from the cache to check the credentials {A,M1} 107 | const newPrivate = JSON.parse(cacheJson); 108 | server = new SRP6JavascriptServerSession(); 109 | server.fromPrivateStoreState(newPrivate); 110 | 111 | // the server takes `A`, internally computes `M1` based on the verifier, and checks that its `M1` matches the value sent from the client. If not it throws an exception. If the `M1` match then the password proof is valid. It then generates `M2` which is a proof that the server has the shared session key. 112 | var M2 = server.step2(credentials.A, credentials.M1); 113 | 114 | // client verifies that the server shows proof of the shared session key which demonstrates that it knows the verifier that matchews the password. 115 | client.step3(M2); 116 | 117 | // we can now use the shared session key that hasn't crossed the network for follow on cryptography (such as JWT token signing or whatever) 118 | 119 | const clientSessionKey = client.getSessionKey(); 120 | 121 | //console.log("clientSessionKey:"+clientSessionKey); 122 | 123 | const serverSessionKey = server.getSessionKey(); 124 | 125 | //console.log("serverSessionKey:"+serverSessionKey); 126 | 127 | // load Unit.js module 128 | const test = require('unit.js'); 129 | 130 | // the javascript client defaults to hashing the session key as that is additional protection of the password in case the key is accidentally exposed to an attacker. 131 | // This the strong session key `K` as described on the [SRP design page](http://srp.stanford.edu/design.html). 132 | // This can be used for follow on cryptography such as HMAC signing of JWT web tokens using HS256. 133 | test.assert.equal(clientSessionKey, serverSessionKey); 134 | 135 | // regrettibly if you browserify the client code it comes in at 694k. 136 | // so we also ship the light weight original thinbus for browsers 137 | // note that the verifier being used below was created by the node verion 138 | // of the client. that proves that you can generate a temporary password verifier 139 | // and email that to a user who can then login with a browser. 140 | 141 | const BrowserSRP6JavascriptClientSession = require('../browser.js')(rfc5054.N_base10, rfc5054.g_base10, rfc5054.k_base16);; 142 | //console.log(JSON.stringify(BrowserSRP6JavascriptClientSession)); 143 | const bclient = new BrowserSRP6JavascriptClientSession(); 144 | bclient.step1(username, password); 145 | var bserver = new SRP6JavascriptServerSession(); 146 | const bB = bserver.step1(username, salt, verifier); 147 | var bcredentials = bclient.step2(salt, bB); 148 | var bM2 = bserver.step2(bcredentials.A, bcredentials.M1); 149 | const bclientSessionKey = bclient.getSessionKey(); 150 | const bserverSessionKey = bserver.getSessionKey(); 151 | test.assert.equal(bclientSessionKey, bserverSessionKey); 152 | // console.log(bclientSessionKey); 153 | // console.log(bserverSessionKey); -------------------------------------------------------------------------------- /thinbus-original-client.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Thinbus Javascript Secure Remote Password (SRP) 3 | * Version 1.6.0 4 | * Copyright 2014-2015 Simon Massey originally published at https://bitbucket.org/simon_massey/thinbus-srp-js 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * ---------------------------------------------------------------------- 7 | * "jsbn.js" 8 | * Copyright (c) 2003-2005 Tom Wu 9 | * All Rights Reserved. 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining 12 | * a copy of this software and associated documentation files (the 13 | * "Software"), to deal in the Software without restriction, including 14 | * without limitation the rights to use, copy, modify, merge, publish, 15 | * distribute, sublicense, and/or sell copies of the Software, and to 16 | * permit persons to whom the Software is furnished to do so, subject to 17 | * the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 23 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 24 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 25 | * 26 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 27 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 28 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 29 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 30 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 31 | * 32 | * In addition, the following condition applies: 33 | * 34 | * All redistributions must retain an intact copy of this copyright notice 35 | * and disclaimer. 36 | * https://github.com/rubycon/isaac.js/blob/master/isaac.js 37 | * ---------------------------------------------------------------------- 38 | * "isaac.js" 39 | * Copyright (c) 2012 Yves-Marie K. Rinquin 40 | * 41 | * Permission is hereby granted, free of charge, to any person obtaining 42 | * a copy of this software and associated documentation files (the 43 | * "Software"), to deal in the Software without restriction, including 44 | * without limitation the rights to use, copy, modify, merge, publish, 45 | * distribute, sublicense, and/or sell copies of the Software, and to 46 | * permit persons to whom the Software is furnished to do so, subject to 47 | * the following conditions: 48 | * 49 | * The above copyright notice and this permission notice shall be 50 | * included in all copies or substantial portions of the Software. 51 | * 52 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 53 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 54 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 55 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 56 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 57 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 58 | * ---------------------------------------------------------------------- 59 | * CryptoJS v3.1.2 60 | * code.google.com/p/crypto-js 61 | * (c) 2009-2013 by Jeff Mott. All rights reserved. 62 | * Redistribution and use in source and binary forms, with or without 63 | * modification, are permitted provided that the following conditions 64 | * are met: 65 | * > Redistributions of source code must retain the above copyright notice, 66 | * this list of conditions, and the following disclaimer. 67 | * > Redistributions in binary form must reproduce the above copyright notice, 68 | * this list of conditions, and the following disclaimer in the documentation 69 | * or other materials provided with the distribution. 70 | * > Neither the name CryptoJS nor the names of its contributors may be used 71 | * to endorse or promote products derived from this software without specific 72 | * prior written permission. 73 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS," 74 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 75 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, 76 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 77 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 78 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 79 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 80 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 81 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 82 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 83 | * THE POSSIBILITY OF SUCH DAMAGE. 84 | */ 85 | 86 | var CryptoJS=CryptoJS||function(e,z){var m={},y=m.lib={},i=function(){},d=y.Base={extend:function(f){i.prototype=this;var g=new i;f&&g.mixIn(f);g.hasOwnProperty("init")||(g.init=function(){g.$super.init.apply(this,arguments)});g.init.prototype=g;g.$super=this;return g},create:function(){var f=this.extend();f.init.apply(f,arguments);return f},init:function(){},mixIn:function(f){for(var g in f){f.hasOwnProperty(g)&&(this[g]=f[g])}f.hasOwnProperty("toString")&&(this.toString=f.toString)},clone:function(){return this.init.prototype.extend(this)}},a=y.WordArray=d.extend({init:function(f,g){f=this.words=f||[];this.sigBytes=g!=z?g:4*f.length},toString:function(f){return(f||r).stringify(this)},concat:function(g){var k=this.words,j=g.words,f=this.sigBytes;g=g.sigBytes;this.clamp();if(f%4){for(var h=0;h>>2]|=(j[h>>>2]>>>24-8*(h%4)&255)<<24-8*((f+h)%4)}}else{if(65535>>2]=j[h>>>2]}}else{k.push.apply(k,j)}}this.sigBytes+=g;return this},clamp:function(){var f=this.words,g=this.sigBytes;f[g>>>2]&=4294967295<<32-8*(g%4);f.length=e.ceil(g/4)},clone:function(){var f=d.clone.call(this);f.words=this.words.slice(0);return f},random:function(f){for(var h=[],g=0;g>>2]>>>24-8*(f%4)&255;j.push((h>>>4).toString(16));j.push((h&15).toString(16))}return j.join("")},parse:function(g){for(var j=g.length,h=[],f=0;f>>3]|=parseInt(g.substr(f,2),16)<<24-4*(f%8)}return new a.init(h,j/2)}},c=p.Latin1={stringify:function(g){var j=g.words;g=g.sigBytes;for(var h=[],f=0;f>>2]>>>24-8*(f%4)&255))}return h.join("")},parse:function(g){for(var j=g.length,h=[],f=0;f>>2]|=(g.charCodeAt(f)&255)<<24-8*(f%4)}return new a.init(h,j)}},b=p.Utf8={stringify:function(f){try{return decodeURIComponent(escape(c.stringify(f)))}catch(g){throw Error("Malformed UTF-8 data")}},parse:function(f){return c.parse(unescape(encodeURIComponent(f)))}},n=y.BufferedBlockAlgorithm=d.extend({reset:function(){this._data=new a.init;this._nDataBytes=0},_append:function(f){"string"==typeof f&&(f=b.parse(f));this._data.concat(f);this._nDataBytes+=f.sigBytes},_process:function(j){var s=this._data,q=s.words,h=s.sigBytes,l=this.blockSize,k=h/(4*l),k=j?e.ceil(k):e.max((k|0)-this._minBufferSize,0);j=k*l;h=e.min(4*j,h);if(j){for(var g=0;gd;){var c;o:{c=z;for(var p=i.sqrt(c),r=2;r<=p;r++){if(!(c%r)){c=!1;break o}}c=!0}c&&(8>d&&(e[d]=y(i.pow(z,0.5))),b[d]=y(i.pow(z,1/3)),d++);z++}var o=[],n=n.SHA256=m.extend({_doReset:function(){this._hash=new A.init(e.slice(0))},_doProcessBlock:function(G,F){for(var H=this._hash.words,E=H[0],D=H[1],t=H[2],x=H[3],q=H[4],w=H[5],v=H[6],u=H[7],s=0;64>s;s++){if(16>s){o[s]=G[F+s]|0}else{var a=o[s-15],C=o[s-2];o[s]=((a<<25|a>>>7)^(a<<14|a>>>18)^a>>>3)+o[s-7]+((C<<15|C>>>17)^(C<<13|C>>>19)^C>>>10)+o[s-16]}a=u+((q<<26|q>>>6)^(q<<21|q>>>11)^(q<<7|q>>>25))+(q&w^~q&v)+b[s]+o[s];C=((E<<30|E>>>2)^(E<<19|E>>>13)^(E<<10|E>>>22))+(E&D^E&t^D&t);u=v;v=w;w=q;q=x+a|0;x=t;t=D;D=E;E=a+C|0}H[0]=H[0]+E|0;H[1]=H[1]+D|0;H[2]=H[2]+t|0;H[3]=H[3]+x|0;H[4]=H[4]+q|0;H[5]=H[5]+w|0;H[6]=H[6]+v|0;H[7]=H[7]+u|0},_doFinalize:function(){var g=this._data,j=g.words,f=8*this._nDataBytes,h=8*g.sigBytes;j[h>>>5]|=128<<24-h%32;j[(h+64>>>9<<4)+14]=i.floor(f/4294967296);j[(h+64>>>9<<4)+15]=f;g.sigBytes=4*j.length;this._process();return this._hash},clone:function(){var f=m.clone.call(this);f._hash=this._hash.clone();return f}});B.SHA256=m._createHelper(n);B.HmacSHA256=m._createHmacHelper(n)})(Math); 87 | var dbits;var canary=244837814094590;var j_lm=((canary&16777215)==15715070);function BigInteger(e,d,f){if(e!=null){if("number"==typeof e){this.fromNumber(e,d,f)}else{if(d==null&&"string"!=typeof e){this.fromString(e,256)}else{this.fromString(e,d)}}}}function nbi(){return new BigInteger(null)}function am1(f,a,b,e,h,g){while(--g>=0){var d=a*this[f++]+b[e]+h;h=Math.floor(d/67108864);b[e++]=d&67108863}return h}function am2(f,q,r,e,o,a){var k=q&32767,p=q>>15;while(--a>=0){var d=this[f]&32767;var g=this[f++]>>15;var b=p*d+g*k;d=k*d+((b&32767)<<15)+r[e]+(o&1073741823);o=(d>>>30)+(b>>>15)+p*g+(o>>>30);r[e++]=d&1073741823}return o}function am3(f,q,r,e,o,a){var k=q&16383,p=q>>14;while(--a>=0){var d=this[f]&16383;var g=this[f++]>>14;var b=p*d+g*k;d=k*d+((b&16383)<<14)+r[e]+o;o=(d>>28)+(b>>14)+p*g;r[e++]=d&268435455}return o}BigInteger.prototype.am=am3;dbits=28;BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--a){b[a]=this[a]}b.t=this.t;b.s=this.s}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0){this[0]=a}else{if(a<-1){this[0]=a+DV}else{this.t=0}}}function nbv(a){var b=nbi();b.fromInt(a);return b}function bnpFromString(h,c){var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==256){e=8}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{this.fromRadix(h,c);return}}}}}}this.t=0;this.s=0;var g=h.length,d=false,f=0;while(--g>=0){var a=(e==8)?h[g]&255:intAt(h,g);if(a<0){if(h.charAt(g)=="-"){d=true}continue}d=false;if(f==0){this[this.t++]=a}else{if(f+e>this.DB){this[this.t-1]|=(a&((1<<(this.DB-f))-1))<>(this.DB-f))}else{this[this.t-1]|=a<=this.DB){f-=this.DB}}if(e==8&&(h[0]&128)!=0){this.s=-1;if(f>0){this[this.t-1]|=((1<<(this.DB-f))-1)<0&&this[this.t-1]==a){--this.t}}function bnToString(c){if(this.s<0){return"-"+this.negate().toString(c)}var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{return this.toRadix(c)}}}}}var g=(1<0){if(j>j)>0){a=true;h=int2char(l)}while(f>=0){if(j>(j+=this.DB-e)}else{l=(this[f]>>(j-=e))&g;if(j<=0){j+=this.DB;--f}}if(l>0){a=true}if(a){h+=int2char(l)}}}return a?h:"0"}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return(this.s<0)?this.negate():this}function bnCompareTo(b){var d=this.s-b.s;if(d!=0){return d}var c=this.t;d=c-b.t;if(d!=0){return d}while(--c>=0){if((d=this[c]-b[c])!=0){return d}}return 0}function nbits(a){var c=1,b;if((b=a>>>16)!=0){a=b;c+=16}if((b=a>>8)!=0){a=b;c+=8}if((b=a>>4)!=0){a=b;c+=4}if((b=a>>2)!=0){a=b;c+=2}if((b=a>>1)!=0){a=b;c+=1}return c}function bnBitLength(){if(this.t<=0){return 0}return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM))}function bnpDLShiftTo(c,b){var a;for(a=this.t-1;a>=0;--a){b[a+c]=this[a]}for(a=c-1;a>=0;--a){b[a]=0}b.t=this.t+c;b.s=this.s}function bnpDRShiftTo(c,b){for(var a=c;a=0;--d){e[d+f+1]=(this[d]>>a)|h;h=(this[d]&g)<=0;--d){e[d]=0}e[f]=h;e.t=this.t+f+1;e.s=this.s;e.clamp()}function bnpRShiftTo(g,d){d.s=this.s;var e=Math.floor(g/this.DB);if(e>=this.t){d.t=0;return}var b=g%this.DB;var a=this.DB-b;var f=(1<>b;for(var c=e+1;c>b}if(b>0){d[this.t-e-1]|=(this.s&f)<>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g-=d.s}f.s=(g<0)?-1:0;if(g<-1){f[e++]=this.DV+g}else{if(g>0){f[e++]=g}}f.t=e;f.clamp()}function bnpMultiplyTo(c,e){var b=this.abs(),f=c.abs();var d=b.t;e.t=d+f.t;while(--d>=0){e[d]=0}for(d=0;d=0){d[b]=0}for(b=0;b=a.DV){d[b+a.t]-=a.DV;d[b+a.t+1]=1}}if(d.t>0){d[d.t-1]+=a.am(b,a[b],d,2*b,0,1)}d.s=0;d.clamp()}function bnpDivRemTo(n,h,g){var w=n.abs();if(w.t<=0){return}var k=this.abs();if(k.t0){w.lShiftTo(v,d);k.lShiftTo(v,g)}else{w.copyTo(d);k.copyTo(g)}var p=d.t;var b=d[p-1];if(b==0){return}var o=b*(1<1)?d[p-2]>>this.F2:0);var A=this.FV/o,z=(1<=0){g[g.t++]=1;g.subTo(f,g)}BigInteger.ONE.dlShiftTo(p,f);f.subTo(d,d);while(d.t=0){var c=(g[--u]==b)?this.DM:Math.floor(g[u]*A+(g[u-1]+x)*z);if((g[u]+=d.am(0,c,g,s,0,p))0){g.rShiftTo(v,g)}if(a<0){BigInteger.ZERO.subTo(g,g)}}function bnMod(b){var c=nbi();this.abs().divRemTo(b,null,c);if(this.s<0&&c.compareTo(BigInteger.ZERO)>0){b.subTo(c,c)}return c}function Classic(a){this.m=a}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0){return a.mod(this.m)}else{return a}}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1){return 0}var a=this[0];if((a&1)==0){return 0}var b=a&3;b=(b*(2-(a&15)*b))&15;b=(b*(2-(a&255)*b))&255;b=(b*(2-(((a&65535)*b)&65535)))&65535;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0){this.m.subTo(b,b)}return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}function montReduce(a){while(a.t<=this.mt2){a[a.t++]=0}for(var c=0;c>15)*this.mpl)&this.um)<<15))&a.DM;b=c+this.m.t;a[b]+=this.m.am(0,d,a,c,0,this.m.t);while(a[b]>=a.DV){a[b]-=a.DV;a[++b]++}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0}function bnpExp(h,j){if(h>4294967295||h<1){return BigInteger.ONE}var f=nbi(),a=nbi(),d=j.convert(this),c=nbits(h)-1;d.copyTo(f);while(--c>=0){j.sqrTo(f,a);if((h&(1<0){j.mulTo(a,d,f)}else{var b=f;f=a;a=b}}return j.revert(f)}function bnModPowInt(b,a){var c;if(b<256||a.isEven()){c=new Classic(a)}else{c=new Montgomery(a)}return this.exp(b,c)}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;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;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);function bnClone(){var a=nbi();this.copyTo(a);return a}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}}}return((this[1]&((1<<(32-this.DB))-1))<>24}function bnShortValue(){return(this.t==0)?this.s:(this[0]<<16)>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){if(this.s<0){return -1}else{if(this.t<=0||(this.t==1&&this[0]<=0)){return 0}else{return 1}}}function bnpToRadix(c){if(c==null){c=10}if(this.signum()==0||c<2||c>36){return"0"}var f=this.chunkSize(c);var e=Math.pow(c,f);var i=nbv(e),j=nbi(),h=nbi(),g="";this.divRemTo(i,j,h);while(j.signum()>0){g=(e+h.intValue()).toString(c).substr(1)+g;j.divRemTo(i,j,h)}return h.intValue().toString(c)+g}function bnpFromRadix(m,h){this.fromInt(0);if(h==null){h=10}var f=this.chunkSize(h);var g=Math.pow(h,f),e=false,a=0,l=0;for(var c=0;c=f){this.dMultiply(g);this.dAddOffset(l,0);a=0;l=0}}if(a>0){this.dMultiply(Math.pow(h,a));this.dAddOffset(l,0)}if(e){BigInteger.ZERO.subTo(this,this)}}function bnpFromNumber(f,e,h){if("number"==typeof e){if(f<2){this.fromInt(1)}else{this.fromNumber(f,h);if(!this.testBit(f-1)){this.bitwiseTo(BigInteger.ONE.shiftLeft(f-1),op_or,this)}if(this.isEven()){this.dAddOffset(1,0)}while(!this.isProbablePrime(e)){this.dAddOffset(2,0);if(this.bitLength()>f){this.subTo(BigInteger.ONE.shiftLeft(f-1),this)}}}}else{var d=new Array(),g=f&7;d.length=(f>>3)+1;e.nextBytes(d);if(g>0){d[0]&=((1<0){if(e>e)!=(this.s&this.DM)>>e){c[a++]=f|(this.s<<(this.DB-e))}while(b>=0){if(e<8){f=(this[b]&((1<>(e+=this.DB-8)}else{f=(this[b]>>(e-=8))&255;if(e<=0){e+=this.DB;--b}}if((f&128)!=0){f|=-256}if(a==0&&(this.s&128)!=(f&128)){++a}if(a>0||f!=this.s){c[a++]=f}}}return c}function bnEquals(b){return(this.compareTo(b)==0)}function bnMin(b){return(this.compareTo(b)<0)?this:b}function bnMax(b){return(this.compareTo(b)>0)?this:b}function bnpBitwiseTo(c,h,e){var d,g,b=Math.min(c.t,this.t);for(d=0;d>=16;b+=16}if((a&255)==0){a>>=8;b+=8}if((a&15)==0){a>>=4;b+=4}if((a&3)==0){a>>=2;b+=2}if((a&1)==0){++b}return b}function bnGetLowestSetBit(){for(var a=0;a=this.t){return(this.s!=0)}return((this[a]&(1<<(b%this.DB)))!=0)}function bnpChangeBit(c,b){var a=BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a,b,a);return a}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}function bnpAddTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g+=d.s}f.s=(g<0)?-1:0;if(g>0){f[e++]=g}else{if(g<-1){f[e++]=this.DV+g}}f.t=e;f.clamp()}function bnAdd(b){var c=nbi();this.addTo(b,c);return c}function bnSubtract(b){var c=nbi();this.subTo(b,c);return c}function bnMultiply(b){var c=nbi();this.multiplyTo(b,c);return c}function bnDivide(b){var c=nbi();this.divRemTo(b,c,null);return c}function bnRemainder(b){var c=nbi();this.divRemTo(b,null,c);return c}function bnDivideAndRemainder(b){var d=nbi(),c=nbi();this.divRemTo(b,d,c);return new Array(d,c)}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(b,a){while(this.t<=a){this[this.t++]=0}this[a]+=b;while(this[a]>=this.DV){this[a]-=this.DV;if(++a>=this.t){this[this.t++]=0}++this[a]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,c,b){a.multiplyTo(c,b)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp())}function bnpMultiplyLowerTo(b,f,e){var d=Math.min(this.t+b.t,f);e.s=0;e.t=d;while(d>0){e[--d]=0}var c;for(c=e.t-this.t;d=0){d[c]=0}for(c=Math.max(e-this.t,0);c2*this.m.t){return a.mod(this.m)}else{if(a.compareTo(this.m)<0){return a}else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}}function barrettRevert(a){return a}function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1){a.t=this.m.t+1;a.clamp()}this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(a.compareTo(this.r2)<0){a.dAddOffset(1,this.m.t+1)}a.subTo(this.r2,a);while(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Barrett.prototype.convert=barrettConvert;Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;function bnModPow(q,f){var o=q.bitLength(),h,b=nbv(1),v;if(o<=0){return b}else{if(o<18){h=1}else{if(o<48){h=3}else{if(o<144){h=4}else{if(o<768){h=5}else{h=6}}}}}if(o<8){v=new Classic(f)}else{if(f.isEven()){v=new Barrett(f)}else{v=new Montgomery(f)}}var p=new Array(),d=3,s=h-1,a=(1<1){var A=nbi();v.sqrTo(p[1],A);while(d<=a){p[d]=nbi();v.mulTo(A,p[d-2],p[d]);d+=2}}var l=q.t-1,x,u=true,c=nbi(),y;o=nbits(q[l])-1;while(l>=0){if(o>=s){x=(q[l]>>(o-s))&a}else{x=(q[l]&((1<<(o+1))-1))<<(s-o);if(l>0){x|=q[l-1]>>(this.DB+o-s)}}d=h;while((x&1)==0){x>>=1;--d}if((o-=d)<0){o+=this.DB;--l}if(u){p[x].copyTo(b);u=false}else{while(d>1){v.sqrTo(b,c);v.sqrTo(c,b);d-=2}if(d>0){v.sqrTo(b,c)}else{y=b;b=c;c=y}v.mulTo(c,p[x],b)}while(l>=0&&(q[l]&(1<0){b.rShiftTo(f,b);h.rShiftTo(f,h)}while(b.signum()>0){if((d=b.getLowestSetBit())>0){b.rShiftTo(d,b)}if((d=h.getLowestSetBit())>0){h.rShiftTo(d,h)}if(b.compareTo(h)>=0){b.subTo(h,b);b.rShiftTo(1,b)}else{h.subTo(b,h);h.rShiftTo(1,h)}}if(f>0){h.lShiftTo(f,h)}return h}function bnpModInt(e){if(e<=0){return 0}var c=this.DV%e,b=(this.s<0)?e-1:0;if(this.t>0){if(c==0){b=this[0]%e}else{for(var a=this.t-1;a>=0;--a){b=(c*b+this[a])%e}}}return b}function bnModInverse(f){var j=f.isEven();if((this.isEven()&&j)||f.signum()==0){return BigInteger.ZERO}var i=f.clone(),h=this.clone();var g=nbv(1),e=nbv(0),l=nbv(0),k=nbv(1);while(i.signum()!=0){while(i.isEven()){i.rShiftTo(1,i);if(j){if(!g.isEven()||!e.isEven()){g.addTo(this,g);e.subTo(f,e)}g.rShiftTo(1,g)}else{if(!e.isEven()){e.subTo(f,e)}}e.rShiftTo(1,e)}while(h.isEven()){h.rShiftTo(1,h);if(j){if(!l.isEven()||!k.isEven()){l.addTo(this,l);k.subTo(f,k)}l.rShiftTo(1,l)}else{if(!k.isEven()){k.subTo(f,k)}}k.rShiftTo(1,k)}if(i.compareTo(h)>=0){i.subTo(h,i);if(j){g.subTo(l,g)}e.subTo(k,e)}else{h.subTo(i,h);if(j){l.subTo(g,l)}k.subTo(e,k)}}if(h.compareTo(BigInteger.ONE)!=0){return BigInteger.ZERO}if(k.compareTo(f)>=0){return k.subtract(f)}if(k.signum()<0){k.addTo(f,k)}else{return k}if(k.signum()<0){return k.add(f)}else{return k}}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];function bnIsProbablePrime(e){var d,b=this.abs();if(b.t==1&&b[0]<=lowprimes[lowprimes.length-1]){for(d=0;d>1;if(f>lowprimes.length){f=lowprimes.length}var b=nbi();for(var e=0;e>>6)&31)|192);h.push(((c>>>0)&63)|128)}else{if((c&63488)!=55296){h.push(((c>>>12)&15)|224);h.push(((c>>>6)&63)|128);h.push(((c>>>0)&63)|128)}else{if(((c&64512)==55296)&&((a&64512)==56320)){d=((a&63)|((c&63)<<10))+65536;h.push(((d>>>18)&7)|240);h.push(((d>>>12)&63)|128);h.push(((d>>>6)&63)|128);h.push(((d>>>0)&63)|128);e++}else{}}}}if(h.length>3){g.push((h.shift()<<0)|(h.shift()<<8)|(h.shift()<<16)|(h.shift()<<24))}}return g};var isaac=(function(){var d=Array(256),e=0,i=0,c=0,a=Array(256),b=0;f(Math.random()*4294967295);function l(m,p){var o=(m&65535)+(p&65535);var n=(m>>>16)+(p>>>16)+(o>>>16);return(n<<16)|(o&65535)}function h(){e=i=c=0;for(var m=0;m<256;++m){d[m]=a[m]=0}b=0}function f(x){var w,v,u,t,r,p,o,n,m;w=v=u=t=r=p=o=n=2654435769;if(x&&typeof(x)==="string"){x=x.toIntArray()}if(x&&typeof(x)==="number"){x=[x]}if(x instanceof Array){h();for(m=0;m>>2;r=l(r,v);u=l(u,t);u^=t<<8;p=l(p,u);t=l(t,r);t^=r>>>16;o=l(o,t);r=l(r,p);r^=p<<10;n=l(n,r);p=l(p,o);p^=o>>>4;w=l(w,p);o=l(o,n);o^=n<<8;v=l(v,o);n=l(n,w);n^=w>>>9;u=l(u,n);w=l(w,v)}for(m=0;m<4;m++){q()}for(m=0;m<256;m+=8){if(x){w=l(w,a[m+0]);v=l(v,a[m+1]);u=l(u,a[m+2]);t=l(t,a[m+3]);r=l(r,a[m+4]);p=l(p,a[m+5]);o=l(o,a[m+6]);n=l(n,a[m+7])}q();d[m+0]=w;d[m+1]=v;d[m+2]=u;d[m+3]=t;d[m+4]=r;d[m+5]=p;d[m+6]=o;d[m+7]=n}if(x){for(m=0;m<256;m+=8){w=l(w,d[m+0]);v=l(v,d[m+1]);u=l(u,d[m+2]);t=l(t,d[m+3]);r=l(r,d[m+4]);p=l(p,d[m+5]);o=l(o,d[m+6]);n=l(n,d[m+7]);q();d[m+0]=w;d[m+1]=v;d[m+2]=u;d[m+3]=t;d[m+4]=r;d[m+5]=p;d[m+6]=o;d[m+7]=n}}k();b=256}function k(q){var o,m,p;q=(q&&typeof(q)==="number")?Math.abs(Math.floor(q)):1;while(q--){c=l(c,1);i=l(i,c);for(o=0;o<256;o++){switch(o&3){case 0:e^=e<<13;break;case 1:e^=e>>>6;break;case 2:e^=e<<2;break;case 3:e^=e>>>16;break}e=l(d[(o+128)&255],e);m=d[o];d[o]=p=l(d[(m>>>2)&255],l(e,i));a[o]=i=l(d[(p>>>10)&255],m)}}}function j(){if(!b--){k();b=255}return a[b]}function g(){return{a:e,b:i,c:c,m:d,r:a}}return{reset:h,seed:f,prng:k,rand:j,internals:g}})();isaac.random=function(){return 0.5+this.rand()*2.3283064365386963e-10}; 89 | var random16byteHex=(function(){function c(){if(typeof(window)!="undefined"&&window.crypto&&window.crypto.getRandomValues){return true}else{if(typeof(window)!="undefined"&&window.msCrypto&&window.msCrypto.getRandomValues){return true}else{return false}}}var e=c();function b(){if(e){return false}var i=+(new Date())+":"+Math.random();if(typeof(window)!="undefined"&&window.cookie){i+=document.cookie}var g=CryptoJS.SHA256||CryptoJS.SHA1;isaac.seed(g(i));return true}var a=b();function d(){var j=4;var n;if(e){var m=window.crypto||window.msCrypto;n=new Int32Array(j);m.getRandomValues(n)}else{var h=+(new Date());var l=h%50;isaac.prng(1+l);n=new Array();for(var k=0;k 153 | * 154 | * Computes x = H(s | H(I | ":" | P)) 155 | *

Uses string concatenation before hashing. 156 | *

Specification RFC 2945 157 | * 158 | * @param salt The salt 's'. Must not be null or empty. 159 | * @param identity The user identity/email 'I'. Must not be null or empty. 160 | * @param password The user password 'P'. Must not be null or empty 161 | * @return The resulting 'x' value as BigInteger. 162 | */ 163 | this.generateX = function(salt, identity, password) { 164 | this.check(salt, "salt"); 165 | this.check(identity, "identity"); 166 | this.check(password, "password"); 167 | //console.log("js salt:"+salt); 168 | //console.log("js i:"+identity); 169 | //console.log("js p:"+password); 170 | this.salt = salt; 171 | var hash1 = this.H(identity+':'+password); 172 | 173 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 174 | while (hash1.substring(0, 1) === '0') { 175 | //console.log("stripping leading zero from M1"); 176 | hash1 = hash1.substring(1); 177 | } 178 | 179 | //console.log("js hash1:"+hash1); 180 | //console.log("js salt:"+salt); 181 | var concat = (salt+hash1).toUpperCase(); 182 | //console.log("js concat:"+concat); 183 | var hash = this.H(concat); 184 | 185 | // Java BigInteger math will trim leading zeros so we do likewise 186 | while (hash.substring(0, 1) === '0') { 187 | //console.log("stripping leading zero from M1"); 188 | hash = hash.substring(1); 189 | } 190 | 191 | //console.log("js hash:"+hash) 192 | //console.log("js x before modN "+this.fromHex(hash)); 193 | this.x = this.fromHex(hash).mod(this.N()); 194 | return this.x; 195 | }; 196 | 197 | /** 198 | * Computes the session key S = (B - k * g^x) ^ (a + u * x) (mod N) 199 | * from client-side parameters. 200 | * 201 | *

Specification: RFC 5054 202 | * 203 | * @param N The prime parameter 'N'. Must not be {@code null}. 204 | * @param g The generator parameter 'g'. Must not be {@code null}. 205 | * @param k The SRP-6a multiplier 'k'. Must not be {@code null}. 206 | * @param x The 'x' value, see {@link #computeX}. Must not be 207 | * {@code null}. 208 | * @param u The random scrambling parameter 'u'. Must not be 209 | * {@code null}. 210 | * @param a The private client value 'a'. Must not be {@code null}. 211 | * @param B The public server value 'B'. Must note be {@code null}. 212 | * 213 | * @return The resulting session key 'S'. 214 | */ 215 | this.computeSessionKey = function(k, x, u, a, B) { 216 | this.check(k, "k"); 217 | this.check(x, "x"); 218 | this.check(u, "u"); 219 | this.check(a, "a"); 220 | this.check(B, "B"); 221 | 222 | var exp = u.multiply(x).add(a); 223 | var tmp = this.g().modPow(x, this.N()).multiply(k); 224 | return B.subtract(tmp).modPow(exp, this.N()); 225 | }; 226 | } 227 | 228 | // public helper 229 | SRP6JavascriptClientSession.prototype.toHex = function(n) { 230 | "use strict"; 231 | return n.toString(16); 232 | }; 233 | 234 | // public helper 235 | /* jshint ignore:start */ 236 | SRP6JavascriptClientSession.prototype.fromHex = function(s) { 237 | "use strict"; 238 | return new BigInteger(""+s, 16); // jdk1.7 rhino requires string concat 239 | }; 240 | /* jshint ignore:end */ 241 | 242 | // public helper to hide BigInteger from the linter 243 | /* jshint ignore:start */ 244 | SRP6JavascriptClientSession.prototype.BigInteger = function(string, radix) { 245 | "use strict"; 246 | return new BigInteger(""+string, radix); // jdk1.7 rhino requires string concat 247 | }; 248 | /* jshint ignore:end */ 249 | 250 | 251 | // public getter of the current workflow state. 252 | SRP6JavascriptClientSession.prototype.getState = function() { 253 | "use strict"; 254 | return this.state; 255 | }; 256 | 257 | /** 258 | * Gets the shared sessionkey 259 | * 260 | * @param hash Boolean With to return the large session key 'S' or 'K=H(S)' 261 | */ 262 | SRP6JavascriptClientSession.prototype.getSessionKey = function(hash) { 263 | "use strict"; 264 | if( this.S === null ) { 265 | return null; 266 | } 267 | this.SS = this.toHex(this.S); 268 | if(typeof hash !== 'undefined' && hash === false){ 269 | return this.SS; 270 | } else { 271 | if( this.K === null ) { 272 | this.K = this.H(this.SS); 273 | } 274 | return this.K; 275 | } 276 | }; 277 | 278 | // public getter 279 | SRP6JavascriptClientSession.prototype.getUserID = function() { 280 | "use strict"; 281 | return this.I; 282 | }; 283 | 284 | /* 285 | * Generates a new salt 's'. This takes the current time, a pure browser random value, and an optional server generated random, and hashes them all together. 286 | * This should ensure that the salt is unique to every use registration regardless of the quality of the browser random generation routine. 287 | * Note that this method is optional as you can choose to always generate the salt at the server and sent it to the browser as it is a public value. 288 | *

289 | * Always add a unique constraint to where you store this in your database to force that all users on the system have a unique salt. 290 | * 291 | * @param opionalServerSalt An optional server salt which is hashed into a locally generated random number. Can be left undefined when calling this function. 292 | * @return 's' Salt as a hex string of length driven by the bit size of the hash algorithm 'H'. 293 | */ 294 | SRP6JavascriptClientSession.prototype.generateRandomSalt = function(opionalServerSalt) { 295 | "use strict"; 296 | var s = null; 297 | 298 | /* jshint ignore:start */ 299 | s = random16byteHex.random(); 300 | /* jshint ignore:end */ 301 | 302 | // if you invoke without passing the string parameter the '+' operator uses 'undefined' so no nullpointer risk here 303 | var ss = this.H((new Date())+':'+opionalServerSalt+':'+s); 304 | return ss; 305 | }; 306 | 307 | /* 308 | * Generates a new verifier 'v' from the specified parameters. 309 | *

The verifier is computed as v = g^x (mod N). 310 | *

Specification RFC 2945 311 | * 312 | * @param salt The salt 's'. Must not be null or empty. 313 | * @param identity The user identity/email 'I'. Must not be null or empty. 314 | * @param password The user password 'P'. Must not be null or empty 315 | * @return The resulting verifier 'v' as a hex string 316 | */ 317 | SRP6JavascriptClientSession.prototype.generateVerifier = function(salt, identity, password) { 318 | "use strict"; 319 | //console.log("SRP6JavascriptClientSession.prototype.generateVerifier"); 320 | // no need to check the parameters as generateX will do this 321 | var x = this.generateX(salt, identity, password); 322 | //console.log("js x: "+this.toHex(x)); 323 | this.v = this.g().modPow(x, this.N()); 324 | //console.log("js v: "+this.toHex(this.v)); 325 | return this.toHex(this.v); 326 | }; 327 | 328 | /** 329 | * Records the identity 'I' and password 'P' of the authenticating user. 330 | * The session is incremented to {@link State#STEP_1}. 331 | *

Argument origin: 332 | *

    333 | *
  • From user: user identity 'I' and password 'P'. 334 | *
335 | * @param userID The identity 'I' of the authenticating user, UTF-8 336 | * encoded. Must not be {@code null} or empty. 337 | * @param password The user password 'P', UTF-8 encoded. Must not be 338 | * {@code null}. 339 | * @throws IllegalStateException If the method is invoked in a state 340 | * other than {@link State#INIT}. 341 | */ 342 | SRP6JavascriptClientSession.prototype.step1 = function(identity, password) { 343 | "use strict"; 344 | //console.log("SRP6JavascriptClientSession.prototype.step1"); 345 | //console.log("N: "+this.N()); 346 | //console.log("g: "+this.g()); 347 | //console.log("k: "+this.toHex(this.k)); 348 | this.check(identity, "identity"); 349 | this.check(password, "password"); 350 | this.I = identity; 351 | this.P = password; 352 | if( this.state !== this.INIT ) { 353 | throw new Error("IllegalStateException not in state INIT"); 354 | } 355 | this.state = this.STEP_1; 356 | }; 357 | 358 | /** 359 | * Computes the random scrambling parameter u = H(A | B) 360 | *

Specification RFC 2945 361 | * Will throw an error if 362 | * 363 | * @param A The public client value 'A'. Must not be {@code null}. 364 | * @param B The public server value 'B'. Must not be {@code null}. 365 | * 366 | * @return The resulting 'u' value. 367 | */ 368 | SRP6JavascriptClientSession.prototype.computeU = function(Astr, Bstr) { 369 | "use strict"; 370 | //console.log("SRP6JavascriptClientSession.prototype.computeU"); 371 | this.check(Astr, "Astr"); 372 | this.check(Bstr, "Bstr"); 373 | /* jshint ignore:start */ 374 | var output = this.H(Astr+Bstr); 375 | //console.log("js raw u:"+output); 376 | var u = new BigInteger(""+output,16); 377 | //console.log("js u:"+this.toHex(u)); 378 | if( BigInteger.ZERO.equals(u) ) { 379 | throw new Error("SRP6Exception bad shared public value 'u' as u==0"); 380 | } 381 | return u; 382 | /* jshint ignore:end */ 383 | }; 384 | 385 | SRP6JavascriptClientSession.prototype.random16byteHex = function() { 386 | "use strict"; 387 | 388 | var r1 = null; 389 | /* jshint ignore:start */ 390 | r1 = random16byteHex.random(); 391 | /* jshint ignore:end */ 392 | return r1; 393 | }; 394 | 395 | /** 396 | * Generate a random value in the range `[1,N)` using a minimum of 256 random bits. 397 | * 398 | * See specification RFC 5054. 399 | * This method users the best random numbers available. Just in case the random number 400 | * generate in the client web browser is totally buggy it also adds `H(I+":"+salt+":"+time())` 401 | * to the generated random number. 402 | * @param N The safe prime. 403 | */ 404 | SRP6JavascriptClientSession.prototype.randomA = function(N) { 405 | "use strict"; 406 | 407 | //console.log("N:"+N); 408 | 409 | // our ideal number of random bits to use for `a` as long as its bigger than 256 bits 410 | var hexLength = this.toHex(N).length; 411 | 412 | var ZERO = this.BigInteger("0", 10); 413 | var ONE = this.BigInteger("1", 10); 414 | 415 | var r = ZERO; 416 | 417 | // loop until we don't have a ZERO value. we would have to generate exactly N to loop so very rare. 418 | while(ZERO.equals(r)){ 419 | // in theory we get 256 bits of good random numbers here 420 | var rstr = this.random16byteHex() + this.random16byteHex(); 421 | 422 | //console.log("rstr:"+rstr); 423 | 424 | // add more random bytes until we are at least as large as N and ignore any overshoot 425 | while( rstr.length < hexLength ) { 426 | rstr = rstr + this.random16byteHex(); 427 | } 428 | 429 | //console.log("rstr:"+rstr); 430 | 431 | // we now have a random just at lest 256 bits but typically more bits than N for large N 432 | var rBi = this.BigInteger(rstr, 16); 433 | 434 | //console.log("rBi:"+rBi); 435 | 436 | // this hashes the time in ms such that we wont get repeated numbers for successive attempts 437 | // it also hashes the salt which can itself be salted by a server strong random which protects 438 | // against rainbow tables. it also hashes the user identity which is unique to each user 439 | // to protect against having simply no good random numbers anywhere 440 | var oneTimeBi = this.BigInteger(this.H(this.I+":"+this.salt+':'+(new Date()).getTime()), 16); 441 | 442 | //console.log("oneTimeBi:"+oneTimeBi); 443 | 444 | // here we add the "one time" hashed time number to our random number to the random number 445 | // this protected against a buggy browser random number generated generating a constant value 446 | // we mod(N) to wrap to the range [0,N) then loop if we get 0 to give [1,N) 447 | // mod(N) is broken due to buggy library code so we workaround with modPow(1,N) 448 | r = (oneTimeBi.add(rBi)).modPow(ONE, N); 449 | } 450 | 451 | //console.log("r:"+r); 452 | 453 | // the result will in the range [1,N) using more random bits than size N 454 | return r; 455 | }; 456 | 457 | /** 458 | * Receives the password salt 's' and public value 'B' from the server. 459 | * The SRP-6a crypto parameters are also set. The session is incremented 460 | * to {@link State#STEP_2}. 461 | *

Argument origin: 462 | *

    463 | *
  • From server: password salt 's', public value 'B'. 464 | *
  • Pre-agreed: crypto parameters prime 'N', 465 | * generator 'g' and hash function 'H'. 466 | *
467 | * @param s The password salt 's' as a hex string. Must not be {@code null}. 468 | * @param B The public server value 'B' as a hex string. Must not be {@code null}. 469 | * @param k k is H(N,g) with padding by the server. Must not be {@code null}. 470 | * @return The client credentials consisting of the client public key 471 | * 'A' and the client evidence message 'M1'. 472 | * @throws IllegalStateException If the method is invoked in a state 473 | * other than {@link State#STEP_1}. 474 | * @throws SRP6Exception If the public server value 'B' is invalid. 475 | */ 476 | SRP6JavascriptClientSession.prototype.step2 = function(s, BB) { 477 | "use strict"; 478 | 479 | //console.log("SRP6JavascriptClientSession.prototype.step2"); 480 | 481 | this.check(s, "s"); 482 | //console.log("s:" + s); 483 | this.check(BB, "BB"); 484 | //console.log("BB:" + BB); 485 | 486 | if( this.state !== this.STEP_1 ) { 487 | throw new Error("IllegalStateException not in state STEP_1"); 488 | } 489 | 490 | // this is checked when passed to computeSessionKey 491 | this.B = this.fromHex(BB); 492 | 493 | var ZERO = null; 494 | 495 | /* jshint ignore:start */ 496 | ZERO = BigInteger.ZERO; 497 | /* jshint ignore:end */ 498 | 499 | if (this.B.mod(this.N()).equals(ZERO)) { 500 | throw new Error("SRP6Exception bad server public value 'B' as B == 0 (mod N)"); 501 | } 502 | 503 | //console.log("k:" + this.k); 504 | 505 | // this is checked when passed to computeSessionKey 506 | var x = this.generateX(s, this.I, this.P); 507 | //console.log("x:" + x); 508 | 509 | // blank the password as there is no reason to keep it around in memory. 510 | this.P = null; 511 | 512 | //console.log("N:"+this.toHex(this.N).toString(16)); 513 | 514 | this.a = this.randomA(this.N); 515 | 516 | //console.log("a:" + this.toHex(this.a)); 517 | 518 | this.A = this.g().modPow(this.a, this.N()); 519 | //console.log("A:" + this.toHex(this.A)); 520 | this.check(this.A, "A"); 521 | 522 | this.u = this.computeU(this.A.toString(16),BB); 523 | //console.log("u:" + this.u); 524 | 525 | this.S = this.computeSessionKey(this.k, x, this.u, this.a, this.B); 526 | this.check(this.S, "S"); 527 | 528 | //console.log("jsU:" + this.toHex(this.u)); 529 | //console.log("jsS:" + this.toHex(this.S)); 530 | 531 | var AA = this.toHex(this.A); 532 | 533 | // console.log("cAA:"+AA); 534 | // console.log("cBB:"+BB); 535 | // console.log("cSS:"+this.toHex(this.S)); 536 | 537 | this.M1str = this.H(AA+BB+this.toHex(this.S)); 538 | this.check(this.M1str, "M1str"); 539 | 540 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 541 | while (this.M1str.substring(0, 1) === '0') { 542 | //console.log("stripping leading zero from M1"); 543 | this.M1str = this.M1str.substring(1); 544 | } 545 | 546 | //console.log("M1str:" + this.M1str); 547 | 548 | //console.log("js ABS:" + AA+BB+this.toHex(this.S)); 549 | //console.log("js A:" + AA); 550 | //console.log("js B:" + BB); 551 | //console.log("js v:" + this.v); 552 | //console.log("js u:" + this.u); 553 | //console.log("js A:" + this.A); 554 | //console.log("js b:" + this.B); 555 | //console.log("js S:" + this.S); 556 | //console.log("js S:" + this.toHex(this.S)); 557 | //console.log("js M1:" + this.M1str); 558 | 559 | this.state = this.STEP_2; 560 | return { A: AA, M1: this.M1str }; 561 | }; 562 | 563 | /** 564 | * Receives the server evidence message 'M1'. The session is incremented 565 | * to {@link State#STEP_3}. 566 | * 567 | *

Argument origin: 568 | *

    569 | *
  • From server: evidence message 'M2'. 570 | *
571 | * @param serverM2 The server evidence message 'M2' as string. Must not be {@code null}. 572 | * @throws IllegalStateException If the method is invoked in a state 573 | * other than {@link State#STEP_2}. 574 | * @throws SRP6Exception If the session has timed out or the 575 | * server evidence message 'M2' is 576 | * invalid. 577 | */ 578 | SRP6JavascriptClientSession.prototype.step3 = function(M2) { 579 | "use strict"; 580 | this.check(M2); 581 | //console.log("SRP6JavascriptClientSession.prototype.step3"); 582 | 583 | // Check current state 584 | if (this.state !== this.STEP_2) 585 | throw new Error("IllegalStateException State violation: Session must be in STEP_2 state"); 586 | 587 | //console.log("js A:" + this.toHex(this.A)); 588 | //console.log("jsM1:" + this.M1str); 589 | //console.log("js S:" + this.toHex(this.S)); 590 | 591 | var computedM2 = this.H(this.toHex(this.A)+this.M1str+this.toHex(this.S)); 592 | 593 | //console.log("jsServerM2:" + M2); 594 | //console.log("jsClientM2:" + computedM2); 595 | 596 | // server BigInteger math will trim leading zeros so we must do likewise to get a match 597 | while (computedM2.substring(0, 1) === '0') { 598 | //console.log("stripping leading zero from computedM2"); 599 | computedM2 = computedM2.substring(1); 600 | } 601 | 602 | //console.log("server M2:"+M2+"\ncomputedM2:"+computedM2); 603 | if ( ""+computedM2 !== ""+M2) { 604 | throw new Error("SRP6Exception Bad server credentials"); 605 | } 606 | 607 | this.state = this.STEP_3; 608 | 609 | return true; 610 | }; 611 | 612 | /** 613 | * For the node.js world we optionally export a factory closure which takes SRP parameters and returns a SRP6JavascriptClientSession class using SHA256 and the parameters bound to it. 614 | * 615 | * @param {string} N_base10 Safe prime N as decimal string. 616 | * @param {string} g_base10 Generator g as decimal string. 617 | * @param {string} k_base16 Symmetry braking k as hexidecimal string. See https://bitbucket.org/simon_massey/thinbus-srp-js/overview 618 | */ 619 | /* jshint ignore:start */ 620 | if( typeof module !== 'undefined') 621 | if( typeof module.exports !== 'undefined' ) 622 | module.exports = function srpClientFactory (N_base10, g_base10, k_base16) { 623 | 624 | function SRP6JavascriptClientSessionSHA256(){ } 625 | 626 | SRP6JavascriptClientSessionSHA256.prototype = new SRP6JavascriptClientSession(); 627 | 628 | SRP6JavascriptClientSessionSHA256.prototype.N = function() { 629 | return new BigInteger(N_base10, 10); 630 | } 631 | 632 | SRP6JavascriptClientSessionSHA256.prototype.g = function() { 633 | return new BigInteger(g_base10, 10); 634 | } 635 | 636 | SRP6JavascriptClientSessionSHA256.prototype.H = function (x) { 637 | return CryptoJS.SHA256(x).toString().toLowerCase(); 638 | } 639 | 640 | SRP6JavascriptClientSessionSHA256.prototype.k = new BigInteger(k_base16, 16); 641 | 642 | // return the new session class 643 | return SRP6JavascriptClientSessionSHA256; 644 | } 645 | /* jshint ignore:end */ 646 | 647 | --------------------------------------------------------------------------------