0) {",
" if(p < this.DB && (d = this.data[i]>>p) > 0) { m = true; r = int2char(d); }",
" while(i >= 0) {",
" if(p < k) {",
" d = (this.data[i]&((1<>(p+=this.DB-k);",
" } else {",
" d = (this.data[i]>>(p-=k))&km;",
" if(p <= 0) { p += this.DB; --i; }",
" }",
" if(d > 0) m = true;",
" if(m) r += int2char(d);",
" }",
" }",
" return m?r:\"0\";",
"}",
"",
"// (public) -this",
"function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }",
"",
"// (public) |this|",
"function bnAbs() { return (this.s<0)?this.negate():this; }",
"",
"// (public) return + if this > a, - if this < a, 0 if equal",
"function bnCompareTo(a) {",
" var r = this.s-a.s;",
" if(r != 0) return r;",
" var i = this.t;",
" r = i-a.t;",
" if(r != 0) return (this.s<0)?-r:r;",
" while(--i >= 0) if((r=this.data[i]-a.data[i]) != 0) return r;",
" return 0;",
"}",
"",
"// returns bit length of the integer x",
"function nbits(x) {",
" var r = 1, t;",
" if((t=x>>>16) != 0) { x = t; r += 16; }",
" if((t=x>>8) != 0) { x = t; r += 8; }",
" if((t=x>>4) != 0) { x = t; r += 4; }",
" if((t=x>>2) != 0) { x = t; r += 2; }",
" if((t=x>>1) != 0) { x = t; r += 1; }",
" return r;",
"}",
"",
"// (public) return the number of bits in \"this\"",
"function bnBitLength() {",
" if(this.t <= 0) return 0;",
" return this.DB*(this.t-1)+nbits(this.data[this.t-1]^(this.s&this.DM));",
"}",
"",
"// (protected) r = this << n*DB",
"function bnpDLShiftTo(n,r) {",
" var i;",
" for(i = this.t-1; i >= 0; --i) r.data[i+n] = this.data[i];",
" for(i = n-1; i >= 0; --i) r.data[i] = 0;",
" r.t = this.t+n;",
" r.s = this.s;",
"}",
"",
"// (protected) r = this >> n*DB",
"function bnpDRShiftTo(n,r) {",
" for(var i = n; i < this.t; ++i) r.data[i-n] = this.data[i];",
" r.t = Math.max(this.t-n,0);",
" r.s = this.s;",
"}",
"",
"// (protected) r = this << n",
"function bnpLShiftTo(n,r) {",
" var bs = n%this.DB;",
" var cbs = this.DB-bs;",
" var bm = (1<= 0; --i) {",
" r.data[i+ds+1] = (this.data[i]>>cbs)|c;",
" c = (this.data[i]&bm)<= 0; --i) r.data[i] = 0;",
" r.data[ds] = c;",
" r.t = this.t+ds+1;",
" r.s = this.s;",
" r.clamp();",
"}",
"",
"// (protected) r = this >> n",
"function bnpRShiftTo(n,r) {",
" r.s = this.s;",
" var ds = Math.floor(n/this.DB);",
" if(ds >= this.t) { r.t = 0; return; }",
" var bs = n%this.DB;",
" var cbs = this.DB-bs;",
" var bm = (1<>bs;",
" for(var i = ds+1; i < this.t; ++i) {",
" r.data[i-ds-1] |= (this.data[i]&bm)<>bs;",
" }",
" if(bs > 0) r.data[this.t-ds-1] |= (this.s&bm)<>= this.DB;",
" }",
" if(a.t < this.t) {",
" c -= a.s;",
" while(i < this.t) {",
" c += this.data[i];",
" r.data[i++] = c&this.DM;",
" c >>= this.DB;",
" }",
" c += this.s;",
" } else {",
" c += this.s;",
" while(i < a.t) {",
" c -= a.data[i];",
" r.data[i++] = c&this.DM;",
" c >>= this.DB;",
" }",
" c -= a.s;",
" }",
" r.s = (c<0)?-1:0;",
" if(c < -1) r.data[i++] = this.DV+c;",
" else if(c > 0) r.data[i++] = c;",
" r.t = i;",
" r.clamp();",
"}",
"",
"// (protected) r = this * a, r != this,a (HAC 14.12)",
"// \"this\" should be the larger one if appropriate.",
"function bnpMultiplyTo(a,r) {",
" var x = this.abs(), y = a.abs();",
" var i = x.t;",
" r.t = i+y.t;",
" while(--i >= 0) r.data[i] = 0;",
" for(i = 0; i < y.t; ++i) r.data[i+x.t] = x.am(0,y.data[i],r,i,0,x.t);",
" r.s = 0;",
" r.clamp();",
" if(this.s != a.s) BigInteger.ZERO.subTo(r,r);",
"}",
"",
"// (protected) r = this^2, r != this (HAC 14.16)",
"function bnpSquareTo(r) {",
" var x = this.abs();",
" var i = r.t = 2*x.t;",
" while(--i >= 0) r.data[i] = 0;",
" for(i = 0; i < x.t-1; ++i) {",
" var c = x.am(i,x.data[i],r,2*i,0,1);",
" if((r.data[i+x.t]+=x.am(i+1,2*x.data[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {",
" r.data[i+x.t] -= x.DV;",
" r.data[i+x.t+1] = 1;",
" }",
" }",
" if(r.t > 0) r.data[r.t-1] += x.am(i,x.data[i],r,2*i,0,1);",
" r.s = 0;",
" r.clamp();",
"}",
"",
"// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)",
"// r != q, this != m. q or r may be null.",
"function bnpDivRemTo(m,q,r) {",
" var pm = m.abs();",
" if(pm.t <= 0) return;",
" var pt = this.abs();",
" if(pt.t < pm.t) {",
" if(q != null) q.fromInt(0);",
" if(r != null) this.copyTo(r);",
" return;",
" }",
" if(r == null) r = nbi();",
" var y = nbi(), ts = this.s, ms = m.s;",
" var nsh = this.DB-nbits(pm.data[pm.t-1]);\t// normalize modulus",
" if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } else { pm.copyTo(y); pt.copyTo(r); }",
" var ys = y.t;",
" var y0 = y.data[ys-1];",
" if(y0 == 0) return;",
" var yt = y0*(1<1)?y.data[ys-2]>>this.F2:0);",
" var d1 = this.FV/yt, d2 = (1<= 0) {",
" r.data[r.t++] = 1;",
" r.subTo(t,r);",
" }",
" BigInteger.ONE.dlShiftTo(ys,t);",
" t.subTo(y,y);\t// \"negative\" y so we can replace sub with am later",
" while(y.t < ys) y.data[y.t++] = 0;",
" while(--j >= 0) {",
" // Estimate quotient digit",
" var qd = (r.data[--i]==y0)?this.DM:Math.floor(r.data[i]*d1+(r.data[i-1]+e)*d2);",
" if((r.data[i]+=y.am(0,qd,r,j,0,ys)) < qd) {\t// Try it out",
" y.dlShiftTo(j,t);",
" r.subTo(t,r);",
" while(r.data[i] < --qd) r.subTo(t,r);",
" }",
" }",
" if(q != null) {",
" r.drShiftTo(ys,q);",
" if(ts != ms) BigInteger.ZERO.subTo(q,q);",
" }",
" r.t = ys;",
" r.clamp();",
" if(nsh > 0) r.rShiftTo(nsh,r);\t// Denormalize remainder",
" if(ts < 0) BigInteger.ZERO.subTo(r,r);",
"}",
"",
"// (public) this mod a",
"function bnMod(a) {",
" var r = nbi();",
" this.abs().divRemTo(a,null,r);",
" if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);",
" return r;",
"}",
"",
"// Modular reduction using \"classic\" algorithm",
"function Classic(m) { this.m = m; }",
"function cConvert(x) {",
" if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);",
" else return x;",
"}",
"function cRevert(x) { return x; }",
"function cReduce(x) { x.divRemTo(this.m,null,x); }",
"function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }",
"function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }",
"",
"Classic.prototype.convert = cConvert;",
"Classic.prototype.revert = cRevert;",
"Classic.prototype.reduce = cReduce;",
"Classic.prototype.mulTo = cMulTo;",
"Classic.prototype.sqrTo = cSqrTo;",
"",
"// (protected) return \"-1/this % 2^DB\"; useful for Mont. reduction",
"// justification:",
"// xy == 1 (mod m)",
"// xy = 1+km",
"// xy(2-xy) = (1+km)(1-km)",
"// x[y(2-xy)] = 1-k^2m^2",
"// x[y(2-xy)] == 1 (mod m^2)",
"// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2",
"// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.",
"// JS multiply \"overflows\" differently from C/C++, so care is needed here.",
"function bnpInvDigit() {",
" if(this.t < 1) return 0;",
" var x = this.data[0];",
" if((x&1) == 0) return 0;",
" var y = x&3;\t\t// y == 1/x mod 2^2",
" y = (y*(2-(x&0xf)*y))&0xf;\t// y == 1/x mod 2^4",
" y = (y*(2-(x&0xff)*y))&0xff;\t// y == 1/x mod 2^8",
" y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff;\t// y == 1/x mod 2^16",
" // last step - calculate inverse mod DV directly;",
" // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints",
" y = (y*(2-x*y%this.DV))%this.DV;\t\t// y == 1/x mod 2^dbits",
" // we really want the negative inverse, and -DV < y < DV",
" return (y>0)?this.DV-y:-y;",
"}",
"",
"// Montgomery reduction",
"function Montgomery(m) {",
" this.m = m;",
" this.mp = m.invDigit();",
" this.mpl = this.mp&0x7fff;",
" this.mph = this.mp>>15;",
" this.um = (1<<(m.DB-15))-1;",
" this.mt2 = 2*m.t;",
"}",
"",
"// xR mod m",
"function montConvert(x) {",
" var r = nbi();",
" x.abs().dlShiftTo(this.m.t,r);",
" r.divRemTo(this.m,null,r);",
" if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);",
" return r;",
"}",
"",
"// x/R mod m",
"function montRevert(x) {",
" var r = nbi();",
" x.copyTo(r);",
" this.reduce(r);",
" return r;",
"}",
"",
"// x = x/R mod m (HAC 14.32)",
"function montReduce(x) {",
" while(x.t <= this.mt2)\t// pad x so am has enough room later",
" x.data[x.t++] = 0;",
" for(var i = 0; i < this.m.t; ++i) {",
" // faster way of calculating u0 = x.data[i]*mp mod DV",
" var j = x.data[i]&0x7fff;",
" var u0 = (j*this.mpl+(((j*this.mph+(x.data[i]>>15)*this.mpl)&this.um)<<15))&x.DM;",
" // use am to combine the multiply-shift-add into one call",
" j = i+this.m.t;",
" x.data[j] += this.m.am(0,u0,x,i,0,this.m.t);",
" // propagate carry",
" while(x.data[j] >= x.DV) { x.data[j] -= x.DV; x.data[++j]++; }",
" }",
" x.clamp();",
" x.drShiftTo(this.m.t,x);",
" if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);",
"}",
"",
"// r = \"x^2/R mod m\"; x != r",
"function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }",
"",
"// r = \"xy/R mod m\"; x,y != r",
"function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }",
"",
"Montgomery.prototype.convert = montConvert;",
"Montgomery.prototype.revert = montRevert;",
"Montgomery.prototype.reduce = montReduce;",
"Montgomery.prototype.mulTo = montMulTo;",
"Montgomery.prototype.sqrTo = montSqrTo;",
"",
"// (protected) true iff this is even",
"function bnpIsEven() { return ((this.t>0)?(this.data[0]&1):this.s) == 0; }",
"",
"// (protected) this^e, e < 2^32, doing sqr and mul with \"r\" (HAC 14.79)",
"function bnpExp(e,z) {",
" if(e > 0xffffffff || e < 1) return BigInteger.ONE;",
" var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;",
" g.copyTo(r);",
" while(--i >= 0) {",
" z.sqrTo(r,r2);",
" if((e&(1< 0) z.mulTo(r2,g,r);",
" else { var t = r; r = r2; r2 = t; }",
" }",
" return z.revert(r);",
"}",
"",
"// (public) this^e % m, 0 <= e < 2^32",
"function bnModPowInt(e,m) {",
" var z;",
" if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);",
" return this.exp(e,z);",
"}",
"",
"// protected",
"BigInteger.prototype.copyTo = bnpCopyTo;",
"BigInteger.prototype.fromInt = bnpFromInt;",
"BigInteger.prototype.fromString = bnpFromString;",
"BigInteger.prototype.clamp = bnpClamp;",
"BigInteger.prototype.dlShiftTo = bnpDLShiftTo;",
"BigInteger.prototype.drShiftTo = bnpDRShiftTo;",
"BigInteger.prototype.lShiftTo = bnpLShiftTo;",
"BigInteger.prototype.rShiftTo = bnpRShiftTo;",
"BigInteger.prototype.subTo = bnpSubTo;",
"BigInteger.prototype.multiplyTo = bnpMultiplyTo;",
"BigInteger.prototype.squareTo = bnpSquareTo;",
"BigInteger.prototype.divRemTo = bnpDivRemTo;",
"BigInteger.prototype.invDigit = bnpInvDigit;",
"BigInteger.prototype.isEven = bnpIsEven;",
"BigInteger.prototype.exp = bnpExp;",
"",
"// public",
"BigInteger.prototype.toString = bnToString;",
"BigInteger.prototype.negate = bnNegate;",
"BigInteger.prototype.abs = bnAbs;",
"BigInteger.prototype.compareTo = bnCompareTo;",
"BigInteger.prototype.bitLength = bnBitLength;",
"BigInteger.prototype.mod = bnMod;",
"BigInteger.prototype.modPowInt = bnModPowInt;",
"",
"// \"constants\"",
"BigInteger.ZERO = nbv(0);",
"BigInteger.ONE = nbv(1);",
"",
"// jsbn2 lib",
"",
"//Copyright (c) 2005-2009 Tom Wu",
"//All Rights Reserved.",
"//See \"LICENSE\" for details (See jsbn.js for LICENSE).",
"",
"//Extended JavaScript BN functions, required for RSA private ops.",
"",
"//Version 1.1: new BigInteger(\"0\", 10) returns \"proper\" zero",
"",
"//(public)",
"function bnClone() { var r = nbi(); this.copyTo(r); return r; }",
"",
"//(public) return value as integer",
"function bnIntValue() {",
"if(this.s < 0) {",
" if(this.t == 1) return this.data[0]-this.DV;",
" else if(this.t == 0) return -1;",
"} else if(this.t == 1) return this.data[0];",
"else if(this.t == 0) return 0;",
"// assumes 16 < DB < 32",
"return ((this.data[1]&((1<<(32-this.DB))-1))<>24; }",
"",
"//(public) return value as short (assumes DB>=16)",
"function bnShortValue() { return (this.t==0)?this.s:(this.data[0]<<16)>>16; }",
"",
"//(protected) return x s.t. r^x < DV",
"function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }",
"",
"//(public) 0 if this == 0, 1 if this > 0",
"function bnSigNum() {",
"if(this.s < 0) return -1;",
"else if(this.t <= 0 || (this.t == 1 && this.data[0] <= 0)) return 0;",
"else return 1;",
"}",
"",
"//(protected) convert to radix string",
"function bnpToRadix(b) {",
"if(b == null) b = 10;",
"if(this.signum() == 0 || b < 2 || b > 36) return \"0\";",
"var cs = this.chunkSize(b);",
"var a = Math.pow(b,cs);",
"var d = nbv(a), y = nbi(), z = nbi(), r = \"\";",
"this.divRemTo(d,y,z);",
"while(y.signum() > 0) {",
" r = (a+z.intValue()).toString(b).substr(1) + r;",
" y.divRemTo(d,y,z);",
"}",
"return z.intValue().toString(b) + r;",
"}",
"",
"//(protected) convert from radix string",
"function bnpFromRadix(s,b) {",
"this.fromInt(0);",
"if(b == null) b = 10;",
"var cs = this.chunkSize(b);",
"var d = Math.pow(b,cs), mi = false, j = 0, w = 0;",
"for(var i = 0; i < s.length; ++i) {",
" var x = intAt(s,i);",
" if(x < 0) {",
" if(s.charAt(i) == \"-\" && this.signum() == 0) mi = true;",
" continue;",
" }",
" w = b*w+x;",
" if(++j >= cs) {",
" this.dMultiply(d);",
" this.dAddOffset(w,0);",
" j = 0;",
" w = 0;",
" }",
"}",
"if(j > 0) {",
" this.dMultiply(Math.pow(b,j));",
" this.dAddOffset(w,0);",
"}",
"if(mi) BigInteger.ZERO.subTo(this,this);",
"}",
"",
"//(protected) alternate constructor",
"function bnpFromNumber(a,b,c) {",
"if(\"number\" == typeof b) {",
" // new BigInteger(int,int,RNG)",
" if(a < 2) this.fromInt(1);",
" else {",
" this.fromNumber(a,c);",
" if(!this.testBit(a-1)) // force MSB set",
" this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);",
" if(this.isEven()) this.dAddOffset(1,0); // force odd",
" while(!this.isProbablePrime(b)) {",
" this.dAddOffset(2,0);",
" if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);",
" }",
" }",
"} else {",
" // new BigInteger(int,RNG)",
" var x = new Array(), t = a&7;",
" x.length = (a>>3)+1;",
" b.nextBytes(x);",
" if(t > 0) x[0] &= ((1< 0) {",
" if(p < this.DB && (d = this.data[i]>>p) != (this.s&this.DM)>>p)",
" r[k++] = d|(this.s<<(this.DB-p));",
" while(i >= 0) {",
" if(p < 8) {",
" d = (this.data[i]&((1<>(p+=this.DB-8);",
" } else {",
" d = (this.data[i]>>(p-=8))&0xff;",
" if(p <= 0) { p += this.DB; --i; }",
" }",
" if((d&0x80) != 0) d |= -256;",
" if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;",
" if(k > 0 || d != this.s) r[k++] = d;",
" }",
"}",
"return r;",
"}",
"",
"function bnEquals(a) { return(this.compareTo(a)==0); }",
"function bnMin(a) { return(this.compareTo(a)<0)?this:a; }",
"function bnMax(a) { return(this.compareTo(a)>0)?this:a; }",
"",
"//(protected) r = this op a (bitwise)",
"function bnpBitwiseTo(a,op,r) {",
"var i, f, m = Math.min(a.t,this.t);",
"for(i = 0; i < m; ++i) r.data[i] = op(this.data[i],a.data[i]);",
"if(a.t < this.t) {",
" f = a.s&this.DM;",
" for(i = m; i < this.t; ++i) r.data[i] = op(this.data[i],f);",
" r.t = this.t;",
"} else {",
" f = this.s&this.DM;",
" for(i = m; i < a.t; ++i) r.data[i] = op(f,a.data[i]);",
" r.t = a.t;",
"}",
"r.s = op(this.s,a.s);",
"r.clamp();",
"}",
"",
"//(public) this & a",
"function op_and(x,y) { return x&y; }",
"function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }",
"",
"//(public) this | a",
"function op_or(x,y) { return x|y; }",
"function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }",
"",
"//(public) this ^ a",
"function op_xor(x,y) { return x^y; }",
"function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }",
"",
"//(public) this & ~a",
"function op_andnot(x,y) { return x&~y; }",
"function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }",
"",
"//(public) ~this",
"function bnNot() {",
"var r = nbi();",
"for(var i = 0; i < this.t; ++i) r.data[i] = this.DM&~this.data[i];",
"r.t = this.t;",
"r.s = ~this.s;",
"return r;",
"}",
"",
"//(public) this << n",
"function bnShiftLeft(n) {",
"var r = nbi();",
"if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);",
"return r;",
"}",
"",
"//(public) this >> n",
"function bnShiftRight(n) {",
"var r = nbi();",
"if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);",
"return r;",
"}",
"",
"//return index of lowest 1-bit in x, x < 2^31",
"function lbit(x) {",
"if(x == 0) return -1;",
"var r = 0;",
"if((x&0xffff) == 0) { x >>= 16; r += 16; }",
"if((x&0xff) == 0) { x >>= 8; r += 8; }",
"if((x&0xf) == 0) { x >>= 4; r += 4; }",
"if((x&3) == 0) { x >>= 2; r += 2; }",
"if((x&1) == 0) ++r;",
"return r;",
"}",
"",
"//(public) returns index of lowest 1-bit (or -1 if none)",
"function bnGetLowestSetBit() {",
"for(var i = 0; i < this.t; ++i)",
" if(this.data[i] != 0) return i*this.DB+lbit(this.data[i]);",
"if(this.s < 0) return this.t*this.DB;",
"return -1;",
"}",
"",
"//return number of 1 bits in x",
"function cbit(x) {",
"var r = 0;",
"while(x != 0) { x &= x-1; ++r; }",
"return r;",
"}",
"",
"//(public) return number of set bits",
"function bnBitCount() {",
"var r = 0, x = this.s&this.DM;",
"for(var i = 0; i < this.t; ++i) r += cbit(this.data[i]^x);",
"return r;",
"}",
"",
"//(public) true iff nth bit is set",
"function bnTestBit(n) {",
"var j = Math.floor(n/this.DB);",
"if(j >= this.t) return(this.s!=0);",
"return((this.data[j]&(1<<(n%this.DB)))!=0);",
"}",
"",
"//(protected) this op (1<>= this.DB;",
"}",
"if(a.t < this.t) {",
" c += a.s;",
" while(i < this.t) {",
" c += this.data[i];",
" r.data[i++] = c&this.DM;",
" c >>= this.DB;",
" }",
" c += this.s;",
"} else {",
" c += this.s;",
" while(i < a.t) {",
" c += a.data[i];",
" r.data[i++] = c&this.DM;",
" c >>= this.DB;",
" }",
" c += a.s;",
"}",
"r.s = (c<0)?-1:0;",
"if(c > 0) r.data[i++] = c;",
"else if(c < -1) r.data[i++] = this.DV+c;",
"r.t = i;",
"r.clamp();",
"}",
"",
"//(public) this + a",
"function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }",
"",
"//(public) this - a",
"function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }",
"",
"//(public) this * a",
"function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }",
"",
"//(public) this / a",
"function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }",
"",
"//(public) this % a",
"function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }",
"",
"//(public) [this/a,this%a]",
"function bnDivideAndRemainder(a) {",
"var q = nbi(), r = nbi();",
"this.divRemTo(a,q,r);",
"return new Array(q,r);",
"}",
"",
"//(protected) this *= n, this >= 0, 1 < n < DV",
"function bnpDMultiply(n) {",
"this.data[this.t] = this.am(0,n-1,this,0,0,this.t);",
"++this.t;",
"this.clamp();",
"}",
"",
"//(protected) this += n << w words, this >= 0",
"function bnpDAddOffset(n,w) {",
"if(n == 0) return;",
"while(this.t <= w) this.data[this.t++] = 0;",
"this.data[w] += n;",
"while(this.data[w] >= this.DV) {",
" this.data[w] -= this.DV;",
" if(++w >= this.t) this.data[this.t++] = 0;",
" ++this.data[w];",
"}",
"}",
"",
"//A \"null\" reducer",
"function NullExp() {}",
"function nNop(x) { return x; }",
"function nMulTo(x,y,r) { x.multiplyTo(y,r); }",
"function nSqrTo(x,r) { x.squareTo(r); }",
"",
"NullExp.prototype.convert = nNop;",
"NullExp.prototype.revert = nNop;",
"NullExp.prototype.mulTo = nMulTo;",
"NullExp.prototype.sqrTo = nSqrTo;",
"",
"//(public) this^e",
"function bnPow(e) { return this.exp(e,new NullExp()); }",
"",
"//(protected) r = lower n words of \"this * a\", a.t <= n",
"//\"this\" should be the larger one if appropriate.",
"function bnpMultiplyLowerTo(a,n,r) {",
"var i = Math.min(this.t+a.t,n);",
"r.s = 0; // assumes a,this >= 0",
"r.t = i;",
"while(i > 0) r.data[--i] = 0;",
"var j;",
"for(j = r.t-this.t; i < j; ++i) r.data[i+this.t] = this.am(0,a.data[i],r,i,0,this.t);",
"for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a.data[i],r,i,0,n-i);",
"r.clamp();",
"}",
"",
"//(protected) r = \"this * a\" without lower n words, n > 0",
"//\"this\" should be the larger one if appropriate.",
"function bnpMultiplyUpperTo(a,n,r) {",
"--n;",
"var i = r.t = this.t+a.t-n;",
"r.s = 0; // assumes a,this >= 0",
"while(--i >= 0) r.data[i] = 0;",
"for(i = Math.max(n-this.t,0); i < a.t; ++i)",
" r.data[this.t+i-n] = this.am(n-i,a.data[i],r,0,0,this.t+i-n);",
"r.clamp();",
"r.drShiftTo(1,r);",
"}",
"",
"//Barrett modular reduction",
"function Barrett(m) {",
"// setup Barrett",
"this.r2 = nbi();",
"this.q3 = nbi();",
"BigInteger.ONE.dlShiftTo(2*m.t,this.r2);",
"this.mu = this.r2.divide(m);",
"this.m = m;",
"}",
"",
"function barrettConvert(x) {",
"if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);",
"else if(x.compareTo(this.m) < 0) return x;",
"else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }",
"}",
"",
"function barrettRevert(x) { return x; }",
"",
"//x = x mod m (HAC 14.42)",
"function barrettReduce(x) {",
"x.drShiftTo(this.m.t-1,this.r2);",
"if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }",
"this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);",
"this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);",
"while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);",
"x.subTo(this.r2,x);",
"while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);",
"}",
"",
"//r = x^2 mod m; x != r",
"function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }",
"",
"//r = x*y mod m; x,y != r",
"function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }",
"",
"Barrett.prototype.convert = barrettConvert;",
"Barrett.prototype.revert = barrettRevert;",
"Barrett.prototype.reduce = barrettReduce;",
"Barrett.prototype.mulTo = barrettMulTo;",
"Barrett.prototype.sqrTo = barrettSqrTo;",
"",
"//(public) this^e % m (HAC 14.85)",
"function bnModPow(e,m) {",
"var i = e.bitLength(), k, r = nbv(1), z;",
"if(i <= 0) return r;",
"else if(i < 18) k = 1;",
"else if(i < 48) k = 3;",
"else if(i < 144) k = 4;",
"else if(i < 768) k = 5;",
"else k = 6;",
"if(i < 8)",
" z = new Classic(m);",
"else if(m.isEven())",
" z = new Barrett(m);",
"else",
" z = new Montgomery(m);",
"",
"// precomputation",
"var g = new Array(), n = 3, k1 = k-1, km = (1< 1) {",
" var g2 = nbi();",
" z.sqrTo(g[1],g2);",
" while(n <= km) {",
" g[n] = nbi();",
" z.mulTo(g2,g[n-2],g[n]);",
" n += 2;",
" }",
"}",
"",
"var j = e.t-1, w, is1 = true, r2 = nbi(), t;",
"i = nbits(e.data[j])-1;",
"while(j >= 0) {",
" if(i >= k1) w = (e.data[j]>>(i-k1))&km;",
" else {",
" w = (e.data[j]&((1<<(i+1))-1))<<(k1-i);",
" if(j > 0) w |= e.data[j-1]>>(this.DB+i-k1);",
" }",
"",
" n = k;",
" while((w&1) == 0) { w >>= 1; --n; }",
" if((i -= n) < 0) { i += this.DB; --j; }",
" if(is1) { // ret == 1, don't bother squaring or multiplying it",
" g[w].copyTo(r);",
" is1 = false;",
" } else {",
" while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }",
" if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }",
" z.mulTo(r2,g[w],r);",
" }",
"",
" while(j >= 0 && (e.data[j]&(1< 0) {",
" x.rShiftTo(g,x);",
" y.rShiftTo(g,y);",
"}",
"while(x.signum() > 0) {",
" if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);",
" if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);",
" if(x.compareTo(y) >= 0) {",
" x.subTo(y,x);",
" x.rShiftTo(1,x);",
" } else {",
" y.subTo(x,y);",
" y.rShiftTo(1,y);",
" }",
"}",
"if(g > 0) y.lShiftTo(g,y);",
"return y;",
"}",
"",
"//(protected) this % n, n < 2^26",
"function bnpModInt(n) {",
"if(n <= 0) return 0;",
"var d = this.DV%n, r = (this.s<0)?n-1:0;",
"if(this.t > 0)",
" if(d == 0) r = this.data[0]%n;",
" else for(var i = this.t-1; i >= 0; --i) r = (d*r+this.data[i])%n;",
"return r;",
"}",
"",
"//(public) 1/this % m (HAC 14.61)",
"function bnModInverse(m) {",
"var ac = m.isEven();",
"if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;",
"var u = m.clone(), v = this.clone();",
"var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);",
"while(u.signum() != 0) {",
" while(u.isEven()) {",
" u.rShiftTo(1,u);",
" if(ac) {",
" if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }",
" a.rShiftTo(1,a);",
" } else if(!b.isEven()) b.subTo(m,b);",
" b.rShiftTo(1,b);",
" }",
" while(v.isEven()) {",
" v.rShiftTo(1,v);",
" if(ac) {",
" if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }",
" c.rShiftTo(1,c);",
" } else if(!d.isEven()) d.subTo(m,d);",
" d.rShiftTo(1,d);",
" }",
" if(u.compareTo(v) >= 0) {",
" u.subTo(v,u);",
" if(ac) a.subTo(c,a);",
" b.subTo(d,b);",
" } else {",
" v.subTo(u,v);",
" if(ac) c.subTo(a,c);",
" d.subTo(b,d);",
" }",
"}",
"if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;",
"if(d.compareTo(m) >= 0) return d.subtract(m);",
"if(d.signum() < 0) d.addTo(m,d); else return d;",
"if(d.signum() < 0) return d.add(m); else return d;",
"}",
"",
"var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509];",
"var lplim = (1<<26)/lowprimes[lowprimes.length-1];",
"",
"//(public) test primality with certainty >= 1-.5^t",
"function bnIsProbablePrime(t) {",
"var i, x = this.abs();",
"if(x.t == 1 && x.data[0] <= lowprimes[lowprimes.length-1]) {",
" for(i = 0; i < lowprimes.length; ++i)",
" if(x.data[0] == lowprimes[i]) return true;",
" return false;",
"}",
"if(x.isEven()) return false;",
"i = 1;",
"while(i < lowprimes.length) {",
" var m = lowprimes[i], j = i+1;",
" while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];",
" m = x.modInt(m);",
" while(i < j) if(m%lowprimes[i++] == 0) return false;",
"}",
"return x.millerRabin(t);",
"}",
"",
"//(protected) true if probably prime (HAC 4.24, Miller-Rabin)",
"function bnpMillerRabin(t) {",
"var n1 = this.subtract(BigInteger.ONE);",
"var k = n1.getLowestSetBit();",
"if(k <= 0) return false;",
"var r = n1.shiftRight(k);",
"var prng = bnGetPrng();",
"var a;",
"for(var i = 0; i < t; ++i) {",
" // select witness 'a' at random from between 1 and n1",
" do {",
" a = new BigInteger(this.bitLength(), prng);",
" }",
" while(a.compareTo(BigInteger.ONE) <= 0 || a.compareTo(n1) >= 0);",
" var y = a.modPow(r,this);",
" if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {",
" var j = 1;",
" while(j++ < k && y.compareTo(n1) != 0) {",
" y = y.modPowInt(2,this);",
" if(y.compareTo(BigInteger.ONE) == 0) return false;",
" }",
" if(y.compareTo(n1) != 0) return false;",
" }",
"}",
"return true;",
"}",
"",
"// get pseudo random number generator",
"function bnGetPrng() {",
" // create prng with api that matches BigInteger secure random",
" return {",
" // x is an array to fill with bytes",
" nextBytes: function(x) {",
" for(var i = 0; i < x.length; ++i) {",
" x[i] = Math.floor(Math.random() * 0x0100);",
" }",
" }",
" };",
"}",
"",
"//protected",
"BigInteger.prototype.chunkSize = bnpChunkSize;",
"BigInteger.prototype.toRadix = bnpToRadix;",
"BigInteger.prototype.fromRadix = bnpFromRadix;",
"BigInteger.prototype.fromNumber = bnpFromNumber;",
"BigInteger.prototype.bitwiseTo = bnpBitwiseTo;",
"BigInteger.prototype.changeBit = bnpChangeBit;",
"BigInteger.prototype.addTo = bnpAddTo;",
"BigInteger.prototype.dMultiply = bnpDMultiply;",
"BigInteger.prototype.dAddOffset = bnpDAddOffset;",
"BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;",
"BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;",
"BigInteger.prototype.modInt = bnpModInt;",
"BigInteger.prototype.millerRabin = bnpMillerRabin;",
"",
"//public",
"BigInteger.prototype.clone = bnClone;",
"BigInteger.prototype.intValue = bnIntValue;",
"BigInteger.prototype.byteValue = bnByteValue;",
"BigInteger.prototype.shortValue = bnShortValue;",
"BigInteger.prototype.signum = bnSigNum;",
"BigInteger.prototype.toByteArray = bnToByteArray;",
"BigInteger.prototype.equals = bnEquals;",
"BigInteger.prototype.min = bnMin;",
"BigInteger.prototype.max = bnMax;",
"BigInteger.prototype.and = bnAnd;",
"BigInteger.prototype.or = bnOr;",
"BigInteger.prototype.xor = bnXor;",
"BigInteger.prototype.andNot = bnAndNot;",
"BigInteger.prototype.not = bnNot;",
"BigInteger.prototype.shiftLeft = bnShiftLeft;",
"BigInteger.prototype.shiftRight = bnShiftRight;",
"BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;",
"BigInteger.prototype.bitCount = bnBitCount;",
"BigInteger.prototype.testBit = bnTestBit;",
"BigInteger.prototype.setBit = bnSetBit;",
"BigInteger.prototype.clearBit = bnClearBit;",
"BigInteger.prototype.flipBit = bnFlipBit;",
"BigInteger.prototype.add = bnAdd;",
"BigInteger.prototype.subtract = bnSubtract;",
"BigInteger.prototype.multiply = bnMultiply;",
"BigInteger.prototype.divide = bnDivide;",
"BigInteger.prototype.remainder = bnRemainder;",
"BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;",
"BigInteger.prototype.modPow = bnModPow;",
"BigInteger.prototype.modInverse = bnModInverse;",
"BigInteger.prototype.pow = bnPow;",
"BigInteger.prototype.gcd = bnGCD;",
"BigInteger.prototype.isProbablePrime = bnIsProbablePrime;",
"",
"//BigInteger interfaces not implemented in jsbn:",
"",
"//BigInteger(int signum, byte[] magnitude)",
"//double doubleValue()",
"//float floatValue()",
"//int hashCode()",
"//long longValue()",
"//static BigInteger valueOf(long val)",
"",
"",
"/***/ }),",
"/* 13 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Cipher base API.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"",
"module.exports = forge.cipher = forge.cipher || {};",
"",
"// registered algorithms",
"forge.cipher.algorithms = forge.cipher.algorithms || {};",
"",
"/**",
" * Creates a cipher object that can be used to encrypt data using the given",
" * algorithm and key. The algorithm may be provided as a string value for a",
" * previously registered algorithm or it may be given as a cipher algorithm",
" * API object.",
" *",
" * @param algorithm the algorithm to use, either a string or an algorithm API",
" * object.",
" * @param key the key to use, as a binary-encoded string of bytes or a",
" * byte buffer.",
" *",
" * @return the cipher.",
" */",
"forge.cipher.createCipher = function(algorithm, key) {",
" var api = algorithm;",
" if(typeof api === 'string') {",
" api = forge.cipher.getAlgorithm(api);",
" if(api) {",
" api = api();",
" }",
" }",
" if(!api) {",
" throw new Error('Unsupported algorithm: ' + algorithm);",
" }",
"",
" // assume block cipher",
" return new forge.cipher.BlockCipher({",
" algorithm: api,",
" key: key,",
" decrypt: false",
" });",
"};",
"",
"/**",
" * Creates a decipher object that can be used to decrypt data using the given",
" * algorithm and key. The algorithm may be provided as a string value for a",
" * previously registered algorithm or it may be given as a cipher algorithm",
" * API object.",
" *",
" * @param algorithm the algorithm to use, either a string or an algorithm API",
" * object.",
" * @param key the key to use, as a binary-encoded string of bytes or a",
" * byte buffer.",
" *",
" * @return the cipher.",
" */",
"forge.cipher.createDecipher = function(algorithm, key) {",
" var api = algorithm;",
" if(typeof api === 'string') {",
" api = forge.cipher.getAlgorithm(api);",
" if(api) {",
" api = api();",
" }",
" }",
" if(!api) {",
" throw new Error('Unsupported algorithm: ' + algorithm);",
" }",
"",
" // assume block cipher",
" return new forge.cipher.BlockCipher({",
" algorithm: api,",
" key: key,",
" decrypt: true",
" });",
"};",
"",
"/**",
" * Registers an algorithm by name. If the name was already registered, the",
" * algorithm API object will be overwritten.",
" *",
" * @param name the name of the algorithm.",
" * @param algorithm the algorithm API object.",
" */",
"forge.cipher.registerAlgorithm = function(name, algorithm) {",
" name = name.toUpperCase();",
" forge.cipher.algorithms[name] = algorithm;",
"};",
"",
"/**",
" * Gets a registered algorithm by name.",
" *",
" * @param name the name of the algorithm.",
" *",
" * @return the algorithm, if found, null if not.",
" */",
"forge.cipher.getAlgorithm = function(name) {",
" name = name.toUpperCase();",
" if(name in forge.cipher.algorithms) {",
" return forge.cipher.algorithms[name];",
" }",
" return null;",
"};",
"",
"var BlockCipher = forge.cipher.BlockCipher = function(options) {",
" this.algorithm = options.algorithm;",
" this.mode = this.algorithm.mode;",
" this.blockSize = this.mode.blockSize;",
" this._finish = false;",
" this._input = null;",
" this.output = null;",
" this._op = options.decrypt ? this.mode.decrypt : this.mode.encrypt;",
" this._decrypt = options.decrypt;",
" this.algorithm.initialize(options);",
"};",
"",
"/**",
" * Starts or restarts the encryption or decryption process, whichever",
" * was previously configured.",
" *",
" * For non-GCM mode, the IV may be a binary-encoded string of bytes, an array",
" * of bytes, a byte buffer, or an array of 32-bit integers. If the IV is in",
" * bytes, then it must be Nb (16) bytes in length. If the IV is given in as",
" * 32-bit integers, then it must be 4 integers long.",
" *",
" * Note: an IV is not required or used in ECB mode.",
" *",
" * For GCM-mode, the IV must be given as a binary-encoded string of bytes or",
" * a byte buffer. The number of bytes should be 12 (96 bits) as recommended",
" * by NIST SP-800-38D but another length may be given.",
" *",
" * @param options the options to use:",
" * iv the initialization vector to use as a binary-encoded string of",
" * bytes, null to reuse the last ciphered block from a previous",
" * update() (this \"residue\" method is for legacy support only).",
" * additionalData additional authentication data as a binary-encoded",
" * string of bytes, for 'GCM' mode, (default: none).",
" * tagLength desired length of authentication tag, in bits, for",
" * 'GCM' mode (0-128, default: 128).",
" * tag the authentication tag to check if decrypting, as a",
" * binary-encoded string of bytes.",
" * output the output the buffer to write to, null to create one.",
" */",
"BlockCipher.prototype.start = function(options) {",
" options = options || {};",
" var opts = {};",
" for(var key in options) {",
" opts[key] = options[key];",
" }",
" opts.decrypt = this._decrypt;",
" this._finish = false;",
" this._input = forge.util.createBuffer();",
" this.output = options.output || forge.util.createBuffer();",
" this.mode.start(opts);",
"};",
"",
"/**",
" * Updates the next block according to the cipher mode.",
" *",
" * @param input the buffer to read from.",
" */",
"BlockCipher.prototype.update = function(input) {",
" if(input) {",
" // input given, so empty it into the input buffer",
" this._input.putBuffer(input);",
" }",
"",
" // do cipher operation until it needs more input and not finished",
" while(!this._op.call(this.mode, this._input, this.output, this._finish) &&",
" !this._finish) {}",
"",
" // free consumed memory from input buffer",
" this._input.compact();",
"};",
"",
"/**",
" * Finishes encrypting or decrypting.",
" *",
" * @param pad a padding function to use in CBC mode, null for default,",
" * signature(blockSize, buffer, decrypt).",
" *",
" * @return true if successful, false on error.",
" */",
"BlockCipher.prototype.finish = function(pad) {",
" // backwards-compatibility w/deprecated padding API",
" // Note: will overwrite padding functions even after another start() call",
" if(pad && (this.mode.name === 'ECB' || this.mode.name === 'CBC')) {",
" this.mode.pad = function(input) {",
" return pad(this.blockSize, input, false);",
" };",
" this.mode.unpad = function(output) {",
" return pad(this.blockSize, output, true);",
" };",
" }",
"",
" // build options for padding and afterFinish functions",
" var options = {};",
" options.decrypt = this._decrypt;",
"",
" // get # of bytes that won't fill a block",
" options.overflow = this._input.length() % this.blockSize;",
"",
" if(!this._decrypt && this.mode.pad) {",
" if(!this.mode.pad(this._input, options)) {",
" return false;",
" }",
" }",
"",
" // do final update",
" this._finish = true;",
" this.update();",
"",
" if(this._decrypt && this.mode.unpad) {",
" if(!this.mode.unpad(this.output, options)) {",
" return false;",
" }",
" }",
"",
" if(this.mode.afterFinish) {",
" if(!this.mode.afterFinish(this.output, options)) {",
" return false;",
" }",
" }",
"",
" return true;",
"};",
"",
"",
"/***/ }),",
"/* 14 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Message Digest Algorithm 5 with 128-bit digest (MD5) implementation.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(4);",
"__webpack_require__(1);",
"",
"var md5 = module.exports = forge.md5 = forge.md5 || {};",
"forge.md.md5 = forge.md.algorithms.md5 = md5;",
"",
"/**",
" * Creates an MD5 message digest object.",
" *",
" * @return a message digest object.",
" */",
"md5.create = function() {",
" // do initialization as necessary",
" if(!_initialized) {",
" _init();",
" }",
"",
" // MD5 state contains four 32-bit integers",
" var _state = null;",
"",
" // input buffer",
" var _input = forge.util.createBuffer();",
"",
" // used for word storage",
" var _w = new Array(16);",
"",
" // message digest object",
" var md = {",
" algorithm: 'md5',",
" blockLength: 64,",
" digestLength: 16,",
" // 56-bit length of message so far (does not including padding)",
" messageLength: 0,",
" // true message length",
" fullMessageLength: null,",
" // size of message length in bytes",
" messageLengthSize: 8",
" };",
"",
" /**",
" * Starts the digest.",
" *",
" * @return this digest object.",
" */",
" md.start = function() {",
" // up to 56-bit message length for convenience",
" md.messageLength = 0;",
"",
" // full message length (set md.messageLength64 for backwards-compatibility)",
" md.fullMessageLength = md.messageLength64 = [];",
" var int32s = md.messageLengthSize / 4;",
" for(var i = 0; i < int32s; ++i) {",
" md.fullMessageLength.push(0);",
" }",
" _input = forge.util.createBuffer();",
" _state = {",
" h0: 0x67452301,",
" h1: 0xEFCDAB89,",
" h2: 0x98BADCFE,",
" h3: 0x10325476",
" };",
" return md;",
" };",
" // start digest automatically for first time",
" md.start();",
"",
" /**",
" * Updates the digest with the given message input. The given input can",
" * treated as raw input (no encoding will be applied) or an encoding of",
" * 'utf8' maybe given to encode the input using UTF-8.",
" *",
" * @param msg the message input to update with.",
" * @param encoding the encoding to use (default: 'raw', other: 'utf8').",
" *",
" * @return this digest object.",
" */",
" md.update = function(msg, encoding) {",
" if(encoding === 'utf8') {",
" msg = forge.util.encodeUtf8(msg);",
" }",
"",
" // update message length",
" var len = msg.length;",
" md.messageLength += len;",
" len = [(len / 0x100000000) >>> 0, len >>> 0];",
" for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {",
" md.fullMessageLength[i] += len[1];",
" len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);",
" md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;",
" len[0] = (len[1] / 0x100000000) >>> 0;",
" }",
"",
" // add bytes to input buffer",
" _input.putBytes(msg);",
"",
" // process bytes",
" _update(_state, _w, _input);",
"",
" // compact input buffer every 2K or if empty",
" if(_input.read > 2048 || _input.length() === 0) {",
" _input.compact();",
" }",
"",
" return md;",
" };",
"",
" /**",
" * Produces the digest.",
" *",
" * @return a byte buffer containing the digest value.",
" */",
" md.digest = function() {",
" /* Note: Here we copy the remaining bytes in the input buffer and",
" add the appropriate MD5 padding. Then we do the final update",
" on a copy of the state so that if the user wants to get",
" intermediate digests they can do so. */",
"",
" /* Determine the number of bytes that must be added to the message",
" to ensure its length is congruent to 448 mod 512. In other words,",
" the data to be digested must be a multiple of 512 bits (or 128 bytes).",
" This data includes the message, some padding, and the length of the",
" message. Since the length of the message will be encoded as 8 bytes (64",
" bits), that means that the last segment of the data must have 56 bytes",
" (448 bits) of message and padding. Therefore, the length of the message",
" plus the padding must be congruent to 448 mod 512 because",
" 512 - 128 = 448.",
"",
" In order to fill up the message length it must be filled with",
" padding that begins with 1 bit followed by all 0 bits. Padding",
" must *always* be present, so if the message length is already",
" congruent to 448 mod 512, then 512 padding bits must be added. */",
"",
" var finalBlock = forge.util.createBuffer();",
" finalBlock.putBytes(_input.bytes());",
"",
" // compute remaining size to be digested (include message length size)",
" var remaining = (",
" md.fullMessageLength[md.fullMessageLength.length - 1] +",
" md.messageLengthSize);",
"",
" // add padding for overflow blockSize - overflow",
" // _padding starts with 1 byte with first bit is set (byte value 128), then",
" // there may be up to (blockSize - 1) other pad bytes",
" var overflow = remaining & (md.blockLength - 1);",
" finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));",
"",
" // serialize message length in bits in little-endian order; since length",
" // is stored in bytes we multiply by 8 and add carry",
" var bits, carry = 0;",
" for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {",
" bits = md.fullMessageLength[i] * 8 + carry;",
" carry = (bits / 0x100000000) >>> 0;",
" finalBlock.putInt32Le(bits >>> 0);",
" }",
"",
" var s2 = {",
" h0: _state.h0,",
" h1: _state.h1,",
" h2: _state.h2,",
" h3: _state.h3",
" };",
" _update(s2, _w, finalBlock);",
" var rval = forge.util.createBuffer();",
" rval.putInt32Le(s2.h0);",
" rval.putInt32Le(s2.h1);",
" rval.putInt32Le(s2.h2);",
" rval.putInt32Le(s2.h3);",
" return rval;",
" };",
"",
" return md;",
"};",
"",
"// padding, constant tables for calculating md5",
"var _padding = null;",
"var _g = null;",
"var _r = null;",
"var _k = null;",
"var _initialized = false;",
"",
"/**",
" * Initializes the constant tables.",
" */",
"function _init() {",
" // create padding",
" _padding = String.fromCharCode(128);",
" _padding += forge.util.fillString(String.fromCharCode(0x00), 64);",
"",
" // g values",
" _g = [",
" 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,",
" 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12,",
" 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2,",
" 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9];",
"",
" // rounds table",
" _r = [",
" 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,",
" 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,",
" 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,",
" 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21];",
"",
" // get the result of abs(sin(i + 1)) as a 32-bit integer",
" _k = new Array(64);",
" for(var i = 0; i < 64; ++i) {",
" _k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 0x100000000);",
" }",
"",
" // now initialized",
" _initialized = true;",
"}",
"",
"/**",
" * Updates an MD5 state with the given byte buffer.",
" *",
" * @param s the MD5 state to update.",
" * @param w the array to use to store words.",
" * @param bytes the byte buffer to update with.",
" */",
"function _update(s, w, bytes) {",
" // consume 512 bit (64 byte) chunks",
" var t, a, b, c, d, f, r, i;",
" var len = bytes.length();",
" while(len >= 64) {",
" // initialize hash value for this chunk",
" a = s.h0;",
" b = s.h1;",
" c = s.h2;",
" d = s.h3;",
"",
" // round 1",
" for(i = 0; i < 16; ++i) {",
" w[i] = bytes.getInt32Le();",
" f = d ^ (b & (c ^ d));",
" t = (a + f + _k[i] + w[i]);",
" r = _r[i];",
" a = d;",
" d = c;",
" c = b;",
" b += (t << r) | (t >>> (32 - r));",
" }",
" // round 2",
" for(; i < 32; ++i) {",
" f = c ^ (d & (b ^ c));",
" t = (a + f + _k[i] + w[_g[i]]);",
" r = _r[i];",
" a = d;",
" d = c;",
" c = b;",
" b += (t << r) | (t >>> (32 - r));",
" }",
" // round 3",
" for(; i < 48; ++i) {",
" f = b ^ c ^ d;",
" t = (a + f + _k[i] + w[_g[i]]);",
" r = _r[i];",
" a = d;",
" d = c;",
" c = b;",
" b += (t << r) | (t >>> (32 - r));",
" }",
" // round 4",
" for(; i < 64; ++i) {",
" f = c ^ (b | ~d);",
" t = (a + f + _k[i] + w[_g[i]]);",
" r = _r[i];",
" a = d;",
" d = c;",
" c = b;",
" b += (t << r) | (t >>> (32 - r));",
" }",
"",
" // update hash state",
" s.h0 = (s.h0 + a) | 0;",
" s.h1 = (s.h1 + b) | 0;",
" s.h2 = (s.h2 + c) | 0;",
" s.h3 = (s.h3 + d) | 0;",
"",
" len -= 64;",
" }",
"}",
"",
"",
"/***/ }),",
"/* 15 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Password-Based Key-Derivation Function #2 implementation.",
" *",
" * See RFC 2898 for details.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2013 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(8);",
"__webpack_require__(4);",
"__webpack_require__(1);",
"",
"var pkcs5 = forge.pkcs5 = forge.pkcs5 || {};",
"",
"var crypto;",
"if(forge.util.isNodejs && !forge.options.usePureJavaScript) {",
" crypto = __webpack_require__(22);",
"}",
"",
"/**",
" * Derives a key from a password.",
" *",
" * @param p the password as a binary-encoded string of bytes.",
" * @param s the salt as a binary-encoded string of bytes.",
" * @param c the iteration count, a positive integer.",
" * @param dkLen the intended length, in bytes, of the derived key,",
" * (max: 2^32 - 1) * hash length of the PRF.",
" * @param [md] the message digest (or algorithm identifier as a string) to use",
" * in the PRF, defaults to SHA-1.",
" * @param [callback(err, key)] presence triggers asynchronous version, called",
" * once the operation completes.",
" *",
" * @return the derived key, as a binary-encoded string of bytes, for the",
" * synchronous version (if no callback is specified).",
" */",
"module.exports = forge.pbkdf2 = pkcs5.pbkdf2 = function(",
" p, s, c, dkLen, md, callback) {",
" if(typeof md === 'function') {",
" callback = md;",
" md = null;",
" }",
"",
" // use native implementation if possible and not disabled, note that",
" // some node versions only support SHA-1, others allow digest to be changed",
" if(forge.util.isNodejs && !forge.options.usePureJavaScript &&",
" crypto.pbkdf2 && (md === null || typeof md !== 'object') &&",
" (crypto.pbkdf2Sync.length > 4 || (!md || md === 'sha1'))) {",
" if(typeof md !== 'string') {",
" // default prf to SHA-1",
" md = 'sha1';",
" }",
" p = new Buffer(p, 'binary');",
" s = new Buffer(s, 'binary');",
" if(!callback) {",
" if(crypto.pbkdf2Sync.length === 4) {",
" return crypto.pbkdf2Sync(p, s, c, dkLen).toString('binary');",
" }",
" return crypto.pbkdf2Sync(p, s, c, dkLen, md).toString('binary');",
" }",
" if(crypto.pbkdf2Sync.length === 4) {",
" return crypto.pbkdf2(p, s, c, dkLen, function(err, key) {",
" if(err) {",
" return callback(err);",
" }",
" callback(null, key.toString('binary'));",
" });",
" }",
" return crypto.pbkdf2(p, s, c, dkLen, md, function(err, key) {",
" if(err) {",
" return callback(err);",
" }",
" callback(null, key.toString('binary'));",
" });",
" }",
"",
" if(typeof md === 'undefined' || md === null) {",
" // default prf to SHA-1",
" md = 'sha1';",
" }",
" if(typeof md === 'string') {",
" if(!(md in forge.md.algorithms)) {",
" throw new Error('Unknown hash algorithm: ' + md);",
" }",
" md = forge.md[md].create();",
" }",
"",
" var hLen = md.digestLength;",
"",
" /* 1. If dkLen > (2^32 - 1) * hLen, output \"derived key too long\" and",
" stop. */",
" if(dkLen > (0xFFFFFFFF * hLen)) {",
" var err = new Error('Derived key is too long.');",
" if(callback) {",
" return callback(err);",
" }",
" throw err;",
" }",
"",
" /* 2. Let len be the number of hLen-octet blocks in the derived key,",
" rounding up, and let r be the number of octets in the last",
" block:",
"",
" len = CEIL(dkLen / hLen),",
" r = dkLen - (len - 1) * hLen. */",
" var len = Math.ceil(dkLen / hLen);",
" var r = dkLen - (len - 1) * hLen;",
"",
" /* 3. For each block of the derived key apply the function F defined",
" below to the password P, the salt S, the iteration count c, and",
" the block index to compute the block:",
"",
" T_1 = F(P, S, c, 1),",
" T_2 = F(P, S, c, 2),",
" ...",
" T_len = F(P, S, c, len),",
"",
" where the function F is defined as the exclusive-or sum of the",
" first c iterates of the underlying pseudorandom function PRF",
" applied to the password P and the concatenation of the salt S",
" and the block index i:",
"",
" F(P, S, c, i) = u_1 XOR u_2 XOR ... XOR u_c",
"",
" where",
"",
" u_1 = PRF(P, S || INT(i)),",
" u_2 = PRF(P, u_1),",
" ...",
" u_c = PRF(P, u_{c-1}).",
"",
" Here, INT(i) is a four-octet encoding of the integer i, most",
" significant octet first. */",
" var prf = forge.hmac.create();",
" prf.start(md, p);",
" var dk = '';",
" var xor, u_c, u_c1;",
"",
" // sync version",
" if(!callback) {",
" for(var i = 1; i <= len; ++i) {",
" // PRF(P, S || INT(i)) (first iteration)",
" prf.start(null, null);",
" prf.update(s);",
" prf.update(forge.util.int32ToBytes(i));",
" xor = u_c1 = prf.digest().getBytes();",
"",
" // PRF(P, u_{c-1}) (other iterations)",
" for(var j = 2; j <= c; ++j) {",
" prf.start(null, null);",
" prf.update(u_c1);",
" u_c = prf.digest().getBytes();",
" // F(p, s, c, i)",
" xor = forge.util.xorBytes(xor, u_c, hLen);",
" u_c1 = u_c;",
" }",
"",
" /* 4. Concatenate the blocks and extract the first dkLen octets to",
" produce a derived key DK:",
"",
" DK = T_1 || T_2 || ... || T_len<0..r-1> */",
" dk += (i < len) ? xor : xor.substr(0, r);",
" }",
" /* 5. Output the derived key DK. */",
" return dk;",
" }",
"",
" // async version",
" var i = 1, j;",
" function outer() {",
" if(i > len) {",
" // done",
" return callback(null, dk);",
" }",
"",
" // PRF(P, S || INT(i)) (first iteration)",
" prf.start(null, null);",
" prf.update(s);",
" prf.update(forge.util.int32ToBytes(i));",
" xor = u_c1 = prf.digest().getBytes();",
"",
" // PRF(P, u_{c-1}) (other iterations)",
" j = 2;",
" inner();",
" }",
"",
" function inner() {",
" if(j <= c) {",
" prf.start(null, null);",
" prf.update(u_c1);",
" u_c = prf.digest().getBytes();",
" // F(p, s, c, i)",
" xor = forge.util.xorBytes(xor, u_c, hLen);",
" u_c1 = u_c;",
" ++j;",
" return forge.util.setImmediate(inner);",
" }",
"",
" /* 4. Concatenate the blocks and extract the first dkLen octets to",
" produce a derived key DK:",
"",
" DK = T_1 || T_2 || ... || T_len<0..r-1> */",
" dk += (i < len) ? xor : xor.substr(0, r);",
"",
" ++i;",
" outer();",
" }",
"",
" outer();",
"};",
"",
"",
"/***/ }),",
"/* 16 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of X.509 and related components (such as",
" * Certification Signing Requests) of a Public Key Infrastructure.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2014 Digital Bazaar, Inc.",
" *",
" * The ASN.1 representation of an X.509v3 certificate is as follows",
" * (see RFC 2459):",
" *",
" * Certificate ::= SEQUENCE {",
" * tbsCertificate TBSCertificate,",
" * signatureAlgorithm AlgorithmIdentifier,",
" * signatureValue BIT STRING",
" * }",
" *",
" * TBSCertificate ::= SEQUENCE {",
" * version [0] EXPLICIT Version DEFAULT v1,",
" * serialNumber CertificateSerialNumber,",
" * signature AlgorithmIdentifier,",
" * issuer Name,",
" * validity Validity,",
" * subject Name,",
" * subjectPublicKeyInfo SubjectPublicKeyInfo,",
" * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,",
" * -- If present, version shall be v2 or v3",
" * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,",
" * -- If present, version shall be v2 or v3",
" * extensions [3] EXPLICIT Extensions OPTIONAL",
" * -- If present, version shall be v3",
" * }",
" *",
" * Version ::= INTEGER { v1(0), v2(1), v3(2) }",
" *",
" * CertificateSerialNumber ::= INTEGER",
" *",
" * Name ::= CHOICE {",
" * // only one possible choice for now",
" * RDNSequence",
" * }",
" *",
" * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName",
" *",
" * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue",
" *",
" * AttributeTypeAndValue ::= SEQUENCE {",
" * type AttributeType,",
" * value AttributeValue",
" * }",
" * AttributeType ::= OBJECT IDENTIFIER",
" * AttributeValue ::= ANY DEFINED BY AttributeType",
" *",
" * Validity ::= SEQUENCE {",
" * notBefore Time,",
" * notAfter Time",
" * }",
" *",
" * Time ::= CHOICE {",
" * utcTime UTCTime,",
" * generalTime GeneralizedTime",
" * }",
" *",
" * UniqueIdentifier ::= BIT STRING",
" *",
" * SubjectPublicKeyInfo ::= SEQUENCE {",
" * algorithm AlgorithmIdentifier,",
" * subjectPublicKey BIT STRING",
" * }",
" *",
" * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension",
" *",
" * Extension ::= SEQUENCE {",
" * extnID OBJECT IDENTIFIER,",
" * critical BOOLEAN DEFAULT FALSE,",
" * extnValue OCTET STRING",
" * }",
" *",
" * The only key algorithm currently supported for PKI is RSA.",
" *",
" * RSASSA-PSS signatures are described in RFC 3447 and RFC 4055.",
" *",
" * PKCS#10 v1.7 describes certificate signing requests:",
" *",
" * CertificationRequestInfo:",
" *",
" * CertificationRequestInfo ::= SEQUENCE {",
" * version INTEGER { v1(0) } (v1,...),",
" * subject Name,",
" * subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},",
" * attributes [0] Attributes{{ CRIAttributes }}",
" * }",
" *",
" * Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}",
" *",
" * CRIAttributes ATTRIBUTE ::= {",
" * ... -- add any locally defined attributes here -- }",
" *",
" * Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {",
" * type ATTRIBUTE.&id({IOSet}),",
" * values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})",
" * }",
" *",
" * CertificationRequest ::= SEQUENCE {",
" * certificationRequestInfo CertificationRequestInfo,",
" * signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }},",
" * signature BIT STRING",
" * }",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(5);",
"__webpack_require__(3);",
"__webpack_require__(10);",
"__webpack_require__(4);",
"__webpack_require__(38);",
"__webpack_require__(6);",
"__webpack_require__(7);",
"__webpack_require__(17);",
"__webpack_require__(11);",
"__webpack_require__(1);",
"",
"// shortcut for asn.1 API",
"var asn1 = forge.asn1;",
"",
"/* Public Key Infrastructure (PKI) implementation. */",
"var pki = module.exports = forge.pki = forge.pki || {};",
"var oids = pki.oids;",
"",
"// short name OID mappings",
"var _shortNames = {};",
"_shortNames['CN'] = oids['commonName'];",
"_shortNames['commonName'] = 'CN';",
"_shortNames['C'] = oids['countryName'];",
"_shortNames['countryName'] = 'C';",
"_shortNames['L'] = oids['localityName'];",
"_shortNames['localityName'] = 'L';",
"_shortNames['ST'] = oids['stateOrProvinceName'];",
"_shortNames['stateOrProvinceName'] = 'ST';",
"_shortNames['O'] = oids['organizationName'];",
"_shortNames['organizationName'] = 'O';",
"_shortNames['OU'] = oids['organizationalUnitName'];",
"_shortNames['organizationalUnitName'] = 'OU';",
"_shortNames['E'] = oids['emailAddress'];",
"_shortNames['emailAddress'] = 'E';",
"",
"// validator for an SubjectPublicKeyInfo structure",
"// Note: Currently only works with an RSA public key",
"var publicKeyValidator = forge.pki.rsa.publicKeyValidator;",
"",
"// validator for an X.509v3 certificate",
"var x509CertificateValidator = {",
" name: 'Certificate',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'Certificate.TBSCertificate',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'tbsCertificate',",
" value: [{",
" name: 'Certificate.TBSCertificate.version',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" constructed: true,",
" optional: true,",
" value: [{",
" name: 'Certificate.TBSCertificate.version.integer',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'certVersion'",
" }]",
" }, {",
" name: 'Certificate.TBSCertificate.serialNumber',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'certSerialNumber'",
" }, {",
" name: 'Certificate.TBSCertificate.signature',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'Certificate.TBSCertificate.signature.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'certinfoSignatureOid'",
" }, {",
" name: 'Certificate.TBSCertificate.signature.parameters',",
" tagClass: asn1.Class.UNIVERSAL,",
" optional: true,",
" captureAsn1: 'certinfoSignatureParams'",
" }]",
" }, {",
" name: 'Certificate.TBSCertificate.issuer',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'certIssuer'",
" }, {",
" name: 'Certificate.TBSCertificate.validity',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" // Note: UTC and generalized times may both appear so the capture",
" // names are based on their detected order, the names used below",
" // are only for the common case, which validity time really means",
" // \"notBefore\" and which means \"notAfter\" will be determined by order",
" value: [{",
" // notBefore (Time) (UTC time case)",
" name: 'Certificate.TBSCertificate.validity.notBefore (utc)',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.UTCTIME,",
" constructed: false,",
" optional: true,",
" capture: 'certValidity1UTCTime'",
" }, {",
" // notBefore (Time) (generalized time case)",
" name: 'Certificate.TBSCertificate.validity.notBefore (generalized)',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.GENERALIZEDTIME,",
" constructed: false,",
" optional: true,",
" capture: 'certValidity2GeneralizedTime'",
" }, {",
" // notAfter (Time) (only UTC time is supported)",
" name: 'Certificate.TBSCertificate.validity.notAfter (utc)',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.UTCTIME,",
" constructed: false,",
" optional: true,",
" capture: 'certValidity3UTCTime'",
" }, {",
" // notAfter (Time) (only UTC time is supported)",
" name: 'Certificate.TBSCertificate.validity.notAfter (generalized)',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.GENERALIZEDTIME,",
" constructed: false,",
" optional: true,",
" capture: 'certValidity4GeneralizedTime'",
" }]",
" }, {",
" // Name (subject) (RDNSequence)",
" name: 'Certificate.TBSCertificate.subject',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'certSubject'",
" },",
" // SubjectPublicKeyInfo",
" publicKeyValidator,",
" {",
" // issuerUniqueID (optional)",
" name: 'Certificate.TBSCertificate.issuerUniqueID',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 1,",
" constructed: true,",
" optional: true,",
" value: [{",
" name: 'Certificate.TBSCertificate.issuerUniqueID.id',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.BITSTRING,",
" constructed: false,",
" // TODO: support arbitrary bit length ids",
" captureBitStringValue: 'certIssuerUniqueId'",
" }]",
" }, {",
" // subjectUniqueID (optional)",
" name: 'Certificate.TBSCertificate.subjectUniqueID',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 2,",
" constructed: true,",
" optional: true,",
" value: [{",
" name: 'Certificate.TBSCertificate.subjectUniqueID.id',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.BITSTRING,",
" constructed: false,",
" // TODO: support arbitrary bit length ids",
" captureBitStringValue: 'certSubjectUniqueId'",
" }]",
" }, {",
" // Extensions (optional)",
" name: 'Certificate.TBSCertificate.extensions',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 3,",
" constructed: true,",
" captureAsn1: 'certExtensions',",
" optional: true",
" }]",
" }, {",
" // AlgorithmIdentifier (signature algorithm)",
" name: 'Certificate.signatureAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" // algorithm",
" name: 'Certificate.signatureAlgorithm.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'certSignatureOid'",
" }, {",
" name: 'Certificate.TBSCertificate.signature.parameters',",
" tagClass: asn1.Class.UNIVERSAL,",
" optional: true,",
" captureAsn1: 'certSignatureParams'",
" }]",
" }, {",
" // SignatureValue",
" name: 'Certificate.signatureValue',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.BITSTRING,",
" constructed: false,",
" captureBitStringValue: 'certSignature'",
" }]",
"};",
"",
"var rsassaPssParameterValidator = {",
" name: 'rsapss',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'rsapss.hashAlgorithm',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" constructed: true,",
" value: [{",
" name: 'rsapss.hashAlgorithm.AlgorithmIdentifier',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Class.SEQUENCE,",
" constructed: true,",
" optional: true,",
" value: [{",
" name: 'rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'hashOid'",
" /* parameter block omitted, for SHA1 NULL anyhow. */",
" }]",
" }]",
" }, {",
" name: 'rsapss.maskGenAlgorithm',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 1,",
" constructed: true,",
" value: [{",
" name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Class.SEQUENCE,",
" constructed: true,",
" optional: true,",
" value: [{",
" name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'maskGenOid'",
" }, {",
" name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'maskGenHashOid'",
" /* parameter block omitted, for SHA1 NULL anyhow. */",
" }]",
" }]",
" }]",
" }, {",
" name: 'rsapss.saltLength',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 2,",
" optional: true,",
" value: [{",
" name: 'rsapss.saltLength.saltLength',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Class.INTEGER,",
" constructed: false,",
" capture: 'saltLength'",
" }]",
" }, {",
" name: 'rsapss.trailerField',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 3,",
" optional: true,",
" value: [{",
" name: 'rsapss.trailer.trailer',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Class.INTEGER,",
" constructed: false,",
" capture: 'trailer'",
" }]",
" }]",
"};",
"",
"// validator for a CertificationRequestInfo structure",
"var certificationRequestInfoValidator = {",
" name: 'CertificationRequestInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'certificationRequestInfo',",
" value: [{",
" name: 'CertificationRequestInfo.integer',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'certificationRequestInfoVersion'",
" }, {",
" // Name (subject) (RDNSequence)",
" name: 'CertificationRequestInfo.subject',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'certificationRequestInfoSubject'",
" },",
" // SubjectPublicKeyInfo",
" publicKeyValidator,",
" {",
" name: 'CertificationRequestInfo.attributes',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" constructed: true,",
" optional: true,",
" capture: 'certificationRequestInfoAttributes',",
" value: [{",
" name: 'CertificationRequestInfo.attributes',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'CertificationRequestInfo.attributes.type',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false",
" }, {",
" name: 'CertificationRequestInfo.attributes.value',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SET,",
" constructed: true",
" }]",
" }]",
" }]",
"};",
"",
"// validator for a CertificationRequest structure",
"var certificationRequestValidator = {",
" name: 'CertificationRequest',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'csr',",
" value: [",
" certificationRequestInfoValidator, {",
" // AlgorithmIdentifier (signature algorithm)",
" name: 'CertificationRequest.signatureAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" // algorithm",
" name: 'CertificationRequest.signatureAlgorithm.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'csrSignatureOid'",
" }, {",
" name: 'CertificationRequest.signatureAlgorithm.parameters',",
" tagClass: asn1.Class.UNIVERSAL,",
" optional: true,",
" captureAsn1: 'csrSignatureParams'",
" }]",
" }, {",
" // signature",
" name: 'CertificationRequest.signature',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.BITSTRING,",
" constructed: false,",
" captureBitStringValue: 'csrSignature'",
" }]",
"};",
"",
"/**",
" * Converts an RDNSequence of ASN.1 DER-encoded RelativeDistinguishedName",
" * sets into an array with objects that have type and value properties.",
" *",
" * @param rdn the RDNSequence to convert.",
" * @param md a message digest to append type and value to if provided.",
" */",
"pki.RDNAttributesAsArray = function(rdn, md) {",
" var rval = [];",
"",
" // each value in 'rdn' in is a SET of RelativeDistinguishedName",
" var set, attr, obj;",
" for(var si = 0; si < rdn.value.length; ++si) {",
" // get the RelativeDistinguishedName set",
" set = rdn.value[si];",
"",
" // each value in the SET is an AttributeTypeAndValue sequence",
" // containing first a type (an OID) and second a value (defined by",
" // the OID)",
" for(var i = 0; i < set.value.length; ++i) {",
" obj = {};",
" attr = set.value[i];",
" obj.type = asn1.derToOid(attr.value[0].value);",
" obj.value = attr.value[1].value;",
" obj.valueTagClass = attr.value[1].type;",
" // if the OID is known, get its name and short name",
" if(obj.type in oids) {",
" obj.name = oids[obj.type];",
" if(obj.name in _shortNames) {",
" obj.shortName = _shortNames[obj.name];",
" }",
" }",
" if(md) {",
" md.update(obj.type);",
" md.update(obj.value);",
" }",
" rval.push(obj);",
" }",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Converts ASN.1 CRIAttributes into an array with objects that have type and",
" * value properties.",
" *",
" * @param attributes the CRIAttributes to convert.",
" */",
"pki.CRIAttributesAsArray = function(attributes) {",
" var rval = [];",
"",
" // each value in 'attributes' in is a SEQUENCE with an OID and a SET",
" for(var si = 0; si < attributes.length; ++si) {",
" // get the attribute sequence",
" var seq = attributes[si];",
"",
" // each value in the SEQUENCE containing first a type (an OID) and",
" // second a set of values (defined by the OID)",
" var type = asn1.derToOid(seq.value[0].value);",
" var values = seq.value[1].value;",
" for(var vi = 0; vi < values.length; ++vi) {",
" var obj = {};",
" obj.type = type;",
" obj.value = values[vi].value;",
" obj.valueTagClass = values[vi].type;",
" // if the OID is known, get its name and short name",
" if(obj.type in oids) {",
" obj.name = oids[obj.type];",
" if(obj.name in _shortNames) {",
" obj.shortName = _shortNames[obj.name];",
" }",
" }",
" // parse extensions",
" if(obj.type === oids.extensionRequest) {",
" obj.extensions = [];",
" for(var ei = 0; ei < obj.value.length; ++ei) {",
" obj.extensions.push(pki.certificateExtensionFromAsn1(obj.value[ei]));",
" }",
" }",
" rval.push(obj);",
" }",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Gets an issuer or subject attribute from its name, type, or short name.",
" *",
" * @param obj the issuer or subject object.",
" * @param options a short name string or an object with:",
" * shortName the short name for the attribute.",
" * name the name for the attribute.",
" * type the type for the attribute.",
" *",
" * @return the attribute.",
" */",
"function _getAttribute(obj, options) {",
" if(typeof options === 'string') {",
" options = {shortName: options};",
" }",
"",
" var rval = null;",
" var attr;",
" for(var i = 0; rval === null && i < obj.attributes.length; ++i) {",
" attr = obj.attributes[i];",
" if(options.type && options.type === attr.type) {",
" rval = attr;",
" } else if(options.name && options.name === attr.name) {",
" rval = attr;",
" } else if(options.shortName && options.shortName === attr.shortName) {",
" rval = attr;",
" }",
" }",
" return rval;",
"}",
"",
"/**",
" * Converts signature parameters from ASN.1 structure.",
" *",
" * Currently only RSASSA-PSS supported. The PKCS#1 v1.5 signature scheme had",
" * no parameters.",
" *",
" * RSASSA-PSS-params ::= SEQUENCE {",
" * hashAlgorithm [0] HashAlgorithm DEFAULT",
" * sha1Identifier,",
" * maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT",
" * mgf1SHA1Identifier,",
" * saltLength [2] INTEGER DEFAULT 20,",
" * trailerField [3] INTEGER DEFAULT 1",
" * }",
" *",
" * HashAlgorithm ::= AlgorithmIdentifier",
" *",
" * MaskGenAlgorithm ::= AlgorithmIdentifier",
" *",
" * AlgorithmIdentifer ::= SEQUENCE {",
" * algorithm OBJECT IDENTIFIER,",
" * parameters ANY DEFINED BY algorithm OPTIONAL",
" * }",
" *",
" * @param oid The OID specifying the signature algorithm",
" * @param obj The ASN.1 structure holding the parameters",
" * @param fillDefaults Whether to use return default values where omitted",
" * @return signature parameter object",
" */",
"var _readSignatureParameters = function(oid, obj, fillDefaults) {",
" var params = {};",
"",
" if(oid !== oids['RSASSA-PSS']) {",
" return params;",
" }",
"",
" if(fillDefaults) {",
" params = {",
" hash: {",
" algorithmOid: oids['sha1']",
" },",
" mgf: {",
" algorithmOid: oids['mgf1'],",
" hash: {",
" algorithmOid: oids['sha1']",
" }",
" },",
" saltLength: 20",
" };",
" }",
"",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, rsassaPssParameterValidator, capture, errors)) {",
" var error = new Error('Cannot read RSASSA-PSS parameter block.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" if(capture.hashOid !== undefined) {",
" params.hash = params.hash || {};",
" params.hash.algorithmOid = asn1.derToOid(capture.hashOid);",
" }",
"",
" if(capture.maskGenOid !== undefined) {",
" params.mgf = params.mgf || {};",
" params.mgf.algorithmOid = asn1.derToOid(capture.maskGenOid);",
" params.mgf.hash = params.mgf.hash || {};",
" params.mgf.hash.algorithmOid = asn1.derToOid(capture.maskGenHashOid);",
" }",
"",
" if(capture.saltLength !== undefined) {",
" params.saltLength = capture.saltLength.charCodeAt(0);",
" }",
"",
" return params;",
"};",
"",
"/**",
" * Converts an X.509 certificate from PEM format.",
" *",
" * Note: If the certificate is to be verified then compute hash should",
" * be set to true. This will scan the TBSCertificate part of the ASN.1",
" * object while it is converted so it doesn't need to be converted back",
" * to ASN.1-DER-encoding later.",
" *",
" * @param pem the PEM-formatted certificate.",
" * @param computeHash true to compute the hash for verification.",
" * @param strict true to be strict when checking ASN.1 value lengths, false to",
" * allow truncated values (default: true).",
" *",
" * @return the certificate.",
" */",
"pki.certificateFromPem = function(pem, computeHash, strict) {",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'CERTIFICATE' &&",
" msg.type !== 'X509 CERTIFICATE' &&",
" msg.type !== 'TRUSTED CERTIFICATE') {",
" var error = new Error('Could not convert certificate from PEM; PEM header type ' +",
" 'is not \"CERTIFICATE\", \"X509 CERTIFICATE\", or \"TRUSTED CERTIFICATE\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert certificate from PEM; PEM is encrypted.');",
" }",
"",
" // convert DER to ASN.1 object",
" var obj = asn1.fromDer(msg.body, strict);",
"",
" return pki.certificateFromAsn1(obj, computeHash);",
"};",
"",
"/**",
" * Converts an X.509 certificate to PEM format.",
" *",
" * @param cert the certificate.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted certificate.",
" */",
"pki.certificateToPem = function(cert, maxline) {",
" // convert to ASN.1, then DER, then PEM-encode",
" var msg = {",
" type: 'CERTIFICATE',",
" body: asn1.toDer(pki.certificateToAsn1(cert)).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"/**",
" * Converts an RSA public key from PEM format.",
" *",
" * @param pem the PEM-formatted public key.",
" *",
" * @return the public key.",
" */",
"pki.publicKeyFromPem = function(pem) {",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'PUBLIC KEY' && msg.type !== 'RSA PUBLIC KEY') {",
" var error = new Error('Could not convert public key from PEM; PEM header ' +",
" 'type is not \"PUBLIC KEY\" or \"RSA PUBLIC KEY\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert public key from PEM; PEM is encrypted.');",
" }",
"",
" // convert DER to ASN.1 object",
" var obj = asn1.fromDer(msg.body);",
"",
" return pki.publicKeyFromAsn1(obj);",
"};",
"",
"/**",
" * Converts an RSA public key to PEM format (using a SubjectPublicKeyInfo).",
" *",
" * @param key the public key.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted public key.",
" */",
"pki.publicKeyToPem = function(key, maxline) {",
" // convert to ASN.1, then DER, then PEM-encode",
" var msg = {",
" type: 'PUBLIC KEY',",
" body: asn1.toDer(pki.publicKeyToAsn1(key)).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"/**",
" * Converts an RSA public key to PEM format (using an RSAPublicKey).",
" *",
" * @param key the public key.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted public key.",
" */",
"pki.publicKeyToRSAPublicKeyPem = function(key, maxline) {",
" // convert to ASN.1, then DER, then PEM-encode",
" var msg = {",
" type: 'RSA PUBLIC KEY',",
" body: asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"/**",
" * Gets a fingerprint for the given public key.",
" *",
" * @param options the options to use.",
" * [md] the message digest object to use (defaults to forge.md.sha1).",
" * [type] the type of fingerprint, such as 'RSAPublicKey',",
" * 'SubjectPublicKeyInfo' (defaults to 'RSAPublicKey').",
" * [encoding] an alternative output encoding, such as 'hex'",
" * (defaults to none, outputs a byte buffer).",
" * [delimiter] the delimiter to use between bytes for 'hex' encoded",
" * output, eg: ':' (defaults to none).",
" *",
" * @return the fingerprint as a byte buffer or other encoding based on options.",
" */",
"pki.getPublicKeyFingerprint = function(key, options) {",
" options = options || {};",
" var md = options.md || forge.md.sha1.create();",
" var type = options.type || 'RSAPublicKey';",
"",
" var bytes;",
" switch(type) {",
" case 'RSAPublicKey':",
" bytes = asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes();",
" break;",
" case 'SubjectPublicKeyInfo':",
" bytes = asn1.toDer(pki.publicKeyToAsn1(key)).getBytes();",
" break;",
" default:",
" throw new Error('Unknown fingerprint type \"' + options.type + '\".');",
" }",
"",
" // hash public key bytes",
" md.start();",
" md.update(bytes);",
" var digest = md.digest();",
" if(options.encoding === 'hex') {",
" var hex = digest.toHex();",
" if(options.delimiter) {",
" return hex.match(/.{2}/g).join(options.delimiter);",
" }",
" return hex;",
" } else if(options.encoding === 'binary') {",
" return digest.getBytes();",
" } else if(options.encoding) {",
" throw new Error('Unknown encoding \"' + options.encoding + '\".');",
" }",
" return digest;",
"};",
"",
"/**",
" * Converts a PKCS#10 certification request (CSR) from PEM format.",
" *",
" * Note: If the certification request is to be verified then compute hash",
" * should be set to true. This will scan the CertificationRequestInfo part of",
" * the ASN.1 object while it is converted so it doesn't need to be converted",
" * back to ASN.1-DER-encoding later.",
" *",
" * @param pem the PEM-formatted certificate.",
" * @param computeHash true to compute the hash for verification.",
" * @param strict true to be strict when checking ASN.1 value lengths, false to",
" * allow truncated values (default: true).",
" *",
" * @return the certification request (CSR).",
" */",
"pki.certificationRequestFromPem = function(pem, computeHash, strict) {",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'CERTIFICATE REQUEST') {",
" var error = new Error('Could not convert certification request from PEM; ' +",
" 'PEM header type is not \"CERTIFICATE REQUEST\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert certification request from PEM; ' +",
" 'PEM is encrypted.');",
" }",
"",
" // convert DER to ASN.1 object",
" var obj = asn1.fromDer(msg.body, strict);",
"",
" return pki.certificationRequestFromAsn1(obj, computeHash);",
"};",
"",
"/**",
" * Converts a PKCS#10 certification request (CSR) to PEM format.",
" *",
" * @param csr the certification request.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted certification request.",
" */",
"pki.certificationRequestToPem = function(csr, maxline) {",
" // convert to ASN.1, then DER, then PEM-encode",
" var msg = {",
" type: 'CERTIFICATE REQUEST',",
" body: asn1.toDer(pki.certificationRequestToAsn1(csr)).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"/**",
" * Creates an empty X.509v3 RSA certificate.",
" *",
" * @return the certificate.",
" */",
"pki.createCertificate = function() {",
" var cert = {};",
" cert.version = 0x02;",
" cert.serialNumber = '00';",
" cert.signatureOid = null;",
" cert.signature = null;",
" cert.siginfo = {};",
" cert.siginfo.algorithmOid = null;",
" cert.validity = {};",
" cert.validity.notBefore = new Date();",
" cert.validity.notAfter = new Date();",
"",
" cert.issuer = {};",
" cert.issuer.getField = function(sn) {",
" return _getAttribute(cert.issuer, sn);",
" };",
" cert.issuer.addField = function(attr) {",
" _fillMissingFields([attr]);",
" cert.issuer.attributes.push(attr);",
" };",
" cert.issuer.attributes = [];",
" cert.issuer.hash = null;",
"",
" cert.subject = {};",
" cert.subject.getField = function(sn) {",
" return _getAttribute(cert.subject, sn);",
" };",
" cert.subject.addField = function(attr) {",
" _fillMissingFields([attr]);",
" cert.subject.attributes.push(attr);",
" };",
" cert.subject.attributes = [];",
" cert.subject.hash = null;",
"",
" cert.extensions = [];",
" cert.publicKey = null;",
" cert.md = null;",
"",
" /**",
" * Sets the subject of this certificate.",
" *",
" * @param attrs the array of subject attributes to use.",
" * @param uniqueId an optional a unique ID to use.",
" */",
" cert.setSubject = function(attrs, uniqueId) {",
" // set new attributes, clear hash",
" _fillMissingFields(attrs);",
" cert.subject.attributes = attrs;",
" delete cert.subject.uniqueId;",
" if(uniqueId) {",
" // TODO: support arbitrary bit length ids",
" cert.subject.uniqueId = uniqueId;",
" }",
" cert.subject.hash = null;",
" };",
"",
" /**",
" * Sets the issuer of this certificate.",
" *",
" * @param attrs the array of issuer attributes to use.",
" * @param uniqueId an optional a unique ID to use.",
" */",
" cert.setIssuer = function(attrs, uniqueId) {",
" // set new attributes, clear hash",
" _fillMissingFields(attrs);",
" cert.issuer.attributes = attrs;",
" delete cert.issuer.uniqueId;",
" if(uniqueId) {",
" // TODO: support arbitrary bit length ids",
" cert.issuer.uniqueId = uniqueId;",
" }",
" cert.issuer.hash = null;",
" };",
"",
" /**",
" * Sets the extensions of this certificate.",
" *",
" * @param exts the array of extensions to use.",
" */",
" cert.setExtensions = function(exts) {",
" for(var i = 0; i < exts.length; ++i) {",
" _fillMissingExtensionFields(exts[i], {cert: cert});",
" }",
" // set new extensions",
" cert.extensions = exts;",
" };",
"",
" /**",
" * Gets an extension by its name or id.",
" *",
" * @param options the name to use or an object with:",
" * name the name to use.",
" * id the id to use.",
" *",
" * @return the extension or null if not found.",
" */",
" cert.getExtension = function(options) {",
" if(typeof options === 'string') {",
" options = {name: options};",
" }",
"",
" var rval = null;",
" var ext;",
" for(var i = 0; rval === null && i < cert.extensions.length; ++i) {",
" ext = cert.extensions[i];",
" if(options.id && ext.id === options.id) {",
" rval = ext;",
" } else if(options.name && ext.name === options.name) {",
" rval = ext;",
" }",
" }",
" return rval;",
" };",
"",
" /**",
" * Signs this certificate using the given private key.",
" *",
" * @param key the private key to sign with.",
" * @param md the message digest object to use (defaults to forge.md.sha1).",
" */",
" cert.sign = function(key, md) {",
" // TODO: get signature OID from private key",
" cert.md = md || forge.md.sha1.create();",
" var algorithmOid = oids[cert.md.algorithm + 'WithRSAEncryption'];",
" if(!algorithmOid) {",
" var error = new Error('Could not compute certificate digest. ' +",
" 'Unknown message digest algorithm OID.');",
" error.algorithm = cert.md.algorithm;",
" throw error;",
" }",
" cert.signatureOid = cert.siginfo.algorithmOid = algorithmOid;",
"",
" // get TBSCertificate, convert to DER",
" cert.tbsCertificate = pki.getTBSCertificate(cert);",
" var bytes = asn1.toDer(cert.tbsCertificate);",
"",
" // digest and sign",
" cert.md.update(bytes.getBytes());",
" cert.signature = key.sign(cert.md);",
" };",
"",
" /**",
" * Attempts verify the signature on the passed certificate using this",
" * certificate's public key.",
" *",
" * @param child the certificate to verify.",
" *",
" * @return true if verified, false if not.",
" */",
" cert.verify = function(child) {",
" var rval = false;",
"",
" if(!cert.issued(child)) {",
" var issuer = child.issuer;",
" var subject = cert.subject;",
" var error = new Error('The parent certificate did not issue the given child ' +",
" 'certificate; the child certificate\\'s issuer does not match the ' +",
" 'parent\\'s subject.');",
" error.expectedIssuer = issuer.attributes;",
" error.actualIssuer = subject.attributes;",
" throw error;",
" }",
"",
" var md = child.md;",
" if(md === null) {",
" // check signature OID for supported signature types",
" if(child.signatureOid in oids) {",
" var oid = oids[child.signatureOid];",
" switch(oid) {",
" case 'sha1WithRSAEncryption':",
" md = forge.md.sha1.create();",
" break;",
" case 'md5WithRSAEncryption':",
" md = forge.md.md5.create();",
" break;",
" case 'sha256WithRSAEncryption':",
" md = forge.md.sha256.create();",
" break;",
" case 'sha384WithRSAEncryption':",
" md = forge.md.sha384.create();",
" break;",
" case 'sha512WithRSAEncryption':",
" md = forge.md.sha512.create();",
" break;",
" case 'RSASSA-PSS':",
" md = forge.md.sha256.create();",
" break;",
" }",
" }",
" if(md === null) {",
" var error = new Error('Could not compute certificate digest. ' +",
" 'Unknown signature OID.');",
" error.signatureOid = child.signatureOid;",
" throw error;",
" }",
"",
" // produce DER formatted TBSCertificate and digest it",
" var tbsCertificate = child.tbsCertificate || pki.getTBSCertificate(child);",
" var bytes = asn1.toDer(tbsCertificate);",
" md.update(bytes.getBytes());",
" }",
"",
" if(md !== null) {",
" var scheme;",
"",
" switch(child.signatureOid) {",
" case oids.sha1WithRSAEncryption:",
" scheme = undefined; /* use PKCS#1 v1.5 padding scheme */",
" break;",
" case oids['RSASSA-PSS']:",
" var hash, mgf;",
"",
" /* initialize mgf */",
" hash = oids[child.signatureParameters.mgf.hash.algorithmOid];",
" if(hash === undefined || forge.md[hash] === undefined) {",
" var error = new Error('Unsupported MGF hash function.');",
" error.oid = child.signatureParameters.mgf.hash.algorithmOid;",
" error.name = hash;",
" throw error;",
" }",
"",
" mgf = oids[child.signatureParameters.mgf.algorithmOid];",
" if(mgf === undefined || forge.mgf[mgf] === undefined) {",
" var error = new Error('Unsupported MGF function.');",
" error.oid = child.signatureParameters.mgf.algorithmOid;",
" error.name = mgf;",
" throw error;",
" }",
"",
" mgf = forge.mgf[mgf].create(forge.md[hash].create());",
"",
" /* initialize hash function */",
" hash = oids[child.signatureParameters.hash.algorithmOid];",
" if(hash === undefined || forge.md[hash] === undefined) {",
" throw {",
" message: 'Unsupported RSASSA-PSS hash function.',",
" oid: child.signatureParameters.hash.algorithmOid,",
" name: hash",
" };",
" }",
"",
" scheme = forge.pss.create(forge.md[hash].create(), mgf,",
" child.signatureParameters.saltLength);",
" break;",
" }",
"",
" // verify signature on cert using public key",
" rval = cert.publicKey.verify(",
" md.digest().getBytes(), child.signature, scheme);",
" }",
"",
" return rval;",
" };",
"",
" /**",
" * Returns true if this certificate's issuer matches the passed",
" * certificate's subject. Note that no signature check is performed.",
" *",
" * @param parent the certificate to check.",
" *",
" * @return true if this certificate's issuer matches the passed certificate's",
" * subject.",
" */",
" cert.isIssuer = function(parent) {",
" var rval = false;",
"",
" var i = cert.issuer;",
" var s = parent.subject;",
"",
" // compare hashes if present",
" if(i.hash && s.hash) {",
" rval = (i.hash === s.hash);",
" } else if(i.attributes.length === s.attributes.length) {",
" // all attributes are the same so issuer matches subject",
" rval = true;",
" var iattr, sattr;",
" for(var n = 0; rval && n < i.attributes.length; ++n) {",
" iattr = i.attributes[n];",
" sattr = s.attributes[n];",
" if(iattr.type !== sattr.type || iattr.value !== sattr.value) {",
" // attribute mismatch",
" rval = false;",
" }",
" }",
" }",
"",
" return rval;",
" };",
"",
" /**",
" * Returns true if this certificate's subject matches the issuer of the",
" * given certificate). Note that not signature check is performed.",
" *",
" * @param child the certificate to check.",
" *",
" * @return true if this certificate's subject matches the passed",
" * certificate's issuer.",
" */",
" cert.issued = function(child) {",
" return child.isIssuer(cert);",
" };",
"",
" /**",
" * Generates the subjectKeyIdentifier for this certificate as byte buffer.",
" *",
" * @return the subjectKeyIdentifier for this certificate as byte buffer.",
" */",
" cert.generateSubjectKeyIdentifier = function() {",
" /* See: 4.2.1.2 section of the the RFC3280, keyIdentifier is either:",
"",
" (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the",
" value of the BIT STRING subjectPublicKey (excluding the tag,",
" length, and number of unused bits).",
"",
" (2) The keyIdentifier is composed of a four bit type field with",
" the value 0100 followed by the least significant 60 bits of the",
" SHA-1 hash of the value of the BIT STRING subjectPublicKey",
" (excluding the tag, length, and number of unused bit string bits).",
" */",
"",
" // skipping the tag, length, and number of unused bits is the same",
" // as just using the RSAPublicKey (for RSA keys, which are the",
" // only ones supported)",
" return pki.getPublicKeyFingerprint(cert.publicKey, {type: 'RSAPublicKey'});",
" };",
"",
" /**",
" * Verifies the subjectKeyIdentifier extension value for this certificate",
" * against its public key. If no extension is found, false will be",
" * returned.",
" *",
" * @return true if verified, false if not.",
" */",
" cert.verifySubjectKeyIdentifier = function() {",
" var oid = oids['subjectKeyIdentifier'];",
" for(var i = 0; i < cert.extensions.length; ++i) {",
" var ext = cert.extensions[i];",
" if(ext.id === oid) {",
" var ski = cert.generateSubjectKeyIdentifier().getBytes();",
" return (forge.util.hexToBytes(ext.subjectKeyIdentifier) === ski);",
" }",
" }",
" return false;",
" };",
"",
" return cert;",
"};",
"",
"/**",
" * Converts an X.509v3 RSA certificate from an ASN.1 object.",
" *",
" * Note: If the certificate is to be verified then compute hash should",
" * be set to true. There is currently no implementation for converting",
" * a certificate back to ASN.1 so the TBSCertificate part of the ASN.1",
" * object needs to be scanned before the cert object is created.",
" *",
" * @param obj the asn1 representation of an X.509v3 RSA certificate.",
" * @param computeHash true to compute the hash for verification.",
" *",
" * @return the certificate.",
" */",
"pki.certificateFromAsn1 = function(obj, computeHash) {",
" // validate certificate and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, x509CertificateValidator, capture, errors)) {",
" var error = new Error('Cannot read X.509 certificate. ' +",
" 'ASN.1 object is not an X509v3 Certificate.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" // get oid",
" var oid = asn1.derToOid(capture.publicKeyOid);",
" if(oid !== pki.oids.rsaEncryption) {",
" throw new Error('Cannot read public key. OID is not RSA.');",
" }",
"",
" // create certificate",
" var cert = pki.createCertificate();",
" cert.version = capture.certVersion ?",
" capture.certVersion.charCodeAt(0) : 0;",
" var serial = forge.util.createBuffer(capture.certSerialNumber);",
" cert.serialNumber = serial.toHex();",
" cert.signatureOid = forge.asn1.derToOid(capture.certSignatureOid);",
" cert.signatureParameters = _readSignatureParameters(",
" cert.signatureOid, capture.certSignatureParams, true);",
" cert.siginfo.algorithmOid = forge.asn1.derToOid(capture.certinfoSignatureOid);",
" cert.siginfo.parameters = _readSignatureParameters(cert.siginfo.algorithmOid,",
" capture.certinfoSignatureParams, false);",
" cert.signature = capture.certSignature;",
"",
" var validity = [];",
" if(capture.certValidity1UTCTime !== undefined) {",
" validity.push(asn1.utcTimeToDate(capture.certValidity1UTCTime));",
" }",
" if(capture.certValidity2GeneralizedTime !== undefined) {",
" validity.push(asn1.generalizedTimeToDate(",
" capture.certValidity2GeneralizedTime));",
" }",
" if(capture.certValidity3UTCTime !== undefined) {",
" validity.push(asn1.utcTimeToDate(capture.certValidity3UTCTime));",
" }",
" if(capture.certValidity4GeneralizedTime !== undefined) {",
" validity.push(asn1.generalizedTimeToDate(",
" capture.certValidity4GeneralizedTime));",
" }",
" if(validity.length > 2) {",
" throw new Error('Cannot read notBefore/notAfter validity times; more ' +",
" 'than two times were provided in the certificate.');",
" }",
" if(validity.length < 2) {",
" throw new Error('Cannot read notBefore/notAfter validity times; they ' +",
" 'were not provided as either UTCTime or GeneralizedTime.');",
" }",
" cert.validity.notBefore = validity[0];",
" cert.validity.notAfter = validity[1];",
"",
" // keep TBSCertificate to preserve signature when exporting",
" cert.tbsCertificate = capture.tbsCertificate;",
"",
" if(computeHash) {",
" // check signature OID for supported signature types",
" cert.md = null;",
" if(cert.signatureOid in oids) {",
" var oid = oids[cert.signatureOid];",
" switch(oid) {",
" case 'sha1WithRSAEncryption':",
" cert.md = forge.md.sha1.create();",
" break;",
" case 'md5WithRSAEncryption':",
" cert.md = forge.md.md5.create();",
" break;",
" case 'sha256WithRSAEncryption':",
" cert.md = forge.md.sha256.create();",
" break;",
" case 'sha384WithRSAEncryption':",
" cert.md = forge.md.sha384.create();",
" break;",
" case 'sha512WithRSAEncryption':",
" cert.md = forge.md.sha512.create();",
" break;",
" case 'RSASSA-PSS':",
" cert.md = forge.md.sha256.create();",
" break;",
" }",
" }",
" if(cert.md === null) {",
" var error = new Error('Could not compute certificate digest. ' +",
" 'Unknown signature OID.');",
" error.signatureOid = cert.signatureOid;",
" throw error;",
" }",
"",
" // produce DER formatted TBSCertificate and digest it",
" var bytes = asn1.toDer(cert.tbsCertificate);",
" cert.md.update(bytes.getBytes());",
" }",
"",
" // handle issuer, build issuer message digest",
" var imd = forge.md.sha1.create();",
" cert.issuer.getField = function(sn) {",
" return _getAttribute(cert.issuer, sn);",
" };",
" cert.issuer.addField = function(attr) {",
" _fillMissingFields([attr]);",
" cert.issuer.attributes.push(attr);",
" };",
" cert.issuer.attributes = pki.RDNAttributesAsArray(capture.certIssuer, imd);",
" if(capture.certIssuerUniqueId) {",
" cert.issuer.uniqueId = capture.certIssuerUniqueId;",
" }",
" cert.issuer.hash = imd.digest().toHex();",
"",
" // handle subject, build subject message digest",
" var smd = forge.md.sha1.create();",
" cert.subject.getField = function(sn) {",
" return _getAttribute(cert.subject, sn);",
" };",
" cert.subject.addField = function(attr) {",
" _fillMissingFields([attr]);",
" cert.subject.attributes.push(attr);",
" };",
" cert.subject.attributes = pki.RDNAttributesAsArray(capture.certSubject, smd);",
" if(capture.certSubjectUniqueId) {",
" cert.subject.uniqueId = capture.certSubjectUniqueId;",
" }",
" cert.subject.hash = smd.digest().toHex();",
"",
" // handle extensions",
" if(capture.certExtensions) {",
" cert.extensions = pki.certificateExtensionsFromAsn1(capture.certExtensions);",
" } else {",
" cert.extensions = [];",
" }",
"",
" // convert RSA public key from ASN.1",
" cert.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo);",
"",
" return cert;",
"};",
"",
"/**",
" * Converts an ASN.1 extensions object (with extension sequences as its",
" * values) into an array of extension objects with types and values.",
" *",
" * Supported extensions:",
" *",
" * id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }",
" * KeyUsage ::= BIT STRING {",
" * digitalSignature (0),",
" * nonRepudiation (1),",
" * keyEncipherment (2),",
" * dataEncipherment (3),",
" * keyAgreement (4),",
" * keyCertSign (5),",
" * cRLSign (6),",
" * encipherOnly (7),",
" * decipherOnly (8)",
" * }",
" *",
" * id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }",
" * BasicConstraints ::= SEQUENCE {",
" * cA BOOLEAN DEFAULT FALSE,",
" * pathLenConstraint INTEGER (0..MAX) OPTIONAL",
" * }",
" *",
" * subjectAltName EXTENSION ::= {",
" * SYNTAX GeneralNames",
" * IDENTIFIED BY id-ce-subjectAltName",
" * }",
" *",
" * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName",
" *",
" * GeneralName ::= CHOICE {",
" * otherName [0] INSTANCE OF OTHER-NAME,",
" * rfc822Name [1] IA5String,",
" * dNSName [2] IA5String,",
" * x400Address [3] ORAddress,",
" * directoryName [4] Name,",
" * ediPartyName [5] EDIPartyName,",
" * uniformResourceIdentifier [6] IA5String,",
" * IPAddress [7] OCTET STRING,",
" * registeredID [8] OBJECT IDENTIFIER",
" * }",
" *",
" * OTHER-NAME ::= TYPE-IDENTIFIER",
" *",
" * EDIPartyName ::= SEQUENCE {",
" * nameAssigner [0] DirectoryString {ub-name} OPTIONAL,",
" * partyName [1] DirectoryString {ub-name}",
" * }",
" *",
" * @param exts the extensions ASN.1 with extension sequences to parse.",
" *",
" * @return the array.",
" */",
"pki.certificateExtensionsFromAsn1 = function(exts) {",
" var rval = [];",
" for(var i = 0; i < exts.value.length; ++i) {",
" // get extension sequence",
" var extseq = exts.value[i];",
" for(var ei = 0; ei < extseq.value.length; ++ei) {",
" rval.push(pki.certificateExtensionFromAsn1(extseq.value[ei]));",
" }",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Parses a single certificate extension from ASN.1.",
" *",
" * @param ext the extension in ASN.1 format.",
" *",
" * @return the parsed extension as an object.",
" */",
"pki.certificateExtensionFromAsn1 = function(ext) {",
" // an extension has:",
" // [0] extnID OBJECT IDENTIFIER",
" // [1] critical BOOLEAN DEFAULT FALSE",
" // [2] extnValue OCTET STRING",
" var e = {};",
" e.id = asn1.derToOid(ext.value[0].value);",
" e.critical = false;",
" if(ext.value[1].type === asn1.Type.BOOLEAN) {",
" e.critical = (ext.value[1].value.charCodeAt(0) !== 0x00);",
" e.value = ext.value[2].value;",
" } else {",
" e.value = ext.value[1].value;",
" }",
" // if the oid is known, get its name",
" if(e.id in oids) {",
" e.name = oids[e.id];",
"",
" // handle key usage",
" if(e.name === 'keyUsage') {",
" // get value as BIT STRING",
" var ev = asn1.fromDer(e.value);",
" var b2 = 0x00;",
" var b3 = 0x00;",
" if(ev.value.length > 1) {",
" // skip first byte, just indicates unused bits which",
" // will be padded with 0s anyway",
" // get bytes with flag bits",
" b2 = ev.value.charCodeAt(1);",
" b3 = ev.value.length > 2 ? ev.value.charCodeAt(2) : 0;",
" }",
" // set flags",
" e.digitalSignature = (b2 & 0x80) === 0x80;",
" e.nonRepudiation = (b2 & 0x40) === 0x40;",
" e.keyEncipherment = (b2 & 0x20) === 0x20;",
" e.dataEncipherment = (b2 & 0x10) === 0x10;",
" e.keyAgreement = (b2 & 0x08) === 0x08;",
" e.keyCertSign = (b2 & 0x04) === 0x04;",
" e.cRLSign = (b2 & 0x02) === 0x02;",
" e.encipherOnly = (b2 & 0x01) === 0x01;",
" e.decipherOnly = (b3 & 0x80) === 0x80;",
" } else if(e.name === 'basicConstraints') {",
" // handle basic constraints",
" // get value as SEQUENCE",
" var ev = asn1.fromDer(e.value);",
" // get cA BOOLEAN flag (defaults to false)",
" if(ev.value.length > 0 && ev.value[0].type === asn1.Type.BOOLEAN) {",
" e.cA = (ev.value[0].value.charCodeAt(0) !== 0x00);",
" } else {",
" e.cA = false;",
" }",
" // get path length constraint",
" var value = null;",
" if(ev.value.length > 0 && ev.value[0].type === asn1.Type.INTEGER) {",
" value = ev.value[0].value;",
" } else if(ev.value.length > 1) {",
" value = ev.value[1].value;",
" }",
" if(value !== null) {",
" e.pathLenConstraint = asn1.derToInteger(value);",
" }",
" } else if(e.name === 'extKeyUsage') {",
" // handle extKeyUsage",
" // value is a SEQUENCE of OIDs",
" var ev = asn1.fromDer(e.value);",
" for(var vi = 0; vi < ev.value.length; ++vi) {",
" var oid = asn1.derToOid(ev.value[vi].value);",
" if(oid in oids) {",
" e[oids[oid]] = true;",
" } else {",
" e[oid] = true;",
" }",
" }",
" } else if(e.name === 'nsCertType') {",
" // handle nsCertType",
" // get value as BIT STRING",
" var ev = asn1.fromDer(e.value);",
" var b2 = 0x00;",
" if(ev.value.length > 1) {",
" // skip first byte, just indicates unused bits which",
" // will be padded with 0s anyway",
" // get bytes with flag bits",
" b2 = ev.value.charCodeAt(1);",
" }",
" // set flags",
" e.client = (b2 & 0x80) === 0x80;",
" e.server = (b2 & 0x40) === 0x40;",
" e.email = (b2 & 0x20) === 0x20;",
" e.objsign = (b2 & 0x10) === 0x10;",
" e.reserved = (b2 & 0x08) === 0x08;",
" e.sslCA = (b2 & 0x04) === 0x04;",
" e.emailCA = (b2 & 0x02) === 0x02;",
" e.objCA = (b2 & 0x01) === 0x01;",
" } else if(",
" e.name === 'subjectAltName' ||",
" e.name === 'issuerAltName') {",
" // handle subjectAltName/issuerAltName",
" e.altNames = [];",
"",
" // ev is a SYNTAX SEQUENCE",
" var gn;",
" var ev = asn1.fromDer(e.value);",
" for(var n = 0; n < ev.value.length; ++n) {",
" // get GeneralName",
" gn = ev.value[n];",
"",
" var altName = {",
" type: gn.type,",
" value: gn.value",
" };",
" e.altNames.push(altName);",
"",
" // Note: Support for types 1,2,6,7,8",
" switch(gn.type) {",
" // rfc822Name",
" case 1:",
" // dNSName",
" case 2:",
" // uniformResourceIdentifier (URI)",
" case 6:",
" break;",
" // IPAddress",
" case 7:",
" // convert to IPv4/IPv6 string representation",
" altName.ip = forge.util.bytesToIP(gn.value);",
" break;",
" // registeredID",
" case 8:",
" altName.oid = asn1.derToOid(gn.value);",
" break;",
" default:",
" // unsupported",
" }",
" }",
" } else if(e.name === 'subjectKeyIdentifier') {",
" // value is an OCTETSTRING w/the hash of the key-type specific",
" // public key structure (eg: RSAPublicKey)",
" var ev = asn1.fromDer(e.value);",
" e.subjectKeyIdentifier = forge.util.bytesToHex(ev.value);",
" }",
" }",
" return e;",
"};",
"",
"/**",
" * Converts a PKCS#10 certification request (CSR) from an ASN.1 object.",
" *",
" * Note: If the certification request is to be verified then compute hash",
" * should be set to true. There is currently no implementation for converting",
" * a certificate back to ASN.1 so the CertificationRequestInfo part of the",
" * ASN.1 object needs to be scanned before the csr object is created.",
" *",
" * @param obj the asn1 representation of a PKCS#10 certification request (CSR).",
" * @param computeHash true to compute the hash for verification.",
" *",
" * @return the certification request (CSR).",
" */",
"pki.certificationRequestFromAsn1 = function(obj, computeHash) {",
" // validate certification request and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, certificationRequestValidator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#10 certificate request. ' +",
" 'ASN.1 object is not a PKCS#10 CertificationRequest.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" // get oid",
" var oid = asn1.derToOid(capture.publicKeyOid);",
" if(oid !== pki.oids.rsaEncryption) {",
" throw new Error('Cannot read public key. OID is not RSA.');",
" }",
"",
" // create certification request",
" var csr = pki.createCertificationRequest();",
" csr.version = capture.csrVersion ? capture.csrVersion.charCodeAt(0) : 0;",
" csr.signatureOid = forge.asn1.derToOid(capture.csrSignatureOid);",
" csr.signatureParameters = _readSignatureParameters(",
" csr.signatureOid, capture.csrSignatureParams, true);",
" csr.siginfo.algorithmOid = forge.asn1.derToOid(capture.csrSignatureOid);",
" csr.siginfo.parameters = _readSignatureParameters(",
" csr.siginfo.algorithmOid, capture.csrSignatureParams, false);",
" csr.signature = capture.csrSignature;",
"",
" // keep CertificationRequestInfo to preserve signature when exporting",
" csr.certificationRequestInfo = capture.certificationRequestInfo;",
"",
" if(computeHash) {",
" // check signature OID for supported signature types",
" csr.md = null;",
" if(csr.signatureOid in oids) {",
" var oid = oids[csr.signatureOid];",
" switch(oid) {",
" case 'sha1WithRSAEncryption':",
" csr.md = forge.md.sha1.create();",
" break;",
" case 'md5WithRSAEncryption':",
" csr.md = forge.md.md5.create();",
" break;",
" case 'sha256WithRSAEncryption':",
" csr.md = forge.md.sha256.create();",
" break;",
" case 'sha384WithRSAEncryption':",
" csr.md = forge.md.sha384.create();",
" break;",
" case 'sha512WithRSAEncryption':",
" csr.md = forge.md.sha512.create();",
" break;",
" case 'RSASSA-PSS':",
" csr.md = forge.md.sha256.create();",
" break;",
" }",
" }",
" if(csr.md === null) {",
" var error = new Error('Could not compute certification request digest. ' +",
" 'Unknown signature OID.');",
" error.signatureOid = csr.signatureOid;",
" throw error;",
" }",
"",
" // produce DER formatted CertificationRequestInfo and digest it",
" var bytes = asn1.toDer(csr.certificationRequestInfo);",
" csr.md.update(bytes.getBytes());",
" }",
"",
" // handle subject, build subject message digest",
" var smd = forge.md.sha1.create();",
" csr.subject.getField = function(sn) {",
" return _getAttribute(csr.subject, sn);",
" };",
" csr.subject.addField = function(attr) {",
" _fillMissingFields([attr]);",
" csr.subject.attributes.push(attr);",
" };",
" csr.subject.attributes = pki.RDNAttributesAsArray(",
" capture.certificationRequestInfoSubject, smd);",
" csr.subject.hash = smd.digest().toHex();",
"",
" // convert RSA public key from ASN.1",
" csr.publicKey = pki.publicKeyFromAsn1(capture.subjectPublicKeyInfo);",
"",
" // convert attributes from ASN.1",
" csr.getAttribute = function(sn) {",
" return _getAttribute(csr, sn);",
" };",
" csr.addAttribute = function(attr) {",
" _fillMissingFields([attr]);",
" csr.attributes.push(attr);",
" };",
" csr.attributes = pki.CRIAttributesAsArray(",
" capture.certificationRequestInfoAttributes || []);",
"",
" return csr;",
"};",
"",
"/**",
" * Creates an empty certification request (a CSR or certificate signing",
" * request). Once created, its public key and attributes can be set and then",
" * it can be signed.",
" *",
" * @return the empty certification request.",
" */",
"pki.createCertificationRequest = function() {",
" var csr = {};",
" csr.version = 0x00;",
" csr.signatureOid = null;",
" csr.signature = null;",
" csr.siginfo = {};",
" csr.siginfo.algorithmOid = null;",
"",
" csr.subject = {};",
" csr.subject.getField = function(sn) {",
" return _getAttribute(csr.subject, sn);",
" };",
" csr.subject.addField = function(attr) {",
" _fillMissingFields([attr]);",
" csr.subject.attributes.push(attr);",
" };",
" csr.subject.attributes = [];",
" csr.subject.hash = null;",
"",
" csr.publicKey = null;",
" csr.attributes = [];",
" csr.getAttribute = function(sn) {",
" return _getAttribute(csr, sn);",
" };",
" csr.addAttribute = function(attr) {",
" _fillMissingFields([attr]);",
" csr.attributes.push(attr);",
" };",
" csr.md = null;",
"",
" /**",
" * Sets the subject of this certification request.",
" *",
" * @param attrs the array of subject attributes to use.",
" */",
" csr.setSubject = function(attrs) {",
" // set new attributes",
" _fillMissingFields(attrs);",
" csr.subject.attributes = attrs;",
" csr.subject.hash = null;",
" };",
"",
" /**",
" * Sets the attributes of this certification request.",
" *",
" * @param attrs the array of attributes to use.",
" */",
" csr.setAttributes = function(attrs) {",
" // set new attributes",
" _fillMissingFields(attrs);",
" csr.attributes = attrs;",
" };",
"",
" /**",
" * Signs this certification request using the given private key.",
" *",
" * @param key the private key to sign with.",
" * @param md the message digest object to use (defaults to forge.md.sha1).",
" */",
" csr.sign = function(key, md) {",
" // TODO: get signature OID from private key",
" csr.md = md || forge.md.sha1.create();",
" var algorithmOid = oids[csr.md.algorithm + 'WithRSAEncryption'];",
" if(!algorithmOid) {",
" var error = new Error('Could not compute certification request digest. ' +",
" 'Unknown message digest algorithm OID.');",
" error.algorithm = csr.md.algorithm;",
" throw error;",
" }",
" csr.signatureOid = csr.siginfo.algorithmOid = algorithmOid;",
"",
" // get CertificationRequestInfo, convert to DER",
" csr.certificationRequestInfo = pki.getCertificationRequestInfo(csr);",
" var bytes = asn1.toDer(csr.certificationRequestInfo);",
"",
" // digest and sign",
" csr.md.update(bytes.getBytes());",
" csr.signature = key.sign(csr.md);",
" };",
"",
" /**",
" * Attempts verify the signature on the passed certification request using",
" * its public key.",
" *",
" * A CSR that has been exported to a file in PEM format can be verified using",
" * OpenSSL using this command:",
" *",
" * openssl req -in -verify -noout -text",
" *",
" * @return true if verified, false if not.",
" */",
" csr.verify = function() {",
" var rval = false;",
"",
" var md = csr.md;",
" if(md === null) {",
" // check signature OID for supported signature types",
" if(csr.signatureOid in oids) {",
" // TODO: create DRY `OID to md` function",
" var oid = oids[csr.signatureOid];",
" switch(oid) {",
" case 'sha1WithRSAEncryption':",
" md = forge.md.sha1.create();",
" break;",
" case 'md5WithRSAEncryption':",
" md = forge.md.md5.create();",
" break;",
" case 'sha256WithRSAEncryption':",
" md = forge.md.sha256.create();",
" break;",
" case 'sha384WithRSAEncryption':",
" md = forge.md.sha384.create();",
" break;",
" case 'sha512WithRSAEncryption':",
" md = forge.md.sha512.create();",
" break;",
" case 'RSASSA-PSS':",
" md = forge.md.sha256.create();",
" break;",
" }",
" }",
" if(md === null) {",
" var error = new Error('Could not compute certification request digest. ' +",
" 'Unknown signature OID.');",
" error.signatureOid = csr.signatureOid;",
" throw error;",
" }",
"",
" // produce DER formatted CertificationRequestInfo and digest it",
" var cri = csr.certificationRequestInfo ||",
" pki.getCertificationRequestInfo(csr);",
" var bytes = asn1.toDer(cri);",
" md.update(bytes.getBytes());",
" }",
"",
" if(md !== null) {",
" var scheme;",
"",
" switch(csr.signatureOid) {",
" case oids.sha1WithRSAEncryption:",
" /* use PKCS#1 v1.5 padding scheme */",
" break;",
" case oids['RSASSA-PSS']:",
" var hash, mgf;",
"",
" /* initialize mgf */",
" hash = oids[csr.signatureParameters.mgf.hash.algorithmOid];",
" if(hash === undefined || forge.md[hash] === undefined) {",
" var error = new Error('Unsupported MGF hash function.');",
" error.oid = csr.signatureParameters.mgf.hash.algorithmOid;",
" error.name = hash;",
" throw error;",
" }",
"",
" mgf = oids[csr.signatureParameters.mgf.algorithmOid];",
" if(mgf === undefined || forge.mgf[mgf] === undefined) {",
" var error = new Error('Unsupported MGF function.');",
" error.oid = csr.signatureParameters.mgf.algorithmOid;",
" error.name = mgf;",
" throw error;",
" }",
"",
" mgf = forge.mgf[mgf].create(forge.md[hash].create());",
"",
" /* initialize hash function */",
" hash = oids[csr.signatureParameters.hash.algorithmOid];",
" if(hash === undefined || forge.md[hash] === undefined) {",
" var error = new Error('Unsupported RSASSA-PSS hash function.');",
" error.oid = csr.signatureParameters.hash.algorithmOid;",
" error.name = hash;",
" throw error;",
" }",
"",
" scheme = forge.pss.create(forge.md[hash].create(), mgf,",
" csr.signatureParameters.saltLength);",
" break;",
" }",
"",
" // verify signature on csr using its public key",
" rval = csr.publicKey.verify(",
" md.digest().getBytes(), csr.signature, scheme);",
" }",
"",
" return rval;",
" };",
"",
" return csr;",
"};",
"",
"/**",
" * Converts an X.509 subject or issuer to an ASN.1 RDNSequence.",
" *",
" * @param obj the subject or issuer (distinguished name).",
" *",
" * @return the ASN.1 RDNSequence.",
" */",
"function _dnToAsn1(obj) {",
" // create an empty RDNSequence",
" var rval = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
"",
" // iterate over attributes",
" var attr, set;",
" var attrs = obj.attributes;",
" for(var i = 0; i < attrs.length; ++i) {",
" attr = attrs[i];",
" var value = attr.value;",
"",
" // reuse tag class for attribute value if available",
" var valueTagClass = asn1.Type.PRINTABLESTRING;",
" if('valueTagClass' in attr) {",
" valueTagClass = attr.valueTagClass;",
"",
" if(valueTagClass === asn1.Type.UTF8) {",
" value = forge.util.encodeUtf8(value);",
" }",
" // FIXME: handle more encodings",
" }",
"",
" // create a RelativeDistinguishedName set",
" // each value in the set is an AttributeTypeAndValue first",
" // containing the type (an OID) and second the value",
" set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // AttributeType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(attr.type).getBytes()),",
" // AttributeValue",
" asn1.create(asn1.Class.UNIVERSAL, valueTagClass, false, value)",
" ])",
" ]);",
" rval.value.push(set);",
" }",
"",
" return rval;",
"}",
"",
"/**",
" * Gets all printable attributes (typically of an issuer or subject) in a",
" * simplified JSON format for display.",
" *",
" * @param attrs the attributes.",
" *",
" * @return the JSON for display.",
" */",
"function _getAttributesAsJson(attrs) {",
" var rval = {};",
" for(var i = 0; i < attrs.length; ++i) {",
" var attr = attrs[i];",
" if(attr.shortName && (",
" attr.valueTagClass === asn1.Type.UTF8 ||",
" attr.valueTagClass === asn1.Type.PRINTABLESTRING ||",
" attr.valueTagClass === asn1.Type.IA5STRING)) {",
" var value = attr.value;",
" if(attr.valueTagClass === asn1.Type.UTF8) {",
" value = forge.util.encodeUtf8(attr.value);",
" }",
" if(!(attr.shortName in rval)) {",
" rval[attr.shortName] = value;",
" } else if(forge.util.isArray(rval[attr.shortName])) {",
" rval[attr.shortName].push(value);",
" } else {",
" rval[attr.shortName] = [rval[attr.shortName], value];",
" }",
" }",
" }",
" return rval;",
"}",
"",
"/**",
" * Fills in missing fields in attributes.",
" *",
" * @param attrs the attributes to fill missing fields in.",
" */",
"function _fillMissingFields(attrs) {",
" var attr;",
" for(var i = 0; i < attrs.length; ++i) {",
" attr = attrs[i];",
"",
" // populate missing name",
" if(typeof attr.name === 'undefined') {",
" if(attr.type && attr.type in pki.oids) {",
" attr.name = pki.oids[attr.type];",
" } else if(attr.shortName && attr.shortName in _shortNames) {",
" attr.name = pki.oids[_shortNames[attr.shortName]];",
" }",
" }",
"",
" // populate missing type (OID)",
" if(typeof attr.type === 'undefined') {",
" if(attr.name && attr.name in pki.oids) {",
" attr.type = pki.oids[attr.name];",
" } else {",
" var error = new Error('Attribute type not specified.');",
" error.attribute = attr;",
" throw error;",
" }",
" }",
"",
" // populate missing shortname",
" if(typeof attr.shortName === 'undefined') {",
" if(attr.name && attr.name in _shortNames) {",
" attr.shortName = _shortNames[attr.name];",
" }",
" }",
"",
" // convert extensions to value",
" if(attr.type === oids.extensionRequest) {",
" attr.valueConstructed = true;",
" attr.valueTagClass = asn1.Type.SEQUENCE;",
" if(!attr.value && attr.extensions) {",
" attr.value = [];",
" for(var ei = 0; ei < attr.extensions.length; ++ei) {",
" attr.value.push(pki.certificateExtensionToAsn1(",
" _fillMissingExtensionFields(attr.extensions[ei])));",
" }",
" }",
" }",
"",
" if(typeof attr.value === 'undefined') {",
" var error = new Error('Attribute value not specified.');",
" error.attribute = attr;",
" throw error;",
" }",
" }",
"}",
"",
"/**",
" * Fills in missing fields in certificate extensions.",
" *",
" * @param e the extension.",
" * @param [options] the options to use.",
" * [cert] the certificate the extensions are for.",
" *",
" * @return the extension.",
" */",
"function _fillMissingExtensionFields(e, options) {",
" options = options || {};",
"",
" // populate missing name",
" if(typeof e.name === 'undefined') {",
" if(e.id && e.id in pki.oids) {",
" e.name = pki.oids[e.id];",
" }",
" }",
"",
" // populate missing id",
" if(typeof e.id === 'undefined') {",
" if(e.name && e.name in pki.oids) {",
" e.id = pki.oids[e.name];",
" } else {",
" var error = new Error('Extension ID not specified.');",
" error.extension = e;",
" throw error;",
" }",
" }",
"",
" if(typeof e.value !== 'undefined') {",
" return e;",
" }",
"",
" // handle missing value:",
"",
" // value is a BIT STRING",
" if(e.name === 'keyUsage') {",
" // build flags",
" var unused = 0;",
" var b2 = 0x00;",
" var b3 = 0x00;",
" if(e.digitalSignature) {",
" b2 |= 0x80;",
" unused = 7;",
" }",
" if(e.nonRepudiation) {",
" b2 |= 0x40;",
" unused = 6;",
" }",
" if(e.keyEncipherment) {",
" b2 |= 0x20;",
" unused = 5;",
" }",
" if(e.dataEncipherment) {",
" b2 |= 0x10;",
" unused = 4;",
" }",
" if(e.keyAgreement) {",
" b2 |= 0x08;",
" unused = 3;",
" }",
" if(e.keyCertSign) {",
" b2 |= 0x04;",
" unused = 2;",
" }",
" if(e.cRLSign) {",
" b2 |= 0x02;",
" unused = 1;",
" }",
" if(e.encipherOnly) {",
" b2 |= 0x01;",
" unused = 0;",
" }",
" if(e.decipherOnly) {",
" b3 |= 0x80;",
" unused = 7;",
" }",
"",
" // create bit string",
" var value = String.fromCharCode(unused);",
" if(b3 !== 0) {",
" value += String.fromCharCode(b2) + String.fromCharCode(b3);",
" } else if(b2 !== 0) {",
" value += String.fromCharCode(b2);",
" }",
" e.value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value);",
" } else if(e.name === 'basicConstraints') {",
" // basicConstraints is a SEQUENCE",
" e.value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
" // cA BOOLEAN flag defaults to false",
" if(e.cA) {",
" e.value.value.push(asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false,",
" String.fromCharCode(0xFF)));",
" }",
" if('pathLenConstraint' in e) {",
" e.value.value.push(asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(e.pathLenConstraint).getBytes()));",
" }",
" } else if(e.name === 'extKeyUsage') {",
" // extKeyUsage is a SEQUENCE of OIDs",
" e.value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
" var seq = e.value.value;",
" for(var key in e) {",
" if(e[key] !== true) {",
" continue;",
" }",
" // key is name in OID map",
" if(key in oids) {",
" seq.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID,",
" false, asn1.oidToDer(oids[key]).getBytes()));",
" } else if(key.indexOf('.') !== -1) {",
" // assume key is an OID",
" seq.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID,",
" false, asn1.oidToDer(key).getBytes()));",
" }",
" }",
" } else if(e.name === 'nsCertType') {",
" // nsCertType is a BIT STRING",
" // build flags",
" var unused = 0;",
" var b2 = 0x00;",
"",
" if(e.client) {",
" b2 |= 0x80;",
" unused = 7;",
" }",
" if(e.server) {",
" b2 |= 0x40;",
" unused = 6;",
" }",
" if(e.email) {",
" b2 |= 0x20;",
" unused = 5;",
" }",
" if(e.objsign) {",
" b2 |= 0x10;",
" unused = 4;",
" }",
" if(e.reserved) {",
" b2 |= 0x08;",
" unused = 3;",
" }",
" if(e.sslCA) {",
" b2 |= 0x04;",
" unused = 2;",
" }",
" if(e.emailCA) {",
" b2 |= 0x02;",
" unused = 1;",
" }",
" if(e.objCA) {",
" b2 |= 0x01;",
" unused = 0;",
" }",
"",
" // create bit string",
" var value = String.fromCharCode(unused);",
" if(b2 !== 0) {",
" value += String.fromCharCode(b2);",
" }",
" e.value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, value);",
" } else if(e.name === 'subjectAltName' || e.name === 'issuerAltName') {",
" // SYNTAX SEQUENCE",
" e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
"",
" var altName;",
" for(var n = 0; n < e.altNames.length; ++n) {",
" altName = e.altNames[n];",
" var value = altName.value;",
" // handle IP",
" if(altName.type === 7 && altName.ip) {",
" value = forge.util.bytesFromIP(altName.ip);",
" if(value === null) {",
" var error = new Error(",
" 'Extension \"ip\" value is not a valid IPv4 or IPv6 address.');",
" error.extension = e;",
" throw error;",
" }",
" } else if(altName.type === 8) {",
" // handle OID",
" if(altName.oid) {",
" value = asn1.oidToDer(asn1.oidToDer(altName.oid));",
" } else {",
" // deprecated ... convert value to OID",
" value = asn1.oidToDer(value);",
" }",
" }",
" e.value.value.push(asn1.create(",
" asn1.Class.CONTEXT_SPECIFIC, altName.type, false,",
" value));",
" }",
" } else if(e.name === 'subjectKeyIdentifier' && options.cert) {",
" var ski = options.cert.generateSubjectKeyIdentifier();",
" e.subjectKeyIdentifier = ski.toHex();",
" // OCTETSTRING w/digest",
" e.value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, ski.getBytes());",
" } else if(e.name === 'authorityKeyIdentifier' && options.cert) {",
" // SYNTAX SEQUENCE",
" e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
" var seq = e.value.value;",
"",
" if(e.keyIdentifier) {",
" var keyIdentifier = (e.keyIdentifier === true ?",
" options.cert.generateSubjectKeyIdentifier().getBytes() :",
" e.keyIdentifier);",
" seq.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, false, keyIdentifier));",
" }",
"",
" if(e.authorityCertIssuer) {",
" var authorityCertIssuer = [",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 4, true, [",
" _dnToAsn1(e.authorityCertIssuer === true ?",
" options.cert.issuer : e.authorityCertIssuer)",
" ])",
" ];",
" seq.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, authorityCertIssuer));",
" }",
"",
" if(e.serialNumber) {",
" var serialNumber = forge.util.hexToBytes(e.serialNumber === true ?",
" options.cert.serialNumber : e.serialNumber);",
" seq.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, false, serialNumber));",
" }",
" } else if (e.name === 'cRLDistributionPoints') {",
" e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
" var seq = e.value.value;",
"",
" // Create sub SEQUENCE of DistributionPointName",
" var subSeq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
"",
" // Create fullName CHOICE",
" var fullNameGeneralNames = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, []);",
" var altName;",
" for(var n = 0; n < e.altNames.length; ++n) {",
" altName = e.altNames[n];",
" var value = altName.value;",
" // handle IP",
" if(altName.type === 7 && altName.ip) {",
" value = forge.util.bytesFromIP(altName.ip);",
" if(value === null) {",
" var error = new Error(",
" 'Extension \"ip\" value is not a valid IPv4 or IPv6 address.');",
" error.extension = e;",
" throw error;",
" }",
" } else if(altName.type === 8) {",
" // handle OID",
" if(altName.oid) {",
" value = asn1.oidToDer(asn1.oidToDer(altName.oid));",
" } else {",
" // deprecated ... convert value to OID",
" value = asn1.oidToDer(value);",
" }",
" }",
" fullNameGeneralNames.value.push(asn1.create(",
" asn1.Class.CONTEXT_SPECIFIC, altName.type, false,",
" value));",
" }",
"",
" // Add to the parent SEQUENCE",
" subSeq.value.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [fullNameGeneralNames]));",
" seq.push(subSeq);",
" }",
"",
" // ensure value has been defined by now",
" if(typeof e.value === 'undefined') {",
" var error = new Error('Extension value not specified.');",
" error.extension = e;",
" throw error;",
" }",
"",
" return e;",
"}",
"",
"/**",
" * Convert signature parameters object to ASN.1",
" *",
" * @param {String} oid Signature algorithm OID",
" * @param params The signature parametrs object",
" * @return ASN.1 object representing signature parameters",
" */",
"function _signatureParametersToAsn1(oid, params) {",
" switch(oid) {",
" case oids['RSASSA-PSS']:",
" var parts = [];",
"",
" if(params.hash.algorithmOid !== undefined) {",
" parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(params.hash.algorithmOid).getBytes()),",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ])",
" ]));",
" }",
"",
" if(params.mgf.algorithmOid !== undefined) {",
" parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(params.mgf.algorithmOid).getBytes()),",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(params.mgf.hash.algorithmOid).getBytes()),",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ])",
" ])",
" ]));",
" }",
"",
" if(params.saltLength !== undefined) {",
" parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(params.saltLength).getBytes())",
" ]));",
" }",
"",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, parts);",
"",
" default:",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '');",
" }",
"}",
"",
"/**",
" * Converts a certification request's attributes to an ASN.1 set of",
" * CRIAttributes.",
" *",
" * @param csr certification request.",
" *",
" * @return the ASN.1 set of CRIAttributes.",
" */",
"function _CRIAttributesToAsn1(csr) {",
" // create an empty context-specific container",
" var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, []);",
"",
" // no attributes, return empty container",
" if(csr.attributes.length === 0) {",
" return rval;",
" }",
"",
" // each attribute has a sequence with a type and a set of values",
" var attrs = csr.attributes;",
" for(var i = 0; i < attrs.length; ++i) {",
" var attr = attrs[i];",
" var value = attr.value;",
"",
" // reuse tag class for attribute value if available",
" var valueTagClass = asn1.Type.UTF8;",
" if('valueTagClass' in attr) {",
" valueTagClass = attr.valueTagClass;",
" }",
" if(valueTagClass === asn1.Type.UTF8) {",
" value = forge.util.encodeUtf8(value);",
" }",
" var valueConstructed = false;",
" if('valueConstructed' in attr) {",
" valueConstructed = attr.valueConstructed;",
" }",
" // FIXME: handle more encodings",
"",
" // create a RelativeDistinguishedName set",
" // each value in the set is an AttributeTypeAndValue first",
" // containing the type (an OID) and second the value",
" var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // AttributeType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(attr.type).getBytes()),",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [",
" // AttributeValue",
" asn1.create(",
" asn1.Class.UNIVERSAL, valueTagClass, valueConstructed, value)",
" ])",
" ]);",
" rval.value.push(seq);",
" }",
"",
" return rval;",
"}",
"",
"/**",
" * Gets the ASN.1 TBSCertificate part of an X.509v3 certificate.",
" *",
" * @param cert the certificate.",
" *",
" * @return the asn1 TBSCertificate.",
" */",
"pki.getTBSCertificate = function(cert) {",
" // TBSCertificate",
" var tbs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // version",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" // integer",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(cert.version).getBytes())",
" ]),",
" // serialNumber",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" forge.util.hexToBytes(cert.serialNumber)),",
" // signature",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(cert.siginfo.algorithmOid).getBytes()),",
" // parameters",
" _signatureParametersToAsn1(",
" cert.siginfo.algorithmOid, cert.siginfo.parameters)",
" ]),",
" // issuer",
" _dnToAsn1(cert.issuer),",
" // validity",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // notBefore",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,",
" asn1.dateToUtcTime(cert.validity.notBefore)),",
" // notAfter",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,",
" asn1.dateToUtcTime(cert.validity.notAfter))",
" ]),",
" // subject",
" _dnToAsn1(cert.subject),",
" // SubjectPublicKeyInfo",
" pki.publicKeyToAsn1(cert.publicKey)",
" ]);",
"",
" if(cert.issuer.uniqueId) {",
" // issuerUniqueID (optional)",
" tbs.value.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,",
" // TODO: support arbitrary bit length ids",
" String.fromCharCode(0x00) +",
" cert.issuer.uniqueId",
" )",
" ])",
" );",
" }",
" if(cert.subject.uniqueId) {",
" // subjectUniqueID (optional)",
" tbs.value.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,",
" // TODO: support arbitrary bit length ids",
" String.fromCharCode(0x00) +",
" cert.subject.uniqueId",
" )",
" ])",
" );",
" }",
"",
" if(cert.extensions.length > 0) {",
" // extensions (optional)",
" tbs.value.push(pki.certificateExtensionsToAsn1(cert.extensions));",
" }",
"",
" return tbs;",
"};",
"",
"/**",
" * Gets the ASN.1 CertificationRequestInfo part of a",
" * PKCS#10 CertificationRequest.",
" *",
" * @param csr the certification request.",
" *",
" * @return the asn1 CertificationRequestInfo.",
" */",
"pki.getCertificationRequestInfo = function(csr) {",
" // CertificationRequestInfo",
" var cri = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // version",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(csr.version).getBytes()),",
" // subject",
" _dnToAsn1(csr.subject),",
" // SubjectPublicKeyInfo",
" pki.publicKeyToAsn1(csr.publicKey),",
" // attributes",
" _CRIAttributesToAsn1(csr)",
" ]);",
"",
" return cri;",
"};",
"",
"/**",
" * Converts a DistinguishedName (subject or issuer) to an ASN.1 object.",
" *",
" * @param dn the DistinguishedName.",
" *",
" * @return the asn1 representation of a DistinguishedName.",
" */",
"pki.distinguishedNameToAsn1 = function(dn) {",
" return _dnToAsn1(dn);",
"};",
"",
"/**",
" * Converts an X.509v3 RSA certificate to an ASN.1 object.",
" *",
" * @param cert the certificate.",
" *",
" * @return the asn1 representation of an X.509v3 RSA certificate.",
" */",
"pki.certificateToAsn1 = function(cert) {",
" // prefer cached TBSCertificate over generating one",
" var tbsCertificate = cert.tbsCertificate || pki.getTBSCertificate(cert);",
"",
" // Certificate",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // TBSCertificate",
" tbsCertificate,",
" // AlgorithmIdentifier (signature algorithm)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(cert.signatureOid).getBytes()),",
" // parameters",
" _signatureParametersToAsn1(cert.signatureOid, cert.signatureParameters)",
" ]),",
" // SignatureValue",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,",
" String.fromCharCode(0x00) + cert.signature)",
" ]);",
"};",
"",
"/**",
" * Converts X.509v3 certificate extensions to ASN.1.",
" *",
" * @param exts the extensions to convert.",
" *",
" * @return the extensions in ASN.1 format.",
" */",
"pki.certificateExtensionsToAsn1 = function(exts) {",
" // create top-level extension container",
" var rval = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 3, true, []);",
"",
" // create extension sequence (stores a sequence for each extension)",
" var seq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
" rval.value.push(seq);",
"",
" for(var i = 0; i < exts.length; ++i) {",
" seq.value.push(pki.certificateExtensionToAsn1(exts[i]));",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Converts a single certificate extension to ASN.1.",
" *",
" * @param ext the extension to convert.",
" *",
" * @return the extension in ASN.1 format.",
" */",
"pki.certificateExtensionToAsn1 = function(ext) {",
" // create a sequence for each extension",
" var extseq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []);",
"",
" // extnID (OID)",
" extseq.value.push(asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(ext.id).getBytes()));",
"",
" // critical defaults to false",
" if(ext.critical) {",
" // critical BOOLEAN DEFAULT FALSE",
" extseq.value.push(asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false,",
" String.fromCharCode(0xFF)));",
" }",
"",
" var value = ext.value;",
" if(typeof ext.value !== 'string') {",
" // value is asn.1",
" value = asn1.toDer(value).getBytes();",
" }",
"",
" // extnValue (OCTET STRING)",
" extseq.value.push(asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, value));",
"",
" return extseq;",
"};",
"",
"/**",
" * Converts a PKCS#10 certification request to an ASN.1 object.",
" *",
" * @param csr the certification request.",
" *",
" * @return the asn1 representation of a certification request.",
" */",
"pki.certificationRequestToAsn1 = function(csr) {",
" // prefer cached CertificationRequestInfo over generating one",
" var cri = csr.certificationRequestInfo ||",
" pki.getCertificationRequestInfo(csr);",
"",
" // Certificate",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // CertificationRequestInfo",
" cri,",
" // AlgorithmIdentifier (signature algorithm)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(csr.signatureOid).getBytes()),",
" // parameters",
" _signatureParametersToAsn1(csr.signatureOid, csr.signatureParameters)",
" ]),",
" // signature",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false,",
" String.fromCharCode(0x00) + csr.signature)",
" ]);",
"};",
"",
"/**",
" * Creates a CA store.",
" *",
" * @param certs an optional array of certificate objects or PEM-formatted",
" * certificate strings to add to the CA store.",
" *",
" * @return the CA store.",
" */",
"pki.createCaStore = function(certs) {",
" // create CA store",
" var caStore = {",
" // stored certificates",
" certs: {}",
" };",
"",
" /**",
" * Gets the certificate that issued the passed certificate or its",
" * 'parent'.",
" *",
" * @param cert the certificate to get the parent for.",
" *",
" * @return the parent certificate or null if none was found.",
" */",
" caStore.getIssuer = function(cert) {",
" var rval = getBySubject(cert.issuer);",
"",
" // see if there are multiple matches",
" /*if(forge.util.isArray(rval)) {",
" // TODO: resolve multiple matches by checking",
" // authorityKey/subjectKey/issuerUniqueID/other identifiers, etc.",
" // FIXME: or alternatively do authority key mapping",
" // if possible (X.509v1 certs can't work?)",
" throw new Error('Resolving multiple issuer matches not implemented yet.');",
" }*/",
"",
" return rval;",
" };",
"",
" /**",
" * Adds a trusted certificate to the store.",
" *",
" * @param cert the certificate to add as a trusted certificate (either a",
" * pki.certificate object or a PEM-formatted certificate).",
" */",
" caStore.addCertificate = function(cert) {",
" // convert from pem if necessary",
" if(typeof cert === 'string') {",
" cert = forge.pki.certificateFromPem(cert);",
" }",
"",
" ensureSubjectHasHash(cert.subject);",
"",
" if(!caStore.hasCertificate(cert)) { // avoid duplicate certificates in store",
" if(cert.subject.hash in caStore.certs) {",
" // subject hash already exists, append to array",
" var tmp = caStore.certs[cert.subject.hash];",
" if(!forge.util.isArray(tmp)) {",
" tmp = [tmp];",
" }",
" tmp.push(cert);",
" caStore.certs[cert.subject.hash] = tmp;",
" } else {",
" caStore.certs[cert.subject.hash] = cert;",
" }",
" }",
" };",
"",
" /**",
" * Checks to see if the given certificate is in the store.",
" *",
" * @param cert the certificate to check (either a pki.certificate or a",
" * PEM-formatted certificate).",
" *",
" * @return true if the certificate is in the store, false if not.",
" */",
" caStore.hasCertificate = function(cert) {",
" // convert from pem if necessary",
" if(typeof cert === 'string') {",
" cert = forge.pki.certificateFromPem(cert);",
" }",
"",
" var match = getBySubject(cert.subject);",
" if(!match) {",
" return false;",
" }",
" if(!forge.util.isArray(match)) {",
" match = [match];",
" }",
" // compare DER-encoding of certificates",
" var der1 = asn1.toDer(pki.certificateToAsn1(cert)).getBytes();",
" for(var i = 0; i < match.length; ++i) {",
" var der2 = asn1.toDer(pki.certificateToAsn1(match[i])).getBytes();",
" if(der1 === der2) {",
" return true;",
" }",
" }",
" return false;",
" };",
"",
" /**",
" * Lists all of the certificates kept in the store.",
" *",
" * @return an array of all of the pki.certificate objects in the store.",
" */",
" caStore.listAllCertificates = function() {",
" var certList = [];",
"",
" for(var hash in caStore.certs) {",
" if(caStore.certs.hasOwnProperty(hash)) {",
" var value = caStore.certs[hash];",
" if(!forge.util.isArray(value)) {",
" certList.push(value);",
" } else {",
" for(var i = 0; i < value.length; ++i) {",
" certList.push(value[i]);",
" }",
" }",
" }",
" }",
"",
" return certList;",
" };",
"",
" /**",
" * Removes a certificate from the store.",
" *",
" * @param cert the certificate to remove (either a pki.certificate or a",
" * PEM-formatted certificate).",
" *",
" * @return the certificate that was removed or null if the certificate",
" * wasn't in store.",
" */",
" caStore.removeCertificate = function(cert) {",
" var result;",
"",
" // convert from pem if necessary",
" if(typeof cert === 'string') {",
" cert = forge.pki.certificateFromPem(cert);",
" }",
" ensureSubjectHasHash(cert.subject);",
" if(!caStore.hasCertificate(cert)) {",
" return null;",
" }",
"",
" var match = getBySubject(cert.subject);",
"",
" if(!forge.util.isArray(match)) {",
" result = caStore.certs[cert.subject.hash];",
" delete caStore.certs[cert.subject.hash];",
" return result;",
" }",
"",
" // compare DER-encoding of certificates",
" var der1 = asn1.toDer(pki.certificateToAsn1(cert)).getBytes();",
" for(var i = 0; i < match.length; ++i) {",
" var der2 = asn1.toDer(pki.certificateToAsn1(match[i])).getBytes();",
" if(der1 === der2) {",
" result = match[i];",
" match.splice(i, 1);",
" }",
" }",
" if(match.length === 0) {",
" delete caStore.certs[cert.subject.hash];",
" }",
"",
" return result;",
" };",
"",
" function getBySubject(subject) {",
" ensureSubjectHasHash(subject);",
" return caStore.certs[subject.hash] || null;",
" }",
"",
" function ensureSubjectHasHash(subject) {",
" // produce subject hash if it doesn't exist",
" if(!subject.hash) {",
" var md = forge.md.sha1.create();",
" subject.attributes = pki.RDNAttributesAsArray(_dnToAsn1(subject), md);",
" subject.hash = md.digest().toHex();",
" }",
" }",
"",
" // auto-add passed in certs",
" if(certs) {",
" // parse PEM-formatted certificates as necessary",
" for(var i = 0; i < certs.length; ++i) {",
" var cert = certs[i];",
" caStore.addCertificate(cert);",
" }",
" }",
"",
" return caStore;",
"};",
"",
"/**",
" * Certificate verification errors, based on TLS.",
" */",
"pki.certificateError = {",
" bad_certificate: 'forge.pki.BadCertificate',",
" unsupported_certificate: 'forge.pki.UnsupportedCertificate',",
" certificate_revoked: 'forge.pki.CertificateRevoked',",
" certificate_expired: 'forge.pki.CertificateExpired',",
" certificate_unknown: 'forge.pki.CertificateUnknown',",
" unknown_ca: 'forge.pki.UnknownCertificateAuthority'",
"};",
"",
"/**",
" * Verifies a certificate chain against the given Certificate Authority store",
" * with an optional custom verify callback.",
" *",
" * @param caStore a certificate store to verify against.",
" * @param chain the certificate chain to verify, with the root or highest",
" * authority at the end (an array of certificates).",
" * @param verify called for every certificate in the chain.",
" *",
" * The verify callback has the following signature:",
" *",
" * verified - Set to true if certificate was verified, otherwise the",
" * pki.certificateError for why the certificate failed.",
" * depth - The current index in the chain, where 0 is the end point's cert.",
" * certs - The certificate chain, *NOTE* an empty chain indicates an anonymous",
" * end point.",
" *",
" * The function returns true on success and on failure either the appropriate",
" * pki.certificateError or an object with 'error' set to the appropriate",
" * pki.certificateError and 'message' set to a custom error message.",
" *",
" * @return true if successful, error thrown if not.",
" */",
"pki.verifyCertificateChain = function(caStore, chain, verify) {",
" /* From: RFC3280 - Internet X.509 Public Key Infrastructure Certificate",
" Section 6: Certification Path Validation",
" See inline parentheticals related to this particular implementation.",
"",
" The primary goal of path validation is to verify the binding between",
" a subject distinguished name or a subject alternative name and subject",
" public key, as represented in the end entity certificate, based on the",
" public key of the trust anchor. This requires obtaining a sequence of",
" certificates that support that binding. That sequence should be provided",
" in the passed 'chain'. The trust anchor should be in the given CA",
" store. The 'end entity' certificate is the certificate provided by the",
" end point (typically a server) and is the first in the chain.",
"",
" To meet this goal, the path validation process verifies, among other",
" things, that a prospective certification path (a sequence of n",
" certificates or a 'chain') satisfies the following conditions:",
"",
" (a) for all x in {1, ..., n-1}, the subject of certificate x is",
" the issuer of certificate x+1;",
"",
" (b) certificate 1 is issued by the trust anchor;",
"",
" (c) certificate n is the certificate to be validated; and",
"",
" (d) for all x in {1, ..., n}, the certificate was valid at the",
" time in question.",
"",
" Note that here 'n' is index 0 in the chain and 1 is the last certificate",
" in the chain and it must be signed by a certificate in the connection's",
" CA store.",
"",
" The path validation process also determines the set of certificate",
" policies that are valid for this path, based on the certificate policies",
" extension, policy mapping extension, policy constraints extension, and",
" inhibit any-policy extension.",
"",
" Note: Policy mapping extension not supported (Not Required).",
"",
" Note: If the certificate has an unsupported critical extension, then it",
" must be rejected.",
"",
" Note: A certificate is self-issued if the DNs that appear in the subject",
" and issuer fields are identical and are not empty.",
"",
" The path validation algorithm assumes the following seven inputs are",
" provided to the path processing logic. What this specific implementation",
" will use is provided parenthetically:",
"",
" (a) a prospective certification path of length n (the 'chain')",
" (b) the current date/time: ('now').",
" (c) user-initial-policy-set: A set of certificate policy identifiers",
" naming the policies that are acceptable to the certificate user.",
" The user-initial-policy-set contains the special value any-policy",
" if the user is not concerned about certificate policy",
" (Not implemented. Any policy is accepted).",
" (d) trust anchor information, describing a CA that serves as a trust",
" anchor for the certification path. The trust anchor information",
" includes:",
"",
" (1) the trusted issuer name,",
" (2) the trusted public key algorithm,",
" (3) the trusted public key, and",
" (4) optionally, the trusted public key parameters associated",
" with the public key.",
"",
" (Trust anchors are provided via certificates in the CA store).",
"",
" The trust anchor information may be provided to the path processing",
" procedure in the form of a self-signed certificate. The trusted anchor",
" information is trusted because it was delivered to the path processing",
" procedure by some trustworthy out-of-band procedure. If the trusted",
" public key algorithm requires parameters, then the parameters are",
" provided along with the trusted public key (No parameters used in this",
" implementation).",
"",
" (e) initial-policy-mapping-inhibit, which indicates if policy mapping is",
" allowed in the certification path.",
" (Not implemented, no policy checking)",
"",
" (f) initial-explicit-policy, which indicates if the path must be valid",
" for at least one of the certificate policies in the user-initial-",
" policy-set.",
" (Not implemented, no policy checking)",
"",
" (g) initial-any-policy-inhibit, which indicates whether the",
" anyPolicy OID should be processed if it is included in a",
" certificate.",
" (Not implemented, so any policy is valid provided that it is",
" not marked as critical) */",
"",
" /* Basic Path Processing:",
"",
" For each certificate in the 'chain', the following is checked:",
"",
" 1. The certificate validity period includes the current time.",
" 2. The certificate was signed by its parent (where the parent is either",
" the next in the chain or from the CA store). Allow processing to",
" continue to the next step if no parent is found but the certificate is",
" in the CA store.",
" 3. TODO: The certificate has not been revoked.",
" 4. The certificate issuer name matches the parent's subject name.",
" 5. TODO: If the certificate is self-issued and not the final certificate",
" in the chain, skip this step, otherwise verify that the subject name",
" is within one of the permitted subtrees of X.500 distinguished names",
" and that each of the alternative names in the subjectAltName extension",
" (critical or non-critical) is within one of the permitted subtrees for",
" that name type.",
" 6. TODO: If the certificate is self-issued and not the final certificate",
" in the chain, skip this step, otherwise verify that the subject name",
" is not within one of the excluded subtrees for X.500 distinguished",
" names and none of the subjectAltName extension names are excluded for",
" that name type.",
" 7. The other steps in the algorithm for basic path processing involve",
" handling the policy extension which is not presently supported in this",
" implementation. Instead, if a critical policy extension is found, the",
" certificate is rejected as not supported.",
" 8. If the certificate is not the first or if its the only certificate in",
" the chain (having no parent from the CA store or is self-signed) and it",
" has a critical key usage extension, verify that the keyCertSign bit is",
" set. If the key usage extension exists, verify that the basic",
" constraints extension exists. If the basic constraints extension exists,",
" verify that the cA flag is set. If pathLenConstraint is set, ensure that",
" the number of certificates that precede in the chain (come earlier",
" in the chain as implemented below), excluding the very first in the",
" chain (typically the end-entity one), isn't greater than the",
" pathLenConstraint. This constraint limits the number of intermediate",
" CAs that may appear below a CA before only end-entity certificates",
" may be issued. */",
"",
" // copy cert chain references to another array to protect against changes",
" // in verify callback",
" chain = chain.slice(0);",
" var certs = chain.slice(0);",
"",
" // get current date",
" var now = new Date();",
"",
" // verify each cert in the chain using its parent, where the parent",
" // is either the next in the chain or from the CA store",
" var first = true;",
" var error = null;",
" var depth = 0;",
" do {",
" var cert = chain.shift();",
" var parent = null;",
" var selfSigned = false;",
"",
" // 1. check valid time",
" if(now < cert.validity.notBefore || now > cert.validity.notAfter) {",
" error = {",
" message: 'Certificate is not valid yet or has expired.',",
" error: pki.certificateError.certificate_expired,",
" notBefore: cert.validity.notBefore,",
" notAfter: cert.validity.notAfter,",
" now: now",
" };",
" }",
"",
" // 2. verify with parent from chain or CA store",
" if(error === null) {",
" parent = chain[0] || caStore.getIssuer(cert);",
" if(parent === null) {",
" // check for self-signed cert",
" if(cert.isIssuer(cert)) {",
" selfSigned = true;",
" parent = cert;",
" }",
" }",
"",
" if(parent) {",
" // FIXME: current CA store implementation might have multiple",
" // certificates where the issuer can't be determined from the",
" // certificate (happens rarely with, eg: old certificates) so normalize",
" // by always putting parents into an array",
" // TODO: there's may be an extreme degenerate case currently uncovered",
" // where an old intermediate certificate seems to have a matching parent",
" // but none of the parents actually verify ... but the intermediate",
" // is in the CA and it should pass this check; needs investigation",
" var parents = parent;",
" if(!forge.util.isArray(parents)) {",
" parents = [parents];",
" }",
"",
" // try to verify with each possible parent (typically only one)",
" var verified = false;",
" while(!verified && parents.length > 0) {",
" parent = parents.shift();",
" try {",
" verified = parent.verify(cert);",
" } catch(ex) {",
" // failure to verify, don't care why, try next one",
" }",
" }",
"",
" if(!verified) {",
" error = {",
" message: 'Certificate signature is invalid.',",
" error: pki.certificateError.bad_certificate",
" };",
" }",
" }",
"",
" if(error === null && (!parent || selfSigned) &&",
" !caStore.hasCertificate(cert)) {",
" // no parent issuer and certificate itself is not trusted",
" error = {",
" message: 'Certificate is not trusted.',",
" error: pki.certificateError.unknown_ca",
" };",
" }",
" }",
"",
" // TODO: 3. check revoked",
"",
" // 4. check for matching issuer/subject",
" if(error === null && parent && !cert.isIssuer(parent)) {",
" // parent is not issuer",
" error = {",
" message: 'Certificate issuer is invalid.',",
" error: pki.certificateError.bad_certificate",
" };",
" }",
"",
" // 5. TODO: check names with permitted names tree",
"",
" // 6. TODO: check names against excluded names tree",
"",
" // 7. check for unsupported critical extensions",
" if(error === null) {",
" // supported extensions",
" var se = {",
" keyUsage: true,",
" basicConstraints: true",
" };",
" for(var i = 0; error === null && i < cert.extensions.length; ++i) {",
" var ext = cert.extensions[i];",
" if(ext.critical && !(ext.name in se)) {",
" error = {",
" message:",
" 'Certificate has an unsupported critical extension.',",
" error: pki.certificateError.unsupported_certificate",
" };",
" }",
" }",
" }",
"",
" // 8. check for CA if cert is not first or is the only certificate",
" // remaining in chain with no parent or is self-signed",
" if(error === null &&",
" (!first || (chain.length === 0 && (!parent || selfSigned)))) {",
" // first check keyUsage extension and then basic constraints",
" var bcExt = cert.getExtension('basicConstraints');",
" var keyUsageExt = cert.getExtension('keyUsage');",
" if(keyUsageExt !== null) {",
" // keyCertSign must be true and there must be a basic",
" // constraints extension",
" if(!keyUsageExt.keyCertSign || bcExt === null) {",
" // bad certificate",
" error = {",
" message:",
" 'Certificate keyUsage or basicConstraints conflict ' +",
" 'or indicate that the certificate is not a CA. ' +",
" 'If the certificate is the only one in the chain or ' +",
" 'isn\\'t the first then the certificate must be a ' +",
" 'valid CA.',",
" error: pki.certificateError.bad_certificate",
" };",
" }",
" }",
" // basic constraints cA flag must be set",
" if(error === null && bcExt !== null && !bcExt.cA) {",
" // bad certificate",
" error = {",
" message:",
" 'Certificate basicConstraints indicates the certificate ' +",
" 'is not a CA.',",
" error: pki.certificateError.bad_certificate",
" };",
" }",
" // if error is not null and keyUsage is available, then we know it",
" // has keyCertSign and there is a basic constraints extension too,",
" // which means we can check pathLenConstraint (if it exists)",
" if(error === null && keyUsageExt !== null &&",
" 'pathLenConstraint' in bcExt) {",
" // pathLen is the maximum # of intermediate CA certs that can be",
" // found between the current certificate and the end-entity (depth 0)",
" // certificate; this number does not include the end-entity (depth 0,",
" // last in the chain) even if it happens to be a CA certificate itself",
" var pathLen = depth - 1;",
" if(pathLen > bcExt.pathLenConstraint) {",
" // pathLenConstraint violated, bad certificate",
" error = {",
" message:",
" 'Certificate basicConstraints pathLenConstraint violated.',",
" error: pki.certificateError.bad_certificate",
" };",
" }",
" }",
" }",
"",
" // call application callback",
" var vfd = (error === null) ? true : error.error;",
" var ret = verify ? verify(vfd, depth, certs) : vfd;",
" if(ret === true) {",
" // clear any set error",
" error = null;",
" } else {",
" // if passed basic tests, set default message and alert",
" if(vfd === true) {",
" error = {",
" message: 'The application rejected the certificate.',",
" error: pki.certificateError.bad_certificate",
" };",
" }",
"",
" // check for custom error info",
" if(ret || ret === 0) {",
" // set custom message and error",
" if(typeof ret === 'object' && !forge.util.isArray(ret)) {",
" if(ret.message) {",
" error.message = ret.message;",
" }",
" if(ret.error) {",
" error.error = ret.error;",
" }",
" } else if(typeof ret === 'string') {",
" // set custom error",
" error.error = ret;",
" }",
" }",
"",
" // throw error",
" throw error;",
" }",
"",
" // no longer first cert in chain",
" first = false;",
" ++depth;",
" } while(chain.length > 0);",
"",
" return true;",
"};",
"",
"",
"/***/ }),",
"/* 17 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of PKCS#1 PSS signature padding.",
" *",
" * @author Stefan Siegl",
" *",
" * Copyright (c) 2012 Stefan Siegl ",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(2);",
"__webpack_require__(1);",
"",
"// shortcut for PSS API",
"var pss = module.exports = forge.pss = forge.pss || {};",
"",
"/**",
" * Creates a PSS signature scheme object.",
" *",
" * There are several ways to provide a salt for encoding:",
" *",
" * 1. Specify the saltLength only and the built-in PRNG will generate it.",
" * 2. Specify the saltLength and a custom PRNG with 'getBytesSync' defined that",
" * will be used.",
" * 3. Specify the salt itself as a forge.util.ByteBuffer.",
" *",
" * @param options the options to use:",
" * md the message digest object to use, a forge md instance.",
" * mgf the mask generation function to use, a forge mgf instance.",
" * [saltLength] the length of the salt in octets.",
" * [prng] the pseudo-random number generator to use to produce a salt.",
" * [salt] the salt to use when encoding.",
" *",
" * @return a signature scheme object.",
" */",
"pss.create = function(options) {",
" // backwards compatibility w/legacy args: hash, mgf, sLen",
" if(arguments.length === 3) {",
" options = {",
" md: arguments[0],",
" mgf: arguments[1],",
" saltLength: arguments[2]",
" };",
" }",
"",
" var hash = options.md;",
" var mgf = options.mgf;",
" var hLen = hash.digestLength;",
"",
" var salt_ = options.salt || null;",
" if(typeof salt_ === 'string') {",
" // assume binary-encoded string",
" salt_ = forge.util.createBuffer(salt_);",
" }",
"",
" var sLen;",
" if('saltLength' in options) {",
" sLen = options.saltLength;",
" } else if(salt_ !== null) {",
" sLen = salt_.length();",
" } else {",
" throw new Error('Salt length not specified or specific salt not given.');",
" }",
"",
" if(salt_ !== null && salt_.length() !== sLen) {",
" throw new Error('Given salt length does not match length of given salt.');",
" }",
"",
" var prng = options.prng || forge.random;",
"",
" var pssobj = {};",
"",
" /**",
" * Encodes a PSS signature.",
" *",
" * This function implements EMSA-PSS-ENCODE as per RFC 3447, section 9.1.1.",
" *",
" * @param md the message digest object with the hash to sign.",
" * @param modsBits the length of the RSA modulus in bits.",
" *",
" * @return the encoded message as a binary-encoded string of length",
" * ceil((modBits - 1) / 8).",
" */",
" pssobj.encode = function(md, modBits) {",
" var i;",
" var emBits = modBits - 1;",
" var emLen = Math.ceil(emBits / 8);",
"",
" /* 2. Let mHash = Hash(M), an octet string of length hLen. */",
" var mHash = md.digest().getBytes();",
"",
" /* 3. If emLen < hLen + sLen + 2, output \"encoding error\" and stop. */",
" if(emLen < hLen + sLen + 2) {",
" throw new Error('Message is too long to encrypt.');",
" }",
"",
" /* 4. Generate a random octet string salt of length sLen; if sLen = 0,",
" * then salt is the empty string. */",
" var salt;",
" if(salt_ === null) {",
" salt = prng.getBytesSync(sLen);",
" } else {",
" salt = salt_.bytes();",
" }",
"",
" /* 5. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; */",
" var m_ = new forge.util.ByteBuffer();",
" m_.fillWithByte(0, 8);",
" m_.putBytes(mHash);",
" m_.putBytes(salt);",
"",
" /* 6. Let H = Hash(M'), an octet string of length hLen. */",
" hash.start();",
" hash.update(m_.getBytes());",
" var h = hash.digest().getBytes();",
"",
" /* 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2",
" * zero octets. The length of PS may be 0. */",
" var ps = new forge.util.ByteBuffer();",
" ps.fillWithByte(0, emLen - sLen - hLen - 2);",
"",
" /* 8. Let DB = PS || 0x01 || salt; DB is an octet string of length",
" * emLen - hLen - 1. */",
" ps.putByte(0x01);",
" ps.putBytes(salt);",
" var db = ps.getBytes();",
"",
" /* 9. Let dbMask = MGF(H, emLen - hLen - 1). */",
" var maskLen = emLen - hLen - 1;",
" var dbMask = mgf.generate(h, maskLen);",
"",
" /* 10. Let maskedDB = DB \\xor dbMask. */",
" var maskedDB = '';",
" for(i = 0; i < maskLen; i++) {",
" maskedDB += String.fromCharCode(db.charCodeAt(i) ^ dbMask.charCodeAt(i));",
" }",
"",
" /* 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in",
" * maskedDB to zero. */",
" var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;",
" maskedDB = String.fromCharCode(maskedDB.charCodeAt(0) & ~mask) +",
" maskedDB.substr(1);",
"",
" /* 12. Let EM = maskedDB || H || 0xbc.",
" * 13. Output EM. */",
" return maskedDB + h + String.fromCharCode(0xbc);",
" };",
"",
" /**",
" * Verifies a PSS signature.",
" *",
" * This function implements EMSA-PSS-VERIFY as per RFC 3447, section 9.1.2.",
" *",
" * @param mHash the message digest hash, as a binary-encoded string, to",
" * compare against the signature.",
" * @param em the encoded message, as a binary-encoded string",
" * (RSA decryption result).",
" * @param modsBits the length of the RSA modulus in bits.",
" *",
" * @return true if the signature was verified, false if not.",
" */",
" pssobj.verify = function(mHash, em, modBits) {",
" var i;",
" var emBits = modBits - 1;",
" var emLen = Math.ceil(emBits / 8);",
"",
" /* c. Convert the message representative m to an encoded message EM",
" * of length emLen = ceil((modBits - 1) / 8) octets, where modBits",
" * is the length in bits of the RSA modulus n */",
" em = em.substr(-emLen);",
"",
" /* 3. If emLen < hLen + sLen + 2, output \"inconsistent\" and stop. */",
" if(emLen < hLen + sLen + 2) {",
" throw new Error('Inconsistent parameters to PSS signature verification.');",
" }",
"",
" /* 4. If the rightmost octet of EM does not have hexadecimal value",
" * 0xbc, output \"inconsistent\" and stop. */",
" if(em.charCodeAt(emLen - 1) !== 0xbc) {",
" throw new Error('Encoded message does not end in 0xBC.');",
" }",
"",
" /* 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and",
" * let H be the next hLen octets. */",
" var maskLen = emLen - hLen - 1;",
" var maskedDB = em.substr(0, maskLen);",
" var h = em.substr(maskLen, hLen);",
"",
" /* 6. If the leftmost 8emLen - emBits bits of the leftmost octet in",
" * maskedDB are not all equal to zero, output \"inconsistent\" and stop. */",
" var mask = (0xFF00 >> (8 * emLen - emBits)) & 0xFF;",
" if((maskedDB.charCodeAt(0) & mask) !== 0) {",
" throw new Error('Bits beyond keysize not zero as expected.');",
" }",
"",
" /* 7. Let dbMask = MGF(H, emLen - hLen - 1). */",
" var dbMask = mgf.generate(h, maskLen);",
"",
" /* 8. Let DB = maskedDB \\xor dbMask. */",
" var db = '';",
" for(i = 0; i < maskLen; i++) {",
" db += String.fromCharCode(maskedDB.charCodeAt(i) ^ dbMask.charCodeAt(i));",
" }",
"",
" /* 9. Set the leftmost 8emLen - emBits bits of the leftmost octet",
" * in DB to zero. */",
" db = String.fromCharCode(db.charCodeAt(0) & ~mask) + db.substr(1);",
"",
" /* 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero",
" * or if the octet at position emLen - hLen - sLen - 1 (the leftmost",
" * position is \"position 1\") does not have hexadecimal value 0x01,",
" * output \"inconsistent\" and stop. */",
" var checkLen = emLen - hLen - sLen - 2;",
" for(i = 0; i < checkLen; i++) {",
" if(db.charCodeAt(i) !== 0x00) {",
" throw new Error('Leftmost octets not zero as expected');",
" }",
" }",
"",
" if(db.charCodeAt(checkLen) !== 0x01) {",
" throw new Error('Inconsistent PSS signature, 0x01 marker not found');",
" }",
"",
" /* 11. Let salt be the last sLen octets of DB. */",
" var salt = db.substr(-sLen);",
"",
" /* 12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */",
" var m_ = new forge.util.ByteBuffer();",
" m_.fillWithByte(0, 8);",
" m_.putBytes(mHash);",
" m_.putBytes(salt);",
"",
" /* 13. Let H' = Hash(M'), an octet string of length hLen. */",
" hash.start();",
" hash.update(m_.getBytes());",
" var h_ = hash.digest().getBytes();",
"",
" /* 14. If H = H', output \"consistent.\" Otherwise, output \"inconsistent.\" */",
" return h === h_;",
" };",
"",
" return pssobj;",
"};",
"",
"",
"/***/ }),",
"/* 18 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Supported cipher modes.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"",
"forge.cipher = forge.cipher || {};",
"",
"// supported cipher modes",
"var modes = module.exports = forge.cipher.modes = forge.cipher.modes || {};",
"",
"/** Electronic codebook (ECB) (Don't use this; it's not secure) **/",
"",
"modes.ecb = function(options) {",
" options = options || {};",
" this.name = 'ECB';",
" this.cipher = options.cipher;",
" this.blockSize = options.blockSize || 16;",
" this._ints = this.blockSize / 4;",
" this._inBlock = new Array(this._ints);",
" this._outBlock = new Array(this._ints);",
"};",
"",
"modes.ecb.prototype.start = function(options) {};",
"",
"modes.ecb.prototype.encrypt = function(input, output, finish) {",
" // not enough input to encrypt",
" if(input.length() < this.blockSize && !(finish && input.length() > 0)) {",
" return true;",
" }",
"",
" // get next block",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = input.getInt32();",
" }",
"",
" // encrypt block",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // write output",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(this._outBlock[i]);",
" }",
"};",
"",
"modes.ecb.prototype.decrypt = function(input, output, finish) {",
" // not enough input to decrypt",
" if(input.length() < this.blockSize && !(finish && input.length() > 0)) {",
" return true;",
" }",
"",
" // get next block",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = input.getInt32();",
" }",
"",
" // decrypt block",
" this.cipher.decrypt(this._inBlock, this._outBlock);",
"",
" // write output",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(this._outBlock[i]);",
" }",
"};",
"",
"modes.ecb.prototype.pad = function(input, options) {",
" // add PKCS#7 padding to block (each pad byte is the",
" // value of the number of pad bytes)",
" var padding = (input.length() === this.blockSize ?",
" this.blockSize : (this.blockSize - input.length()));",
" input.fillWithByte(padding, padding);",
" return true;",
"};",
"",
"modes.ecb.prototype.unpad = function(output, options) {",
" // check for error: input data not a multiple of blockSize",
" if(options.overflow > 0) {",
" return false;",
" }",
"",
" // ensure padding byte count is valid",
" var len = output.length();",
" var count = output.at(len - 1);",
" if(count > (this.blockSize << 2)) {",
" return false;",
" }",
"",
" // trim off padding bytes",
" output.truncate(count);",
" return true;",
"};",
"",
"/** Cipher-block Chaining (CBC) **/",
"",
"modes.cbc = function(options) {",
" options = options || {};",
" this.name = 'CBC';",
" this.cipher = options.cipher;",
" this.blockSize = options.blockSize || 16;",
" this._ints = this.blockSize / 4;",
" this._inBlock = new Array(this._ints);",
" this._outBlock = new Array(this._ints);",
"};",
"",
"modes.cbc.prototype.start = function(options) {",
" // Note: legacy support for using IV residue (has security flaws)",
" // if IV is null, reuse block from previous processing",
" if(options.iv === null) {",
" // must have a previous block",
" if(!this._prev) {",
" throw new Error('Invalid IV parameter.');",
" }",
" this._iv = this._prev.slice(0);",
" } else if(!('iv' in options)) {",
" throw new Error('Invalid IV parameter.');",
" } else {",
" // save IV as \"previous\" block",
" this._iv = transformIV(options.iv);",
" this._prev = this._iv.slice(0);",
" }",
"};",
"",
"modes.cbc.prototype.encrypt = function(input, output, finish) {",
" // not enough input to encrypt",
" if(input.length() < this.blockSize && !(finish && input.length() > 0)) {",
" return true;",
" }",
"",
" // get next block",
" // CBC XOR's IV (or previous block) with plaintext",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = this._prev[i] ^ input.getInt32();",
" }",
"",
" // encrypt block",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // write output, save previous block",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(this._outBlock[i]);",
" }",
" this._prev = this._outBlock;",
"};",
"",
"modes.cbc.prototype.decrypt = function(input, output, finish) {",
" // not enough input to decrypt",
" if(input.length() < this.blockSize && !(finish && input.length() > 0)) {",
" return true;",
" }",
"",
" // get next block",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = input.getInt32();",
" }",
"",
" // decrypt block",
" this.cipher.decrypt(this._inBlock, this._outBlock);",
"",
" // write output, save previous ciphered block",
" // CBC XOR's IV (or previous block) with ciphertext",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(this._prev[i] ^ this._outBlock[i]);",
" }",
" this._prev = this._inBlock.slice(0);",
"};",
"",
"modes.cbc.prototype.pad = function(input, options) {",
" // add PKCS#7 padding to block (each pad byte is the",
" // value of the number of pad bytes)",
" var padding = (input.length() === this.blockSize ?",
" this.blockSize : (this.blockSize - input.length()));",
" input.fillWithByte(padding, padding);",
" return true;",
"};",
"",
"modes.cbc.prototype.unpad = function(output, options) {",
" // check for error: input data not a multiple of blockSize",
" if(options.overflow > 0) {",
" return false;",
" }",
"",
" // ensure padding byte count is valid",
" var len = output.length();",
" var count = output.at(len - 1);",
" if(count > (this.blockSize << 2)) {",
" return false;",
" }",
"",
" // trim off padding bytes",
" output.truncate(count);",
" return true;",
"};",
"",
"/** Cipher feedback (CFB) **/",
"",
"modes.cfb = function(options) {",
" options = options || {};",
" this.name = 'CFB';",
" this.cipher = options.cipher;",
" this.blockSize = options.blockSize || 16;",
" this._ints = this.blockSize / 4;",
" this._inBlock = null;",
" this._outBlock = new Array(this._ints);",
" this._partialBlock = new Array(this._ints);",
" this._partialOutput = forge.util.createBuffer();",
" this._partialBytes = 0;",
"};",
"",
"modes.cfb.prototype.start = function(options) {",
" if(!('iv' in options)) {",
" throw new Error('Invalid IV parameter.');",
" }",
" // use IV as first input",
" this._iv = transformIV(options.iv);",
" this._inBlock = this._iv.slice(0);",
" this._partialBytes = 0;",
"};",
"",
"modes.cfb.prototype.encrypt = function(input, output, finish) {",
" // not enough input to encrypt",
" var inputLength = input.length();",
" if(inputLength === 0) {",
" return true;",
" }",
"",
" // encrypt block",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // handle full block",
" if(this._partialBytes === 0 && inputLength >= this.blockSize) {",
" // XOR input with output, write input as output",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = input.getInt32() ^ this._outBlock[i];",
" output.putInt32(this._inBlock[i]);",
" }",
" return;",
" }",
"",
" // handle partial block",
" var partialBytes = (this.blockSize - inputLength) % this.blockSize;",
" if(partialBytes > 0) {",
" partialBytes = this.blockSize - partialBytes;",
" }",
"",
" // XOR input with output, write input as partial output",
" this._partialOutput.clear();",
" for(var i = 0; i < this._ints; ++i) {",
" this._partialBlock[i] = input.getInt32() ^ this._outBlock[i];",
" this._partialOutput.putInt32(this._partialBlock[i]);",
" }",
"",
" if(partialBytes > 0) {",
" // block still incomplete, restore input buffer",
" input.read -= this.blockSize;",
" } else {",
" // block complete, update input block",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = this._partialBlock[i];",
" }",
" }",
"",
" // skip any previous partial bytes",
" if(this._partialBytes > 0) {",
" this._partialOutput.getBytes(this._partialBytes);",
" }",
"",
" if(partialBytes > 0 && !finish) {",
" output.putBytes(this._partialOutput.getBytes(",
" partialBytes - this._partialBytes));",
" this._partialBytes = partialBytes;",
" return true;",
" }",
"",
" output.putBytes(this._partialOutput.getBytes(",
" inputLength - this._partialBytes));",
" this._partialBytes = 0;",
"};",
"",
"modes.cfb.prototype.decrypt = function(input, output, finish) {",
" // not enough input to decrypt",
" var inputLength = input.length();",
" if(inputLength === 0) {",
" return true;",
" }",
"",
" // encrypt block (CFB always uses encryption mode)",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // handle full block",
" if(this._partialBytes === 0 && inputLength >= this.blockSize) {",
" // XOR input with output, write input as output",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = input.getInt32();",
" output.putInt32(this._inBlock[i] ^ this._outBlock[i]);",
" }",
" return;",
" }",
"",
" // handle partial block",
" var partialBytes = (this.blockSize - inputLength) % this.blockSize;",
" if(partialBytes > 0) {",
" partialBytes = this.blockSize - partialBytes;",
" }",
"",
" // XOR input with output, write input as partial output",
" this._partialOutput.clear();",
" for(var i = 0; i < this._ints; ++i) {",
" this._partialBlock[i] = input.getInt32();",
" this._partialOutput.putInt32(this._partialBlock[i] ^ this._outBlock[i]);",
" }",
"",
" if(partialBytes > 0) {",
" // block still incomplete, restore input buffer",
" input.read -= this.blockSize;",
" } else {",
" // block complete, update input block",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = this._partialBlock[i];",
" }",
" }",
"",
" // skip any previous partial bytes",
" if(this._partialBytes > 0) {",
" this._partialOutput.getBytes(this._partialBytes);",
" }",
"",
" if(partialBytes > 0 && !finish) {",
" output.putBytes(this._partialOutput.getBytes(",
" partialBytes - this._partialBytes));",
" this._partialBytes = partialBytes;",
" return true;",
" }",
"",
" output.putBytes(this._partialOutput.getBytes(",
" inputLength - this._partialBytes));",
" this._partialBytes = 0;",
"};",
"",
"/** Output feedback (OFB) **/",
"",
"modes.ofb = function(options) {",
" options = options || {};",
" this.name = 'OFB';",
" this.cipher = options.cipher;",
" this.blockSize = options.blockSize || 16;",
" this._ints = this.blockSize / 4;",
" this._inBlock = null;",
" this._outBlock = new Array(this._ints);",
" this._partialOutput = forge.util.createBuffer();",
" this._partialBytes = 0;",
"};",
"",
"modes.ofb.prototype.start = function(options) {",
" if(!('iv' in options)) {",
" throw new Error('Invalid IV parameter.');",
" }",
" // use IV as first input",
" this._iv = transformIV(options.iv);",
" this._inBlock = this._iv.slice(0);",
" this._partialBytes = 0;",
"};",
"",
"modes.ofb.prototype.encrypt = function(input, output, finish) {",
" // not enough input to encrypt",
" var inputLength = input.length();",
" if(input.length() === 0) {",
" return true;",
" }",
"",
" // encrypt block (OFB always uses encryption mode)",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // handle full block",
" if(this._partialBytes === 0 && inputLength >= this.blockSize) {",
" // XOR input with output and update next input",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(input.getInt32() ^ this._outBlock[i]);",
" this._inBlock[i] = this._outBlock[i];",
" }",
" return;",
" }",
"",
" // handle partial block",
" var partialBytes = (this.blockSize - inputLength) % this.blockSize;",
" if(partialBytes > 0) {",
" partialBytes = this.blockSize - partialBytes;",
" }",
"",
" // XOR input with output",
" this._partialOutput.clear();",
" for(var i = 0; i < this._ints; ++i) {",
" this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]);",
" }",
"",
" if(partialBytes > 0) {",
" // block still incomplete, restore input buffer",
" input.read -= this.blockSize;",
" } else {",
" // block complete, update input block",
" for(var i = 0; i < this._ints; ++i) {",
" this._inBlock[i] = this._outBlock[i];",
" }",
" }",
"",
" // skip any previous partial bytes",
" if(this._partialBytes > 0) {",
" this._partialOutput.getBytes(this._partialBytes);",
" }",
"",
" if(partialBytes > 0 && !finish) {",
" output.putBytes(this._partialOutput.getBytes(",
" partialBytes - this._partialBytes));",
" this._partialBytes = partialBytes;",
" return true;",
" }",
"",
" output.putBytes(this._partialOutput.getBytes(",
" inputLength - this._partialBytes));",
" this._partialBytes = 0;",
"};",
"",
"modes.ofb.prototype.decrypt = modes.ofb.prototype.encrypt;",
"",
"/** Counter (CTR) **/",
"",
"modes.ctr = function(options) {",
" options = options || {};",
" this.name = 'CTR';",
" this.cipher = options.cipher;",
" this.blockSize = options.blockSize || 16;",
" this._ints = this.blockSize / 4;",
" this._inBlock = null;",
" this._outBlock = new Array(this._ints);",
" this._partialOutput = forge.util.createBuffer();",
" this._partialBytes = 0;",
"};",
"",
"modes.ctr.prototype.start = function(options) {",
" if(!('iv' in options)) {",
" throw new Error('Invalid IV parameter.');",
" }",
" // use IV as first input",
" this._iv = transformIV(options.iv);",
" this._inBlock = this._iv.slice(0);",
" this._partialBytes = 0;",
"};",
"",
"modes.ctr.prototype.encrypt = function(input, output, finish) {",
" // not enough input to encrypt",
" var inputLength = input.length();",
" if(inputLength === 0) {",
" return true;",
" }",
"",
" // encrypt block (CTR always uses encryption mode)",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // handle full block",
" if(this._partialBytes === 0 && inputLength >= this.blockSize) {",
" // XOR input with output",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(input.getInt32() ^ this._outBlock[i]);",
" }",
" } else {",
" // handle partial block",
" var partialBytes = (this.blockSize - inputLength) % this.blockSize;",
" if(partialBytes > 0) {",
" partialBytes = this.blockSize - partialBytes;",
" }",
"",
" // XOR input with output",
" this._partialOutput.clear();",
" for(var i = 0; i < this._ints; ++i) {",
" this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]);",
" }",
"",
" if(partialBytes > 0) {",
" // block still incomplete, restore input buffer",
" input.read -= this.blockSize;",
" }",
"",
" // skip any previous partial bytes",
" if(this._partialBytes > 0) {",
" this._partialOutput.getBytes(this._partialBytes);",
" }",
"",
" if(partialBytes > 0 && !finish) {",
" output.putBytes(this._partialOutput.getBytes(",
" partialBytes - this._partialBytes));",
" this._partialBytes = partialBytes;",
" return true;",
" }",
"",
" output.putBytes(this._partialOutput.getBytes(",
" inputLength - this._partialBytes));",
" this._partialBytes = 0;",
" }",
"",
" // block complete, increment counter (input block)",
" inc32(this._inBlock);",
"};",
"",
"modes.ctr.prototype.decrypt = modes.ctr.prototype.encrypt;",
"",
"/** Galois/Counter Mode (GCM) **/",
"",
"modes.gcm = function(options) {",
" options = options || {};",
" this.name = 'GCM';",
" this.cipher = options.cipher;",
" this.blockSize = options.blockSize || 16;",
" this._ints = this.blockSize / 4;",
" this._inBlock = new Array(this._ints);",
" this._outBlock = new Array(this._ints);",
" this._partialOutput = forge.util.createBuffer();",
" this._partialBytes = 0;",
"",
" // R is actually this value concatenated with 120 more zero bits, but",
" // we only XOR against R so the other zeros have no effect -- we just",
" // apply this value to the first integer in a block",
" this._R = 0xE1000000;",
"};",
"",
"modes.gcm.prototype.start = function(options) {",
" if(!('iv' in options)) {",
" throw new Error('Invalid IV parameter.');",
" }",
" // ensure IV is a byte buffer",
" var iv = forge.util.createBuffer(options.iv);",
"",
" // no ciphered data processed yet",
" this._cipherLength = 0;",
"",
" // default additional data is none",
" var additionalData;",
" if('additionalData' in options) {",
" additionalData = forge.util.createBuffer(options.additionalData);",
" } else {",
" additionalData = forge.util.createBuffer();",
" }",
"",
" // default tag length is 128 bits",
" if('tagLength' in options) {",
" this._tagLength = options.tagLength;",
" } else {",
" this._tagLength = 128;",
" }",
"",
" // if tag is given, ensure tag matches tag length",
" this._tag = null;",
" if(options.decrypt) {",
" // save tag to check later",
" this._tag = forge.util.createBuffer(options.tag).getBytes();",
" if(this._tag.length !== (this._tagLength / 8)) {",
" throw new Error('Authentication tag does not match tag length.');",
" }",
" }",
"",
" // create tmp storage for hash calculation",
" this._hashBlock = new Array(this._ints);",
"",
" // no tag generated yet",
" this.tag = null;",
"",
" // generate hash subkey",
" // (apply block cipher to \"zero\" block)",
" this._hashSubkey = new Array(this._ints);",
" this.cipher.encrypt([0, 0, 0, 0], this._hashSubkey);",
"",
" // generate table M",
" // use 4-bit tables (32 component decomposition of a 16 byte value)",
" // 8-bit tables take more space and are known to have security",
" // vulnerabilities (in native implementations)",
" this.componentBits = 4;",
" this._m = this.generateHashTable(this._hashSubkey, this.componentBits);",
"",
" // Note: support IV length different from 96 bits? (only supporting",
" // 96 bits is recommended by NIST SP-800-38D)",
" // generate J_0",
" var ivLength = iv.length();",
" if(ivLength === 12) {",
" // 96-bit IV",
" this._j0 = [iv.getInt32(), iv.getInt32(), iv.getInt32(), 1];",
" } else {",
" // IV is NOT 96-bits",
" this._j0 = [0, 0, 0, 0];",
" while(iv.length() > 0) {",
" this._j0 = this.ghash(",
" this._hashSubkey, this._j0,",
" [iv.getInt32(), iv.getInt32(), iv.getInt32(), iv.getInt32()]);",
" }",
" this._j0 = this.ghash(",
" this._hashSubkey, this._j0, [0, 0].concat(from64To32(ivLength * 8)));",
" }",
"",
" // generate ICB (initial counter block)",
" this._inBlock = this._j0.slice(0);",
" inc32(this._inBlock);",
" this._partialBytes = 0;",
"",
" // consume authentication data",
" additionalData = forge.util.createBuffer(additionalData);",
" // save additional data length as a BE 64-bit number",
" this._aDataLength = from64To32(additionalData.length() * 8);",
" // pad additional data to 128 bit (16 byte) block size",
" var overflow = additionalData.length() % this.blockSize;",
" if(overflow) {",
" additionalData.fillWithByte(0, this.blockSize - overflow);",
" }",
" this._s = [0, 0, 0, 0];",
" while(additionalData.length() > 0) {",
" this._s = this.ghash(this._hashSubkey, this._s, [",
" additionalData.getInt32(),",
" additionalData.getInt32(),",
" additionalData.getInt32(),",
" additionalData.getInt32()",
" ]);",
" }",
"};",
"",
"modes.gcm.prototype.encrypt = function(input, output, finish) {",
" // not enough input to encrypt",
" var inputLength = input.length();",
" if(inputLength === 0) {",
" return true;",
" }",
"",
" // encrypt block",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // handle full block",
" if(this._partialBytes === 0 && inputLength >= this.blockSize) {",
" // XOR input with output",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(this._outBlock[i] ^= input.getInt32());",
" }",
" this._cipherLength += this.blockSize;",
" } else {",
" // handle partial block",
" var partialBytes = (this.blockSize - inputLength) % this.blockSize;",
" if(partialBytes > 0) {",
" partialBytes = this.blockSize - partialBytes;",
" }",
"",
" // XOR input with output",
" this._partialOutput.clear();",
" for(var i = 0; i < this._ints; ++i) {",
" this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]);",
" }",
"",
" if(partialBytes === 0 || finish) {",
" // handle overflow prior to hashing",
" if(finish) {",
" // get block overflow",
" var overflow = inputLength % this.blockSize;",
" this._cipherLength += overflow;",
" // truncate for hash function",
" this._partialOutput.truncate(this.blockSize - overflow);",
" } else {",
" this._cipherLength += this.blockSize;",
" }",
"",
" // get output block for hashing",
" for(var i = 0; i < this._ints; ++i) {",
" this._outBlock[i] = this._partialOutput.getInt32();",
" }",
" this._partialOutput.read -= this.blockSize;",
" }",
"",
" // skip any previous partial bytes",
" if(this._partialBytes > 0) {",
" this._partialOutput.getBytes(this._partialBytes);",
" }",
"",
" if(partialBytes > 0 && !finish) {",
" // block still incomplete, restore input buffer, get partial output,",
" // and return early",
" input.read -= this.blockSize;",
" output.putBytes(this._partialOutput.getBytes(",
" partialBytes - this._partialBytes));",
" this._partialBytes = partialBytes;",
" return true;",
" }",
"",
" output.putBytes(this._partialOutput.getBytes(",
" inputLength - this._partialBytes));",
" this._partialBytes = 0;",
" }",
"",
" // update hash block S",
" this._s = this.ghash(this._hashSubkey, this._s, this._outBlock);",
"",
" // increment counter (input block)",
" inc32(this._inBlock);",
"};",
"",
"modes.gcm.prototype.decrypt = function(input, output, finish) {",
" // not enough input to decrypt",
" var inputLength = input.length();",
" if(inputLength < this.blockSize && !(finish && inputLength > 0)) {",
" return true;",
" }",
"",
" // encrypt block (GCM always uses encryption mode)",
" this.cipher.encrypt(this._inBlock, this._outBlock);",
"",
" // increment counter (input block)",
" inc32(this._inBlock);",
"",
" // update hash block S",
" this._hashBlock[0] = input.getInt32();",
" this._hashBlock[1] = input.getInt32();",
" this._hashBlock[2] = input.getInt32();",
" this._hashBlock[3] = input.getInt32();",
" this._s = this.ghash(this._hashSubkey, this._s, this._hashBlock);",
"",
" // XOR hash input with output",
" for(var i = 0; i < this._ints; ++i) {",
" output.putInt32(this._outBlock[i] ^ this._hashBlock[i]);",
" }",
"",
" // increment cipher data length",
" if(inputLength < this.blockSize) {",
" this._cipherLength += inputLength % this.blockSize;",
" } else {",
" this._cipherLength += this.blockSize;",
" }",
"};",
"",
"modes.gcm.prototype.afterFinish = function(output, options) {",
" var rval = true;",
"",
" // handle overflow",
" if(options.decrypt && options.overflow) {",
" output.truncate(this.blockSize - options.overflow);",
" }",
"",
" // handle authentication tag",
" this.tag = forge.util.createBuffer();",
"",
" // concatenate additional data length with cipher length",
" var lengths = this._aDataLength.concat(from64To32(this._cipherLength * 8));",
"",
" // include lengths in hash",
" this._s = this.ghash(this._hashSubkey, this._s, lengths);",
"",
" // do GCTR(J_0, S)",
" var tag = [];",
" this.cipher.encrypt(this._j0, tag);",
" for(var i = 0; i < this._ints; ++i) {",
" this.tag.putInt32(this._s[i] ^ tag[i]);",
" }",
"",
" // trim tag to length",
" this.tag.truncate(this.tag.length() % (this._tagLength / 8));",
"",
" // check authentication tag",
" if(options.decrypt && this.tag.bytes() !== this._tag) {",
" rval = false;",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * See NIST SP-800-38D 6.3 (Algorithm 1). This function performs Galois",
" * field multiplication. The field, GF(2^128), is defined by the polynomial:",
" *",
" * x^128 + x^7 + x^2 + x + 1",
" *",
" * Which is represented in little-endian binary form as: 11100001 (0xe1). When",
" * the value of a coefficient is 1, a bit is set. The value R, is the",
" * concatenation of this value and 120 zero bits, yielding a 128-bit value",
" * which matches the block size.",
" *",
" * This function will multiply two elements (vectors of bytes), X and Y, in",
" * the field GF(2^128). The result is initialized to zero. For each bit of",
" * X (out of 128), x_i, if x_i is set, then the result is multiplied (XOR'd)",
" * by the current value of Y. For each bit, the value of Y will be raised by",
" * a power of x (multiplied by the polynomial x). This can be achieved by",
" * shifting Y once to the right. If the current value of Y, prior to being",
" * multiplied by x, has 0 as its LSB, then it is a 127th degree polynomial.",
" * Otherwise, we must divide by R after shifting to find the remainder.",
" *",
" * @param x the first block to multiply by the second.",
" * @param y the second block to multiply by the first.",
" *",
" * @return the block result of the multiplication.",
" */",
"modes.gcm.prototype.multiply = function(x, y) {",
" var z_i = [0, 0, 0, 0];",
" var v_i = y.slice(0);",
"",
" // calculate Z_128 (block has 128 bits)",
" for(var i = 0; i < 128; ++i) {",
" // if x_i is 0, Z_{i+1} = Z_i (unchanged)",
" // else Z_{i+1} = Z_i ^ V_i",
" // get x_i by finding 32-bit int position, then left shift 1 by remainder",
" var x_i = x[(i / 32) | 0] & (1 << (31 - i % 32));",
" if(x_i) {",
" z_i[0] ^= v_i[0];",
" z_i[1] ^= v_i[1];",
" z_i[2] ^= v_i[2];",
" z_i[3] ^= v_i[3];",
" }",
"",
" // if LSB(V_i) is 1, V_i = V_i >> 1",
" // else V_i = (V_i >> 1) ^ R",
" this.pow(v_i, v_i);",
" }",
"",
" return z_i;",
"};",
"",
"modes.gcm.prototype.pow = function(x, out) {",
" // if LSB(x) is 1, x = x >>> 1",
" // else x = (x >>> 1) ^ R",
" var lsb = x[3] & 1;",
"",
" // always do x >>> 1:",
" // starting with the rightmost integer, shift each integer to the right",
" // one bit, pulling in the bit from the integer to the left as its top",
" // most bit (do this for the last 3 integers)",
" for(var i = 3; i > 0; --i) {",
" out[i] = (x[i] >>> 1) | ((x[i - 1] & 1) << 31);",
" }",
" // shift the first integer normally",
" out[0] = x[0] >>> 1;",
"",
" // if lsb was not set, then polynomial had a degree of 127 and doesn't",
" // need to divided; otherwise, XOR with R to find the remainder; we only",
" // need to XOR the first integer since R technically ends w/120 zero bits",
" if(lsb) {",
" out[0] ^= this._R;",
" }",
"};",
"",
"modes.gcm.prototype.tableMultiply = function(x) {",
" // assumes 4-bit tables are used",
" var z = [0, 0, 0, 0];",
" for(var i = 0; i < 32; ++i) {",
" var idx = (i / 8) | 0;",
" var x_i = (x[idx] >>> ((7 - (i % 8)) * 4)) & 0xF;",
" var ah = this._m[i][x_i];",
" z[0] ^= ah[0];",
" z[1] ^= ah[1];",
" z[2] ^= ah[2];",
" z[3] ^= ah[3];",
" }",
" return z;",
"};",
"",
"/**",
" * A continuing version of the GHASH algorithm that operates on a single",
" * block. The hash block, last hash value (Ym) and the new block to hash",
" * are given.",
" *",
" * @param h the hash block.",
" * @param y the previous value for Ym, use [0, 0, 0, 0] for a new hash.",
" * @param x the block to hash.",
" *",
" * @return the hashed value (Ym).",
" */",
"modes.gcm.prototype.ghash = function(h, y, x) {",
" y[0] ^= x[0];",
" y[1] ^= x[1];",
" y[2] ^= x[2];",
" y[3] ^= x[3];",
" return this.tableMultiply(y);",
" //return this.multiply(y, h);",
"};",
"",
"/**",
" * Precomputes a table for multiplying against the hash subkey. This",
" * mechanism provides a substantial speed increase over multiplication",
" * performed without a table. The table-based multiplication this table is",
" * for solves X * H by multiplying each component of X by H and then",
" * composing the results together using XOR.",
" *",
" * This function can be used to generate tables with different bit sizes",
" * for the components, however, this implementation assumes there are",
" * 32 components of X (which is a 16 byte vector), therefore each component",
" * takes 4-bits (so the table is constructed with bits=4).",
" *",
" * @param h the hash subkey.",
" * @param bits the bit size for a component.",
" */",
"modes.gcm.prototype.generateHashTable = function(h, bits) {",
" // TODO: There are further optimizations that would use only the",
" // first table M_0 (or some variant) along with a remainder table;",
" // this can be explored in the future",
" var multiplier = 8 / bits;",
" var perInt = 4 * multiplier;",
" var size = 16 * multiplier;",
" var m = new Array(size);",
" for(var i = 0; i < size; ++i) {",
" var tmp = [0, 0, 0, 0];",
" var idx = (i / perInt) | 0;",
" var shft = ((perInt - 1 - (i % perInt)) * bits);",
" tmp[idx] = (1 << (bits - 1)) << shft;",
" m[i] = this.generateSubHashTable(this.multiply(tmp, h), bits);",
" }",
" return m;",
"};",
"",
"/**",
" * Generates a table for multiplying against the hash subkey for one",
" * particular component (out of all possible component values).",
" *",
" * @param mid the pre-multiplied value for the middle key of the table.",
" * @param bits the bit size for a component.",
" */",
"modes.gcm.prototype.generateSubHashTable = function(mid, bits) {",
" // compute the table quickly by minimizing the number of",
" // POW operations -- they only need to be performed for powers of 2,",
" // all other entries can be composed from those powers using XOR",
" var size = 1 << bits;",
" var half = size >>> 1;",
" var m = new Array(size);",
" m[half] = mid.slice(0);",
" var i = half >>> 1;",
" while(i > 0) {",
" // raise m0[2 * i] and store in m0[i]",
" this.pow(m[2 * i], m[i] = []);",
" i >>= 1;",
" }",
" i = 2;",
" while(i < half) {",
" for(var j = 1; j < i; ++j) {",
" var m_i = m[i];",
" var m_j = m[j];",
" m[i + j] = [",
" m_i[0] ^ m_j[0],",
" m_i[1] ^ m_j[1],",
" m_i[2] ^ m_j[2],",
" m_i[3] ^ m_j[3]",
" ];",
" }",
" i *= 2;",
" }",
" m[0] = [0, 0, 0, 0];",
" /* Note: We could avoid storing these by doing composition during multiply",
" calculate top half using composition by speed is preferred. */",
" for(i = half + 1; i < size; ++i) {",
" var c = m[i ^ half];",
" m[i] = [mid[0] ^ c[0], mid[1] ^ c[1], mid[2] ^ c[2], mid[3] ^ c[3]];",
" }",
" return m;",
"};",
"",
"/** Utility functions */",
"",
"function transformIV(iv) {",
" if(typeof iv === 'string') {",
" // convert iv string into byte buffer",
" iv = forge.util.createBuffer(iv);",
" }",
"",
" if(forge.util.isArray(iv) && iv.length > 4) {",
" // convert iv byte array into byte buffer",
" var tmp = iv;",
" iv = forge.util.createBuffer();",
" for(var i = 0; i < tmp.length; ++i) {",
" iv.putByte(tmp[i]);",
" }",
" }",
" if(!forge.util.isArray(iv)) {",
" // convert iv byte buffer into 32-bit integer array",
" iv = [iv.getInt32(), iv.getInt32(), iv.getInt32(), iv.getInt32()];",
" }",
"",
" return iv;",
"}",
"",
"function inc32(block) {",
" // increment last 32 bits of block only",
" block[block.length - 1] = (block[block.length - 1] + 1) & 0xFFFFFFFF;",
"}",
"",
"function from64To32(num) {",
" // convert 64-bit number to two BE Int32s",
" return [(num / 0x100000000) | 0, num & 0xFFFFFFFF];",
"}",
"",
"",
"/***/ }),",
"/* 19 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * A Javascript implementation of Transport Layer Security (TLS).",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2009-2014 Digital Bazaar, Inc.",
" *",
" * The TLS Handshake Protocol involves the following steps:",
" *",
" * - Exchange hello messages to agree on algorithms, exchange random values,",
" * and check for session resumption.",
" *",
" * - Exchange the necessary cryptographic parameters to allow the client and",
" * server to agree on a premaster secret.",
" *",
" * - Exchange certificates and cryptographic information to allow the client",
" * and server to authenticate themselves.",
" *",
" * - Generate a master secret from the premaster secret and exchanged random",
" * values.",
" *",
" * - Provide security parameters to the record layer.",
" *",
" * - Allow the client and server to verify that their peer has calculated the",
" * same security parameters and that the handshake occurred without tampering",
" * by an attacker.",
" *",
" * Up to 4 different messages may be sent during a key exchange. The server",
" * certificate, the server key exchange, the client certificate, and the",
" * client key exchange.",
" *",
" * A typical handshake (from the client's perspective).",
" *",
" * 1. Client sends ClientHello.",
" * 2. Client receives ServerHello.",
" * 3. Client receives optional Certificate.",
" * 4. Client receives optional ServerKeyExchange.",
" * 5. Client receives ServerHelloDone.",
" * 6. Client sends optional Certificate.",
" * 7. Client sends ClientKeyExchange.",
" * 8. Client sends optional CertificateVerify.",
" * 9. Client sends ChangeCipherSpec.",
" * 10. Client sends Finished.",
" * 11. Client receives ChangeCipherSpec.",
" * 12. Client receives Finished.",
" * 13. Client sends/receives application data.",
" *",
" * To reuse an existing session:",
" *",
" * 1. Client sends ClientHello with session ID for reuse.",
" * 2. Client receives ServerHello with same session ID if reusing.",
" * 3. Client receives ChangeCipherSpec message if reusing.",
" * 4. Client receives Finished.",
" * 5. Client sends ChangeCipherSpec.",
" * 6. Client sends Finished.",
" *",
" * Note: Client ignores HelloRequest if in the middle of a handshake.",
" *",
" * Record Layer:",
" *",
" * The record layer fragments information blocks into TLSPlaintext records",
" * carrying data in chunks of 2^14 bytes or less. Client message boundaries are",
" * not preserved in the record layer (i.e., multiple client messages of the",
" * same ContentType MAY be coalesced into a single TLSPlaintext record, or a",
" * single message MAY be fragmented across several records).",
" *",
" * struct {",
" * uint8 major;",
" * uint8 minor;",
" * } ProtocolVersion;",
" *",
" * struct {",
" * ContentType type;",
" * ProtocolVersion version;",
" * uint16 length;",
" * opaque fragment[TLSPlaintext.length];",
" * } TLSPlaintext;",
" *",
" * type:",
" * The higher-level protocol used to process the enclosed fragment.",
" *",
" * version:",
" * The version of the protocol being employed. TLS Version 1.2 uses version",
" * {3, 3}. TLS Version 1.0 uses version {3, 1}. Note that a client that",
" * supports multiple versions of TLS may not know what version will be",
" * employed before it receives the ServerHello.",
" *",
" * length:",
" * The length (in bytes) of the following TLSPlaintext.fragment. The length",
" * MUST NOT exceed 2^14 = 16384 bytes.",
" *",
" * fragment:",
" * The application data. This data is transparent and treated as an",
" * independent block to be dealt with by the higher-level protocol specified",
" * by the type field.",
" *",
" * Implementations MUST NOT send zero-length fragments of Handshake, Alert, or",
" * ChangeCipherSpec content types. Zero-length fragments of Application data",
" * MAY be sent as they are potentially useful as a traffic analysis",
" * countermeasure.",
" *",
" * Note: Data of different TLS record layer content types MAY be interleaved.",
" * Application data is generally of lower precedence for transmission than",
" * other content types. However, records MUST be delivered to the network in",
" * the same order as they are protected by the record layer. Recipients MUST",
" * receive and process interleaved application layer traffic during handshakes",
" * subsequent to the first one on a connection.",
" *",
" * struct {",
" * ContentType type; // same as TLSPlaintext.type",
" * ProtocolVersion version;// same as TLSPlaintext.version",
" * uint16 length;",
" * opaque fragment[TLSCompressed.length];",
" * } TLSCompressed;",
" *",
" * length:",
" * The length (in bytes) of the following TLSCompressed.fragment.",
" * The length MUST NOT exceed 2^14 + 1024.",
" *",
" * fragment:",
" * The compressed form of TLSPlaintext.fragment.",
" *",
" * Note: A CompressionMethod.null operation is an identity operation; no fields",
" * are altered. In this implementation, since no compression is supported,",
" * uncompressed records are always the same as compressed records.",
" *",
" * Encryption Information:",
" *",
" * The encryption and MAC functions translate a TLSCompressed structure into a",
" * TLSCiphertext. The decryption functions reverse the process. The MAC of the",
" * record also includes a sequence number so that missing, extra, or repeated",
" * messages are detectable.",
" *",
" * struct {",
" * ContentType type;",
" * ProtocolVersion version;",
" * uint16 length;",
" * select (SecurityParameters.cipher_type) {",
" * case stream: GenericStreamCipher;",
" * case block: GenericBlockCipher;",
" * case aead: GenericAEADCipher;",
" * } fragment;",
" * } TLSCiphertext;",
" *",
" * type:",
" * The type field is identical to TLSCompressed.type.",
" *",
" * version:",
" * The version field is identical to TLSCompressed.version.",
" *",
" * length:",
" * The length (in bytes) of the following TLSCiphertext.fragment.",
" * The length MUST NOT exceed 2^14 + 2048.",
" *",
" * fragment:",
" * The encrypted form of TLSCompressed.fragment, with the MAC.",
" *",
" * Note: Only CBC Block Ciphers are supported by this implementation.",
" *",
" * The TLSCompressed.fragment structures are converted to/from block",
" * TLSCiphertext.fragment structures.",
" *",
" * struct {",
" * opaque IV[SecurityParameters.record_iv_length];",
" * block-ciphered struct {",
" * opaque content[TLSCompressed.length];",
" * opaque MAC[SecurityParameters.mac_length];",
" * uint8 padding[GenericBlockCipher.padding_length];",
" * uint8 padding_length;",
" * };",
" * } GenericBlockCipher;",
" *",
" * The MAC is generated as described in Section 6.2.3.1.",
" *",
" * IV:",
" * The Initialization Vector (IV) SHOULD be chosen at random, and MUST be",
" * unpredictable. Note that in versions of TLS prior to 1.1, there was no",
" * IV field, and the last ciphertext block of the previous record (the \"CBC",
" * residue\") was used as the IV. This was changed to prevent the attacks",
" * described in [CBCATT]. For block ciphers, the IV length is of length",
" * SecurityParameters.record_iv_length, which is equal to the",
" * SecurityParameters.block_size.",
" *",
" * padding:",
" * Padding that is added to force the length of the plaintext to be an",
" * integral multiple of the block cipher's block length. The padding MAY be",
" * any length up to 255 bytes, as long as it results in the",
" * TLSCiphertext.length being an integral multiple of the block length.",
" * Lengths longer than necessary might be desirable to frustrate attacks on",
" * a protocol that are based on analysis of the lengths of exchanged",
" * messages. Each uint8 in the padding data vector MUST be filled with the",
" * padding length value. The receiver MUST check this padding and MUST use",
" * the bad_record_mac alert to indicate padding errors.",
" *",
" * padding_length:",
" * The padding length MUST be such that the total size of the",
" * GenericBlockCipher structure is a multiple of the cipher's block length.",
" * Legal values range from zero to 255, inclusive. This length specifies the",
" * length of the padding field exclusive of the padding_length field itself.",
" *",
" * The encrypted data length (TLSCiphertext.length) is one more than the sum of",
" * SecurityParameters.block_length, TLSCompressed.length,",
" * SecurityParameters.mac_length, and padding_length.",
" *",
" * Example: If the block length is 8 bytes, the content length",
" * (TLSCompressed.length) is 61 bytes, and the MAC length is 20 bytes, then the",
" * length before padding is 82 bytes (this does not include the IV. Thus, the",
" * padding length modulo 8 must be equal to 6 in order to make the total length",
" * an even multiple of 8 bytes (the block length). The padding length can be",
" * 6, 14, 22, and so on, through 254. If the padding length were the minimum",
" * necessary, 6, the padding would be 6 bytes, each containing the value 6.",
" * Thus, the last 8 octets of the GenericBlockCipher before block encryption",
" * would be xx 06 06 06 06 06 06 06, where xx is the last octet of the MAC.",
" *",
" * Note: With block ciphers in CBC mode (Cipher Block Chaining), it is critical",
" * that the entire plaintext of the record be known before any ciphertext is",
" * transmitted. Otherwise, it is possible for the attacker to mount the attack",
" * described in [CBCATT].",
" *",
" * Implementation note: Canvel et al. [CBCTIME] have demonstrated a timing",
" * attack on CBC padding based on the time required to compute the MAC. In",
" * order to defend against this attack, implementations MUST ensure that",
" * record processing time is essentially the same whether or not the padding",
" * is correct. In general, the best way to do this is to compute the MAC even",
" * if the padding is incorrect, and only then reject the packet. For instance,",
" * if the pad appears to be incorrect, the implementation might assume a",
" * zero-length pad and then compute the MAC. This leaves a small timing",
" * channel, since MAC performance depends, to some extent, on the size of the",
" * data fragment, but it is not believed to be large enough to be exploitable,",
" * due to the large block size of existing MACs and the small size of the",
" * timing signal.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(3);",
"__webpack_require__(8);",
"__webpack_require__(14);",
"__webpack_require__(7);",
"__webpack_require__(20);",
"__webpack_require__(2);",
"__webpack_require__(9);",
"__webpack_require__(1);",
"",
"/**",
" * Generates pseudo random bytes by mixing the result of two hash functions,",
" * MD5 and SHA-1.",
" *",
" * prf_TLS1(secret, label, seed) =",
" * P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);",
" *",
" * Each P_hash function functions as follows:",
" *",
" * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +",
" * HMAC_hash(secret, A(2) + seed) +",
" * HMAC_hash(secret, A(3) + seed) + ...",
" * A() is defined as:",
" * A(0) = seed",
" * A(i) = HMAC_hash(secret, A(i-1))",
" *",
" * The '+' operator denotes concatenation.",
" *",
" * As many iterations A(N) as are needed are performed to generate enough",
" * pseudo random byte output. If an iteration creates more data than is",
" * necessary, then it is truncated.",
" *",
" * Therefore:",
" * A(1) = HMAC_hash(secret, A(0))",
" * = HMAC_hash(secret, seed)",
" * A(2) = HMAC_hash(secret, A(1))",
" * = HMAC_hash(secret, HMAC_hash(secret, seed))",
" *",
" * Therefore:",
" * P_hash(secret, seed) =",
" * HMAC_hash(secret, HMAC_hash(secret, A(0)) + seed) +",
" * HMAC_hash(secret, HMAC_hash(secret, A(1)) + seed) +",
" * ...",
" *",
" * Therefore:",
" * P_hash(secret, seed) =",
" * HMAC_hash(secret, HMAC_hash(secret, seed) + seed) +",
" * HMAC_hash(secret, HMAC_hash(secret, HMAC_hash(secret, seed)) + seed) +",
" * ...",
" *",
" * @param secret the secret to use.",
" * @param label the label to use.",
" * @param seed the seed value to use.",
" * @param length the number of bytes to generate.",
" *",
" * @return the pseudo random bytes in a byte buffer.",
" */",
"var prf_TLS1 = function(secret, label, seed, length) {",
" var rval = forge.util.createBuffer();",
"",
" /* For TLS 1.0, the secret is split in half, into two secrets of equal",
" length. If the secret has an odd length then the last byte of the first",
" half will be the same as the first byte of the second. The length of the",
" two secrets is half of the secret rounded up. */",
" var idx = (secret.length >> 1);",
" var slen = idx + (secret.length & 1);",
" var s1 = secret.substr(0, slen);",
" var s2 = secret.substr(idx, slen);",
" var ai = forge.util.createBuffer();",
" var hmac = forge.hmac.create();",
" seed = label + seed;",
"",
" // determine the number of iterations that must be performed to generate",
" // enough output bytes, md5 creates 16 byte hashes, sha1 creates 20",
" var md5itr = Math.ceil(length / 16);",
" var sha1itr = Math.ceil(length / 20);",
"",
" // do md5 iterations",
" hmac.start('MD5', s1);",
" var md5bytes = forge.util.createBuffer();",
" ai.putBytes(seed);",
" for(var i = 0; i < md5itr; ++i) {",
" // HMAC_hash(secret, A(i-1))",
" hmac.start(null, null);",
" hmac.update(ai.getBytes());",
" ai.putBuffer(hmac.digest());",
"",
" // HMAC_hash(secret, A(i) + seed)",
" hmac.start(null, null);",
" hmac.update(ai.bytes() + seed);",
" md5bytes.putBuffer(hmac.digest());",
" }",
"",
" // do sha1 iterations",
" hmac.start('SHA1', s2);",
" var sha1bytes = forge.util.createBuffer();",
" ai.clear();",
" ai.putBytes(seed);",
" for(var i = 0; i < sha1itr; ++i) {",
" // HMAC_hash(secret, A(i-1))",
" hmac.start(null, null);",
" hmac.update(ai.getBytes());",
" ai.putBuffer(hmac.digest());",
"",
" // HMAC_hash(secret, A(i) + seed)",
" hmac.start(null, null);",
" hmac.update(ai.bytes() + seed);",
" sha1bytes.putBuffer(hmac.digest());",
" }",
"",
" // XOR the md5 bytes with the sha1 bytes",
" rval.putBytes(forge.util.xorBytes(",
" md5bytes.getBytes(), sha1bytes.getBytes(), length));",
"",
" return rval;",
"};",
"",
"/**",
" * Generates pseudo random bytes using a SHA256 algorithm. For TLS 1.2.",
" *",
" * @param secret the secret to use.",
" * @param label the label to use.",
" * @param seed the seed value to use.",
" * @param length the number of bytes to generate.",
" *",
" * @return the pseudo random bytes in a byte buffer.",
" */",
"var prf_sha256 = function(secret, label, seed, length) {",
" // FIXME: implement me for TLS 1.2",
"};",
"",
"/**",
" * Gets a MAC for a record using the SHA-1 hash algorithm.",
" *",
" * @param key the mac key.",
" * @param state the sequence number (array of two 32-bit integers).",
" * @param record the record.",
" *",
" * @return the sha-1 hash (20 bytes) for the given record.",
" */",
"var hmac_sha1 = function(key, seqNum, record) {",
" /* MAC is computed like so:",
" HMAC_hash(",
" key, seqNum +",
" TLSCompressed.type +",
" TLSCompressed.version +",
" TLSCompressed.length +",
" TLSCompressed.fragment)",
" */",
" var hmac = forge.hmac.create();",
" hmac.start('SHA1', key);",
" var b = forge.util.createBuffer();",
" b.putInt32(seqNum[0]);",
" b.putInt32(seqNum[1]);",
" b.putByte(record.type);",
" b.putByte(record.version.major);",
" b.putByte(record.version.minor);",
" b.putInt16(record.length);",
" b.putBytes(record.fragment.bytes());",
" hmac.update(b.getBytes());",
" return hmac.digest().getBytes();",
"};",
"",
"/**",
" * Compresses the TLSPlaintext record into a TLSCompressed record using the",
" * deflate algorithm.",
" *",
" * @param c the TLS connection.",
" * @param record the TLSPlaintext record to compress.",
" * @param s the ConnectionState to use.",
" *",
" * @return true on success, false on failure.",
" */",
"var deflate = function(c, record, s) {",
" var rval = false;",
"",
" try {",
" var bytes = c.deflate(record.fragment.getBytes());",
" record.fragment = forge.util.createBuffer(bytes);",
" record.length = bytes.length;",
" rval = true;",
" } catch(ex) {",
" // deflate error, fail out",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Decompresses the TLSCompressed record into a TLSPlaintext record using the",
" * deflate algorithm.",
" *",
" * @param c the TLS connection.",
" * @param record the TLSCompressed record to decompress.",
" * @param s the ConnectionState to use.",
" *",
" * @return true on success, false on failure.",
" */",
"var inflate = function(c, record, s) {",
" var rval = false;",
"",
" try {",
" var bytes = c.inflate(record.fragment.getBytes());",
" record.fragment = forge.util.createBuffer(bytes);",
" record.length = bytes.length;",
" rval = true;",
" } catch(ex) {",
" // inflate error, fail out",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Reads a TLS variable-length vector from a byte buffer.",
" *",
" * Variable-length vectors are defined by specifying a subrange of legal",
" * lengths, inclusively, using the notation . When these are",
" * encoded, the actual length precedes the vector's contents in the byte",
" * stream. The length will be in the form of a number consuming as many bytes",
" * as required to hold the vector's specified maximum (ceiling) length. A",
" * variable-length vector with an actual length field of zero is referred to",
" * as an empty vector.",
" *",
" * @param b the byte buffer.",
" * @param lenBytes the number of bytes required to store the length.",
" *",
" * @return the resulting byte buffer.",
" */",
"var readVector = function(b, lenBytes) {",
" var len = 0;",
" switch(lenBytes) {",
" case 1:",
" len = b.getByte();",
" break;",
" case 2:",
" len = b.getInt16();",
" break;",
" case 3:",
" len = b.getInt24();",
" break;",
" case 4:",
" len = b.getInt32();",
" break;",
" }",
"",
" // read vector bytes into a new buffer",
" return forge.util.createBuffer(b.getBytes(len));",
"};",
"",
"/**",
" * Writes a TLS variable-length vector to a byte buffer.",
" *",
" * @param b the byte buffer.",
" * @param lenBytes the number of bytes required to store the length.",
" * @param v the byte buffer vector.",
" */",
"var writeVector = function(b, lenBytes, v) {",
" // encode length at the start of the vector, where the number of bytes for",
" // the length is the maximum number of bytes it would take to encode the",
" // vector's ceiling",
" b.putInt(v.length(), lenBytes << 3);",
" b.putBuffer(v);",
"};",
"",
"/**",
" * The tls implementation.",
" */",
"var tls = {};",
"",
"/**",
" * Version: TLS 1.2 = 3.3, TLS 1.1 = 3.2, TLS 1.0 = 3.1. Both TLS 1.1 and",
" * TLS 1.2 were still too new (ie: openSSL didn't implement them) at the time",
" * of this implementation so TLS 1.0 was implemented instead.",
" */",
"tls.Versions = {",
" TLS_1_0: {major: 3, minor: 1},",
" TLS_1_1: {major: 3, minor: 2},",
" TLS_1_2: {major: 3, minor: 3}",
"};",
"tls.SupportedVersions = [",
" tls.Versions.TLS_1_1,",
" tls.Versions.TLS_1_0",
"];",
"tls.Version = tls.SupportedVersions[0];",
"",
"/**",
" * Maximum fragment size. True maximum is 16384, but we fragment before that",
" * to allow for unusual small increases during compression.",
" */",
"tls.MaxFragment = 16384 - 1024;",
"",
"/**",
" * Whether this entity is considered the \"client\" or \"server\".",
" * enum { server, client } ConnectionEnd;",
" */",
"tls.ConnectionEnd = {",
" server: 0,",
" client: 1",
"};",
"",
"/**",
" * Pseudo-random function algorithm used to generate keys from the master",
" * secret.",
" * enum { tls_prf_sha256 } PRFAlgorithm;",
" */",
"tls.PRFAlgorithm = {",
" tls_prf_sha256: 0",
"};",
"",
"/**",
" * Bulk encryption algorithms.",
" * enum { null, rc4, des3, aes } BulkCipherAlgorithm;",
" */",
"tls.BulkCipherAlgorithm = {",
" none: null,",
" rc4: 0,",
" des3: 1,",
" aes: 2",
"};",
"",
"/**",
" * Cipher types.",
" * enum { stream, block, aead } CipherType;",
" */",
"tls.CipherType = {",
" stream: 0,",
" block: 1,",
" aead: 2",
"};",
"",
"/**",
" * MAC (Message Authentication Code) algorithms.",
" * enum { null, hmac_md5, hmac_sha1, hmac_sha256,",
" * hmac_sha384, hmac_sha512} MACAlgorithm;",
" */",
"tls.MACAlgorithm = {",
" none: null,",
" hmac_md5: 0,",
" hmac_sha1: 1,",
" hmac_sha256: 2,",
" hmac_sha384: 3,",
" hmac_sha512: 4",
"};",
"",
"/**",
" * Compression algorithms.",
" * enum { null(0), deflate(1), (255) } CompressionMethod;",
" */",
"tls.CompressionMethod = {",
" none: 0,",
" deflate: 1",
"};",
"",
"/**",
" * TLS record content types.",
" * enum {",
" * change_cipher_spec(20), alert(21), handshake(22),",
" * application_data(23), (255)",
" * } ContentType;",
" */",
"tls.ContentType = {",
" change_cipher_spec: 20,",
" alert: 21,",
" handshake: 22,",
" application_data: 23,",
" heartbeat: 24",
"};",
"",
"/**",
" * TLS handshake types.",
" * enum {",
" * hello_request(0), client_hello(1), server_hello(2),",
" * certificate(11), server_key_exchange (12),",
" * certificate_request(13), server_hello_done(14),",
" * certificate_verify(15), client_key_exchange(16),",
" * finished(20), (255)",
" * } HandshakeType;",
" */",
"tls.HandshakeType = {",
" hello_request: 0,",
" client_hello: 1,",
" server_hello: 2,",
" certificate: 11,",
" server_key_exchange: 12,",
" certificate_request: 13,",
" server_hello_done: 14,",
" certificate_verify: 15,",
" client_key_exchange: 16,",
" finished: 20",
"};",
"",
"/**",
" * TLS Alert Protocol.",
" *",
" * enum { warning(1), fatal(2), (255) } AlertLevel;",
" *",
" * enum {",
" * close_notify(0),",
" * unexpected_message(10),",
" * bad_record_mac(20),",
" * decryption_failed(21),",
" * record_overflow(22),",
" * decompression_failure(30),",
" * handshake_failure(40),",
" * bad_certificate(42),",
" * unsupported_certificate(43),",
" * certificate_revoked(44),",
" * certificate_expired(45),",
" * certificate_unknown(46),",
" * illegal_parameter(47),",
" * unknown_ca(48),",
" * access_denied(49),",
" * decode_error(50),",
" * decrypt_error(51),",
" * export_restriction(60),",
" * protocol_version(70),",
" * insufficient_security(71),",
" * internal_error(80),",
" * user_canceled(90),",
" * no_renegotiation(100),",
" * (255)",
" * } AlertDescription;",
" *",
" * struct {",
" * AlertLevel level;",
" * AlertDescription description;",
" * } Alert;",
" */",
"tls.Alert = {};",
"tls.Alert.Level = {",
" warning: 1,",
" fatal: 2",
"};",
"tls.Alert.Description = {",
" close_notify: 0,",
" unexpected_message: 10,",
" bad_record_mac: 20,",
" decryption_failed: 21,",
" record_overflow: 22,",
" decompression_failure: 30,",
" handshake_failure: 40,",
" bad_certificate: 42,",
" unsupported_certificate: 43,",
" certificate_revoked: 44,",
" certificate_expired: 45,",
" certificate_unknown: 46,",
" illegal_parameter: 47,",
" unknown_ca: 48,",
" access_denied: 49,",
" decode_error: 50,",
" decrypt_error: 51,",
" export_restriction: 60,",
" protocol_version: 70,",
" insufficient_security: 71,",
" internal_error: 80,",
" user_canceled: 90,",
" no_renegotiation: 100",
"};",
"",
"/**",
" * TLS Heartbeat Message types.",
" * enum {",
" * heartbeat_request(1),",
" * heartbeat_response(2),",
" * (255)",
" * } HeartbeatMessageType;",
" */",
"tls.HeartbeatMessageType = {",
" heartbeat_request: 1,",
" heartbeat_response: 2",
"};",
"",
"/**",
" * Supported cipher suites.",
" */",
"tls.CipherSuites = {};",
"",
"/**",
" * Gets a supported cipher suite from its 2 byte ID.",
" *",
" * @param twoBytes two bytes in a string.",
" *",
" * @return the matching supported cipher suite or null.",
" */",
"tls.getCipherSuite = function(twoBytes) {",
" var rval = null;",
" for(var key in tls.CipherSuites) {",
" var cs = tls.CipherSuites[key];",
" if(cs.id[0] === twoBytes.charCodeAt(0) &&",
" cs.id[1] === twoBytes.charCodeAt(1)) {",
" rval = cs;",
" break;",
" }",
" }",
" return rval;",
"};",
"",
"/**",
" * Called when an unexpected record is encountered.",
" *",
" * @param c the connection.",
" * @param record the record.",
" */",
"tls.handleUnexpected = function(c, record) {",
" // if connection is client and closed, ignore unexpected messages",
" var ignore = (!c.open && c.entity === tls.ConnectionEnd.client);",
" if(!ignore) {",
" c.error(c, {",
" message: 'Unexpected message. Received TLS record out of order.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.unexpected_message",
" }",
" });",
" }",
"};",
"",
"/**",
" * Called when a client receives a HelloRequest record.",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleHelloRequest = function(c, record, length) {",
" // ignore renegotiation requests from the server during a handshake, but",
" // if handshaking, send a warning alert that renegotation is denied",
" if(!c.handshaking && c.handshakes > 0) {",
" // send alert warning",
" tls.queue(c, tls.createAlert(c, {",
" level: tls.Alert.Level.warning,",
" description: tls.Alert.Description.no_renegotiation",
" }));",
" tls.flush(c);",
" }",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Parses a hello message from a ClientHello or ServerHello record.",
" *",
" * @param record the record to parse.",
" *",
" * @return the parsed message.",
" */",
"tls.parseHelloMessage = function(c, record, length) {",
" var msg = null;",
"",
" var client = (c.entity === tls.ConnectionEnd.client);",
"",
" // minimum of 38 bytes in message",
" if(length < 38) {",
" c.error(c, {",
" message: client ?",
" 'Invalid ServerHello message. Message too short.' :",
" 'Invalid ClientHello message. Message too short.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.illegal_parameter",
" }",
" });",
" } else {",
" // use 'remaining' to calculate # of remaining bytes in the message",
" var b = record.fragment;",
" var remaining = b.length();",
" msg = {",
" version: {",
" major: b.getByte(),",
" minor: b.getByte()",
" },",
" random: forge.util.createBuffer(b.getBytes(32)),",
" session_id: readVector(b, 1),",
" extensions: []",
" };",
" if(client) {",
" msg.cipher_suite = b.getBytes(2);",
" msg.compression_method = b.getByte();",
" } else {",
" msg.cipher_suites = readVector(b, 2);",
" msg.compression_methods = readVector(b, 1);",
" }",
"",
" // read extensions if there are any bytes left in the message",
" remaining = length - (remaining - b.length());",
" if(remaining > 0) {",
" // parse extensions",
" var exts = readVector(b, 2);",
" while(exts.length() > 0) {",
" msg.extensions.push({",
" type: [exts.getByte(), exts.getByte()],",
" data: readVector(exts, 2)",
" });",
" }",
"",
" // TODO: make extension support modular",
" if(!client) {",
" for(var i = 0; i < msg.extensions.length; ++i) {",
" var ext = msg.extensions[i];",
"",
" // support SNI extension",
" if(ext.type[0] === 0x00 && ext.type[1] === 0x00) {",
" // get server name list",
" var snl = readVector(ext.data, 2);",
" while(snl.length() > 0) {",
" // read server name type",
" var snType = snl.getByte();",
"",
" // only HostName type (0x00) is known, break out if",
" // another type is detected",
" if(snType !== 0x00) {",
" break;",
" }",
"",
" // add host name to server name list",
" c.session.extensions.server_name.serverNameList.push(",
" readVector(snl, 2).getBytes());",
" }",
" }",
" }",
" }",
" }",
"",
" // version already set, do not allow version change",
" if(c.session.version) {",
" if(msg.version.major !== c.session.version.major ||",
" msg.version.minor !== c.session.version.minor) {",
" return c.error(c, {",
" message: 'TLS version change is disallowed during renegotiation.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.protocol_version",
" }",
" });",
" }",
" }",
"",
" // get the chosen (ServerHello) cipher suite",
" if(client) {",
" // FIXME: should be checking configured acceptable cipher suites",
" c.session.cipherSuite = tls.getCipherSuite(msg.cipher_suite);",
" } else {",
" // get a supported preferred (ClientHello) cipher suite",
" // choose the first supported cipher suite",
" var tmp = forge.util.createBuffer(msg.cipher_suites.bytes());",
" while(tmp.length() > 0) {",
" // FIXME: should be checking configured acceptable suites",
" // cipher suites take up 2 bytes",
" c.session.cipherSuite = tls.getCipherSuite(tmp.getBytes(2));",
" if(c.session.cipherSuite !== null) {",
" break;",
" }",
" }",
" }",
"",
" // cipher suite not supported",
" if(c.session.cipherSuite === null) {",
" return c.error(c, {",
" message: 'No cipher suites in common.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.handshake_failure",
" },",
" cipherSuite: forge.util.bytesToHex(msg.cipher_suite)",
" });",
" }",
"",
" // TODO: handle compression methods",
" if(client) {",
" c.session.compressionMethod = msg.compression_method;",
" } else {",
" // no compression",
" c.session.compressionMethod = tls.CompressionMethod.none;",
" }",
" }",
"",
" return msg;",
"};",
"",
"/**",
" * Creates security parameters for the given connection based on the given",
" * hello message.",
" *",
" * @param c the TLS connection.",
" * @param msg the hello message.",
" */",
"tls.createSecurityParameters = function(c, msg) {",
" /* Note: security params are from TLS 1.2, some values like prf_algorithm",
" are ignored for TLS 1.0/1.1 and the builtin as specified in the spec is",
" used. */",
"",
" // TODO: handle other options from server when more supported",
"",
" // get client and server randoms",
" var client = (c.entity === tls.ConnectionEnd.client);",
" var msgRandom = msg.random.bytes();",
" var cRandom = client ? c.session.sp.client_random : msgRandom;",
" var sRandom = client ? msgRandom : tls.createRandom().getBytes();",
"",
" // create new security parameters",
" c.session.sp = {",
" entity: c.entity,",
" prf_algorithm: tls.PRFAlgorithm.tls_prf_sha256,",
" bulk_cipher_algorithm: null,",
" cipher_type: null,",
" enc_key_length: null,",
" block_length: null,",
" fixed_iv_length: null,",
" record_iv_length: null,",
" mac_algorithm: null,",
" mac_length: null,",
" mac_key_length: null,",
" compression_algorithm: c.session.compressionMethod,",
" pre_master_secret: null,",
" master_secret: null,",
" client_random: cRandom,",
" server_random: sRandom",
" };",
"};",
"",
"/**",
" * Called when a client receives a ServerHello record.",
" *",
" * When a ServerHello message will be sent:",
" * The server will send this message in response to a client hello message",
" * when it was able to find an acceptable set of algorithms. If it cannot",
" * find such a match, it will respond with a handshake failure alert.",
" *",
" * uint24 length;",
" * struct {",
" * ProtocolVersion server_version;",
" * Random random;",
" * SessionID session_id;",
" * CipherSuite cipher_suite;",
" * CompressionMethod compression_method;",
" * select(extensions_present) {",
" * case false:",
" * struct {};",
" * case true:",
" * Extension extensions<0..2^16-1>;",
" * };",
" * } ServerHello;",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleServerHello = function(c, record, length) {",
" var msg = tls.parseHelloMessage(c, record, length);",
" if(c.fail) {",
" return;",
" }",
"",
" // ensure server version is compatible",
" if(msg.version.minor <= c.version.minor) {",
" c.version.minor = msg.version.minor;",
" } else {",
" return c.error(c, {",
" message: 'Incompatible TLS version.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.protocol_version",
" }",
" });",
" }",
"",
" // indicate session version has been set",
" c.session.version = c.version;",
"",
" // get the session ID from the message",
" var sessionId = msg.session_id.bytes();",
"",
" // if the session ID is not blank and matches the cached one, resume",
" // the session",
" if(sessionId.length > 0 && sessionId === c.session.id) {",
" // resuming session, expect a ChangeCipherSpec next",
" c.expect = SCC;",
" c.session.resuming = true;",
"",
" // get new server random",
" c.session.sp.server_random = msg.random.bytes();",
" } else {",
" // not resuming, expect a server Certificate message next",
" c.expect = SCE;",
" c.session.resuming = false;",
"",
" // create new security parameters",
" tls.createSecurityParameters(c, msg);",
" }",
"",
" // set new session ID",
" c.session.id = sessionId;",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a server receives a ClientHello record.",
" *",
" * When a ClientHello message will be sent:",
" * When a client first connects to a server it is required to send the",
" * client hello as its first message. The client can also send a client",
" * hello in response to a hello request or on its own initiative in order",
" * to renegotiate the security parameters in an existing connection.",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleClientHello = function(c, record, length) {",
" var msg = tls.parseHelloMessage(c, record, length);",
" if(c.fail) {",
" return;",
" }",
"",
" // get the session ID from the message",
" var sessionId = msg.session_id.bytes();",
"",
" // see if the given session ID is in the cache",
" var session = null;",
" if(c.sessionCache) {",
" session = c.sessionCache.getSession(sessionId);",
" if(session === null) {",
" // session ID not found",
" sessionId = '';",
" } else if(session.version.major !== msg.version.major ||",
" session.version.minor > msg.version.minor) {",
" // if session version is incompatible with client version, do not resume",
" session = null;",
" sessionId = '';",
" }",
" }",
"",
" // no session found to resume, generate a new session ID",
" if(sessionId.length === 0) {",
" sessionId = forge.random.getBytes(32);",
" }",
"",
" // update session",
" c.session.id = sessionId;",
" c.session.clientHelloVersion = msg.version;",
" c.session.sp = {};",
" if(session) {",
" // use version and security parameters from resumed session",
" c.version = c.session.version = session.version;",
" c.session.sp = session.sp;",
" } else {",
" // use highest compatible minor version",
" var version;",
" for(var i = 1; i < tls.SupportedVersions.length; ++i) {",
" version = tls.SupportedVersions[i];",
" if(version.minor <= msg.version.minor) {",
" break;",
" }",
" }",
" c.version = {major: version.major, minor: version.minor};",
" c.session.version = c.version;",
" }",
"",
" // if a session is set, resume it",
" if(session !== null) {",
" // resuming session, expect a ChangeCipherSpec next",
" c.expect = CCC;",
" c.session.resuming = true;",
"",
" // get new client random",
" c.session.sp.client_random = msg.random.bytes();",
" } else {",
" // not resuming, expect a Certificate or ClientKeyExchange",
" c.expect = (c.verifyClient !== false) ? CCE : CKE;",
" c.session.resuming = false;",
"",
" // create new security parameters",
" tls.createSecurityParameters(c, msg);",
" }",
"",
" // connection now open",
" c.open = true;",
"",
" // queue server hello",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createServerHello(c)",
" }));",
"",
" if(c.session.resuming) {",
" // queue change cipher spec message",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.change_cipher_spec,",
" data: tls.createChangeCipherSpec()",
" }));",
"",
" // create pending state",
" c.state.pending = tls.createConnectionState(c);",
"",
" // change current write state to pending write state",
" c.state.current.write = c.state.pending.write;",
"",
" // queue finished",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createFinished(c)",
" }));",
" } else {",
" // queue server certificate",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createCertificate(c)",
" }));",
"",
" if(!c.fail) {",
" // queue server key exchange",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createServerKeyExchange(c)",
" }));",
"",
" // request client certificate if set",
" if(c.verifyClient !== false) {",
" // queue certificate request",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createCertificateRequest(c)",
" }));",
" }",
"",
" // queue server hello done",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createServerHelloDone(c)",
" }));",
" }",
" }",
"",
" // send records",
" tls.flush(c);",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a client receives a Certificate record.",
" *",
" * When this message will be sent:",
" * The server must send a certificate whenever the agreed-upon key exchange",
" * method is not an anonymous one. This message will always immediately",
" * follow the server hello message.",
" *",
" * Meaning of this message:",
" * The certificate type must be appropriate for the selected cipher suite's",
" * key exchange algorithm, and is generally an X.509v3 certificate. It must",
" * contain a key which matches the key exchange method, as follows. Unless",
" * otherwise specified, the signing algorithm for the certificate must be",
" * the same as the algorithm for the certificate key. Unless otherwise",
" * specified, the public key may be of any length.",
" *",
" * opaque ASN.1Cert<1..2^24-1>;",
" * struct {",
" * ASN.1Cert certificate_list<1..2^24-1>;",
" * } Certificate;",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleCertificate = function(c, record, length) {",
" // minimum of 3 bytes in message",
" if(length < 3) {",
" return c.error(c, {",
" message: 'Invalid Certificate message. Message too short.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.illegal_parameter",
" }",
" });",
" }",
"",
" var b = record.fragment;",
" var msg = {",
" certificate_list: readVector(b, 3)",
" };",
"",
" /* The sender's certificate will be first in the list (chain), each",
" subsequent one that follows will certify the previous one, but root",
" certificates (self-signed) that specify the certificate authority may",
" be omitted under the assumption that clients must already possess it. */",
" var cert, asn1;",
" var certs = [];",
" try {",
" while(msg.certificate_list.length() > 0) {",
" // each entry in msg.certificate_list is a vector with 3 len bytes",
" cert = readVector(msg.certificate_list, 3);",
" asn1 = forge.asn1.fromDer(cert);",
" cert = forge.pki.certificateFromAsn1(asn1, true);",
" certs.push(cert);",
" }",
" } catch(ex) {",
" return c.error(c, {",
" message: 'Could not parse certificate list.',",
" cause: ex,",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.bad_certificate",
" }",
" });",
" }",
"",
" // ensure at least 1 certificate was provided if in client-mode",
" // or if verifyClient was set to true to require a certificate",
" // (as opposed to 'optional')",
" var client = (c.entity === tls.ConnectionEnd.client);",
" if((client || c.verifyClient === true) && certs.length === 0) {",
" // error, no certificate",
" c.error(c, {",
" message: client ?",
" 'No server certificate provided.' :",
" 'No client certificate provided.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.illegal_parameter",
" }",
" });",
" } else if(certs.length === 0) {",
" // no certs to verify",
" // expect a ServerKeyExchange or ClientKeyExchange message next",
" c.expect = client ? SKE : CKE;",
" } else {",
" // save certificate in session",
" if(client) {",
" c.session.serverCertificate = certs[0];",
" } else {",
" c.session.clientCertificate = certs[0];",
" }",
"",
" if(tls.verifyCertificateChain(c, certs)) {",
" // expect a ServerKeyExchange or ClientKeyExchange message next",
" c.expect = client ? SKE : CKE;",
" }",
" }",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a client receives a ServerKeyExchange record.",
" *",
" * When this message will be sent:",
" * This message will be sent immediately after the server certificate",
" * message (or the server hello message, if this is an anonymous",
" * negotiation).",
" *",
" * The server key exchange message is sent by the server only when the",
" * server certificate message (if sent) does not contain enough data to",
" * allow the client to exchange a premaster secret.",
" *",
" * Meaning of this message:",
" * This message conveys cryptographic information to allow the client to",
" * communicate the premaster secret: either an RSA public key to encrypt",
" * the premaster secret with, or a Diffie-Hellman public key with which the",
" * client can complete a key exchange (with the result being the premaster",
" * secret.)",
" *",
" * enum {",
" * dhe_dss, dhe_rsa, dh_anon, rsa, dh_dss, dh_rsa",
" * } KeyExchangeAlgorithm;",
" *",
" * struct {",
" * opaque dh_p<1..2^16-1>;",
" * opaque dh_g<1..2^16-1>;",
" * opaque dh_Ys<1..2^16-1>;",
" * } ServerDHParams;",
" *",
" * struct {",
" * select(KeyExchangeAlgorithm) {",
" * case dh_anon:",
" * ServerDHParams params;",
" * case dhe_dss:",
" * case dhe_rsa:",
" * ServerDHParams params;",
" * digitally-signed struct {",
" * opaque client_random[32];",
" * opaque server_random[32];",
" * ServerDHParams params;",
" * } signed_params;",
" * case rsa:",
" * case dh_dss:",
" * case dh_rsa:",
" * struct {};",
" * };",
" * } ServerKeyExchange;",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleServerKeyExchange = function(c, record, length) {",
" // this implementation only supports RSA, no Diffie-Hellman support",
" // so any length > 0 is invalid",
" if(length > 0) {",
" return c.error(c, {",
" message: 'Invalid key parameters. Only RSA is supported.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.unsupported_certificate",
" }",
" });",
" }",
"",
" // expect an optional CertificateRequest message next",
" c.expect = SCR;",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a client receives a ClientKeyExchange record.",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleClientKeyExchange = function(c, record, length) {",
" // this implementation only supports RSA, no Diffie-Hellman support",
" // so any length < 48 is invalid",
" if(length < 48) {",
" return c.error(c, {",
" message: 'Invalid key parameters. Only RSA is supported.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.unsupported_certificate",
" }",
" });",
" }",
"",
" var b = record.fragment;",
" var msg = {",
" enc_pre_master_secret: readVector(b, 2).getBytes()",
" };",
"",
" // do rsa decryption",
" var privateKey = null;",
" if(c.getPrivateKey) {",
" try {",
" privateKey = c.getPrivateKey(c, c.session.serverCertificate);",
" privateKey = forge.pki.privateKeyFromPem(privateKey);",
" } catch(ex) {",
" c.error(c, {",
" message: 'Could not get private key.',",
" cause: ex,",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.internal_error",
" }",
" });",
" }",
" }",
"",
" if(privateKey === null) {",
" return c.error(c, {",
" message: 'No private key set.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.internal_error",
" }",
" });",
" }",
"",
" try {",
" // decrypt 48-byte pre-master secret",
" var sp = c.session.sp;",
" sp.pre_master_secret = privateKey.decrypt(msg.enc_pre_master_secret);",
"",
" // ensure client hello version matches first 2 bytes",
" var version = c.session.clientHelloVersion;",
" if(version.major !== sp.pre_master_secret.charCodeAt(0) ||",
" version.minor !== sp.pre_master_secret.charCodeAt(1)) {",
" // error, do not send alert (see BLEI attack below)",
" throw new Error('TLS version rollback attack detected.');",
" }",
" } catch(ex) {",
" /* Note: Daniel Bleichenbacher [BLEI] can be used to attack a",
" TLS server which is using PKCS#1 encoded RSA, so instead of",
" failing here, we generate 48 random bytes and use that as",
" the pre-master secret. */",
" sp.pre_master_secret = forge.random.getBytes(48);",
" }",
"",
" // expect a CertificateVerify message if a Certificate was received that",
" // does not have fixed Diffie-Hellman params, otherwise expect",
" // ChangeCipherSpec",
" c.expect = CCC;",
" if(c.session.clientCertificate !== null) {",
" // only RSA support, so expect CertificateVerify",
" // TODO: support Diffie-Hellman",
" c.expect = CCV;",
" }",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a client receives a CertificateRequest record.",
" *",
" * When this message will be sent:",
" * A non-anonymous server can optionally request a certificate from the",
" * client, if appropriate for the selected cipher suite. This message, if",
" * sent, will immediately follow the Server Key Exchange message (if it is",
" * sent; otherwise, the Server Certificate message).",
" *",
" * enum {",
" * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),",
" * rsa_ephemeral_dh_RESERVED(5), dss_ephemeral_dh_RESERVED(6),",
" * fortezza_dms_RESERVED(20), (255)",
" * } ClientCertificateType;",
" *",
" * opaque DistinguishedName<1..2^16-1>;",
" *",
" * struct {",
" * ClientCertificateType certificate_types<1..2^8-1>;",
" * SignatureAndHashAlgorithm supported_signature_algorithms<2^16-1>;",
" * DistinguishedName certificate_authorities<0..2^16-1>;",
" * } CertificateRequest;",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleCertificateRequest = function(c, record, length) {",
" // minimum of 3 bytes in message",
" if(length < 3) {",
" return c.error(c, {",
" message: 'Invalid CertificateRequest. Message too short.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.illegal_parameter",
" }",
" });",
" }",
"",
" // TODO: TLS 1.2+ has different format including",
" // SignatureAndHashAlgorithm after cert types",
" var b = record.fragment;",
" var msg = {",
" certificate_types: readVector(b, 1),",
" certificate_authorities: readVector(b, 2)",
" };",
"",
" // save certificate request in session",
" c.session.certificateRequest = msg;",
"",
" // expect a ServerHelloDone message next",
" c.expect = SHD;",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a server receives a CertificateVerify record.",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleCertificateVerify = function(c, record, length) {",
" if(length < 2) {",
" return c.error(c, {",
" message: 'Invalid CertificateVerify. Message too short.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.illegal_parameter",
" }",
" });",
" }",
"",
" // rewind to get full bytes for message so it can be manually",
" // digested below (special case for CertificateVerify messages because",
" // they must be digested *after* handling as opposed to all others)",
" var b = record.fragment;",
" b.read -= 4;",
" var msgBytes = b.bytes();",
" b.read += 4;",
"",
" var msg = {",
" signature: readVector(b, 2).getBytes()",
" };",
"",
" // TODO: add support for DSA",
"",
" // generate data to verify",
" var verify = forge.util.createBuffer();",
" verify.putBuffer(c.session.md5.digest());",
" verify.putBuffer(c.session.sha1.digest());",
" verify = verify.getBytes();",
"",
" try {",
" var cert = c.session.clientCertificate;",
" /*b = forge.pki.rsa.decrypt(",
" msg.signature, cert.publicKey, true, verify.length);",
" if(b !== verify) {*/",
" if(!cert.publicKey.verify(verify, msg.signature, 'NONE')) {",
" throw new Error('CertificateVerify signature does not match.');",
" }",
"",
" // digest message now that it has been handled",
" c.session.md5.update(msgBytes);",
" c.session.sha1.update(msgBytes);",
" } catch(ex) {",
" return c.error(c, {",
" message: 'Bad signature in CertificateVerify.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.handshake_failure",
" }",
" });",
" }",
"",
" // expect ChangeCipherSpec",
" c.expect = CCC;",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a client receives a ServerHelloDone record.",
" *",
" * When this message will be sent:",
" * The server hello done message is sent by the server to indicate the end",
" * of the server hello and associated messages. After sending this message",
" * the server will wait for a client response.",
" *",
" * Meaning of this message:",
" * This message means that the server is done sending messages to support",
" * the key exchange, and the client can proceed with its phase of the key",
" * exchange.",
" *",
" * Upon receipt of the server hello done message the client should verify",
" * that the server provided a valid certificate if required and check that",
" * the server hello parameters are acceptable.",
" *",
" * struct {} ServerHelloDone;",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleServerHelloDone = function(c, record, length) {",
" // len must be 0 bytes",
" if(length > 0) {",
" return c.error(c, {",
" message: 'Invalid ServerHelloDone message. Invalid length.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.record_overflow",
" }",
" });",
" }",
"",
" if(c.serverCertificate === null) {",
" // no server certificate was provided",
" var error = {",
" message: 'No server certificate provided. Not enough security.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.insufficient_security",
" }",
" };",
"",
" // call application callback",
" var depth = 0;",
" var ret = c.verify(c, error.alert.description, depth, []);",
" if(ret !== true) {",
" // check for custom alert info",
" if(ret || ret === 0) {",
" // set custom message and alert description",
" if(typeof ret === 'object' && !forge.util.isArray(ret)) {",
" if(ret.message) {",
" error.message = ret.message;",
" }",
" if(ret.alert) {",
" error.alert.description = ret.alert;",
" }",
" } else if(typeof ret === 'number') {",
" // set custom alert description",
" error.alert.description = ret;",
" }",
" }",
"",
" // send error",
" return c.error(c, error);",
" }",
" }",
"",
" // create client certificate message if requested",
" if(c.session.certificateRequest !== null) {",
" record = tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createCertificate(c)",
" });",
" tls.queue(c, record);",
" }",
"",
" // create client key exchange message",
" record = tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createClientKeyExchange(c)",
" });",
" tls.queue(c, record);",
"",
" // expect no messages until the following callback has been called",
" c.expect = SER;",
"",
" // create callback to handle client signature (for client-certs)",
" var callback = function(c, signature) {",
" if(c.session.certificateRequest !== null &&",
" c.session.clientCertificate !== null) {",
" // create certificate verify message",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createCertificateVerify(c, signature)",
" }));",
" }",
"",
" // create change cipher spec message",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.change_cipher_spec,",
" data: tls.createChangeCipherSpec()",
" }));",
"",
" // create pending state",
" c.state.pending = tls.createConnectionState(c);",
"",
" // change current write state to pending write state",
" c.state.current.write = c.state.pending.write;",
"",
" // create finished message",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createFinished(c)",
" }));",
"",
" // expect a server ChangeCipherSpec message next",
" c.expect = SCC;",
"",
" // send records",
" tls.flush(c);",
"",
" // continue",
" c.process();",
" };",
"",
" // if there is no certificate request or no client certificate, do",
" // callback immediately",
" if(c.session.certificateRequest === null ||",
" c.session.clientCertificate === null) {",
" return callback(c, null);",
" }",
"",
" // otherwise get the client signature",
" tls.getClientSignature(c, callback);",
"};",
"",
"/**",
" * Called when a ChangeCipherSpec record is received.",
" *",
" * @param c the connection.",
" * @param record the record.",
" */",
"tls.handleChangeCipherSpec = function(c, record) {",
" if(record.fragment.getByte() !== 0x01) {",
" return c.error(c, {",
" message: 'Invalid ChangeCipherSpec message received.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.illegal_parameter",
" }",
" });",
" }",
"",
" // create pending state if:",
" // 1. Resuming session in client mode OR",
" // 2. NOT resuming session in server mode",
" var client = (c.entity === tls.ConnectionEnd.client);",
" if((c.session.resuming && client) || (!c.session.resuming && !client)) {",
" c.state.pending = tls.createConnectionState(c);",
" }",
"",
" // change current read state to pending read state",
" c.state.current.read = c.state.pending.read;",
"",
" // clear pending state if:",
" // 1. NOT resuming session in client mode OR",
" // 2. resuming a session in server mode",
" if((!c.session.resuming && client) || (c.session.resuming && !client)) {",
" c.state.pending = null;",
" }",
"",
" // expect a Finished record next",
" c.expect = client ? SFI : CFI;",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a Finished record is received.",
" *",
" * When this message will be sent:",
" * A finished message is always sent immediately after a change",
" * cipher spec message to verify that the key exchange and",
" * authentication processes were successful. It is essential that a",
" * change cipher spec message be received between the other",
" * handshake messages and the Finished message.",
" *",
" * Meaning of this message:",
" * The finished message is the first protected with the just-",
" * negotiated algorithms, keys, and secrets. Recipients of finished",
" * messages must verify that the contents are correct. Once a side",
" * has sent its Finished message and received and validated the",
" * Finished message from its peer, it may begin to send and receive",
" * application data over the connection.",
" *",
" * struct {",
" * opaque verify_data[verify_data_length];",
" * } Finished;",
" *",
" * verify_data",
" * PRF(master_secret, finished_label, Hash(handshake_messages))",
" * [0..verify_data_length-1];",
" *",
" * finished_label",
" * For Finished messages sent by the client, the string",
" * \"client finished\". For Finished messages sent by the server, the",
" * string \"server finished\".",
" *",
" * verify_data_length depends on the cipher suite. If it is not specified",
" * by the cipher suite, then it is 12. Versions of TLS < 1.2 always used",
" * 12 bytes.",
" *",
" * @param c the connection.",
" * @param record the record.",
" * @param length the length of the handshake message.",
" */",
"tls.handleFinished = function(c, record, length) {",
" // rewind to get full bytes for message so it can be manually",
" // digested below (special case for Finished messages because they",
" // must be digested *after* handling as opposed to all others)",
" var b = record.fragment;",
" b.read -= 4;",
" var msgBytes = b.bytes();",
" b.read += 4;",
"",
" // message contains only verify_data",
" var vd = record.fragment.getBytes();",
"",
" // ensure verify data is correct",
" b = forge.util.createBuffer();",
" b.putBuffer(c.session.md5.digest());",
" b.putBuffer(c.session.sha1.digest());",
"",
" // set label based on entity type",
" var client = (c.entity === tls.ConnectionEnd.client);",
" var label = client ? 'server finished' : 'client finished';",
"",
" // TODO: determine prf function and verify length for TLS 1.2",
" var sp = c.session.sp;",
" var vdl = 12;",
" var prf = prf_TLS1;",
" b = prf(sp.master_secret, label, b.getBytes(), vdl);",
" if(b.getBytes() !== vd) {",
" return c.error(c, {",
" message: 'Invalid verify_data in Finished message.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.decrypt_error",
" }",
" });",
" }",
"",
" // digest finished message now that it has been handled",
" c.session.md5.update(msgBytes);",
" c.session.sha1.update(msgBytes);",
"",
" // resuming session as client or NOT resuming session as server",
" if((c.session.resuming && client) || (!c.session.resuming && !client)) {",
" // create change cipher spec message",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.change_cipher_spec,",
" data: tls.createChangeCipherSpec()",
" }));",
"",
" // change current write state to pending write state, clear pending",
" c.state.current.write = c.state.pending.write;",
" c.state.pending = null;",
"",
" // create finished message",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createFinished(c)",
" }));",
" }",
"",
" // expect application data next",
" c.expect = client ? SAD : CAD;",
"",
" // handshake complete",
" c.handshaking = false;",
" ++c.handshakes;",
"",
" // save access to peer certificate",
" c.peerCertificate = client ?",
" c.session.serverCertificate : c.session.clientCertificate;",
"",
" // send records",
" tls.flush(c);",
"",
" // now connected",
" c.isConnected = true;",
" c.connected(c);",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when an Alert record is received.",
" *",
" * @param c the connection.",
" * @param record the record.",
" */",
"tls.handleAlert = function(c, record) {",
" // read alert",
" var b = record.fragment;",
" var alert = {",
" level: b.getByte(),",
" description: b.getByte()",
" };",
"",
" // TODO: consider using a table?",
" // get appropriate message",
" var msg;",
" switch(alert.description) {",
" case tls.Alert.Description.close_notify:",
" msg = 'Connection closed.';",
" break;",
" case tls.Alert.Description.unexpected_message:",
" msg = 'Unexpected message.';",
" break;",
" case tls.Alert.Description.bad_record_mac:",
" msg = 'Bad record MAC.';",
" break;",
" case tls.Alert.Description.decryption_failed:",
" msg = 'Decryption failed.';",
" break;",
" case tls.Alert.Description.record_overflow:",
" msg = 'Record overflow.';",
" break;",
" case tls.Alert.Description.decompression_failure:",
" msg = 'Decompression failed.';",
" break;",
" case tls.Alert.Description.handshake_failure:",
" msg = 'Handshake failure.';",
" break;",
" case tls.Alert.Description.bad_certificate:",
" msg = 'Bad certificate.';",
" break;",
" case tls.Alert.Description.unsupported_certificate:",
" msg = 'Unsupported certificate.';",
" break;",
" case tls.Alert.Description.certificate_revoked:",
" msg = 'Certificate revoked.';",
" break;",
" case tls.Alert.Description.certificate_expired:",
" msg = 'Certificate expired.';",
" break;",
" case tls.Alert.Description.certificate_unknown:",
" msg = 'Certificate unknown.';",
" break;",
" case tls.Alert.Description.illegal_parameter:",
" msg = 'Illegal parameter.';",
" break;",
" case tls.Alert.Description.unknown_ca:",
" msg = 'Unknown certificate authority.';",
" break;",
" case tls.Alert.Description.access_denied:",
" msg = 'Access denied.';",
" break;",
" case tls.Alert.Description.decode_error:",
" msg = 'Decode error.';",
" break;",
" case tls.Alert.Description.decrypt_error:",
" msg = 'Decrypt error.';",
" break;",
" case tls.Alert.Description.export_restriction:",
" msg = 'Export restriction.';",
" break;",
" case tls.Alert.Description.protocol_version:",
" msg = 'Unsupported protocol version.';",
" break;",
" case tls.Alert.Description.insufficient_security:",
" msg = 'Insufficient security.';",
" break;",
" case tls.Alert.Description.internal_error:",
" msg = 'Internal error.';",
" break;",
" case tls.Alert.Description.user_canceled:",
" msg = 'User canceled.';",
" break;",
" case tls.Alert.Description.no_renegotiation:",
" msg = 'Renegotiation not supported.';",
" break;",
" default:",
" msg = 'Unknown error.';",
" break;",
" }",
"",
" // close connection on close_notify, not an error",
" if(alert.description === tls.Alert.Description.close_notify) {",
" return c.close();",
" }",
"",
" // call error handler",
" c.error(c, {",
" message: msg,",
" send: false,",
" // origin is the opposite end",
" origin: (c.entity === tls.ConnectionEnd.client) ? 'server' : 'client',",
" alert: alert",
" });",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a Handshake record is received.",
" *",
" * @param c the connection.",
" * @param record the record.",
" */",
"tls.handleHandshake = function(c, record) {",
" // get the handshake type and message length",
" var b = record.fragment;",
" var type = b.getByte();",
" var length = b.getInt24();",
"",
" // see if the record fragment doesn't yet contain the full message",
" if(length > b.length()) {",
" // cache the record, clear its fragment, and reset the buffer read",
" // pointer before the type and length were read",
" c.fragmented = record;",
" record.fragment = forge.util.createBuffer();",
" b.read -= 4;",
"",
" // continue",
" return c.process();",
" }",
"",
" // full message now available, clear cache, reset read pointer to",
" // before type and length",
" c.fragmented = null;",
" b.read -= 4;",
"",
" // save the handshake bytes for digestion after handler is found",
" // (include type and length of handshake msg)",
" var bytes = b.bytes(length + 4);",
"",
" // restore read pointer",
" b.read += 4;",
"",
" // handle expected message",
" if(type in hsTable[c.entity][c.expect]) {",
" // initialize server session",
" if(c.entity === tls.ConnectionEnd.server && !c.open && !c.fail) {",
" c.handshaking = true;",
" c.session = {",
" version: null,",
" extensions: {",
" server_name: {",
" serverNameList: []",
" }",
" },",
" cipherSuite: null,",
" compressionMethod: null,",
" serverCertificate: null,",
" clientCertificate: null,",
" md5: forge.md.md5.create(),",
" sha1: forge.md.sha1.create()",
" };",
" }",
"",
" /* Update handshake messages digest. Finished and CertificateVerify",
" messages are not digested here. They can't be digested as part of",
" the verify_data that they contain. These messages are manually",
" digested in their handlers. HelloRequest messages are simply never",
" included in the handshake message digest according to spec. */",
" if(type !== tls.HandshakeType.hello_request &&",
" type !== tls.HandshakeType.certificate_verify &&",
" type !== tls.HandshakeType.finished) {",
" c.session.md5.update(bytes);",
" c.session.sha1.update(bytes);",
" }",
"",
" // handle specific handshake type record",
" hsTable[c.entity][c.expect][type](c, record, length);",
" } else {",
" // unexpected record",
" tls.handleUnexpected(c, record);",
" }",
"};",
"",
"/**",
" * Called when an ApplicationData record is received.",
" *",
" * @param c the connection.",
" * @param record the record.",
" */",
"tls.handleApplicationData = function(c, record) {",
" // buffer data, notify that its ready",
" c.data.putBuffer(record.fragment);",
" c.dataReady(c);",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * Called when a Heartbeat record is received.",
" *",
" * @param c the connection.",
" * @param record the record.",
" */",
"tls.handleHeartbeat = function(c, record) {",
" // get the heartbeat type and payload",
" var b = record.fragment;",
" var type = b.getByte();",
" var length = b.getInt16();",
" var payload = b.getBytes(length);",
"",
" if(type === tls.HeartbeatMessageType.heartbeat_request) {",
" // discard request during handshake or if length is too large",
" if(c.handshaking || length > payload.length) {",
" // continue",
" return c.process();",
" }",
" // retransmit payload",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.heartbeat,",
" data: tls.createHeartbeat(",
" tls.HeartbeatMessageType.heartbeat_response, payload)",
" }));",
" tls.flush(c);",
" } else if(type === tls.HeartbeatMessageType.heartbeat_response) {",
" // check payload against expected payload, discard heartbeat if no match",
" if(payload !== c.expectedHeartbeatPayload) {",
" // continue",
" return c.process();",
" }",
"",
" // notify that a valid heartbeat was received",
" if(c.heartbeatReceived) {",
" c.heartbeatReceived(c, forge.util.createBuffer(payload));",
" }",
" }",
"",
" // continue",
" c.process();",
"};",
"",
"/**",
" * The transistional state tables for receiving TLS records. It maps the",
" * current TLS engine state and a received record to a function to handle the",
" * record and update the state.",
" *",
" * For instance, if the current state is SHE, then the TLS engine is expecting",
" * a ServerHello record. Once a record is received, the handler function is",
" * looked up using the state SHE and the record's content type.",
" *",
" * The resulting function will either be an error handler or a record handler.",
" * The function will take whatever action is appropriate and update the state",
" * for the next record.",
" *",
" * The states are all based on possible server record types. Note that the",
" * client will never specifically expect to receive a HelloRequest or an alert",
" * from the server so there is no state that reflects this. These messages may",
" * occur at any time.",
" *",
" * There are two tables for mapping states because there is a second tier of",
" * types for handshake messages. Once a record with a content type of handshake",
" * is received, the handshake record handler will look up the handshake type in",
" * the secondary map to get its appropriate handler.",
" *",
" * Valid message orders are as follows:",
" *",
" * =======================FULL HANDSHAKE======================",
" * Client Server",
" *",
" * ClientHello -------->",
" * ServerHello",
" * Certificate*",
" * ServerKeyExchange*",
" * CertificateRequest*",
" * <-------- ServerHelloDone",
" * Certificate*",
" * ClientKeyExchange",
" * CertificateVerify*",
" * [ChangeCipherSpec]",
" * Finished -------->",
" * [ChangeCipherSpec]",
" * <-------- Finished",
" * Application Data <-------> Application Data",
" *",
" * =====================SESSION RESUMPTION=====================",
" * Client Server",
" *",
" * ClientHello -------->",
" * ServerHello",
" * [ChangeCipherSpec]",
" * <-------- Finished",
" * [ChangeCipherSpec]",
" * Finished -------->",
" * Application Data <-------> Application Data",
" */",
"// client expect states (indicate which records are expected to be received)",
"var SHE = 0; // rcv server hello",
"var SCE = 1; // rcv server certificate",
"var SKE = 2; // rcv server key exchange",
"var SCR = 3; // rcv certificate request",
"var SHD = 4; // rcv server hello done",
"var SCC = 5; // rcv change cipher spec",
"var SFI = 6; // rcv finished",
"var SAD = 7; // rcv application data",
"var SER = 8; // not expecting any messages at this point",
"",
"// server expect states",
"var CHE = 0; // rcv client hello",
"var CCE = 1; // rcv client certificate",
"var CKE = 2; // rcv client key exchange",
"var CCV = 3; // rcv certificate verify",
"var CCC = 4; // rcv change cipher spec",
"var CFI = 5; // rcv finished",
"var CAD = 6; // rcv application data",
"var CER = 7; // not expecting any messages at this point",
"",
"// map client current expect state and content type to function",
"var __ = tls.handleUnexpected;",
"var R0 = tls.handleChangeCipherSpec;",
"var R1 = tls.handleAlert;",
"var R2 = tls.handleHandshake;",
"var R3 = tls.handleApplicationData;",
"var R4 = tls.handleHeartbeat;",
"var ctTable = [];",
"ctTable[tls.ConnectionEnd.client] = [",
"// CC,AL,HS,AD,HB",
"/*SHE*/[__,R1,R2,__,R4],",
"/*SCE*/[__,R1,R2,__,R4],",
"/*SKE*/[__,R1,R2,__,R4],",
"/*SCR*/[__,R1,R2,__,R4],",
"/*SHD*/[__,R1,R2,__,R4],",
"/*SCC*/[R0,R1,__,__,R4],",
"/*SFI*/[__,R1,R2,__,R4],",
"/*SAD*/[__,R1,R2,R3,R4],",
"/*SER*/[__,R1,R2,__,R4]",
"];",
"",
"// map server current expect state and content type to function",
"ctTable[tls.ConnectionEnd.server] = [",
"// CC,AL,HS,AD",
"/*CHE*/[__,R1,R2,__,R4],",
"/*CCE*/[__,R1,R2,__,R4],",
"/*CKE*/[__,R1,R2,__,R4],",
"/*CCV*/[__,R1,R2,__,R4],",
"/*CCC*/[R0,R1,__,__,R4],",
"/*CFI*/[__,R1,R2,__,R4],",
"/*CAD*/[__,R1,R2,R3,R4],",
"/*CER*/[__,R1,R2,__,R4]",
"];",
"",
"// map client current expect state and handshake type to function",
"var H0 = tls.handleHelloRequest;",
"var H1 = tls.handleServerHello;",
"var H2 = tls.handleCertificate;",
"var H3 = tls.handleServerKeyExchange;",
"var H4 = tls.handleCertificateRequest;",
"var H5 = tls.handleServerHelloDone;",
"var H6 = tls.handleFinished;",
"var hsTable = [];",
"hsTable[tls.ConnectionEnd.client] = [",
"// HR,01,SH,03,04,05,06,07,08,09,10,SC,SK,CR,HD,15,CK,17,18,19,FI",
"/*SHE*/[__,__,H1,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],",
"/*SCE*/[H0,__,__,__,__,__,__,__,__,__,__,H2,H3,H4,H5,__,__,__,__,__,__],",
"/*SKE*/[H0,__,__,__,__,__,__,__,__,__,__,__,H3,H4,H5,__,__,__,__,__,__],",
"/*SCR*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,H4,H5,__,__,__,__,__,__],",
"/*SHD*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,H5,__,__,__,__,__,__],",
"/*SCC*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],",
"/*SFI*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H6],",
"/*SAD*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],",
"/*SER*/[H0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__]",
"];",
"",
"// map server current expect state and handshake type to function",
"// Note: CAD[CH] does not map to FB because renegotation is prohibited",
"var H7 = tls.handleClientHello;",
"var H8 = tls.handleClientKeyExchange;",
"var H9 = tls.handleCertificateVerify;",
"hsTable[tls.ConnectionEnd.server] = [",
"// 01,CH,02,03,04,05,06,07,08,09,10,CC,12,13,14,CV,CK,17,18,19,FI",
"/*CHE*/[__,H7,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],",
"/*CCE*/[__,__,__,__,__,__,__,__,__,__,__,H2,__,__,__,__,__,__,__,__,__],",
"/*CKE*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H8,__,__,__,__],",
"/*CCV*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H9,__,__,__,__,__],",
"/*CCC*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],",
"/*CFI*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,H6],",
"/*CAD*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__],",
"/*CER*/[__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__]",
"];",
"",
"/**",
" * Generates the master_secret and keys using the given security parameters.",
" *",
" * The security parameters for a TLS connection state are defined as such:",
" *",
" * struct {",
" * ConnectionEnd entity;",
" * PRFAlgorithm prf_algorithm;",
" * BulkCipherAlgorithm bulk_cipher_algorithm;",
" * CipherType cipher_type;",
" * uint8 enc_key_length;",
" * uint8 block_length;",
" * uint8 fixed_iv_length;",
" * uint8 record_iv_length;",
" * MACAlgorithm mac_algorithm;",
" * uint8 mac_length;",
" * uint8 mac_key_length;",
" * CompressionMethod compression_algorithm;",
" * opaque master_secret[48];",
" * opaque client_random[32];",
" * opaque server_random[32];",
" * } SecurityParameters;",
" *",
" * Note that this definition is from TLS 1.2. In TLS 1.0 some of these",
" * parameters are ignored because, for instance, the PRFAlgorithm is a",
" * builtin-fixed algorithm combining iterations of MD5 and SHA-1 in TLS 1.0.",
" *",
" * The Record Protocol requires an algorithm to generate keys required by the",
" * current connection state.",
" *",
" * The master secret is expanded into a sequence of secure bytes, which is then",
" * split to a client write MAC key, a server write MAC key, a client write",
" * encryption key, and a server write encryption key. In TLS 1.0 a client write",
" * IV and server write IV are also generated. Each of these is generated from",
" * the byte sequence in that order. Unused values are empty. In TLS 1.2, some",
" * AEAD ciphers may additionally require a client write IV and a server write",
" * IV (see Section 6.2.3.3).",
" *",
" * When keys, MAC keys, and IVs are generated, the master secret is used as an",
" * entropy source.",
" *",
" * To generate the key material, compute:",
" *",
" * master_secret = PRF(pre_master_secret, \"master secret\",",
" * ClientHello.random + ServerHello.random)",
" *",
" * key_block = PRF(SecurityParameters.master_secret,",
" * \"key expansion\",",
" * SecurityParameters.server_random +",
" * SecurityParameters.client_random);",
" *",
" * until enough output has been generated. Then, the key_block is",
" * partitioned as follows:",
" *",
" * client_write_MAC_key[SecurityParameters.mac_key_length]",
" * server_write_MAC_key[SecurityParameters.mac_key_length]",
" * client_write_key[SecurityParameters.enc_key_length]",
" * server_write_key[SecurityParameters.enc_key_length]",
" * client_write_IV[SecurityParameters.fixed_iv_length]",
" * server_write_IV[SecurityParameters.fixed_iv_length]",
" *",
" * In TLS 1.2, the client_write_IV and server_write_IV are only generated for",
" * implicit nonce techniques as described in Section 3.2.1 of [AEAD]. This",
" * implementation uses TLS 1.0 so IVs are generated.",
" *",
" * Implementation note: The currently defined cipher suite which requires the",
" * most material is AES_256_CBC_SHA256. It requires 2 x 32 byte keys and 2 x 32",
" * byte MAC keys, for a total 128 bytes of key material. In TLS 1.0 it also",
" * requires 2 x 16 byte IVs, so it actually takes 160 bytes of key material.",
" *",
" * @param c the connection.",
" * @param sp the security parameters to use.",
" *",
" * @return the security keys.",
" */",
"tls.generateKeys = function(c, sp) {",
" // TLS_RSA_WITH_AES_128_CBC_SHA (required to be compliant with TLS 1.2) &",
" // TLS_RSA_WITH_AES_256_CBC_SHA are the only cipher suites implemented",
" // at present",
"",
" // TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA is required to be compliant with",
" // TLS 1.0 but we don't care right now because AES is better and we have",
" // an implementation for it",
"",
" // TODO: TLS 1.2 implementation",
" /*",
" // determine the PRF",
" var prf;",
" switch(sp.prf_algorithm) {",
" case tls.PRFAlgorithm.tls_prf_sha256:",
" prf = prf_sha256;",
" break;",
" default:",
" // should never happen",
" throw new Error('Invalid PRF');",
" }",
" */",
"",
" // TLS 1.0/1.1 implementation",
" var prf = prf_TLS1;",
"",
" // concatenate server and client random",
" var random = sp.client_random + sp.server_random;",
"",
" // only create master secret if session is new",
" if(!c.session.resuming) {",
" // create master secret, clean up pre-master secret",
" sp.master_secret = prf(",
" sp.pre_master_secret, 'master secret', random, 48).bytes();",
" sp.pre_master_secret = null;",
" }",
"",
" // generate the amount of key material needed",
" random = sp.server_random + sp.client_random;",
" var length = 2 * sp.mac_key_length + 2 * sp.enc_key_length;",
"",
" // include IV for TLS/1.0",
" var tls10 = (c.version.major === tls.Versions.TLS_1_0.major &&",
" c.version.minor === tls.Versions.TLS_1_0.minor);",
" if(tls10) {",
" length += 2 * sp.fixed_iv_length;",
" }",
" var km = prf(sp.master_secret, 'key expansion', random, length);",
"",
" // split the key material into the MAC and encryption keys",
" var rval = {",
" client_write_MAC_key: km.getBytes(sp.mac_key_length),",
" server_write_MAC_key: km.getBytes(sp.mac_key_length),",
" client_write_key: km.getBytes(sp.enc_key_length),",
" server_write_key: km.getBytes(sp.enc_key_length)",
" };",
"",
" // include TLS 1.0 IVs",
" if(tls10) {",
" rval.client_write_IV = km.getBytes(sp.fixed_iv_length);",
" rval.server_write_IV = km.getBytes(sp.fixed_iv_length);",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Creates a new initialized TLS connection state. A connection state has",
" * a read mode and a write mode.",
" *",
" * compression state:",
" * The current state of the compression algorithm.",
" *",
" * cipher state:",
" * The current state of the encryption algorithm. This will consist of the",
" * scheduled key for that connection. For stream ciphers, this will also",
" * contain whatever state information is necessary to allow the stream to",
" * continue to encrypt or decrypt data.",
" *",
" * MAC key:",
" * The MAC key for the connection.",
" *",
" * sequence number:",
" * Each connection state contains a sequence number, which is maintained",
" * separately for read and write states. The sequence number MUST be set to",
" * zero whenever a connection state is made the active state. Sequence",
" * numbers are of type uint64 and may not exceed 2^64-1. Sequence numbers do",
" * not wrap. If a TLS implementation would need to wrap a sequence number,",
" * it must renegotiate instead. A sequence number is incremented after each",
" * record: specifically, the first record transmitted under a particular",
" * connection state MUST use sequence number 0.",
" *",
" * @param c the connection.",
" *",
" * @return the new initialized TLS connection state.",
" */",
"tls.createConnectionState = function(c) {",
" var client = (c.entity === tls.ConnectionEnd.client);",
"",
" var createMode = function() {",
" var mode = {",
" // two 32-bit numbers, first is most significant",
" sequenceNumber: [0, 0],",
" macKey: null,",
" macLength: 0,",
" macFunction: null,",
" cipherState: null,",
" cipherFunction: function(record) {return true;},",
" compressionState: null,",
" compressFunction: function(record) {return true;},",
" updateSequenceNumber: function() {",
" if(mode.sequenceNumber[1] === 0xFFFFFFFF) {",
" mode.sequenceNumber[1] = 0;",
" ++mode.sequenceNumber[0];",
" } else {",
" ++mode.sequenceNumber[1];",
" }",
" }",
" };",
" return mode;",
" };",
" var state = {",
" read: createMode(),",
" write: createMode()",
" };",
"",
" // update function in read mode will decrypt then decompress a record",
" state.read.update = function(c, record) {",
" if(!state.read.cipherFunction(record, state.read)) {",
" c.error(c, {",
" message: 'Could not decrypt record or bad MAC.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" // doesn't matter if decryption failed or MAC was",
" // invalid, return the same error so as not to reveal",
" // which one occurred",
" description: tls.Alert.Description.bad_record_mac",
" }",
" });",
" } else if(!state.read.compressFunction(c, record, state.read)) {",
" c.error(c, {",
" message: 'Could not decompress record.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.decompression_failure",
" }",
" });",
" }",
" return !c.fail;",
" };",
"",
" // update function in write mode will compress then encrypt a record",
" state.write.update = function(c, record) {",
" if(!state.write.compressFunction(c, record, state.write)) {",
" // error, but do not send alert since it would require",
" // compression as well",
" c.error(c, {",
" message: 'Could not compress record.',",
" send: false,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.internal_error",
" }",
" });",
" } else if(!state.write.cipherFunction(record, state.write)) {",
" // error, but do not send alert since it would require",
" // encryption as well",
" c.error(c, {",
" message: 'Could not encrypt record.',",
" send: false,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.internal_error",
" }",
" });",
" }",
" return !c.fail;",
" };",
"",
" // handle security parameters",
" if(c.session) {",
" var sp = c.session.sp;",
" c.session.cipherSuite.initSecurityParameters(sp);",
"",
" // generate keys",
" sp.keys = tls.generateKeys(c, sp);",
" state.read.macKey = client ?",
" sp.keys.server_write_MAC_key : sp.keys.client_write_MAC_key;",
" state.write.macKey = client ?",
" sp.keys.client_write_MAC_key : sp.keys.server_write_MAC_key;",
"",
" // cipher suite setup",
" c.session.cipherSuite.initConnectionState(state, c, sp);",
"",
" // compression setup",
" switch(sp.compression_algorithm) {",
" case tls.CompressionMethod.none:",
" break;",
" case tls.CompressionMethod.deflate:",
" state.read.compressFunction = inflate;",
" state.write.compressFunction = deflate;",
" break;",
" default:",
" throw new Error('Unsupported compression algorithm.');",
" }",
" }",
"",
" return state;",
"};",
"",
"/**",
" * Creates a Random structure.",
" *",
" * struct {",
" * uint32 gmt_unix_time;",
" * opaque random_bytes[28];",
" * } Random;",
" *",
" * gmt_unix_time:",
" * The current time and date in standard UNIX 32-bit format (seconds since",
" * the midnight starting Jan 1, 1970, UTC, ignoring leap seconds) according",
" * to the sender's internal clock. Clocks are not required to be set",
" * correctly by the basic TLS protocol; higher-level or application",
" * protocols may define additional requirements. Note that, for historical",
" * reasons, the data element is named using GMT, the predecessor of the",
" * current worldwide time base, UTC.",
" * random_bytes:",
" * 28 bytes generated by a secure random number generator.",
" *",
" * @return the Random structure as a byte array.",
" */",
"tls.createRandom = function() {",
" // get UTC milliseconds",
" var d = new Date();",
" var utc = +d + d.getTimezoneOffset() * 60000;",
" var rval = forge.util.createBuffer();",
" rval.putInt32(utc);",
" rval.putBytes(forge.random.getBytes(28));",
" return rval;",
"};",
"",
"/**",
" * Creates a TLS record with the given type and data.",
" *",
" * @param c the connection.",
" * @param options:",
" * type: the record type.",
" * data: the plain text data in a byte buffer.",
" *",
" * @return the created record.",
" */",
"tls.createRecord = function(c, options) {",
" if(!options.data) {",
" return null;",
" }",
" var record = {",
" type: options.type,",
" version: {",
" major: c.version.major,",
" minor: c.version.minor",
" },",
" length: options.data.length(),",
" fragment: options.data",
" };",
" return record;",
"};",
"",
"/**",
" * Creates a TLS alert record.",
" *",
" * @param c the connection.",
" * @param alert:",
" * level: the TLS alert level.",
" * description: the TLS alert description.",
" *",
" * @return the created alert record.",
" */",
"tls.createAlert = function(c, alert) {",
" var b = forge.util.createBuffer();",
" b.putByte(alert.level);",
" b.putByte(alert.description);",
" return tls.createRecord(c, {",
" type: tls.ContentType.alert,",
" data: b",
" });",
"};",
"",
"/* The structure of a TLS handshake message.",
" *",
" * struct {",
" * HandshakeType msg_type; // handshake type",
" * uint24 length; // bytes in message",
" * select(HandshakeType) {",
" * case hello_request: HelloRequest;",
" * case client_hello: ClientHello;",
" * case server_hello: ServerHello;",
" * case certificate: Certificate;",
" * case server_key_exchange: ServerKeyExchange;",
" * case certificate_request: CertificateRequest;",
" * case server_hello_done: ServerHelloDone;",
" * case certificate_verify: CertificateVerify;",
" * case client_key_exchange: ClientKeyExchange;",
" * case finished: Finished;",
" * } body;",
" * } Handshake;",
" */",
"",
"/**",
" * Creates a ClientHello message.",
" *",
" * opaque SessionID<0..32>;",
" * enum { null(0), deflate(1), (255) } CompressionMethod;",
" * uint8 CipherSuite[2];",
" *",
" * struct {",
" * ProtocolVersion client_version;",
" * Random random;",
" * SessionID session_id;",
" * CipherSuite cipher_suites<2..2^16-2>;",
" * CompressionMethod compression_methods<1..2^8-1>;",
" * select(extensions_present) {",
" * case false:",
" * struct {};",
" * case true:",
" * Extension extensions<0..2^16-1>;",
" * };",
" * } ClientHello;",
" *",
" * The extension format for extended client hellos and server hellos is:",
" *",
" * struct {",
" * ExtensionType extension_type;",
" * opaque extension_data<0..2^16-1>;",
" * } Extension;",
" *",
" * Here:",
" *",
" * - \"extension_type\" identifies the particular extension type.",
" * - \"extension_data\" contains information specific to the particular",
" * extension type.",
" *",
" * The extension types defined in this document are:",
" *",
" * enum {",
" * server_name(0), max_fragment_length(1),",
" * client_certificate_url(2), trusted_ca_keys(3),",
" * truncated_hmac(4), status_request(5), (65535)",
" * } ExtensionType;",
" *",
" * @param c the connection.",
" *",
" * @return the ClientHello byte buffer.",
" */",
"tls.createClientHello = function(c) {",
" // save hello version",
" c.session.clientHelloVersion = {",
" major: c.version.major,",
" minor: c.version.minor",
" };",
"",
" // create supported cipher suites",
" var cipherSuites = forge.util.createBuffer();",
" for(var i = 0; i < c.cipherSuites.length; ++i) {",
" var cs = c.cipherSuites[i];",
" cipherSuites.putByte(cs.id[0]);",
" cipherSuites.putByte(cs.id[1]);",
" }",
" var cSuites = cipherSuites.length();",
"",
" // create supported compression methods, null always supported, but",
" // also support deflate if connection has inflate and deflate methods",
" var compressionMethods = forge.util.createBuffer();",
" compressionMethods.putByte(tls.CompressionMethod.none);",
" // FIXME: deflate support disabled until issues with raw deflate data",
" // without zlib headers are resolved",
" /*",
" if(c.inflate !== null && c.deflate !== null) {",
" compressionMethods.putByte(tls.CompressionMethod.deflate);",
" }",
" */",
" var cMethods = compressionMethods.length();",
"",
" // create TLS SNI (server name indication) extension if virtual host",
" // has been specified, see RFC 3546",
" var extensions = forge.util.createBuffer();",
" if(c.virtualHost) {",
" // create extension struct",
" var ext = forge.util.createBuffer();",
" ext.putByte(0x00); // type server_name (ExtensionType is 2 bytes)",
" ext.putByte(0x00);",
"",
" /* In order to provide the server name, clients MAY include an",
" * extension of type \"server_name\" in the (extended) client hello.",
" * The \"extension_data\" field of this extension SHALL contain",
" * \"ServerNameList\" where:",
" *",
" * struct {",
" * NameType name_type;",
" * select(name_type) {",
" * case host_name: HostName;",
" * } name;",
" * } ServerName;",
" *",
" * enum {",
" * host_name(0), (255)",
" * } NameType;",
" *",
" * opaque HostName<1..2^16-1>;",
" *",
" * struct {",
" * ServerName server_name_list<1..2^16-1>",
" * } ServerNameList;",
" */",
" var serverName = forge.util.createBuffer();",
" serverName.putByte(0x00); // type host_name",
" writeVector(serverName, 2, forge.util.createBuffer(c.virtualHost));",
"",
" // ServerNameList is in extension_data",
" var snList = forge.util.createBuffer();",
" writeVector(snList, 2, serverName);",
" writeVector(ext, 2, snList);",
" extensions.putBuffer(ext);",
" }",
" var extLength = extensions.length();",
" if(extLength > 0) {",
" // add extension vector length",
" extLength += 2;",
" }",
"",
" // determine length of the handshake message",
" // cipher suites and compression methods size will need to be",
" // updated if more get added to the list",
" var sessionId = c.session.id;",
" var length =",
" sessionId.length + 1 + // session ID vector",
" 2 + // version (major + minor)",
" 4 + 28 + // random time and random bytes",
" 2 + cSuites + // cipher suites vector",
" 1 + cMethods + // compression methods vector",
" extLength; // extensions vector",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.client_hello);",
" rval.putInt24(length); // handshake length",
" rval.putByte(c.version.major); // major version",
" rval.putByte(c.version.minor); // minor version",
" rval.putBytes(c.session.sp.client_random); // random time + bytes",
" writeVector(rval, 1, forge.util.createBuffer(sessionId));",
" writeVector(rval, 2, cipherSuites);",
" writeVector(rval, 1, compressionMethods);",
" if(extLength > 0) {",
" writeVector(rval, 2, extensions);",
" }",
" return rval;",
"};",
"",
"/**",
" * Creates a ServerHello message.",
" *",
" * @param c the connection.",
" *",
" * @return the ServerHello byte buffer.",
" */",
"tls.createServerHello = function(c) {",
" // determine length of the handshake message",
" var sessionId = c.session.id;",
" var length =",
" sessionId.length + 1 + // session ID vector",
" 2 + // version (major + minor)",
" 4 + 28 + // random time and random bytes",
" 2 + // chosen cipher suite",
" 1; // chosen compression method",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.server_hello);",
" rval.putInt24(length); // handshake length",
" rval.putByte(c.version.major); // major version",
" rval.putByte(c.version.minor); // minor version",
" rval.putBytes(c.session.sp.server_random); // random time + bytes",
" writeVector(rval, 1, forge.util.createBuffer(sessionId));",
" rval.putByte(c.session.cipherSuite.id[0]);",
" rval.putByte(c.session.cipherSuite.id[1]);",
" rval.putByte(c.session.compressionMethod);",
" return rval;",
"};",
"",
"/**",
" * Creates a Certificate message.",
" *",
" * When this message will be sent:",
" * This is the first message the client can send after receiving a server",
" * hello done message and the first message the server can send after",
" * sending a ServerHello. This client message is only sent if the server",
" * requests a certificate. If no suitable certificate is available, the",
" * client should send a certificate message containing no certificates. If",
" * client authentication is required by the server for the handshake to",
" * continue, it may respond with a fatal handshake failure alert.",
" *",
" * opaque ASN.1Cert<1..2^24-1>;",
" *",
" * struct {",
" * ASN.1Cert certificate_list<0..2^24-1>;",
" * } Certificate;",
" *",
" * @param c the connection.",
" *",
" * @return the Certificate byte buffer.",
" */",
"tls.createCertificate = function(c) {",
" // TODO: check certificate request to ensure types are supported",
"",
" // get a certificate (a certificate as a PEM string)",
" var client = (c.entity === tls.ConnectionEnd.client);",
" var cert = null;",
" if(c.getCertificate) {",
" var hint;",
" if(client) {",
" hint = c.session.certificateRequest;",
" } else {",
" hint = c.session.extensions.server_name.serverNameList;",
" }",
" cert = c.getCertificate(c, hint);",
" }",
"",
" // buffer to hold certificate list",
" var certList = forge.util.createBuffer();",
" if(cert !== null) {",
" try {",
" // normalize cert to a chain of certificates",
" if(!forge.util.isArray(cert)) {",
" cert = [cert];",
" }",
" var asn1 = null;",
" for(var i = 0; i < cert.length; ++i) {",
" var msg = forge.pem.decode(cert[i])[0];",
" if(msg.type !== 'CERTIFICATE' &&",
" msg.type !== 'X509 CERTIFICATE' &&",
" msg.type !== 'TRUSTED CERTIFICATE') {",
" var error = new Error('Could not convert certificate from PEM; PEM ' +",
" 'header type is not \"CERTIFICATE\", \"X509 CERTIFICATE\", or ' +",
" '\"TRUSTED CERTIFICATE\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert certificate from PEM; PEM is encrypted.');",
" }",
"",
" var der = forge.util.createBuffer(msg.body);",
" if(asn1 === null) {",
" asn1 = forge.asn1.fromDer(der.bytes(), false);",
" }",
"",
" // certificate entry is itself a vector with 3 length bytes",
" var certBuffer = forge.util.createBuffer();",
" writeVector(certBuffer, 3, der);",
"",
" // add cert vector to cert list vector",
" certList.putBuffer(certBuffer);",
" }",
"",
" // save certificate",
" cert = forge.pki.certificateFromAsn1(asn1);",
" if(client) {",
" c.session.clientCertificate = cert;",
" } else {",
" c.session.serverCertificate = cert;",
" }",
" } catch(ex) {",
" return c.error(c, {",
" message: 'Could not send certificate list.',",
" cause: ex,",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.bad_certificate",
" }",
" });",
" }",
" }",
"",
" // determine length of the handshake message",
" var length = 3 + certList.length(); // cert list vector",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.certificate);",
" rval.putInt24(length);",
" writeVector(rval, 3, certList);",
" return rval;",
"};",
"",
"/**",
" * Creates a ClientKeyExchange message.",
" *",
" * When this message will be sent:",
" * This message is always sent by the client. It will immediately follow the",
" * client certificate message, if it is sent. Otherwise it will be the first",
" * message sent by the client after it receives the server hello done",
" * message.",
" *",
" * Meaning of this message:",
" * With this message, the premaster secret is set, either though direct",
" * transmission of the RSA-encrypted secret, or by the transmission of",
" * Diffie-Hellman parameters which will allow each side to agree upon the",
" * same premaster secret. When the key exchange method is DH_RSA or DH_DSS,",
" * client certification has been requested, and the client was able to",
" * respond with a certificate which contained a Diffie-Hellman public key",
" * whose parameters (group and generator) matched those specified by the",
" * server in its certificate, this message will not contain any data.",
" *",
" * Meaning of this message:",
" * If RSA is being used for key agreement and authentication, the client",
" * generates a 48-byte premaster secret, encrypts it using the public key",
" * from the server's certificate or the temporary RSA key provided in a",
" * server key exchange message, and sends the result in an encrypted",
" * premaster secret message. This structure is a variant of the client",
" * key exchange message, not a message in itself.",
" *",
" * struct {",
" * select(KeyExchangeAlgorithm) {",
" * case rsa: EncryptedPreMasterSecret;",
" * case diffie_hellman: ClientDiffieHellmanPublic;",
" * } exchange_keys;",
" * } ClientKeyExchange;",
" *",
" * struct {",
" * ProtocolVersion client_version;",
" * opaque random[46];",
" * } PreMasterSecret;",
" *",
" * struct {",
" * public-key-encrypted PreMasterSecret pre_master_secret;",
" * } EncryptedPreMasterSecret;",
" *",
" * A public-key-encrypted element is encoded as a vector <0..2^16-1>.",
" *",
" * @param c the connection.",
" *",
" * @return the ClientKeyExchange byte buffer.",
" */",
"tls.createClientKeyExchange = function(c) {",
" // create buffer to encrypt",
" var b = forge.util.createBuffer();",
"",
" // add highest client-supported protocol to help server avoid version",
" // rollback attacks",
" b.putByte(c.session.clientHelloVersion.major);",
" b.putByte(c.session.clientHelloVersion.minor);",
"",
" // generate and add 46 random bytes",
" b.putBytes(forge.random.getBytes(46));",
"",
" // save pre-master secret",
" var sp = c.session.sp;",
" sp.pre_master_secret = b.getBytes();",
"",
" // RSA-encrypt the pre-master secret",
" var key = c.session.serverCertificate.publicKey;",
" b = key.encrypt(sp.pre_master_secret);",
"",
" /* Note: The encrypted pre-master secret will be stored in a",
" public-key-encrypted opaque vector that has the length prefixed using",
" 2 bytes, so include those 2 bytes in the handshake message length. This",
" is done as a minor optimization instead of calling writeVector(). */",
"",
" // determine length of the handshake message",
" var length = b.length + 2;",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.client_key_exchange);",
" rval.putInt24(length);",
" // add vector length bytes",
" rval.putInt16(b.length);",
" rval.putBytes(b);",
" return rval;",
"};",
"",
"/**",
" * Creates a ServerKeyExchange message.",
" *",
" * @param c the connection.",
" *",
" * @return the ServerKeyExchange byte buffer.",
" */",
"tls.createServerKeyExchange = function(c) {",
" // this implementation only supports RSA, no Diffie-Hellman support,",
" // so this record is empty",
"",
" // determine length of the handshake message",
" var length = 0;",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" if(length > 0) {",
" rval.putByte(tls.HandshakeType.server_key_exchange);",
" rval.putInt24(length);",
" }",
" return rval;",
"};",
"",
"/**",
" * Gets the signed data used to verify a client-side certificate. See",
" * tls.createCertificateVerify() for details.",
" *",
" * @param c the connection.",
" * @param callback the callback to call once the signed data is ready.",
" */",
"tls.getClientSignature = function(c, callback) {",
" // generate data to RSA encrypt",
" var b = forge.util.createBuffer();",
" b.putBuffer(c.session.md5.digest());",
" b.putBuffer(c.session.sha1.digest());",
" b = b.getBytes();",
"",
" // create default signing function as necessary",
" c.getSignature = c.getSignature || function(c, b, callback) {",
" // do rsa encryption, call callback",
" var privateKey = null;",
" if(c.getPrivateKey) {",
" try {",
" privateKey = c.getPrivateKey(c, c.session.clientCertificate);",
" privateKey = forge.pki.privateKeyFromPem(privateKey);",
" } catch(ex) {",
" c.error(c, {",
" message: 'Could not get private key.',",
" cause: ex,",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.internal_error",
" }",
" });",
" }",
" }",
" if(privateKey === null) {",
" c.error(c, {",
" message: 'No private key set.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.internal_error",
" }",
" });",
" } else {",
" b = privateKey.sign(b, null);",
" }",
" callback(c, b);",
" };",
"",
" // get client signature",
" c.getSignature(c, b, callback);",
"};",
"",
"/**",
" * Creates a CertificateVerify message.",
" *",
" * Meaning of this message:",
" * This structure conveys the client's Diffie-Hellman public value",
" * (Yc) if it was not already included in the client's certificate.",
" * The encoding used for Yc is determined by the enumerated",
" * PublicValueEncoding. This structure is a variant of the client",
" * key exchange message, not a message in itself.",
" *",
" * When this message will be sent:",
" * This message is used to provide explicit verification of a client",
" * certificate. This message is only sent following a client",
" * certificate that has signing capability (i.e. all certificates",
" * except those containing fixed Diffie-Hellman parameters). When",
" * sent, it will immediately follow the client key exchange message.",
" *",
" * struct {",
" * Signature signature;",
" * } CertificateVerify;",
" *",
" * CertificateVerify.signature.md5_hash",
" * MD5(handshake_messages);",
" *",
" * Certificate.signature.sha_hash",
" * SHA(handshake_messages);",
" *",
" * Here handshake_messages refers to all handshake messages sent or",
" * received starting at client hello up to but not including this",
" * message, including the type and length fields of the handshake",
" * messages.",
" *",
" * select(SignatureAlgorithm) {",
" * case anonymous: struct { };",
" * case rsa:",
" * digitally-signed struct {",
" * opaque md5_hash[16];",
" * opaque sha_hash[20];",
" * };",
" * case dsa:",
" * digitally-signed struct {",
" * opaque sha_hash[20];",
" * };",
" * } Signature;",
" *",
" * In digital signing, one-way hash functions are used as input for a",
" * signing algorithm. A digitally-signed element is encoded as an opaque",
" * vector <0..2^16-1>, where the length is specified by the signing",
" * algorithm and key.",
" *",
" * In RSA signing, a 36-byte structure of two hashes (one SHA and one",
" * MD5) is signed (encrypted with the private key). It is encoded with",
" * PKCS #1 block type 0 or type 1 as described in [PKCS1].",
" *",
" * In DSS, the 20 bytes of the SHA hash are run directly through the",
" * Digital Signing Algorithm with no additional hashing.",
" *",
" * @param c the connection.",
" * @param signature the signature to include in the message.",
" *",
" * @return the CertificateVerify byte buffer.",
" */",
"tls.createCertificateVerify = function(c, signature) {",
" /* Note: The signature will be stored in a \"digitally-signed\" opaque",
" vector that has the length prefixed using 2 bytes, so include those",
" 2 bytes in the handshake message length. This is done as a minor",
" optimization instead of calling writeVector(). */",
"",
" // determine length of the handshake message",
" var length = signature.length + 2;",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.certificate_verify);",
" rval.putInt24(length);",
" // add vector length bytes",
" rval.putInt16(signature.length);",
" rval.putBytes(signature);",
" return rval;",
"};",
"",
"/**",
" * Creates a CertificateRequest message.",
" *",
" * @param c the connection.",
" *",
" * @return the CertificateRequest byte buffer.",
" */",
"tls.createCertificateRequest = function(c) {",
" // TODO: support other certificate types",
" var certTypes = forge.util.createBuffer();",
"",
" // common RSA certificate type",
" certTypes.putByte(0x01);",
"",
" // add distinguished names from CA store",
" var cAs = forge.util.createBuffer();",
" for(var key in c.caStore.certs) {",
" var cert = c.caStore.certs[key];",
" var dn = forge.pki.distinguishedNameToAsn1(cert.subject);",
" var byteBuffer = forge.asn1.toDer(dn);",
" cAs.putInt16(byteBuffer.length());",
" cAs.putBuffer(byteBuffer);",
" }",
"",
" // TODO: TLS 1.2+ has a different format",
"",
" // determine length of the handshake message",
" var length =",
" 1 + certTypes.length() +",
" 2 + cAs.length();",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.certificate_request);",
" rval.putInt24(length);",
" writeVector(rval, 1, certTypes);",
" writeVector(rval, 2, cAs);",
" return rval;",
"};",
"",
"/**",
" * Creates a ServerHelloDone message.",
" *",
" * @param c the connection.",
" *",
" * @return the ServerHelloDone byte buffer.",
" */",
"tls.createServerHelloDone = function(c) {",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.server_hello_done);",
" rval.putInt24(0);",
" return rval;",
"};",
"",
"/**",
" * Creates a ChangeCipherSpec message.",
" *",
" * The change cipher spec protocol exists to signal transitions in",
" * ciphering strategies. The protocol consists of a single message,",
" * which is encrypted and compressed under the current (not the pending)",
" * connection state. The message consists of a single byte of value 1.",
" *",
" * struct {",
" * enum { change_cipher_spec(1), (255) } type;",
" * } ChangeCipherSpec;",
" *",
" * @return the ChangeCipherSpec byte buffer.",
" */",
"tls.createChangeCipherSpec = function() {",
" var rval = forge.util.createBuffer();",
" rval.putByte(0x01);",
" return rval;",
"};",
"",
"/**",
" * Creates a Finished message.",
" *",
" * struct {",
" * opaque verify_data[12];",
" * } Finished;",
" *",
" * verify_data",
" * PRF(master_secret, finished_label, MD5(handshake_messages) +",
" * SHA-1(handshake_messages)) [0..11];",
" *",
" * finished_label",
" * For Finished messages sent by the client, the string \"client",
" * finished\". For Finished messages sent by the server, the",
" * string \"server finished\".",
" *",
" * handshake_messages",
" * All of the data from all handshake messages up to but not",
" * including this message. This is only data visible at the",
" * handshake layer and does not include record layer headers.",
" * This is the concatenation of all the Handshake structures as",
" * defined in 7.4 exchanged thus far.",
" *",
" * @param c the connection.",
" *",
" * @return the Finished byte buffer.",
" */",
"tls.createFinished = function(c) {",
" // generate verify_data",
" var b = forge.util.createBuffer();",
" b.putBuffer(c.session.md5.digest());",
" b.putBuffer(c.session.sha1.digest());",
"",
" // TODO: determine prf function and verify length for TLS 1.2",
" var client = (c.entity === tls.ConnectionEnd.client);",
" var sp = c.session.sp;",
" var vdl = 12;",
" var prf = prf_TLS1;",
" var label = client ? 'client finished' : 'server finished';",
" b = prf(sp.master_secret, label, b.getBytes(), vdl);",
"",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(tls.HandshakeType.finished);",
" rval.putInt24(b.length());",
" rval.putBuffer(b);",
" return rval;",
"};",
"",
"/**",
" * Creates a HeartbeatMessage (See RFC 6520).",
" *",
" * struct {",
" * HeartbeatMessageType type;",
" * uint16 payload_length;",
" * opaque payload[HeartbeatMessage.payload_length];",
" * opaque padding[padding_length];",
" * } HeartbeatMessage;",
" *",
" * The total length of a HeartbeatMessage MUST NOT exceed 2^14 or",
" * max_fragment_length when negotiated as defined in [RFC6066].",
" *",
" * type: The message type, either heartbeat_request or heartbeat_response.",
" *",
" * payload_length: The length of the payload.",
" *",
" * payload: The payload consists of arbitrary content.",
" *",
" * padding: The padding is random content that MUST be ignored by the",
" * receiver. The length of a HeartbeatMessage is TLSPlaintext.length",
" * for TLS and DTLSPlaintext.length for DTLS. Furthermore, the",
" * length of the type field is 1 byte, and the length of the",
" * payload_length is 2. Therefore, the padding_length is",
" * TLSPlaintext.length - payload_length - 3 for TLS and",
" * DTLSPlaintext.length - payload_length - 3 for DTLS. The",
" * padding_length MUST be at least 16.",
" *",
" * The sender of a HeartbeatMessage MUST use a random padding of at",
" * least 16 bytes. The padding of a received HeartbeatMessage message",
" * MUST be ignored.",
" *",
" * If the payload_length of a received HeartbeatMessage is too large,",
" * the received HeartbeatMessage MUST be discarded silently.",
" *",
" * @param c the connection.",
" * @param type the tls.HeartbeatMessageType.",
" * @param payload the heartbeat data to send as the payload.",
" * @param [payloadLength] the payload length to use, defaults to the",
" * actual payload length.",
" *",
" * @return the HeartbeatRequest byte buffer.",
" */",
"tls.createHeartbeat = function(type, payload, payloadLength) {",
" if(typeof payloadLength === 'undefined') {",
" payloadLength = payload.length;",
" }",
" // build record fragment",
" var rval = forge.util.createBuffer();",
" rval.putByte(type); // heartbeat message type",
" rval.putInt16(payloadLength); // payload length",
" rval.putBytes(payload); // payload",
" // padding",
" var plaintextLength = rval.length();",
" var paddingLength = Math.max(16, plaintextLength - payloadLength - 3);",
" rval.putBytes(forge.random.getBytes(paddingLength));",
" return rval;",
"};",
"",
"/**",
" * Fragments, compresses, encrypts, and queues a record for delivery.",
" *",
" * @param c the connection.",
" * @param record the record to queue.",
" */",
"tls.queue = function(c, record) {",
" // error during record creation",
" if(!record) {",
" return;",
" }",
"",
" if(record.fragment.length() === 0) {",
" if(record.type === tls.ContentType.handshake ||",
" record.type === tls.ContentType.alert ||",
" record.type === tls.ContentType.change_cipher_spec) {",
" // Empty handshake, alert of change cipher spec messages are not allowed per the TLS specification and should not be sent.",
" return;",
" }",
" }",
"",
" // if the record is a handshake record, update handshake hashes",
" if(record.type === tls.ContentType.handshake) {",
" var bytes = record.fragment.bytes();",
" c.session.md5.update(bytes);",
" c.session.sha1.update(bytes);",
" bytes = null;",
" }",
"",
" // handle record fragmentation",
" var records;",
" if(record.fragment.length() <= tls.MaxFragment) {",
" records = [record];",
" } else {",
" // fragment data as long as it is too long",
" records = [];",
" var data = record.fragment.bytes();",
" while(data.length > tls.MaxFragment) {",
" records.push(tls.createRecord(c, {",
" type: record.type,",
" data: forge.util.createBuffer(data.slice(0, tls.MaxFragment))",
" }));",
" data = data.slice(tls.MaxFragment);",
" }",
" // add last record",
" if(data.length > 0) {",
" records.push(tls.createRecord(c, {",
" type: record.type,",
" data: forge.util.createBuffer(data)",
" }));",
" }",
" }",
"",
" // compress and encrypt all fragmented records",
" for(var i = 0; i < records.length && !c.fail; ++i) {",
" // update the record using current write state",
" var rec = records[i];",
" var s = c.state.current.write;",
" if(s.update(c, rec)) {",
" // store record",
" c.records.push(rec);",
" }",
" }",
"};",
"",
"/**",
" * Flushes all queued records to the output buffer and calls the",
" * tlsDataReady() handler on the given connection.",
" *",
" * @param c the connection.",
" *",
" * @return true on success, false on failure.",
" */",
"tls.flush = function(c) {",
" for(var i = 0; i < c.records.length; ++i) {",
" var record = c.records[i];",
"",
" // add record header and fragment",
" c.tlsData.putByte(record.type);",
" c.tlsData.putByte(record.version.major);",
" c.tlsData.putByte(record.version.minor);",
" c.tlsData.putInt16(record.fragment.length());",
" c.tlsData.putBuffer(c.records[i].fragment);",
" }",
" c.records = [];",
" return c.tlsDataReady(c);",
"};",
"",
"/**",
" * Maps a pki.certificateError to a tls.Alert.Description.",
" *",
" * @param error the error to map.",
" *",
" * @return the alert description.",
" */",
"var _certErrorToAlertDesc = function(error) {",
" switch(error) {",
" case true:",
" return true;",
" case forge.pki.certificateError.bad_certificate:",
" return tls.Alert.Description.bad_certificate;",
" case forge.pki.certificateError.unsupported_certificate:",
" return tls.Alert.Description.unsupported_certificate;",
" case forge.pki.certificateError.certificate_revoked:",
" return tls.Alert.Description.certificate_revoked;",
" case forge.pki.certificateError.certificate_expired:",
" return tls.Alert.Description.certificate_expired;",
" case forge.pki.certificateError.certificate_unknown:",
" return tls.Alert.Description.certificate_unknown;",
" case forge.pki.certificateError.unknown_ca:",
" return tls.Alert.Description.unknown_ca;",
" default:",
" return tls.Alert.Description.bad_certificate;",
" }",
"};",
"",
"/**",
" * Maps a tls.Alert.Description to a pki.certificateError.",
" *",
" * @param desc the alert description.",
" *",
" * @return the certificate error.",
" */",
"var _alertDescToCertError = function(desc) {",
" switch(desc) {",
" case true:",
" return true;",
" case tls.Alert.Description.bad_certificate:",
" return forge.pki.certificateError.bad_certificate;",
" case tls.Alert.Description.unsupported_certificate:",
" return forge.pki.certificateError.unsupported_certificate;",
" case tls.Alert.Description.certificate_revoked:",
" return forge.pki.certificateError.certificate_revoked;",
" case tls.Alert.Description.certificate_expired:",
" return forge.pki.certificateError.certificate_expired;",
" case tls.Alert.Description.certificate_unknown:",
" return forge.pki.certificateError.certificate_unknown;",
" case tls.Alert.Description.unknown_ca:",
" return forge.pki.certificateError.unknown_ca;",
" default:",
" return forge.pki.certificateError.bad_certificate;",
" }",
"};",
"",
"/**",
" * Verifies a certificate chain against the given connection's",
" * Certificate Authority store.",
" *",
" * @param c the TLS connection.",
" * @param chain the certificate chain to verify, with the root or highest",
" * authority at the end.",
" *",
" * @return true if successful, false if not.",
" */",
"tls.verifyCertificateChain = function(c, chain) {",
" try {",
" // verify chain",
" forge.pki.verifyCertificateChain(c.caStore, chain,",
" function verify(vfd, depth, chain) {",
" // convert pki.certificateError to tls alert description",
" var desc = _certErrorToAlertDesc(vfd);",
"",
" // call application callback",
" var ret = c.verify(c, vfd, depth, chain);",
" if(ret !== true) {",
" if(typeof ret === 'object' && !forge.util.isArray(ret)) {",
" // throw custom error",
" var error = new Error('The application rejected the certificate.');",
" error.send = true;",
" error.alert = {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.bad_certificate",
" };",
" if(ret.message) {",
" error.message = ret.message;",
" }",
" if(ret.alert) {",
" error.alert.description = ret.alert;",
" }",
" throw error;",
" }",
"",
" // convert tls alert description to pki.certificateError",
" if(ret !== vfd) {",
" ret = _alertDescToCertError(ret);",
" }",
" }",
"",
" return ret;",
" });",
" } catch(ex) {",
" // build tls error if not already customized",
" var err = ex;",
" if(typeof err !== 'object' || forge.util.isArray(err)) {",
" err = {",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: _certErrorToAlertDesc(ex)",
" }",
" };",
" }",
" if(!('send' in err)) {",
" err.send = true;",
" }",
" if(!('alert' in err)) {",
" err.alert = {",
" level: tls.Alert.Level.fatal,",
" description: _certErrorToAlertDesc(err.error)",
" };",
" }",
"",
" // send error",
" c.error(c, err);",
" }",
"",
" return !c.fail;",
"};",
"",
"/**",
" * Creates a new TLS session cache.",
" *",
" * @param cache optional map of session ID to cached session.",
" * @param capacity the maximum size for the cache (default: 100).",
" *",
" * @return the new TLS session cache.",
" */",
"tls.createSessionCache = function(cache, capacity) {",
" var rval = null;",
"",
" // assume input is already a session cache object",
" if(cache && cache.getSession && cache.setSession && cache.order) {",
" rval = cache;",
" } else {",
" // create cache",
" rval = {};",
" rval.cache = cache || {};",
" rval.capacity = Math.max(capacity || 100, 1);",
" rval.order = [];",
"",
" // store order for sessions, delete session overflow",
" for(var key in cache) {",
" if(rval.order.length <= capacity) {",
" rval.order.push(key);",
" } else {",
" delete cache[key];",
" }",
" }",
"",
" // get a session from a session ID (or get any session)",
" rval.getSession = function(sessionId) {",
" var session = null;",
" var key = null;",
"",
" // if session ID provided, use it",
" if(sessionId) {",
" key = forge.util.bytesToHex(sessionId);",
" } else if(rval.order.length > 0) {",
" // get first session from cache",
" key = rval.order[0];",
" }",
"",
" if(key !== null && key in rval.cache) {",
" // get cached session and remove from cache",
" session = rval.cache[key];",
" delete rval.cache[key];",
" for(var i in rval.order) {",
" if(rval.order[i] === key) {",
" rval.order.splice(i, 1);",
" break;",
" }",
" }",
" }",
"",
" return session;",
" };",
"",
" // set a session in the cache",
" rval.setSession = function(sessionId, session) {",
" // remove session from cache if at capacity",
" if(rval.order.length === rval.capacity) {",
" var key = rval.order.shift();",
" delete rval.cache[key];",
" }",
" // add session to cache",
" var key = forge.util.bytesToHex(sessionId);",
" rval.order.push(key);",
" rval.cache[key] = session;",
" };",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Creates a new TLS connection.",
" *",
" * See public createConnection() docs for more details.",
" *",
" * @param options the options for this connection.",
" *",
" * @return the new TLS connection.",
" */",
"tls.createConnection = function(options) {",
" var caStore = null;",
" if(options.caStore) {",
" // if CA store is an array, convert it to a CA store object",
" if(forge.util.isArray(options.caStore)) {",
" caStore = forge.pki.createCaStore(options.caStore);",
" } else {",
" caStore = options.caStore;",
" }",
" } else {",
" // create empty CA store",
" caStore = forge.pki.createCaStore();",
" }",
"",
" // setup default cipher suites",
" var cipherSuites = options.cipherSuites || null;",
" if(cipherSuites === null) {",
" cipherSuites = [];",
" for(var key in tls.CipherSuites) {",
" cipherSuites.push(tls.CipherSuites[key]);",
" }",
" }",
"",
" // set default entity",
" var entity = (options.server || false) ?",
" tls.ConnectionEnd.server : tls.ConnectionEnd.client;",
"",
" // create session cache if requested",
" var sessionCache = options.sessionCache ?",
" tls.createSessionCache(options.sessionCache) : null;",
"",
" // create TLS connection",
" var c = {",
" version: {major: tls.Version.major, minor: tls.Version.minor},",
" entity: entity,",
" sessionId: options.sessionId,",
" caStore: caStore,",
" sessionCache: sessionCache,",
" cipherSuites: cipherSuites,",
" connected: options.connected,",
" virtualHost: options.virtualHost || null,",
" verifyClient: options.verifyClient || false,",
" verify: options.verify || function(cn, vfd, dpth, cts) {return vfd;},",
" getCertificate: options.getCertificate || null,",
" getPrivateKey: options.getPrivateKey || null,",
" getSignature: options.getSignature || null,",
" input: forge.util.createBuffer(),",
" tlsData: forge.util.createBuffer(),",
" data: forge.util.createBuffer(),",
" tlsDataReady: options.tlsDataReady,",
" dataReady: options.dataReady,",
" heartbeatReceived: options.heartbeatReceived,",
" closed: options.closed,",
" error: function(c, ex) {",
" // set origin if not set",
" ex.origin = ex.origin ||",
" ((c.entity === tls.ConnectionEnd.client) ? 'client' : 'server');",
"",
" // send TLS alert",
" if(ex.send) {",
" tls.queue(c, tls.createAlert(c, ex.alert));",
" tls.flush(c);",
" }",
"",
" // error is fatal by default",
" var fatal = (ex.fatal !== false);",
" if(fatal) {",
" // set fail flag",
" c.fail = true;",
" }",
"",
" // call error handler first",
" options.error(c, ex);",
"",
" if(fatal) {",
" // fatal error, close connection, do not clear fail",
" c.close(false);",
" }",
" },",
" deflate: options.deflate || null,",
" inflate: options.inflate || null",
" };",
"",
" /**",
" * Resets a closed TLS connection for reuse. Called in c.close().",
" *",
" * @param clearFail true to clear the fail flag (default: true).",
" */",
" c.reset = function(clearFail) {",
" c.version = {major: tls.Version.major, minor: tls.Version.minor};",
" c.record = null;",
" c.session = null;",
" c.peerCertificate = null;",
" c.state = {",
" pending: null,",
" current: null",
" };",
" c.expect = (c.entity === tls.ConnectionEnd.client) ? SHE : CHE;",
" c.fragmented = null;",
" c.records = [];",
" c.open = false;",
" c.handshakes = 0;",
" c.handshaking = false;",
" c.isConnected = false;",
" c.fail = !(clearFail || typeof(clearFail) === 'undefined');",
" c.input.clear();",
" c.tlsData.clear();",
" c.data.clear();",
" c.state.current = tls.createConnectionState(c);",
" };",
"",
" // do initial reset of connection",
" c.reset();",
"",
" /**",
" * Updates the current TLS engine state based on the given record.",
" *",
" * @param c the TLS connection.",
" * @param record the TLS record to act on.",
" */",
" var _update = function(c, record) {",
" // get record handler (align type in table by subtracting lowest)",
" var aligned = record.type - tls.ContentType.change_cipher_spec;",
" var handlers = ctTable[c.entity][c.expect];",
" if(aligned in handlers) {",
" handlers[aligned](c, record);",
" } else {",
" // unexpected record",
" tls.handleUnexpected(c, record);",
" }",
" };",
"",
" /**",
" * Reads the record header and initializes the next record on the given",
" * connection.",
" *",
" * @param c the TLS connection with the next record.",
" *",
" * @return 0 if the input data could be processed, otherwise the",
" * number of bytes required for data to be processed.",
" */",
" var _readRecordHeader = function(c) {",
" var rval = 0;",
"",
" // get input buffer and its length",
" var b = c.input;",
" var len = b.length();",
"",
" // need at least 5 bytes to initialize a record",
" if(len < 5) {",
" rval = 5 - len;",
" } else {",
" // enough bytes for header",
" // initialize record",
" c.record = {",
" type: b.getByte(),",
" version: {",
" major: b.getByte(),",
" minor: b.getByte()",
" },",
" length: b.getInt16(),",
" fragment: forge.util.createBuffer(),",
" ready: false",
" };",
"",
" // check record version",
" var compatibleVersion = (c.record.version.major === c.version.major);",
" if(compatibleVersion && c.session && c.session.version) {",
" // session version already set, require same minor version",
" compatibleVersion = (c.record.version.minor === c.version.minor);",
" }",
" if(!compatibleVersion) {",
" c.error(c, {",
" message: 'Incompatible TLS version.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description: tls.Alert.Description.protocol_version",
" }",
" });",
" }",
" }",
"",
" return rval;",
" };",
"",
" /**",
" * Reads the next record's contents and appends its message to any",
" * previously fragmented message.",
" *",
" * @param c the TLS connection with the next record.",
" *",
" * @return 0 if the input data could be processed, otherwise the",
" * number of bytes required for data to be processed.",
" */",
" var _readRecord = function(c) {",
" var rval = 0;",
"",
" // ensure there is enough input data to get the entire record",
" var b = c.input;",
" var len = b.length();",
" if(len < c.record.length) {",
" // not enough data yet, return how much is required",
" rval = c.record.length - len;",
" } else {",
" // there is enough data to parse the pending record",
" // fill record fragment and compact input buffer",
" c.record.fragment.putBytes(b.getBytes(c.record.length));",
" b.compact();",
"",
" // update record using current read state",
" var s = c.state.current.read;",
" if(s.update(c, c.record)) {",
" // see if there is a previously fragmented message that the",
" // new record's message fragment should be appended to",
" if(c.fragmented !== null) {",
" // if the record type matches a previously fragmented",
" // record, append the record fragment to it",
" if(c.fragmented.type === c.record.type) {",
" // concatenate record fragments",
" c.fragmented.fragment.putBuffer(c.record.fragment);",
" c.record = c.fragmented;",
" } else {",
" // error, invalid fragmented record",
" c.error(c, {",
" message: 'Invalid fragmented record.',",
" send: true,",
" alert: {",
" level: tls.Alert.Level.fatal,",
" description:",
" tls.Alert.Description.unexpected_message",
" }",
" });",
" }",
" }",
"",
" // record is now ready",
" c.record.ready = true;",
" }",
" }",
"",
" return rval;",
" };",
"",
" /**",
" * Performs a handshake using the TLS Handshake Protocol, as a client.",
" *",
" * This method should only be called if the connection is in client mode.",
" *",
" * @param sessionId the session ID to use, null to start a new one.",
" */",
" c.handshake = function(sessionId) {",
" // error to call this in non-client mode",
" if(c.entity !== tls.ConnectionEnd.client) {",
" // not fatal error",
" c.error(c, {",
" message: 'Cannot initiate handshake as a server.',",
" fatal: false",
" });",
" } else if(c.handshaking) {",
" // handshake is already in progress, fail but not fatal error",
" c.error(c, {",
" message: 'Handshake already in progress.',",
" fatal: false",
" });",
" } else {",
" // clear fail flag on reuse",
" if(c.fail && !c.open && c.handshakes === 0) {",
" c.fail = false;",
" }",
"",
" // now handshaking",
" c.handshaking = true;",
"",
" // default to blank (new session)",
" sessionId = sessionId || '';",
"",
" // if a session ID was specified, try to find it in the cache",
" var session = null;",
" if(sessionId.length > 0) {",
" if(c.sessionCache) {",
" session = c.sessionCache.getSession(sessionId);",
" }",
"",
" // matching session not found in cache, clear session ID",
" if(session === null) {",
" sessionId = '';",
" }",
" }",
"",
" // no session given, grab a session from the cache, if available",
" if(sessionId.length === 0 && c.sessionCache) {",
" session = c.sessionCache.getSession();",
" if(session !== null) {",
" sessionId = session.id;",
" }",
" }",
"",
" // set up session",
" c.session = {",
" id: sessionId,",
" version: null,",
" cipherSuite: null,",
" compressionMethod: null,",
" serverCertificate: null,",
" certificateRequest: null,",
" clientCertificate: null,",
" sp: {},",
" md5: forge.md.md5.create(),",
" sha1: forge.md.sha1.create()",
" };",
"",
" // use existing session information",
" if(session) {",
" // only update version on connection, session version not yet set",
" c.version = session.version;",
" c.session.sp = session.sp;",
" }",
"",
" // generate new client random",
" c.session.sp.client_random = tls.createRandom().getBytes();",
"",
" // connection now open",
" c.open = true;",
"",
" // send hello",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.handshake,",
" data: tls.createClientHello(c)",
" }));",
" tls.flush(c);",
" }",
" };",
"",
" /**",
" * Called when TLS protocol data has been received from somewhere and should",
" * be processed by the TLS engine.",
" *",
" * @param data the TLS protocol data, as a string, to process.",
" *",
" * @return 0 if the data could be processed, otherwise the number of bytes",
" * required for data to be processed.",
" */",
" c.process = function(data) {",
" var rval = 0;",
"",
" // buffer input data",
" if(data) {",
" c.input.putBytes(data);",
" }",
"",
" // process next record if no failure, process will be called after",
" // each record is handled (since handling can be asynchronous)",
" if(!c.fail) {",
" // reset record if ready and now empty",
" if(c.record !== null &&",
" c.record.ready && c.record.fragment.isEmpty()) {",
" c.record = null;",
" }",
"",
" // if there is no pending record, try to read record header",
" if(c.record === null) {",
" rval = _readRecordHeader(c);",
" }",
"",
" // read the next record (if record not yet ready)",
" if(!c.fail && c.record !== null && !c.record.ready) {",
" rval = _readRecord(c);",
" }",
"",
" // record ready to be handled, update engine state",
" if(!c.fail && c.record !== null && c.record.ready) {",
" _update(c, c.record);",
" }",
" }",
"",
" return rval;",
" };",
"",
" /**",
" * Requests that application data be packaged into a TLS record. The",
" * tlsDataReady handler will be called when the TLS record(s) have been",
" * prepared.",
" *",
" * @param data the application data, as a raw 'binary' encoded string, to",
" * be sent; to send utf-16/utf-8 string data, use the return value",
" * of util.encodeUtf8(str).",
" *",
" * @return true on success, false on failure.",
" */",
" c.prepare = function(data) {",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.application_data,",
" data: forge.util.createBuffer(data)",
" }));",
" return tls.flush(c);",
" };",
"",
" /**",
" * Requests that a heartbeat request be packaged into a TLS record for",
" * transmission. The tlsDataReady handler will be called when TLS record(s)",
" * have been prepared.",
" *",
" * When a heartbeat response has been received, the heartbeatReceived",
" * handler will be called with the matching payload. This handler can",
" * be used to clear a retransmission timer, etc.",
" *",
" * @param payload the heartbeat data to send as the payload in the message.",
" * @param [payloadLength] the payload length to use, defaults to the",
" * actual payload length.",
" *",
" * @return true on success, false on failure.",
" */",
" c.prepareHeartbeatRequest = function(payload, payloadLength) {",
" if(payload instanceof forge.util.ByteBuffer) {",
" payload = payload.bytes();",
" }",
" if(typeof payloadLength === 'undefined') {",
" payloadLength = payload.length;",
" }",
" c.expectedHeartbeatPayload = payload;",
" tls.queue(c, tls.createRecord(c, {",
" type: tls.ContentType.heartbeat,",
" data: tls.createHeartbeat(",
" tls.HeartbeatMessageType.heartbeat_request, payload, payloadLength)",
" }));",
" return tls.flush(c);",
" };",
"",
" /**",
" * Closes the connection (sends a close_notify alert).",
" *",
" * @param clearFail true to clear the fail flag (default: true).",
" */",
" c.close = function(clearFail) {",
" // save session if connection didn't fail",
" if(!c.fail && c.sessionCache && c.session) {",
" // only need to preserve session ID, version, and security params",
" var session = {",
" id: c.session.id,",
" version: c.session.version,",
" sp: c.session.sp",
" };",
" session.sp.keys = null;",
" c.sessionCache.setSession(session.id, session);",
" }",
"",
" if(c.open) {",
" // connection no longer open, clear input",
" c.open = false;",
" c.input.clear();",
"",
" // if connected or handshaking, send an alert",
" if(c.isConnected || c.handshaking) {",
" c.isConnected = c.handshaking = false;",
"",
" // send close_notify alert",
" tls.queue(c, tls.createAlert(c, {",
" level: tls.Alert.Level.warning,",
" description: tls.Alert.Description.close_notify",
" }));",
" tls.flush(c);",
" }",
"",
" // call handler",
" c.closed(c);",
" }",
"",
" // reset TLS connection, do not clear fail flag",
" c.reset(clearFail);",
" };",
"",
" return c;",
"};",
"",
"/* TLS API */",
"module.exports = forge.tls = forge.tls || {};",
"",
"// expose non-functions",
"for(var key in tls) {",
" if(typeof tls[key] !== 'function') {",
" forge.tls[key] = tls[key];",
" }",
"}",
"",
"// expose prf_tls1 for testing",
"forge.tls.prf_tls1 = prf_TLS1;",
"",
"// expose sha1 hmac method",
"forge.tls.hmac_sha1 = hmac_sha1;",
"",
"// expose session cache creation",
"forge.tls.createSessionCache = tls.createSessionCache;",
"",
"/**",
" * Creates a new TLS connection. This does not make any assumptions about the",
" * transport layer that TLS is working on top of, ie: it does not assume there",
" * is a TCP/IP connection or establish one. A TLS connection is totally",
" * abstracted away from the layer is runs on top of, it merely establishes a",
" * secure channel between a client\" and a \"server\".",
" *",
" * A TLS connection contains 4 connection states: pending read and write, and",
" * current read and write.",
" *",
" * At initialization, the current read and write states will be null. Only once",
" * the security parameters have been set and the keys have been generated can",
" * the pending states be converted into current states. Current states will be",
" * updated for each record processed.",
" *",
" * A custom certificate verify callback may be provided to check information",
" * like the common name on the server's certificate. It will be called for",
" * every certificate in the chain. It has the following signature:",
" *",
" * variable func(c, certs, index, preVerify)",
" * Where:",
" * c The TLS connection",
" * verified Set to true if certificate was verified, otherwise the alert",
" * tls.Alert.Description for why the certificate failed.",
" * depth The current index in the chain, where 0 is the server's cert.",
" * certs The certificate chain, *NOTE* if the server was anonymous then",
" * the chain will be empty.",
" *",
" * The function returns true on success and on failure either the appropriate",
" * tls.Alert.Description or an object with 'alert' set to the appropriate",
" * tls.Alert.Description and 'message' set to a custom error message. If true",
" * is not returned then the connection will abort using, in order of",
" * availability, first the returned alert description, second the preVerify",
" * alert description, and lastly the default 'bad_certificate'.",
" *",
" * There are three callbacks that can be used to make use of client-side",
" * certificates where each takes the TLS connection as the first parameter:",
" *",
" * getCertificate(conn, hint)",
" * The second parameter is a hint as to which certificate should be",
" * returned. If the connection entity is a client, then the hint will be",
" * the CertificateRequest message from the server that is part of the",
" * TLS protocol. If the connection entity is a server, then it will be",
" * the servername list provided via an SNI extension the ClientHello, if",
" * one was provided (empty array if not). The hint can be examined to",
" * determine which certificate to use (advanced). Most implementations",
" * will just return a certificate. The return value must be a",
" * PEM-formatted certificate or an array of PEM-formatted certificates",
" * that constitute a certificate chain, with the first in the array/chain",
" * being the client's certificate.",
" * getPrivateKey(conn, certificate)",
" * The second parameter is an forge.pki X.509 certificate object that",
" * is associated with the requested private key. The return value must",
" * be a PEM-formatted private key.",
" * getSignature(conn, bytes, callback)",
" * This callback can be used instead of getPrivateKey if the private key",
" * is not directly accessible in javascript or should not be. For",
" * instance, a secure external web service could provide the signature",
" * in exchange for appropriate credentials. The second parameter is a",
" * string of bytes to be signed that are part of the TLS protocol. These",
" * bytes are used to verify that the private key for the previously",
" * provided client-side certificate is accessible to the client. The",
" * callback is a function that takes 2 parameters, the TLS connection",
" * and the RSA encrypted (signed) bytes as a string. This callback must",
" * be called once the signature is ready.",
" *",
" * @param options the options for this connection:",
" * server: true if the connection is server-side, false for client.",
" * sessionId: a session ID to reuse, null for a new connection.",
" * caStore: an array of certificates to trust.",
" * sessionCache: a session cache to use.",
" * cipherSuites: an optional array of cipher suites to use,",
" * see tls.CipherSuites.",
" * connected: function(conn) called when the first handshake completes.",
" * virtualHost: the virtual server name to use in a TLS SNI extension.",
" * verifyClient: true to require a client certificate in server mode,",
" * 'optional' to request one, false not to (default: false).",
" * verify: a handler used to custom verify certificates in the chain.",
" * getCertificate: an optional callback used to get a certificate or",
" * a chain of certificates (as an array).",
" * getPrivateKey: an optional callback used to get a private key.",
" * getSignature: an optional callback used to get a signature.",
" * tlsDataReady: function(conn) called when TLS protocol data has been",
" * prepared and is ready to be used (typically sent over a socket",
" * connection to its destination), read from conn.tlsData buffer.",
" * dataReady: function(conn) called when application data has",
" * been parsed from a TLS record and should be consumed by the",
" * application, read from conn.data buffer.",
" * closed: function(conn) called when the connection has been closed.",
" * error: function(conn, error) called when there was an error.",
" * deflate: function(inBytes) if provided, will deflate TLS records using",
" * the deflate algorithm if the server supports it.",
" * inflate: function(inBytes) if provided, will inflate TLS records using",
" * the deflate algorithm if the server supports it.",
" *",
" * @return the new TLS connection.",
" */",
"forge.tls.createConnection = tls.createConnection;",
"",
"",
"/***/ }),",
"/* 20 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of a basic Public Key Infrastructure, including",
" * support for RSA public and private keys.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2013 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(3);",
"__webpack_require__(6);",
"__webpack_require__(21);",
"__webpack_require__(7);",
"__webpack_require__(15);",
"__webpack_require__(28);",
"__webpack_require__(17);",
"__webpack_require__(11);",
"__webpack_require__(1);",
"__webpack_require__(16);",
"",
"// shortcut for asn.1 API",
"var asn1 = forge.asn1;",
"",
"/* Public Key Infrastructure (PKI) implementation. */",
"var pki = module.exports = forge.pki = forge.pki || {};",
"",
"/**",
" * NOTE: THIS METHOD IS DEPRECATED. Use pem.decode() instead.",
" *",
" * Converts PEM-formatted data to DER.",
" *",
" * @param pem the PEM-formatted data.",
" *",
" * @return the DER-formatted data.",
" */",
"pki.pemToDer = function(pem) {",
" var msg = forge.pem.decode(pem)[0];",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert PEM to DER; PEM is encrypted.');",
" }",
" return forge.util.createBuffer(msg.body);",
"};",
"",
"/**",
" * Converts an RSA private key from PEM format.",
" *",
" * @param pem the PEM-formatted private key.",
" *",
" * @return the private key.",
" */",
"pki.privateKeyFromPem = function(pem) {",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'PRIVATE KEY' && msg.type !== 'RSA PRIVATE KEY') {",
" var error = new Error('Could not convert private key from PEM; PEM ' +",
" 'header type is not \"PRIVATE KEY\" or \"RSA PRIVATE KEY\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert private key from PEM; PEM is encrypted.');",
" }",
"",
" // convert DER to ASN.1 object",
" var obj = asn1.fromDer(msg.body);",
"",
" return pki.privateKeyFromAsn1(obj);",
"};",
"",
"/**",
" * Converts an RSA private key to PEM format.",
" *",
" * @param key the private key.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted private key.",
" */",
"pki.privateKeyToPem = function(key, maxline) {",
" // convert to ASN.1, then DER, then PEM-encode",
" var msg = {",
" type: 'RSA PRIVATE KEY',",
" body: asn1.toDer(pki.privateKeyToAsn1(key)).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"/**",
" * Converts a PrivateKeyInfo to PEM format.",
" *",
" * @param pki the PrivateKeyInfo.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted private key.",
" */",
"pki.privateKeyInfoToPem = function(pki, maxline) {",
" // convert to DER, then PEM-encode",
" var msg = {",
" type: 'PRIVATE KEY',",
" body: asn1.toDer(pki).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"",
"/***/ }),",
"/* 21 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Password-based encryption functions.",
" *",
" * @author Dave Longley",
" * @author Stefan Siegl ",
" *",
" * Copyright (c) 2010-2013 Digital Bazaar, Inc.",
" * Copyright (c) 2012 Stefan Siegl ",
" *",
" * An EncryptedPrivateKeyInfo:",
" *",
" * EncryptedPrivateKeyInfo ::= SEQUENCE {",
" * encryptionAlgorithm EncryptionAlgorithmIdentifier,",
" * encryptedData EncryptedData }",
" *",
" * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier",
" *",
" * EncryptedData ::= OCTET STRING",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(5);",
"__webpack_require__(3);",
"__webpack_require__(10);",
"__webpack_require__(4);",
"__webpack_require__(6);",
"__webpack_require__(15);",
"__webpack_require__(7);",
"__webpack_require__(2);",
"__webpack_require__(25);",
"__webpack_require__(11);",
"__webpack_require__(1);",
"",
"if(typeof BigInteger === 'undefined') {",
" var BigInteger = forge.jsbn.BigInteger;",
"}",
"",
"// shortcut for asn.1 API",
"var asn1 = forge.asn1;",
"",
"/* Password-based encryption implementation. */",
"var pki = forge.pki = forge.pki || {};",
"module.exports = pki.pbe = forge.pbe = forge.pbe || {};",
"var oids = pki.oids;",
"",
"// validator for an EncryptedPrivateKeyInfo structure",
"// Note: Currently only works w/algorithm params",
"var encryptedPrivateKeyValidator = {",
" name: 'EncryptedPrivateKeyInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'EncryptedPrivateKeyInfo.encryptionAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'AlgorithmIdentifier.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'encryptionOid'",
" }, {",
" name: 'AlgorithmIdentifier.parameters',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'encryptionParams'",
" }]",
" }, {",
" // encryptedData",
" name: 'EncryptedPrivateKeyInfo.encryptedData',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'encryptedData'",
" }]",
"};",
"",
"// validator for a PBES2Algorithms structure",
"// Note: Currently only works w/PBKDF2 + AES encryption schemes",
"var PBES2AlgorithmsValidator = {",
" name: 'PBES2Algorithms',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'PBES2Algorithms.keyDerivationFunc',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'PBES2Algorithms.keyDerivationFunc.oid',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'kdfOid'",
" }, {",
" name: 'PBES2Algorithms.params',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'PBES2Algorithms.params.salt',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'kdfSalt'",
" }, {",
" name: 'PBES2Algorithms.params.iterationCount',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'kdfIterationCount'",
" }, {",
" name: 'PBES2Algorithms.params.keyLength',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" optional: true,",
" capture: 'keyLength'",
" }, {",
" // prf",
" name: 'PBES2Algorithms.params.prf',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" optional: true,",
" value: [{",
" name: 'PBES2Algorithms.params.prf.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'prfOid'",
" }]",
" }]",
" }]",
" }, {",
" name: 'PBES2Algorithms.encryptionScheme',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'PBES2Algorithms.encryptionScheme.oid',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'encOid'",
" }, {",
" name: 'PBES2Algorithms.encryptionScheme.iv',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'encIv'",
" }]",
" }]",
"};",
"",
"var pkcs12PbeParamsValidator = {",
" name: 'pkcs-12PbeParams',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'pkcs-12PbeParams.salt',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'salt'",
" }, {",
" name: 'pkcs-12PbeParams.iterations',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'iterations'",
" }]",
"};",
"",
"/**",
" * Encrypts a ASN.1 PrivateKeyInfo object, producing an EncryptedPrivateKeyInfo.",
" *",
" * PBES2Algorithms ALGORITHM-IDENTIFIER ::=",
" * { {PBES2-params IDENTIFIED BY id-PBES2}, ...}",
" *",
" * id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}",
" *",
" * PBES2-params ::= SEQUENCE {",
" * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},",
" * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}",
" * }",
" *",
" * PBES2-KDFs ALGORITHM-IDENTIFIER ::=",
" * { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }",
" *",
" * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }",
" *",
" * PBKDF2-params ::= SEQUENCE {",
" * salt CHOICE {",
" * specified OCTET STRING,",
" * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}",
" * },",
" * iterationCount INTEGER (1..MAX),",
" * keyLength INTEGER (1..MAX) OPTIONAL,",
" * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1",
" * }",
" *",
" * @param obj the ASN.1 PrivateKeyInfo object.",
" * @param password the password to encrypt with.",
" * @param options:",
" * algorithm the encryption algorithm to use",
" * ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.",
" * count the iteration count to use.",
" * saltSize the salt size to use.",
" * prfAlgorithm the PRF message digest algorithm to use",
" * ('sha1', 'sha224', 'sha256', 'sha384', 'sha512')",
" *",
" * @return the ASN.1 EncryptedPrivateKeyInfo.",
" */",
"pki.encryptPrivateKeyInfo = function(obj, password, options) {",
" // set default options",
" options = options || {};",
" options.saltSize = options.saltSize || 8;",
" options.count = options.count || 2048;",
" options.algorithm = options.algorithm || 'aes128';",
" options.prfAlgorithm = options.prfAlgorithm || 'sha1';",
"",
" // generate PBE params",
" var salt = forge.random.getBytesSync(options.saltSize);",
" var count = options.count;",
" var countBytes = asn1.integerToDer(count);",
" var dkLen;",
" var encryptionAlgorithm;",
" var encryptedData;",
" if(options.algorithm.indexOf('aes') === 0 || options.algorithm === 'des') {",
" // do PBES2",
" var ivLen, encOid, cipherFn;",
" switch(options.algorithm) {",
" case 'aes128':",
" dkLen = 16;",
" ivLen = 16;",
" encOid = oids['aes128-CBC'];",
" cipherFn = forge.aes.createEncryptionCipher;",
" break;",
" case 'aes192':",
" dkLen = 24;",
" ivLen = 16;",
" encOid = oids['aes192-CBC'];",
" cipherFn = forge.aes.createEncryptionCipher;",
" break;",
" case 'aes256':",
" dkLen = 32;",
" ivLen = 16;",
" encOid = oids['aes256-CBC'];",
" cipherFn = forge.aes.createEncryptionCipher;",
" break;",
" case 'des':",
" dkLen = 8;",
" ivLen = 8;",
" encOid = oids['desCBC'];",
" cipherFn = forge.des.createEncryptionCipher;",
" break;",
" default:",
" var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');",
" error.algorithm = options.algorithm;",
" throw error;",
" }",
"",
" // get PRF message digest",
" var prfAlgorithm = 'hmacWith' + options.prfAlgorithm.toUpperCase();",
" var md = prfAlgorithmToMessageDigest(prfAlgorithm);",
"",
" // encrypt private key using pbe SHA-1 and AES/DES",
" var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen, md);",
" var iv = forge.random.getBytesSync(ivLen);",
" var cipher = cipherFn(dk);",
" cipher.start(iv);",
" cipher.update(asn1.toDer(obj));",
" cipher.finish();",
" encryptedData = cipher.output.getBytes();",
"",
" // get PBKDF2-params",
" var params = createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm);",
"",
" encryptionAlgorithm = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(oids['pkcs5PBES2']).getBytes()),",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // keyDerivationFunc",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(oids['pkcs5PBKDF2']).getBytes()),",
" // PBKDF2-params",
" params",
" ]),",
" // encryptionScheme",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(encOid).getBytes()),",
" // iv",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, iv)",
" ])",
" ])",
" ]);",
" } else if(options.algorithm === '3des') {",
" // Do PKCS12 PBE",
" dkLen = 24;",
"",
" var saltBytes = new forge.util.ByteBuffer(salt);",
" var dk = pki.pbe.generatePkcs12Key(password, saltBytes, 1, count, dkLen);",
" var iv = pki.pbe.generatePkcs12Key(password, saltBytes, 2, count, dkLen);",
" var cipher = forge.des.createEncryptionCipher(dk);",
" cipher.start(iv);",
" cipher.update(asn1.toDer(obj));",
" cipher.finish();",
" encryptedData = cipher.output.getBytes();",
"",
" encryptionAlgorithm = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(oids['pbeWithSHAAnd3-KeyTripleDES-CBC']).getBytes()),",
" // pkcs-12PbeParams",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // salt",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),",
" // iteration count",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" countBytes.getBytes())",
" ])",
" ]);",
" } else {",
" var error = new Error('Cannot encrypt private key. Unknown encryption algorithm.');",
" error.algorithm = options.algorithm;",
" throw error;",
" }",
"",
" // EncryptedPrivateKeyInfo",
" var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // encryptionAlgorithm",
" encryptionAlgorithm,",
" // encryptedData",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, encryptedData)",
" ]);",
" return rval;",
"};",
"",
"/**",
" * Decrypts a ASN.1 PrivateKeyInfo object.",
" *",
" * @param obj the ASN.1 EncryptedPrivateKeyInfo object.",
" * @param password the password to decrypt with.",
" *",
" * @return the ASN.1 PrivateKeyInfo on success, null on failure.",
" */",
"pki.decryptPrivateKeyInfo = function(obj, password) {",
" var rval = null;",
"",
" // get PBE params",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, encryptedPrivateKeyValidator, capture, errors)) {",
" var error = new Error('Cannot read encrypted private key. ' +",
" 'ASN.1 object is not a supported EncryptedPrivateKeyInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" // get cipher",
" var oid = asn1.derToOid(capture.encryptionOid);",
" var cipher = pki.pbe.getCipher(oid, capture.encryptionParams, password);",
"",
" // get encrypted data",
" var encrypted = forge.util.createBuffer(capture.encryptedData);",
"",
" cipher.update(encrypted);",
" if(cipher.finish()) {",
" rval = asn1.fromDer(cipher.output);",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Converts a EncryptedPrivateKeyInfo to PEM format.",
" *",
" * @param epki the EncryptedPrivateKeyInfo.",
" * @param maxline the maximum characters per line, defaults to 64.",
" *",
" * @return the PEM-formatted encrypted private key.",
" */",
"pki.encryptedPrivateKeyToPem = function(epki, maxline) {",
" // convert to DER, then PEM-encode",
" var msg = {",
" type: 'ENCRYPTED PRIVATE KEY',",
" body: asn1.toDer(epki).getBytes()",
" };",
" return forge.pem.encode(msg, {maxline: maxline});",
"};",
"",
"/**",
" * Converts a PEM-encoded EncryptedPrivateKeyInfo to ASN.1 format. Decryption",
" * is not performed.",
" *",
" * @param pem the EncryptedPrivateKeyInfo in PEM-format.",
" *",
" * @return the ASN.1 EncryptedPrivateKeyInfo.",
" */",
"pki.encryptedPrivateKeyFromPem = function(pem) {",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'ENCRYPTED PRIVATE KEY') {",
" var error = new Error('Could not convert encrypted private key from PEM; ' +",
" 'PEM header type is \"ENCRYPTED PRIVATE KEY\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert encrypted private key from PEM; ' +",
" 'PEM is encrypted.');",
" }",
"",
" // convert DER to ASN.1 object",
" return asn1.fromDer(msg.body);",
"};",
"",
"/**",
" * Encrypts an RSA private key. By default, the key will be wrapped in",
" * a PrivateKeyInfo and encrypted to produce a PKCS#8 EncryptedPrivateKeyInfo.",
" * This is the standard, preferred way to encrypt a private key.",
" *",
" * To produce a non-standard PEM-encrypted private key that uses encapsulated",
" * headers to indicate the encryption algorithm (old-style non-PKCS#8 OpenSSL",
" * private key encryption), set the 'legacy' option to true. Note: Using this",
" * option will cause the iteration count to be forced to 1.",
" *",
" * Note: The 'des' algorithm is supported, but it is not considered to be",
" * secure because it only uses a single 56-bit key. If possible, it is highly",
" * recommended that a different algorithm be used.",
" *",
" * @param rsaKey the RSA key to encrypt.",
" * @param password the password to use.",
" * @param options:",
" * algorithm: the encryption algorithm to use",
" * ('aes128', 'aes192', 'aes256', '3des', 'des').",
" * count: the iteration count to use.",
" * saltSize: the salt size to use.",
" * legacy: output an old non-PKCS#8 PEM-encrypted+encapsulated",
" * headers (DEK-Info) private key.",
" *",
" * @return the PEM-encoded ASN.1 EncryptedPrivateKeyInfo.",
" */",
"pki.encryptRsaPrivateKey = function(rsaKey, password, options) {",
" // standard PKCS#8",
" options = options || {};",
" if(!options.legacy) {",
" // encrypt PrivateKeyInfo",
" var rval = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(rsaKey));",
" rval = pki.encryptPrivateKeyInfo(rval, password, options);",
" return pki.encryptedPrivateKeyToPem(rval);",
" }",
"",
" // legacy non-PKCS#8",
" var algorithm;",
" var iv;",
" var dkLen;",
" var cipherFn;",
" switch(options.algorithm) {",
" case 'aes128':",
" algorithm = 'AES-128-CBC';",
" dkLen = 16;",
" iv = forge.random.getBytesSync(16);",
" cipherFn = forge.aes.createEncryptionCipher;",
" break;",
" case 'aes192':",
" algorithm = 'AES-192-CBC';",
" dkLen = 24;",
" iv = forge.random.getBytesSync(16);",
" cipherFn = forge.aes.createEncryptionCipher;",
" break;",
" case 'aes256':",
" algorithm = 'AES-256-CBC';",
" dkLen = 32;",
" iv = forge.random.getBytesSync(16);",
" cipherFn = forge.aes.createEncryptionCipher;",
" break;",
" case '3des':",
" algorithm = 'DES-EDE3-CBC';",
" dkLen = 24;",
" iv = forge.random.getBytesSync(8);",
" cipherFn = forge.des.createEncryptionCipher;",
" break;",
" case 'des':",
" algorithm = 'DES-CBC';",
" dkLen = 8;",
" iv = forge.random.getBytesSync(8);",
" cipherFn = forge.des.createEncryptionCipher;",
" break;",
" default:",
" var error = new Error('Could not encrypt RSA private key; unsupported ' +",
" 'encryption algorithm \"' + options.algorithm + '\".');",
" error.algorithm = options.algorithm;",
" throw error;",
" }",
"",
" // encrypt private key using OpenSSL legacy key derivation",
" var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);",
" var cipher = cipherFn(dk);",
" cipher.start(iv);",
" cipher.update(asn1.toDer(pki.privateKeyToAsn1(rsaKey)));",
" cipher.finish();",
"",
" var msg = {",
" type: 'RSA PRIVATE KEY',",
" procType: {",
" version: '4',",
" type: 'ENCRYPTED'",
" },",
" dekInfo: {",
" algorithm: algorithm,",
" parameters: forge.util.bytesToHex(iv).toUpperCase()",
" },",
" body: cipher.output.getBytes()",
" };",
" return forge.pem.encode(msg);",
"};",
"",
"/**",
" * Decrypts an RSA private key.",
" *",
" * @param pem the PEM-formatted EncryptedPrivateKeyInfo to decrypt.",
" * @param password the password to use.",
" *",
" * @return the RSA key on success, null on failure.",
" */",
"pki.decryptRsaPrivateKey = function(pem, password) {",
" var rval = null;",
"",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'ENCRYPTED PRIVATE KEY' &&",
" msg.type !== 'PRIVATE KEY' &&",
" msg.type !== 'RSA PRIVATE KEY') {",
" var error = new Error('Could not convert private key from PEM; PEM header type ' +",
" 'is not \"ENCRYPTED PRIVATE KEY\", \"PRIVATE KEY\", or \"RSA PRIVATE KEY\".');",
" error.headerType = error;",
" throw error;",
" }",
"",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" var dkLen;",
" var cipherFn;",
" switch(msg.dekInfo.algorithm) {",
" case 'DES-CBC':",
" dkLen = 8;",
" cipherFn = forge.des.createDecryptionCipher;",
" break;",
" case 'DES-EDE3-CBC':",
" dkLen = 24;",
" cipherFn = forge.des.createDecryptionCipher;",
" break;",
" case 'AES-128-CBC':",
" dkLen = 16;",
" cipherFn = forge.aes.createDecryptionCipher;",
" break;",
" case 'AES-192-CBC':",
" dkLen = 24;",
" cipherFn = forge.aes.createDecryptionCipher;",
" break;",
" case 'AES-256-CBC':",
" dkLen = 32;",
" cipherFn = forge.aes.createDecryptionCipher;",
" break;",
" case 'RC2-40-CBC':",
" dkLen = 5;",
" cipherFn = function(key) {",
" return forge.rc2.createDecryptionCipher(key, 40);",
" };",
" break;",
" case 'RC2-64-CBC':",
" dkLen = 8;",
" cipherFn = function(key) {",
" return forge.rc2.createDecryptionCipher(key, 64);",
" };",
" break;",
" case 'RC2-128-CBC':",
" dkLen = 16;",
" cipherFn = function(key) {",
" return forge.rc2.createDecryptionCipher(key, 128);",
" };",
" break;",
" default:",
" var error = new Error('Could not decrypt private key; unsupported ' +",
" 'encryption algorithm \"' + msg.dekInfo.algorithm + '\".');",
" error.algorithm = msg.dekInfo.algorithm;",
" throw error;",
" }",
"",
" // use OpenSSL legacy key derivation",
" var iv = forge.util.hexToBytes(msg.dekInfo.parameters);",
" var dk = forge.pbe.opensslDeriveBytes(password, iv.substr(0, 8), dkLen);",
" var cipher = cipherFn(dk);",
" cipher.start(iv);",
" cipher.update(forge.util.createBuffer(msg.body));",
" if(cipher.finish()) {",
" rval = cipher.output.getBytes();",
" } else {",
" return rval;",
" }",
" } else {",
" rval = msg.body;",
" }",
"",
" if(msg.type === 'ENCRYPTED PRIVATE KEY') {",
" rval = pki.decryptPrivateKeyInfo(asn1.fromDer(rval), password);",
" } else {",
" // decryption already performed above",
" rval = asn1.fromDer(rval);",
" }",
"",
" if(rval !== null) {",
" rval = pki.privateKeyFromAsn1(rval);",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Derives a PKCS#12 key.",
" *",
" * @param password the password to derive the key material from, null or",
" * undefined for none.",
" * @param salt the salt, as a ByteBuffer, to use.",
" * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).",
" * @param iter the iteration count.",
" * @param n the number of bytes to derive from the password.",
" * @param md the message digest to use, defaults to SHA-1.",
" *",
" * @return a ByteBuffer with the bytes derived from the password.",
" */",
"pki.pbe.generatePkcs12Key = function(password, salt, id, iter, n, md) {",
" var j, l;",
"",
" if(typeof md === 'undefined' || md === null) {",
" if(!('sha1' in forge.md)) {",
" throw new Error('\"sha1\" hash algorithm unavailable.');",
" }",
" md = forge.md.sha1.create();",
" }",
"",
" var u = md.digestLength;",
" var v = md.blockLength;",
" var result = new forge.util.ByteBuffer();",
"",
" /* Convert password to Unicode byte buffer + trailing 0-byte. */",
" var passBuf = new forge.util.ByteBuffer();",
" if(password !== null && password !== undefined) {",
" for(l = 0; l < password.length; l++) {",
" passBuf.putInt16(password.charCodeAt(l));",
" }",
" passBuf.putInt16(0);",
" }",
"",
" /* Length of salt and password in BYTES. */",
" var p = passBuf.length();",
" var s = salt.length();",
"",
" /* 1. Construct a string, D (the \"diversifier\"), by concatenating",
" v copies of ID. */",
" var D = new forge.util.ByteBuffer();",
" D.fillWithByte(id, v);",
"",
" /* 2. Concatenate copies of the salt together to create a string S of length",
" v * ceil(s / v) bytes (the final copy of the salt may be trunacted",
" to create S).",
" Note that if the salt is the empty string, then so is S. */",
" var Slen = v * Math.ceil(s / v);",
" var S = new forge.util.ByteBuffer();",
" for(l = 0; l < Slen; l++) {",
" S.putByte(salt.at(l % s));",
" }",
"",
" /* 3. Concatenate copies of the password together to create a string P of",
" length v * ceil(p / v) bytes (the final copy of the password may be",
" truncated to create P).",
" Note that if the password is the empty string, then so is P. */",
" var Plen = v * Math.ceil(p / v);",
" var P = new forge.util.ByteBuffer();",
" for(l = 0; l < Plen; l++) {",
" P.putByte(passBuf.at(l % p));",
" }",
"",
" /* 4. Set I=S||P to be the concatenation of S and P. */",
" var I = S;",
" I.putBuffer(P);",
"",
" /* 5. Set c=ceil(n / u). */",
" var c = Math.ceil(n / u);",
"",
" /* 6. For i=1, 2, ..., c, do the following: */",
" for(var i = 1; i <= c; i++) {",
" /* a) Set Ai=H^r(D||I). (l.e. the rth hash of D||I, H(H(H(...H(D||I)))) */",
" var buf = new forge.util.ByteBuffer();",
" buf.putBytes(D.bytes());",
" buf.putBytes(I.bytes());",
" for(var round = 0; round < iter; round++) {",
" md.start();",
" md.update(buf.getBytes());",
" buf = md.digest();",
" }",
"",
" /* b) Concatenate copies of Ai to create a string B of length v bytes (the",
" final copy of Ai may be truncated to create B). */",
" var B = new forge.util.ByteBuffer();",
" for(l = 0; l < v; l++) {",
" B.putByte(buf.at(l % u));",
" }",
"",
" /* c) Treating I as a concatenation I0, I1, ..., Ik-1 of v-byte blocks,",
" where k=ceil(s / v) + ceil(p / v), modify I by setting",
" Ij=(Ij+B+1) mod 2v for each j. */",
" var k = Math.ceil(s / v) + Math.ceil(p / v);",
" var Inew = new forge.util.ByteBuffer();",
" for(j = 0; j < k; j++) {",
" var chunk = new forge.util.ByteBuffer(I.getBytes(v));",
" var x = 0x1ff;",
" for(l = B.length() - 1; l >= 0; l--) {",
" x = x >> 8;",
" x += B.at(l) + chunk.at(l);",
" chunk.setAt(l, x & 0xff);",
" }",
" Inew.putBuffer(chunk);",
" }",
" I = Inew;",
"",
" /* Add Ai to A. */",
" result.putBuffer(buf);",
" }",
"",
" result.truncate(result.length() - n);",
" return result;",
"};",
"",
"/**",
" * Get new Forge cipher object instance.",
" *",
" * @param oid the OID (in string notation).",
" * @param params the ASN.1 params object.",
" * @param password the password to decrypt with.",
" *",
" * @return new cipher object instance.",
" */",
"pki.pbe.getCipher = function(oid, params, password) {",
" switch(oid) {",
" case pki.oids['pkcs5PBES2']:",
" return pki.pbe.getCipherForPBES2(oid, params, password);",
"",
" case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:",
" case pki.oids['pbewithSHAAnd40BitRC2-CBC']:",
" return pki.pbe.getCipherForPKCS12PBE(oid, params, password);",
"",
" default:",
" var error = new Error('Cannot read encrypted PBE data block. Unsupported OID.');",
" error.oid = oid;",
" error.supportedOids = [",
" 'pkcs5PBES2',",
" 'pbeWithSHAAnd3-KeyTripleDES-CBC',",
" 'pbewithSHAAnd40BitRC2-CBC'",
" ];",
" throw error;",
" }",
"};",
"",
"/**",
" * Get new Forge cipher object instance according to PBES2 params block.",
" *",
" * The returned cipher instance is already started using the IV",
" * from PBES2 parameter block.",
" *",
" * @param oid the PKCS#5 PBKDF2 OID (in string notation).",
" * @param params the ASN.1 PBES2-params object.",
" * @param password the password to decrypt with.",
" *",
" * @return new cipher object instance.",
" */",
"pki.pbe.getCipherForPBES2 = function(oid, params, password) {",
" // get PBE params",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(params, PBES2AlgorithmsValidator, capture, errors)) {",
" var error = new Error('Cannot read password-based-encryption algorithm ' +",
" 'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" // check oids",
" oid = asn1.derToOid(capture.kdfOid);",
" if(oid !== pki.oids['pkcs5PBKDF2']) {",
" var error = new Error('Cannot read encrypted private key. ' +",
" 'Unsupported key derivation function OID.');",
" error.oid = oid;",
" error.supportedOids = ['pkcs5PBKDF2'];",
" throw error;",
" }",
" oid = asn1.derToOid(capture.encOid);",
" if(oid !== pki.oids['aes128-CBC'] &&",
" oid !== pki.oids['aes192-CBC'] &&",
" oid !== pki.oids['aes256-CBC'] &&",
" oid !== pki.oids['des-EDE3-CBC'] &&",
" oid !== pki.oids['desCBC']) {",
" var error = new Error('Cannot read encrypted private key. ' +",
" 'Unsupported encryption scheme OID.');",
" error.oid = oid;",
" error.supportedOids = [",
" 'aes128-CBC', 'aes192-CBC', 'aes256-CBC', 'des-EDE3-CBC', 'desCBC'];",
" throw error;",
" }",
"",
" // set PBE params",
" var salt = capture.kdfSalt;",
" var count = forge.util.createBuffer(capture.kdfIterationCount);",
" count = count.getInt(count.length() << 3);",
" var dkLen;",
" var cipherFn;",
" switch(pki.oids[oid]) {",
" case 'aes128-CBC':",
" dkLen = 16;",
" cipherFn = forge.aes.createDecryptionCipher;",
" break;",
" case 'aes192-CBC':",
" dkLen = 24;",
" cipherFn = forge.aes.createDecryptionCipher;",
" break;",
" case 'aes256-CBC':",
" dkLen = 32;",
" cipherFn = forge.aes.createDecryptionCipher;",
" break;",
" case 'des-EDE3-CBC':",
" dkLen = 24;",
" cipherFn = forge.des.createDecryptionCipher;",
" break;",
" case 'desCBC':",
" dkLen = 8;",
" cipherFn = forge.des.createDecryptionCipher;",
" break;",
" }",
"",
" // get PRF message digest",
" var md = prfOidToMessageDigest(capture.prfOid);",
"",
" // decrypt private key using pbe with chosen PRF and AES/DES",
" var dk = forge.pkcs5.pbkdf2(password, salt, count, dkLen, md);",
" var iv = capture.encIv;",
" var cipher = cipherFn(dk);",
" cipher.start(iv);",
"",
" return cipher;",
"};",
"",
"/**",
" * Get new Forge cipher object instance for PKCS#12 PBE.",
" *",
" * The returned cipher instance is already started using the key & IV",
" * derived from the provided password and PKCS#12 PBE salt.",
" *",
" * @param oid The PKCS#12 PBE OID (in string notation).",
" * @param params The ASN.1 PKCS#12 PBE-params object.",
" * @param password The password to decrypt with.",
" *",
" * @return the new cipher object instance.",
" */",
"pki.pbe.getCipherForPKCS12PBE = function(oid, params, password) {",
" // get PBE params",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(params, pkcs12PbeParamsValidator, capture, errors)) {",
" var error = new Error('Cannot read password-based-encryption algorithm ' +",
" 'parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" var salt = forge.util.createBuffer(capture.salt);",
" var count = forge.util.createBuffer(capture.iterations);",
" count = count.getInt(count.length() << 3);",
"",
" var dkLen, dIvLen, cipherFn;",
" switch(oid) {",
" case pki.oids['pbeWithSHAAnd3-KeyTripleDES-CBC']:",
" dkLen = 24;",
" dIvLen = 8;",
" cipherFn = forge.des.startDecrypting;",
" break;",
"",
" case pki.oids['pbewithSHAAnd40BitRC2-CBC']:",
" dkLen = 5;",
" dIvLen = 8;",
" cipherFn = function(key, iv) {",
" var cipher = forge.rc2.createDecryptionCipher(key, 40);",
" cipher.start(iv, null);",
" return cipher;",
" };",
" break;",
"",
" default:",
" var error = new Error('Cannot read PKCS #12 PBE data block. Unsupported OID.');",
" error.oid = oid;",
" throw error;",
" }",
"",
" // get PRF message digest",
" var md = prfOidToMessageDigest(capture.prfOid);",
" var key = pki.pbe.generatePkcs12Key(password, salt, 1, count, dkLen, md);",
" md.start();",
" var iv = pki.pbe.generatePkcs12Key(password, salt, 2, count, dIvLen, md);",
"",
" return cipherFn(key, iv);",
"};",
"",
"/**",
" * OpenSSL's legacy key derivation function.",
" *",
" * See: http://www.openssl.org/docs/crypto/EVP_BytesToKey.html",
" *",
" * @param password the password to derive the key from.",
" * @param salt the salt to use, null for none.",
" * @param dkLen the number of bytes needed for the derived key.",
" * @param [options] the options to use:",
" * [md] an optional message digest object to use.",
" */",
"pki.pbe.opensslDeriveBytes = function(password, salt, dkLen, md) {",
" if(typeof md === 'undefined' || md === null) {",
" if(!('md5' in forge.md)) {",
" throw new Error('\"md5\" hash algorithm unavailable.');",
" }",
" md = forge.md.md5.create();",
" }",
" if(salt === null) {",
" salt = '';",
" }",
" var digests = [hash(md, password + salt)];",
" for(var length = 16, i = 1; length < dkLen; ++i, length += 16) {",
" digests.push(hash(md, digests[i - 1] + password + salt));",
" }",
" return digests.join('').substr(0, dkLen);",
"};",
"",
"function hash(md, bytes) {",
" return md.start().update(bytes).digest().getBytes();",
"}",
"",
"function prfOidToMessageDigest(prfOid) {",
" // get PRF algorithm, default to SHA-1",
" var prfAlgorithm;",
" if(!prfOid) {",
" prfAlgorithm = 'hmacWithSHA1';",
" } else {",
" prfAlgorithm = pki.oids[asn1.derToOid(prfOid)];",
" if(!prfAlgorithm) {",
" var error = new Error('Unsupported PRF OID.');",
" error.oid = prfOid;",
" error.supported = [",
" 'hmacWithSHA1', 'hmacWithSHA224', 'hmacWithSHA256', 'hmacWithSHA384',",
" 'hmacWithSHA512'];",
" throw error;",
" }",
" }",
" return prfAlgorithmToMessageDigest(prfAlgorithm);",
"}",
"",
"function prfAlgorithmToMessageDigest(prfAlgorithm) {",
" var factory = forge.md;",
" switch(prfAlgorithm) {",
" case 'hmacWithSHA224':",
" factory = forge.md.sha512;",
" case 'hmacWithSHA1':",
" case 'hmacWithSHA256':",
" case 'hmacWithSHA384':",
" case 'hmacWithSHA512':",
" prfAlgorithm = prfAlgorithm.substr(8).toLowerCase();",
" break;",
" default:",
" var error = new Error('Unsupported PRF algorithm.');",
" error.algorithm = prfAlgorithm;",
" error.supported = [",
" 'hmacWithSHA1', 'hmacWithSHA224', 'hmacWithSHA256', 'hmacWithSHA384',",
" 'hmacWithSHA512'];",
" throw error;",
" }",
" if(!factory || !(prfAlgorithm in factory)) {",
" throw new Error('Unknown hash algorithm: ' + prfAlgorithm);",
" }",
" return factory[prfAlgorithm].create();",
"}",
"",
"function createPbkdf2Params(salt, countBytes, dkLen, prfAlgorithm) {",
" var params = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // salt",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, salt),",
" // iteration count",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" countBytes.getBytes())",
" ]);",
" // when PRF algorithm is not SHA-1 default, add key length and PRF algorithm",
" if(prfAlgorithm !== 'hmacWithSHA1') {",
" params.value.push(",
" // key length",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" forge.util.hexToBytes(dkLen.toString(16))),",
" // AlgorithmIdentifier",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids[prfAlgorithm]).getBytes()),",
" // parameters (null)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ]));",
" }",
" return params;",
"}",
"",
"",
"/***/ }),",
"/* 22 */",
"/***/ (function(module, exports) {",
"",
"/* (ignored) */",
"",
"/***/ }),",
"/* 23 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Secure Hash Algorithm with 256-bit digest (SHA-256) implementation.",
" *",
" * See FIPS 180-2 for details.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2015 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(4);",
"__webpack_require__(1);",
"",
"var sha256 = module.exports = forge.sha256 = forge.sha256 || {};",
"forge.md.sha256 = forge.md.algorithms.sha256 = sha256;",
"",
"/**",
" * Creates a SHA-256 message digest object.",
" *",
" * @return a message digest object.",
" */",
"sha256.create = function() {",
" // do initialization as necessary",
" if(!_initialized) {",
" _init();",
" }",
"",
" // SHA-256 state contains eight 32-bit integers",
" var _state = null;",
"",
" // input buffer",
" var _input = forge.util.createBuffer();",
"",
" // used for word storage",
" var _w = new Array(64);",
"",
" // message digest object",
" var md = {",
" algorithm: 'sha256',",
" blockLength: 64,",
" digestLength: 32,",
" // 56-bit length of message so far (does not including padding)",
" messageLength: 0,",
" // true message length",
" fullMessageLength: null,",
" // size of message length in bytes",
" messageLengthSize: 8",
" };",
"",
" /**",
" * Starts the digest.",
" *",
" * @return this digest object.",
" */",
" md.start = function() {",
" // up to 56-bit message length for convenience",
" md.messageLength = 0;",
"",
" // full message length (set md.messageLength64 for backwards-compatibility)",
" md.fullMessageLength = md.messageLength64 = [];",
" var int32s = md.messageLengthSize / 4;",
" for(var i = 0; i < int32s; ++i) {",
" md.fullMessageLength.push(0);",
" }",
" _input = forge.util.createBuffer();",
" _state = {",
" h0: 0x6A09E667,",
" h1: 0xBB67AE85,",
" h2: 0x3C6EF372,",
" h3: 0xA54FF53A,",
" h4: 0x510E527F,",
" h5: 0x9B05688C,",
" h6: 0x1F83D9AB,",
" h7: 0x5BE0CD19",
" };",
" return md;",
" };",
" // start digest automatically for first time",
" md.start();",
"",
" /**",
" * Updates the digest with the given message input. The given input can",
" * treated as raw input (no encoding will be applied) or an encoding of",
" * 'utf8' maybe given to encode the input using UTF-8.",
" *",
" * @param msg the message input to update with.",
" * @param encoding the encoding to use (default: 'raw', other: 'utf8').",
" *",
" * @return this digest object.",
" */",
" md.update = function(msg, encoding) {",
" if(encoding === 'utf8') {",
" msg = forge.util.encodeUtf8(msg);",
" }",
"",
" // update message length",
" var len = msg.length;",
" md.messageLength += len;",
" len = [(len / 0x100000000) >>> 0, len >>> 0];",
" for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {",
" md.fullMessageLength[i] += len[1];",
" len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);",
" md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;",
" len[0] = ((len[1] / 0x100000000) >>> 0);",
" }",
"",
" // add bytes to input buffer",
" _input.putBytes(msg);",
"",
" // process bytes",
" _update(_state, _w, _input);",
"",
" // compact input buffer every 2K or if empty",
" if(_input.read > 2048 || _input.length() === 0) {",
" _input.compact();",
" }",
"",
" return md;",
" };",
"",
" /**",
" * Produces the digest.",
" *",
" * @return a byte buffer containing the digest value.",
" */",
" md.digest = function() {",
" /* Note: Here we copy the remaining bytes in the input buffer and",
" add the appropriate SHA-256 padding. Then we do the final update",
" on a copy of the state so that if the user wants to get",
" intermediate digests they can do so. */",
"",
" /* Determine the number of bytes that must be added to the message",
" to ensure its length is congruent to 448 mod 512. In other words,",
" the data to be digested must be a multiple of 512 bits (or 128 bytes).",
" This data includes the message, some padding, and the length of the",
" message. Since the length of the message will be encoded as 8 bytes (64",
" bits), that means that the last segment of the data must have 56 bytes",
" (448 bits) of message and padding. Therefore, the length of the message",
" plus the padding must be congruent to 448 mod 512 because",
" 512 - 128 = 448.",
"",
" In order to fill up the message length it must be filled with",
" padding that begins with 1 bit followed by all 0 bits. Padding",
" must *always* be present, so if the message length is already",
" congruent to 448 mod 512, then 512 padding bits must be added. */",
"",
" var finalBlock = forge.util.createBuffer();",
" finalBlock.putBytes(_input.bytes());",
"",
" // compute remaining size to be digested (include message length size)",
" var remaining = (",
" md.fullMessageLength[md.fullMessageLength.length - 1] +",
" md.messageLengthSize);",
"",
" // add padding for overflow blockSize - overflow",
" // _padding starts with 1 byte with first bit is set (byte value 128), then",
" // there may be up to (blockSize - 1) other pad bytes",
" var overflow = remaining & (md.blockLength - 1);",
" finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));",
"",
" // serialize message length in bits in big-endian order; since length",
" // is stored in bytes we multiply by 8 and add carry from next int",
" var next, carry;",
" var bits = md.fullMessageLength[0] * 8;",
" for(var i = 0; i < md.fullMessageLength.length - 1; ++i) {",
" next = md.fullMessageLength[i + 1] * 8;",
" carry = (next / 0x100000000) >>> 0;",
" bits += carry;",
" finalBlock.putInt32(bits >>> 0);",
" bits = next >>> 0;",
" }",
" finalBlock.putInt32(bits);",
"",
" var s2 = {",
" h0: _state.h0,",
" h1: _state.h1,",
" h2: _state.h2,",
" h3: _state.h3,",
" h4: _state.h4,",
" h5: _state.h5,",
" h6: _state.h6,",
" h7: _state.h7",
" };",
" _update(s2, _w, finalBlock);",
" var rval = forge.util.createBuffer();",
" rval.putInt32(s2.h0);",
" rval.putInt32(s2.h1);",
" rval.putInt32(s2.h2);",
" rval.putInt32(s2.h3);",
" rval.putInt32(s2.h4);",
" rval.putInt32(s2.h5);",
" rval.putInt32(s2.h6);",
" rval.putInt32(s2.h7);",
" return rval;",
" };",
"",
" return md;",
"};",
"",
"// sha-256 padding bytes not initialized yet",
"var _padding = null;",
"var _initialized = false;",
"",
"// table of constants",
"var _k = null;",
"",
"/**",
" * Initializes the constant tables.",
" */",
"function _init() {",
" // create padding",
" _padding = String.fromCharCode(128);",
" _padding += forge.util.fillString(String.fromCharCode(0x00), 64);",
"",
" // create K table for SHA-256",
" _k = [",
" 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,",
" 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,",
" 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,",
" 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,",
" 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,",
" 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,",
" 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,",
" 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,",
" 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,",
" 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,",
" 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,",
" 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,",
" 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,",
" 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,",
" 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,",
" 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];",
"",
" // now initialized",
" _initialized = true;",
"}",
"",
"/**",
" * Updates a SHA-256 state with the given byte buffer.",
" *",
" * @param s the SHA-256 state to update.",
" * @param w the array to use to store words.",
" * @param bytes the byte buffer to update with.",
" */",
"function _update(s, w, bytes) {",
" // consume 512 bit (64 byte) chunks",
" var t1, t2, s0, s1, ch, maj, i, a, b, c, d, e, f, g, h;",
" var len = bytes.length();",
" while(len >= 64) {",
" // the w array will be populated with sixteen 32-bit big-endian words",
" // and then extended into 64 32-bit words according to SHA-256",
" for(i = 0; i < 16; ++i) {",
" w[i] = bytes.getInt32();",
" }",
" for(; i < 64; ++i) {",
" // XOR word 2 words ago rot right 17, rot right 19, shft right 10",
" t1 = w[i - 2];",
" t1 =",
" ((t1 >>> 17) | (t1 << 15)) ^",
" ((t1 >>> 19) | (t1 << 13)) ^",
" (t1 >>> 10);",
" // XOR word 15 words ago rot right 7, rot right 18, shft right 3",
" t2 = w[i - 15];",
" t2 =",
" ((t2 >>> 7) | (t2 << 25)) ^",
" ((t2 >>> 18) | (t2 << 14)) ^",
" (t2 >>> 3);",
" // sum(t1, word 7 ago, t2, word 16 ago) modulo 2^32",
" w[i] = (t1 + w[i - 7] + t2 + w[i - 16]) | 0;",
" }",
"",
" // initialize hash value for this chunk",
" a = s.h0;",
" b = s.h1;",
" c = s.h2;",
" d = s.h3;",
" e = s.h4;",
" f = s.h5;",
" g = s.h6;",
" h = s.h7;",
"",
" // round function",
" for(i = 0; i < 64; ++i) {",
" // Sum1(e)",
" s1 =",
" ((e >>> 6) | (e << 26)) ^",
" ((e >>> 11) | (e << 21)) ^",
" ((e >>> 25) | (e << 7));",
" // Ch(e, f, g) (optimized the same way as SHA-1)",
" ch = g ^ (e & (f ^ g));",
" // Sum0(a)",
" s0 =",
" ((a >>> 2) | (a << 30)) ^",
" ((a >>> 13) | (a << 19)) ^",
" ((a >>> 22) | (a << 10));",
" // Maj(a, b, c) (optimized the same way as SHA-1)",
" maj = (a & b) | (c & (a ^ b));",
"",
" // main algorithm",
" t1 = h + s1 + ch + _k[i] + w[i];",
" t2 = s0 + maj;",
" h = g;",
" g = f;",
" f = e;",
" // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug",
" // can't truncate with `| 0`",
" e = (d + t1) >>> 0;",
" d = c;",
" c = b;",
" b = a;",
" // `>>> 0` necessary to avoid iOS/Safari 10 optimization bug",
" // can't truncate with `| 0`",
" a = (t1 + t2) >>> 0;",
" }",
"",
" // update hash state",
" s.h0 = (s.h0 + a) | 0;",
" s.h1 = (s.h1 + b) | 0;",
" s.h2 = (s.h2 + c) | 0;",
" s.h3 = (s.h3 + d) | 0;",
" s.h4 = (s.h4 + e) | 0;",
" s.h5 = (s.h5 + f) | 0;",
" s.h6 = (s.h6 + g) | 0;",
" s.h7 = (s.h7 + h) | 0;",
" len -= 64;",
" }",
"}",
"",
"",
"/***/ }),",
"/* 24 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * A javascript implementation of a cryptographically-secure",
" * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is followed",
" * here though the use of SHA-256 is not enforced; when generating an",
" * a PRNG context, the hashing algorithm and block cipher used for",
" * the generator are specified via a plugin.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2010-2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"",
"var _crypto = null;",
"if(forge.util.isNodejs && !forge.options.usePureJavaScript &&",
" !process.versions['node-webkit']) {",
" _crypto = __webpack_require__(22);",
"}",
"",
"/* PRNG API */",
"var prng = module.exports = forge.prng = forge.prng || {};",
"",
"/**",
" * Creates a new PRNG context.",
" *",
" * A PRNG plugin must be passed in that will provide:",
" *",
" * 1. A function that initializes the key and seed of a PRNG context. It",
" * will be given a 16 byte key and a 16 byte seed. Any key expansion",
" * or transformation of the seed from a byte string into an array of",
" * integers (or similar) should be performed.",
" * 2. The cryptographic function used by the generator. It takes a key and",
" * a seed.",
" * 3. A seed increment function. It takes the seed and returns seed + 1.",
" * 4. An api to create a message digest.",
" *",
" * For an example, see random.js.",
" *",
" * @param plugin the PRNG plugin to use.",
" */",
"prng.create = function(plugin) {",
" var ctx = {",
" plugin: plugin,",
" key: null,",
" seed: null,",
" time: null,",
" // number of reseeds so far",
" reseeds: 0,",
" // amount of data generated so far",
" generated: 0,",
" // no initial key bytes",
" keyBytes: ''",
" };",
"",
" // create 32 entropy pools (each is a message digest)",
" var md = plugin.md;",
" var pools = new Array(32);",
" for(var i = 0; i < 32; ++i) {",
" pools[i] = md.create();",
" }",
" ctx.pools = pools;",
"",
" // entropy pools are written to cyclically, starting at index 0",
" ctx.pool = 0;",
"",
" /**",
" * Generates random bytes. The bytes may be generated synchronously or",
" * asynchronously. Web workers must use the asynchronous interface or",
" * else the behavior is undefined.",
" *",
" * @param count the number of random bytes to generate.",
" * @param [callback(err, bytes)] called once the operation completes.",
" *",
" * @return count random bytes as a string.",
" */",
" ctx.generate = function(count, callback) {",
" // do synchronously",
" if(!callback) {",
" return ctx.generateSync(count);",
" }",
"",
" // simple generator using counter-based CBC",
" var cipher = ctx.plugin.cipher;",
" var increment = ctx.plugin.increment;",
" var formatKey = ctx.plugin.formatKey;",
" var formatSeed = ctx.plugin.formatSeed;",
" var b = forge.util.createBuffer();",
"",
" // paranoid deviation from Fortuna:",
" // reset key for every request to protect previously",
" // generated random bytes should the key be discovered;",
" // there is no 100ms based reseeding because of this",
" // forced reseed for every `generate` call",
" ctx.key = null;",
"",
" generate();",
"",
" function generate(err) {",
" if(err) {",
" return callback(err);",
" }",
"",
" // sufficient bytes generated",
" if(b.length() >= count) {",
" return callback(null, b.getBytes(count));",
" }",
"",
" // if amount of data generated is greater than 1 MiB, trigger reseed",
" if(ctx.generated > 0xfffff) {",
" ctx.key = null;",
" }",
"",
" if(ctx.key === null) {",
" // prevent stack overflow",
" return forge.util.nextTick(function() {",
" _reseed(generate);",
" });",
" }",
"",
" // generate the random bytes",
" var bytes = cipher(ctx.key, ctx.seed);",
" ctx.generated += bytes.length;",
" b.putBytes(bytes);",
"",
" // generate bytes for a new key and seed",
" ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));",
" ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));",
"",
" forge.util.setImmediate(generate);",
" }",
" };",
"",
" /**",
" * Generates random bytes synchronously.",
" *",
" * @param count the number of random bytes to generate.",
" *",
" * @return count random bytes as a string.",
" */",
" ctx.generateSync = function(count) {",
" // simple generator using counter-based CBC",
" var cipher = ctx.plugin.cipher;",
" var increment = ctx.plugin.increment;",
" var formatKey = ctx.plugin.formatKey;",
" var formatSeed = ctx.plugin.formatSeed;",
"",
" // paranoid deviation from Fortuna:",
" // reset key for every request to protect previously",
" // generated random bytes should the key be discovered;",
" // there is no 100ms based reseeding because of this",
" // forced reseed for every `generateSync` call",
" ctx.key = null;",
"",
" var b = forge.util.createBuffer();",
" while(b.length() < count) {",
" // if amount of data generated is greater than 1 MiB, trigger reseed",
" if(ctx.generated > 0xfffff) {",
" ctx.key = null;",
" }",
"",
" if(ctx.key === null) {",
" _reseedSync();",
" }",
"",
" // generate the random bytes",
" var bytes = cipher(ctx.key, ctx.seed);",
" ctx.generated += bytes.length;",
" b.putBytes(bytes);",
"",
" // generate bytes for a new key and seed",
" ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));",
" ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));",
" }",
"",
" return b.getBytes(count);",
" };",
"",
" /**",
" * Private function that asynchronously reseeds a generator.",
" *",
" * @param callback(err) called once the operation completes.",
" */",
" function _reseed(callback) {",
" if(ctx.pools[0].messageLength >= 32) {",
" _seed();",
" return callback();",
" }",
" // not enough seed data...",
" var needed = (32 - ctx.pools[0].messageLength) << 5;",
" ctx.seedFile(needed, function(err, bytes) {",
" if(err) {",
" return callback(err);",
" }",
" ctx.collect(bytes);",
" _seed();",
" callback();",
" });",
" }",
"",
" /**",
" * Private function that synchronously reseeds a generator.",
" */",
" function _reseedSync() {",
" if(ctx.pools[0].messageLength >= 32) {",
" return _seed();",
" }",
" // not enough seed data...",
" var needed = (32 - ctx.pools[0].messageLength) << 5;",
" ctx.collect(ctx.seedFileSync(needed));",
" _seed();",
" }",
"",
" /**",
" * Private function that seeds a generator once enough bytes are available.",
" */",
" function _seed() {",
" // update reseed count",
" ctx.reseeds = (ctx.reseeds === 0xffffffff) ? 0 : ctx.reseeds + 1;",
"",
" // goal is to update `key` via:",
" // key = hash(key + s)",
" // where 's' is all collected entropy from selected pools, then...",
"",
" // create a plugin-based message digest",
" var md = ctx.plugin.md.create();",
"",
" // consume current key bytes",
" md.update(ctx.keyBytes);",
"",
" // digest the entropy of pools whose index k meet the",
" // condition 'n mod 2^k == 0' where n is the number of reseeds",
" var _2powK = 1;",
" for(var k = 0; k < 32; ++k) {",
" if(ctx.reseeds % _2powK === 0) {",
" md.update(ctx.pools[k].digest().getBytes());",
" ctx.pools[k].start();",
" }",
" _2powK = _2powK << 1;",
" }",
"",
" // get digest for key bytes",
" ctx.keyBytes = md.digest().getBytes();",
"",
" // paranoid deviation from Fortuna:",
" // update `seed` via `seed = hash(key)`",
" // instead of initializing to zero once and only",
" // ever incrementing it",
" md.start();",
" md.update(ctx.keyBytes);",
" var seedBytes = md.digest().getBytes();",
"",
" // update state",
" ctx.key = ctx.plugin.formatKey(ctx.keyBytes);",
" ctx.seed = ctx.plugin.formatSeed(seedBytes);",
" ctx.generated = 0;",
" }",
"",
" /**",
" * The built-in default seedFile. This seedFile is used when entropy",
" * is needed immediately.",
" *",
" * @param needed the number of bytes that are needed.",
" *",
" * @return the random bytes.",
" */",
" function defaultSeedFile(needed) {",
" // use window.crypto.getRandomValues strong source of entropy if available",
" var getRandomValues = null;",
" if(typeof window !== 'undefined') {",
" var _crypto = window.crypto || window.msCrypto;",
" if(_crypto && _crypto.getRandomValues) {",
" getRandomValues = function(arr) {",
" return _crypto.getRandomValues(arr);",
" };",
" }",
" }",
"",
" var b = forge.util.createBuffer();",
" if(getRandomValues) {",
" while(b.length() < needed) {",
" // max byte length is 65536 before QuotaExceededError is thrown",
" // http://www.w3.org/TR/WebCryptoAPI/#RandomSource-method-getRandomValues",
" var count = Math.max(1, Math.min(needed - b.length(), 65536) / 4);",
" var entropy = new Uint32Array(Math.floor(count));",
" try {",
" getRandomValues(entropy);",
" for(var i = 0; i < entropy.length; ++i) {",
" b.putInt32(entropy[i]);",
" }",
" } catch(e) {",
" /* only ignore QuotaExceededError */",
" if(!(typeof QuotaExceededError !== 'undefined' &&",
" e instanceof QuotaExceededError)) {",
" throw e;",
" }",
" }",
" }",
" }",
"",
" // be sad and add some weak random data",
" if(b.length() < needed) {",
" /* Draws from Park-Miller \"minimal standard\" 31 bit PRNG,",
" implemented with David G. Carta's optimization: with 32 bit math",
" and without division (Public Domain). */",
" var hi, lo, next;",
" var seed = Math.floor(Math.random() * 0x010000);",
" while(b.length() < needed) {",
" lo = 16807 * (seed & 0xFFFF);",
" hi = 16807 * (seed >> 16);",
" lo += (hi & 0x7FFF) << 16;",
" lo += hi >> 15;",
" lo = (lo & 0x7FFFFFFF) + (lo >> 31);",
" seed = lo & 0xFFFFFFFF;",
"",
" // consume lower 3 bytes of seed",
" for(var i = 0; i < 3; ++i) {",
" // throw in more pseudo random",
" next = seed >>> (i << 3);",
" next ^= Math.floor(Math.random() * 0x0100);",
" b.putByte(String.fromCharCode(next & 0xFF));",
" }",
" }",
" }",
"",
" return b.getBytes(needed);",
" }",
" // initialize seed file APIs",
" if(_crypto) {",
" // use nodejs async API",
" ctx.seedFile = function(needed, callback) {",
" _crypto.randomBytes(needed, function(err, bytes) {",
" if(err) {",
" return callback(err);",
" }",
" callback(null, bytes.toString());",
" });",
" };",
" // use nodejs sync API",
" ctx.seedFileSync = function(needed) {",
" return _crypto.randomBytes(needed).toString();",
" };",
" } else {",
" ctx.seedFile = function(needed, callback) {",
" try {",
" callback(null, defaultSeedFile(needed));",
" } catch(e) {",
" callback(e);",
" }",
" };",
" ctx.seedFileSync = defaultSeedFile;",
" }",
"",
" /**",
" * Adds entropy to a prng ctx's accumulator.",
" *",
" * @param bytes the bytes of entropy as a string.",
" */",
" ctx.collect = function(bytes) {",
" // iterate over pools distributing entropy cyclically",
" var count = bytes.length;",
" for(var i = 0; i < count; ++i) {",
" ctx.pools[ctx.pool].update(bytes.substr(i, 1));",
" ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;",
" }",
" };",
"",
" /**",
" * Collects an integer of n bits.",
" *",
" * @param i the integer entropy.",
" * @param n the number of bits in the integer.",
" */",
" ctx.collectInt = function(i, n) {",
" var bytes = '';",
" for(var x = 0; x < n; x += 8) {",
" bytes += String.fromCharCode((i >> x) & 0xFF);",
" }",
" ctx.collect(bytes);",
" };",
"",
" /**",
" * Registers a Web Worker to receive immediate entropy from the main thread.",
" * This method is required until Web Workers can access the native crypto",
" * API. This method should be called twice for each created worker, once in",
" * the main thread, and once in the worker itself.",
" *",
" * @param worker the worker to register.",
" */",
" ctx.registerWorker = function(worker) {",
" // worker receives random bytes",
" if(worker === self) {",
" ctx.seedFile = function(needed, callback) {",
" function listener(e) {",
" var data = e.data;",
" if(data.forge && data.forge.prng) {",
" self.removeEventListener('message', listener);",
" callback(data.forge.prng.err, data.forge.prng.bytes);",
" }",
" }",
" self.addEventListener('message', listener);",
" self.postMessage({forge: {prng: {needed: needed}}});",
" };",
" } else {",
" // main thread sends random bytes upon request",
" var listener = function(e) {",
" var data = e.data;",
" if(data.forge && data.forge.prng) {",
" ctx.seedFile(data.forge.prng.needed, function(err, bytes) {",
" worker.postMessage({forge: {prng: {err: err, bytes: bytes}}});",
" });",
" }",
" };",
" // TODO: do we need to remove the event listener when the worker dies?",
" worker.addEventListener('message', listener);",
" }",
" };",
"",
" return ctx;",
"};",
"",
"",
"/***/ }),",
"/* 25 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * RC2 implementation.",
" *",
" * @author Stefan Siegl",
" *",
" * Copyright (c) 2012 Stefan Siegl ",
" *",
" * Information on the RC2 cipher is available from RFC #2268,",
" * http://www.ietf.org/rfc/rfc2268.txt",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"",
"var piTable = [",
" 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,",
" 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,",
" 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,",
" 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,",
" 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,",
" 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,",
" 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,",
" 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,",
" 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,",
" 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,",
" 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,",
" 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,",
" 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,",
" 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,",
" 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,",
" 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad",
"];",
"",
"var s = [1, 2, 3, 5];",
"",
"/**",
" * Rotate a word left by given number of bits.",
" *",
" * Bits that are shifted out on the left are put back in on the right",
" * hand side.",
" *",
" * @param word The word to shift left.",
" * @param bits The number of bits to shift by.",
" * @return The rotated word.",
" */",
"var rol = function(word, bits) {",
" return ((word << bits) & 0xffff) | ((word & 0xffff) >> (16 - bits));",
"};",
"",
"/**",
" * Rotate a word right by given number of bits.",
" *",
" * Bits that are shifted out on the right are put back in on the left",
" * hand side.",
" *",
" * @param word The word to shift right.",
" * @param bits The number of bits to shift by.",
" * @return The rotated word.",
" */",
"var ror = function(word, bits) {",
" return ((word & 0xffff) >> bits) | ((word << (16 - bits)) & 0xffff);",
"};",
"",
"/* RC2 API */",
"module.exports = forge.rc2 = forge.rc2 || {};",
"",
"/**",
" * Perform RC2 key expansion as per RFC #2268, section 2.",
" *",
" * @param key variable-length user key (between 1 and 128 bytes)",
" * @param effKeyBits number of effective key bits (default: 128)",
" * @return the expanded RC2 key (ByteBuffer of 128 bytes)",
" */",
"forge.rc2.expandKey = function(key, effKeyBits) {",
" if(typeof key === 'string') {",
" key = forge.util.createBuffer(key);",
" }",
" effKeyBits = effKeyBits || 128;",
"",
" /* introduce variables that match the names used in RFC #2268 */",
" var L = key;",
" var T = key.length();",
" var T1 = effKeyBits;",
" var T8 = Math.ceil(T1 / 8);",
" var TM = 0xff >> (T1 & 0x07);",
" var i;",
"",
" for(i = T; i < 128; i++) {",
" L.putByte(piTable[(L.at(i - 1) + L.at(i - T)) & 0xff]);",
" }",
"",
" L.setAt(128 - T8, piTable[L.at(128 - T8) & TM]);",
"",
" for(i = 127 - T8; i >= 0; i--) {",
" L.setAt(i, piTable[L.at(i + 1) ^ L.at(i + T8)]);",
" }",
"",
" return L;",
"};",
"",
"/**",
" * Creates a RC2 cipher object.",
" *",
" * @param key the symmetric key to use (as base for key generation).",
" * @param bits the number of effective key bits.",
" * @param encrypt false for decryption, true for encryption.",
" *",
" * @return the cipher.",
" */",
"var createCipher = function(key, bits, encrypt) {",
" var _finish = false, _input = null, _output = null, _iv = null;",
" var mixRound, mashRound;",
" var i, j, K = [];",
"",
" /* Expand key and fill into K[] Array */",
" key = forge.rc2.expandKey(key, bits);",
" for(i = 0; i < 64; i++) {",
" K.push(key.getInt16Le());",
" }",
"",
" if(encrypt) {",
" /**",
" * Perform one mixing round \"in place\".",
" *",
" * @param R Array of four words to perform mixing on.",
" */",
" mixRound = function(R) {",
" for(i = 0; i < 4; i++) {",
" R[i] += K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +",
" ((~R[(i + 3) % 4]) & R[(i + 1) % 4]);",
" R[i] = rol(R[i], s[i]);",
" j++;",
" }",
" };",
"",
" /**",
" * Perform one mashing round \"in place\".",
" *",
" * @param R Array of four words to perform mashing on.",
" */",
" mashRound = function(R) {",
" for(i = 0; i < 4; i++) {",
" R[i] += K[R[(i + 3) % 4] & 63];",
" }",
" };",
" } else {",
" /**",
" * Perform one r-mixing round \"in place\".",
" *",
" * @param R Array of four words to perform mixing on.",
" */",
" mixRound = function(R) {",
" for(i = 3; i >= 0; i--) {",
" R[i] = ror(R[i], s[i]);",
" R[i] -= K[j] + (R[(i + 3) % 4] & R[(i + 2) % 4]) +",
" ((~R[(i + 3) % 4]) & R[(i + 1) % 4]);",
" j--;",
" }",
" };",
"",
" /**",
" * Perform one r-mashing round \"in place\".",
" *",
" * @param R Array of four words to perform mashing on.",
" */",
" mashRound = function(R) {",
" for(i = 3; i >= 0; i--) {",
" R[i] -= K[R[(i + 3) % 4] & 63];",
" }",
" };",
" }",
"",
" /**",
" * Run the specified cipher execution plan.",
" *",
" * This function takes four words from the input buffer, applies the IV on",
" * it (if requested) and runs the provided execution plan.",
" *",
" * The plan must be put together in form of a array of arrays. Where the",
" * outer one is simply a list of steps to perform and the inner one needs",
" * to have two elements: the first one telling how many rounds to perform,",
" * the second one telling what to do (i.e. the function to call).",
" *",
" * @param {Array} plan The plan to execute.",
" */",
" var runPlan = function(plan) {",
" var R = [];",
"",
" /* Get data from input buffer and fill the four words into R */",
" for(i = 0; i < 4; i++) {",
" var val = _input.getInt16Le();",
"",
" if(_iv !== null) {",
" if(encrypt) {",
" /* We're encrypting, apply the IV first. */",
" val ^= _iv.getInt16Le();",
" } else {",
" /* We're decryption, keep cipher text for next block. */",
" _iv.putInt16Le(val);",
" }",
" }",
"",
" R.push(val & 0xffff);",
" }",
"",
" /* Reset global \"j\" variable as per spec. */",
" j = encrypt ? 0 : 63;",
"",
" /* Run execution plan. */",
" for(var ptr = 0; ptr < plan.length; ptr++) {",
" for(var ctr = 0; ctr < plan[ptr][0]; ctr++) {",
" plan[ptr][1](R);",
" }",
" }",
"",
" /* Write back result to output buffer. */",
" for(i = 0; i < 4; i++) {",
" if(_iv !== null) {",
" if(encrypt) {",
" /* We're encrypting in CBC-mode, feed back encrypted bytes into",
" IV buffer to carry it forward to next block. */",
" _iv.putInt16Le(R[i]);",
" } else {",
" R[i] ^= _iv.getInt16Le();",
" }",
" }",
"",
" _output.putInt16Le(R[i]);",
" }",
" };",
"",
" /* Create cipher object */",
" var cipher = null;",
" cipher = {",
" /**",
" * Starts or restarts the encryption or decryption process, whichever",
" * was previously configured.",
" *",
" * To use the cipher in CBC mode, iv may be given either as a string",
" * of bytes, or as a byte buffer. For ECB mode, give null as iv.",
" *",
" * @param iv the initialization vector to use, null for ECB mode.",
" * @param output the output the buffer to write to, null to create one.",
" */",
" start: function(iv, output) {",
" if(iv) {",
" /* CBC mode */",
" if(typeof iv === 'string') {",
" iv = forge.util.createBuffer(iv);",
" }",
" }",
"",
" _finish = false;",
" _input = forge.util.createBuffer();",
" _output = output || new forge.util.createBuffer();",
" _iv = iv;",
"",
" cipher.output = _output;",
" },",
"",
" /**",
" * Updates the next block.",
" *",
" * @param input the buffer to read from.",
" */",
" update: function(input) {",
" if(!_finish) {",
" // not finishing, so fill the input buffer with more input",
" _input.putBuffer(input);",
" }",
"",
" while(_input.length() >= 8) {",
" runPlan([",
" [ 5, mixRound ],",
" [ 1, mashRound ],",
" [ 6, mixRound ],",
" [ 1, mashRound ],",
" [ 5, mixRound ]",
" ]);",
" }",
" },",
"",
" /**",
" * Finishes encrypting or decrypting.",
" *",
" * @param pad a padding function to use, null for PKCS#7 padding,",
" * signature(blockSize, buffer, decrypt).",
" *",
" * @return true if successful, false on error.",
" */",
" finish: function(pad) {",
" var rval = true;",
"",
" if(encrypt) {",
" if(pad) {",
" rval = pad(8, _input, !encrypt);",
" } else {",
" // add PKCS#7 padding to block (each pad byte is the",
" // value of the number of pad bytes)",
" var padding = (_input.length() === 8) ? 8 : (8 - _input.length());",
" _input.fillWithByte(padding, padding);",
" }",
" }",
"",
" if(rval) {",
" // do final update",
" _finish = true;",
" cipher.update();",
" }",
"",
" if(!encrypt) {",
" // check for error: input data not a multiple of block size",
" rval = (_input.length() === 0);",
" if(rval) {",
" if(pad) {",
" rval = pad(8, _output, !encrypt);",
" } else {",
" // ensure padding byte count is valid",
" var len = _output.length();",
" var count = _output.at(len - 1);",
"",
" if(count > len) {",
" rval = false;",
" } else {",
" // trim off padding bytes",
" _output.truncate(count);",
" }",
" }",
" }",
" }",
"",
" return rval;",
" }",
" };",
"",
" return cipher;",
"};",
"",
"/**",
" * Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the",
" * given symmetric key. The output will be stored in the 'output' member",
" * of the returned cipher.",
" *",
" * The key and iv may be given as a string of bytes or a byte buffer.",
" * The cipher is initialized to use 128 effective key bits.",
" *",
" * @param key the symmetric key to use.",
" * @param iv the initialization vector to use.",
" * @param output the buffer to write to, null to create one.",
" *",
" * @return the cipher.",
" */",
"forge.rc2.startEncrypting = function(key, iv, output) {",
" var cipher = forge.rc2.createEncryptionCipher(key, 128);",
" cipher.start(iv, output);",
" return cipher;",
"};",
"",
"/**",
" * Creates an RC2 cipher object to encrypt data in ECB or CBC mode using the",
" * given symmetric key.",
" *",
" * The key may be given as a string of bytes or a byte buffer.",
" *",
" * To start encrypting call start() on the cipher with an iv and optional",
" * output buffer.",
" *",
" * @param key the symmetric key to use.",
" *",
" * @return the cipher.",
" */",
"forge.rc2.createEncryptionCipher = function(key, bits) {",
" return createCipher(key, bits, true);",
"};",
"",
"/**",
" * Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the",
" * given symmetric key. The output will be stored in the 'output' member",
" * of the returned cipher.",
" *",
" * The key and iv may be given as a string of bytes or a byte buffer.",
" * The cipher is initialized to use 128 effective key bits.",
" *",
" * @param key the symmetric key to use.",
" * @param iv the initialization vector to use.",
" * @param output the buffer to write to, null to create one.",
" *",
" * @return the cipher.",
" */",
"forge.rc2.startDecrypting = function(key, iv, output) {",
" var cipher = forge.rc2.createDecryptionCipher(key, 128);",
" cipher.start(iv, output);",
" return cipher;",
"};",
"",
"/**",
" * Creates an RC2 cipher object to decrypt data in ECB or CBC mode using the",
" * given symmetric key.",
" *",
" * The key may be given as a string of bytes or a byte buffer.",
" *",
" * To start decrypting call start() on the cipher with an iv and optional",
" * output buffer.",
" *",
" * @param key the symmetric key to use.",
" *",
" * @return the cipher.",
" */",
"forge.rc2.createDecryptionCipher = function(key, bits) {",
" return createCipher(key, bits, false);",
"};",
"",
"",
"/***/ }),",
"/* 26 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Partial implementation of PKCS#1 v2.2: RSA-OEAP",
" *",
" * Modified but based on the following MIT and BSD licensed code:",
" *",
" * https://github.com/kjur/jsjws/blob/master/rsa.js:",
" *",
" * The 'jsjws'(JSON Web Signature JavaScript Library) License",
" *",
" * Copyright (c) 2012 Kenji Urushima",
" *",
" * Permission is hereby granted, free of charge, to any person obtaining a copy",
" * of this software and associated documentation files (the \"Software\"), to deal",
" * in the Software without restriction, including without limitation the rights",
" * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
" * copies of the Software, and to permit persons to whom the Software is",
" * furnished to do so, subject to the following conditions:",
" *",
" * The above copyright notice and this permission notice shall be included in",
" * all copies or substantial portions of the Software.",
" *",
" * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
" * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
" * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
" * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
" * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
" * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN",
" * THE SOFTWARE.",
" *",
" * http://webrsa.cvs.sourceforge.net/viewvc/webrsa/Client/RSAES-OAEP.js?content-type=text%2Fplain:",
" *",
" * RSAES-OAEP.js",
" * $Id: RSAES-OAEP.js,v 1.1.1.1 2003/03/19 15:37:20 ellispritchard Exp $",
" * JavaScript Implementation of PKCS #1 v2.1 RSA CRYPTOGRAPHY STANDARD (RSA Laboratories, June 14, 2002)",
" * Copyright (C) Ellis Pritchard, Guardian Unlimited 2003.",
" * Contact: ellis@nukinetics.com",
" * Distributed under the BSD License.",
" *",
" * Official documentation: http://www.rsa.com/rsalabs/node.asp?id=2125",
" *",
" * @author Evan Jones (http://evanjones.ca/)",
" * @author Dave Longley",
" *",
" * Copyright (c) 2013-2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"__webpack_require__(2);",
"__webpack_require__(9);",
"",
"// shortcut for PKCS#1 API",
"var pkcs1 = module.exports = forge.pkcs1 = forge.pkcs1 || {};",
"",
"/**",
" * Encode the given RSAES-OAEP message (M) using key, with optional label (L)",
" * and seed.",
" *",
" * This method does not perform RSA encryption, it only encodes the message",
" * using RSAES-OAEP.",
" *",
" * @param key the RSA key to use.",
" * @param message the message to encode.",
" * @param options the options to use:",
" * label an optional label to use.",
" * seed the seed to use.",
" * md the message digest object to use, undefined for SHA-1.",
" * mgf1 optional mgf1 parameters:",
" * md the message digest object to use for MGF1.",
" *",
" * @return the encoded message bytes.",
" */",
"pkcs1.encode_rsa_oaep = function(key, message, options) {",
" // parse arguments",
" var label;",
" var seed;",
" var md;",
" var mgf1Md;",
" // legacy args (label, seed, md)",
" if(typeof options === 'string') {",
" label = options;",
" seed = arguments[3] || undefined;",
" md = arguments[4] || undefined;",
" } else if(options) {",
" label = options.label || undefined;",
" seed = options.seed || undefined;",
" md = options.md || undefined;",
" if(options.mgf1 && options.mgf1.md) {",
" mgf1Md = options.mgf1.md;",
" }",
" }",
"",
" // default OAEP to SHA-1 message digest",
" if(!md) {",
" md = forge.md.sha1.create();",
" } else {",
" md.start();",
" }",
"",
" // default MGF-1 to same as OAEP",
" if(!mgf1Md) {",
" mgf1Md = md;",
" }",
"",
" // compute length in bytes and check output",
" var keyLength = Math.ceil(key.n.bitLength() / 8);",
" var maxLength = keyLength - 2 * md.digestLength - 2;",
" if(message.length > maxLength) {",
" var error = new Error('RSAES-OAEP input message length is too long.');",
" error.length = message.length;",
" error.maxLength = maxLength;",
" throw error;",
" }",
"",
" if(!label) {",
" label = '';",
" }",
" md.update(label, 'raw');",
" var lHash = md.digest();",
"",
" var PS = '';",
" var PS_length = maxLength - message.length;",
" for (var i = 0; i < PS_length; i++) {",
" PS += '\\x00';",
" }",
"",
" var DB = lHash.getBytes() + PS + '\\x01' + message;",
"",
" if(!seed) {",
" seed = forge.random.getBytes(md.digestLength);",
" } else if(seed.length !== md.digestLength) {",
" var error = new Error('Invalid RSAES-OAEP seed. The seed length must ' +",
" 'match the digest length.');",
" error.seedLength = seed.length;",
" error.digestLength = md.digestLength;",
" throw error;",
" }",
"",
" var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, mgf1Md);",
" var maskedDB = forge.util.xorBytes(DB, dbMask, DB.length);",
"",
" var seedMask = rsa_mgf1(maskedDB, md.digestLength, mgf1Md);",
" var maskedSeed = forge.util.xorBytes(seed, seedMask, seed.length);",
"",
" // return encoded message",
" return '\\x00' + maskedSeed + maskedDB;",
"};",
"",
"/**",
" * Decode the given RSAES-OAEP encoded message (EM) using key, with optional",
" * label (L).",
" *",
" * This method does not perform RSA decryption, it only decodes the message",
" * using RSAES-OAEP.",
" *",
" * @param key the RSA key to use.",
" * @param em the encoded message to decode.",
" * @param options the options to use:",
" * label an optional label to use.",
" * md the message digest object to use for OAEP, undefined for SHA-1.",
" * mgf1 optional mgf1 parameters:",
" * md the message digest object to use for MGF1.",
" *",
" * @return the decoded message bytes.",
" */",
"pkcs1.decode_rsa_oaep = function(key, em, options) {",
" // parse args",
" var label;",
" var md;",
" var mgf1Md;",
" // legacy args",
" if(typeof options === 'string') {",
" label = options;",
" md = arguments[3] || undefined;",
" } else if(options) {",
" label = options.label || undefined;",
" md = options.md || undefined;",
" if(options.mgf1 && options.mgf1.md) {",
" mgf1Md = options.mgf1.md;",
" }",
" }",
"",
" // compute length in bytes",
" var keyLength = Math.ceil(key.n.bitLength() / 8);",
"",
" if(em.length !== keyLength) {",
" var error = new Error('RSAES-OAEP encoded message length is invalid.');",
" error.length = em.length;",
" error.expectedLength = keyLength;",
" throw error;",
" }",
"",
" // default OAEP to SHA-1 message digest",
" if(md === undefined) {",
" md = forge.md.sha1.create();",
" } else {",
" md.start();",
" }",
"",
" // default MGF-1 to same as OAEP",
" if(!mgf1Md) {",
" mgf1Md = md;",
" }",
"",
" if(keyLength < 2 * md.digestLength + 2) {",
" throw new Error('RSAES-OAEP key is too short for the hash function.');",
" }",
"",
" if(!label) {",
" label = '';",
" }",
" md.update(label, 'raw');",
" var lHash = md.digest().getBytes();",
"",
" // split the message into its parts",
" var y = em.charAt(0);",
" var maskedSeed = em.substring(1, md.digestLength + 1);",
" var maskedDB = em.substring(1 + md.digestLength);",
"",
" var seedMask = rsa_mgf1(maskedDB, md.digestLength, mgf1Md);",
" var seed = forge.util.xorBytes(maskedSeed, seedMask, maskedSeed.length);",
"",
" var dbMask = rsa_mgf1(seed, keyLength - md.digestLength - 1, mgf1Md);",
" var db = forge.util.xorBytes(maskedDB, dbMask, maskedDB.length);",
"",
" var lHashPrime = db.substring(0, md.digestLength);",
"",
" // constant time check that all values match what is expected",
" var error = (y !== '\\x00');",
"",
" // constant time check lHash vs lHashPrime",
" for(var i = 0; i < md.digestLength; ++i) {",
" error |= (lHash.charAt(i) !== lHashPrime.charAt(i));",
" }",
"",
" // \"constant time\" find the 0x1 byte separating the padding (zeros) from the",
" // message",
" // TODO: It must be possible to do this in a better/smarter way?",
" var in_ps = 1;",
" var index = md.digestLength;",
" for(var j = md.digestLength; j < db.length; j++) {",
" var code = db.charCodeAt(j);",
"",
" var is_0 = (code & 0x1) ^ 0x1;",
"",
" // non-zero if not 0 or 1 in the ps section",
" var error_mask = in_ps ? 0xfffe : 0x0000;",
" error |= (code & error_mask);",
"",
" // latch in_ps to zero after we find 0x1",
" in_ps = in_ps & is_0;",
" index += in_ps;",
" }",
"",
" if(error || db.charCodeAt(index) !== 0x1) {",
" throw new Error('Invalid RSAES-OAEP padding.');",
" }",
"",
" return db.substring(index + 1);",
"};",
"",
"function rsa_mgf1(seed, maskLength, hash) {",
" // default to SHA-1 message digest",
" if(!hash) {",
" hash = forge.md.sha1.create();",
" }",
" var t = '';",
" var count = Math.ceil(maskLength / hash.digestLength);",
" for(var i = 0; i < count; ++i) {",
" var c = String.fromCharCode(",
" (i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);",
" hash.start();",
" hash.update(seed + c);",
" t += hash.digest().getBytes();",
" }",
" return t.substring(0, maskLength);",
"}",
"",
"",
"/***/ }),",
"/* 27 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Prime number generation API.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"__webpack_require__(12);",
"__webpack_require__(2);",
"",
"(function() {",
"",
"// forge.prime already defined",
"if(forge.prime) {",
" module.exports = forge.prime;",
" return;",
"}",
"",
"/* PRIME API */",
"var prime = module.exports = forge.prime = forge.prime || {};",
"",
"var BigInteger = forge.jsbn.BigInteger;",
"",
"// primes are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29",
"var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2];",
"var THIRTY = new BigInteger(null);",
"THIRTY.fromInt(30);",
"var op_or = function(x, y) {return x|y;};",
"",
"/**",
" * Generates a random probable prime with the given number of bits.",
" *",
" * Alternative algorithms can be specified by name as a string or as an",
" * object with custom options like so:",
" *",
" * {",
" * name: 'PRIMEINC',",
" * options: {",
" * maxBlockTime: ,",
" * millerRabinTests: ,",
" * workerScript: ,",
" * workers: .",
" * workLoad: the size of the work load, ie: number of possible prime",
" * numbers for each web worker to check per work assignment,",
" * (default: 100).",
" * }",
" * }",
" *",
" * @param bits the number of bits for the prime number.",
" * @param options the options to use.",
" * [algorithm] the algorithm to use (default: 'PRIMEINC').",
" * [prng] a custom crypto-secure pseudo-random number generator to use,",
" * that must define \"getBytesSync\".",
" *",
" * @return callback(err, num) called once the operation completes.",
" */",
"prime.generateProbablePrime = function(bits, options, callback) {",
" if(typeof options === 'function') {",
" callback = options;",
" options = {};",
" }",
" options = options || {};",
"",
" // default to PRIMEINC algorithm",
" var algorithm = options.algorithm || 'PRIMEINC';",
" if(typeof algorithm === 'string') {",
" algorithm = {name: algorithm};",
" }",
" algorithm.options = algorithm.options || {};",
"",
" // create prng with api that matches BigInteger secure random",
" var prng = options.prng || forge.random;",
" var rng = {",
" // x is an array to fill with bytes",
" nextBytes: function(x) {",
" var b = prng.getBytesSync(x.length);",
" for(var i = 0; i < x.length; ++i) {",
" x[i] = b.charCodeAt(i);",
" }",
" }",
" };",
"",
" if(algorithm.name === 'PRIMEINC') {",
" return primeincFindPrime(bits, rng, algorithm.options, callback);",
" }",
"",
" throw new Error('Invalid prime generation algorithm: ' + algorithm.name);",
"};",
"",
"function primeincFindPrime(bits, rng, options, callback) {",
" if('workers' in options) {",
" return primeincFindPrimeWithWorkers(bits, rng, options, callback);",
" }",
" return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);",
"}",
"",
"function primeincFindPrimeWithoutWorkers(bits, rng, options, callback) {",
" // initialize random number",
" var num = generateRandom(bits, rng);",
"",
" /* Note: All primes are of the form 30k+i for i < 30 and gcd(30, i)=1. The",
" number we are given is always aligned at 30k + 1. Each time the number is",
" determined not to be prime we add to get to the next 'i', eg: if the number",
" was at 30k + 1 we add 6. */",
" var deltaIdx = 0;",
"",
" // get required number of MR tests",
" var mrTests = getMillerRabinTests(num.bitLength());",
" if('millerRabinTests' in options) {",
" mrTests = options.millerRabinTests;",
" }",
"",
" // find prime nearest to 'num' for maxBlockTime ms",
" // 10 ms gives 5ms of leeway for other calculations before dropping",
" // below 60fps (1000/60 == 16.67), but in reality, the number will",
" // likely be higher due to an 'atomic' big int modPow",
" var maxBlockTime = 10;",
" if('maxBlockTime' in options) {",
" maxBlockTime = options.maxBlockTime;",
" }",
"",
" _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);",
"}",
"",
"function _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback) {",
" var start = +new Date();",
" do {",
" // overflow, regenerate random number",
" if(num.bitLength() > bits) {",
" num = generateRandom(bits, rng);",
" }",
" // do primality test",
" if(num.isProbablePrime(mrTests)) {",
" return callback(null, num);",
" }",
" // get next potential prime",
" num.dAddOffset(GCD_30_DELTA[deltaIdx++ % 8], 0);",
" } while(maxBlockTime < 0 || (+new Date() - start < maxBlockTime));",
"",
" // keep trying later",
" forge.util.setImmediate(function() {",
" _primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback);",
" });",
"}",
"",
"// NOTE: This algorithm is indeterminate in nature because workers",
"// run in parallel looking at different segments of numbers. Even if this",
"// algorithm is run twice with the same input from a predictable RNG, it",
"// may produce different outputs.",
"function primeincFindPrimeWithWorkers(bits, rng, options, callback) {",
" // web workers unavailable",
" if(typeof Worker === 'undefined') {",
" return primeincFindPrimeWithoutWorkers(bits, rng, options, callback);",
" }",
"",
" // initialize random number",
" var num = generateRandom(bits, rng);",
"",
" // use web workers to generate keys",
" var numWorkers = options.workers;",
" var workLoad = options.workLoad || 100;",
" var range = workLoad * 30 / 8;",
" var workerScript = options.workerScript || 'forge/prime.worker.js';",
" if(numWorkers === -1) {",
" return forge.util.estimateCores(function(err, cores) {",
" if(err) {",
" // default to 2",
" cores = 2;",
" }",
" numWorkers = cores - 1;",
" generate();",
" });",
" }",
" generate();",
"",
" function generate() {",
" // require at least 1 worker",
" numWorkers = Math.max(1, numWorkers);",
"",
" // TODO: consider optimizing by starting workers outside getPrime() ...",
" // note that in order to clean up they will have to be made internally",
" // asynchronous which may actually be slower",
"",
" // start workers immediately",
" var workers = [];",
" for(var i = 0; i < numWorkers; ++i) {",
" // FIXME: fix path or use blob URLs",
" workers[i] = new Worker(workerScript);",
" }",
" var running = numWorkers;",
"",
" // listen for requests from workers and assign ranges to find prime",
" for(var i = 0; i < numWorkers; ++i) {",
" workers[i].addEventListener('message', workerMessage);",
" }",
"",
" /* Note: The distribution of random numbers is unknown. Therefore, each",
" web worker is continuously allocated a range of numbers to check for a",
" random number until one is found.",
"",
" Every 30 numbers will be checked just 8 times, because prime numbers",
" have the form:",
"",
" 30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)",
"",
" Therefore, if we want a web worker to run N checks before asking for",
" a new range of numbers, each range must contain N*30/8 numbers.",
"",
" For 100 checks (workLoad), this is a range of 375. */",
"",
" var found = false;",
" function workerMessage(e) {",
" // ignore message, prime already found",
" if(found) {",
" return;",
" }",
"",
" --running;",
" var data = e.data;",
" if(data.found) {",
" // terminate all workers",
" for(var i = 0; i < workers.length; ++i) {",
" workers[i].terminate();",
" }",
" found = true;",
" return callback(null, new BigInteger(data.prime, 16));",
" }",
"",
" // overflow, regenerate random number",
" if(num.bitLength() > bits) {",
" num = generateRandom(bits, rng);",
" }",
"",
" // assign new range to check",
" var hex = num.toString(16);",
"",
" // start prime search",
" e.target.postMessage({",
" hex: hex,",
" workLoad: workLoad",
" });",
"",
" num.dAddOffset(range, 0);",
" }",
" }",
"}",
"",
"/**",
" * Generates a random number using the given number of bits and RNG.",
" *",
" * @param bits the number of bits for the number.",
" * @param rng the random number generator to use.",
" *",
" * @return the random number.",
" */",
"function generateRandom(bits, rng) {",
" var num = new BigInteger(bits, rng);",
" // force MSB set",
" var bits1 = bits - 1;",
" if(!num.testBit(bits1)) {",
" num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);",
" }",
" // align number on 30k+1 boundary",
" num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);",
" return num;",
"}",
"",
"/**",
" * Returns the required number of Miller-Rabin tests to generate a",
" * prime with an error probability of (1/2)^80.",
" *",
" * See Handbook of Applied Cryptography Chapter 4, Table 4.4.",
" *",
" * @param bits the bit size.",
" *",
" * @return the required number of iterations.",
" */",
"function getMillerRabinTests(bits) {",
" if(bits <= 100) return 27;",
" if(bits <= 150) return 18;",
" if(bits <= 200) return 15;",
" if(bits <= 250) return 12;",
" if(bits <= 300) return 9;",
" if(bits <= 350) return 8;",
" if(bits <= 400) return 7;",
" if(bits <= 500) return 6;",
" if(bits <= 600) return 5;",
" if(bits <= 800) return 4;",
" if(bits <= 1250) return 3;",
" return 2;",
"}",
"",
"})();",
"",
"",
"/***/ }),",
"/* 28 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of PKCS#12.",
" *",
" * @author Dave Longley",
" * @author Stefan Siegl ",
" *",
" * Copyright (c) 2010-2014 Digital Bazaar, Inc.",
" * Copyright (c) 2012 Stefan Siegl ",
" *",
" * The ASN.1 representation of PKCS#12 is as follows",
" * (see ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12-tc1.pdf for details)",
" *",
" * PFX ::= SEQUENCE {",
" * version INTEGER {v3(3)}(v3,...),",
" * authSafe ContentInfo,",
" * macData MacData OPTIONAL",
" * }",
" *",
" * MacData ::= SEQUENCE {",
" * mac DigestInfo,",
" * macSalt OCTET STRING,",
" * iterations INTEGER DEFAULT 1",
" * }",
" * Note: The iterations default is for historical reasons and its use is",
" * deprecated. A higher value, like 1024, is recommended.",
" *",
" * DigestInfo is defined in PKCS#7 as follows:",
" *",
" * DigestInfo ::= SEQUENCE {",
" * digestAlgorithm DigestAlgorithmIdentifier,",
" * digest Digest",
" * }",
" *",
" * DigestAlgorithmIdentifier ::= AlgorithmIdentifier",
" *",
" * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters",
" * for the algorithm, if any. In the case of SHA1 there is none.",
" *",
" * AlgorithmIdentifer ::= SEQUENCE {",
" * algorithm OBJECT IDENTIFIER,",
" * parameters ANY DEFINED BY algorithm OPTIONAL",
" * }",
" *",
" * Digest ::= OCTET STRING",
" *",
" *",
" * ContentInfo ::= SEQUENCE {",
" * contentType ContentType,",
" * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL",
" * }",
" *",
" * ContentType ::= OBJECT IDENTIFIER",
" *",
" * AuthenticatedSafe ::= SEQUENCE OF ContentInfo",
" * -- Data if unencrypted",
" * -- EncryptedData if password-encrypted",
" * -- EnvelopedData if public key-encrypted",
" *",
" *",
" * SafeContents ::= SEQUENCE OF SafeBag",
" *",
" * SafeBag ::= SEQUENCE {",
" * bagId BAG-TYPE.&id ({PKCS12BagSet})",
" * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),",
" * bagAttributes SET OF PKCS12Attribute OPTIONAL",
" * }",
" *",
" * PKCS12Attribute ::= SEQUENCE {",
" * attrId ATTRIBUTE.&id ({PKCS12AttrSet}),",
" * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})",
" * } -- This type is compatible with the X.500 type ’Attribute’",
" *",
" * PKCS12AttrSet ATTRIBUTE ::= {",
" * friendlyName | -- from PKCS #9",
" * localKeyId, -- from PKCS #9",
" * ... -- Other attributes are allowed",
" * }",
" *",
" * CertBag ::= SEQUENCE {",
" * certId BAG-TYPE.&id ({CertTypes}),",
" * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})",
" * }",
" *",
" * x509Certificate BAG-TYPE ::= {OCTET STRING IDENTIFIED BY {certTypes 1}}",
" * -- DER-encoded X.509 certificate stored in OCTET STRING",
" *",
" * sdsiCertificate BAG-TYPE ::= {IA5String IDENTIFIED BY {certTypes 2}}",
" * -- Base64-encoded SDSI certificate stored in IA5String",
" *",
" * CertTypes BAG-TYPE ::= {",
" * x509Certificate |",
" * sdsiCertificate,",
" * ... -- For future extensions",
" * }",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(3);",
"__webpack_require__(8);",
"__webpack_require__(6);",
"__webpack_require__(29);",
"__webpack_require__(21);",
"__webpack_require__(2);",
"__webpack_require__(11);",
"__webpack_require__(9);",
"__webpack_require__(1);",
"__webpack_require__(16);",
"",
"// shortcut for asn.1 & PKI API",
"var asn1 = forge.asn1;",
"var pki = forge.pki;",
"",
"// shortcut for PKCS#12 API",
"var p12 = module.exports = forge.pkcs12 = forge.pkcs12 || {};",
"",
"var contentInfoValidator = {",
" name: 'ContentInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE, // a ContentInfo",
" constructed: true,",
" value: [{",
" name: 'ContentInfo.contentType',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'contentType'",
" }, {",
" name: 'ContentInfo.content',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" constructed: true,",
" captureAsn1: 'content'",
" }]",
"};",
"",
"var pfxValidator = {",
" name: 'PFX',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'PFX.version',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'version'",
" },",
" contentInfoValidator, {",
" name: 'PFX.macData',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" optional: true,",
" captureAsn1: 'mac',",
" value: [{",
" name: 'PFX.macData.mac',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE, // DigestInfo",
" constructed: true,",
" value: [{",
" name: 'PFX.macData.mac.digestAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE, // DigestAlgorithmIdentifier",
" constructed: true,",
" value: [{",
" name: 'PFX.macData.mac.digestAlgorithm.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'macAlgorithm'",
" }, {",
" name: 'PFX.macData.mac.digestAlgorithm.parameters',",
" tagClass: asn1.Class.UNIVERSAL,",
" captureAsn1: 'macAlgorithmParameters'",
" }]",
" }, {",
" name: 'PFX.macData.mac.digest',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'macDigest'",
" }]",
" }, {",
" name: 'PFX.macData.macSalt',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'macSalt'",
" }, {",
" name: 'PFX.macData.iterations',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" optional: true,",
" capture: 'macIterations'",
" }]",
" }]",
"};",
"",
"var safeBagValidator = {",
" name: 'SafeBag',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'SafeBag.bagId',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'bagId'",
" }, {",
" name: 'SafeBag.bagValue',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" constructed: true,",
" captureAsn1: 'bagValue'",
" }, {",
" name: 'SafeBag.bagAttributes',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SET,",
" constructed: true,",
" optional: true,",
" capture: 'bagAttributes'",
" }]",
"};",
"",
"var attributeValidator = {",
" name: 'Attribute',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'Attribute.attrId',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'oid'",
" }, {",
" name: 'Attribute.attrValues',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SET,",
" constructed: true,",
" capture: 'values'",
" }]",
"};",
"",
"var certBagValidator = {",
" name: 'CertBag',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'CertBag.certId',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'certId'",
" }, {",
" name: 'CertBag.certValue',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" constructed: true,",
" /* So far we only support X.509 certificates (which are wrapped in",
" an OCTET STRING, hence hard code that here). */",
" value: [{",
" name: 'CertBag.certValue[0]',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Class.OCTETSTRING,",
" constructed: false,",
" capture: 'cert'",
" }]",
" }]",
"};",
"",
"/**",
" * Search SafeContents structure for bags with matching attributes.",
" *",
" * The search can optionally be narrowed by a certain bag type.",
" *",
" * @param safeContents the SafeContents structure to search in.",
" * @param attrName the name of the attribute to compare against.",
" * @param attrValue the attribute value to search for.",
" * @param [bagType] bag type to narrow search by.",
" *",
" * @return an array of matching bags.",
" */",
"function _getBagsByAttribute(safeContents, attrName, attrValue, bagType) {",
" var result = [];",
"",
" for(var i = 0; i < safeContents.length; i++) {",
" for(var j = 0; j < safeContents[i].safeBags.length; j++) {",
" var bag = safeContents[i].safeBags[j];",
" if(bagType !== undefined && bag.type !== bagType) {",
" continue;",
" }",
" // only filter by bag type, no attribute specified",
" if(attrName === null) {",
" result.push(bag);",
" continue;",
" }",
" if(bag.attributes[attrName] !== undefined &&",
" bag.attributes[attrName].indexOf(attrValue) >= 0) {",
" result.push(bag);",
" }",
" }",
" }",
"",
" return result;",
"}",
"",
"/**",
" * Converts a PKCS#12 PFX in ASN.1 notation into a PFX object.",
" *",
" * @param obj The PKCS#12 PFX in ASN.1 notation.",
" * @param strict true to use strict DER decoding, false not to (default: true).",
" * @param {String} password Password to decrypt with (optional).",
" *",
" * @return PKCS#12 PFX object.",
" */",
"p12.pkcs12FromAsn1 = function(obj, strict, password) {",
" // handle args",
" if(typeof strict === 'string') {",
" password = strict;",
" strict = true;",
" } else if(strict === undefined) {",
" strict = true;",
" }",
"",
" // validate PFX and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, pfxValidator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#12 PFX. ' +",
" 'ASN.1 object is not an PKCS#12 PFX.');",
" error.errors = error;",
" throw error;",
" }",
"",
" var pfx = {",
" version: capture.version.charCodeAt(0),",
" safeContents: [],",
"",
" /**",
" * Gets bags with matching attributes.",
" *",
" * @param filter the attributes to filter by:",
" * [localKeyId] the localKeyId to search for.",
" * [localKeyIdHex] the localKeyId in hex to search for.",
" * [friendlyName] the friendly name to search for.",
" * [bagType] bag type to narrow each attribute search by.",
" *",
" * @return a map of attribute type to an array of matching bags or, if no",
" * attribute was given but a bag type, the map key will be the",
" * bag type.",
" */",
" getBags: function(filter) {",
" var rval = {};",
"",
" var localKeyId;",
" if('localKeyId' in filter) {",
" localKeyId = filter.localKeyId;",
" } else if('localKeyIdHex' in filter) {",
" localKeyId = forge.util.hexToBytes(filter.localKeyIdHex);",
" }",
"",
" // filter on bagType only",
" if(localKeyId === undefined && !('friendlyName' in filter) &&",
" 'bagType' in filter) {",
" rval[filter.bagType] = _getBagsByAttribute(",
" pfx.safeContents, null, null, filter.bagType);",
" }",
"",
" if(localKeyId !== undefined) {",
" rval.localKeyId = _getBagsByAttribute(",
" pfx.safeContents, 'localKeyId',",
" localKeyId, filter.bagType);",
" }",
" if('friendlyName' in filter) {",
" rval.friendlyName = _getBagsByAttribute(",
" pfx.safeContents, 'friendlyName',",
" filter.friendlyName, filter.bagType);",
" }",
"",
" return rval;",
" },",
"",
" /**",
" * DEPRECATED: use getBags() instead.",
" *",
" * Get bags with matching friendlyName attribute.",
" *",
" * @param friendlyName the friendly name to search for.",
" * @param [bagType] bag type to narrow search by.",
" *",
" * @return an array of bags with matching friendlyName attribute.",
" */",
" getBagsByFriendlyName: function(friendlyName, bagType) {",
" return _getBagsByAttribute(",
" pfx.safeContents, 'friendlyName', friendlyName, bagType);",
" },",
"",
" /**",
" * DEPRECATED: use getBags() instead.",
" *",
" * Get bags with matching localKeyId attribute.",
" *",
" * @param localKeyId the localKeyId to search for.",
" * @param [bagType] bag type to narrow search by.",
" *",
" * @return an array of bags with matching localKeyId attribute.",
" */",
" getBagsByLocalKeyId: function(localKeyId, bagType) {",
" return _getBagsByAttribute(",
" pfx.safeContents, 'localKeyId', localKeyId, bagType);",
" }",
" };",
"",
" if(capture.version.charCodeAt(0) !== 3) {",
" var error = new Error('PKCS#12 PFX of version other than 3 not supported.');",
" error.version = capture.version.charCodeAt(0);",
" throw error;",
" }",
"",
" if(asn1.derToOid(capture.contentType) !== pki.oids.data) {",
" var error = new Error('Only PKCS#12 PFX in password integrity mode supported.');",
" error.oid = asn1.derToOid(capture.contentType);",
" throw error;",
" }",
"",
" var data = capture.content.value[0];",
" if(data.tagClass !== asn1.Class.UNIVERSAL ||",
" data.type !== asn1.Type.OCTETSTRING) {",
" throw new Error('PKCS#12 authSafe content data is not an OCTET STRING.');",
" }",
" data = _decodePkcs7Data(data);",
"",
" // check for MAC",
" if(capture.mac) {",
" var md = null;",
" var macKeyBytes = 0;",
" var macAlgorithm = asn1.derToOid(capture.macAlgorithm);",
" switch(macAlgorithm) {",
" case pki.oids.sha1:",
" md = forge.md.sha1.create();",
" macKeyBytes = 20;",
" break;",
" case pki.oids.sha256:",
" md = forge.md.sha256.create();",
" macKeyBytes = 32;",
" break;",
" case pki.oids.sha384:",
" md = forge.md.sha384.create();",
" macKeyBytes = 48;",
" break;",
" case pki.oids.sha512:",
" md = forge.md.sha512.create();",
" macKeyBytes = 64;",
" break;",
" case pki.oids.md5:",
" md = forge.md.md5.create();",
" macKeyBytes = 16;",
" break;",
" }",
" if(md === null) {",
" throw new Error('PKCS#12 uses unsupported MAC algorithm: ' + macAlgorithm);",
" }",
"",
" // verify MAC (iterations default to 1)",
" var macSalt = new forge.util.ByteBuffer(capture.macSalt);",
" var macIterations = (('macIterations' in capture) ?",
" parseInt(forge.util.bytesToHex(capture.macIterations), 16) : 1);",
" var macKey = p12.generateKey(",
" password, macSalt, 3, macIterations, macKeyBytes, md);",
" var mac = forge.hmac.create();",
" mac.start(md, macKey);",
" mac.update(data.value);",
" var macValue = mac.getMac();",
" if(macValue.getBytes() !== capture.macDigest) {",
" throw new Error('PKCS#12 MAC could not be verified. Invalid password?');",
" }",
" }",
"",
" _decodeAuthenticatedSafe(pfx, data.value, strict, password);",
" return pfx;",
"};",
"",
"/**",
" * Decodes PKCS#7 Data. PKCS#7 (RFC 2315) defines \"Data\" as an OCTET STRING,",
" * but it is sometimes an OCTET STRING that is composed/constructed of chunks,",
" * each its own OCTET STRING. This is BER-encoding vs. DER-encoding. This",
" * function transforms this corner-case into the usual simple,",
" * non-composed/constructed OCTET STRING.",
" *",
" * This function may be moved to ASN.1 at some point to better deal with",
" * more BER-encoding issues, should they arise.",
" *",
" * @param data the ASN.1 Data object to transform.",
" */",
"function _decodePkcs7Data(data) {",
" // handle special case of \"chunked\" data content: an octet string composed",
" // of other octet strings",
" if(data.composed || data.constructed) {",
" var value = forge.util.createBuffer();",
" for(var i = 0; i < data.value.length; ++i) {",
" value.putBytes(data.value[i].value);",
" }",
" data.composed = data.constructed = false;",
" data.value = value.getBytes();",
" }",
" return data;",
"}",
"",
"/**",
" * Decode PKCS#12 AuthenticatedSafe (BER encoded) into PFX object.",
" *",
" * The AuthenticatedSafe is a BER-encoded SEQUENCE OF ContentInfo.",
" *",
" * @param pfx The PKCS#12 PFX object to fill.",
" * @param {String} authSafe BER-encoded AuthenticatedSafe.",
" * @param strict true to use strict DER decoding, false not to.",
" * @param {String} password Password to decrypt with (optional).",
" */",
"function _decodeAuthenticatedSafe(pfx, authSafe, strict, password) {",
" authSafe = asn1.fromDer(authSafe, strict); /* actually it's BER encoded */",
"",
" if(authSafe.tagClass !== asn1.Class.UNIVERSAL ||",
" authSafe.type !== asn1.Type.SEQUENCE ||",
" authSafe.constructed !== true) {",
" throw new Error('PKCS#12 AuthenticatedSafe expected to be a ' +",
" 'SEQUENCE OF ContentInfo');",
" }",
"",
" for(var i = 0; i < authSafe.value.length; i++) {",
" var contentInfo = authSafe.value[i];",
"",
" // validate contentInfo and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(contentInfo, contentInfoValidator, capture, errors)) {",
" var error = new Error('Cannot read ContentInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" var obj = {",
" encrypted: false",
" };",
" var safeContents = null;",
" var data = capture.content.value[0];",
" switch(asn1.derToOid(capture.contentType)) {",
" case pki.oids.data:",
" if(data.tagClass !== asn1.Class.UNIVERSAL ||",
" data.type !== asn1.Type.OCTETSTRING) {",
" throw new Error('PKCS#12 SafeContents Data is not an OCTET STRING.');",
" }",
" safeContents = _decodePkcs7Data(data).value;",
" break;",
" case pki.oids.encryptedData:",
" safeContents = _decryptSafeContents(data, password);",
" obj.encrypted = true;",
" break;",
" default:",
" var error = new Error('Unsupported PKCS#12 contentType.');",
" error.contentType = asn1.derToOid(capture.contentType);",
" throw error;",
" }",
"",
" obj.safeBags = _decodeSafeContents(safeContents, strict, password);",
" pfx.safeContents.push(obj);",
" }",
"}",
"",
"/**",
" * Decrypt PKCS#7 EncryptedData structure.",
" *",
" * @param data ASN.1 encoded EncryptedContentInfo object.",
" * @param password The user-provided password.",
" *",
" * @return The decrypted SafeContents (ASN.1 object).",
" */",
"function _decryptSafeContents(data, password) {",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(",
" data, forge.pkcs7.asn1.encryptedDataValidator, capture, errors)) {",
" var error = new Error('Cannot read EncryptedContentInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" var oid = asn1.derToOid(capture.contentType);",
" if(oid !== pki.oids.data) {",
" var error = new Error(",
" 'PKCS#12 EncryptedContentInfo ContentType is not Data.');",
" error.oid = oid;",
" throw error;",
" }",
"",
" // get cipher",
" oid = asn1.derToOid(capture.encAlgorithm);",
" var cipher = pki.pbe.getCipher(oid, capture.encParameter, password);",
"",
" // get encrypted data",
" var encryptedContentAsn1 = _decodePkcs7Data(capture.encryptedContentAsn1);",
" var encrypted = forge.util.createBuffer(encryptedContentAsn1.value);",
"",
" cipher.update(encrypted);",
" if(!cipher.finish()) {",
" throw new Error('Failed to decrypt PKCS#12 SafeContents.');",
" }",
"",
" return cipher.output.getBytes();",
"}",
"",
"/**",
" * Decode PKCS#12 SafeContents (BER-encoded) into array of Bag objects.",
" *",
" * The safeContents is a BER-encoded SEQUENCE OF SafeBag.",
" *",
" * @param {String} safeContents BER-encoded safeContents.",
" * @param strict true to use strict DER decoding, false not to.",
" * @param {String} password Password to decrypt with (optional).",
" *",
" * @return {Array} Array of Bag objects.",
" */",
"function _decodeSafeContents(safeContents, strict, password) {",
" // if strict and no safe contents, return empty safes",
" if(!strict && safeContents.length === 0) {",
" return [];",
" }",
"",
" // actually it's BER-encoded",
" safeContents = asn1.fromDer(safeContents, strict);",
"",
" if(safeContents.tagClass !== asn1.Class.UNIVERSAL ||",
" safeContents.type !== asn1.Type.SEQUENCE ||",
" safeContents.constructed !== true) {",
" throw new Error(",
" 'PKCS#12 SafeContents expected to be a SEQUENCE OF SafeBag.');",
" }",
"",
" var res = [];",
" for(var i = 0; i < safeContents.value.length; i++) {",
" var safeBag = safeContents.value[i];",
"",
" // validate SafeBag and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(safeBag, safeBagValidator, capture, errors)) {",
" var error = new Error('Cannot read SafeBag.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" /* Create bag object and push to result array. */",
" var bag = {",
" type: asn1.derToOid(capture.bagId),",
" attributes: _decodeBagAttributes(capture.bagAttributes)",
" };",
" res.push(bag);",
"",
" var validator, decoder;",
" var bagAsn1 = capture.bagValue.value[0];",
" switch(bag.type) {",
" case pki.oids.pkcs8ShroudedKeyBag:",
" /* bagAsn1 has a EncryptedPrivateKeyInfo, which we need to decrypt.",
" Afterwards we can handle it like a keyBag,",
" which is a PrivateKeyInfo. */",
" bagAsn1 = pki.decryptPrivateKeyInfo(bagAsn1, password);",
" if(bagAsn1 === null) {",
" throw new Error(",
" 'Unable to decrypt PKCS#8 ShroudedKeyBag, wrong password?');",
" }",
"",
" /* fall through */",
" case pki.oids.keyBag:",
" /* A PKCS#12 keyBag is a simple PrivateKeyInfo as understood by our",
" PKI module, hence we don't have to do validation/capturing here,",
" just pass what we already got. */",
" try {",
" bag.key = pki.privateKeyFromAsn1(bagAsn1);",
" } catch(e) {",
" // ignore unknown key type, pass asn1 value",
" bag.key = null;",
" bag.asn1 = bagAsn1;",
" }",
" continue; /* Nothing more to do. */",
"",
" case pki.oids.certBag:",
" /* A PKCS#12 certBag can wrap both X.509 and sdsi certificates.",
" Therefore put the SafeBag content through another validator to",
" capture the fields. Afterwards check & store the results. */",
" validator = certBagValidator;",
" decoder = function() {",
" if(asn1.derToOid(capture.certId) !== pki.oids.x509Certificate) {",
" var error = new Error(",
" 'Unsupported certificate type, only X.509 supported.');",
" error.oid = asn1.derToOid(capture.certId);",
" throw error;",
" }",
"",
" // true=produce cert hash",
" var certAsn1 = asn1.fromDer(capture.cert, strict);",
" try {",
" bag.cert = pki.certificateFromAsn1(certAsn1, true);",
" } catch(e) {",
" // ignore unknown cert type, pass asn1 value",
" bag.cert = null;",
" bag.asn1 = certAsn1;",
" }",
" };",
" break;",
"",
" default:",
" var error = new Error('Unsupported PKCS#12 SafeBag type.');",
" error.oid = bag.type;",
" throw error;",
" }",
"",
" /* Validate SafeBag value (i.e. CertBag, etc.) and capture data if needed. */",
" if(validator !== undefined &&",
" !asn1.validate(bagAsn1, validator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#12 ' + validator.name);",
" error.errors = errors;",
" throw error;",
" }",
"",
" /* Call decoder function from above to store the results. */",
" decoder();",
" }",
"",
" return res;",
"}",
"",
"/**",
" * Decode PKCS#12 SET OF PKCS12Attribute into JavaScript object.",
" *",
" * @param attributes SET OF PKCS12Attribute (ASN.1 object).",
" *",
" * @return the decoded attributes.",
" */",
"function _decodeBagAttributes(attributes) {",
" var decodedAttrs = {};",
"",
" if(attributes !== undefined) {",
" for(var i = 0; i < attributes.length; ++i) {",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(attributes[i], attributeValidator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#12 BagAttribute.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" var oid = asn1.derToOid(capture.oid);",
" if(pki.oids[oid] === undefined) {",
" // unsupported attribute type, ignore.",
" continue;",
" }",
"",
" decodedAttrs[pki.oids[oid]] = [];",
" for(var j = 0; j < capture.values.length; ++j) {",
" decodedAttrs[pki.oids[oid]].push(capture.values[j].value);",
" }",
" }",
" }",
"",
" return decodedAttrs;",
"}",
"",
"/**",
" * Wraps a private key and certificate in a PKCS#12 PFX wrapper. If a",
" * password is provided then the private key will be encrypted.",
" *",
" * An entire certificate chain may also be included. To do this, pass",
" * an array for the \"cert\" parameter where the first certificate is",
" * the one that is paired with the private key and each subsequent one",
" * verifies the previous one. The certificates may be in PEM format or",
" * have been already parsed by Forge.",
" *",
" * @todo implement password-based-encryption for the whole package",
" *",
" * @param key the private key.",
" * @param cert the certificate (may be an array of certificates in order",
" * to specify a certificate chain).",
" * @param password the password to use, null for none.",
" * @param options:",
" * algorithm the encryption algorithm to use",
" * ('aes128', 'aes192', 'aes256', '3des'), defaults to 'aes128'.",
" * count the iteration count to use.",
" * saltSize the salt size to use.",
" * useMac true to include a MAC, false not to, defaults to true.",
" * localKeyId the local key ID to use, in hex.",
" * friendlyName the friendly name to use.",
" * generateLocalKeyId true to generate a random local key ID,",
" * false not to, defaults to true.",
" *",
" * @return the PKCS#12 PFX ASN.1 object.",
" */",
"p12.toPkcs12Asn1 = function(key, cert, password, options) {",
" // set default options",
" options = options || {};",
" options.saltSize = options.saltSize || 8;",
" options.count = options.count || 2048;",
" options.algorithm = options.algorithm || options.encAlgorithm || 'aes128';",
" if(!('useMac' in options)) {",
" options.useMac = true;",
" }",
" if(!('localKeyId' in options)) {",
" options.localKeyId = null;",
" }",
" if(!('generateLocalKeyId' in options)) {",
" options.generateLocalKeyId = true;",
" }",
"",
" var localKeyId = options.localKeyId;",
" var bagAttrs;",
" if(localKeyId !== null) {",
" localKeyId = forge.util.hexToBytes(localKeyId);",
" } else if(options.generateLocalKeyId) {",
" // use SHA-1 of paired cert, if available",
" if(cert) {",
" var pairedCert = forge.util.isArray(cert) ? cert[0] : cert;",
" if(typeof pairedCert === 'string') {",
" pairedCert = pki.certificateFromPem(pairedCert);",
" }",
" var sha1 = forge.md.sha1.create();",
" sha1.update(asn1.toDer(pki.certificateToAsn1(pairedCert)).getBytes());",
" localKeyId = sha1.digest().getBytes();",
" } else {",
" // FIXME: consider using SHA-1 of public key (which can be generated",
" // from private key components), see: cert.generateSubjectKeyIdentifier",
" // generate random bytes",
" localKeyId = forge.random.getBytes(20);",
" }",
" }",
"",
" var attrs = [];",
" if(localKeyId !== null) {",
" attrs.push(",
" // localKeyID",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // attrId",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.localKeyId).getBytes()),",
" // attrValues",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" localKeyId)",
" ])",
" ]));",
" }",
" if('friendlyName' in options) {",
" attrs.push(",
" // friendlyName",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // attrId",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.friendlyName).getBytes()),",
" // attrValues",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BMPSTRING, false,",
" options.friendlyName)",
" ])",
" ]));",
" }",
"",
" if(attrs.length > 0) {",
" bagAttrs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs);",
" }",
"",
" // collect contents for AuthenticatedSafe",
" var contents = [];",
"",
" // create safe bag(s) for certificate chain",
" var chain = [];",
" if(cert !== null) {",
" if(forge.util.isArray(cert)) {",
" chain = cert;",
" } else {",
" chain = [cert];",
" }",
" }",
"",
" var certSafeBags = [];",
" for(var i = 0; i < chain.length; ++i) {",
" // convert cert from PEM as necessary",
" cert = chain[i];",
" if(typeof cert === 'string') {",
" cert = pki.certificateFromPem(cert);",
" }",
"",
" // SafeBag",
" var certBagAttrs = (i === 0) ? bagAttrs : undefined;",
" var certAsn1 = pki.certificateToAsn1(cert);",
" var certSafeBag =",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // bagId",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.certBag).getBytes()),",
" // bagValue",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" // CertBag",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // certId",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.x509Certificate).getBytes()),",
" // certValue (x509Certificate)",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" asn1.toDer(certAsn1).getBytes())",
" ])])]),",
" // bagAttributes (OPTIONAL)",
" certBagAttrs",
" ]);",
" certSafeBags.push(certSafeBag);",
" }",
"",
" if(certSafeBags.length > 0) {",
" // SafeContents",
" var certSafeContents = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, certSafeBags);",
"",
" // ContentInfo",
" var certCI =",
" // PKCS#7 ContentInfo",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // contentType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" // OID for the content type is 'data'",
" asn1.oidToDer(pki.oids.data).getBytes()),",
" // content",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" asn1.toDer(certSafeContents).getBytes())",
" ])",
" ]);",
" contents.push(certCI);",
" }",
"",
" // create safe contents for private key",
" var keyBag = null;",
" if(key !== null) {",
" // SafeBag",
" var pkAsn1 = pki.wrapRsaPrivateKey(pki.privateKeyToAsn1(key));",
" if(password === null) {",
" // no encryption",
" keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // bagId",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.keyBag).getBytes()),",
" // bagValue",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" // PrivateKeyInfo",
" pkAsn1",
" ]),",
" // bagAttributes (OPTIONAL)",
" bagAttrs",
" ]);",
" } else {",
" // encrypted PrivateKeyInfo",
" keyBag = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // bagId",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.pkcs8ShroudedKeyBag).getBytes()),",
" // bagValue",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" // EncryptedPrivateKeyInfo",
" pki.encryptPrivateKeyInfo(pkAsn1, password, options)",
" ]),",
" // bagAttributes (OPTIONAL)",
" bagAttrs",
" ]);",
" }",
"",
" // SafeContents",
" var keySafeContents =",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [keyBag]);",
"",
" // ContentInfo",
" var keyCI =",
" // PKCS#7 ContentInfo",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // contentType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" // OID for the content type is 'data'",
" asn1.oidToDer(pki.oids.data).getBytes()),",
" // content",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" asn1.toDer(keySafeContents).getBytes())",
" ])",
" ]);",
" contents.push(keyCI);",
" }",
"",
" // create AuthenticatedSafe by stringing together the contents",
" var safe = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, contents);",
"",
" var macData;",
" if(options.useMac) {",
" // MacData",
" var sha1 = forge.md.sha1.create();",
" var macSalt = new forge.util.ByteBuffer(",
" forge.random.getBytes(options.saltSize));",
" var count = options.count;",
" // 160-bit key",
" var key = p12.generateKey(password, macSalt, 3, count, 20);",
" var mac = forge.hmac.create();",
" mac.start(sha1, key);",
" mac.update(asn1.toDer(safe).getBytes());",
" var macValue = mac.getMac();",
" macData = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // mac DigestInfo",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // digestAlgorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm = SHA-1",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(pki.oids.sha1).getBytes()),",
" // parameters = Null",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ]),",
" // digest",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING,",
" false, macValue.getBytes())",
" ]),",
" // macSalt OCTET STRING",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, macSalt.getBytes()),",
" // iterations INTEGER (XXX: Only support count < 65536)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(count).getBytes()",
" )",
" ]);",
" }",
"",
" // PFX",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // version (3)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(3).getBytes()),",
" // PKCS#7 ContentInfo",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // contentType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" // OID for the content type is 'data'",
" asn1.oidToDer(pki.oids.data).getBytes()),",
" // content",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" asn1.toDer(safe).getBytes())",
" ])",
" ]),",
" macData",
" ]);",
"};",
"",
"/**",
" * Derives a PKCS#12 key.",
" *",
" * @param password the password to derive the key material from, null or",
" * undefined for none.",
" * @param salt the salt, as a ByteBuffer, to use.",
" * @param id the PKCS#12 ID byte (1 = key material, 2 = IV, 3 = MAC).",
" * @param iter the iteration count.",
" * @param n the number of bytes to derive from the password.",
" * @param md the message digest to use, defaults to SHA-1.",
" *",
" * @return a ByteBuffer with the bytes derived from the password.",
" */",
"p12.generateKey = forge.pbe.generatePkcs12Key;",
"",
"",
"/***/ }),",
"/* 29 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of ASN.1 validators for PKCS#7 v1.5.",
" *",
" * @author Dave Longley",
" * @author Stefan Siegl",
" *",
" * Copyright (c) 2012-2015 Digital Bazaar, Inc.",
" * Copyright (c) 2012 Stefan Siegl ",
" *",
" * The ASN.1 representation of PKCS#7 is as follows",
" * (see RFC #2315 for details, http://www.ietf.org/rfc/rfc2315.txt):",
" *",
" * A PKCS#7 message consists of a ContentInfo on root level, which may",
" * contain any number of further ContentInfo nested into it.",
" *",
" * ContentInfo ::= SEQUENCE {",
" * contentType ContentType,",
" * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL",
" * }",
" *",
" * ContentType ::= OBJECT IDENTIFIER",
" *",
" * EnvelopedData ::= SEQUENCE {",
" * version Version,",
" * recipientInfos RecipientInfos,",
" * encryptedContentInfo EncryptedContentInfo",
" * }",
" *",
" * EncryptedData ::= SEQUENCE {",
" * version Version,",
" * encryptedContentInfo EncryptedContentInfo",
" * }",
" *",
" * id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)",
" * us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }",
" *",
" * SignedData ::= SEQUENCE {",
" * version INTEGER,",
" * digestAlgorithms DigestAlgorithmIdentifiers,",
" * contentInfo ContentInfo,",
" * certificates [0] IMPLICIT Certificates OPTIONAL,",
" * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,",
" * signerInfos SignerInfos",
" * }",
" *",
" * SignerInfos ::= SET OF SignerInfo",
" *",
" * SignerInfo ::= SEQUENCE {",
" * version Version,",
" * issuerAndSerialNumber IssuerAndSerialNumber,",
" * digestAlgorithm DigestAlgorithmIdentifier,",
" * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,",
" * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,",
" * encryptedDigest EncryptedDigest,",
" * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL",
" * }",
" *",
" * EncryptedDigest ::= OCTET STRING",
" *",
" * Attributes ::= SET OF Attribute",
" *",
" * Attribute ::= SEQUENCE {",
" * attrType OBJECT IDENTIFIER,",
" * attrValues SET OF AttributeValue",
" * }",
" *",
" * AttributeValue ::= ANY",
" *",
" * Version ::= INTEGER",
" *",
" * RecipientInfos ::= SET OF RecipientInfo",
" *",
" * EncryptedContentInfo ::= SEQUENCE {",
" * contentType ContentType,",
" * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,",
" * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL",
" * }",
" *",
" * ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier",
" *",
" * The AlgorithmIdentifier contains an Object Identifier (OID) and parameters",
" * for the algorithm, if any. In the case of AES and DES3, there is only one,",
" * the IV.",
" *",
" * AlgorithmIdentifer ::= SEQUENCE {",
" * algorithm OBJECT IDENTIFIER,",
" * parameters ANY DEFINED BY algorithm OPTIONAL",
" * }",
" *",
" * EncryptedContent ::= OCTET STRING",
" *",
" * RecipientInfo ::= SEQUENCE {",
" * version Version,",
" * issuerAndSerialNumber IssuerAndSerialNumber,",
" * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,",
" * encryptedKey EncryptedKey",
" * }",
" *",
" * IssuerAndSerialNumber ::= SEQUENCE {",
" * issuer Name,",
" * serialNumber CertificateSerialNumber",
" * }",
" *",
" * CertificateSerialNumber ::= INTEGER",
" *",
" * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier",
" *",
" * EncryptedKey ::= OCTET STRING",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(3);",
"__webpack_require__(1);",
"",
"// shortcut for ASN.1 API",
"var asn1 = forge.asn1;",
"",
"// shortcut for PKCS#7 API",
"var p7v = module.exports = forge.pkcs7asn1 = forge.pkcs7asn1 || {};",
"forge.pkcs7 = forge.pkcs7 || {};",
"forge.pkcs7.asn1 = p7v;",
"",
"var contentInfoValidator = {",
" name: 'ContentInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'ContentInfo.ContentType',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'contentType'",
" }, {",
" name: 'ContentInfo.content',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" constructed: true,",
" optional: true,",
" captureAsn1: 'content'",
" }]",
"};",
"p7v.contentInfoValidator = contentInfoValidator;",
"",
"var encryptedContentInfoValidator = {",
" name: 'EncryptedContentInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'EncryptedContentInfo.contentType',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'contentType'",
" }, {",
" name: 'EncryptedContentInfo.contentEncryptionAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'EncryptedContentInfo.contentEncryptionAlgorithm.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'encAlgorithm'",
" }, {",
" name: 'EncryptedContentInfo.contentEncryptionAlgorithm.parameter',",
" tagClass: asn1.Class.UNIVERSAL,",
" captureAsn1: 'encParameter'",
" }]",
" }, {",
" name: 'EncryptedContentInfo.encryptedContent',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" /* The PKCS#7 structure output by OpenSSL somewhat differs from what",
" * other implementations do generate.",
" *",
" * OpenSSL generates a structure like this:",
" * SEQUENCE {",
" * ...",
" * [0]",
" * 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38",
" * C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45",
" * ...",
" * }",
" *",
" * Whereas other implementations (and this PKCS#7 module) generate:",
" * SEQUENCE {",
" * ...",
" * [0] {",
" * OCTET STRING",
" * 26 DA 67 D2 17 9C 45 3C B1 2A A8 59 2F 29 33 38",
" * C3 C3 DF 86 71 74 7A 19 9F 40 D0 29 BE 85 90 45",
" * ...",
" * }",
" * }",
" *",
" * In order to support both, we just capture the context specific",
" * field here. The OCTET STRING bit is removed below.",
" */",
" capture: 'encryptedContent',",
" captureAsn1: 'encryptedContentAsn1'",
" }]",
"};",
"",
"p7v.envelopedDataValidator = {",
" name: 'EnvelopedData',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'EnvelopedData.Version',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'version'",
" }, {",
" name: 'EnvelopedData.RecipientInfos',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SET,",
" constructed: true,",
" captureAsn1: 'recipientInfos'",
" }].concat(encryptedContentInfoValidator)",
"};",
"",
"p7v.encryptedDataValidator = {",
" name: 'EncryptedData',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'EncryptedData.Version',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'version'",
" }].concat(encryptedContentInfoValidator)",
"};",
"",
"var signerValidator = {",
" name: 'SignerInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'SignerInfo.version',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false",
" }, {",
" name: 'SignerInfo.issuerAndSerialNumber',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'SignerInfo.issuerAndSerialNumber.issuer',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'issuer'",
" }, {",
" name: 'SignerInfo.issuerAndSerialNumber.serialNumber',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'serial'",
" }]",
" }, {",
" name: 'SignerInfo.digestAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'SignerInfo.digestAlgorithm.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'digestAlgorithm'",
" }, {",
" name: 'SignerInfo.digestAlgorithm.parameter',",
" tagClass: asn1.Class.UNIVERSAL,",
" constructed: false,",
" captureAsn1: 'digestParameter',",
" optional: true",
" }]",
" }, {",
" name: 'SignerInfo.authenticatedAttributes',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" constructed: true,",
" optional: true,",
" capture: 'authenticatedAttributes'",
" }, {",
" name: 'SignerInfo.digestEncryptionAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" capture: 'signatureAlgorithm'",
" }, {",
" name: 'SignerInfo.encryptedDigest',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'signature'",
" }, {",
" name: 'SignerInfo.unauthenticatedAttributes',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 1,",
" constructed: true,",
" optional: true,",
" capture: 'unauthenticatedAttributes'",
" }]",
"};",
"",
"p7v.signedDataValidator = {",
" name: 'SignedData',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'SignedData.Version',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'version'",
" }, {",
" name: 'SignedData.DigestAlgorithms',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SET,",
" constructed: true,",
" captureAsn1: 'digestAlgorithms'",
" },",
" contentInfoValidator,",
" {",
" name: 'SignedData.Certificates',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 0,",
" optional: true,",
" captureAsn1: 'certificates'",
" }, {",
" name: 'SignedData.CertificateRevocationLists',",
" tagClass: asn1.Class.CONTEXT_SPECIFIC,",
" type: 1,",
" optional: true,",
" captureAsn1: 'crls'",
" }, {",
" name: 'SignedData.SignerInfos',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SET,",
" capture: 'signerInfos',",
" optional: true,",
" value: [signerValidator]",
" }]",
"};",
"",
"p7v.recipientInfoValidator = {",
" name: 'RecipientInfo',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'RecipientInfo.version',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'version'",
" }, {",
" name: 'RecipientInfo.issuerAndSerial',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'RecipientInfo.issuerAndSerial.issuer',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" captureAsn1: 'issuer'",
" }, {",
" name: 'RecipientInfo.issuerAndSerial.serialNumber',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.INTEGER,",
" constructed: false,",
" capture: 'serial'",
" }]",
" }, {",
" name: 'RecipientInfo.keyEncryptionAlgorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.SEQUENCE,",
" constructed: true,",
" value: [{",
" name: 'RecipientInfo.keyEncryptionAlgorithm.algorithm',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OID,",
" constructed: false,",
" capture: 'encAlgorithm'",
" }, {",
" name: 'RecipientInfo.keyEncryptionAlgorithm.parameter',",
" tagClass: asn1.Class.UNIVERSAL,",
" constructed: false,",
" captureAsn1: 'encParameter'",
" }]",
" }, {",
" name: 'RecipientInfo.encryptedKey',",
" tagClass: asn1.Class.UNIVERSAL,",
" type: asn1.Type.OCTETSTRING,",
" constructed: false,",
" capture: 'encKey'",
" }]",
"};",
"",
"",
"/***/ }),",
"/* 30 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of mask generation function MGF1.",
" *",
" * @author Stefan Siegl",
" * @author Dave Longley",
" *",
" * Copyright (c) 2012 Stefan Siegl ",
" * Copyright (c) 2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"",
"forge.mgf = forge.mgf || {};",
"var mgf1 = module.exports = forge.mgf.mgf1 = forge.mgf1 = forge.mgf1 || {};",
"",
"/**",
" * Creates a MGF1 mask generation function object.",
" *",
" * @param md the message digest API to use (eg: forge.md.sha1.create()).",
" *",
" * @return a mask generation function object.",
" */",
"mgf1.create = function(md) {",
" var mgf = {",
" /**",
" * Generate mask of specified length.",
" *",
" * @param {String} seed The seed for mask generation.",
" * @param maskLen Number of bytes to generate.",
" * @return {String} The generated mask.",
" */",
" generate: function(seed, maskLen) {",
" /* 2. Let T be the empty octet string. */",
" var t = new forge.util.ByteBuffer();",
"",
" /* 3. For counter from 0 to ceil(maskLen / hLen), do the following: */",
" var len = Math.ceil(maskLen / md.digestLength);",
" for(var i = 0; i < len; i++) {",
" /* a. Convert counter to an octet string C of length 4 octets */",
" var c = new forge.util.ByteBuffer();",
" c.putInt32(i);",
"",
" /* b. Concatenate the hash of the seed mgfSeed and C to the octet",
" * string T: */",
" md.start();",
" md.update(seed + c.getBytes());",
" t.putBuffer(md.digest());",
" }",
"",
" /* Output the leading maskLen octets of T as the octet string mask. */",
" t.truncate(t.length() - maskLen);",
" return t.getBytes();",
" }",
" };",
"",
" return mgf;",
"};",
"",
"",
"/***/ }),",
"/* 31 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Debugging support for web applications.",
" *",
" * @author David I. Lehn ",
" *",
" * Copyright 2008-2013 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"",
"/* DEBUG API */",
"module.exports = forge.debug = forge.debug || {};",
"",
"// Private storage for debugging.",
"// Useful to expose data that is otherwise unviewable behind closures.",
"// NOTE: remember that this can hold references to data and cause leaks!",
"// format is \"forge._debug.. = data\"",
"// Example:",
"// (function() {",
"// var cat = 'forge.test.Test'; // debugging category",
"// var sState = {...}; // local state",
"// forge.debug.set(cat, 'sState', sState);",
"// })();",
"forge.debug.storage = {};",
"",
"/**",
" * Gets debug data. Omit name for all cat data Omit name and cat for",
" * all data.",
" *",
" * @param cat name of debugging category.",
" * @param name name of data to get (optional).",
" * @return object with requested debug data or undefined.",
" */",
"forge.debug.get = function(cat, name) {",
" var rval;",
" if(typeof(cat) === 'undefined') {",
" rval = forge.debug.storage;",
" } else if(cat in forge.debug.storage) {",
" if(typeof(name) === 'undefined') {",
" rval = forge.debug.storage[cat];",
" } else {",
" rval = forge.debug.storage[cat][name];",
" }",
" }",
" return rval;",
"};",
"",
"/**",
" * Sets debug data.",
" *",
" * @param cat name of debugging category.",
" * @param name name of data to set.",
" * @param data data to set.",
" */",
"forge.debug.set = function(cat, name, data) {",
" if(!(cat in forge.debug.storage)) {",
" forge.debug.storage[cat] = {};",
" }",
" forge.debug.storage[cat][name] = data;",
"};",
"",
"/**",
" * Clears debug data. Omit name for all cat data. Omit name and cat for",
" * all data.",
" *",
" * @param cat name of debugging category.",
" * @param name name of data to clear or omit to clear entire category.",
" */",
"forge.debug.clear = function(cat, name) {",
" if(typeof(cat) === 'undefined') {",
" forge.debug.storage = {};",
" } else if(cat in forge.debug.storage) {",
" if(typeof(name) === 'undefined') {",
" delete forge.debug.storage[cat];",
" } else {",
" delete forge.debug.storage[cat][name];",
" }",
" }",
"};",
"",
"",
"/***/ }),",
"/* 32 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Secure Hash Algorithm with a 1024-bit block size implementation.",
" *",
" * This includes: SHA-512, SHA-384, SHA-512/224, and SHA-512/256. For",
" * SHA-256 (block size 512 bits), see sha256.js.",
" *",
" * See FIPS 180-4 for details.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2014-2015 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(4);",
"__webpack_require__(1);",
"",
"var sha512 = module.exports = forge.sha512 = forge.sha512 || {};",
"",
"// SHA-512",
"forge.md.sha512 = forge.md.algorithms.sha512 = sha512;",
"",
"// SHA-384",
"var sha384 = forge.sha384 = forge.sha512.sha384 = forge.sha512.sha384 || {};",
"sha384.create = function() {",
" return sha512.create('SHA-384');",
"};",
"forge.md.sha384 = forge.md.algorithms.sha384 = sha384;",
"",
"// SHA-512/256",
"forge.sha512.sha256 = forge.sha512.sha256 || {",
" create: function() {",
" return sha512.create('SHA-512/256');",
" }",
"};",
"forge.md['sha512/256'] = forge.md.algorithms['sha512/256'] =",
" forge.sha512.sha256;",
"",
"// SHA-512/224",
"forge.sha512.sha224 = forge.sha512.sha224 || {",
" create: function() {",
" return sha512.create('SHA-512/224');",
" }",
"};",
"forge.md['sha512/224'] = forge.md.algorithms['sha512/224'] =",
" forge.sha512.sha224;",
"",
"/**",
" * Creates a SHA-2 message digest object.",
" *",
" * @param algorithm the algorithm to use (SHA-512, SHA-384, SHA-512/224,",
" * SHA-512/256).",
" *",
" * @return a message digest object.",
" */",
"sha512.create = function(algorithm) {",
" // do initialization as necessary",
" if(!_initialized) {",
" _init();",
" }",
"",
" if(typeof algorithm === 'undefined') {",
" algorithm = 'SHA-512';",
" }",
"",
" if(!(algorithm in _states)) {",
" throw new Error('Invalid SHA-512 algorithm: ' + algorithm);",
" }",
"",
" // SHA-512 state contains eight 64-bit integers (each as two 32-bit ints)",
" var _state = _states[algorithm];",
" var _h = null;",
"",
" // input buffer",
" var _input = forge.util.createBuffer();",
"",
" // used for 64-bit word storage",
" var _w = new Array(80);",
" for(var wi = 0; wi < 80; ++wi) {",
" _w[wi] = new Array(2);",
" }",
"",
" // determine digest length by algorithm name (default)",
" var digestLength = 64;",
" switch (algorithm) {",
" case 'SHA-384':",
" digestLength = 48;",
" break;",
" case 'SHA-512/256':",
" digestLength = 32;",
" break;",
" case 'SHA-512/224':",
" digestLength = 28;",
" break;",
" }",
"",
" // message digest object",
" var md = {",
" // SHA-512 => sha512",
" algorithm: algorithm.replace('-', '').toLowerCase(),",
" blockLength: 128,",
" digestLength: digestLength,",
" // 56-bit length of message so far (does not including padding)",
" messageLength: 0,",
" // true message length",
" fullMessageLength: null,",
" // size of message length in bytes",
" messageLengthSize: 16",
" };",
"",
" /**",
" * Starts the digest.",
" *",
" * @return this digest object.",
" */",
" md.start = function() {",
" // up to 56-bit message length for convenience",
" md.messageLength = 0;",
"",
" // full message length (set md.messageLength128 for backwards-compatibility)",
" md.fullMessageLength = md.messageLength128 = [];",
" var int32s = md.messageLengthSize / 4;",
" for(var i = 0; i < int32s; ++i) {",
" md.fullMessageLength.push(0);",
" }",
" _input = forge.util.createBuffer();",
" _h = new Array(_state.length);",
" for(var i = 0; i < _state.length; ++i) {",
" _h[i] = _state[i].slice(0);",
" }",
" return md;",
" };",
" // start digest automatically for first time",
" md.start();",
"",
" /**",
" * Updates the digest with the given message input. The given input can",
" * treated as raw input (no encoding will be applied) or an encoding of",
" * 'utf8' maybe given to encode the input using UTF-8.",
" *",
" * @param msg the message input to update with.",
" * @param encoding the encoding to use (default: 'raw', other: 'utf8').",
" *",
" * @return this digest object.",
" */",
" md.update = function(msg, encoding) {",
" if(encoding === 'utf8') {",
" msg = forge.util.encodeUtf8(msg);",
" }",
"",
" // update message length",
" var len = msg.length;",
" md.messageLength += len;",
" len = [(len / 0x100000000) >>> 0, len >>> 0];",
" for(var i = md.fullMessageLength.length - 1; i >= 0; --i) {",
" md.fullMessageLength[i] += len[1];",
" len[1] = len[0] + ((md.fullMessageLength[i] / 0x100000000) >>> 0);",
" md.fullMessageLength[i] = md.fullMessageLength[i] >>> 0;",
" len[0] = ((len[1] / 0x100000000) >>> 0);",
" }",
"",
" // add bytes to input buffer",
" _input.putBytes(msg);",
"",
" // process bytes",
" _update(_h, _w, _input);",
"",
" // compact input buffer every 2K or if empty",
" if(_input.read > 2048 || _input.length() === 0) {",
" _input.compact();",
" }",
"",
" return md;",
" };",
"",
" /**",
" * Produces the digest.",
" *",
" * @return a byte buffer containing the digest value.",
" */",
" md.digest = function() {",
" /* Note: Here we copy the remaining bytes in the input buffer and",
" add the appropriate SHA-512 padding. Then we do the final update",
" on a copy of the state so that if the user wants to get",
" intermediate digests they can do so. */",
"",
" /* Determine the number of bytes that must be added to the message",
" to ensure its length is congruent to 896 mod 1024. In other words,",
" the data to be digested must be a multiple of 1024 bits (or 128 bytes).",
" This data includes the message, some padding, and the length of the",
" message. Since the length of the message will be encoded as 16 bytes (128",
" bits), that means that the last segment of the data must have 112 bytes",
" (896 bits) of message and padding. Therefore, the length of the message",
" plus the padding must be congruent to 896 mod 1024 because",
" 1024 - 128 = 896.",
"",
" In order to fill up the message length it must be filled with",
" padding that begins with 1 bit followed by all 0 bits. Padding",
" must *always* be present, so if the message length is already",
" congruent to 896 mod 1024, then 1024 padding bits must be added. */",
"",
" var finalBlock = forge.util.createBuffer();",
" finalBlock.putBytes(_input.bytes());",
"",
" // compute remaining size to be digested (include message length size)",
" var remaining = (",
" md.fullMessageLength[md.fullMessageLength.length - 1] +",
" md.messageLengthSize);",
"",
" // add padding for overflow blockSize - overflow",
" // _padding starts with 1 byte with first bit is set (byte value 128), then",
" // there may be up to (blockSize - 1) other pad bytes",
" var overflow = remaining & (md.blockLength - 1);",
" finalBlock.putBytes(_padding.substr(0, md.blockLength - overflow));",
"",
" // serialize message length in bits in big-endian order; since length",
" // is stored in bytes we multiply by 8 and add carry from next int",
" var next, carry;",
" var bits = md.fullMessageLength[0] * 8;",
" for(var i = 0; i < md.fullMessageLength.length - 1; ++i) {",
" next = md.fullMessageLength[i + 1] * 8;",
" carry = (next / 0x100000000) >>> 0;",
" bits += carry;",
" finalBlock.putInt32(bits >>> 0);",
" bits = next >>> 0;",
" }",
" finalBlock.putInt32(bits);",
"",
" var h = new Array(_h.length);",
" for(var i = 0; i < _h.length; ++i) {",
" h[i] = _h[i].slice(0);",
" }",
" _update(h, _w, finalBlock);",
" var rval = forge.util.createBuffer();",
" var hlen;",
" if(algorithm === 'SHA-512') {",
" hlen = h.length;",
" } else if(algorithm === 'SHA-384') {",
" hlen = h.length - 2;",
" } else {",
" hlen = h.length - 4;",
" }",
" for(var i = 0; i < hlen; ++i) {",
" rval.putInt32(h[i][0]);",
" if(i !== hlen - 1 || algorithm !== 'SHA-512/224') {",
" rval.putInt32(h[i][1]);",
" }",
" }",
" return rval;",
" };",
"",
" return md;",
"};",
"",
"// sha-512 padding bytes not initialized yet",
"var _padding = null;",
"var _initialized = false;",
"",
"// table of constants",
"var _k = null;",
"",
"// initial hash states",
"var _states = null;",
"",
"/**",
" * Initializes the constant tables.",
" */",
"function _init() {",
" // create padding",
" _padding = String.fromCharCode(128);",
" _padding += forge.util.fillString(String.fromCharCode(0x00), 128);",
"",
" // create K table for SHA-512",
" _k = [",
" [0x428a2f98, 0xd728ae22], [0x71374491, 0x23ef65cd],",
" [0xb5c0fbcf, 0xec4d3b2f], [0xe9b5dba5, 0x8189dbbc],",
" [0x3956c25b, 0xf348b538], [0x59f111f1, 0xb605d019],",
" [0x923f82a4, 0xaf194f9b], [0xab1c5ed5, 0xda6d8118],",
" [0xd807aa98, 0xa3030242], [0x12835b01, 0x45706fbe],",
" [0x243185be, 0x4ee4b28c], [0x550c7dc3, 0xd5ffb4e2],",
" [0x72be5d74, 0xf27b896f], [0x80deb1fe, 0x3b1696b1],",
" [0x9bdc06a7, 0x25c71235], [0xc19bf174, 0xcf692694],",
" [0xe49b69c1, 0x9ef14ad2], [0xefbe4786, 0x384f25e3],",
" [0x0fc19dc6, 0x8b8cd5b5], [0x240ca1cc, 0x77ac9c65],",
" [0x2de92c6f, 0x592b0275], [0x4a7484aa, 0x6ea6e483],",
" [0x5cb0a9dc, 0xbd41fbd4], [0x76f988da, 0x831153b5],",
" [0x983e5152, 0xee66dfab], [0xa831c66d, 0x2db43210],",
" [0xb00327c8, 0x98fb213f], [0xbf597fc7, 0xbeef0ee4],",
" [0xc6e00bf3, 0x3da88fc2], [0xd5a79147, 0x930aa725],",
" [0x06ca6351, 0xe003826f], [0x14292967, 0x0a0e6e70],",
" [0x27b70a85, 0x46d22ffc], [0x2e1b2138, 0x5c26c926],",
" [0x4d2c6dfc, 0x5ac42aed], [0x53380d13, 0x9d95b3df],",
" [0x650a7354, 0x8baf63de], [0x766a0abb, 0x3c77b2a8],",
" [0x81c2c92e, 0x47edaee6], [0x92722c85, 0x1482353b],",
" [0xa2bfe8a1, 0x4cf10364], [0xa81a664b, 0xbc423001],",
" [0xc24b8b70, 0xd0f89791], [0xc76c51a3, 0x0654be30],",
" [0xd192e819, 0xd6ef5218], [0xd6990624, 0x5565a910],",
" [0xf40e3585, 0x5771202a], [0x106aa070, 0x32bbd1b8],",
" [0x19a4c116, 0xb8d2d0c8], [0x1e376c08, 0x5141ab53],",
" [0x2748774c, 0xdf8eeb99], [0x34b0bcb5, 0xe19b48a8],",
" [0x391c0cb3, 0xc5c95a63], [0x4ed8aa4a, 0xe3418acb],",
" [0x5b9cca4f, 0x7763e373], [0x682e6ff3, 0xd6b2b8a3],",
" [0x748f82ee, 0x5defb2fc], [0x78a5636f, 0x43172f60],",
" [0x84c87814, 0xa1f0ab72], [0x8cc70208, 0x1a6439ec],",
" [0x90befffa, 0x23631e28], [0xa4506ceb, 0xde82bde9],",
" [0xbef9a3f7, 0xb2c67915], [0xc67178f2, 0xe372532b],",
" [0xca273ece, 0xea26619c], [0xd186b8c7, 0x21c0c207],",
" [0xeada7dd6, 0xcde0eb1e], [0xf57d4f7f, 0xee6ed178],",
" [0x06f067aa, 0x72176fba], [0x0a637dc5, 0xa2c898a6],",
" [0x113f9804, 0xbef90dae], [0x1b710b35, 0x131c471b],",
" [0x28db77f5, 0x23047d84], [0x32caab7b, 0x40c72493],",
" [0x3c9ebe0a, 0x15c9bebc], [0x431d67c4, 0x9c100d4c],",
" [0x4cc5d4be, 0xcb3e42b6], [0x597f299c, 0xfc657e2a],",
" [0x5fcb6fab, 0x3ad6faec], [0x6c44198c, 0x4a475817]",
" ];",
"",
" // initial hash states",
" _states = {};",
" _states['SHA-512'] = [",
" [0x6a09e667, 0xf3bcc908],",
" [0xbb67ae85, 0x84caa73b],",
" [0x3c6ef372, 0xfe94f82b],",
" [0xa54ff53a, 0x5f1d36f1],",
" [0x510e527f, 0xade682d1],",
" [0x9b05688c, 0x2b3e6c1f],",
" [0x1f83d9ab, 0xfb41bd6b],",
" [0x5be0cd19, 0x137e2179]",
" ];",
" _states['SHA-384'] = [",
" [0xcbbb9d5d, 0xc1059ed8],",
" [0x629a292a, 0x367cd507],",
" [0x9159015a, 0x3070dd17],",
" [0x152fecd8, 0xf70e5939],",
" [0x67332667, 0xffc00b31],",
" [0x8eb44a87, 0x68581511],",
" [0xdb0c2e0d, 0x64f98fa7],",
" [0x47b5481d, 0xbefa4fa4]",
" ];",
" _states['SHA-512/256'] = [",
" [0x22312194, 0xFC2BF72C],",
" [0x9F555FA3, 0xC84C64C2],",
" [0x2393B86B, 0x6F53B151],",
" [0x96387719, 0x5940EABD],",
" [0x96283EE2, 0xA88EFFE3],",
" [0xBE5E1E25, 0x53863992],",
" [0x2B0199FC, 0x2C85B8AA],",
" [0x0EB72DDC, 0x81C52CA2]",
" ];",
" _states['SHA-512/224'] = [",
" [0x8C3D37C8, 0x19544DA2],",
" [0x73E19966, 0x89DCD4D6],",
" [0x1DFAB7AE, 0x32FF9C82],",
" [0x679DD514, 0x582F9FCF],",
" [0x0F6D2B69, 0x7BD44DA8],",
" [0x77E36F73, 0x04C48942],",
" [0x3F9D85A8, 0x6A1D36C8],",
" [0x1112E6AD, 0x91D692A1]",
" ];",
"",
" // now initialized",
" _initialized = true;",
"}",
"",
"/**",
" * Updates a SHA-512 state with the given byte buffer.",
" *",
" * @param s the SHA-512 state to update.",
" * @param w the array to use to store words.",
" * @param bytes the byte buffer to update with.",
" */",
"function _update(s, w, bytes) {",
" // consume 512 bit (128 byte) chunks",
" var t1_hi, t1_lo;",
" var t2_hi, t2_lo;",
" var s0_hi, s0_lo;",
" var s1_hi, s1_lo;",
" var ch_hi, ch_lo;",
" var maj_hi, maj_lo;",
" var a_hi, a_lo;",
" var b_hi, b_lo;",
" var c_hi, c_lo;",
" var d_hi, d_lo;",
" var e_hi, e_lo;",
" var f_hi, f_lo;",
" var g_hi, g_lo;",
" var h_hi, h_lo;",
" var i, hi, lo, w2, w7, w15, w16;",
" var len = bytes.length();",
" while(len >= 128) {",
" // the w array will be populated with sixteen 64-bit big-endian words",
" // and then extended into 64 64-bit words according to SHA-512",
" for(i = 0; i < 16; ++i) {",
" w[i][0] = bytes.getInt32() >>> 0;",
" w[i][1] = bytes.getInt32() >>> 0;",
" }",
" for(; i < 80; ++i) {",
" // for word 2 words ago: ROTR 19(x) ^ ROTR 61(x) ^ SHR 6(x)",
" w2 = w[i - 2];",
" hi = w2[0];",
" lo = w2[1];",
"",
" // high bits",
" t1_hi = (",
" ((hi >>> 19) | (lo << 13)) ^ // ROTR 19",
" ((lo >>> 29) | (hi << 3)) ^ // ROTR 61/(swap + ROTR 29)",
" (hi >>> 6)) >>> 0; // SHR 6",
" // low bits",
" t1_lo = (",
" ((hi << 13) | (lo >>> 19)) ^ // ROTR 19",
" ((lo << 3) | (hi >>> 29)) ^ // ROTR 61/(swap + ROTR 29)",
" ((hi << 26) | (lo >>> 6))) >>> 0; // SHR 6",
"",
" // for word 15 words ago: ROTR 1(x) ^ ROTR 8(x) ^ SHR 7(x)",
" w15 = w[i - 15];",
" hi = w15[0];",
" lo = w15[1];",
"",
" // high bits",
" t2_hi = (",
" ((hi >>> 1) | (lo << 31)) ^ // ROTR 1",
" ((hi >>> 8) | (lo << 24)) ^ // ROTR 8",
" (hi >>> 7)) >>> 0; // SHR 7",
" // low bits",
" t2_lo = (",
" ((hi << 31) | (lo >>> 1)) ^ // ROTR 1",
" ((hi << 24) | (lo >>> 8)) ^ // ROTR 8",
" ((hi << 25) | (lo >>> 7))) >>> 0; // SHR 7",
"",
" // sum(t1, word 7 ago, t2, word 16 ago) modulo 2^64 (carry lo overflow)",
" w7 = w[i - 7];",
" w16 = w[i - 16];",
" lo = (t1_lo + w7[1] + t2_lo + w16[1]);",
" w[i][0] = (t1_hi + w7[0] + t2_hi + w16[0] +",
" ((lo / 0x100000000) >>> 0)) >>> 0;",
" w[i][1] = lo >>> 0;",
" }",
"",
" // initialize hash value for this chunk",
" a_hi = s[0][0];",
" a_lo = s[0][1];",
" b_hi = s[1][0];",
" b_lo = s[1][1];",
" c_hi = s[2][0];",
" c_lo = s[2][1];",
" d_hi = s[3][0];",
" d_lo = s[3][1];",
" e_hi = s[4][0];",
" e_lo = s[4][1];",
" f_hi = s[5][0];",
" f_lo = s[5][1];",
" g_hi = s[6][0];",
" g_lo = s[6][1];",
" h_hi = s[7][0];",
" h_lo = s[7][1];",
"",
" // round function",
" for(i = 0; i < 80; ++i) {",
" // Sum1(e) = ROTR 14(e) ^ ROTR 18(e) ^ ROTR 41(e)",
" s1_hi = (",
" ((e_hi >>> 14) | (e_lo << 18)) ^ // ROTR 14",
" ((e_hi >>> 18) | (e_lo << 14)) ^ // ROTR 18",
" ((e_lo >>> 9) | (e_hi << 23))) >>> 0; // ROTR 41/(swap + ROTR 9)",
" s1_lo = (",
" ((e_hi << 18) | (e_lo >>> 14)) ^ // ROTR 14",
" ((e_hi << 14) | (e_lo >>> 18)) ^ // ROTR 18",
" ((e_lo << 23) | (e_hi >>> 9))) >>> 0; // ROTR 41/(swap + ROTR 9)",
"",
" // Ch(e, f, g) (optimized the same way as SHA-1)",
" ch_hi = (g_hi ^ (e_hi & (f_hi ^ g_hi))) >>> 0;",
" ch_lo = (g_lo ^ (e_lo & (f_lo ^ g_lo))) >>> 0;",
"",
" // Sum0(a) = ROTR 28(a) ^ ROTR 34(a) ^ ROTR 39(a)",
" s0_hi = (",
" ((a_hi >>> 28) | (a_lo << 4)) ^ // ROTR 28",
" ((a_lo >>> 2) | (a_hi << 30)) ^ // ROTR 34/(swap + ROTR 2)",
" ((a_lo >>> 7) | (a_hi << 25))) >>> 0; // ROTR 39/(swap + ROTR 7)",
" s0_lo = (",
" ((a_hi << 4) | (a_lo >>> 28)) ^ // ROTR 28",
" ((a_lo << 30) | (a_hi >>> 2)) ^ // ROTR 34/(swap + ROTR 2)",
" ((a_lo << 25) | (a_hi >>> 7))) >>> 0; // ROTR 39/(swap + ROTR 7)",
"",
" // Maj(a, b, c) (optimized the same way as SHA-1)",
" maj_hi = ((a_hi & b_hi) | (c_hi & (a_hi ^ b_hi))) >>> 0;",
" maj_lo = ((a_lo & b_lo) | (c_lo & (a_lo ^ b_lo))) >>> 0;",
"",
" // main algorithm",
" // t1 = (h + s1 + ch + _k[i] + _w[i]) modulo 2^64 (carry lo overflow)",
" lo = (h_lo + s1_lo + ch_lo + _k[i][1] + w[i][1]);",
" t1_hi = (h_hi + s1_hi + ch_hi + _k[i][0] + w[i][0] +",
" ((lo / 0x100000000) >>> 0)) >>> 0;",
" t1_lo = lo >>> 0;",
"",
" // t2 = s0 + maj modulo 2^64 (carry lo overflow)",
" lo = s0_lo + maj_lo;",
" t2_hi = (s0_hi + maj_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" t2_lo = lo >>> 0;",
"",
" h_hi = g_hi;",
" h_lo = g_lo;",
"",
" g_hi = f_hi;",
" g_lo = f_lo;",
"",
" f_hi = e_hi;",
" f_lo = e_lo;",
"",
" // e = (d + t1) modulo 2^64 (carry lo overflow)",
" lo = d_lo + t1_lo;",
" e_hi = (d_hi + t1_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" e_lo = lo >>> 0;",
"",
" d_hi = c_hi;",
" d_lo = c_lo;",
"",
" c_hi = b_hi;",
" c_lo = b_lo;",
"",
" b_hi = a_hi;",
" b_lo = a_lo;",
"",
" // a = (t1 + t2) modulo 2^64 (carry lo overflow)",
" lo = t1_lo + t2_lo;",
" a_hi = (t1_hi + t2_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" a_lo = lo >>> 0;",
" }",
"",
" // update hash state (additional modulo 2^64)",
" lo = s[0][1] + a_lo;",
" s[0][0] = (s[0][0] + a_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[0][1] = lo >>> 0;",
"",
" lo = s[1][1] + b_lo;",
" s[1][0] = (s[1][0] + b_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[1][1] = lo >>> 0;",
"",
" lo = s[2][1] + c_lo;",
" s[2][0] = (s[2][0] + c_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[2][1] = lo >>> 0;",
"",
" lo = s[3][1] + d_lo;",
" s[3][0] = (s[3][0] + d_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[3][1] = lo >>> 0;",
"",
" lo = s[4][1] + e_lo;",
" s[4][0] = (s[4][0] + e_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[4][1] = lo >>> 0;",
"",
" lo = s[5][1] + f_lo;",
" s[5][0] = (s[5][0] + f_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[5][1] = lo >>> 0;",
"",
" lo = s[6][1] + g_lo;",
" s[6][0] = (s[6][0] + g_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[6][1] = lo >>> 0;",
"",
" lo = s[7][1] + h_lo;",
" s[7][0] = (s[7][0] + h_hi + ((lo / 0x100000000) >>> 0)) >>> 0;",
" s[7][1] = lo >>> 0;",
"",
" len -= 128;",
" }",
"}",
"",
"",
"/***/ }),",
"/* 33 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Cross-browser support for logging in a web application.",
" *",
" * @author David I. Lehn ",
" *",
" * Copyright (c) 2008-2013 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"",
"/* LOG API */",
"module.exports = forge.log = forge.log || {};",
"",
"/**",
" * Application logging system.",
" *",
" * Each logger level available as it's own function of the form:",
" * forge.log.level(category, args...)",
" * The category is an arbitrary string, and the args are the same as",
" * Firebug's console.log API. By default the call will be output as:",
" * 'LEVEL [category] , args[1], ...'",
" * This enables proper % formatting via the first argument.",
" * Each category is enabled by default but can be enabled or disabled with",
" * the setCategoryEnabled() function.",
" */",
"// list of known levels",
"forge.log.levels = [",
" 'none', 'error', 'warning', 'info', 'debug', 'verbose', 'max'];",
"// info on the levels indexed by name:",
"// index: level index",
"// name: uppercased display name",
"var sLevelInfo = {};",
"// list of loggers",
"var sLoggers = [];",
"/**",
" * Standard console logger. If no console support is enabled this will",
" * remain null. Check before using.",
" */",
"var sConsoleLogger = null;",
"",
"// logger flags",
"/**",
" * Lock the level at the current value. Used in cases where user config may",
" * set the level such that only critical messages are seen but more verbose",
" * messages are needed for debugging or other purposes.",
" */",
"forge.log.LEVEL_LOCKED = (1 << 1);",
"/**",
" * Always call log function. By default, the logging system will check the",
" * message level against logger.level before calling the log function. This",
" * flag allows the function to do its own check.",
" */",
"forge.log.NO_LEVEL_CHECK = (1 << 2);",
"/**",
" * Perform message interpolation with the passed arguments. \"%\" style",
" * fields in log messages will be replaced by arguments as needed. Some",
" * loggers, such as Firebug, may do this automatically. The original log",
" * message will be available as 'message' and the interpolated version will",
" * be available as 'fullMessage'.",
" */",
"forge.log.INTERPOLATE = (1 << 3);",
"",
"// setup each log level",
"for(var i = 0; i < forge.log.levels.length; ++i) {",
" var level = forge.log.levels[i];",
" sLevelInfo[level] = {",
" index: i,",
" name: level.toUpperCase()",
" };",
"}",
"",
"/**",
" * Message logger. Will dispatch a message to registered loggers as needed.",
" *",
" * @param message message object",
" */",
"forge.log.logMessage = function(message) {",
" var messageLevelIndex = sLevelInfo[message.level].index;",
" for(var i = 0; i < sLoggers.length; ++i) {",
" var logger = sLoggers[i];",
" if(logger.flags & forge.log.NO_LEVEL_CHECK) {",
" logger.f(message);",
" } else {",
" // get logger level",
" var loggerLevelIndex = sLevelInfo[logger.level].index;",
" // check level",
" if(messageLevelIndex <= loggerLevelIndex) {",
" // message critical enough, call logger",
" logger.f(logger, message);",
" }",
" }",
" }",
"};",
"",
"/**",
" * Sets the 'standard' key on a message object to:",
" * \"LEVEL [category] \" + message",
" *",
" * @param message a message log object",
" */",
"forge.log.prepareStandard = function(message) {",
" if(!('standard' in message)) {",
" message.standard =",
" sLevelInfo[message.level].name +",
" //' ' + +message.timestamp +",
" ' [' + message.category + '] ' +",
" message.message;",
" }",
"};",
"",
"/**",
" * Sets the 'full' key on a message object to the original message",
" * interpolated via % formatting with the message arguments.",
" *",
" * @param message a message log object.",
" */",
"forge.log.prepareFull = function(message) {",
" if(!('full' in message)) {",
" // copy args and insert message at the front",
" var args = [message.message];",
" args = args.concat([] || message['arguments']);",
" // format the message",
" message.full = forge.util.format.apply(this, args);",
" }",
"};",
"",
"/**",
" * Applies both preparseStandard() and prepareFull() to a message object and",
" * store result in 'standardFull'.",
" *",
" * @param message a message log object.",
" */",
"forge.log.prepareStandardFull = function(message) {",
" if(!('standardFull' in message)) {",
" // FIXME implement 'standardFull' logging",
" forge.log.prepareStandard(message);",
" message.standardFull = message.standard;",
" }",
"};",
"",
"// create log level functions",
"if(true) {",
" // levels for which we want functions",
" var levels = ['error', 'warning', 'info', 'debug', 'verbose'];",
" for(var i = 0; i < levels.length; ++i) {",
" // wrap in a function to ensure proper level var is passed",
" (function(level) {",
" // create function for this level",
" forge.log[level] = function(category, message/*, args...*/) {",
" // convert arguments to real array, remove category and message",
" var args = Array.prototype.slice.call(arguments).slice(2);",
" // create message object",
" // Note: interpolation and standard formatting is done lazily",
" var msg = {",
" timestamp: new Date(),",
" level: level,",
" category: category,",
" message: message,",
" 'arguments': args",
" /*standard*/",
" /*full*/",
" /*fullMessage*/",
" };",
" // process this message",
" forge.log.logMessage(msg);",
" };",
" })(levels[i]);",
" }",
"}",
"",
"/**",
" * Creates a new logger with specified custom logging function.",
" *",
" * The logging function has a signature of:",
" * function(logger, message)",
" * logger: current logger",
" * message: object:",
" * level: level id",
" * category: category",
" * message: string message",
" * arguments: Array of extra arguments",
" * fullMessage: interpolated message and arguments if INTERPOLATE flag set",
" *",
" * @param logFunction a logging function which takes a log message object",
" * as a parameter.",
" *",
" * @return a logger object.",
" */",
"forge.log.makeLogger = function(logFunction) {",
" var logger = {",
" flags: 0,",
" f: logFunction",
" };",
" forge.log.setLevel(logger, 'none');",
" return logger;",
"};",
"",
"/**",
" * Sets the current log level on a logger.",
" *",
" * @param logger the target logger.",
" * @param level the new maximum log level as a string.",
" *",
" * @return true if set, false if not.",
" */",
"forge.log.setLevel = function(logger, level) {",
" var rval = false;",
" if(logger && !(logger.flags & forge.log.LEVEL_LOCKED)) {",
" for(var i = 0; i < forge.log.levels.length; ++i) {",
" var aValidLevel = forge.log.levels[i];",
" if(level == aValidLevel) {",
" // set level",
" logger.level = level;",
" rval = true;",
" break;",
" }",
" }",
" }",
"",
" return rval;",
"};",
"",
"/**",
" * Locks the log level at its current value.",
" *",
" * @param logger the target logger.",
" * @param lock boolean lock value, default to true.",
" */",
"forge.log.lock = function(logger, lock) {",
" if(typeof lock === 'undefined' || lock) {",
" logger.flags |= forge.log.LEVEL_LOCKED;",
" } else {",
" logger.flags &= ~forge.log.LEVEL_LOCKED;",
" }",
"};",
"",
"/**",
" * Adds a logger.",
" *",
" * @param logger the logger object.",
" */",
"forge.log.addLogger = function(logger) {",
" sLoggers.push(logger);",
"};",
"",
"// setup the console logger if possible, else create fake console.log",
"if(typeof(console) !== 'undefined' && 'log' in console) {",
" var logger;",
" if(console.error && console.warn && console.info && console.debug) {",
" // looks like Firebug-style logging is available",
" // level handlers map",
" var levelHandlers = {",
" error: console.error,",
" warning: console.warn,",
" info: console.info,",
" debug: console.debug,",
" verbose: console.debug",
" };",
" var f = function(logger, message) {",
" forge.log.prepareStandard(message);",
" var handler = levelHandlers[message.level];",
" // prepend standard message and concat args",
" var args = [message.standard];",
" args = args.concat(message['arguments'].slice());",
" // apply to low-level console function",
" handler.apply(console, args);",
" };",
" logger = forge.log.makeLogger(f);",
" } else {",
" // only appear to have basic console.log",
" var f = function(logger, message) {",
" forge.log.prepareStandardFull(message);",
" console.log(message.standardFull);",
" };",
" logger = forge.log.makeLogger(f);",
" }",
" forge.log.setLevel(logger, 'debug');",
" forge.log.addLogger(logger);",
" sConsoleLogger = logger;",
"} else {",
" // define fake console.log to avoid potential script errors on",
" // browsers that do not have console logging",
" console = {",
" log: function() {}",
" };",
"}",
"",
"/*",
" * Check for logging control query vars.",
" *",
" * console.level=",
" * Set's the console log level by name. Useful to override defaults and",
" * allow more verbose logging before a user config is loaded.",
" *",
" * console.lock=",
" * Lock the console log level at whatever level it is set at. This is run",
" * after console.level is processed. Useful to force a level of verbosity",
" * that could otherwise be limited by a user config.",
" */",
"if(sConsoleLogger !== null) {",
" var query = forge.util.getQueryVariables();",
" if('console.level' in query) {",
" // set with last value",
" forge.log.setLevel(",
" sConsoleLogger, query['console.level'].slice(-1)[0]);",
" }",
" if('console.lock' in query) {",
" // set with last value",
" var lock = query['console.lock'].slice(-1)[0];",
" if(lock == 'true') {",
" forge.log.lock(sConsoleLogger);",
" }",
" }",
"}",
"",
"// provide public access to console logger",
"forge.log.consoleLogger = sConsoleLogger;",
"",
"",
"/***/ }),",
"/* 34 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"module.exports = __webpack_require__(35);",
"",
"",
"/***/ }),",
"/* 35 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Node.js module for Forge.",
" *",
" * @author Dave Longley",
" *",
" * Copyright 2011-2016 Digital Bazaar, Inc.",
" */",
"module.exports = __webpack_require__(0);",
"__webpack_require__(5);",
"__webpack_require__(37);",
"__webpack_require__(3);",
"__webpack_require__(13);",
"__webpack_require__(31);",
"__webpack_require__(10);",
"__webpack_require__(39);",
"__webpack_require__(8);",
"__webpack_require__(40);",
"__webpack_require__(33);",
"__webpack_require__(41);",
"__webpack_require__(30);",
"__webpack_require__(15);",
"__webpack_require__(7);",
"__webpack_require__(26);",
"__webpack_require__(28);",
"__webpack_require__(42);",
"__webpack_require__(20);",
"__webpack_require__(27);",
"__webpack_require__(24);",
"__webpack_require__(17);",
"__webpack_require__(2);",
"__webpack_require__(25);",
"__webpack_require__(43);",
"__webpack_require__(44);",
"__webpack_require__(19);",
"__webpack_require__(1);",
"",
"",
"/***/ }),",
"/* 36 */",
"/***/ (function(module, exports) {",
"",
"/**",
" * Base-N/Base-X encoding/decoding functions.",
" *",
" * Original implementation from base-x:",
" * https://github.com/cryptocoinjs/base-x",
" *",
" * Which is MIT licensed:",
" *",
" * The MIT License (MIT)",
" *",
" * Copyright base-x contributors (c) 2016",
" *",
" * Permission is hereby granted, free of charge, to any person obtaining a copy",
" * of this software and associated documentation files (the \"Software\"), to deal",
" * in the Software without restriction, including without limitation the rights",
" * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
" * copies of the Software, and to permit persons to whom the Software is",
" * furnished to do so, subject to the following conditions:",
" *",
" * The above copyright notice and this permission notice shall be included in",
" * all copies or substantial portions of the Software.",
" *",
" * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
" * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
" * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
" * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
" * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING",
" * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER",
" * DEALINGS IN THE SOFTWARE.",
" */",
"var api = {};",
"module.exports = api;",
"",
"// baseN alphabet indexes",
"var _reverseAlphabets = {};",
"",
"/**",
" * BaseN-encodes a Uint8Array using the given alphabet.",
" *",
" * @param input the Uint8Array to encode.",
" * @param maxline the maximum number of encoded characters per line to use,",
" * defaults to none.",
" *",
" * @return the baseN-encoded output string.",
" */",
"api.encode = function(input, alphabet, maxline) {",
" if(typeof alphabet !== 'string') {",
" throw new TypeError('\"alphabet\" must be a string.');",
" }",
" if(maxline !== undefined && typeof maxline !== 'number') {",
" throw new TypeError('\"maxline\" must be a number.');",
" }",
"",
" var output = '';",
"",
" if(!(input instanceof Uint8Array)) {",
" // assume forge byte buffer",
" output = _encodeWithByteBuffer(input, alphabet);",
" } else {",
" var i = 0;",
" var base = alphabet.length;",
" var first = alphabet.charAt(0);",
" var digits = [0];",
" for(i = 0; i < input.length; ++i) {",
" for(var j = 0, carry = input[i]; j < digits.length; ++j) {",
" carry += digits[j] << 8;",
" digits[j] = carry % base;",
" carry = (carry / base) | 0;",
" }",
"",
" while(carry > 0) {",
" digits.push(carry % base);",
" carry = (carry / base) | 0;",
" }",
" }",
"",
" // deal with leading zeros",
" for(i = 0; input[i] === 0 && i < input.length - 1; ++i) {",
" output += first;",
" }",
" // convert digits to a string",
" for(i = digits.length - 1; i >= 0; --i) {",
" output += alphabet[digits[i]];",
" }",
" }",
"",
" if(maxline) {",
" var regex = new RegExp('.{1,' + maxline + '}', 'g');",
" output = output.match(regex).join('\\r\\n');",
" }",
"",
" return output;",
"};",
"",
"/**",
" * Decodes a baseN-encoded (using the given alphabet) string to a",
" * Uint8Array.",
" *",
" * @param input the baseN-encoded input string.",
" *",
" * @return the Uint8Array.",
" */",
"api.decode = function(input, alphabet) {",
" if(typeof input !== 'string') {",
" throw new TypeError('\"input\" must be a string.');",
" }",
" if(typeof alphabet !== 'string') {",
" throw new TypeError('\"alphabet\" must be a string.');",
" }",
"",
" var table = _reverseAlphabets[alphabet];",
" if(!table) {",
" // compute reverse alphabet",
" table = _reverseAlphabets[alphabet] = [];",
" for(var i = 0; i < alphabet.length; ++i) {",
" table[alphabet.charCodeAt(i)] = i;",
" }",
" }",
"",
" // remove whitespace characters",
" input = input.replace(/\\s/g, '');",
"",
" var base = alphabet.length;",
" var first = alphabet.charAt(0);",
" var bytes = [0];",
" for(var i = 0; i < input.length; i++) {",
" var value = table[input.charCodeAt(i)];",
" if(value === undefined) {",
" return;",
" }",
"",
" for(var j = 0, carry = value; j < bytes.length; ++j) {",
" carry += bytes[j] * base;",
" bytes[j] = carry & 0xff;",
" carry >>= 8;",
" }",
"",
" while(carry > 0) {",
" bytes.push(carry & 0xff);",
" carry >>= 8;",
" }",
" }",
"",
" // deal with leading zeros",
" for(var k = 0; input[k] === first && k < input.length - 1; ++k) {",
" bytes.push(0);",
" }",
"",
" if(typeof Buffer !== 'undefined') {",
" return Buffer.from(bytes.reverse());",
" }",
"",
" return new Uint8Array(bytes.reverse());",
"};",
"",
"function _encodeWithByteBuffer(input, alphabet) {",
" var i = 0;",
" var base = alphabet.length;",
" var first = alphabet.charAt(0);",
" var digits = [0];",
" for(i = 0; i < input.length(); ++i) {",
" for(var j = 0, carry = input.at(i); j < digits.length; ++j) {",
" carry += digits[j] << 8;",
" digits[j] = carry % base;",
" carry = (carry / base) | 0;",
" }",
"",
" while(carry > 0) {",
" digits.push(carry % base);",
" carry = (carry / base) | 0;",
" }",
" }",
"",
" var output = '';",
"",
" // deal with leading zeros",
" for(i = 0; input.at(i) === 0 && i < input.length() - 1; ++i) {",
" output += first;",
" }",
" // convert digits to a string",
" for(i = digits.length - 1; i >= 0; --i) {",
" output += alphabet[digits[i]];",
" }",
"",
" return output;",
"}",
"",
"",
"/***/ }),",
"/* 37 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * A Javascript implementation of AES Cipher Suites for TLS.",
" *",
" * @author Dave Longley",
" *",
" * Copyright (c) 2009-2015 Digital Bazaar, Inc.",
" *",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(5);",
"__webpack_require__(19);",
"",
"var tls = module.exports = forge.tls;",
"",
"/**",
" * Supported cipher suites.",
" */",
"tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = {",
" id: [0x00,0x2f],",
" name: 'TLS_RSA_WITH_AES_128_CBC_SHA',",
" initSecurityParameters: function(sp) {",
" sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;",
" sp.cipher_type = tls.CipherType.block;",
" sp.enc_key_length = 16;",
" sp.block_length = 16;",
" sp.fixed_iv_length = 16;",
" sp.record_iv_length = 16;",
" sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;",
" sp.mac_length = 20;",
" sp.mac_key_length = 20;",
" },",
" initConnectionState: initConnectionState",
"};",
"tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = {",
" id: [0x00,0x35],",
" name: 'TLS_RSA_WITH_AES_256_CBC_SHA',",
" initSecurityParameters: function(sp) {",
" sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;",
" sp.cipher_type = tls.CipherType.block;",
" sp.enc_key_length = 32;",
" sp.block_length = 16;",
" sp.fixed_iv_length = 16;",
" sp.record_iv_length = 16;",
" sp.mac_algorithm = tls.MACAlgorithm.hmac_sha1;",
" sp.mac_length = 20;",
" sp.mac_key_length = 20;",
" },",
" initConnectionState: initConnectionState",
"};",
"",
"function initConnectionState(state, c, sp) {",
" var client = (c.entity === forge.tls.ConnectionEnd.client);",
"",
" // cipher setup",
" state.read.cipherState = {",
" init: false,",
" cipher: forge.cipher.createDecipher('AES-CBC', client ?",
" sp.keys.server_write_key : sp.keys.client_write_key),",
" iv: client ? sp.keys.server_write_IV : sp.keys.client_write_IV",
" };",
" state.write.cipherState = {",
" init: false,",
" cipher: forge.cipher.createCipher('AES-CBC', client ?",
" sp.keys.client_write_key : sp.keys.server_write_key),",
" iv: client ? sp.keys.client_write_IV : sp.keys.server_write_IV",
" };",
" state.read.cipherFunction = decrypt_aes_cbc_sha1;",
" state.write.cipherFunction = encrypt_aes_cbc_sha1;",
"",
" // MAC setup",
" state.read.macLength = state.write.macLength = sp.mac_length;",
" state.read.macFunction = state.write.macFunction = tls.hmac_sha1;",
"}",
"",
"/**",
" * Encrypts the TLSCompressed record into a TLSCipherText record using AES",
" * in CBC mode.",
" *",
" * @param record the TLSCompressed record to encrypt.",
" * @param s the ConnectionState to use.",
" *",
" * @return true on success, false on failure.",
" */",
"function encrypt_aes_cbc_sha1(record, s) {",
" var rval = false;",
"",
" // append MAC to fragment, update sequence number",
" var mac = s.macFunction(s.macKey, s.sequenceNumber, record);",
" record.fragment.putBytes(mac);",
" s.updateSequenceNumber();",
"",
" // TLS 1.1+ use an explicit IV every time to protect against CBC attacks",
" var iv;",
" if(record.version.minor === tls.Versions.TLS_1_0.minor) {",
" // use the pre-generated IV when initializing for TLS 1.0, otherwise use",
" // the residue from the previous encryption",
" iv = s.cipherState.init ? null : s.cipherState.iv;",
" } else {",
" iv = forge.random.getBytesSync(16);",
" }",
"",
" s.cipherState.init = true;",
"",
" // start cipher",
" var cipher = s.cipherState.cipher;",
" cipher.start({iv: iv});",
"",
" // TLS 1.1+ write IV into output",
" if(record.version.minor >= tls.Versions.TLS_1_1.minor) {",
" cipher.output.putBytes(iv);",
" }",
"",
" // do encryption (default padding is appropriate)",
" cipher.update(record.fragment);",
" if(cipher.finish(encrypt_aes_cbc_sha1_padding)) {",
" // set record fragment to encrypted output",
" record.fragment = cipher.output;",
" record.length = record.fragment.length();",
" rval = true;",
" }",
"",
" return rval;",
"}",
"",
"/**",
" * Handles padding for aes_cbc_sha1 in encrypt mode.",
" *",
" * @param blockSize the block size.",
" * @param input the input buffer.",
" * @param decrypt true in decrypt mode, false in encrypt mode.",
" *",
" * @return true on success, false on failure.",
" */",
"function encrypt_aes_cbc_sha1_padding(blockSize, input, decrypt) {",
" /* The encrypted data length (TLSCiphertext.length) is one more than the sum",
" of SecurityParameters.block_length, TLSCompressed.length,",
" SecurityParameters.mac_length, and padding_length.",
"",
" The padding may be any length up to 255 bytes long, as long as it results in",
" the TLSCiphertext.length being an integral multiple of the block length.",
" Lengths longer than necessary might be desirable to frustrate attacks on a",
" protocol based on analysis of the lengths of exchanged messages. Each uint8",
" in the padding data vector must be filled with the padding length value.",
"",
" The padding length should be such that the total size of the",
" GenericBlockCipher structure is a multiple of the cipher's block length.",
" Legal values range from zero to 255, inclusive. This length specifies the",
" length of the padding field exclusive of the padding_length field itself.",
"",
" This is slightly different from PKCS#7 because the padding value is 1",
" less than the actual number of padding bytes if you include the",
" padding_length uint8 itself as a padding byte. */",
" if(!decrypt) {",
" // get the number of padding bytes required to reach the blockSize and",
" // subtract 1 for the padding value (to make room for the padding_length",
" // uint8)",
" var padding = blockSize - (input.length() % blockSize);",
" input.fillWithByte(padding - 1, padding);",
" }",
" return true;",
"}",
"",
"/**",
" * Handles padding for aes_cbc_sha1 in decrypt mode.",
" *",
" * @param blockSize the block size.",
" * @param output the output buffer.",
" * @param decrypt true in decrypt mode, false in encrypt mode.",
" *",
" * @return true on success, false on failure.",
" */",
"function decrypt_aes_cbc_sha1_padding(blockSize, output, decrypt) {",
" var rval = true;",
" if(decrypt) {",
" /* The last byte in the output specifies the number of padding bytes not",
" including itself. Each of the padding bytes has the same value as that",
" last byte (known as the padding_length). Here we check all padding",
" bytes to ensure they have the value of padding_length even if one of",
" them is bad in order to ward-off timing attacks. */",
" var len = output.length();",
" var paddingLength = output.last();",
" for(var i = len - 1 - paddingLength; i < len - 1; ++i) {",
" rval = rval && (output.at(i) == paddingLength);",
" }",
" if(rval) {",
" // trim off padding bytes and last padding length byte",
" output.truncate(paddingLength + 1);",
" }",
" }",
" return rval;",
"}",
"",
"/**",
" * Decrypts a TLSCipherText record into a TLSCompressed record using",
" * AES in CBC mode.",
" *",
" * @param record the TLSCipherText record to decrypt.",
" * @param s the ConnectionState to use.",
" *",
" * @return true on success, false on failure.",
" */",
"var count = 0;",
"function decrypt_aes_cbc_sha1(record, s) {",
" var rval = false;",
" ++count;",
"",
" var iv;",
" if(record.version.minor === tls.Versions.TLS_1_0.minor) {",
" // use pre-generated IV when initializing for TLS 1.0, otherwise use the",
" // residue from the previous decryption",
" iv = s.cipherState.init ? null : s.cipherState.iv;",
" } else {",
" // TLS 1.1+ use an explicit IV every time to protect against CBC attacks",
" // that is appended to the record fragment",
" iv = record.fragment.getBytes(16);",
" }",
"",
" s.cipherState.init = true;",
"",
" // start cipher",
" var cipher = s.cipherState.cipher;",
" cipher.start({iv: iv});",
"",
" // do decryption",
" cipher.update(record.fragment);",
" rval = cipher.finish(decrypt_aes_cbc_sha1_padding);",
"",
" // even if decryption fails, keep going to minimize timing attacks",
"",
" // decrypted data:",
" // first (len - 20) bytes = application data",
" // last 20 bytes = MAC",
" var macLen = s.macLength;",
"",
" // create a random MAC to check against should the mac length check fail",
" // Note: do this regardless of the failure to keep timing consistent",
" var mac = forge.random.getBytesSync(macLen);",
"",
" // get fragment and mac",
" var len = cipher.output.length();",
" if(len >= macLen) {",
" record.fragment = cipher.output.getBytes(len - macLen);",
" mac = cipher.output.getBytes(macLen);",
" } else {",
" // bad data, but get bytes anyway to try to keep timing consistent",
" record.fragment = cipher.output.getBytes();",
" }",
" record.fragment = forge.util.createBuffer(record.fragment);",
" record.length = record.fragment.length();",
"",
" // see if data integrity checks out, update sequence number",
" var mac2 = s.macFunction(s.macKey, s.sequenceNumber, record);",
" s.updateSequenceNumber();",
" rval = compareMacs(s.macKey, mac, mac2) && rval;",
" return rval;",
"}",
"",
"/**",
" * Safely compare two MACs. This function will compare two MACs in a way",
" * that protects against timing attacks.",
" *",
" * TODO: Expose elsewhere as a utility API.",
" *",
" * See: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/",
" *",
" * @param key the MAC key to use.",
" * @param mac1 as a binary-encoded string of bytes.",
" * @param mac2 as a binary-encoded string of bytes.",
" *",
" * @return true if the MACs are the same, false if not.",
" */",
"function compareMacs(key, mac1, mac2) {",
" var hmac = forge.hmac.create();",
"",
" hmac.start('SHA1', key);",
" hmac.update(mac1);",
" mac1 = hmac.digest().getBytes();",
"",
" hmac.start(null, null);",
" hmac.update(mac2);",
" mac2 = hmac.digest().getBytes();",
"",
" return mac1 === mac2;",
"}",
"",
"",
"/***/ }),",
"/* 38 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Node.js module for Forge mask generation functions.",
" *",
" * @author Stefan Siegl",
" *",
" * Copyright 2012 Stefan Siegl ",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(30);",
"",
"module.exports = forge.mgf = forge.mgf || {};",
"forge.mgf.mgf1 = forge.mgf1;",
"",
"",
"/***/ }),",
"/* 39 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * JavaScript implementation of Ed25519.",
" *",
" * Copyright (c) 2017-2018 Digital Bazaar, Inc.",
" *",
" * This implementation is based on the most excellent TweetNaCl which is",
" * in the public domain. Many thanks to its contributors:",
" *",
" * https://github.com/dchest/tweetnacl-js",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(12);",
"__webpack_require__(2);",
"__webpack_require__(32);",
"__webpack_require__(1);",
"",
"if(typeof BigInteger === 'undefined') {",
" var BigInteger = forge.jsbn.BigInteger;",
"}",
"",
"var ByteBuffer = forge.util.ByteBuffer;",
"var NativeBuffer = typeof Buffer === 'undefined' ? Uint8Array : Buffer;",
"",
"/*",
" * Ed25519 algorithms, see RFC 8032:",
" * https://tools.ietf.org/html/rfc8032",
" */",
"forge.pki = forge.pki || {};",
"module.exports = forge.pki.ed25519 = forge.ed25519 = forge.ed25519 || {};",
"var ed25519 = forge.ed25519;",
"",
"ed25519.constants = {};",
"ed25519.constants.PUBLIC_KEY_BYTE_LENGTH = 32;",
"ed25519.constants.PRIVATE_KEY_BYTE_LENGTH = 64;",
"ed25519.constants.SEED_BYTE_LENGTH = 32;",
"ed25519.constants.SIGN_BYTE_LENGTH = 64;",
"ed25519.constants.HASH_BYTE_LENGTH = 64;",
"",
"ed25519.generateKeyPair = function(options) {",
" options = options || {};",
" var seed = options.seed;",
" if(seed === undefined) {",
" // generate seed",
" seed = forge.random.getBytesSync(ed25519.constants.SEED_BYTE_LENGTH);",
" } else if(typeof seed === 'string') {",
" if(seed.length !== ed25519.constants.SEED_BYTE_LENGTH) {",
" throw new TypeError(",
" '\"seed\" must be ' + ed25519.constants.SEED_BYTE_LENGTH +",
" ' bytes in length.');",
" }",
" } else if(!(seed instanceof Uint8Array)) {",
" throw new TypeError(",
" '\"seed\" must be a node.js Buffer, Uint8Array, or a binary string.');",
" }",
"",
" seed = messageToNativeBuffer({message: seed, encoding: 'binary'});",
"",
" var pk = new NativeBuffer(ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);",
" var sk = new NativeBuffer(ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);",
" for(var i = 0; i < 32; ++i) {",
" sk[i] = seed[i];",
" }",
" crypto_sign_keypair(pk, sk);",
" return {publicKey: pk, privateKey: sk};",
"};",
"",
"ed25519.publicKeyFromPrivateKey = function(options) {",
" options = options || {};",
" var privateKey = messageToNativeBuffer({",
" message: options.privateKey, encoding: 'binary'",
" });",
" if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) {",
" throw new TypeError(",
" '\"options.privateKey\" must have a byte length of ' +",
" ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);",
" }",
"",
" var pk = new NativeBuffer(ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);",
" for(var i = 0; i < pk.length; ++i) {",
" pk[i] = privateKey[32 + i];",
" }",
" return pk;",
"};",
"",
"ed25519.sign = function(options) {",
" options = options || {};",
" var msg = messageToNativeBuffer(options);",
" var privateKey = messageToNativeBuffer({",
" message: options.privateKey,",
" encoding: 'binary'",
" });",
" if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) {",
" throw new TypeError(",
" '\"options.privateKey\" must have a byte length of ' +",
" ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);",
" }",
"",
" var signedMsg = new NativeBuffer(",
" ed25519.constants.SIGN_BYTE_LENGTH + msg.length);",
" crypto_sign(signedMsg, msg, msg.length, privateKey);",
"",
" var sig = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH);",
" for(var i = 0; i < sig.length; ++i) {",
" sig[i] = signedMsg[i];",
" }",
" return sig;",
"};",
"",
"ed25519.verify = function(options) {",
" options = options || {};",
" var msg = messageToNativeBuffer(options);",
" if(options.signature === undefined) {",
" throw new TypeError(",
" '\"options.signature\" must be a node.js Buffer, a Uint8Array, a forge ' +",
" 'ByteBuffer, or a binary string.');",
" }",
" var sig = messageToNativeBuffer({",
" message: options.signature,",
" encoding: 'binary'",
" });",
" if(sig.length !== ed25519.constants.SIGN_BYTE_LENGTH) {",
" throw new TypeError(",
" '\"options.signature\" must have a byte length of ' +",
" ed25519.constants.SIGN_BYTE_LENGTH);",
" }",
" var publicKey = messageToNativeBuffer({",
" message: options.publicKey,",
" encoding: 'binary'",
" });",
" if(publicKey.length !== ed25519.constants.PUBLIC_KEY_BYTE_LENGTH) {",
" throw new TypeError(",
" '\"options.publicKey\" must have a byte length of ' +",
" ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);",
" }",
"",
" var sm = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH + msg.length);",
" var m = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH + msg.length);",
" var i;",
" for(i = 0; i < ed25519.constants.SIGN_BYTE_LENGTH; ++i) {",
" sm[i] = sig[i];",
" }",
" for(i = 0; i < msg.length; ++i) {",
" sm[i + ed25519.constants.SIGN_BYTE_LENGTH] = msg[i];",
" }",
" return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0);",
"};",
"",
"function messageToNativeBuffer(options) {",
" var message = options.message;",
" if(message instanceof Uint8Array) {",
" return message;",
" }",
"",
" var encoding = options.encoding;",
" if(message === undefined) {",
" if(options.md) {",
" // TODO: more rigorous validation that `md` is a MessageDigest",
" message = options.md.digest().getBytes();",
" encoding = 'binary';",
" } else {",
" throw new TypeError('\"options.message\" or \"options.md\" not specified.');",
" }",
" }",
"",
" if(typeof message === 'string' && !encoding) {",
" throw new TypeError('\"options.encoding\" must be \"binary\" or \"utf8\".');",
" }",
"",
" if(typeof message === 'string') {",
" if(typeof Buffer !== 'undefined') {",
" return new Buffer(message, encoding);",
" }",
" message = new ByteBuffer(message, encoding);",
" } else if(!(message instanceof ByteBuffer)) {",
" throw new TypeError(",
" '\"options.message\" must be a node.js Buffer, a Uint8Array, a forge ' +",
" 'ByteBuffer, or a string with \"options.encoding\" specifying its ' +",
" 'encoding.');",
" }",
"",
" // convert to native buffer",
" var buffer = new NativeBuffer(message.length());",
" for(var i = 0; i < buffer.length; ++i) {",
" buffer[i] = message.at(i);",
" }",
" return buffer;",
"}",
"",
"var gf0 = gf();",
"var gf1 = gf([1]);",
"var D = gf([",
" 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,",
" 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]);",
"var D2 = gf([",
" 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,",
" 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]);",
"var X = gf([",
" 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,",
" 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]);",
"var Y = gf([",
" 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,",
" 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]);",
"var L = new Float64Array([",
" 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,",
" 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);",
"var I = gf([",
" 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,",
" 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);",
"",
"// TODO: update forge buffer implementation to use `Buffer` or `Uint8Array`,",
"// whichever is available, to improve performance",
"function sha512(msg, msgLen) {",
" // Note: `out` and `msg` are NativeBuffer",
" var md = forge.md.sha512.create();",
" var buffer = new ByteBuffer(msg);",
" md.update(buffer.getBytes(msgLen), 'binary');",
" var hash = md.digest().getBytes();",
" if(typeof Buffer !== 'undefined') {",
" return new Buffer(hash, 'binary');",
" }",
" var out = new NativeBuffer(ed25519.constants.HASH_BYTE_LENGTH);",
" for(var i = 0; i < 64; ++i) {",
" out[i] = hash.charCodeAt(i);",
" }",
" return out;",
"}",
"",
"function crypto_sign_keypair(pk, sk) {",
" var p = [gf(), gf(), gf(), gf()];",
" var i;",
"",
" var d = sha512(sk, 32);",
" d[0] &= 248;",
" d[31] &= 127;",
" d[31] |= 64;",
"",
" scalarbase(p, d);",
" pack(pk, p);",
"",
" for(i = 0; i < 32; ++i) {",
" sk[i + 32] = pk[i];",
" }",
" return 0;",
"}",
"",
"// Note: difference from C - smlen returned, not passed as argument.",
"function crypto_sign(sm, m, n, sk) {",
" var i, j, x = new Float64Array(64);",
" var p = [gf(), gf(), gf(), gf()];",
"",
" var d = sha512(sk, 32);",
" d[0] &= 248;",
" d[31] &= 127;",
" d[31] |= 64;",
"",
" var smlen = n + 64;",
" for(i = 0; i < n; ++i) {",
" sm[64 + i] = m[i];",
" }",
" for(i = 0; i < 32; ++i) {",
" sm[32 + i] = d[32 + i];",
" }",
"",
" var r = sha512(sm.subarray(32), n + 32);",
" reduce(r);",
" scalarbase(p, r);",
" pack(sm, p);",
"",
" for(i = 32; i < 64; ++i) {",
" sm[i] = sk[i];",
" }",
" var h = sha512(sm, n + 64);",
" reduce(h);",
"",
" for(i = 32; i < 64; ++i) {",
" x[i] = 0;",
" }",
" for(i = 0; i < 32; ++i) {",
" x[i] = r[i];",
" }",
" for(i = 0; i < 32; ++i) {",
" for(j = 0; j < 32; j++) {",
" x[i + j] += h[i] * d[j];",
" }",
" }",
"",
" modL(sm.subarray(32), x);",
" return smlen;",
"}",
"",
"function crypto_sign_open(m, sm, n, pk) {",
" var i, mlen;",
" var t = new NativeBuffer(32);",
" var p = [gf(), gf(), gf(), gf()],",
" q = [gf(), gf(), gf(), gf()];",
"",
" mlen = -1;",
" if(n < 64) {",
" return -1;",
" }",
"",
" if(unpackneg(q, pk)) {",
" return -1;",
" }",
"",
" for(i = 0; i < n; ++i) {",
" m[i] = sm[i];",
" }",
" for(i = 0; i < 32; ++i) {",
" m[i + 32] = pk[i];",
" }",
" var h = sha512(m, n);",
" reduce(h);",
" scalarmult(p, q, h);",
"",
" scalarbase(q, sm.subarray(32));",
" add(p, q);",
" pack(t, p);",
"",
" n -= 64;",
" if(crypto_verify_32(sm, 0, t, 0)) {",
" for(i = 0; i < n; ++i) {",
" m[i] = 0;",
" }",
" return -1;",
" }",
"",
" for(i = 0; i < n; ++i) {",
" m[i] = sm[i + 64];",
" }",
" mlen = n;",
" return mlen;",
"}",
"",
"function modL(r, x) {",
" var carry, i, j, k;",
" for(i = 63; i >= 32; --i) {",
" carry = 0;",
" for(j = i - 32, k = i - 12; j < k; ++j) {",
" x[j] += carry - 16 * x[i] * L[j - (i - 32)];",
" carry = (x[j] + 128) >> 8;",
" x[j] -= carry * 256;",
" }",
" x[j] += carry;",
" x[i] = 0;",
" }",
" carry = 0;",
" for(j = 0; j < 32; ++j) {",
" x[j] += carry - (x[31] >> 4) * L[j];",
" carry = x[j] >> 8;",
" x[j] &= 255;",
" }",
" for(j = 0; j < 32; ++j) {",
" x[j] -= carry * L[j];",
" }",
" for(i = 0; i < 32; ++i) {",
" x[i + 1] += x[i] >> 8;",
" r[i] = x[i] & 255;",
" }",
"}",
"",
"function reduce(r) {",
" var x = new Float64Array(64);",
" for(var i = 0; i < 64; ++i) {",
" x[i] = r[i];",
" r[i] = 0;",
" }",
" modL(r, x);",
"}",
"",
"function add(p, q) {",
" var a = gf(), b = gf(), c = gf(),",
" d = gf(), e = gf(), f = gf(),",
" g = gf(), h = gf(), t = gf();",
"",
" Z(a, p[1], p[0]);",
" Z(t, q[1], q[0]);",
" M(a, a, t);",
" A(b, p[0], p[1]);",
" A(t, q[0], q[1]);",
" M(b, b, t);",
" M(c, p[3], q[3]);",
" M(c, c, D2);",
" M(d, p[2], q[2]);",
" A(d, d, d);",
" Z(e, b, a);",
" Z(f, d, c);",
" A(g, d, c);",
" A(h, b, a);",
"",
" M(p[0], e, f);",
" M(p[1], h, g);",
" M(p[2], g, f);",
" M(p[3], e, h);",
"}",
"",
"function cswap(p, q, b) {",
" for(var i = 0; i < 4; ++i) {",
" sel25519(p[i], q[i], b);",
" }",
"}",
"",
"function pack(r, p) {",
" var tx = gf(), ty = gf(), zi = gf();",
" inv25519(zi, p[2]);",
" M(tx, p[0], zi);",
" M(ty, p[1], zi);",
" pack25519(r, ty);",
" r[31] ^= par25519(tx) << 7;",
"}",
"",
"function pack25519(o, n) {",
" var i, j, b;",
" var m = gf(), t = gf();",
" for(i = 0; i < 16; ++i) {",
" t[i] = n[i];",
" }",
" car25519(t);",
" car25519(t);",
" car25519(t);",
" for(j = 0; j < 2; ++j) {",
" m[0] = t[0] - 0xffed;",
" for(i = 1; i < 15; ++i) {",
" m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);",
" m[i-1] &= 0xffff;",
" }",
" m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);",
" b = (m[15] >> 16) & 1;",
" m[14] &= 0xffff;",
" sel25519(t, m, 1 - b);",
" }",
" for (i = 0; i < 16; i++) {",
" o[2 * i] = t[i] & 0xff;",
" o[2 * i + 1] = t[i] >> 8;",
" }",
"}",
"",
"function unpackneg(r, p) {",
" var t = gf(), chk = gf(), num = gf(),",
" den = gf(), den2 = gf(), den4 = gf(),",
" den6 = gf();",
"",
" set25519(r[2], gf1);",
" unpack25519(r[1], p);",
" S(num, r[1]);",
" M(den, num, D);",
" Z(num, num, r[2]);",
" A(den, r[2], den);",
"",
" S(den2, den);",
" S(den4, den2);",
" M(den6, den4, den2);",
" M(t, den6, num);",
" M(t, t, den);",
"",
" pow2523(t, t);",
" M(t, t, num);",
" M(t, t, den);",
" M(t, t, den);",
" M(r[0], t, den);",
"",
" S(chk, r[0]);",
" M(chk, chk, den);",
" if(neq25519(chk, num)) {",
" M(r[0], r[0], I);",
" }",
"",
" S(chk, r[0]);",
" M(chk, chk, den);",
" if(neq25519(chk, num)) {",
" return -1;",
" }",
"",
" if(par25519(r[0]) === (p[31] >> 7)) {",
" Z(r[0], gf0, r[0]);",
" }",
"",
" M(r[3], r[0], r[1]);",
" return 0;",
"}",
"",
"function unpack25519(o, n) {",
" var i;",
" for(i = 0; i < 16; ++i) {",
" o[i] = n[2 * i] + (n[2 * i + 1] << 8);",
" }",
" o[15] &= 0x7fff;",
"}",
"",
"function pow2523(o, i) {",
" var c = gf();",
" var a;",
" for(a = 0; a < 16; ++a) {",
" c[a] = i[a];",
" }",
" for(a = 250; a >= 0; --a) {",
" S(c, c);",
" if(a !== 1) {",
" M(c, c, i);",
" }",
" }",
" for(a = 0; a < 16; ++a) {",
" o[a] = c[a];",
" }",
"}",
"",
"function neq25519(a, b) {",
" var c = new NativeBuffer(32);",
" var d = new NativeBuffer(32);",
" pack25519(c, a);",
" pack25519(d, b);",
" return crypto_verify_32(c, 0, d, 0);",
"}",
"",
"function crypto_verify_32(x, xi, y, yi) {",
" return vn(x, xi, y, yi, 32);",
"}",
"",
"function vn(x, xi, y, yi, n) {",
" var i, d = 0;",
" for(i = 0; i < n; ++i) {",
" d |= x[xi + i] ^ y[yi + i];",
" }",
" return (1 & ((d - 1) >>> 8)) - 1;",
"}",
"",
"function par25519(a) {",
" var d = new NativeBuffer(32);",
" pack25519(d, a);",
" return d[0] & 1;",
"}",
"",
"function scalarmult(p, q, s) {",
" var b, i;",
" set25519(p[0], gf0);",
" set25519(p[1], gf1);",
" set25519(p[2], gf1);",
" set25519(p[3], gf0);",
" for(i = 255; i >= 0; --i) {",
" b = (s[(i / 8)|0] >> (i & 7)) & 1;",
" cswap(p, q, b);",
" add(q, p);",
" add(p, p);",
" cswap(p, q, b);",
" }",
"}",
"",
"function scalarbase(p, s) {",
" var q = [gf(), gf(), gf(), gf()];",
" set25519(q[0], X);",
" set25519(q[1], Y);",
" set25519(q[2], gf1);",
" M(q[3], X, Y);",
" scalarmult(p, q, s);",
"}",
"",
"function set25519(r, a) {",
" var i;",
" for(i = 0; i < 16; i++) {",
" r[i] = a[i] | 0;",
" }",
"}",
"",
"function inv25519(o, i) {",
" var c = gf();",
" var a;",
" for(a = 0; a < 16; ++a) {",
" c[a] = i[a];",
" }",
" for(a = 253; a >= 0; --a) {",
" S(c, c);",
" if(a !== 2 && a !== 4) {",
" M(c, c, i);",
" }",
" }",
" for(a = 0; a < 16; ++a) {",
" o[a] = c[a];",
" }",
"}",
"",
"function car25519(o) {",
" var i, v, c = 1;",
" for(i = 0; i < 16; ++i) {",
" v = o[i] + c + 65535;",
" c = Math.floor(v / 65536);",
" o[i] = v - c * 65536;",
" }",
" o[0] += c - 1 + 37 * (c - 1);",
"}",
"",
"function sel25519(p, q, b) {",
" var t, c = ~(b - 1);",
" for(var i = 0; i < 16; ++i) {",
" t = c & (p[i] ^ q[i]);",
" p[i] ^= t;",
" q[i] ^= t;",
" }",
"}",
"",
"function gf(init) {",
" var i, r = new Float64Array(16);",
" if(init) {",
" for(i = 0; i < init.length; ++i) {",
" r[i] = init[i];",
" }",
" }",
" return r;",
"}",
"",
"function A(o, a, b) {",
" for(var i = 0; i < 16; ++i) {",
" o[i] = a[i] + b[i];",
" }",
"}",
"",
"function Z(o, a, b) {",
" for(var i = 0; i < 16; ++i) {",
" o[i] = a[i] - b[i];",
" }",
"}",
"",
"function S(o, a) {",
" M(o, a, a);",
"}",
"",
"function M(o, a, b) {",
" var v, c,",
" t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,",
" t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,",
" t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,",
" t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,",
" b0 = b[0],",
" b1 = b[1],",
" b2 = b[2],",
" b3 = b[3],",
" b4 = b[4],",
" b5 = b[5],",
" b6 = b[6],",
" b7 = b[7],",
" b8 = b[8],",
" b9 = b[9],",
" b10 = b[10],",
" b11 = b[11],",
" b12 = b[12],",
" b13 = b[13],",
" b14 = b[14],",
" b15 = b[15];",
"",
" v = a[0];",
" t0 += v * b0;",
" t1 += v * b1;",
" t2 += v * b2;",
" t3 += v * b3;",
" t4 += v * b4;",
" t5 += v * b5;",
" t6 += v * b6;",
" t7 += v * b7;",
" t8 += v * b8;",
" t9 += v * b9;",
" t10 += v * b10;",
" t11 += v * b11;",
" t12 += v * b12;",
" t13 += v * b13;",
" t14 += v * b14;",
" t15 += v * b15;",
" v = a[1];",
" t1 += v * b0;",
" t2 += v * b1;",
" t3 += v * b2;",
" t4 += v * b3;",
" t5 += v * b4;",
" t6 += v * b5;",
" t7 += v * b6;",
" t8 += v * b7;",
" t9 += v * b8;",
" t10 += v * b9;",
" t11 += v * b10;",
" t12 += v * b11;",
" t13 += v * b12;",
" t14 += v * b13;",
" t15 += v * b14;",
" t16 += v * b15;",
" v = a[2];",
" t2 += v * b0;",
" t3 += v * b1;",
" t4 += v * b2;",
" t5 += v * b3;",
" t6 += v * b4;",
" t7 += v * b5;",
" t8 += v * b6;",
" t9 += v * b7;",
" t10 += v * b8;",
" t11 += v * b9;",
" t12 += v * b10;",
" t13 += v * b11;",
" t14 += v * b12;",
" t15 += v * b13;",
" t16 += v * b14;",
" t17 += v * b15;",
" v = a[3];",
" t3 += v * b0;",
" t4 += v * b1;",
" t5 += v * b2;",
" t6 += v * b3;",
" t7 += v * b4;",
" t8 += v * b5;",
" t9 += v * b6;",
" t10 += v * b7;",
" t11 += v * b8;",
" t12 += v * b9;",
" t13 += v * b10;",
" t14 += v * b11;",
" t15 += v * b12;",
" t16 += v * b13;",
" t17 += v * b14;",
" t18 += v * b15;",
" v = a[4];",
" t4 += v * b0;",
" t5 += v * b1;",
" t6 += v * b2;",
" t7 += v * b3;",
" t8 += v * b4;",
" t9 += v * b5;",
" t10 += v * b6;",
" t11 += v * b7;",
" t12 += v * b8;",
" t13 += v * b9;",
" t14 += v * b10;",
" t15 += v * b11;",
" t16 += v * b12;",
" t17 += v * b13;",
" t18 += v * b14;",
" t19 += v * b15;",
" v = a[5];",
" t5 += v * b0;",
" t6 += v * b1;",
" t7 += v * b2;",
" t8 += v * b3;",
" t9 += v * b4;",
" t10 += v * b5;",
" t11 += v * b6;",
" t12 += v * b7;",
" t13 += v * b8;",
" t14 += v * b9;",
" t15 += v * b10;",
" t16 += v * b11;",
" t17 += v * b12;",
" t18 += v * b13;",
" t19 += v * b14;",
" t20 += v * b15;",
" v = a[6];",
" t6 += v * b0;",
" t7 += v * b1;",
" t8 += v * b2;",
" t9 += v * b3;",
" t10 += v * b4;",
" t11 += v * b5;",
" t12 += v * b6;",
" t13 += v * b7;",
" t14 += v * b8;",
" t15 += v * b9;",
" t16 += v * b10;",
" t17 += v * b11;",
" t18 += v * b12;",
" t19 += v * b13;",
" t20 += v * b14;",
" t21 += v * b15;",
" v = a[7];",
" t7 += v * b0;",
" t8 += v * b1;",
" t9 += v * b2;",
" t10 += v * b3;",
" t11 += v * b4;",
" t12 += v * b5;",
" t13 += v * b6;",
" t14 += v * b7;",
" t15 += v * b8;",
" t16 += v * b9;",
" t17 += v * b10;",
" t18 += v * b11;",
" t19 += v * b12;",
" t20 += v * b13;",
" t21 += v * b14;",
" t22 += v * b15;",
" v = a[8];",
" t8 += v * b0;",
" t9 += v * b1;",
" t10 += v * b2;",
" t11 += v * b3;",
" t12 += v * b4;",
" t13 += v * b5;",
" t14 += v * b6;",
" t15 += v * b7;",
" t16 += v * b8;",
" t17 += v * b9;",
" t18 += v * b10;",
" t19 += v * b11;",
" t20 += v * b12;",
" t21 += v * b13;",
" t22 += v * b14;",
" t23 += v * b15;",
" v = a[9];",
" t9 += v * b0;",
" t10 += v * b1;",
" t11 += v * b2;",
" t12 += v * b3;",
" t13 += v * b4;",
" t14 += v * b5;",
" t15 += v * b6;",
" t16 += v * b7;",
" t17 += v * b8;",
" t18 += v * b9;",
" t19 += v * b10;",
" t20 += v * b11;",
" t21 += v * b12;",
" t22 += v * b13;",
" t23 += v * b14;",
" t24 += v * b15;",
" v = a[10];",
" t10 += v * b0;",
" t11 += v * b1;",
" t12 += v * b2;",
" t13 += v * b3;",
" t14 += v * b4;",
" t15 += v * b5;",
" t16 += v * b6;",
" t17 += v * b7;",
" t18 += v * b8;",
" t19 += v * b9;",
" t20 += v * b10;",
" t21 += v * b11;",
" t22 += v * b12;",
" t23 += v * b13;",
" t24 += v * b14;",
" t25 += v * b15;",
" v = a[11];",
" t11 += v * b0;",
" t12 += v * b1;",
" t13 += v * b2;",
" t14 += v * b3;",
" t15 += v * b4;",
" t16 += v * b5;",
" t17 += v * b6;",
" t18 += v * b7;",
" t19 += v * b8;",
" t20 += v * b9;",
" t21 += v * b10;",
" t22 += v * b11;",
" t23 += v * b12;",
" t24 += v * b13;",
" t25 += v * b14;",
" t26 += v * b15;",
" v = a[12];",
" t12 += v * b0;",
" t13 += v * b1;",
" t14 += v * b2;",
" t15 += v * b3;",
" t16 += v * b4;",
" t17 += v * b5;",
" t18 += v * b6;",
" t19 += v * b7;",
" t20 += v * b8;",
" t21 += v * b9;",
" t22 += v * b10;",
" t23 += v * b11;",
" t24 += v * b12;",
" t25 += v * b13;",
" t26 += v * b14;",
" t27 += v * b15;",
" v = a[13];",
" t13 += v * b0;",
" t14 += v * b1;",
" t15 += v * b2;",
" t16 += v * b3;",
" t17 += v * b4;",
" t18 += v * b5;",
" t19 += v * b6;",
" t20 += v * b7;",
" t21 += v * b8;",
" t22 += v * b9;",
" t23 += v * b10;",
" t24 += v * b11;",
" t25 += v * b12;",
" t26 += v * b13;",
" t27 += v * b14;",
" t28 += v * b15;",
" v = a[14];",
" t14 += v * b0;",
" t15 += v * b1;",
" t16 += v * b2;",
" t17 += v * b3;",
" t18 += v * b4;",
" t19 += v * b5;",
" t20 += v * b6;",
" t21 += v * b7;",
" t22 += v * b8;",
" t23 += v * b9;",
" t24 += v * b10;",
" t25 += v * b11;",
" t26 += v * b12;",
" t27 += v * b13;",
" t28 += v * b14;",
" t29 += v * b15;",
" v = a[15];",
" t15 += v * b0;",
" t16 += v * b1;",
" t17 += v * b2;",
" t18 += v * b3;",
" t19 += v * b4;",
" t20 += v * b5;",
" t21 += v * b6;",
" t22 += v * b7;",
" t23 += v * b8;",
" t24 += v * b9;",
" t25 += v * b10;",
" t26 += v * b11;",
" t27 += v * b12;",
" t28 += v * b13;",
" t29 += v * b14;",
" t30 += v * b15;",
"",
" t0 += 38 * t16;",
" t1 += 38 * t17;",
" t2 += 38 * t18;",
" t3 += 38 * t19;",
" t4 += 38 * t20;",
" t5 += 38 * t21;",
" t6 += 38 * t22;",
" t7 += 38 * t23;",
" t8 += 38 * t24;",
" t9 += 38 * t25;",
" t10 += 38 * t26;",
" t11 += 38 * t27;",
" t12 += 38 * t28;",
" t13 += 38 * t29;",
" t14 += 38 * t30;",
" // t15 left as is",
"",
" // first car",
" c = 1;",
" v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;",
" v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;",
" v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;",
" v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;",
" v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;",
" v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;",
" v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;",
" v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;",
" v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;",
" v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;",
" v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;",
" v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;",
" v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;",
" v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;",
" v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;",
" v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;",
" t0 += c-1 + 37 * (c-1);",
"",
" // second car",
" c = 1;",
" v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;",
" v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;",
" v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;",
" v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;",
" v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;",
" v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;",
" v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;",
" v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;",
" v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;",
" v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;",
" v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;",
" v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;",
" v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;",
" v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;",
" v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;",
" v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;",
" t0 += c-1 + 37 * (c-1);",
"",
" o[ 0] = t0;",
" o[ 1] = t1;",
" o[ 2] = t2;",
" o[ 3] = t3;",
" o[ 4] = t4;",
" o[ 5] = t5;",
" o[ 6] = t6;",
" o[ 7] = t7;",
" o[ 8] = t8;",
" o[ 9] = t9;",
" o[10] = t10;",
" o[11] = t11;",
" o[12] = t12;",
" o[13] = t13;",
" o[14] = t14;",
" o[15] = t15;",
"}",
"",
"",
"/***/ }),",
"/* 40 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of RSA-KEM.",
" *",
" * @author Lautaro Cozzani Rodriguez",
" * @author Dave Longley",
" *",
" * Copyright (c) 2014 Lautaro Cozzani ",
" * Copyright (c) 2014 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(1);",
"__webpack_require__(2);",
"__webpack_require__(12);",
"",
"module.exports = forge.kem = forge.kem || {};",
"",
"var BigInteger = forge.jsbn.BigInteger;",
"",
"/**",
" * The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.",
" */",
"forge.kem.rsa = {};",
"",
"/**",
" * Creates an RSA KEM API object for generating a secret asymmetric key.",
" *",
" * The symmetric key may be generated via a call to 'encrypt', which will",
" * produce a ciphertext to be transmitted to the recipient and a key to be",
" * kept secret. The ciphertext is a parameter to be passed to 'decrypt' which",
" * will produce the same secret key for the recipient to use to decrypt a",
" * message that was encrypted with the secret key.",
" *",
" * @param kdf the KDF API to use (eg: new forge.kem.kdf1()).",
" * @param options the options to use.",
" * [prng] a custom crypto-secure pseudo-random number generator to use,",
" * that must define \"getBytesSync\".",
" */",
"forge.kem.rsa.create = function(kdf, options) {",
" options = options || {};",
" var prng = options.prng || forge.random;",
"",
" var kem = {};",
"",
" /**",
" * Generates a secret key and its encapsulation.",
" *",
" * @param publicKey the RSA public key to encrypt with.",
" * @param keyLength the length, in bytes, of the secret key to generate.",
" *",
" * @return an object with:",
" * encapsulation: the ciphertext for generating the secret key, as a",
" * binary-encoded string of bytes.",
" * key: the secret key to use for encrypting a message.",
" */",
" kem.encrypt = function(publicKey, keyLength) {",
" // generate a random r where 1 > r > n",
" var byteLength = Math.ceil(publicKey.n.bitLength() / 8);",
" var r;",
" do {",
" r = new BigInteger(",
" forge.util.bytesToHex(prng.getBytesSync(byteLength)),",
" 16).mod(publicKey.n);",
" } while(r.equals(BigInteger.ZERO));",
"",
" // prepend r with zeros",
" r = forge.util.hexToBytes(r.toString(16));",
" var zeros = byteLength - r.length;",
" if(zeros > 0) {",
" r = forge.util.fillString(String.fromCharCode(0), zeros) + r;",
" }",
"",
" // encrypt the random",
" var encapsulation = publicKey.encrypt(r, 'NONE');",
"",
" // generate the secret key",
" var key = kdf.generate(r, keyLength);",
"",
" return {encapsulation: encapsulation, key: key};",
" };",
"",
" /**",
" * Decrypts an encapsulated secret key.",
" *",
" * @param privateKey the RSA private key to decrypt with.",
" * @param encapsulation the ciphertext for generating the secret key, as",
" * a binary-encoded string of bytes.",
" * @param keyLength the length, in bytes, of the secret key to generate.",
" *",
" * @return the secret key as a binary-encoded string of bytes.",
" */",
" kem.decrypt = function(privateKey, encapsulation, keyLength) {",
" // decrypt the encapsulation and generate the secret key",
" var r = privateKey.decrypt(encapsulation, 'NONE');",
" return kdf.generate(r, keyLength);",
" };",
"",
" return kem;",
"};",
"",
"// TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?",
"",
"/**",
" * Creates a key derivation API object that implements KDF1 per ISO 18033-2.",
" *",
" * @param md the hash API to use.",
" * @param [digestLength] an optional digest length that must be positive and",
" * less than or equal to md.digestLength.",
" *",
" * @return a KDF1 API object.",
" */",
"forge.kem.kdf1 = function(md, digestLength) {",
" _createKDF(this, md, 0, digestLength || md.digestLength);",
"};",
"",
"/**",
" * Creates a key derivation API object that implements KDF2 per ISO 18033-2.",
" *",
" * @param md the hash API to use.",
" * @param [digestLength] an optional digest length that must be positive and",
" * less than or equal to md.digestLength.",
" *",
" * @return a KDF2 API object.",
" */",
"forge.kem.kdf2 = function(md, digestLength) {",
" _createKDF(this, md, 1, digestLength || md.digestLength);",
"};",
"",
"/**",
" * Creates a KDF1 or KDF2 API object.",
" *",
" * @param md the hash API to use.",
" * @param counterStart the starting index for the counter.",
" * @param digestLength the digest length to use.",
" *",
" * @return the KDF API object.",
" */",
"function _createKDF(kdf, md, counterStart, digestLength) {",
" /**",
" * Generate a key of the specified length.",
" *",
" * @param x the binary-encoded byte string to generate a key from.",
" * @param length the number of bytes to generate (the size of the key).",
" *",
" * @return the key as a binary-encoded string.",
" */",
" kdf.generate = function(x, length) {",
" var key = new forge.util.ByteBuffer();",
"",
" // run counter from counterStart to ceil(length / Hash.len)",
" var k = Math.ceil(length / digestLength) + counterStart;",
"",
" var c = new forge.util.ByteBuffer();",
" for(var i = counterStart; i < k; ++i) {",
" // I2OSP(i, 4): convert counter to an octet string of 4 octets",
" c.putInt32(i);",
"",
" // digest 'x' and the counter and add the result to the key",
" md.start();",
" md.update(x + c.getBytes());",
" var hash = md.digest();",
" key.putBytes(hash.getBytes(digestLength));",
" }",
"",
" // truncate to the correct key length",
" key.truncate(key.length() - length);",
" return key.getBytes();",
" };",
"}",
"",
"",
"/***/ }),",
"/* 41 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Node.js module for all known Forge message digests.",
" *",
" * @author Dave Longley",
" *",
" * Copyright 2011-2017 Digital Bazaar, Inc.",
" */",
"module.exports = __webpack_require__(4);",
"",
"__webpack_require__(14);",
"__webpack_require__(9);",
"__webpack_require__(23);",
"__webpack_require__(32);",
"",
"",
"/***/ }),",
"/* 42 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Javascript implementation of PKCS#7 v1.5.",
" *",
" * @author Stefan Siegl",
" * @author Dave Longley",
" *",
" * Copyright (c) 2012 Stefan Siegl ",
" * Copyright (c) 2012-2015 Digital Bazaar, Inc.",
" *",
" * Currently this implementation only supports ContentType of EnvelopedData,",
" * EncryptedData, or SignedData at the root level. The top level elements may",
" * contain only a ContentInfo of ContentType Data, i.e. plain data. Further",
" * nesting is not (yet) supported.",
" *",
" * The Forge validators for PKCS #7's ASN.1 structures are available from",
" * a separate file pkcs7asn1.js, since those are referenced from other",
" * PKCS standards like PKCS #12.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(5);",
"__webpack_require__(3);",
"__webpack_require__(10);",
"__webpack_require__(6);",
"__webpack_require__(7);",
"__webpack_require__(29);",
"__webpack_require__(2);",
"__webpack_require__(1);",
"__webpack_require__(16);",
"",
"// shortcut for ASN.1 API",
"var asn1 = forge.asn1;",
"",
"// shortcut for PKCS#7 API",
"var p7 = module.exports = forge.pkcs7 = forge.pkcs7 || {};",
"",
"/**",
" * Converts a PKCS#7 message from PEM format.",
" *",
" * @param pem the PEM-formatted PKCS#7 message.",
" *",
" * @return the PKCS#7 message.",
" */",
"p7.messageFromPem = function(pem) {",
" var msg = forge.pem.decode(pem)[0];",
"",
" if(msg.type !== 'PKCS7') {",
" var error = new Error('Could not convert PKCS#7 message from PEM; PEM ' +",
" 'header type is not \"PKCS#7\".');",
" error.headerType = msg.type;",
" throw error;",
" }",
" if(msg.procType && msg.procType.type === 'ENCRYPTED') {",
" throw new Error('Could not convert PKCS#7 message from PEM; PEM is encrypted.');",
" }",
"",
" // convert DER to ASN.1 object",
" var obj = asn1.fromDer(msg.body);",
"",
" return p7.messageFromAsn1(obj);",
"};",
"",
"/**",
" * Converts a PKCS#7 message to PEM format.",
" *",
" * @param msg The PKCS#7 message object",
" * @param maxline The maximum characters per line, defaults to 64.",
" *",
" * @return The PEM-formatted PKCS#7 message.",
" */",
"p7.messageToPem = function(msg, maxline) {",
" // convert to ASN.1, then DER, then PEM-encode",
" var pemObj = {",
" type: 'PKCS7',",
" body: asn1.toDer(msg.toAsn1()).getBytes()",
" };",
" return forge.pem.encode(pemObj, {maxline: maxline});",
"};",
"",
"/**",
" * Converts a PKCS#7 message from an ASN.1 object.",
" *",
" * @param obj the ASN.1 representation of a ContentInfo.",
" *",
" * @return the PKCS#7 message.",
" */",
"p7.messageFromAsn1 = function(obj) {",
" // validate root level ContentInfo and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, p7.asn1.contentInfoValidator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#7 message. ' +",
" 'ASN.1 object is not an PKCS#7 ContentInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" var contentType = asn1.derToOid(capture.contentType);",
" var msg;",
"",
" switch(contentType) {",
" case forge.pki.oids.envelopedData:",
" msg = p7.createEnvelopedData();",
" break;",
"",
" case forge.pki.oids.encryptedData:",
" msg = p7.createEncryptedData();",
" break;",
"",
" case forge.pki.oids.signedData:",
" msg = p7.createSignedData();",
" break;",
"",
" default:",
" throw new Error('Cannot read PKCS#7 message. ContentType with OID ' +",
" contentType + ' is not (yet) supported.');",
" }",
"",
" msg.fromAsn1(capture.content.value[0]);",
" return msg;",
"};",
"",
"p7.createSignedData = function() {",
" var msg = null;",
" msg = {",
" type: forge.pki.oids.signedData,",
" version: 1,",
" certificates: [],",
" crls: [],",
" // TODO: add json-formatted signer stuff here?",
" signers: [],",
" // populated during sign()",
" digestAlgorithmIdentifiers: [],",
" contentInfo: null,",
" signerInfos: [],",
"",
" fromAsn1: function(obj) {",
" // validate SignedData content block and capture data.",
" _fromAsn1(msg, obj, p7.asn1.signedDataValidator);",
" msg.certificates = [];",
" msg.crls = [];",
" msg.digestAlgorithmIdentifiers = [];",
" msg.contentInfo = null;",
" msg.signerInfos = [];",
"",
" if(msg.rawCapture.certificates) {",
" var certs = msg.rawCapture.certificates.value;",
" for(var i = 0; i < certs.length; ++i) {",
" msg.certificates.push(forge.pki.certificateFromAsn1(certs[i]));",
" }",
" }",
"",
" // TODO: parse crls",
" },",
"",
" toAsn1: function() {",
" // degenerate case with no content",
" if(!msg.contentInfo) {",
" msg.sign();",
" }",
"",
" var certs = [];",
" for(var i = 0; i < msg.certificates.length; ++i) {",
" certs.push(forge.pki.certificateToAsn1(msg.certificates[i]));",
" }",
"",
" var crls = [];",
" // TODO: implement CRLs",
"",
" // [0] SignedData",
" var signedData = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // Version",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(msg.version).getBytes()),",
" // DigestAlgorithmIdentifiers",
" asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SET, true,",
" msg.digestAlgorithmIdentifiers),",
" // ContentInfo",
" msg.contentInfo",
" ])",
" ]);",
" if(certs.length > 0) {",
" // [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL",
" signedData.value[0].value.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, certs));",
" }",
" if(crls.length > 0) {",
" // [1] IMPLICIT CertificateRevocationLists OPTIONAL",
" signedData.value[0].value.push(",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, crls));",
" }",
" // SignerInfos",
" signedData.value[0].value.push(",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,",
" msg.signerInfos));",
"",
" // ContentInfo",
" return asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // ContentType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(msg.type).getBytes()),",
" // [0] SignedData",
" signedData",
" ]);",
" },",
"",
" /**",
" * Add (another) entity to list of signers.",
" *",
" * Note: If authenticatedAttributes are provided, then, per RFC 2315,",
" * they must include at least two attributes: content type and",
" * message digest. The message digest attribute value will be",
" * auto-calculated during signing and will be ignored if provided.",
" *",
" * Here's an example of providing these two attributes:",
" *",
" * forge.pkcs7.createSignedData();",
" * p7.addSigner({",
" * issuer: cert.issuer.attributes,",
" * serialNumber: cert.serialNumber,",
" * key: privateKey,",
" * digestAlgorithm: forge.pki.oids.sha1,",
" * authenticatedAttributes: [{",
" * type: forge.pki.oids.contentType,",
" * value: forge.pki.oids.data",
" * }, {",
" * type: forge.pki.oids.messageDigest",
" * }]",
" * });",
" *",
" * TODO: Support [subjectKeyIdentifier] as signer's ID.",
" *",
" * @param signer the signer information:",
" * key the signer's private key.",
" * [certificate] a certificate containing the public key",
" * associated with the signer's private key; use this option as",
" * an alternative to specifying signer.issuer and",
" * signer.serialNumber.",
" * [issuer] the issuer attributes (eg: cert.issuer.attributes).",
" * [serialNumber] the signer's certificate's serial number in",
" * hexadecimal (eg: cert.serialNumber).",
" * [digestAlgorithm] the message digest OID, as a string, to use",
" * (eg: forge.pki.oids.sha1).",
" * [authenticatedAttributes] an optional array of attributes",
" * to also sign along with the content.",
" */",
" addSigner: function(signer) {",
" var issuer = signer.issuer;",
" var serialNumber = signer.serialNumber;",
" if(signer.certificate) {",
" var cert = signer.certificate;",
" if(typeof cert === 'string') {",
" cert = forge.pki.certificateFromPem(cert);",
" }",
" issuer = cert.issuer.attributes;",
" serialNumber = cert.serialNumber;",
" }",
" var key = signer.key;",
" if(!key) {",
" throw new Error(",
" 'Could not add PKCS#7 signer; no private key specified.');",
" }",
" if(typeof key === 'string') {",
" key = forge.pki.privateKeyFromPem(key);",
" }",
"",
" // ensure OID known for digest algorithm",
" var digestAlgorithm = signer.digestAlgorithm || forge.pki.oids.sha1;",
" switch(digestAlgorithm) {",
" case forge.pki.oids.sha1:",
" case forge.pki.oids.sha256:",
" case forge.pki.oids.sha384:",
" case forge.pki.oids.sha512:",
" case forge.pki.oids.md5:",
" break;",
" default:",
" throw new Error(",
" 'Could not add PKCS#7 signer; unknown message digest algorithm: ' +",
" digestAlgorithm);",
" }",
"",
" // if authenticatedAttributes is present, then the attributes",
" // must contain at least PKCS #9 content-type and message-digest",
" var authenticatedAttributes = signer.authenticatedAttributes || [];",
" if(authenticatedAttributes.length > 0) {",
" var contentType = false;",
" var messageDigest = false;",
" for(var i = 0; i < authenticatedAttributes.length; ++i) {",
" var attr = authenticatedAttributes[i];",
" if(!contentType && attr.type === forge.pki.oids.contentType) {",
" contentType = true;",
" if(messageDigest) {",
" break;",
" }",
" continue;",
" }",
" if(!messageDigest && attr.type === forge.pki.oids.messageDigest) {",
" messageDigest = true;",
" if(contentType) {",
" break;",
" }",
" continue;",
" }",
" }",
"",
" if(!contentType || !messageDigest) {",
" throw new Error('Invalid signer.authenticatedAttributes. If ' +",
" 'signer.authenticatedAttributes is specified, then it must ' +",
" 'contain at least two attributes, PKCS #9 content-type and ' +",
" 'PKCS #9 message-digest.');",
" }",
" }",
"",
" msg.signers.push({",
" key: key,",
" version: 1,",
" issuer: issuer,",
" serialNumber: serialNumber,",
" digestAlgorithm: digestAlgorithm,",
" signatureAlgorithm: forge.pki.oids.rsaEncryption,",
" signature: null,",
" authenticatedAttributes: authenticatedAttributes,",
" unauthenticatedAttributes: []",
" });",
" },",
"",
" /**",
" * Signs the content.",
" */",
" sign: function() {",
" // auto-generate content info",
" if(typeof msg.content !== 'object' || msg.contentInfo === null) {",
" // use Data ContentInfo",
" msg.contentInfo = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // ContentType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(forge.pki.oids.data).getBytes())",
" ]);",
"",
" // add actual content, if present",
" if('content' in msg) {",
" var content;",
" if(msg.content instanceof forge.util.ByteBuffer) {",
" content = msg.content.bytes();",
" } else if(typeof msg.content === 'string') {",
" content = forge.util.encodeUtf8(msg.content);",
" }",
"",
" msg.contentInfo.value.push(",
" // [0] EXPLICIT content",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" content)",
" ]));",
" }",
" }",
"",
" // no signers, return early (degenerate case for certificate container)",
" if(msg.signers.length === 0) {",
" return;",
" }",
"",
" // generate digest algorithm identifiers",
" var mds = addDigestAlgorithmIds();",
"",
" // generate signerInfos",
" addSignerInfos(mds);",
" },",
"",
" verify: function() {",
" throw new Error('PKCS#7 signature verification not yet implemented.');",
" },",
"",
" /**",
" * Add a certificate.",
" *",
" * @param cert the certificate to add.",
" */",
" addCertificate: function(cert) {",
" // convert from PEM",
" if(typeof cert === 'string') {",
" cert = forge.pki.certificateFromPem(cert);",
" }",
" msg.certificates.push(cert);",
" },",
"",
" /**",
" * Add a certificate revokation list.",
" *",
" * @param crl the certificate revokation list to add.",
" */",
" addCertificateRevokationList: function(crl) {",
" throw new Error('PKCS#7 CRL support not yet implemented.');",
" }",
" };",
" return msg;",
"",
" function addDigestAlgorithmIds() {",
" var mds = {};",
"",
" for(var i = 0; i < msg.signers.length; ++i) {",
" var signer = msg.signers[i];",
" var oid = signer.digestAlgorithm;",
" if(!(oid in mds)) {",
" // content digest",
" mds[oid] = forge.md[forge.pki.oids[oid]].create();",
" }",
" if(signer.authenticatedAttributes.length === 0) {",
" // no custom attributes to digest; use content message digest",
" signer.md = mds[oid];",
" } else {",
" // custom attributes to be digested; use own message digest",
" // TODO: optimize to just copy message digest state if that",
" // feature is ever supported with message digests",
" signer.md = forge.md[forge.pki.oids[oid]].create();",
" }",
" }",
"",
" // add unique digest algorithm identifiers",
" msg.digestAlgorithmIdentifiers = [];",
" for(var oid in mds) {",
" msg.digestAlgorithmIdentifiers.push(",
" // AlgorithmIdentifier",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(oid).getBytes()),",
" // parameters (null)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ]));",
" }",
"",
" return mds;",
" }",
"",
" function addSignerInfos(mds) {",
" // Note: ContentInfo is a SEQUENCE with 2 values, second value is",
" // the content field and is optional for a ContentInfo but required here",
" // since signers are present",
" if(msg.contentInfo.value.length < 2) {",
" throw new Error(",
" 'Could not sign PKCS#7 message; there is no content to sign.');",
" }",
"",
" // get ContentInfo content type",
" var contentType = asn1.derToOid(msg.contentInfo.value[0].value);",
"",
" // get ContentInfo content",
" var content = msg.contentInfo.value[1];",
" // skip [0] EXPLICIT content wrapper",
" content = content.value[0];",
"",
" // serialize content",
" var bytes = asn1.toDer(content);",
"",
" // skip identifier and length per RFC 2315 9.3",
" // skip identifier (1 byte)",
" bytes.getByte();",
" // read and discard length bytes",
" asn1.getBerValueLength(bytes);",
" bytes = bytes.getBytes();",
"",
" // digest content DER value bytes",
" for(var oid in mds) {",
" mds[oid].start().update(bytes);",
" }",
"",
" // sign content",
" var signingTime = new Date();",
" for(var i = 0; i < msg.signers.length; ++i) {",
" var signer = msg.signers[i];",
"",
" if(signer.authenticatedAttributes.length === 0) {",
" // if ContentInfo content type is not \"Data\", then",
" // authenticatedAttributes must be present per RFC 2315",
" if(contentType !== forge.pki.oids.data) {",
" throw new Error(",
" 'Invalid signer; authenticatedAttributes must be present ' +",
" 'when the ContentInfo content type is not PKCS#7 Data.');",
" }",
" } else {",
" // process authenticated attributes",
" // [0] IMPLICIT",
" signer.authenticatedAttributesAsn1 = asn1.create(",
" asn1.Class.CONTEXT_SPECIFIC, 0, true, []);",
"",
" // per RFC 2315, attributes are to be digested using a SET container",
" // not the above [0] IMPLICIT container",
" var attrsAsn1 = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.SET, true, []);",
"",
" for(var ai = 0; ai < signer.authenticatedAttributes.length; ++ai) {",
" var attr = signer.authenticatedAttributes[ai];",
" if(attr.type === forge.pki.oids.messageDigest) {",
" // use content message digest as value",
" attr.value = mds[signer.digestAlgorithm].digest();",
" } else if(attr.type === forge.pki.oids.signingTime) {",
" // auto-populate signing time if not already set",
" if(!attr.value) {",
" attr.value = signingTime;",
" }",
" }",
"",
" // convert to ASN.1 and push onto Attributes SET (for signing) and",
" // onto authenticatedAttributesAsn1 to complete SignedData ASN.1",
" // TODO: optimize away duplication",
" attrsAsn1.value.push(_attributeToAsn1(attr));",
" signer.authenticatedAttributesAsn1.value.push(_attributeToAsn1(attr));",
" }",
"",
" // DER-serialize and digest SET OF attributes only",
" bytes = asn1.toDer(attrsAsn1).getBytes();",
" signer.md.start().update(bytes);",
" }",
"",
" // sign digest",
" signer.signature = signer.key.sign(signer.md, 'RSASSA-PKCS1-V1_5');",
" }",
"",
" // add signer info",
" msg.signerInfos = _signersToAsn1(msg.signers);",
" }",
"};",
"",
"/**",
" * Creates an empty PKCS#7 message of type EncryptedData.",
" *",
" * @return the message.",
" */",
"p7.createEncryptedData = function() {",
" var msg = null;",
" msg = {",
" type: forge.pki.oids.encryptedData,",
" version: 0,",
" encryptedContent: {",
" algorithm: forge.pki.oids['aes256-CBC']",
" },",
"",
" /**",
" * Reads an EncryptedData content block (in ASN.1 format)",
" *",
" * @param obj The ASN.1 representation of the EncryptedData content block",
" */",
" fromAsn1: function(obj) {",
" // Validate EncryptedData content block and capture data.",
" _fromAsn1(msg, obj, p7.asn1.encryptedDataValidator);",
" },",
"",
" /**",
" * Decrypt encrypted content",
" *",
" * @param key The (symmetric) key as a byte buffer",
" */",
" decrypt: function(key) {",
" if(key !== undefined) {",
" msg.encryptedContent.key = key;",
" }",
" _decryptContent(msg);",
" }",
" };",
" return msg;",
"};",
"",
"/**",
" * Creates an empty PKCS#7 message of type EnvelopedData.",
" *",
" * @return the message.",
" */",
"p7.createEnvelopedData = function() {",
" var msg = null;",
" msg = {",
" type: forge.pki.oids.envelopedData,",
" version: 0,",
" recipients: [],",
" encryptedContent: {",
" algorithm: forge.pki.oids['aes256-CBC']",
" },",
"",
" /**",
" * Reads an EnvelopedData content block (in ASN.1 format)",
" *",
" * @param obj the ASN.1 representation of the EnvelopedData content block.",
" */",
" fromAsn1: function(obj) {",
" // validate EnvelopedData content block and capture data",
" var capture = _fromAsn1(msg, obj, p7.asn1.envelopedDataValidator);",
" msg.recipients = _recipientsFromAsn1(capture.recipientInfos.value);",
" },",
"",
" toAsn1: function() {",
" // ContentInfo",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // ContentType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(msg.type).getBytes()),",
" // [0] EnvelopedData",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // Version",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(msg.version).getBytes()),",
" // RecipientInfos",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true,",
" _recipientsToAsn1(msg.recipients)),",
" // EncryptedContentInfo",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true,",
" _encryptedContentToAsn1(msg.encryptedContent))",
" ])",
" ])",
" ]);",
" },",
"",
" /**",
" * Find recipient by X.509 certificate's issuer.",
" *",
" * @param cert the certificate with the issuer to look for.",
" *",
" * @return the recipient object.",
" */",
" findRecipient: function(cert) {",
" var sAttr = cert.issuer.attributes;",
"",
" for(var i = 0; i < msg.recipients.length; ++i) {",
" var r = msg.recipients[i];",
" var rAttr = r.issuer;",
"",
" if(r.serialNumber !== cert.serialNumber) {",
" continue;",
" }",
"",
" if(rAttr.length !== sAttr.length) {",
" continue;",
" }",
"",
" var match = true;",
" for(var j = 0; j < sAttr.length; ++j) {",
" if(rAttr[j].type !== sAttr[j].type ||",
" rAttr[j].value !== sAttr[j].value) {",
" match = false;",
" break;",
" }",
" }",
"",
" if(match) {",
" return r;",
" }",
" }",
"",
" return null;",
" },",
"",
" /**",
" * Decrypt enveloped content",
" *",
" * @param recipient The recipient object related to the private key",
" * @param privKey The (RSA) private key object",
" */",
" decrypt: function(recipient, privKey) {",
" if(msg.encryptedContent.key === undefined && recipient !== undefined &&",
" privKey !== undefined) {",
" switch(recipient.encryptedContent.algorithm) {",
" case forge.pki.oids.rsaEncryption:",
" case forge.pki.oids.desCBC:",
" var key = privKey.decrypt(recipient.encryptedContent.content);",
" msg.encryptedContent.key = forge.util.createBuffer(key);",
" break;",
"",
" default:",
" throw new Error('Unsupported asymmetric cipher, ' +",
" 'OID ' + recipient.encryptedContent.algorithm);",
" }",
" }",
"",
" _decryptContent(msg);",
" },",
"",
" /**",
" * Add (another) entity to list of recipients.",
" *",
" * @param cert The certificate of the entity to add.",
" */",
" addRecipient: function(cert) {",
" msg.recipients.push({",
" version: 0,",
" issuer: cert.issuer.attributes,",
" serialNumber: cert.serialNumber,",
" encryptedContent: {",
" // We simply assume rsaEncryption here, since forge.pki only",
" // supports RSA so far. If the PKI module supports other",
" // ciphers one day, we need to modify this one as well.",
" algorithm: forge.pki.oids.rsaEncryption,",
" key: cert.publicKey",
" }",
" });",
" },",
"",
" /**",
" * Encrypt enveloped content.",
" *",
" * This function supports two optional arguments, cipher and key, which",
" * can be used to influence symmetric encryption. Unless cipher is",
" * provided, the cipher specified in encryptedContent.algorithm is used",
" * (defaults to AES-256-CBC). If no key is provided, encryptedContent.key",
" * is (re-)used. If that one's not set, a random key will be generated",
" * automatically.",
" *",
" * @param [key] The key to be used for symmetric encryption.",
" * @param [cipher] The OID of the symmetric cipher to use.",
" */",
" encrypt: function(key, cipher) {",
" // Part 1: Symmetric encryption",
" if(msg.encryptedContent.content === undefined) {",
" cipher = cipher || msg.encryptedContent.algorithm;",
" key = key || msg.encryptedContent.key;",
"",
" var keyLen, ivLen, ciphFn;",
" switch(cipher) {",
" case forge.pki.oids['aes128-CBC']:",
" keyLen = 16;",
" ivLen = 16;",
" ciphFn = forge.aes.createEncryptionCipher;",
" break;",
"",
" case forge.pki.oids['aes192-CBC']:",
" keyLen = 24;",
" ivLen = 16;",
" ciphFn = forge.aes.createEncryptionCipher;",
" break;",
"",
" case forge.pki.oids['aes256-CBC']:",
" keyLen = 32;",
" ivLen = 16;",
" ciphFn = forge.aes.createEncryptionCipher;",
" break;",
"",
" case forge.pki.oids['des-EDE3-CBC']:",
" keyLen = 24;",
" ivLen = 8;",
" ciphFn = forge.des.createEncryptionCipher;",
" break;",
"",
" default:",
" throw new Error('Unsupported symmetric cipher, OID ' + cipher);",
" }",
"",
" if(key === undefined) {",
" key = forge.util.createBuffer(forge.random.getBytes(keyLen));",
" } else if(key.length() != keyLen) {",
" throw new Error('Symmetric key has wrong length; ' +",
" 'got ' + key.length() + ' bytes, expected ' + keyLen + '.');",
" }",
"",
" // Keep a copy of the key & IV in the object, so the caller can",
" // use it for whatever reason.",
" msg.encryptedContent.algorithm = cipher;",
" msg.encryptedContent.key = key;",
" msg.encryptedContent.parameter = forge.util.createBuffer(",
" forge.random.getBytes(ivLen));",
"",
" var ciph = ciphFn(key);",
" ciph.start(msg.encryptedContent.parameter.copy());",
" ciph.update(msg.content);",
"",
" // The finish function does PKCS#7 padding by default, therefore",
" // no action required by us.",
" if(!ciph.finish()) {",
" throw new Error('Symmetric encryption failed.');",
" }",
"",
" msg.encryptedContent.content = ciph.output;",
" }",
"",
" // Part 2: asymmetric encryption for each recipient",
" for(var i = 0; i < msg.recipients.length; ++i) {",
" var recipient = msg.recipients[i];",
"",
" // Nothing to do, encryption already done.",
" if(recipient.encryptedContent.content !== undefined) {",
" continue;",
" }",
"",
" switch(recipient.encryptedContent.algorithm) {",
" case forge.pki.oids.rsaEncryption:",
" recipient.encryptedContent.content =",
" recipient.encryptedContent.key.encrypt(",
" msg.encryptedContent.key.data);",
" break;",
"",
" default:",
" throw new Error('Unsupported asymmetric cipher, OID ' +",
" recipient.encryptedContent.algorithm);",
" }",
" }",
" }",
" };",
" return msg;",
"};",
"",
"/**",
" * Converts a single recipient from an ASN.1 object.",
" *",
" * @param obj the ASN.1 RecipientInfo.",
" *",
" * @return the recipient object.",
" */",
"function _recipientFromAsn1(obj) {",
" // validate EnvelopedData content block and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, p7.asn1.recipientInfoValidator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#7 RecipientInfo. ' +",
" 'ASN.1 object is not an PKCS#7 RecipientInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" return {",
" version: capture.version.charCodeAt(0),",
" issuer: forge.pki.RDNAttributesAsArray(capture.issuer),",
" serialNumber: forge.util.createBuffer(capture.serial).toHex(),",
" encryptedContent: {",
" algorithm: asn1.derToOid(capture.encAlgorithm),",
" parameter: capture.encParameter.value,",
" content: capture.encKey",
" }",
" };",
"}",
"",
"/**",
" * Converts a single recipient object to an ASN.1 object.",
" *",
" * @param obj the recipient object.",
" *",
" * @return the ASN.1 RecipientInfo.",
" */",
"function _recipientToAsn1(obj) {",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // Version",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(obj.version).getBytes()),",
" // IssuerAndSerialNumber",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // Name",
" forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),",
" // Serial",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" forge.util.hexToBytes(obj.serialNumber))",
" ]),",
" // KeyEncryptionAlgorithmIdentifier",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // Algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),",
" // Parameter, force NULL, only RSA supported for now.",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ]),",
" // EncryptedKey",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" obj.encryptedContent.content)",
" ]);",
"}",
"",
"/**",
" * Map a set of RecipientInfo ASN.1 objects to recipient objects.",
" *",
" * @param infos an array of ASN.1 representations RecipientInfo (i.e. SET OF).",
" *",
" * @return an array of recipient objects.",
" */",
"function _recipientsFromAsn1(infos) {",
" var ret = [];",
" for(var i = 0; i < infos.length; ++i) {",
" ret.push(_recipientFromAsn1(infos[i]));",
" }",
" return ret;",
"}",
"",
"/**",
" * Map an array of recipient objects to ASN.1 RecipientInfo objects.",
" *",
" * @param recipients an array of recipientInfo objects.",
" *",
" * @return an array of ASN.1 RecipientInfos.",
" */",
"function _recipientsToAsn1(recipients) {",
" var ret = [];",
" for(var i = 0; i < recipients.length; ++i) {",
" ret.push(_recipientToAsn1(recipients[i]));",
" }",
" return ret;",
"}",
"",
"/**",
" * Converts a single signer from an ASN.1 object.",
" *",
" * @param obj the ASN.1 representation of a SignerInfo.",
" *",
" * @return the signer object.",
" */",
"function _signerFromAsn1(obj) {",
" // validate EnvelopedData content block and capture data",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, p7.asn1.signerInfoValidator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#7 SignerInfo. ' +",
" 'ASN.1 object is not an PKCS#7 SignerInfo.');",
" error.errors = errors;",
" throw error;",
" }",
"",
" var rval = {",
" version: capture.version.charCodeAt(0),",
" issuer: forge.pki.RDNAttributesAsArray(capture.issuer),",
" serialNumber: forge.util.createBuffer(capture.serial).toHex(),",
" digestAlgorithm: asn1.derToOid(capture.digestAlgorithm),",
" signatureAlgorithm: asn1.derToOid(capture.signatureAlgorithm),",
" signature: capture.signature,",
" authenticatedAttributes: [],",
" unauthenticatedAttributes: []",
" };",
"",
" // TODO: convert attributes",
" var authenticatedAttributes = capture.authenticatedAttributes || [];",
" var unauthenticatedAttributes = capture.unauthenticatedAttributes || [];",
"",
" return rval;",
"}",
"",
"/**",
" * Converts a single signerInfo object to an ASN.1 object.",
" *",
" * @param obj the signerInfo object.",
" *",
" * @return the ASN.1 representation of a SignerInfo.",
" */",
"function _signerToAsn1(obj) {",
" // SignerInfo",
" var rval = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // version",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" asn1.integerToDer(obj.version).getBytes()),",
" // issuerAndSerialNumber",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // name",
" forge.pki.distinguishedNameToAsn1({attributes: obj.issuer}),",
" // serial",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,",
" forge.util.hexToBytes(obj.serialNumber))",
" ]),",
" // digestAlgorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(obj.digestAlgorithm).getBytes()),",
" // parameters (null)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ])",
" ]);",
"",
" // authenticatedAttributes (OPTIONAL)",
" if(obj.authenticatedAttributesAsn1) {",
" // add ASN.1 previously generated during signing",
" rval.value.push(obj.authenticatedAttributesAsn1);",
" }",
"",
" // digestEncryptionAlgorithm",
" rval.value.push(asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(obj.signatureAlgorithm).getBytes()),",
" // parameters (null)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')",
" ]));",
"",
" // encryptedDigest",
" rval.value.push(asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, obj.signature));",
"",
" // unauthenticatedAttributes (OPTIONAL)",
" if(obj.unauthenticatedAttributes.length > 0) {",
" // [1] IMPLICIT",
" var attrsAsn1 = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, []);",
" for(var i = 0; i < obj.unauthenticatedAttributes.length; ++i) {",
" var attr = obj.unauthenticatedAttributes[i];",
" attrsAsn1.values.push(_attributeToAsn1(attr));",
" }",
" rval.value.push(attrsAsn1);",
" }",
"",
" return rval;",
"}",
"",
"/**",
" * Map a set of SignerInfo ASN.1 objects to an array of signer objects.",
" *",
" * @param signerInfoAsn1s an array of ASN.1 SignerInfos (i.e. SET OF).",
" *",
" * @return an array of signers objects.",
" */",
"function _signersFromAsn1(signerInfoAsn1s) {",
" var ret = [];",
" for(var i = 0; i < signerInfoAsn1s.length; ++i) {",
" ret.push(_signerFromAsn1(signerInfoAsn1s[i]));",
" }",
" return ret;",
"}",
"",
"/**",
" * Map an array of signer objects to ASN.1 objects.",
" *",
" * @param signers an array of signer objects.",
" *",
" * @return an array of ASN.1 SignerInfos.",
" */",
"function _signersToAsn1(signers) {",
" var ret = [];",
" for(var i = 0; i < signers.length; ++i) {",
" ret.push(_signerToAsn1(signers[i]));",
" }",
" return ret;",
"}",
"",
"/**",
" * Convert an attribute object to an ASN.1 Attribute.",
" *",
" * @param attr the attribute object.",
" *",
" * @return the ASN.1 Attribute.",
" */",
"function _attributeToAsn1(attr) {",
" var value;",
"",
" // TODO: generalize to support more attributes",
" if(attr.type === forge.pki.oids.contentType) {",
" value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(attr.value).getBytes());",
" } else if(attr.type === forge.pki.oids.messageDigest) {",
" value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" attr.value.bytes());",
" } else if(attr.type === forge.pki.oids.signingTime) {",
" /* Note per RFC 2985: Dates between 1 January 1950 and 31 December 2049",
" (inclusive) MUST be encoded as UTCTime. Any dates with year values",
" before 1950 or after 2049 MUST be encoded as GeneralizedTime. [Further,]",
" UTCTime values MUST be expressed in Greenwich Mean Time (Zulu) and MUST",
" include seconds (i.e., times are YYMMDDHHMMSSZ), even where the",
" number of seconds is zero. Midnight (GMT) must be represented as",
" \"YYMMDD000000Z\". */",
" // TODO: make these module-level constants",
" var jan_1_1950 = new Date('1950-01-01T00:00:00Z');",
" var jan_1_2050 = new Date('2050-01-01T00:00:00Z');",
" var date = attr.value;",
" if(typeof date === 'string') {",
" // try to parse date",
" var timestamp = Date.parse(date);",
" if(!isNaN(timestamp)) {",
" date = new Date(timestamp);",
" } else if(date.length === 13) {",
" // YYMMDDHHMMSSZ (13 chars for UTCTime)",
" date = asn1.utcTimeToDate(date);",
" } else {",
" // assume generalized time",
" date = asn1.generalizedTimeToDate(date);",
" }",
" }",
"",
" if(date >= jan_1_1950 && date < jan_1_2050) {",
" value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false,",
" asn1.dateToUtcTime(date));",
" } else {",
" value = asn1.create(",
" asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false,",
" asn1.dateToGeneralizedTime(date));",
" }",
" }",
"",
" // TODO: expose as common API call",
" // create a RelativeDistinguishedName set",
" // each value in the set is an AttributeTypeAndValue first",
" // containing the type (an OID) and second the value",
" return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // AttributeType",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(attr.type).getBytes()),",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [",
" // AttributeValue",
" value",
" ])",
" ]);",
"}",
"",
"/**",
" * Map messages encrypted content to ASN.1 objects.",
" *",
" * @param ec The encryptedContent object of the message.",
" *",
" * @return ASN.1 representation of the encryptedContent object (SEQUENCE).",
" */",
"function _encryptedContentToAsn1(ec) {",
" return [",
" // ContentType, always Data for the moment",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(forge.pki.oids.data).getBytes()),",
" // ContentEncryptionAlgorithmIdentifier",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [",
" // Algorithm",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,",
" asn1.oidToDer(ec.algorithm).getBytes()),",
" // Parameters (IV)",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" ec.parameter.getBytes())",
" ]),",
" // [0] EncryptedContent",
" asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [",
" asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,",
" ec.content.getBytes())",
" ])",
" ];",
"}",
"",
"/**",
" * Reads the \"common part\" of an PKCS#7 content block (in ASN.1 format)",
" *",
" * This function reads the \"common part\" of the PKCS#7 content blocks",
" * EncryptedData and EnvelopedData, i.e. version number and symmetrically",
" * encrypted content block.",
" *",
" * The result of the ASN.1 validate and capture process is returned",
" * to allow the caller to extract further data, e.g. the list of recipients",
" * in case of a EnvelopedData object.",
" *",
" * @param msg the PKCS#7 object to read the data to.",
" * @param obj the ASN.1 representation of the content block.",
" * @param validator the ASN.1 structure validator object to use.",
" *",
" * @return the value map captured by validator object.",
" */",
"function _fromAsn1(msg, obj, validator) {",
" var capture = {};",
" var errors = [];",
" if(!asn1.validate(obj, validator, capture, errors)) {",
" var error = new Error('Cannot read PKCS#7 message. ' +",
" 'ASN.1 object is not a supported PKCS#7 message.');",
" error.errors = error;",
" throw error;",
" }",
"",
" // Check contentType, so far we only support (raw) Data.",
" var contentType = asn1.derToOid(capture.contentType);",
" if(contentType !== forge.pki.oids.data) {",
" throw new Error('Unsupported PKCS#7 message. ' +",
" 'Only wrapped ContentType Data supported.');",
" }",
"",
" if(capture.encryptedContent) {",
" var content = '';",
" if(forge.util.isArray(capture.encryptedContent)) {",
" for(var i = 0; i < capture.encryptedContent.length; ++i) {",
" if(capture.encryptedContent[i].type !== asn1.Type.OCTETSTRING) {",
" throw new Error('Malformed PKCS#7 message, expecting encrypted ' +",
" 'content constructed of only OCTET STRING objects.');",
" }",
" content += capture.encryptedContent[i].value;",
" }",
" } else {",
" content = capture.encryptedContent;",
" }",
" msg.encryptedContent = {",
" algorithm: asn1.derToOid(capture.encAlgorithm),",
" parameter: forge.util.createBuffer(capture.encParameter.value),",
" content: forge.util.createBuffer(content)",
" };",
" }",
"",
" if(capture.content) {",
" var content = '';",
" if(forge.util.isArray(capture.content)) {",
" for(var i = 0; i < capture.content.length; ++i) {",
" if(capture.content[i].type !== asn1.Type.OCTETSTRING) {",
" throw new Error('Malformed PKCS#7 message, expecting ' +",
" 'content constructed of only OCTET STRING objects.');",
" }",
" content += capture.content[i].value;",
" }",
" } else {",
" content = capture.content;",
" }",
" msg.content = forge.util.createBuffer(content);",
" }",
"",
" msg.version = capture.version.charCodeAt(0);",
" msg.rawCapture = capture;",
"",
" return capture;",
"}",
"",
"/**",
" * Decrypt the symmetrically encrypted content block of the PKCS#7 message.",
" *",
" * Decryption is skipped in case the PKCS#7 message object already has a",
" * (decrypted) content attribute. The algorithm, key and cipher parameters",
" * (probably the iv) are taken from the encryptedContent attribute of the",
" * message object.",
" *",
" * @param The PKCS#7 message object.",
" */",
"function _decryptContent(msg) {",
" if(msg.encryptedContent.key === undefined) {",
" throw new Error('Symmetric key not available.');",
" }",
"",
" if(msg.content === undefined) {",
" var ciph;",
"",
" switch(msg.encryptedContent.algorithm) {",
" case forge.pki.oids['aes128-CBC']:",
" case forge.pki.oids['aes192-CBC']:",
" case forge.pki.oids['aes256-CBC']:",
" ciph = forge.aes.createDecryptionCipher(msg.encryptedContent.key);",
" break;",
"",
" case forge.pki.oids['desCBC']:",
" case forge.pki.oids['des-EDE3-CBC']:",
" ciph = forge.des.createDecryptionCipher(msg.encryptedContent.key);",
" break;",
"",
" default:",
" throw new Error('Unsupported symmetric cipher, OID ' +",
" msg.encryptedContent.algorithm);",
" }",
" ciph.start(msg.encryptedContent.parameter);",
" ciph.update(msg.encryptedContent.content);",
"",
" if(!ciph.finish()) {",
" throw new Error('Symmetric decryption failed.');",
" }",
"",
" msg.content = ciph.output;",
" }",
"}",
"",
"",
"/***/ }),",
"/* 43 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Functions to output keys in SSH-friendly formats.",
" *",
" * This is part of the Forge project which may be used under the terms of",
" * either the BSD License or the GNU General Public License (GPL) Version 2.",
" *",
" * See: https://github.com/digitalbazaar/forge/blob/cbebca3780658703d925b61b2caffb1d263a6c1d/LICENSE",
" *",
" * @author https://github.com/shellac",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(5);",
"__webpack_require__(8);",
"__webpack_require__(14);",
"__webpack_require__(9);",
"__webpack_require__(1);",
"",
"var ssh = module.exports = forge.ssh = forge.ssh || {};",
"",
"/**",
" * Encodes (and optionally encrypts) a private RSA key as a Putty PPK file.",
" *",
" * @param privateKey the key.",
" * @param passphrase a passphrase to protect the key (falsy for no encryption).",
" * @param comment a comment to include in the key file.",
" *",
" * @return the PPK file as a string.",
" */",
"ssh.privateKeyToPutty = function(privateKey, passphrase, comment) {",
" comment = comment || '';",
" passphrase = passphrase || '';",
" var algorithm = 'ssh-rsa';",
" var encryptionAlgorithm = (passphrase === '') ? 'none' : 'aes256-cbc';",
"",
" var ppk = 'PuTTY-User-Key-File-2: ' + algorithm + '\\r\\n';",
" ppk += 'Encryption: ' + encryptionAlgorithm + '\\r\\n';",
" ppk += 'Comment: ' + comment + '\\r\\n';",
"",
" // public key into buffer for ppk",
" var pubbuffer = forge.util.createBuffer();",
" _addStringToBuffer(pubbuffer, algorithm);",
" _addBigIntegerToBuffer(pubbuffer, privateKey.e);",
" _addBigIntegerToBuffer(pubbuffer, privateKey.n);",
"",
" // write public key",
" var pub = forge.util.encode64(pubbuffer.bytes(), 64);",
" var length = Math.floor(pub.length / 66) + 1; // 66 = 64 + \\r\\n",
" ppk += 'Public-Lines: ' + length + '\\r\\n';",
" ppk += pub;",
"",
" // private key into a buffer",
" var privbuffer = forge.util.createBuffer();",
" _addBigIntegerToBuffer(privbuffer, privateKey.d);",
" _addBigIntegerToBuffer(privbuffer, privateKey.p);",
" _addBigIntegerToBuffer(privbuffer, privateKey.q);",
" _addBigIntegerToBuffer(privbuffer, privateKey.qInv);",
"",
" // optionally encrypt the private key",
" var priv;",
" if(!passphrase) {",
" // use the unencrypted buffer",
" priv = forge.util.encode64(privbuffer.bytes(), 64);",
" } else {",
" // encrypt RSA key using passphrase",
" var encLen = privbuffer.length() + 16 - 1;",
" encLen -= encLen % 16;",
"",
" // pad private key with sha1-d data -- needs to be a multiple of 16",
" var padding = _sha1(privbuffer.bytes());",
"",
" padding.truncate(padding.length() - encLen + privbuffer.length());",
" privbuffer.putBuffer(padding);",
"",
" var aeskey = forge.util.createBuffer();",
" aeskey.putBuffer(_sha1('\\x00\\x00\\x00\\x00', passphrase));",
" aeskey.putBuffer(_sha1('\\x00\\x00\\x00\\x01', passphrase));",
"",
" // encrypt some bytes using CBC mode",
" // key is 40 bytes, so truncate *by* 8 bytes",
" var cipher = forge.aes.createEncryptionCipher(aeskey.truncate(8), 'CBC');",
" cipher.start(forge.util.createBuffer().fillWithByte(0, 16));",
" cipher.update(privbuffer.copy());",
" cipher.finish();",
" var encrypted = cipher.output;",
"",
" // Note: this appears to differ from Putty -- is forge wrong, or putty?",
" // due to padding we finish as an exact multiple of 16",
" encrypted.truncate(16); // all padding",
"",
" priv = forge.util.encode64(encrypted.bytes(), 64);",
" }",
"",
" // output private key",
" length = Math.floor(priv.length / 66) + 1; // 64 + \\r\\n",
" ppk += '\\r\\nPrivate-Lines: ' + length + '\\r\\n';",
" ppk += priv;",
"",
" // MAC",
" var mackey = _sha1('putty-private-key-file-mac-key', passphrase);",
"",
" var macbuffer = forge.util.createBuffer();",
" _addStringToBuffer(macbuffer, algorithm);",
" _addStringToBuffer(macbuffer, encryptionAlgorithm);",
" _addStringToBuffer(macbuffer, comment);",
" macbuffer.putInt32(pubbuffer.length());",
" macbuffer.putBuffer(pubbuffer);",
" macbuffer.putInt32(privbuffer.length());",
" macbuffer.putBuffer(privbuffer);",
"",
" var hmac = forge.hmac.create();",
" hmac.start('sha1', mackey);",
" hmac.update(macbuffer.bytes());",
"",
" ppk += '\\r\\nPrivate-MAC: ' + hmac.digest().toHex() + '\\r\\n';",
"",
" return ppk;",
"};",
"",
"/**",
" * Encodes a public RSA key as an OpenSSH file.",
" *",
" * @param key the key.",
" * @param comment a comment.",
" *",
" * @return the public key in OpenSSH format.",
" */",
"ssh.publicKeyToOpenSSH = function(key, comment) {",
" var type = 'ssh-rsa';",
" comment = comment || '';",
"",
" var buffer = forge.util.createBuffer();",
" _addStringToBuffer(buffer, type);",
" _addBigIntegerToBuffer(buffer, key.e);",
" _addBigIntegerToBuffer(buffer, key.n);",
"",
" return type + ' ' + forge.util.encode64(buffer.bytes()) + ' ' + comment;",
"};",
"",
"/**",
" * Encodes a private RSA key as an OpenSSH file.",
" *",
" * @param key the key.",
" * @param passphrase a passphrase to protect the key (falsy for no encryption).",
" *",
" * @return the public key in OpenSSH format.",
" */",
"ssh.privateKeyToOpenSSH = function(privateKey, passphrase) {",
" if(!passphrase) {",
" return forge.pki.privateKeyToPem(privateKey);",
" }",
" // OpenSSH private key is just a legacy format, it seems",
" return forge.pki.encryptRsaPrivateKey(privateKey, passphrase,",
" {legacy: true, algorithm: 'aes128'});",
"};",
"",
"/**",
" * Gets the SSH fingerprint for the given public key.",
" *",
" * @param options the options to use.",
" * [md] the message digest object to use (defaults to forge.md.md5).",
" * [encoding] an alternative output encoding, such as 'hex'",
" * (defaults to none, outputs a byte buffer).",
" * [delimiter] the delimiter to use between bytes for 'hex' encoded",
" * output, eg: ':' (defaults to none).",
" *",
" * @return the fingerprint as a byte buffer or other encoding based on options.",
" */",
"ssh.getPublicKeyFingerprint = function(key, options) {",
" options = options || {};",
" var md = options.md || forge.md.md5.create();",
"",
" var type = 'ssh-rsa';",
" var buffer = forge.util.createBuffer();",
" _addStringToBuffer(buffer, type);",
" _addBigIntegerToBuffer(buffer, key.e);",
" _addBigIntegerToBuffer(buffer, key.n);",
"",
" // hash public key bytes",
" md.start();",
" md.update(buffer.getBytes());",
" var digest = md.digest();",
" if(options.encoding === 'hex') {",
" var hex = digest.toHex();",
" if(options.delimiter) {",
" return hex.match(/.{2}/g).join(options.delimiter);",
" }",
" return hex;",
" } else if(options.encoding === 'binary') {",
" return digest.getBytes();",
" } else if(options.encoding) {",
" throw new Error('Unknown encoding \"' + options.encoding + '\".');",
" }",
" return digest;",
"};",
"",
"/**",
" * Adds len(val) then val to a buffer.",
" *",
" * @param buffer the buffer to add to.",
" * @param val a big integer.",
" */",
"function _addBigIntegerToBuffer(buffer, val) {",
" var hexVal = val.toString(16);",
" // ensure 2s complement +ve",
" if(hexVal[0] >= '8') {",
" hexVal = '00' + hexVal;",
" }",
" var bytes = forge.util.hexToBytes(hexVal);",
" buffer.putInt32(bytes.length);",
" buffer.putBytes(bytes);",
"}",
"",
"/**",
" * Adds len(val) then val to a buffer.",
" *",
" * @param buffer the buffer to add to.",
" * @param val a string.",
" */",
"function _addStringToBuffer(buffer, val) {",
" buffer.putInt32(val.length);",
" buffer.putString(val);",
"}",
"",
"/**",
" * Hashes the arguments into one value using SHA-1.",
" *",
" * @return the sha1 hash of the provided arguments.",
" */",
"function _sha1() {",
" var sha = forge.md.sha1.create();",
" var num = arguments.length;",
" for (var i = 0; i < num; ++i) {",
" sha.update(arguments[i]);",
" }",
" return sha.digest();",
"}",
"",
"",
"/***/ }),",
"/* 44 */",
"/***/ (function(module, exports, __webpack_require__) {",
"",
"/**",
" * Support for concurrent task management and synchronization in web",
" * applications.",
" *",
" * @author Dave Longley",
" * @author David I. Lehn ",
" *",
" * Copyright (c) 2009-2013 Digital Bazaar, Inc.",
" */",
"var forge = __webpack_require__(0);",
"__webpack_require__(31);",
"__webpack_require__(33);",
"__webpack_require__(1);",
"",
"// logging category",
"var cat = 'forge.task';",
"",
"// verbose level",
"// 0: off, 1: a little, 2: a whole lot",
"// Verbose debug logging is surrounded by a level check to avoid the",
"// performance issues with even calling the logging code regardless if it",
"// is actually logged. For performance reasons this should not be set to 2",
"// for production use.",
"// ex: if(sVL >= 2) forge.log.verbose(....)",
"var sVL = 0;",
"",
"// track tasks for debugging",
"var sTasks = {};",
"var sNextTaskId = 0;",
"// debug access",
"forge.debug.set(cat, 'tasks', sTasks);",
"",
"// a map of task type to task queue",
"var sTaskQueues = {};",
"// debug access",
"forge.debug.set(cat, 'queues', sTaskQueues);",
"",
"// name for unnamed tasks",
"var sNoTaskName = '?';",
"",
"// maximum number of doNext() recursions before a context swap occurs",
"// FIXME: might need to tweak this based on the browser",
"var sMaxRecursions = 30;",
"",
"// time slice for doing tasks before a context swap occurs",
"// FIXME: might need to tweak this based on the browser",
"var sTimeSlice = 20;",
"",
"/**",
" * Task states.",
" *",
" * READY: ready to start processing",
" * RUNNING: task or a subtask is running",
" * BLOCKED: task is waiting to acquire N permits to continue",
" * SLEEPING: task is sleeping for a period of time",
" * DONE: task is done",
" * ERROR: task has an error",
" */",
"var READY = 'ready';",
"var RUNNING = 'running';",
"var BLOCKED = 'blocked';",
"var SLEEPING = 'sleeping';",
"var DONE = 'done';",
"var ERROR = 'error';",
"",
"/**",
" * Task actions. Used to control state transitions.",
" *",
" * STOP: stop processing",
" * START: start processing tasks",
" * BLOCK: block task from continuing until 1 or more permits are released",
" * UNBLOCK: release one or more permits",
" * SLEEP: sleep for a period of time",
" * WAKEUP: wakeup early from SLEEPING state",
" * CANCEL: cancel further tasks",
" * FAIL: a failure occured",
" */",
"var STOP = 'stop';",
"var START = 'start';",
"var BLOCK = 'block';",
"var UNBLOCK = 'unblock';",
"var SLEEP = 'sleep';",
"var WAKEUP = 'wakeup';",
"var CANCEL = 'cancel';",
"var FAIL = 'fail';",
"",
"/**",
" * State transition table.",
" *",
" * nextState = sStateTable[currentState][action]",
" */",
"var sStateTable = {};",
"",
"sStateTable[READY] = {};",
"sStateTable[READY][STOP] = READY;",
"sStateTable[READY][START] = RUNNING;",
"sStateTable[READY][CANCEL] = DONE;",
"sStateTable[READY][FAIL] = ERROR;",
"",
"sStateTable[RUNNING] = {};",
"sStateTable[RUNNING][STOP] = READY;",
"sStateTable[RUNNING][START] = RUNNING;",
"sStateTable[RUNNING][BLOCK] = BLOCKED;",
"sStateTable[RUNNING][UNBLOCK] = RUNNING;",
"sStateTable[RUNNING][SLEEP] = SLEEPING;",
"sStateTable[RUNNING][WAKEUP] = RUNNING;",
"sStateTable[RUNNING][CANCEL] = DONE;",
"sStateTable[RUNNING][FAIL] = ERROR;",
"",
"sStateTable[BLOCKED] = {};",
"sStateTable[BLOCKED][STOP] = BLOCKED;",
"sStateTable[BLOCKED][START] = BLOCKED;",
"sStateTable[BLOCKED][BLOCK] = BLOCKED;",
"sStateTable[BLOCKED][UNBLOCK] = BLOCKED;",
"sStateTable[BLOCKED][SLEEP] = BLOCKED;",
"sStateTable[BLOCKED][WAKEUP] = BLOCKED;",
"sStateTable[BLOCKED][CANCEL] = DONE;",
"sStateTable[BLOCKED][FAIL] = ERROR;",
"",
"sStateTable[SLEEPING] = {};",
"sStateTable[SLEEPING][STOP] = SLEEPING;",
"sStateTable[SLEEPING][START] = SLEEPING;",
"sStateTable[SLEEPING][BLOCK] = SLEEPING;",
"sStateTable[SLEEPING][UNBLOCK] = SLEEPING;",
"sStateTable[SLEEPING][SLEEP] = SLEEPING;",
"sStateTable[SLEEPING][WAKEUP] = SLEEPING;",
"sStateTable[SLEEPING][CANCEL] = DONE;",
"sStateTable[SLEEPING][FAIL] = ERROR;",
"",
"sStateTable[DONE] = {};",
"sStateTable[DONE][STOP] = DONE;",
"sStateTable[DONE][START] = DONE;",
"sStateTable[DONE][BLOCK] = DONE;",
"sStateTable[DONE][UNBLOCK] = DONE;",
"sStateTable[DONE][SLEEP] = DONE;",
"sStateTable[DONE][WAKEUP] = DONE;",
"sStateTable[DONE][CANCEL] = DONE;",
"sStateTable[DONE][FAIL] = ERROR;",
"",
"sStateTable[ERROR] = {};",
"sStateTable[ERROR][STOP] = ERROR;",
"sStateTable[ERROR][START] = ERROR;",
"sStateTable[ERROR][BLOCK] = ERROR;",
"sStateTable[ERROR][UNBLOCK] = ERROR;",
"sStateTable[ERROR][SLEEP] = ERROR;",
"sStateTable[ERROR][WAKEUP] = ERROR;",
"sStateTable[ERROR][CANCEL] = ERROR;",
"sStateTable[ERROR][FAIL] = ERROR;",
"",
"/**",
" * Creates a new task.",
" *",
" * @param options options for this task",
" * run: the run function for the task (required)",
" * name: the run function for the task (optional)",
" * parent: parent of this task (optional)",
" *",
" * @return the empty task.",
" */",
"var Task = function(options) {",
" // task id",
" this.id = -1;",
"",
" // task name",
" this.name = options.name || sNoTaskName;",
"",
" // task has no parent",
" this.parent = options.parent || null;",
"",
" // save run function",
" this.run = options.run;",
"",
" // create a queue of subtasks to run",
" this.subtasks = [];",
"",
" // error flag",
" this.error = false;",
"",
" // state of the task",
" this.state = READY;",
"",
" // number of times the task has been blocked (also the number",
" // of permits needed to be released to continue running)",
" this.blocks = 0;",
"",
" // timeout id when sleeping",
" this.timeoutId = null;",
"",
" // no swap time yet",
" this.swapTime = null;",
"",
" // no user data",
" this.userData = null;",
"",
" // initialize task",
" // FIXME: deal with overflow",
" this.id = sNextTaskId++;",
" sTasks[this.id] = this;",
" if(sVL >= 1) {",
" forge.log.verbose(cat, '[%s][%s] init', this.id, this.name, this);",
" }",
"};",
"",
"/**",
" * Logs debug information on this task and the system state.",
" */",
"Task.prototype.debug = function(msg) {",
" msg = msg || '';",
" forge.log.debug(cat, msg,",
" '[%s][%s] task:', this.id, this.name, this,",
" 'subtasks:', this.subtasks.length,",
" 'queue:', sTaskQueues);",
"};",
"",
"/**",
" * Adds a subtask to run after task.doNext() or task.fail() is called.",
" *",
" * @param name human readable name for this task (optional).",
" * @param subrun a function to run that takes the current task as",
" * its first parameter.",
" *",
" * @return the current task (useful for chaining next() calls).",
" */",
"Task.prototype.next = function(name, subrun) {",
" // juggle parameters if it looks like no name is given",
" if(typeof(name) === 'function') {",
" subrun = name;",
"",
" // inherit parent's name",
" name = this.name;",
" }",
" // create subtask, set parent to this task, propagate callbacks",
" var subtask = new Task({",
" run: subrun,",
" name: name,",
" parent: this",
" });",
" // start subtasks running",
" subtask.state = RUNNING;",
" subtask.type = this.type;",
" subtask.successCallback = this.successCallback || null;",
" subtask.failureCallback = this.failureCallback || null;",
"",
" // queue a new subtask",
" this.subtasks.push(subtask);",
"",
" return this;",
"};",
"",
"/**",
" * Adds subtasks to run in parallel after task.doNext() or task.fail()",
" * is called.",
" *",
" * @param name human readable name for this task (optional).",
" * @param subrun functions to run that take the current task as",
" * their first parameter.",
" *",
" * @return the current task (useful for chaining next() calls).",
" */",
"Task.prototype.parallel = function(name, subrun) {",
" // juggle parameters if it looks like no name is given",
" if(forge.util.isArray(name)) {",
" subrun = name;",
"",
" // inherit parent's name",
" name = this.name;",
" }",
" // Wrap parallel tasks in a regular task so they are started at the",
" // proper time.",
" return this.next(name, function(task) {",
" // block waiting for subtasks",
" var ptask = task;",
" ptask.block(subrun.length);",
"",
" // we pass the iterator from the loop below as a parameter",
" // to a function because it is otherwise included in the",
" // closure and changes as the loop changes -- causing i",
" // to always be set to its highest value",
" var startParallelTask = function(pname, pi) {",
" forge.task.start({",
" type: pname,",
" run: function(task) {",
" subrun[pi](task);",
" },",
" success: function(task) {",
" ptask.unblock();",
" },",
" failure: function(task) {",
" ptask.unblock();",
" }",
" });",
" };",
"",
" for(var i = 0; i < subrun.length; i++) {",
" // Type must be unique so task starts in parallel:",
" // name + private string + task id + sub-task index",
" // start tasks in parallel and unblock when the finish",
" var pname = name + '__parallel-' + task.id + '-' + i;",
" var pi = i;",
" startParallelTask(pname, pi);",
" }",
" });",
"};",
"",
"/**",
" * Stops a running task.",
" */",
"Task.prototype.stop = function() {",
" this.state = sStateTable[this.state][STOP];",
"};",
"",
"/**",
" * Starts running a task.",
" */",
"Task.prototype.start = function() {",
" this.error = false;",
" this.state = sStateTable[this.state][START];",
"",
" // try to restart",
" if(this.state === RUNNING) {",
" this.start = new Date();",
" this.run(this);",
" runNext(this, 0);",
" }",
"};",
"",
"/**",
" * Blocks a task until it one or more permits have been released. The",
" * task will not resume until the requested number of permits have",
" * been released with call(s) to unblock().",
" *",
" * @param n number of permits to wait for(default: 1).",
" */",
"Task.prototype.block = function(n) {",
" n = typeof(n) === 'undefined' ? 1 : n;",
" this.blocks += n;",
" if(this.blocks > 0) {",
" this.state = sStateTable[this.state][BLOCK];",
" }",
"};",
"",
"/**",
" * Releases a permit to unblock a task. If a task was blocked by",
" * requesting N permits via block(), then it will only continue",
" * running once enough permits have been released via unblock() calls.",
" *",
" * If multiple processes need to synchronize with a single task then",
" * use a condition variable (see forge.task.createCondition). It is",
" * an error to unblock a task more times than it has been blocked.",
" *",
" * @param n number of permits to release (default: 1).",
" *",
" * @return the current block count (task is unblocked when count is 0)",
" */",
"Task.prototype.unblock = function(n) {",
" n = typeof(n) === 'undefined' ? 1 : n;",
" this.blocks -= n;",
" if(this.blocks === 0 && this.state !== DONE) {",
" this.state = RUNNING;",
" runNext(this, 0);",
" }",
" return this.blocks;",
"};",
"",
"/**",
" * Sleep for a period of time before resuming tasks.",
" *",
" * @param n number of milliseconds to sleep (default: 0).",
" */",
"Task.prototype.sleep = function(n) {",
" n = typeof(n) === 'undefined' ? 0 : n;",
" this.state = sStateTable[this.state][SLEEP];",
" var self = this;",
" this.timeoutId = setTimeout(function() {",
" self.timeoutId = null;",
" self.state = RUNNING;",
" runNext(self, 0);",
" }, n);",
"};",
"",
"/**",
" * Waits on a condition variable until notified. The next task will",
" * not be scheduled until notification. A condition variable can be",
" * created with forge.task.createCondition().",
" *",
" * Once cond.notify() is called, the task will continue.",
" *",
" * @param cond the condition variable to wait on.",
" */",
"Task.prototype.wait = function(cond) {",
" cond.wait(this);",
"};",
"",
"/**",
" * If sleeping, wakeup and continue running tasks.",
" */",
"Task.prototype.wakeup = function() {",
" if(this.state === SLEEPING) {",
" cancelTimeout(this.timeoutId);",
" this.timeoutId = null;",
" this.state = RUNNING;",
" runNext(this, 0);",
" }",
"};",
"",
"/**",
" * Cancel all remaining subtasks of this task.",
" */",
"Task.prototype.cancel = function() {",
" this.state = sStateTable[this.state][CANCEL];",
" // remove permits needed",
" this.permitsNeeded = 0;",
" // cancel timeouts",
" if(this.timeoutId !== null) {",
" cancelTimeout(this.timeoutId);",
" this.timeoutId = null;",
" }",
" // remove subtasks",
" this.subtasks = [];",
"};",
"",
"/**",
" * Finishes this task with failure and sets error flag. The entire",
" * task will be aborted unless the next task that should execute",
" * is passed as a parameter. This allows levels of subtasks to be",
" * skipped. For instance, to abort only this tasks's subtasks, then",
" * call fail(task.parent). To abort this task's subtasks and its",
" * parent's subtasks, call fail(task.parent.parent). To abort",
" * all tasks and simply call the task callback, call fail() or",
" * fail(null).",
" *",
" * The task callback (success or failure) will always, eventually, be",
" * called.",
" *",
" * @param next the task to continue at, or null to abort entirely.",
" */",
"Task.prototype.fail = function(next) {",
" // set error flag",
" this.error = true;",
"",
" // finish task",
" finish(this, true);",
"",
" if(next) {",
" // propagate task info",
" next.error = this.error;",
" next.swapTime = this.swapTime;",
" next.userData = this.userData;",
"",
" // do next task as specified",
" runNext(next, 0);",
" } else {",
" if(this.parent !== null) {",
" // finish root task (ensures it is removed from task queue)",
" var parent = this.parent;",
" while(parent.parent !== null) {",
" // propagate task info",
" parent.error = this.error;",
" parent.swapTime = this.swapTime;",
" parent.userData = this.userData;",
" parent = parent.parent;",
" }",
" finish(parent, true);",
" }",
"",
" // call failure callback if one exists",
" if(this.failureCallback) {",
" this.failureCallback(this);",
" }",
" }",
"};",
"",
"/**",
" * Asynchronously start a task.",
" *",
" * @param task the task to start.",
" */",
"var start = function(task) {",
" task.error = false;",
" task.state = sStateTable[task.state][START];",
" setTimeout(function() {",
" if(task.state === RUNNING) {",
" task.swapTime = +new Date();",
" task.run(task);",
" runNext(task, 0);",
" }",
" }, 0);",
"};",
"",
"/**",
" * Run the next subtask or finish this task.",
" *",
" * @param task the task to process.",
" * @param recurse the recursion count.",
" */",
"var runNext = function(task, recurse) {",
" // get time since last context swap (ms), if enough time has passed set",
" // swap to true to indicate that doNext was performed asynchronously",
" // also, if recurse is too high do asynchronously",
" var swap =",
" (recurse > sMaxRecursions) ||",
" (+new Date() - task.swapTime) > sTimeSlice;",
"",
" var doNext = function(recurse) {",
" recurse++;",
" if(task.state === RUNNING) {",
" if(swap) {",
" // update swap time",
" task.swapTime = +new Date();",
" }",
"",
" if(task.subtasks.length > 0) {",
" // run next subtask",
" var subtask = task.subtasks.shift();",
" subtask.error = task.error;",
" subtask.swapTime = task.swapTime;",
" subtask.userData = task.userData;",
" subtask.run(subtask);",
" if(!subtask.error) {",
" runNext(subtask, recurse);",
" }",
" } else {",
" finish(task);",
"",
" if(!task.error) {",
" // chain back up and run parent",
" if(task.parent !== null) {",
" // propagate task info",
" task.parent.error = task.error;",
" task.parent.swapTime = task.swapTime;",
" task.parent.userData = task.userData;",
"",
" // no subtasks left, call run next subtask on parent",
" runNext(task.parent, recurse);",
" }",
" }",
" }",
" }",
" };",
"",
" if(swap) {",
" // we're swapping, so run asynchronously",
" setTimeout(doNext, 0);",
" } else {",
" // not swapping, so run synchronously",
" doNext(recurse);",
" }",
"};",
"",
"/**",
" * Finishes a task and looks for the next task in the queue to start.",
" *",
" * @param task the task to finish.",
" * @param suppressCallbacks true to suppress callbacks.",
" */",
"var finish = function(task, suppressCallbacks) {",
" // subtask is now done",
" task.state = DONE;",
"",
" delete sTasks[task.id];",
" if(sVL >= 1) {",
" forge.log.verbose(cat, '[%s][%s] finish',",
" task.id, task.name, task);",
" }",
"",
" // only do queue processing for root tasks",
" if(task.parent === null) {",
" // report error if queue is missing",
" if(!(task.type in sTaskQueues)) {",
" forge.log.error(cat,",
" '[%s][%s] task queue missing [%s]',",
" task.id, task.name, task.type);",
" } else if(sTaskQueues[task.type].length === 0) {",
" // report error if queue is empty",
" forge.log.error(cat,",
" '[%s][%s] task queue empty [%s]',",
" task.id, task.name, task.type);",
" } else if(sTaskQueues[task.type][0] !== task) {",
" // report error if this task isn't the first in the queue",
" forge.log.error(cat,",
" '[%s][%s] task not first in queue [%s]',",
" task.id, task.name, task.type);",
" } else {",
" // remove ourselves from the queue",
" sTaskQueues[task.type].shift();",
" // clean up queue if it is empty",
" if(sTaskQueues[task.type].length === 0) {",
" if(sVL >= 1) {",
" forge.log.verbose(cat, '[%s][%s] delete queue [%s]',",
" task.id, task.name, task.type);",
" }",
" /* Note: Only a task can delete a queue of its own type. This",
" is used as a way to synchronize tasks. If a queue for a certain",
" task type exists, then a task of that type is running.",
" */",
" delete sTaskQueues[task.type];",
" } else {",
" // dequeue the next task and start it",
" if(sVL >= 1) {",
" forge.log.verbose(cat,",
" '[%s][%s] queue start next [%s] remain:%s',",
" task.id, task.name, task.type,",
" sTaskQueues[task.type].length);",
" }",
" sTaskQueues[task.type][0].start();",
" }",
" }",
"",
" if(!suppressCallbacks) {",
" // call final callback if one exists",
" if(task.error && task.failureCallback) {",
" task.failureCallback(task);",
" } else if(!task.error && task.successCallback) {",
" task.successCallback(task);",
" }",
" }",
" }",
"};",
"",
"/* Tasks API */",
"module.exports = forge.task = forge.task || {};",
"",
"/**",
" * Starts a new task that will run the passed function asynchronously.",
" *",
" * In order to finish the task, either task.doNext() or task.fail()",
" * *must* be called.",
" *",
" * The task must have a type (a string identifier) that can be used to",
" * synchronize it with other tasks of the same type. That type can also",
" * be used to cancel tasks that haven't started yet.",
" *",
" * To start a task, the following object must be provided as a parameter",
" * (each function takes a task object as its first parameter):",
" *",
" * {",
" * type: the type of task.",
" * run: the function to run to execute the task.",
" * success: a callback to call when the task succeeds (optional).",
" * failure: a callback to call when the task fails (optional).",
" * }",
" *",
" * @param options the object as described above.",
" */",
"forge.task.start = function(options) {",
" // create a new task",
" var task = new Task({",
" run: options.run,",
" name: options.name || sNoTaskName",
" });",
" task.type = options.type;",
" task.successCallback = options.success || null;",
" task.failureCallback = options.failure || null;",
"",
" // append the task onto the appropriate queue",
" if(!(task.type in sTaskQueues)) {",
" if(sVL >= 1) {",
" forge.log.verbose(cat, '[%s][%s] create queue [%s]',",
" task.id, task.name, task.type);",
" }",
" // create the queue with the new task",
" sTaskQueues[task.type] = [task];",
" start(task);",
" } else {",
" // push the task onto the queue, it will be run after a task",
" // with the same type completes",
" sTaskQueues[options.type].push(task);",
" }",
"};",
"",
"/**",
" * Cancels all tasks of the given type that haven't started yet.",
" *",
" * @param type the type of task to cancel.",
" */",
"forge.task.cancel = function(type) {",
" // find the task queue",
" if(type in sTaskQueues) {",
" // empty all but the current task from the queue",
" sTaskQueues[type] = [sTaskQueues[type][0]];",
" }",
"};",
"",
"/**",
" * Creates a condition variable to synchronize tasks. To make a task wait",
" * on the condition variable, call task.wait(condition). To notify all",
" * tasks that are waiting, call condition.notify().",
" *",
" * @return the condition variable.",
" */",
"forge.task.createCondition = function() {",
" var cond = {",
" // all tasks that are blocked",
" tasks: {}",
" };",
"",
" /**",
" * Causes the given task to block until notify is called. If the task",
" * is already waiting on this condition then this is a no-op.",
" *",
" * @param task the task to cause to wait.",
" */",
" cond.wait = function(task) {",
" // only block once",
" if(!(task.id in cond.tasks)) {",
" task.block();",
" cond.tasks[task.id] = task;",
" }",
" };",
"",
" /**",
" * Notifies all waiting tasks to wake up.",
" */",
" cond.notify = function() {",
" // since unblock() will run the next task from here, make sure to",
" // clear the condition's blocked task list before unblocking",
" var tmp = cond.tasks;",
" cond.tasks = {};",
" for(var id in tmp) {",
" tmp[id].unblock();",
" }",
" };",
"",
" return cond;",
"};",
"",
"",
"/***/ })",
"/******/ ]);",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"// ========================== OBI Logic ==========================",
"",
"// Common",
"function getHeaderValue(headerName) {",
" const headerValue = request.headers[headerName];",
" if (headerValue === undefined) {",
" throw new Error(`Requried header: ${headerName} is not defined`);",
" }",
" return resolveVariables(headerValue);",
"}",
"",
"function resolveVariables(textWithPossibleVaraibles) {",
" return textWithPossibleVaraibles.replace(/{{(\\w*)}}/g, (str, key) => {",
" const value = environment[key];",
" return value === undefined ? \"{{\" + key + \"}}\" : value;",
" });",
"}",
"",
"function isUrlEncodedForm() {",
" return request.headers[\"content-type\"] === \"application/x-www-form-urlencoded\";",
"}",
"",
"function noBody() {",
" return Object.entries(request.data).length === 0 && request.data.constructor === Object;",
"}",
"",
"// Digest Calculation",
"function resolveRequestBody() {",
" if (isUrlEncodedForm()) {",
" const data = Object.keys(request.data)",
" .sort((a, b) => {",
" if(a < b) { return -1; }",
" if(a > b) { return 1; }",
" return 0;",
" })",
" .map(key => key + \"=\" + request.data[key])",
" .join('&');",
" return resolveVariables(data);",
" } else if (noBody()) {",
" return \"\";",
" }",
"",
" return resolveVariables(request.data.toString());",
"}",
"",
"function calculateDigest() {",
" const requestData = resolveRequestBody();",
" console.log(`Request data: ${requestData}`);",
"",
" const sha256digest = CryptoJS.SHA256(requestData);",
" const base64sha256 = CryptoJS.enc.Base64.stringify(sha256digest);",
" const calculatedDigest = 'sha-256=' + base64sha256;",
" ",
" console.log(`Digest header: ${calculatedDigest}`);",
" pm.environment.set(\"Digest\", calculatedDigest);",
" return calculatedDigest;",
"}",
"",
"// Signature Calculation",
"",
"const sdk = require(\"postman-collection\");",
"const moment = require(\"moment\")",
"",
"const requestWithoutContentHeaders = \"(request-target) x-nordea-originating-host x-nordea-originating-date\";",
"const requestWithContentHeaders = \"(request-target) x-nordea-originating-host x-nordea-originating-date content-type digest\";",
"",
"function getSignatureBaseOnRequest() {",
" const url = new sdk.Url(resolveVariables(request.url));",
" const host = url.getHost().toLowerCase();",
" const path = url.getPathWithQuery();",
" const method = request.method.toLowerCase();",
" const date = moment().utc().format(\"ddd, DD MMM YYYY HH:mm:ss\") + \" GMT\";",
"",
" let headers = requestWithoutContentHeaders;",
"",
" let normalizedString =",
" `(request-target): ${method} ${path}\\n` +",
" `x-nordea-originating-host: ${host}\\n` +",
" `x-nordea-originating-date: ${date}`;",
"",
" if (isUrlEncodedForm() || !noBody()) {",
" const contentType = getHeaderValue(\"content-type\");",
" const digest = calculateDigest();",
" normalizedString += `\\ncontent-type: ${contentType}\\ndigest: ${digest}`",
"",
" headers = requestWithContentHeaders;",
" }",
" return {host, path, method, date, headers, normalizedString};",
"}",
"",
"function encryptSignature(normalizedSignatureString) {",
" const messageDigest = forge.md.sha256.create();",
" messageDigest.update(normalizedSignatureString, \"utf8\");",
" return forge.util.encode64(getPrivateKey().sign(messageDigest));",
"}",
"",
"function getPrivateKey() {",
" let eidasPrivateKey = pm.environment.get(\"eidasPrivateKey\");",
"",
" if (!eidasPrivateKey.includes('PRIVATE KEY')) {",
" eidasPrivateKey = \"-----BEGIN RSA PRIVATE KEY-----\\n\" + eidasPrivateKey + \"\\n\" + \"-----END RSA PRIVATE KEY-----\";",
" }",
" console.log(eidasPrivateKey);",
" return forge.pki.privateKeyFromPem(eidasPrivateKey);",
"}",
"",
"const clientId = getHeaderValue(\"x-ibm-client-id\")",
"const signature = getSignatureBaseOnRequest();",
"const encryptedSignature = encryptSignature(signature.normalizedString);",
"const signatureHeader = `keyId=\"${clientId}\",algorithm=\"rsa-sha256\",headers=\"${signature.headers}\",signature=\"${encryptedSignature}\"`;",
"",
"console.log(`Normalized signature string: ${signature.normalizedString}`);",
"console.log(`Signature header: ${signatureHeader}`);",
"",
"pm.environment.set(\"Signature\", signatureHeader);",
"pm.environment.set(\"X-Nordea-Originating-Host\", signature.host);",
"pm.environment.set(\"X-Nordea-Originating-Date\", signature.date);"
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
""
]
}
}
]
}