0) {
if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p)
r[k++] = d|(this.s<<(this.DB-p));
while(i >= 0) {
if(p < 8) {
d = (this[i]&((1<>(p+=this.DB-8);
}
else {
d = (this[i]>>(p-=8))&0xff;
if(p <= 0) { p += this.DB; --i; }
}
//if((d&0x80) != 0) d |= -256;
//if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
if(k > 0 || d != this.s) r[k++] = d;
}
}
return r;
}
function bnEquals(a) { return(this.compareTo(a)==0); }
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }
// (protected) r = this op a (bitwise)
function bnpBitwiseTo(a,op,r) {
var i, f, m = Math.min(a.t,this.t);
for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
if(a.t < this.t) {
f = a.s&this.DM;
for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
r.t = this.t;
}
else {
f = this.s&this.DM;
for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
r.t = a.t;
}
r.s = op(this.s,a.s);
r.clamp();
}
// (public) this & a
function op_and(x,y) { return x&y; }
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }
// (public) this | a
function op_or(x,y) { return x|y; }
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }
// (public) this ^ a
function op_xor(x,y) { return x^y; }
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }
// (public) this & ~a
function op_andnot(x,y) { return x&~y; }
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }
// (public) ~this
function bnNot() {
var r = nbi();
for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
r.t = this.t;
r.s = ~this.s;
return r;
}
// (public) this << n
function bnShiftLeft(n) {
var r = nbi();
if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
return r;
}
// (public) this >> n
function bnShiftRight(n) {
var r = nbi();
if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
return r;
}
// return index of lowest 1-bit in x, x < 2^31
function lbit(x) {
if(x == 0) return -1;
var r = 0;
if((x&0xffff) == 0) { x >>= 16; r += 16; }
if((x&0xff) == 0) { x >>= 8; r += 8; }
if((x&0xf) == 0) { x >>= 4; r += 4; }
if((x&3) == 0) { x >>= 2; r += 2; }
if((x&1) == 0) ++r;
return r;
}
// (public) returns index of lowest 1-bit (or -1 if none)
function bnGetLowestSetBit() {
for(var i = 0; i < this.t; ++i)
if(this[i] != 0) return i*this.DB+lbit(this[i]);
if(this.s < 0) return this.t*this.DB;
return -1;
}
// return number of 1 bits in x
function cbit(x) {
var r = 0;
while(x != 0) { x &= x-1; ++r; }
return r;
}
// (public) return number of set bits
function bnBitCount() {
var r = 0, x = this.s&this.DM;
for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
return r;
}
// (public) true iff nth bit is set
function bnTestBit(n) {
var j = Math.floor(n/this.DB);
if(j >= this.t) return(this.s!=0);
return((this[j]&(1<<(n%this.DB)))!=0);
}
// (protected) this op (1<>= this.DB;
}
if(a.t < this.t) {
c += a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c += a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += a.s;
}
r.s = (c<0)?-1:0;
if(c > 0) r[i++] = c;
else if(c < -1) r[i++] = this.DV+c;
r.t = i;
r.clamp();
}
// (public) this + a
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }
// (public) this - a
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }
// (public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }
// (public) this^2
function bnSquare() { var r = nbi(); this.squareTo(r); return r; }
// (public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }
// (public) this % a
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }
// (public) [this/a,this%a]
function bnDivideAndRemainder(a) {
var q = nbi(), r = nbi();
this.divRemTo(a,q,r);
return new Array(q,r);
}
// (protected) this *= n, this >= 0, 1 < n < DV
function bnpDMultiply(n) {
this[this.t] = this.am(0,n-1,this,0,0,this.t);
++this.t;
this.clamp();
}
// (protected) this += n << w words, this >= 0
function bnpDAddOffset(n,w) {
if(n == 0) return;
while(this.t <= w) this[this.t++] = 0;
this[w] += n;
while(this[w] >= this.DV) {
this[w] -= this.DV;
if(++w >= this.t) this[this.t++] = 0;
++this[w];
}
}
// A "null" reducer
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }
NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;
// (public) this^e
function bnPow(e) { return this.exp(e,new NullExp()); }
// (protected) r = lower n words of "this * a", a.t <= n
// "this" should be the larger one if appropriate.
function bnpMultiplyLowerTo(a,n,r) {
var i = Math.min(this.t+a.t,n);
r.s = 0; // assumes a,this >= 0
r.t = i;
while(i > 0) r[--i] = 0;
var j;
for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
r.clamp();
}
// (protected) r = "this * a" without lower n words, n > 0
// "this" should be the larger one if appropriate.
function bnpMultiplyUpperTo(a,n,r) {
--n;
var i = r.t = this.t+a.t-n;
r.s = 0; // assumes a,this >= 0
while(--i >= 0) r[i] = 0;
for(i = Math.max(n-this.t,0); i < a.t; ++i)
r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
r.clamp();
r.drShiftTo(1,r);
}
// Barrett modular reduction
function Barrett(m) {
// setup Barrett
this.r2 = nbi();
this.q3 = nbi();
BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
this.mu = this.r2.divide(m);
this.m = m;
}
function barrettConvert(x) {
if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
else if(x.compareTo(this.m) < 0) return x;
else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
}
function barrettRevert(x) { return x; }
// x = x mod m (HAC 14.42)
function barrettReduce(x) {
x.drShiftTo(this.m.t-1,this.r2);
if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
x.subTo(this.r2,x);
while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Barrett.prototype.convert = barrettConvert;
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;
// (public) this^e % m (HAC 14.85)
function bnModPow(e,m) {
var i = e.bitLength(), k, r = nbv(1), z;
if(i <= 0) return r;
else if(i < 18) k = 1;
else if(i < 48) k = 3;
else if(i < 144) k = 4;
else if(i < 768) k = 5;
else k = 6;
if(i < 8)
z = new Classic(m);
else if(m.isEven())
z = new Barrett(m);
else
z = new Montgomery(m);
// precomputation
var g = new Array(), n = 3, k1 = k-1, km = (1< 1) {
var g2 = nbi();
z.sqrTo(g[1],g2);
while(n <= km) {
g[n] = nbi();
z.mulTo(g2,g[n-2],g[n]);
n += 2;
}
}
var j = e.t-1, w, is1 = true, r2 = nbi(), t;
i = nbits(e[j])-1;
while(j >= 0) {
if(i >= k1) w = (e[j]>>(i-k1))&km;
else {
w = (e[j]&((1<<(i+1))-1))<<(k1-i);
if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
}
n = k;
while((w&1) == 0) { w >>= 1; --n; }
if((i -= n) < 0) { i += this.DB; --j; }
if(is1) { // ret == 1, don't bother squaring or multiplying it
g[w].copyTo(r);
is1 = false;
}
else {
while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
z.mulTo(r2,g[w],r);
}
while(j >= 0 && (e[j]&(1< 0) {
x.rShiftTo(g,x);
y.rShiftTo(g,y);
}
while(x.signum() > 0) {
if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
if(x.compareTo(y) >= 0) {
x.subTo(y,x);
x.rShiftTo(1,x);
}
else {
y.subTo(x,y);
y.rShiftTo(1,y);
}
}
if(g > 0) y.lShiftTo(g,y);
return y;
}
// (protected) this % n, n < 2^26
function bnpModInt(n) {
if(n <= 0) return 0;
var d = this.DV%n, r = (this.s<0)?n-1:0;
if(this.t > 0)
if(d == 0) r = this[0]%n;
else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
return r;
}
// (public) 1/this % m (HAC 14.61)
function bnModInverse(m) {
var ac = m.isEven();
if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
var u = m.clone(), v = this.clone();
var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
while(u.signum() != 0) {
while(u.isEven()) {
u.rShiftTo(1,u);
if(ac) {
if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
a.rShiftTo(1,a);
}
else if(!b.isEven()) b.subTo(m,b);
b.rShiftTo(1,b);
}
while(v.isEven()) {
v.rShiftTo(1,v);
if(ac) {
if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
c.rShiftTo(1,c);
}
else if(!d.isEven()) d.subTo(m,d);
d.rShiftTo(1,d);
}
if(u.compareTo(v) >= 0) {
u.subTo(v,u);
if(ac) a.subTo(c,a);
b.subTo(d,b);
}
else {
v.subTo(u,v);
if(ac) c.subTo(a,c);
d.subTo(b,d);
}
}
if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
if(d.compareTo(m) >= 0) return d.subtract(m);
if(d.signum() < 0) d.addTo(m,d); else return d;
if(d.signum() < 0) return d.add(m); else return d;
}
var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];
// (public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
var i, x = this.abs();
if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) {
for(i = 0; i < lowprimes.length; ++i)
if(x[0] == lowprimes[i]) return true;
return false;
}
if(x.isEven()) return false;
i = 1;
while(i < lowprimes.length) {
var m = lowprimes[i], j = i+1;
while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
m = x.modInt(m);
while(i < j) if(m%lowprimes[i++] == 0) return false;
}
return x.millerRabin(t);
}
/* added by Recurity Labs */
function nbits(x) {
var n = 1, t;
if ((t = x >>> 16) != 0) {
x = t;
n += 16;
}
if ((t = x >> 8) != 0) {
x = t;
n += 8;
}
if ((t = x >> 4) != 0) {
x = t;
n += 4;
}
if ((t = x >> 2) != 0) {
x = t;
n += 2;
}
if ((t = x >> 1) != 0) {
x = t;
n += 1;
}
return n;
}
function bnToMPI () {
var ba = this.toByteArray();
var size = (ba.length-1)*8+nbits(ba[0]);
var result = "";
result += String.fromCharCode((size & 0xFF00) >> 8);
result += String.fromCharCode(size & 0xFF);
result += util.bin2str(ba);
return result;
}
/* END of addition */
// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
var n1 = this.subtract(BigInteger.ONE);
var k = n1.getLowestSetBit();
if(k <= 0) return false;
var r = n1.shiftRight(k);
t = (t+1)>>1;
if(t > lowprimes.length) t = lowprimes.length;
var a = nbi();
for(var i = 0; i < t; ++i) {
//Pick bases at random, instead of starting at 2
a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);
var y = a.modPow(r,this);
if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
var j = 1;
while(j++ < k && y.compareTo(n1) != 0) {
y = y.modPowInt(2,this);
if(y.compareTo(BigInteger.ONE) == 0) return false;
}
if(y.compareTo(n1) != 0) return false;
}
}
return true;
}
// protected
BigInteger.prototype.chunkSize = bnpChunkSize;
BigInteger.prototype.toRadix = bnpToRadix;
BigInteger.prototype.fromRadix = bnpFromRadix;
BigInteger.prototype.fromNumber = bnpFromNumber;
BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
BigInteger.prototype.changeBit = bnpChangeBit;
BigInteger.prototype.addTo = bnpAddTo;
BigInteger.prototype.dMultiply = bnpDMultiply;
BigInteger.prototype.dAddOffset = bnpDAddOffset;
BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
BigInteger.prototype.modInt = bnpModInt;
BigInteger.prototype.millerRabin = bnpMillerRabin;
// public
BigInteger.prototype.clone = bnClone;
BigInteger.prototype.intValue = bnIntValue;
BigInteger.prototype.byteValue = bnByteValue;
BigInteger.prototype.shortValue = bnShortValue;
BigInteger.prototype.signum = bnSigNum;
BigInteger.prototype.toByteArray = bnToByteArray;
BigInteger.prototype.equals = bnEquals;
BigInteger.prototype.min = bnMin;
BigInteger.prototype.max = bnMax;
BigInteger.prototype.and = bnAnd;
BigInteger.prototype.or = bnOr;
BigInteger.prototype.xor = bnXor;
BigInteger.prototype.andNot = bnAndNot;
BigInteger.prototype.not = bnNot;
BigInteger.prototype.shiftLeft = bnShiftLeft;
BigInteger.prototype.shiftRight = bnShiftRight;
BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
BigInteger.prototype.bitCount = bnBitCount;
BigInteger.prototype.testBit = bnTestBit;
BigInteger.prototype.setBit = bnSetBit;
BigInteger.prototype.clearBit = bnClearBit;
BigInteger.prototype.flipBit = bnFlipBit;
BigInteger.prototype.add = bnAdd;
BigInteger.prototype.subtract = bnSubtract;
BigInteger.prototype.multiply = bnMultiply;
BigInteger.prototype.divide = bnDivide;
BigInteger.prototype.remainder = bnRemainder;
BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
BigInteger.prototype.modPow = bnModPow;
BigInteger.prototype.modInverse = bnModInverse;
BigInteger.prototype.pow = bnPow;
BigInteger.prototype.gcd = bnGCD;
BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
BigInteger.prototype.toMPI = bnToMPI;
// JSBN-specific extension
BigInteger.prototype.square = bnSquare;
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// A Digital signature algorithm implementation
function DSA() {
// s1 = ((g**s) mod p) mod q
// s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q)
function sign(hashalgo, m, g, p, q, x) {
// If the output size of the chosen hash is larger than the number of
// bits of q, the hash result is truncated to fit by taking the number
// of leftmost bits equal to the number of bits of q. This (possibly
// truncated) hash function result is treated as a number and used
// directly in the DSA signature algorithm.
var hashed_data = util.getLeftNBits(openpgp_crypto_hashData(hashalgo,m),q.bitLength());
var hash = new BigInteger(util.hexstrdump(hashed_data), 16);
var k = openpgp_crypto_getRandomBigIntegerInRange(BigInteger.ONE.add(BigInteger.ONE), q.subtract(BigInteger.ONE));
var s1 = (g.modPow(k,p)).mod(q);
var s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q);
var result = new Array();
result[0] = s1.toMPI();
result[1] = s2.toMPI();
return result;
}
function select_hash_algorithm(q) {
var usersetting = openpgp.config.config.prefer_hash_algorithm;
/*
* 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash
* 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash
* 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
* 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
*/
switch (Math.round(q.bitLength() / 8)) {
case 20: // 1024 bit
if (usersetting != 2 &&
usersetting > 11 &&
usersetting != 10 &&
usersetting < 8)
return 2; // prefer sha1
return usersetting;
case 28: // 2048 bit
if (usersetting > 11 &&
usersetting < 8)
return 11;
return usersetting;
case 32: // 4096 bit // prefer sha224
if (usersetting > 10 &&
usersetting < 8)
return 8; // prefer sha256
return usersetting;
default:
util.print_debug("DSA select hash algorithm: returning null for an unknown length of q");
return null;
}
}
this.select_hash_algorithm = select_hash_algorithm;
function verify(hashalgo, s1,s2,m,p,q,g,y) {
var hashed_data = util.getLeftNBits(openpgp_crypto_hashData(hashalgo,m),q.bitLength());
var hash = new BigInteger(util.hexstrdump(hashed_data), 16);
if (BigInteger.ZERO.compareTo(s1) > 0 ||
s1.compareTo(q) > 0 ||
BigInteger.ZERO.compareTo(s2) > 0 ||
s2.compareTo(q) > 0) {
util.print_error("invalid DSA Signature");
return null;
}
var w = s2.modInverse(q);
var u1 = hash.multiply(w).mod(q);
var u2 = s1.multiply(w).mod(q);
return g.modPow(u1,p).multiply(y.modPow(u2,p)).mod(p).mod(q);
}
/*
* unused code. This can be used as a start to write a key generator
* function.
function generateKey(bitcount) {
var qi = new BigInteger(bitcount, primeCenterie);
var pi = generateP(q, 512);
var gi = generateG(p, q, bitcount);
var xi;
do {
xi = new BigInteger(q.bitCount(), rand);
} while (x.compareTo(BigInteger.ZERO) != 1 && x.compareTo(q) != -1);
var yi = g.modPow(x, p);
return {x: xi, q: qi, p: pi, g: gi, y: yi};
}
function generateP(q, bitlength, randomfn) {
if (bitlength % 64 != 0) {
return false;
}
var pTemp;
var pTemp2;
do {
pTemp = randomfn(bitcount, true);
pTemp2 = pTemp.subtract(BigInteger.ONE);
pTemp = pTemp.subtract(pTemp2.remainder(q));
} while (!pTemp.isProbablePrime(primeCenterie) || pTemp.bitLength() != l);
return pTemp;
}
function generateG(p, q, bitlength, randomfn) {
var aux = p.subtract(BigInteger.ONE);
var pow = aux.divide(q);
var gTemp;
do {
gTemp = randomfn(bitlength);
} while (gTemp.compareTo(aux) != -1 && gTemp.compareTo(BigInteger.ONE) != 1);
return gTemp.modPow(pow, p);
}
function generateK(q, bitlength, randomfn) {
var tempK;
do {
tempK = randomfn(bitlength, false);
} while (tempK.compareTo(q) != -1 && tempK.compareTo(BigInteger.ZERO) != 1);
return tempK;
}
function generateR(q,p) {
k = generateK(q);
var r = g.modPow(k, p).mod(q);
return r;
}
function generateS(hashfn,k,r,m,q,x) {
var hash = hashfn(m);
s = (k.modInverse(q).multiply(hash.add(x.multiply(r)))).mod(q);
return s;
} */
this.sign = sign;
this.verify = verify;
// this.generate = generateKey;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// ElGamal implementation
function Elgamal() {
function encrypt(m,g,p,y) {
// choose k in {2,...,p-2}
var two = BigInteger.ONE.add(BigInteger.ONE);
var pMinus2 = p.subtract(two);
var k = openpgp_crypto_getRandomBigIntegerInRange(two, pMinus2);
var k = k.mod(pMinus2).add(BigInteger.ONE);
var c = new Array();
c[0] = g.modPow(k, p);
c[1] = y.modPow(k, p).multiply(m).mod(p).toMPI();
c[0] = c[0].toMPI();
return c;
}
function decrypt(c1,c2,p,x) {
util.print_debug("Elgamal Decrypt:\nc1:"+util.hexstrdump(c1.toMPI())+"\n"+
"c2:"+util.hexstrdump(c2.toMPI())+"\n"+
"p:"+util.hexstrdump(p.toMPI())+"\n"+
"x:"+util.hexstrdump(x.toMPI()));
return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p);
//var c = c1.pow(x).modInverse(p); // c0^-a mod p
//return c.multiply(c2).mod(p);
}
// signing and signature verification using Elgamal is not required by OpenPGP.
this.encrypt = encrypt;
this.decrypt = decrypt;
}/*
* Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU)
* All Rights Reserved.
*
* Modified by Recurity Labs GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
// Basic JavaScript BN library - subset useful for RSA encryption.
// Bits per digit
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.
// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}
// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh));
}
else
this[this.t-1] |= x<= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1< 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}
// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }
// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}
// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}
// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}
// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1< 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);
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// RSA implementation
function SecureRandom(){
function nextBytes(byteArray){
for(var n = 0; n < byteArray.length;n++){
byteArray[n] = openpgp_crypto_getSecureRandomOctet();
}
}
this.nextBytes = nextBytes;
}
function RSA() {
/**
* This function uses jsbn Big Num library to decrypt RSA
* @param m
* message
* @param d
* RSA d as BigInteger
* @param p
* RSA p as BigInteger
* @param q
* RSA q as BigInteger
* @param u
* RSA u as BigInteger
* @return {BigInteger} The decrypted value of the message
*/
function decrypt(m, d, p, q, u) {
var xp = m.mod(p).modPow(d.mod(p.subtract(BigInteger.ONE)), p);
var xq = m.mod(q).modPow(d.mod(q.subtract(BigInteger.ONE)), q);
util.print_debug("rsa.js decrypt\nxpn:"+util.hexstrdump(xp.toMPI())+"\nxqn:"+util.hexstrdump(xq.toMPI()));
var t = xq.subtract(xp);
if (t[0] == 0) {
t = xp.subtract(xq);
t = t.multiply(u).mod(q);
t = q.subtract(t);
} else {
t = t.multiply(u).mod(q);
}
return t.multiply(p).add(xp);
}
/**
* encrypt message
* @param m message as BigInteger
* @param e public MPI part as BigInteger
* @param n public MPI part as BigInteger
* @return BigInteger
*/
function encrypt(m,e,n) {
return m.modPowInt(e, n);
}
/* Sign and Verify */
function sign(m,d,n) {
return m.modPow(d, n);
}
function verify(x,e,n) {
return x.modPowInt(e, n);
}
// "empty" RSA key constructor
function keyObject() {
this.n = null;
this.e = 0;
this.ee = null;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.u = null;
}
// Generate a new random private key B bits long, using public expt E
function generate(B,E) {
var key = new keyObject();
var rng = new SecureRandom();
var qs = B>>1;
key.e = parseInt(E,16);
key.ee = new BigInteger(E,16);
for(;;) {
for(;;) {
key.p = new BigInteger(B-qs,1,rng);
if(key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) == 0 && key.p.isProbablePrime(10)) break;
}
for(;;) {
key.q = new BigInteger(qs,1,rng);
if(key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) == 0 && key.q.isProbablePrime(10)) break;
}
if(key.p.compareTo(key.q) <= 0) {
var t = key.p;
key.p = key.q;
key.q = t;
}
var p1 = key.p.subtract(BigInteger.ONE);
var q1 = key.q.subtract(BigInteger.ONE);
var phi = p1.multiply(q1);
if(phi.gcd(key.ee).compareTo(BigInteger.ONE) == 0) {
key.n = key.p.multiply(key.q);
key.d = key.ee.modInverse(phi);
key.dmp1 = key.d.mod(p1);
key.dmq1 = key.d.mod(q1);
key.u = key.p.modInverse(key.q);
break;
}
}
return key;
}
this.encrypt = encrypt;
this.decrypt = decrypt;
this.verify = verify;
this.sign = sign;
this.generate = generate;
this.keyObject = keyObject;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Compressed Data Packet (Tag 8)
*
* RFC4880 5.6:
* The Compressed Data packet contains compressed data. Typically, this
* packet is found as the contents of an encrypted packet, or following
* a Signature or One-Pass Signature packet, and contains a literal data
* packet.
*/
function openpgp_packet_compressed() {
this.tagType = 8;
/**
* parsing function for the packet.
* @param {string} input payload of a tag 8 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of input at position
* @return {openpgp_packet_compressed} object representation
*/
function read_packet (input, position, len) {
this.packetLength = len;
var mypos = position;
// One octet that gives the algorithm used to compress the packet.
this.type = input.charCodeAt(mypos++);
// Compressed data, which makes up the remainder of the packet.
this.compressedData = input.substring(position+1, position+len);
return this;
}
/**
* decompression method for decompressing the compressed data
* read by read_packet
* @return {String} the decompressed data
*/
function decompress() {
if (this.decompressedData != null)
return this.decompressedData;
if (this.type == null)
return null;
switch (this.type) {
case 0: // - Uncompressed
this.decompressedData = this.compressedData;
break;
case 1: // - ZIP [RFC1951]
var compData = this.compressedData;
var radix = s2r(compData).replace(/\n/g,"");
// no header in this case, directly call deflate
var jxg_obj = new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(radix));
var outputString = unescape(jxg_obj.deflate()[0][0]);
var packet = openpgp_packet.read_packet(outputString, 0, outputString.length);
util.print_info('Decompressed packet [Type 1-ZIP]: ' + packet);
this.decompressedData = packet.data;
break;
case 2: // - ZLIB [RFC1950]
var compressionMethod = this.compressedData.charCodeAt(0) % 0x10; //RFC 1950. Bits 0-3 Compression Method
//Bits 4-7 RFC 1950 are LZ77 Window. Generally this value is 7 == 32k window size.
//2nd Byte in RFC 1950 is for "FLAGs" Allows for a Dictionary (how is this defined). Basic checksum, and compression level.
if (compressionMethod == 8) { //CM 8 is for DEFLATE, RFC 1951
// remove 4 bytes ADLER32 checksum from the end
var compData = this.compressedData.substring(0, this.compressedData.length - 4);
var radix = s2r(compData).replace(/\n/g,"");
var outputString = JXG.decompress(radix);
//TODO check ADLER32 checksum
var dearmored = {type: 3, text: outputString, openpgp: outputString};
var messages = openpgp.read_messages_dearmored(dearmored);
for(var m in messages){
if(messages[m].data){
this.decompressedData = messages[m].data;
}
}
} else {
util.print_error("Compression algorithm ZLIB only supports DEFLATE compression method.");
}
break;
case 3: // - BZip2 [BZ2]
// TODO: need to implement this
util.print_error("Compression algorithm BZip2 [BZ2] is not implemented.");
break;
default:
util.print_error("Compression algorithm unknown :"+this.type);
break;
}
util.print_debug("decompressed:"+util.hexstrdump(this.decompressedData));
return this.decompressedData;
}
/**
* Compress the packet data (member decompressedData)
* @param {integer} type algorithm to be used // See RFC 4880 9.3
* @param {String} data data to be compressed
* @return {String} The compressed data stored in attribute compressedData
*/
function compress(type, data) {
this.type = type;
this.decompressedData = data;
switch (this.type) {
case 0: // - Uncompressed
this.compressedData = this.decompressedData;
break;
case 1: // - ZIP [RFC1951]
util.print_error("Compression algorithm ZIP [RFC1951] is not implemented.");
break;
case 2: // - ZLIB [RFC1950]
// TODO: need to implement this
util.print_error("Compression algorithm ZLIB [RFC1950] is not implemented.");
break;
case 3: // - BZip2 [BZ2]
// TODO: need to implement this
util.print_error("Compression algorithm BZip2 [BZ2] is not implemented.");
break;
default:
util.print_error("Compression algorithm unknown :"+this.type);
break;
}
this.packetLength = this.compressedData.length +1;
return this.compressedData;
}
/**
* creates a string representation of the packet
* @param {integer} algorithm algorithm to be used // See RFC 4880 9.3
* @param {String} data data to be compressed
* @return {String} string-representation of the packet
*/
function write_packet(algorithm, data) {
this.decompressedData = data;
if (algorithm == null) {
this.type = 1;
}
var result = String.fromCharCode(this.type)+this.compress(this.type);
return openpgp_packet.write_packet_header(8, result.length)+result;
}
/**
* pretty printing the packet (useful for debug purposes)
* @return {String}
*/
function toString() {
return '5.6. Compressed Data Packet (Tag 8)\n'+
' length: '+this.packetLength+'\n'+
' Compression Algorithm = '+this.type+'\n'+
' Compressed Data: Byte ['+util.hexstrdump(this.compressedData)+']\n';
}
this.read_packet = read_packet;
this.toString = toString;
this.compress = compress;
this.decompress = decompress;
this.write_packet = write_packet;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the User Attribute Packet (Tag 17)
* The User Attribute packet is a variation of the User ID packet. It
* is capable of storing more types of data than the User ID packet,
* which is limited to text. Like the User ID packet, a User Attribute
* packet may be certified by the key owner ("self-signed") or any other
* key owner who cares to certify it. Except as noted, a User Attribute
* packet may be used anywhere that a User ID packet may be used.
*
* While User Attribute packets are not a required part of the OpenPGP
* standard, implementations SHOULD provide at least enough
* compatibility to properly handle a certification signature on the
* User Attribute packet. A simple way to do this is by treating the
* User Attribute packet as a User ID packet with opaque contents, but
* an implementation may use any method desired.
*/
function openpgp_packet_userattribute() {
this.tagType = 17;
this.certificationSignatures = new Array();
this.certificationRevocationSignatures = new Array();
this.revocationSignatures = new Array();
this.parentNode = null;
/**
* parsing function for a user attribute packet (tag 17).
* @param {string} input payload of a tag 17 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet (input, position, len) {
var total_len = 0;
this.packetLength = len;
this.userattributes = new Array();
var count = 0;
var mypos = position;
while (len != total_len) {
var current_len = 0;
// 4.2.2.1. One-Octet Lengths
if (input[mypos].charCodeAt() < 192) {
packet_length = input[mypos++].charCodeAt();
current_len = 1;
// 4.2.2.2. Two-Octet Lengths
} else if (input[mypos].charCodeAt() >= 192 && input[mypos].charCodeAt() < 224) {
packet_length = ((input[mypos++].charCodeAt() - 192) << 8)
+ (input[mypos++].charCodeAt()) + 192;
current_len = 2;
// 4.2.2.4. Partial Body Lengths
} else if (input[mypos].charCodeAt() > 223 && input[mypos].charCodeAt() < 255) {
packet_length = 1 << (input[mypos++].charCodeAt() & 0x1F);
current_len = 1;
// 4.2.2.3. Five-Octet Lengths
} else {
current_len = 5;
mypos++;
packet_length = (input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16)
| (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt();
}
var subpackettype = input[mypos++].charCodeAt();
packet_length--;
current_len++;
this.userattributes[count] = new Array();
this.userattributes[count] = input.substring(mypos, mypos + packet_length);
mypos += packet_length;
total_len += current_len+packet_length;
}
this.packetLength = mypos - position;
return this;
}
/**
* generates debug output (pretty print)
* @return {string} String which gives some information about the user attribute packet
*/
function toString() {
var result = '5.12. User Attribute Packet (Tag 17)\n'+
' AttributePackets: (count = '+this.userattributes.length+')\n';
for (var i = 0; i < this.userattributes.length; i++) {
result += ' ('+this.userattributes[i].length+') bytes: ['+util.hexidump(this.userattributes[i])+']\n';
}
return result;
}
/**
* Continue parsing packets belonging to the user attribute packet such as signatures
* @param {openpgp_*} parent_node the parent object
* @param {String} input input string to read the packet(s) from
* @param {integer} position start position for the parser
* @param {integer} len length of the packet(s) or remaining length of input
* @return {integer} length of nodes read
*/
function read_nodes(parent_node, input, position, len) {
this.parentNode = parent_node;
var exit = false;
var pos = position;
var l = len;
while (input.length != pos) {
var result = openpgp_packet.read_packet(input, pos, l);
if (result == null) {
util.print_error("openpgp.packet.userattribute.js\n"+'[user_attr] parsing ends here @:' + pos + " l:" + l);
break;
} else {
switch (result.tagType) {
case 2: // Signature Packet
if (result.signatureType > 15
&& result.signatureType < 20) // certification
// //
// signature
this.certificationSignatures[this.certificationSignatures.length] = result;
else if (result.signatureType == 32) // certification revocation signature
this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result;
pos += result.packetLength + result.headerLength;
l = len - (pos - position);
break;
default:
this.data = input;
this.position = position - parent_node.packetLength;
this.len = pos - position;
return this.len;
break;
}
}
}
this.data = input;
this.position = position - parent_node.packetLength;
this.len = pos - position;
return this.len;
}
this.read_packet = read_packet;
this.read_nodes = read_nodes;
this.toString = toString;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Symmetrically Encrypted Data Packet (Tag 9)
*
* RFC4880 5.7: The Symmetrically Encrypted Data packet contains data encrypted
* with a symmetric-key algorithm. When it has been decrypted, it contains other
* packets (usually a literal data packet or compressed data packet, but in
* theory other Symmetrically Encrypted Data packets or sequences of packets
* that form whole OpenPGP messages).
*/
function openpgp_packet_encrypteddata() {
this.tagType = 9;
this.packetLength = null;
this.encryptedData = null;
this.decryptedData = null;
/**
* parsing function for the packet.
*
* @param {string} input payload of a tag 9 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
var mypos = position;
this.packetLength = len;
// - Encrypted data, the output of the selected symmetric-key cipher
// operating in OpenPGP's variant of Cipher Feedback (CFB) mode.
this.encryptedData = input.substring(position, position + len);
return this;
}
/**
* symmetrically decrypt the packet data
*
* @param {integer} symmetric_algorithm_type
* symmetric key algorithm to use // See RFC4880 9.2
* @param {String} key
* key as string with the corresponding length to the
* algorithm
* @return the decrypted data;
*/
function decrypt_sym(symmetric_algorithm_type, key) {
this.decryptedData = openpgp_crypto_symmetricDecrypt(
symmetric_algorithm_type, key, this.encryptedData, true);
util.print_debug("openpgp.packet.encryptedintegrityprotecteddata.js\n"+
"data: "+util.hexstrdump(this.decryptedData));
return this.decryptedData;
}
/**
* Creates a string representation of the packet
*
* @param {Integer} algo symmetric key algorithm to use // See RFC4880 9.2
* @param {String} key key as string with the corresponding length to the
* algorithm
* @param {String} data data to be
* @return {String} string-representation of the packet
*/
function write_packet(algo, key, data) {
var result = "";
result += openpgp_crypto_symmetricEncrypt(
openpgp_crypto_getPrefixRandom(algo), algo, key, data, true);
result = openpgp_packet.write_packet_header(9, result.length) + result;
return result;
}
function toString() {
return '5.7. Symmetrically Encrypted Data Packet (Tag 9)\n'
+ ' length: ' + this.packetLength + '\n'
+ ' Used symmetric algorithm: ' + this.algorithmType + '\n'
+ ' encrypted data: Bytes ['
+ util.hexstrdump(this.encryptedData) + ']\n';
}
this.decrypt_sym = decrypt_sym;
this.toString = toString;
this.read_packet = read_packet;
this.write_packet = write_packet;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Signature Packet (Tag 2)
*
* RFC4480 5.2:
* A Signature packet describes a binding between some public key and
* some data. The most common signatures are a signature of a file or a
* block of text, and a signature that is a certification of a User ID.
*/
function openpgp_packet_signature() {
this.tagType = 2;
this.signatureType = null;
this.creationTime = null;
this.keyId = null;
this.signatureData = null;
this.signatureExpirationTime = null;
this.signatureNeverExpires = null;
this.signedHashValue = null;
this.MPIs = null;
this.publicKeyAlgorithm = null;
this.hashAlgorithm = null;
this.exportable = null;
this.trustLevel = null;
this.trustAmount = null;
this.regular_expression = null;
this.revocable = null;
this.keyExpirationTime = null;
this.keyNeverExpires = null;
this.preferredSymmetricAlgorithms = null;
this.revocationKeyClass = null;
this.revocationKeyAlgorithm = null;
this.revocationKeyFingerprint = null;
this.issuerKeyId = null;
this.notationFlags = null;
this.notationName = null;
this.notationValue = null;
this.preferredHashAlgorithms = null;
this.preferredCompressionAlgorithms = null;
this.keyServerPreferences = null;
this.preferredKeyServer = null;
this.isPrimaryUserID = null;
this.policyURI = null;
this.keyFlags = null;
this.signersUserId = null;
this.reasonForRevocationFlag = null;
this.reasonForRevocationString = null;
this.signatureTargetPublicKeyAlgorithm = null;
this.signatureTargetHashAlgorithm = null;
this.signatureTargetHash = null;
this.embeddedSignature = null;
/**
* parsing function for a signature packet (tag 2).
* @param {string} input payload of a tag 2 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
this.data = input.substring (position, position+len);
if (len < 0) {
util.print_debug("openpgp.packet.signature.js\n"+"openpgp_packet_signature read_packet length < 0 @:"+position);
return null;
}
var mypos = position;
this.packetLength = len;
// alert('starting parsing signature: '+position+' '+this.packetLength);
this.version = input[mypos++].charCodeAt();
// switch on version (3 and 4)
switch (this.version) {
case 3:
// One-octet length of following hashed material. MUST be 5.
if (input[mypos++].charCodeAt() != 5)
util.print_debug("openpgp.packet.signature.js\n"+'invalid One-octet length of following hashed material. MUST be 5. @:'+(mypos-1));
var sigpos = mypos;
// One-octet signature type.
this.signatureType = input[mypos++].charCodeAt();
// Four-octet creation time.
this.creationTime = new Date(((input[mypos++].charCodeAt()) << 24 |
(input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) |
input[mypos++].charCodeAt())* 1000);
// storing data appended to data which gets verified
this.signatureData = input.substring(position, mypos);
// Eight-octet Key ID of signer.
this.keyId = input.substring(mypos, mypos +8);
mypos += 8;
// One-octet public-key algorithm.
this.publicKeyAlgorithm = input[mypos++].charCodeAt();
// One-octet hash algorithm.
this.hashAlgorithm = input[mypos++].charCodeAt();
// Two-octet field holding left 16 bits of signed hash value.
this.signedHashValue = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt();
var mpicount = 0;
// Algorithm-Specific Fields for RSA signatures:
// - multiprecision integer (MPI) of RSA signature value m**d mod n.
if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4)
mpicount = 1;
// Algorithm-Specific Fields for DSA signatures:
// - MPI of DSA value r.
// - MPI of DSA value s.
else if (this.publicKeyAlgorithm == 17)
mpicount = 2;
this.MPIs = new Array();
for (var i = 0; i < mpicount; i++) {
this.MPIs[i] = new openpgp_type_mpi();
if (this.MPIs[i].read(input, mypos, (mypos-position)) != null &&
!this.packetLength < (mypos-position)) {
mypos += this.MPIs[i].packetLength;
} else {
util.print_error('signature contains invalid MPI @:'+mypos);
}
}
break;
case 4:
this.signatureType = input[mypos++].charCodeAt();
this.publicKeyAlgorithm = input[mypos++].charCodeAt();
this.hashAlgorithm = input[mypos++].charCodeAt();
// Two-octet scalar octet count for following hashed subpacket
// data.
var hashed_subpacket_count = (input[mypos++].charCodeAt() << 8) + input[mypos++].charCodeAt();
// Hashed subpacket data set (zero or more subpackets)
var subpacket_length = 0;
while (hashed_subpacket_count != subpacket_length) {
if (hashed_subpacket_count < subpacket_length) {
util.print_debug("openpgp.packet.signature.js\n"+"hashed missed something: "+mypos+" c:"+hashed_subpacket_count+" l:"+subpacket_length);
}
subpacket_length += this._raw_read_signature_sub_packet(input,
mypos + subpacket_length, hashed_subpacket_count
- subpacket_length);
}
mypos += hashed_subpacket_count;
this.signatureData = input.substring(position, mypos);
// alert("signatureData: "+util.hexstrdump(this.signatureData));
// Two-octet scalar octet count for the following unhashed subpacket
var subpacket_count = (input[mypos++].charCodeAt() << 8) + input[mypos++].charCodeAt();
// Unhashed subpacket data set (zero or more subpackets).
subpacket_length = 0;
while (subpacket_count != subpacket_length) {
if (subpacket_count < subpacket_length) {
util.print_debug("openpgp.packet.signature.js\n"+"missed something: "+subpacket_length+" c:"+subpacket_count+" "+" l:"+subpacket_length);
}
subpacket_length += this._raw_read_signature_sub_packet(input,
mypos + subpacket_length, subpacket_count
- subpacket_length);
}
mypos += subpacket_count;
// Two-octet field holding the left 16 bits of the signed hash
// value.
this.signedHashValue = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt();
// One or more multiprecision integers comprising the signature.
// This portion is algorithm specific, as described above.
var mpicount = 0;
if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4)
mpicount = 1;
else if (this.publicKeyAlgorithm == 17)
mpicount = 2;
this.MPIs = new Array();
for (var i = 0; i < mpicount; i++) {
this.MPIs[i] = new openpgp_type_mpi();
if (this.MPIs[i].read(input, mypos, (mypos-position)) != null &&
!this.packetLength < (mypos-position)) {
mypos += this.MPIs[i].packetLength;
} else {
util.print_error('signature contains invalid MPI @:'+mypos);
}
}
break;
default:
util.print_error("openpgp.packet.signature.js\n"+'unknown signature packet version'+this.version);
break;
}
// util.print_message("openpgp.packet.signature.js\n"+"end signature: l: "+this.packetLength+"m: "+mypos+" m-p: "+(mypos-position));
return this;
}
/**
* creates a string representation of a message signature packet (tag 2).
* This can be only used on text data
* @param {integer} signature_type should be 1 (one)
* @param {String} data data to be signed
* @param {openpgp_msg_privatekey} privatekey private key used to sign the message. (secMPIs MUST be unlocked)
* @return {string} string representation of a signature packet
*/
function write_message_signature(signature_type, data, privatekey) {
var publickey = privatekey.privateKeyPacket.publicKey;
var hash_algo = privatekey.getPreferredSignatureHashAlgorithm();
var result = String.fromCharCode(4);
result += String.fromCharCode(signature_type);
result += String.fromCharCode(publickey.publicKeyAlgorithm);
result += String.fromCharCode(hash_algo);
var d = Math.round(new Date().getTime() / 1000);
var datesubpacket = write_sub_signature_packet(2,""+
String.fromCharCode((d >> 24) & 0xFF) +
String.fromCharCode((d >> 16) & 0xFF) +
String.fromCharCode((d >> 8) & 0xFF) +
String.fromCharCode(d & 0xFF));
var issuersubpacket = write_sub_signature_packet(16, privatekey.getKeyId());
result += String.fromCharCode(((datesubpacket.length + issuersubpacket.length) >> 8) & 0xFF);
result += String.fromCharCode ((datesubpacket.length + issuersubpacket.length) & 0xFF);
result += datesubpacket;
result += issuersubpacket;
var trailer = '';
trailer += String.fromCharCode(4);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode((result.length) >> 24);
trailer += String.fromCharCode(((result.length) >> 16) & 0xFF);
trailer += String.fromCharCode(((result.length) >> 8) & 0xFF);
trailer += String.fromCharCode((result.length) & 0xFF);
var result2 = String.fromCharCode(0);
result2 += String.fromCharCode(0);
var hash = openpgp_crypto_hashData(hash_algo, data+result+trailer);
util.print_debug("DSA Signature is calculated with:|"+data+result+trailer+"|\n"+util.hexstrdump(data+result+trailer)+"\n hash:"+util.hexstrdump(hash));
result2 += hash.charAt(0);
result2 += hash.charAt(1);
result2 += openpgp_crypto_signData(hash_algo,privatekey.privateKeyPacket.publicKey.publicKeyAlgorithm,
publickey.MPIs,
privatekey.privateKeyPacket.secMPIs,
data+result+trailer);
return {openpgp: (openpgp_packet.write_packet_header(2, (result+result2).length)+result + result2),
hash: util.get_hashAlgorithmString(hash_algo)};
}
/**
* creates a string representation of a sub signature packet (See RFC 4880 5.2.3.1)
* @param {integer} type subpacket signature type. Signature types as described in RFC4880 Section 5.2.3.2
* @param {String} data data to be included
* @return {String} a string-representation of a sub signature packet (See RFC 4880 5.2.3.1)
*/
function write_sub_signature_packet(type, data) {
var result = "";
result += openpgp_packet.encode_length(data.length+1);
result += String.fromCharCode(type);
result += data;
return result;
}
// V4 signature sub packets
this._raw_read_signature_sub_packet = function(input, position, len) {
if (len < 0)
util.print_debug("openpgp.packet.signature.js\n"+"_raw_read_signature_sub_packet length < 0 @:"+position);
var mypos = position;
var subplen = 0;
// alert('starting signature subpackage read at position:'+position+' length:'+len);
if (input[mypos].charCodeAt() < 192) {
subplen = input[mypos++].charCodeAt();
} else if (input[mypos].charCodeAt() >= 192 && input[mypos].charCodeAt() < 224) {
subplen = ((input[mypos++].charCodeAt() - 192) << 8) + (input[mypos++].charCodeAt()) + 192;
} else if (input[mypos].charCodeAt() > 223 && input[mypos].charCodeAt() < 255) {
subplen = 1 << (input[mypos++].charCodeAt() & 0x1F);
} else if (input[mypos].charCodeAt() < 255) {
mypos++;
subplen = (input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16)
| (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt();
}
var type = input[mypos++].charCodeAt() & 0x7F;
// alert('signature subpacket type '+type+" with length: "+subplen);
// subpacket type
switch (type) {
case 2: // Signature Creation Time
this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16)
| (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt())*1000);
break;
case 3: // Signature Expiration Time
this.signatureExpirationTime = (input[mypos++].charCodeAt() << 24)
| (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8)
| input[mypos++].charCodeAt();
this.signatureNeverExpires = (this.signature_expiration_time == 0);
break;
case 4: // Exportable Certification
this.exportable = input[mypos++].charCodeAt() == 1;
break;
case 5: // Trust Signature
this.trustLevel = input[mypos++].charCodeAt();
this.trustAmount = input[mypos++].charCodeAt();
break;
case 6: // Regular Expression
this.regular_expression = new String();
for (var i = 0; i < subplen - 1; i++)
this.regular_expression += (input[mypos++]);
break;
case 7: // Revocable
this.revocable = input[mypos++].charCodeAt() == 1;
break;
case 9: // Key Expiration Time
this.keyExpirationTime = (input[mypos++].charCodeAt() << 24)
| (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8)
| input[mypos++].charCodeAt();
this.keyNeverExpires = (this.keyExpirationTime == 0);
break;
case 11: // Preferred Symmetric Algorithms
this.preferredSymmetricAlgorithms = new Array();
for (var i = 0; i < subplen-1; i++) {
this.preferredSymmetricAlgorithms = input[mypos++].charCodeAt();
}
break;
case 12: // Revocation Key
// (1 octet of class, 1 octet of public-key algorithm ID, 20
// octets of
// fingerprint)
this.revocationKeyClass = input[mypos++].charCodeAt();
this.revocationKeyAlgorithm = input[mypos++].charCodeAt();
this.revocationKeyFingerprint = new Array();
for ( var i = 0; i < 20; i++) {
this.revocationKeyFingerprint = input[mypos++].charCodeAt();
}
break;
case 16: // Issuer
this.issuerKeyId = input.substring(mypos,mypos+8);
mypos += 8;
break;
case 20: // Notation Data
this.notationFlags = (input[mypos++].charCodeAt() << 24) |
(input[mypos++].charCodeAt() << 16) |
(input[mypos++].charCodeAt() << 8) |
(input[mypos++].charCodeAt());
var nameLength = (input[mypos++].charCodeAt() << 8) | (input[mypos++].charCodeAt());
var valueLength = (input[mypos++].charCodeAt() << 8) | (input[mypos++].charCodeAt());
this.notationName = "";
for (var i = 0; i < nameLength; i++) {
this.notationName += input[mypos++];
}
this.notationValue = "";
for (var i = 0; i < valueLength; i++) {
this.notationValue += input[mypos++];
}
break;
case 21: // Preferred Hash Algorithms
this.preferredHashAlgorithms = new Array();
for (var i = 0; i < subplen-1; i++) {
this.preferredHashAlgorithms = input[mypos++].charCodeAt();
}
break;
case 22: // Preferred Compression Algorithms
this.preferredCompressionAlgorithms = new Array();
for ( var i = 0; i < subplen-1; i++) {
this.preferredCompressionAlgorithms = input[mypos++].charCodeAt();
}
break;
case 23: // Key Server Preferences
this.keyServerPreferences = new Array();
for ( var i = 0; i < subplen-1; i++) {
this.keyServerPreferences = input[mypos++].charCodeAt();
}
break;
case 24: // Preferred Key Server
this.preferredKeyServer = new String();
for ( var i = 0; i < subplen-1; i++) {
this.preferredKeyServer += input[mypos++];
}
break;
case 25: // Primary User ID
this.isPrimaryUserID = input[mypos++] != 0;
break;
case 26: // Policy URI
this.policyURI = new String();
for ( var i = 0; i < subplen-1; i++) {
this.policyURI += input[mypos++];
}
break;
case 27: // Key Flags
this.keyFlags = new Array();
for ( var i = 0; i < subplen-1; i++) {
this.keyFlags = input[mypos++].charCodeAt();
}
break;
case 28: // Signer's User ID
this.signersUserId = new String();
for ( var i = 0; i < subplen-1; i++) {
this.signersUserId += input[mypos++];
}
break;
case 29: // Reason for Revocation
this.reasonForRevocationFlag = input[mypos++].charCodeAt();
this.reasonForRevocationString = new String();
for ( var i = 0; i < subplen -2; i++) {
this.reasonForRevocationString += input[mypos++];
}
break;
case 30: // Features
// TODO: to be implemented
return subplen+1;
case 31: // Signature Target
// (1 octet public-key algorithm, 1 octet hash algorithm, N octets hash)
this.signatureTargetPublicKeyAlgorithm = input[mypos++].charCodeAt();
this.signatureTargetHashAlgorithm = input[mypos++].charCodeAt();
var signatureTargetHashAlgorithmLength = 0;
switch(this.signatureTargetHashAlgorithm) {
case 1: // - MD5 [HAC] "MD5"
case 2: // - SHA-1 [FIPS180] "SHA1"
signatureTargetHashAlgorithmLength = 20;
break;
case 3: // - RIPE-MD/160 [HAC] "RIPEMD160"
case 8: // - SHA256 [FIPS180] "SHA256"
case 9: // - SHA384 [FIPS180] "SHA384"
case 10: // - SHA512 [FIPS180] "SHA512"
case 11: // - SHA224 [FIPS180] "SHA224"
break;
// 100 to 110 - Private/Experimental algorithm
default:
util.print_error("openpgp.packet.signature.js\n"+"unknown signature target hash algorithm:"+this.signatureTargetHashAlgorithm);
return null;
}
this.signatureTargetHash = new Array();
for (var i = 0; i < signatureTargetHashAlgorithmLength; i++) {
this.signatureTargetHash[i] = input[mypos++];
}
case 32: // Embedded Signature
this.embeddedSignature = new openpgp_packet_signature();
this.embeddedSignature.read_packet(input, mypos, len -(mypos-position));
return ((mypos+ this.embeddedSignature.packetLength) - position);
break;
case 100: // Private or experimental
case 101: // Private or experimental
case 102: // Private or experimental
case 103: // Private or experimental
case 104: // Private or experimental
case 105: // Private or experimental
case 106: // Private or experimental
case 107: // Private or experimental
case 108: // Private or experimental
case 109: // Private or experimental
case 110: // Private or experimental
util.print_error("openpgp.packet.signature.js\n"+'private or experimental signature subpacket type '+type+" @:"+mypos+" subplen:"+subplen+" len:"+len);
return subplen+1;
break;
case 0: // Reserved
case 1: // Reserved
case 8: // Reserved
case 10: // Placeholder for backward compatibility
case 13: // Reserved
case 14: // Reserved
case 15: // Reserved
case 17: // Reserved
case 18: // Reserved
case 19: // Reserved
default:
util.print_error("openpgp.packet.signature.js\n"+'unknown signature subpacket type '+type+" @:"+mypos+" subplen:"+subplen+" len:"+len);
return subplen+1;
break;
}
return mypos -position;
};
/**
* verifys the signature packet. Note: not signature types are implemented
* @param {String} data data which on the signature applies
* @param {openpgp_msg_privatekey} key the public key to verify the signature
* @return {boolean} True if message is verified, else false.
*/
function verify(data, key) {
switch(this.signatureType) {
// calculating the trailer
case 0: // 0x00: Signature of a binary document.
if (this.version == 4) {
var trailer = '';
trailer += String.fromCharCode(this.version);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode(this.signatureData.length >> 24);
trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF);
trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF);
trailer += String.fromCharCode(this.signatureData.length & 0xFF);
return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, data.substring(i)+this.signatureData+trailer);
} else if (this.version == 3) {
return false;
}
case 1: // 0x01: Signature of a canonical text document.
if (this.version == 4) {
var trailer = '';
trailer += String.fromCharCode(this.version);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode(this.signatureData.length >> 24);
trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF);
trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF);
trailer += String.fromCharCode(this.signatureData.length &0xFF);
return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer);
} else if (this.version == 3) {
return false;
}
case 2: // 0x02: Standalone signature.
// This signature is a signature of only its own subpacket contents.
// It is calculated identically to a signature over a zero-length
// binary document. Note that it doesn't make sense to have a V3
// standalone signature.
if (this.version == 3)
return false;
var trailer = '';
trailer += String.fromCharCode(this.version);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode(this.signatureData.length >> 24);
trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF);
trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF);
trailer += String.fromCharCode(this.signatureData.length &0xFF);
return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, this.signatureData+trailer);
case 16:
// 0x10: Generic certification of a User ID and Public-Key packet.
// The issuer of this certification does not make any particular
// assertion as to how well the certifier has checked that the owner
// of the key is in fact the person described by the User ID.
case 17:
// 0x11: Persona certification of a User ID and Public-Key packet.
// The issuer of this certification has not done any verification of
// the claim that the owner of this key is the User ID specified.
case 18:
// 0x12: Casual certification of a User ID and Public-Key packet.
// The issuer of this certification has done some casual
// verification of the claim of identity.
case 19:
// 0x13: Positive certification of a User ID and Public-Key packet.
// The issuer of this certification has done substantial
// verification of the claim of identity.
//
// Most OpenPGP implementations make their "key signatures" as 0x10
// certifications. Some implementations can issue 0x11-0x13
// certifications, but few differentiate between the types.
case 48:
// 0x30: Certification revocation signature
// This signature revokes an earlier User ID certification signature
// (signature class 0x10 through 0x13) or direct-key signature
// (0x1F). It should be issued by the same key that issued the
// revoked signature or an authorized revocation key. The signature
// is computed over the same data as the certificate that it
// revokes, and should have a later creation date than that
// certificate.
var trailer = '';
trailer += String.fromCharCode(this.version);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode(this.signatureData.length >> 24);
trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF);
trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF);
trailer += String.fromCharCode(this.signatureData.length &0xFF);
return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.MPIs, data+this.signatureData+trailer);
case 24:
// 0x18: Subkey Binding Signature
// This signature is a statement by the top-level signing key that
// indicates that it owns the subkey. This signature is calculated
// directly on the primary key and subkey, and not on any User ID or
// other packets. A signature that binds a signing subkey MUST have
// an Embedded Signature subpacket in this binding signature that
// contains a 0x19 signature made by the signing subkey on the
// primary key and subkey.
if (this.version == 3)
return false;
var trailer = '';
trailer += String.fromCharCode(this.version);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode(this.signatureData.length >> 24);
trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF);
trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF);
trailer += String.fromCharCode(this.signatureData.length &0xFF);
return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.MPIs, data+this.signatureData+trailer);
case 25:
// 0x19: Primary Key Binding Signature
// This signature is a statement by a signing subkey, indicating
// that it is owned by the primary key and subkey. This signature
// is calculated the same way as a 0x18 signature: directly on the
// primary key and subkey, and not on any User ID or other packets.
// When a signature is made over a key, the hash data starts with the
// octet 0x99, followed by a two-octet length of the key, and then body
// of the key packet. (Note that this is an old-style packet header for
// a key packet with two-octet length.) A subkey binding signature
// (type 0x18) or primary key binding signature (type 0x19) then hashes
// the subkey using the same format as the main key (also using 0x99 as
// the first octet).
case 31:
// 0x1F: Signature directly on a key
// This signature is calculated directly on a key. It binds the
// information in the Signature subpackets to the key, and is
// appropriate to be used for subpackets that provide information
// about the key, such as the Revocation Key subpacket. It is also
// appropriate for statements that non-self certifiers want to make
// about the key itself, rather than the binding between a key and a
// name.
case 32:
// 0x20: Key revocation signature
// The signature is calculated directly on the key being revoked. A
// revoked key is not to be used. Only revocation signatures by the
// key being revoked, or by an authorized revocation key, should be
// considered valid revocation signatures.
case 40:
// 0x28: Subkey revocation signature
// The signature is calculated directly on the subkey being revoked.
// A revoked subkey is not to be used. Only revocation signatures
// by the top-level signature key that is bound to this subkey, or
// by an authorized revocation key, should be considered valid
// revocation signatures.
var trailer = '';
trailer += String.fromCharCode(this.version);
trailer += String.fromCharCode(0xFF);
trailer += String.fromCharCode(this.signatureData.length >> 24);
trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF);
trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF);
trailer += String.fromCharCode(this.signatureData.length &0xFF);
return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.MPIs, data+this.signatureData+trailer);
// Key revocation signatures (types 0x20 and 0x28)
// hash only the key being revoked.
case 64:
// 0x40: Timestamp signature.
// This signature is only meaningful for the timestamp contained in
// it.
case 80:
// 0x50: Third-Party Confirmation signature.
// This signature is a signature over some other OpenPGP Signature
// packet(s). It is analogous to a notary seal on the signed data.
// A third-party signature SHOULD include Signature Target
// subpacket(s) to give easy identification. Note that we really do
// mean SHOULD. There are plausible uses for this (such as a blind
// party that only sees the signature, not the key or source
// document) that cannot include a target subpacket.
default:
util.print_error("openpgp.packet.signature.js\n"+"signature verification for type"+ this.signatureType+" not implemented");
break;
}
}
/**
* generates debug output (pretty print)
* @return {string} String which gives some information about the signature packet
*/
function toString () {
if (this.version == 3) {
var result = '5.2. Signature Packet (Tag 2)\n'+
"Packet Length: :"+this.packetLength+'\n'+
"Packet version: :"+this.version+'\n'+
"One-octet signature type :"+this.signatureType+'\n'+
"Four-octet creation time. :"+this.creationTime+'\n'+
"Eight-octet Key ID of signer. :"+util.hexidump(this.keyId)+'\n'+
"One-octet public-key algorithm. :"+this.publicKeyAlgorithm+'\n'+
"One-octet hash algorithm. :"+this.hashAlgorithm+'\n'+
"Two-octet field holding left\n" +
" 16 bits of signed hash value. :"+this.signedHashValue+'\n';
} else {
var result = '5.2. Signature Packet (Tag 2)\n'+
"Packet Length: :"+this.packetLength+'\n'+
"Packet version: :"+this.version+'\n'+
"One-octet signature type :"+this.signatureType+'\n'+
"One-octet public-key algorithm. :"+this.publicKeyAlgorithm+'\n'+
"One-octet hash algorithm. :"+this.hashAlgorithm+'\n'+
"Two-octet field holding left\n" +
" 16 bits of signed hash value. :"+this.signedHashValue+'\n'+
"Signature Creation Time :"+this.creationTime+'\n'+
"Signature Expiration Time :"+this.signatureExpirationTime+'\n'+
"Signature Never Expires :"+this.signatureNeverExpires+'\n'+
"Exportable Certification :"+this.exportable+'\n'+
"Trust Signature level: :"+this.trustLevel+' amount'+this.trustAmount+'\n'+
"Regular Expression :"+this.regular_expression+'\n'+
"Revocable :"+this.revocable+'\n'+
"Key Expiration Time :"+this.keyExpirationTime+" "+this.keyNeverExpires+'\n'+
"Preferred Symmetric Algorithms :"+this.preferredSymmetricAlgorithms+'\n'+
"Revocation Key"+'\n'+
" ( 1 octet of class, :"+this.revocationKeyClass +'\n'+
" 1 octet of public-key ID, :" +this.revocationKeyAlgorithm+'\n'+
" 20 octets of fingerprint) :"+this.revocationKeyFingerprint+'\n'+
"Issuer :"+util.hexstrdump(this.issuerKeyId)+'\n'+
"Preferred Hash Algorithms :"+this.preferredHashAlgorithms+'\n'+
"Preferred Compression Alg. :"+this.preferredCompressionAlgorithms+'\n'+
"Key Server Preferences :"+this.keyServerPreferences+'\n'+
"Preferred Key Server :"+this.preferredKeyServer+'\n'+
"Primary User ID :"+this.isPrimaryUserID+'\n'+
"Policy URI :"+this.policyURI+'\n'+
"Key Flags :"+this.keyFlags+'\n'+
"Signer's User ID :"+this.signersUserId+'\n'+
"Notation :"+this.notationName+" = "+this.notationValue+"\n"+
"Reason for Revocation\n"+
" Flag :"+this.reasonForRevocationFlag+'\n'+
" Reason :"+this.reasonForRevocationString+'\nMPI:\n';
}
for (var i = 0; i < this.MPIs.length; i++) {
result += this.MPIs[i].toString();
}
return result;
}
/**
* gets the issuer key id of this signature
* @return {String} issuer key id as string (8bytes)
*/
function getIssuer() {
if (this.version == 4)
return this.issuerKeyId;
if (this.verions == 4)
return this.keyId;
return null;
}
/**
* Tries to get the corresponding public key out of the public keyring for the issuer created this signature
* @return {obj: [openpgp_msg_publickey], text: [String]} if found the public key will be returned. null otherwise
*/
function getIssuerKey() {
var result = null;
if (this.version == 4) {
result = openpgp.keyring.getPublicKeysForKeyId(this.issuerKeyId);
} else if (this.version == 3) {
result = openpgp.keyring.getPublicKeysForKeyId(this.keyId);
} else return null;
if (result.length == 0)
return null;
return result[0];
}
this.getIssuerKey = getIssuerKey;
this.getIssuer = getIssuer;
this.write_message_signature = write_message_signature;
this.verify = verify;
this.read_packet = read_packet;
this.toString = toString;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Modification Detection Code Packet (Tag 19)
*
* RFC4880 5.14: The Modification Detection Code packet contains a SHA-1 hash of
* plaintext data, which is used to detect message modification. It is only used
* with a Symmetrically Encrypted Integrity Protected Data packet. The
* Modification Detection Code packet MUST be the last packet in the plaintext
* data that is encrypted in the Symmetrically Encrypted Integrity Protected
* Data packet, and MUST appear in no other place.
*/
function openpgp_packet_modificationdetectioncode() {
this.tagType = 19;
this.hash = null;
/**
* parsing function for a modification detection code packet (tag 19).
*
* @param {String} input payload of a tag 19 packet
* @param {Integer} position
* position to start reading from the input string
* @param {Integer} len
* length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
this.packetLength = len;
if (len != 20) {
util
.print_error("openpgp.packet.modificationdetectioncode.js\n"
+ 'invalid length for a modification detection code packet!'
+ len);
return null;
}
// - A 20-octet SHA-1 hash of the preceding plaintext data of the
// Symmetrically Encrypted Integrity Protected Data packet,
// including prefix data, the tag octet, and length octet of the
// Modification Detection Code packet.
this.hash = input.substring(position, position + 20);
return this;
}
/*
* this packet is created within the encryptedintegrityprotected packet
* function write_packet(data) { }
*/
/**
* generates debug output (pretty print)
*
* @return {string} String which gives some information about the modification
* detection code
*/
function toString() {
return '5.14 Modification detection code packet\n' + ' bytes ('
+ this.hash.length + '): [' + util.hexstrdump(this.hash) + ']';
}
this.read_packet = read_packet;
this.toString = toString;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the strange "Marker packet" (Tag 10)
*
* RFC4880 5.8: An experimental version of PGP used this packet as the Literal
* packet, but no released version of PGP generated Literal packets with this
* tag. With PGP 5.x, this packet has been reassigned and is reserved for use as
* the Marker packet.
*
* Such a packet MUST be ignored when received.
*/
function openpgp_packet_marker() {
this.tagType = 10;
/**
* parsing function for a literal data packet (tag 10).
*
* @param {string} input payload of a tag 10 packet
* @param {integer} position
* position to start reading from the input string
* @param {integer} len
* length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
this.packetLength = 3;
if (input[position].charCodeAt() == 0x50 && // P
input[position + 1].charCodeAt() == 0x47 && // G
input[position + 2].charCodeAt() == 0x50) // P
return this;
// marker packet does not contain "PGP"
return null;
}
/**
* Generates Debug output
*
* @return {string} String which gives some information about the keymaterial
*/
function toString() {
return "5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)\n"
+ " packet reads: \"PGP\"\n";
}
this.read_packet = read_packet;
this.toString = toString;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Public-Key Encrypted Session Key Packets (Tag 1)
*
* RFC4880 5.1: A Public-Key Encrypted Session Key packet holds the session key
* used to encrypt a message. Zero or more Public-Key Encrypted Session Key
* packets and/or Symmetric-Key Encrypted Session Key packets may precede a
* Symmetrically Encrypted Data Packet, which holds an encrypted message. The
* message is encrypted with the session key, and the session key is itself
* encrypted and stored in the Encrypted Session Key packet(s). The
* Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted
* Session Key packet for each OpenPGP key to which the message is encrypted.
* The recipient of the message finds a session key that is encrypted to their
* public key, decrypts the session key, and then uses the session key to
* decrypt the message.
*/
function openpgp_packet_encryptedsessionkey() {
/**
* parsing function for a publickey encrypted session key packet (tag 1).
*
* @param {string} input payload of a tag 1 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_pub_key_packet(input, position, len) {
this.tagType = 1;
this.packetLength = len;
var mypos = position;
if (len < 10) {
util
.print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length');
return null;
}
this.version = input[mypos++].charCodeAt();
this.keyId = new openpgp_type_keyid();
this.keyId.read_packet(input, mypos);
mypos += 8;
this.publicKeyAlgorithmUsed = input[mypos++].charCodeAt();
switch (this.publicKeyAlgorithmUsed) {
case 1:
case 2: // RSA
this.MPIs = new Array();
this.MPIs[0] = new openpgp_type_mpi();
this.MPIs[0].read(input, mypos, mypos - position);
break;
case 16: // Elgamal
this.MPIs = new Array();
this.MPIs[0] = new openpgp_type_mpi();
this.MPIs[0].read(input, mypos, mypos - position);
mypos += this.MPIs[0].packetLength;
this.MPIs[1] = new openpgp_type_mpi();
this.MPIs[1].read(input, mypos, mypos - position);
break;
default:
util.print_error("openpgp.packet.encryptedsessionkey.js\n"
+ "unknown public key packet algorithm type "
+ this.publicKeyAlgorithmType);
break;
}
return this;
}
/**
* create a string representation of a tag 1 packet
*
* @param {String} publicKeyId
* the public key id corresponding to publicMPIs key as string
* @param {Array[openpgp_type_mpi]} publicMPIs
* multiprecision integer objects describing the public key
* @param {integer} pubalgo
* the corresponding public key algorithm // See RFC4880 9.1
* @param {integer} symmalgo
* the symmetric cipher algorithm used to encrypt the data within
* an encrypteddatapacket or encryptedintegrityprotecteddatapacket
* following this packet //See RFC4880 9.2
* @param {String} sessionkey
* a string of randombytes representing the session key
* @return {String} the string representation
*/
function write_pub_key_packet(publicKeyId, publicMPIs, pubalgo, symmalgo,
sessionkey) {
var result = String.fromCharCode(3);
var data = String.fromCharCode(symmalgo);
data += sessionkey;
var checksum = util.calc_checksum(sessionkey);
data += String.fromCharCode((checksum >> 8) & 0xFF);
data += String.fromCharCode((checksum) & 0xFF);
result += publicKeyId;
result += String.fromCharCode(pubalgo);
var mpi = new openpgp_type_mpi();
var mpiresult = openpgp_crypto_asymetricEncrypt(pubalgo, publicMPIs,
mpi.create(openpgp_encoding_eme_pkcs1_encode(data,
publicMPIs[0].mpiByteLength)));
for ( var i = 0; i < mpiresult.length; i++) {
result += mpiresult[i];
}
result = openpgp_packet.write_packet_header(1, result.length) + result;
return result;
}
/**
* parsing function for a symmetric encrypted session key packet (tag 3).
*
* @param {string} input payload of a tag 1 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len
* length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_symmetric_key_packet(input, position, len) {
this.tagType = 3;
var mypos = position;
// A one-octet version number. The only currently defined version is 4.
this.version = input[mypos++];
// A one-octet number describing the symmetric algorithm used.
this.symmetricKeyAlgorithmUsed = input[mypos++];
// A string-to-key (S2K) specifier, length as defined above.
this.s2k = new openpgp_type_s2k();
this.s2k.read(input, mypos);
// Optionally, the encrypted session key itself, which is decrypted
// with the string-to-key object.
if ((s2k.s2kLength + mypos) < len) {
this.encryptedSessionKey = new Array();
for ( var i = (mypos - position); i < len; i++) {
this.encryptedSessionKey[i] = input[mypos++];
}
}
return this;
}
/**
* Decrypts the session key (only for public key encrypted session key
* packets (tag 1)
*
* @param {openpgp_msg_message} msg
* the message object (with member encryptedData
* @param {openpgp_msg_privatekey} key
* private key with secMPIs unlocked
* @return {String} the unencrypted session key
*/
function decrypt(msg, key) {
if (this.tagType == 1) {
var result = openpgp_crypto_asymetricDecrypt(
this.publicKeyAlgorithmUsed, key.publicKey.MPIs,
key.secMPIs, this.MPIs).toMPI();
var checksum = ((result.charCodeAt(result.length - 2) << 8) + result
.charCodeAt(result.length - 1));
var decoded = openpgp_encoding_eme_pkcs1_decode(result.substring(2, result.length - 2), key.publicKey.MPIs[0].getByteLength());
var sesskey = decoded.substring(1);
var algo = decoded.charCodeAt(0);
if (msg.encryptedData.tagType == 18)
return msg.encryptedData.decrypt(algo, sesskey);
else
return msg.encryptedData.decrypt_sym(algo, sesskey);
} else if (this.tagType == 3) {
util
.print_error("Symmetric encrypted sessionkey is not supported!");
return null;
}
}
/**
* Creates a string representation of this object (useful for debug
* purposes)
*
* @return the string containing a openpgp description
*/
function toString() {
if (this.tagType == 1) {
var result = '5.1. Public-Key Encrypted Session Key Packets (Tag 1)\n'
+ ' KeyId: '
+ this.keyId.toString()
+ '\n'
+ ' length: '
+ this.packetLength
+ '\n'
+ ' version:'
+ this.version
+ '\n'
+ ' pubAlgUs:'
+ this.publicKeyAlgorithmUsed + '\n';
for ( var i = 0; i < this.MPIs.length; i++) {
result += this.MPIs[i].toString();
}
return result;
} else
return '5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n'
+ ' KeyId: ' + this.keyId.toString() + '\n'
+ ' length: ' + this.packetLength + '\n'
+ ' version:' + this.version + '\n' + ' symKeyA:'
+ this.symmetricKeyAlgorithmUsed + '\n' + ' s2k: '
+ this.s2k + '\n';
}
this.read_pub_key_packet = read_pub_key_packet;
this.read_symmetric_key_packet = read_symmetric_key_packet;
this.write_pub_key_packet = write_pub_key_packet;
this.toString = toString;
this.decrypt = decrypt;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Key Material Packet (Tag 5,6,7,14)
*
* RFC4480 5.5:
* A key material packet contains all the information about a public or
* private key. There are four variants of this packet type, and two
* major versions. Consequently, this section is complex.
*/
function openpgp_packet_keymaterial() {
// members:
this.publicKeyAlgorithm = null;
this.tagType = null;
this.creationTime = null;
this.version = null;
this.expiration = null;// V3
this.MPIs = null;
this.secMPIs = null;
this.publicKey = null;
this.symmetricEncryptionAlgorithm = null;
this.s2kUsageConventions = null;
this.IVLength = null;
this.encryptedMPIData = null;
this.hasUnencryptedSecretKeyData = null;
this.checksum = null;
this.parentNode = null;
this.subKeySignature = null;
this.subKeyRevocationSignature = null;
// 5.5.1. Key Packet Variants
// 5.5.1.3. Secret-Key Packet (Tag 5)
/**
* This function reads the payload of a secret key packet (Tag 5)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag5(input, position, len) {
this.tagType = 5;
this.read_priv_key(input, position, len);
return this;
}
// 5.5.1.1. Public-Key Packet (Tag 6)
/**
* This function reads the payload of a public key packet (Tag 6)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag6(input, position, len) {
// A Public-Key packet starts a series of packets that forms an OpenPGP
// key (sometimes called an OpenPGP certificate).
this.tagType = 6;
this.packetLength = len;
this.read_pub_key(input, position,len);
return this;
}
// 5.5.1.4. Secret-Subkey Packet (Tag 7)
/**
* This function reads the payload of a secret key sub packet (Tag 7)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag7(input, position, len) {
this.tagType = 7;
this.packetLength = len;
return this.read_priv_key(input, position, len);
}
// 5.5.1.2. Public-Subkey Packet (Tag 14)
/**
* This function reads the payload of a public key sub packet (Tag 14)
* and initializes the openpgp_packet_keymaterial
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return openpgp_packet_keymaterial object
*/
function read_tag14(input, position, len) {
this.subKeySignature = null;
this.subKeyRevocationSignature = new Array();
this.tagType = 14;
this.packetLength = len;
this.read_pub_key(input, position,len);
return this;
}
/**
* Internal Parser for public keys as specified in RFC 4880 section 5.5.2 Public-Key Packet Formats
* called by read_tag<num>
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return this object with attributes set by the parser
*/
function read_pub_key(input, position, len) {
var mypos = position;
// A one-octet version number (3 or 4).
this.version = input[mypos++].charCodeAt();
if (this.version == 3) {
// A four-octet number denoting the time that the key was created.
this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) |
(input[mypos++].charCodeAt() << 16) |
(input[mypos++].charCodeAt() << 8) |
(input[mypos++].charCodeAt()))*1000);
// - A two-octet number denoting the time in days that this key is
// valid. If this number is zero, then it does not expire.
this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt();
// - A one-octet number denoting the public-key algorithm of this key.
this.publicKeyAlgorithm = input[mypos++].charCodeAt();
var mpicount = 0;
// - A series of multiprecision integers comprising the key material:
// Algorithm-Specific Fields for RSA public keys:
// - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e.
if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4)
mpicount = 2;
// Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 16)
mpicount = 3;
// Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 17)
mpicount = 4;
this.MPIs = new Array();
for (var i = 0; i < mpicount; i++) {
this.MPIs[i] = new openpgp_type_mpi();
if (this.MPIs[i].read(input, mypos, (mypos-position)) != null &&
!this.packetLength < (mypos-position)) {
mypos += this.MPIs[i].packetLength;
} else {
util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos);
}
}
this.packetLength = mypos-position;
} else if (this.version == 4) {
// - A four-octet number denoting the time that the key was created.
this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) |
(input[mypos++].charCodeAt() << 16) |
(input[mypos++].charCodeAt() << 8) |
(input[mypos++].charCodeAt()))*1000);
// - A one-octet number denoting the public-key algorithm of this key.
this.publicKeyAlgorithm = input[mypos++].charCodeAt();
var mpicount = 0;
// - A series of multiprecision integers comprising the key material:
// Algorithm-Specific Fields for RSA public keys:
// - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e.
if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4)
mpicount = 2;
// Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 16)
mpicount = 3;
// Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
else if (this.publicKeyAlgorithm == 17)
mpicount = 4;
this.MPIs = new Array();
var i = 0;
for (var i = 0; i < mpicount; i++) {
this.MPIs[i] = new openpgp_type_mpi();
if (this.MPIs[i].read(input, mypos, (mypos-position)) != null &&
!this.packetLength < (mypos-position)) {
mypos += this.MPIs[i].packetLength;
} else {
util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos);
}
}
this.packetLength = mypos-position;
} else {
return null;
}
this.data = input.substring(position, mypos);
this.packetdata = input.substring(position, mypos);
return this;
}
// 5.5.3. Secret-Key Packet Formats
/**
* Internal parser for private keys as specified in RFC 4880 section 5.5.3
* @param input input string to read the packet from
* @param position start position for the parser
* @param len length of the packet or remaining length of input
* @return this object with attributes set by the parser
*/
function read_priv_key(input,position, len) {
// - A Public-Key or Public-Subkey packet, as described above.
this.publicKey = new openpgp_packet_keymaterial();
if (this.publicKey.read_pub_key(input,position, len) == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input[position].charCodeAt()+" "+position+" "+len+"\n Aborting here...");
return null;
}
this.publicKey.header = openpgp_packet.write_old_packet_header(6,this.publicKey.packetLength);
// this.publicKey.header = String.fromCharCode(0x99) + String.fromCharCode(this.publicKey.packetLength >> 8 & 0xFF)+String.fromCharCode(this.publicKey.packetLength & 0xFF);
var mypos = position + this.publicKey.data.length;
this.packetLength = len;
// - One octet indicating string-to-key usage conventions. Zero
// indicates that the secret-key data is not encrypted. 255 or 254
// indicates that a string-to-key specifier is being given. Any
// other value is a symmetric-key encryption algorithm identifier.
this.s2kUsageConventions = input[mypos++].charCodeAt();
if (this.s2kUsageConventions == 0)
this.hasUnencryptedSecretKeyData = true;
// - [Optional] If string-to-key usage octet was 255 or 254, a one-
// octet symmetric encryption algorithm.
if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) {
this.symmetricEncryptionAlgorithm = input[mypos++].charCodeAt();
}
// - [Optional] If string-to-key usage octet was 255 or 254, a
// string-to-key specifier. The length of the string-to-key
// specifier is implied by its type, as described above.
if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) {
this.s2k = new openpgp_type_s2k();
this.s2k.read(input, mypos);
mypos +=this.s2k.s2kLength;
}
// - [Optional] If secret data is encrypted (string-to-key usage octet
// not zero), an Initial Vector (IV) of the same length as the
// cipher's block size.
this.symkeylength = 0;
if (this.s2kUsageConventions != 0 && this.s2kUsageConventions != 255 &&
this.s2kUsageConventions != 254) {
this.symmetricEncryptionAlgorithm = this.s2kUsageConventions;
}
if (this.s2kUsageConventions != 0) {
this.hasIV = true;
switch (this.symmetricEncryptionAlgorithm) {
case 1: // - IDEA [IDEA]
util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encrytryption algorithim: IDEA is not implemented");
return null;
case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
case 3: // - CAST5 (128 bit key, as per [RFC2144])
this.IVLength = 8;
break;
case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
case 7: // - AES with 128-bit key [AES]
case 8: // - AES with 192-bit key
case 9: // - AES with 256-bit key
this.IVLength = 16;
break;
case 10: // - Twofish with 256-bit key [TWOFISH]
this.IVLength = 32;
break;
case 5: // - Reserved
case 6: // - Reserved
default:
util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm);
return null;
}
mypos++;
this.IV = input.substring(mypos, mypos+this.IVLength);
mypos += this.IVLength;
}
// - Plain or encrypted multiprecision integers comprising the secret
// key data. These algorithm-specific fields are as described
// below.
//
//
if (!this.hasUnencryptedSecretKeyData) {
this.encryptedMPIData = input.substring(mypos, len);
mypos += this.encryptedMPIData.length;
} else {
if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 4) {
// Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[0].packetLength;
this.secMPIs[1] = new openpgp_type_mpi();
this.secMPIs[1].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[1].packetLength;
this.secMPIs[2] = new openpgp_type_mpi();
this.secMPIs[2].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[2].packetLength;
this.secMPIs[3] = new openpgp_type_mpi();
this.secMPIs[3].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[3].packetLength;
} else if (this.publicKey.publicKeyAlgorithm == 16) {
// Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[0].packetLength;
} else if (this.publicKey.publicKeyAlgorithm == 17) {
// Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(input, mypos, len-2- (mypos - position));
mypos += this.secMPIs[0].packetLength;
}
// checksum because s2k usage convention is 0
this.checksum = new Array();
this.checksum[0] = input[mypos++].charCodeAt();
this.checksum[1] = input[mypos++].charCodeAt();
}
return this;
}
/**
* Decrypts the private key MPIs which are needed to use the key.
* openpgp_packet_keymaterial.hasUnencryptedSecretKeyData should be false otherwise
* a call to this function is not needed
*
* @param str_passphrase the passphrase for this private key as string
* @return true if the passphrase was correct; false if not
*/
function decryptSecretMPIs(str_passphrase) {
if (this.hasUnencryptedSecretKeyData)
return this.secMPIs;
// creating a key out of the passphrase
var key = this.s2k.produce_key(str_passphrase);
var cleartextMPIs = "";
switch (this.symmetricEncryptionAlgorithm) {
case 1: // - IDEA [IDEA]
util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encryption algorithim: IDEA is not implemented");
return false;
case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
cleartextMPIs = normal_cfb_decrypt(function(block, key) {
return des(key, block,1,null,0);
}, this.IVLength, key, this.encryptedMPIData, this.IV);
break;
case 3: // - CAST5 (128 bit key, as per [RFC2144])
cleartextMPIs = normal_cfb_decrypt(function(block, key) {
var cast5 = new openpgp_symenc_cast5();
cast5.setKey(key);
return cast5.encrypt(util.str2bin(block));
}, this.IVLength, util.str2bin(key.substring(0,16)), this.encryptedMPIData, this.IV);
break;
case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH]
cleartextMPIs = normal_cfb_decrypt(function(block, key) {
var blowfish = new Blowfish(key);
return blowfish.encrypt(block);
}, this.IVLength, key, this.encryptedMPIData, this.IV);
break;
case 7: // - AES with 128-bit key [AES]
case 8: // - AES with 192-bit key
case 9: // - AES with 256-bit key
var numBytes = 16;
//This is a weird way to achieve this. If's within a switch is probably not ideal.
if(this.symmetricEncryptionAlgorithm == 8){
numBytes = 24;
key = this.s2k.produce_key(str_passphrase,numBytes);
}
if(this.symmetricEncryptionAlgorithm == 9){
numBytes = 32;
key = this.s2k.produce_key(str_passphrase,numBytes);
}
cleartextMPIs = normal_cfb_decrypt(function(block,key){
return AESencrypt(util.str2bin(block),key);
},
this.IVLength, keyExpansion(key.substring(0,numBytes)), this.encryptedMPIData, this.IV);
break;
case 10: // - Twofish with 256-bit key [TWOFISH]
util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented");
return false;
case 5: // - Reserved
case 6: // - Reserved
default:
util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm);
return false;
}
if (cleartextMPIs == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+"cleartextMPIs was null");
return false;
}
var cleartextMPIslength = cleartextMPIs.length;
if (this.s2kUsageConventions == 254 &&
str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) ==
cleartextMPIs.substring(cleartextMPIs.length - 20)) {
cleartextMPIslength -= 20;
} else if (this.s2kUsageConventions != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) ==
(cleartextMPIs.charCodeAt(cleartextMPIs.length -2) << 8 | cleartextMPIs.charCodeAt(cleartextMPIs.length -1))) {
cleartextMPIslength -= 2;
} else {
return false;
}
if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 4) {
// Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q.
var mypos = 0;
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength);
mypos += this.secMPIs[0].packetLength;
this.secMPIs[1] = new openpgp_type_mpi();
this.secMPIs[1].read(cleartextMPIs, mypos, cleartextMPIslength-mypos);
mypos += this.secMPIs[1].packetLength;
this.secMPIs[2] = new openpgp_type_mpi();
this.secMPIs[2].read(cleartextMPIs, mypos, cleartextMPIslength-mypos);
mypos += this.secMPIs[2].packetLength;
this.secMPIs[3] = new openpgp_type_mpi();
this.secMPIs[3].read(cleartextMPIs, mypos, cleartextMPIslength-mypos);
mypos += this.secMPIs[3].packetLength;
} else if (this.publicKey.publicKeyAlgorithm == 16) {
// Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIs);
} else if (this.publicKey.publicKeyAlgorithm == 17) {
// Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x.
this.secMPIs = new Array();
this.secMPIs[0] = new openpgp_type_mpi();
this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength);
}
return true;
}
/**
* Generates Debug output
* @return String which gives some information about the keymaterial
*/
function toString() {
var result = "";
switch (this.tagType) {
case 6:
result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+
' length: '+this.packetLength+'\n'+
' version: '+this.version+'\n'+
' creation time: '+this.creationTime+'\n'+
' expiration time: '+this.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n';
break;
case 14:
result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+
' length: '+this.packetLength+'\n'+
' version: '+this.version+'\n'+
' creation time: '+this.creationTime+'\n'+
' expiration time: '+this.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n';
break;
case 5:
result +='5.5.1.3. Secret-Key Packet (Tag 5)\n'+
' length: '+this.packetLength+'\n'+
' version: '+this.publicKey.version+'\n'+
' creation time: '+this.publicKey.creationTime+'\n'+
' expiration time: '+this.publicKey.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKey.publicKeyAlgorithm+'\n';
break;
case 7:
result += '5.5.1.4. Secret-Subkey Packet (Tag 7)\n'+
' length: '+this.packetLength+'\n'+
' version[1]: '+(this.version == 4)+'\n'+
' creationtime[4]: '+this.creationTime+'\n'+
' expiration[2]: '+this.expiration+'\n'+
' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n';
break;
default:
result += 'unknown key material packet\n';
}
if (this.MPIs != null) {
result += "Public Key MPIs:\n";
for (var i = 0; i < this.MPIs.length; i++) {
result += this.MPIs[i].toString();
}
}
if (this.publicKey != null && this.publicKey.MPIs != null) {
result += "Public Key MPIs:\n";
for (var i = 0; i < this.publicKey.MPIs.length; i++) {
result += this.publicKey.MPIs[i].toString();
}
}
if (this.secMPIs != null) {
result += "Secret Key MPIs:\n";
for (var i = 0; i < this.secMPIs.length; i++) {
result += this.secMPIs[i].toString();
}
}
if (this.subKeySignature != null)
result += "subKey Signature:\n"+this.subKeySignature.toString();
if (this.subKeyRevocationSignature != null )
result += "subKey Revocation Signature:\n"+this.subKeyRevocationSignature.toString();
return result;
}
/**
* Continue parsing packets belonging to the key material such as signatures
* @param {openpgp_*} parent_node the parent object
* @param {String} input input string to read the packet(s) from
* @param {integer} position start position for the parser
* @param {integer} len length of the packet(s) or remaining length of input
* @return {integer} length of nodes read
*/
function read_nodes(parent_node, input, position, len) {
this.parentNode = parent_node;
if (this.tagType == 14) { // public sub-key packet
var pos = position;
var result = null;
while (input.length != pos) {
var l = input.length - pos;
result = openpgp_packet.read_packet(input, pos, l);
if (result == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_pub]parsing ends here @:' + pos + " l:" + l);
break;
} else {
switch (result.tagType) {
case 2: // Signature Packet certification signature
if (result.signatureType == 24) { // subkey binding signature
this.subKeySignature = result;
pos += result.packetLength + result.headerLength;
break;
} else if (result.signatureType == 40) { // subkey revocation signature
this.subKeyRevocationSignature = result;
pos += result.packetLength + result.headerLength;
break;
} else {
util.print_error("openpgp.packet.keymaterial.js\nunknown signature:"+result.toString());
}
default:
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
break;
}
}
}
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
} else if (this.tagType == 7) { // private sub-key packet
var pos = position;
while (input.length != pos) {
var result = openpgp_packet.read_packet(input, pos, len - (pos - position));
if (result == null) {
util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_priv] parsing ends here @:' + pos);
break;
} else {
switch (result.tagType) {
case 2: // Signature Packet certification signature
if (result.signatureType == 24) // subkey embedded signature
this.subKeySignature = result;
else if (result.signatureType == 40) // subkey revocation signature
this.subKeyRevocationSignature[this.subKeyRevocationSignature] = result;
pos += result.packetLength + result.headerLength;
break;
default:
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
}
}
}
this.data = input;
this.position = position - this.parentNode.packetLength;
this.len = pos - position;
return this.len;
} else {
util.print_error("openpgp.packet.keymaterial.js\n"+"unknown parent node for a key material packet "+parent_node.tagType);
}
}
/**
* Checks the validity for usage of this (sub)key
* @return 0 = bad key, 1 = expired, 2 = revoked, 3 = valid
*/
function verifyKey() {
if (this.tagType == 14) {
if (this.subKeySignature == null)
return 0;
if (this.subKeySignature.version == 4 &&
this.subKeySignature.keyNeverExpires != null &&
!this.subKeySignature.keyNeverExpires &&
new Date((this.subKeySignature.keyExpirationTime*1000)+ this.creationTime.getTime()) < new Date())
return 1;
var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+
String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata;
if (!this.subKeySignature.verify(hashdata,this.parentNode)) {
return 0;
}
for (var i = 0; i < this.subKeyRevocationSignature.length; i++) {
if (this.subKeyRevocationSignature[i])
var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+
String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata;
if (this.subKeyRevocationSignature[i].verify(hashdata, this.parentNode))
return 2;
else
return 0;
}
}
return 3;
}
/**
* calculates the key id of they key
* @return {String} a 8 byte key id
*/
function getKeyId() {
if (this.version == 4) {
var f = this.getFingerprint();
return f.substring(12,20);
} else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) {
var key_id = this.MPIs[0].substring((this.MPIs[0].mpiByteLength-8));
util.print_debug("openpgp.msg.publickey read_nodes:\n"+"V3 key ID: "+key_id);
return key_id;
}
}
/**
* calculates the fingerprint of the key
* @return {String} a string containing the fingerprint
*/
function getFingerprint() {
if (this.version == 4) {
tohash = String.fromCharCode(0x99)+ String.fromCharCode(((this.packetdata.length) >> 8) & 0xFF)
+ String.fromCharCode((this.packetdata.length) & 0xFF)+this.packetdata;
util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm);
return str_sha1(tohash, tohash.length);
} else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) {
return MD5(this.MPIs[0].MPI);
}
}
/*
* creates an OpenPGP key packet for the given key. much TODO in regards to s2k, subkeys.
* @param {int} keyType follows the OpenPGP algorithm standard, IE 1 corresponds to RSA.
* @param {RSA.keyObject} key
* @param password
* @param s2kHash
* @param symmetricEncryptionAlgorithm
* @param timePacket
* @return {body: [string]OpenPGP packet body contents, header: [string] OpenPGP packet header, string: [string] header+body}
*/
function write_private_key(keyType, key, password, s2kHash, symmetricEncryptionAlgorithm, timePacket){
this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm;
var tag = 5;
var body = String.fromCharCode(4);
body += timePacket;
switch(keyType){
case 1:
body += String.fromCharCode(keyType);//public key algo
body += key.n.toMPI();
body += key.ee.toMPI();
var algorithmStart = body.length;
//below shows ske/s2k
if(password){
body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1
//if s2k == 255,254 then 1 octet symmetric encryption algo
body += String.fromCharCode(this.symmetricEncryptionAlgorithm);
//if s2k == 255,254 then s2k specifier
body += String.fromCharCode(3); //s2k salt+iter
body += String.fromCharCode(s2kHash);
//8 octet salt value
//1 octet count
var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI();
var sha1Hash = str_sha1(cleartextMPIs);
util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash);
var salt = openpgp_crypto_getRandomBytes(8);
util.print_debug_hexstr_dump('write_private_key Salt: ',salt);
body += salt;
var c = 96; //c of 96 translates to count of 65536
body += String.fromCharCode(c);
util.print_debug('write_private_key c: '+ c);
var s2k = new openpgp_type_s2k();
var hashKey = s2k.write(3, s2kHash, password, salt, c);
//if s2k, IV of same length as cipher's block
switch(this.symmetricEncryptionAlgorithm){
case 3:
this.IVLength = 8;
this.IV = openpgp_crypto_getRandomBytes(this.IVLength);
ciphertextMPIs = normal_cfb_encrypt(function(block, key) {
var cast5 = new openpgp_symenc_cast5();
cast5.setKey(key);
return cast5.encrypt(util.str2bin(block));
}, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV);
body += this.IV + ciphertextMPIs;
break;
case 7:
case 8:
case 9:
this.IVLength = 16;
this.IV = openpgp_crypto_getRandomBytes(this.IVLength);
ciphertextMPIs = normal_cfb_encrypt(AESencrypt,
this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV);
body += this.IV + ciphertextMPIs;
break;
}
}
else{
body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k
body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI();
var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI());
body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536
util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum);
}
break;
default :
body = "";
util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType);
}
var header = openpgp_packet.write_packet_header(tag,body.length);
return {string: header+body , header: header, body: body};
}
/*
* same as write_private_key, but has less information because of public key.
* @param {int} keyType follows the OpenPGP algorithm standard, IE 1 corresponds to RSA.
* @param {RSA.keyObject} key
* @param timePacket
* @return {body: [string]OpenPGP packet body contents, header: [string] OpenPGP packet header, string: [string] header+body}
*/
function write_public_key(keyType, key, timePacket){
var tag = 6;
var body = String.fromCharCode(4);
body += timePacket;
switch(keyType){
case 1:
body += String.fromCharCode(1);//public key algo
body += key.n.toMPI();
body += key.ee.toMPI();
break;
default:
util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType);
}
var header = openpgp_packet.write_packet_header(tag,body.length);
return {string: header+body , header: header, body: body};
}
this.read_tag5 = read_tag5;
this.read_tag6 = read_tag6;
this.read_tag7 = read_tag7;
this.read_tag14 = read_tag14;
this.toString = toString;
this.read_pub_key = read_pub_key;
this.read_priv_key = read_priv_key;
this.decryptSecretMPIs = decryptSecretMPIs;
this.read_nodes = read_nodes;
this.verifyKey = verifyKey;
this.getKeyId = getKeyId;
this.getFingerprint = getFingerprint;
this.write_private_key = write_private_key;
this.write_public_key = write_public_key;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the User ID Packet (Tag 13)
* A User ID packet consists of UTF-8 text that is intended to represent
* the name and email address of the key holder. By convention, it
* includes an RFC 2822 [RFC2822] mail name-addr, but there are no
* restrictions on its content. The packet length in the header
* specifies the length of the User ID.
*/
function openpgp_packet_userid() {
this.tagType = 13;
this.certificationSignatures = new Array();
this.certificationRevocationSignatures = new Array();
this.revocationSignatures = new Array();
this.parentNode = null;
/**
* parsing function for a user id packet (tag 13).
* @param {string} input payload of a tag 13 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
this.text = '';
this.packetLength = len;
for ( var i = 0; i < len; i++) {
this.text += input[position + i];
}
return this;
}
/**
* creates a string representation of the user id packet
* @param {String} user_id the user id as string ("John Doe 15
&& result.signatureType < 20) { // certification
// //
// signature
this.certificationSignatures[this.certificationSignatures.length] = result;
break;
} else if (result.signatureType == 48) {// certification revocation signature
this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result;
break;
} else if (result.signatureType == 24) { // omg. standalone signature
this.certificationSignatures[this.certificationSignatures.length] = result;
break;
} else {
util.debug("unknown sig t: "+result.signatureType+"@"+(pos - (result.packetLength + result.headerLength)));
}
default:
this.data = input;
this.position = position - parent_node.packetLength;
this.len = pos - position -(result.headerLength + result.packetLength);
return this.len;
}
}
}
this.data = input;
this.position = position - parent_node.packetLength;
this.len = pos - position -(result.headerLength + result.packetLength);
return this.len;
} else if (parent_node.tagType == 5) { // secret Key
this.parentNode = parent_node;
var exit = false;
var pos = position;
while (input.length != pos) {
var result = openpgp_packet.read_packet(input, pos, l - (pos - position));
if (result == null) {
util.print_error('parsing ends here @:' + pos + " l:" + l);
break;
} else {
pos += result.packetLength + result.headerLength;
l = input.length - pos;
switch (result.tagType) {
case 2: // Signature Packet certification signature
if (result.signatureType > 15
&& result.signatureType < 20)
this.certificationSignatures[this.certificationSignatures.length] = result;
// certification revocation signature
else if (result.signatureType == 48)
this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result;
default:
this.data = input;
this.position = position - parent_node.packetLength;
this.len = pos - position -(result.headerLength + result.packetLength);
return this.len;
}
}
}
} else {
util.print_error("unknown parent node for a userId packet "+parent_node.tagType);
}
}
/**
* generates debug output (pretty print)
* @return {string} String which gives some information about the user id packet
*/
function toString() {
var result = ' 5.11. User ID Packet (Tag 13)\n' + ' text ('
+ this.text.length + '): "' + this.text.replace("<", "<")
+ '"\n';
result +="certification signatures:\n";
for (var i = 0; i < this.certificationSignatures.length; i++) {
result += " "+this.certificationSignatures[i].toString();
}
result +="certification revocation signatures:\n";
for (var i = 0; i < this.certificationRevocationSignatures.length; i++) {
result += " "+this.certificationRevocationSignatures[i].toString();
}
return result;
}
/**
* lookup function to find certification revocation signatures
* @param {string} keyId string containing the key id of the issuer of this signature
* @return a CertificationRevocationSignature if found; otherwise null
*/
function hasCertificationRevocationSignature(keyId) {
for (var i = 0; i < this.certificationRevocationSignatures.length; i++) {
if ((this.certificationRevocationSignatures[i].version == 3 &&
this.certificationRevocationSignatures[i].keyId == keyId) ||
(this.certificationRevocationSignatures[i].version == 4 &&
this.certificationRevocationSignatures[i].issuerKeyId == keyId))
return this.certificationRevocationSignatures[i];
}
return null;
}
/**
* Verifies all certification signatures. This method does not consider possible revocation signatures.
* @param publicKeyPacket the top level key material
* @return an array of integers corresponding to the array of certification signatures. The meaning of each integer is the following:
* 0 = bad signature
* 1 = signature expired
* 2 = issuer key not available
* 3 = revoked
* 4 = signature valid
* 5 = signature by key owner expired
* 6 = signature by key owner revoked
*/
function verifyCertificationSignatures(publicKeyPacket) {
result = new Array();
for (var i = 0 ; i < this.certificationSignatures.length; i++) {
// A certification signature (type 0x10 through 0x13) hashes the User
// ID being bound to the key into the hash context after the above
// data. A V3 certification hashes the contents of the User ID or
// attribute packet packet, without any header. A V4 certification
// hashes the constant 0xB4 for User ID certifications or the constant
// 0xD1 for User Attribute certifications, followed by a four-octet
// number giving the length of the User ID or User Attribute data, and
// then the User ID or User Attribute data.
if (this.certificationSignatures[i].version == 4) {
if (this.certificationSignatures[i].signatureExpirationTime != null &&
this.certificationSignatures[i].signatureExpirationTime != null &&
this.certificationSignatures[i].signatureExpirationTime != 0 &&
!this.certificationSignatures[i].signatureNeverExpires &&
new Date(this.certificationSignatures[i].creationTime.getTime() +(this.certificationSignatures[i].signatureExpirationTime*1000)) < new Date()) {
if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId())
result[i] = 5;
else
result[i] = 1;
continue;
}
if (this.certificationSignatures[i].issuerKeyId == null) {
result[i] = 0;
continue;
}
var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].issuerKeyId);
if (issuerPublicKey == null || issuerPublicKey.length == 0) {
result[i] = 2;
continue;
}
// TODO: try to verify all returned issuer public keys (key ids are not unique!)
var issuerPublicKey = issuerPublicKey[0];
var signingKey = issuerPublicKey.obj.getSigningKey();
if (signingKey == null) {
result[i] = 0;
continue;
}
var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].issuerKeyId);
if (revocation != null && revocation.creationTime >
this.certificationSignatures[i].creationTime) {
var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+
publicKeyPacket.data+String.fromCharCode(0xB4)+
String.fromCharCode((this.text.length >> 24) & 0xFF)+
String.fromCharCode((this.text.length >> 16) & 0xFF)+
String.fromCharCode((this.text.length >> 8) & 0xFF)+
String.fromCharCode((this.text.length) & 0xFF)+
this.text;
if (revocation.verify(signaturedata, signingKey)) {
if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId())
result[i] = 6;
else
result[i] = 3;
continue;
}
}
var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+
publicKeyPacket.data+String.fromCharCode(0xB4)+
String.fromCharCode((this.text.length >> 24) & 0xFF)+
String.fromCharCode((this.text.length >> 16) & 0xFF)+
String.fromCharCode((this.text.length >> 8) & 0xFF)+
String.fromCharCode((this.text.length) & 0xFF)+
this.text;
if (this.certificationSignatures[i].verify(signaturedata, signingKey)) {
result[i] = 4;
} else
result[i] = 0;
} else if (this.certificationSignatures[i].version == 3) {
if (this.certificationSignatures[i].keyId == null) {
result[i] = 0;
continue;
}
var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].keyId);
if (issuerPublicKey == null || issuerPublicKey.length == 0) {
result[i] = 2;
continue;
}
issuerPublicKey = issuerPublicKey[0];
var signingKey = publicKey.obj.getSigningKey();
if (signingKey == null) {
result[i] = 0;
continue;
}
var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].keyId);
if (revocation != null && revocation.creationTime >
this.certificationSignatures[i].creationTime) {
var signaturedata = String.fromCharCode(0x99)+ this.publicKeyPacket.header.substring(1)+
this.publicKeyPacket.data+this.text;
if (revocation.verify(signaturedata, signingKey)) {
if (revocation.keyId == publicKeyPacket.getKeyId())
result[i] = 6;
else
result[i] = 3;
continue;
}
}
var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+
publicKeyPacket.data+this.text;
if (this.certificationSignatures[i].verify(signaturedata, signingKey)) {
result[i] = 4;
} else
result[i] = 0;
} else {
result[i] = 0;
}
}
return result;
}
/**
* verifies the signatures of the user id
* @return 0 if the userid is valid; 1 = userid expired; 2 = userid revoked
*/
function verify(publicKeyPacket) {
var result = this.verifyCertificationSignatures(publicKeyPacket);
if (result.indexOf(6) != -1)
return 2;
if (result.indexOf(5) != -1)
return 1;
return 0;
}
// TODO: implementation missing
function addCertification(publicKeyPacket, privateKeyPacket) {
}
// TODO: implementation missing
function revokeCertification(publicKeyPacket, privateKeyPacket) {
}
this.hasCertificationRevocationSignature = hasCertificationRevocationSignature;
this.verifyCertificationSignatures = verifyCertificationSignatures;
this.verify = verify;
this.read_packet = read_packet;
this.write_packet = write_packet;
this.toString = toString;
this.read_nodes = read_nodes;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Parent openpgp packet class. Operations focus on determining packet types
* and packet header.
*/
function _openpgp_packet() {
/**
* Encodes a given integer of length to the openpgp length specifier to a
* string
*
* @param {Integer} length of the length to encode
* @return {string} string with openpgp length representation
*/
function encode_length(length) {
result = "";
if (length < 192) {
result += String.fromCharCode(length);
} else if (length > 191 && length < 8384) {
/*
* let a = (total data packet length) - 192 let bc = two octet
* representation of a let d = b + 192
*/
result += String.fromCharCode(((length - 192) >> 8) + 192);
result += String.fromCharCode((length - 192) & 0xFF);
} else {
result += String.fromCharCode(255);
result += String.fromCharCode((length >> 24) & 0xFF);
result += String.fromCharCode((length >> 16) & 0xFF);
result += String.fromCharCode((length >> 8) & 0xFF);
result += String.fromCharCode(length & 0xFF);
}
return result;
}
this.encode_length = encode_length;
/**
* Writes a packet header version 4 with the given tag_type and length to a
* string
*
* @param {integer} tag_type tag type
* @param {integer} length length of the payload
* @return {string} string of the header
*/
function write_packet_header(tag_type, length) {
/* we're only generating v4 packet headers here */
var result = "";
result += String.fromCharCode(0xC0 | tag_type);
result += encode_length(length);
return result;
}
/**
* Writes a packet header Version 3 with the given tag_type and length to a
* string
*
* @param {integer} tag_type tag type
* @param {integer} length length of the payload
* @return {string} string of the header
*/
function write_old_packet_header(tag_type, length) {
var result = "";
if (length < 256) {
result += String.fromCharCode(0x80 | (tag_type << 2));
result += String.fromCharCode(length);
} else if (length < 65536) {
result += String.fromCharCode(0x80 | (tag_type << 2) | 1);
result += String.fromCharCode(length >> 8);
result += String.fromCharCode(length & 0xFF);
} else {
result += String.fromCharCode(0x80 | (tag_type << 2) | 2);
result += String.fromCharCode((length >> 24) & 0xFF);
result += String.fromCharCode((length >> 16) & 0xFF);
result += String.fromCharCode((length >> 8) & 0xFF);
result += String.fromCharCode(length & 0xFF);
}
return result;
}
this.write_old_packet_header = write_old_packet_header;
this.write_packet_header = write_packet_header;
/**
* Generic static Packet Parser function
*
* @param {String} input input stream as string
* @param {integer} position position to start parsing
* @param {integer} len length of the input from position on
* @return {openpgp_packet_*} returns a parsed openpgp_packet
*/
function read_packet(input, position, len) {
// some sanity checks
if (input == null || input.length <= position
|| input.substring(position).length < 2
|| (input[position].charCodeAt() & 0x80) == 0) {
util
.print_error("Error during parsing. This message / key is probably not containing a valid OpenPGP format.");
return null;
}
var mypos = position;
var tag = -1;
var format = -1;
format = 0; // 0 = old format; 1 = new format
if ((input[mypos].charCodeAt() & 0x40) != 0) {
format = 1;
}
var packet_length_type;
if (format) {
// new format header
tag = input[mypos].charCodeAt() & 0x3F; // bit 5-0
} else {
// old format header
tag = (input[mypos].charCodeAt() & 0x3F) >> 2; // bit 5-2
packet_length_type = input[mypos].charCodeAt() & 0x03; // bit 1-0
}
// header octet parsing done
mypos++;
// parsed length from length field
var bodydata = null;
// used for partial body lengths
var real_packet_length = -1;
if (!format) {
// 4.2.1. Old Format Packet Lengths
switch (packet_length_type) {
case 0: // The packet has a one-octet length. The header is 2 octets
// long.
packet_length = input[mypos++].charCodeAt();
break;
case 1: // The packet has a two-octet length. The header is 3 octets
// long.
packet_length = (input[mypos++].charCodeAt() << 8)
| input[mypos++].charCodeAt();
break;
case 2: // The packet has a four-octet length. The header is 5
// octets long.
packet_length = (input[mypos++].charCodeAt() << 24)
| (input[mypos++].charCodeAt() << 16)
| (input[mypos++].charCodeAt() << 8)
| input[mypos++].charCodeAt();
break;
default:
// 3 - The packet is of indeterminate length. The header is 1
// octet long, and the implementation must determine how long
// the packet is. If the packet is in a file, this means that
// the packet extends until the end of the file. In general,
// an implementation SHOULD NOT use indeterminate-length
// packets except where the end of the data will be clear
// from the context, and even then it is better to use a
// definite length, or a new format header. The new format
// headers described below have a mechanism for precisely
// encoding data of indeterminate length.
packet_length = len;
break;
}
} else // 4.2.2. New Format Packet Lengths
{
// 4.2.2.1. One-Octet Lengths
if (input[mypos].charCodeAt() < 192) {
packet_length = input[mypos++].charCodeAt();
util.print_debug("1 byte length:" + packet_length);
// 4.2.2.2. Two-Octet Lengths
} else if (input[mypos].charCodeAt() >= 192
&& input[mypos].charCodeAt() < 224) {
packet_length = ((input[mypos++].charCodeAt() - 192) << 8)
+ (input[mypos++].charCodeAt()) + 192;
util.print_debug("2 byte length:" + packet_length);
// 4.2.2.4. Partial Body Lengths
} else if (input[mypos].charCodeAt() > 223
&& input[mypos].charCodeAt() < 255) {
packet_length = 1 << (input[mypos++].charCodeAt() & 0x1F);
util.print_debug("4 byte length:" + packet_length);
// EEEK, we're reading the full data here...
var mypos2 = mypos + packet_length;
bodydata = input.substring(mypos, mypos + packet_length);
while (true) {
if (input[mypos2].charCodeAt() < 192) {
var tmplen = input[mypos2++].charCodeAt();
packet_length += tmplen;
bodydata += input.substring(mypos2, mypos2 + tmplen);
mypos2 += tmplen;
break;
} else if (input[mypos2].charCodeAt() >= 192
&& input[mypos2].charCodeAt() < 224) {
var tmplen = ((input[mypos2++].charCodeAt() - 192) << 8)
+ (input[mypos2++].charCodeAt()) + 192;
packet_length += tmplen;
bodydata += input.substring(mypos2, mypos2 + tmplen);
mypos2 += tmplen;
break;
} else if (input[mypos2].charCodeAt() > 223
&& input[mypos2].charCodeAt() < 255) {
var tmplen = 1 << (input[mypos2++].charCodeAt() & 0x1F);
packet_length += tmplen;
bodydata += input.substring(mypos2, mypos2 + tmplen);
mypos2 += tmplen;
} else {
mypos2++;
var tmplen = (input[mypos2++].charCodeAt() << 24)
| (input[mypos2++].charCodeAt() << 16)
| (input[mypos2++].charCodeAt() << 8)
| input[mypos2++].charCodeAt();
bodydata += input.substring(mypos2, mypos2 + tmplen);
packet_length += tmplen;
mypos2 += tmplen;
break;
}
}
real_packet_length = mypos2;
// 4.2.2.3. Five-Octet Lengths
} else {
mypos++;
packet_length = (input[mypos++].charCodeAt() << 24)
| (input[mypos++].charCodeAt() << 16)
| (input[mypos++].charCodeAt() << 8)
| input[mypos++].charCodeAt();
}
}
// if there was'nt a partial body length: use the specified
// packet_length
if (real_packet_length == -1) {
real_packet_length = packet_length;
}
if (bodydata == null) {
bodydata = input.substring(mypos, mypos + real_packet_length);
}
// alert('tag type: '+this.tag+' length: '+packet_length);
var version = 1; // (old format; 2= new format)
// if (input[mypos++].charCodeAt() > 15)
// version = 2;
switch (tag) {
case 0: // Reserved - a packet tag MUST NOT have this value
break;
case 1: // Public-Key Encrypted Session Key Packet
var result = new openpgp_packet_encryptedsessionkey();
if (result.read_pub_key_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 2: // Signature Packet
var result = new openpgp_packet_signature();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 3: // Symmetric-Key Encrypted Session Key Packet
var result = new openpgp_packet_encryptedsessionkey();
if (result.read_symmetric_key_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 4: // One-Pass Signature Packet
var result = new openpgp_packet_onepasssignature();
if (result.read_packet(bodydata, 0, packet_length)) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 5: // Secret-Key Packet
var result = new openpgp_packet_keymaterial();
result.header = input.substring(position, mypos);
if (result.read_tag5(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 6: // Public-Key Packet
var result = new openpgp_packet_keymaterial();
result.header = input.substring(position, mypos);
if (result.read_tag6(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 7: // Secret-Subkey Packet
var result = new openpgp_packet_keymaterial();
if (result.read_tag7(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 8: // Compressed Data Packet
var result = new openpgp_packet_compressed();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 9: // Symmetrically Encrypted Data Packet
var result = new openpgp_packet_encrypteddata();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 10: // Marker Packet = PGP (0x50, 0x47, 0x50)
var result = new openpgp_packet_marker();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 11: // Literal Data Packet
var result = new openpgp_packet_literaldata();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.header = input.substring(position, mypos);
result.packetLength = real_packet_length;
return result;
}
break;
case 12: // Trust Packet
// TODO: to be implemented
break;
case 13: // User ID Packet
var result = new openpgp_packet_userid();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 14: // Public-Subkey Packet
var result = new openpgp_packet_keymaterial();
result.header = input.substring(position, mypos);
if (result.read_tag14(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 17: // User Attribute Packet
var result = new openpgp_packet_userattribute();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 18: // Sym. Encrypted and Integrity Protected Data Packet
var result = new openpgp_packet_encryptedintegrityprotecteddata();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
case 19: // Modification Detection Code Packet
var result = new openpgp_packet_modificationdetectioncode();
if (result.read_packet(bodydata, 0, packet_length) != null) {
result.headerLength = mypos - position;
result.packetLength = real_packet_length;
return result;
}
break;
default:
util.print_error("openpgp.packet.js\n"
+ "[ERROR] openpgp_packet: failed to parse packet @:"
+ mypos + "\nchar:'"
+ util.hexstrdump(input.substring(mypos)) + "'\ninput:"
+ util.hexstrdump(input));
return null;
break;
}
}
this.read_packet = read_packet;
}
var openpgp_packet = new _openpgp_packet();
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18)
*
* RFC4880 5.13: The Symmetrically Encrypted Integrity Protected Data packet is
* a variant of the Symmetrically Encrypted Data packet. It is a new feature
* created for OpenPGP that addresses the problem of detecting a modification to
* encrypted data. It is used in combination with a Modification Detection Code
* packet.
*/
function openpgp_packet_encryptedintegrityprotecteddata() {
this.tagType = 18;
this.version = null; // integer == 1
this.packetLength = null; // integer
this.encryptedData = null; // string
this.decrytpedData = null; // string
this.hash = null; // string
/**
* parsing function for the packet.
*
* @param {string} input payload of a tag 18 packet
* @param {integer} position
* position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encryptedintegrityprotecteddata} object
* representation
*/
function read_packet(input, position, len) {
this.packetLength = len;
// - A one-octet version number. The only currently defined value is
// 1.
this.version = input[position].charCodeAt();
if (this.version != 1) {
util
.print_error('openpgp.packet.encryptedintegrityprotecteddata.js\nunknown encrypted integrity protected data packet version: '
+ this.version
+ " , @ "
+ position
+ "hex:"
+ util.hexstrdump(input));
return null;
}
// - Encrypted data, the output of the selected symmetric-key cipher
// operating in Cipher Feedback mode with shift amount equal to the
// block size of the cipher (CFB-n where n is the block size).
this.encryptedData = input.substring(position + 1, position + 1 + len);
util.print_debug("openpgp.packet.encryptedintegrityprotecteddata.js\n"
+ this.toString());
return this;
}
/**
* Creates a string representation of a Sym. Encrypted Integrity Protected
* Data Packet (tag 18) (see RFC4880 5.13)
*
* @param {integer} symmetric_algorithm
* the selected symmetric encryption algorithm to be used
* @param {String} key the key of cipher blocksize length to be used
* @param data
* plaintext data to be encrypted within the packet
* @return a string representation of the packet
*/
function write_packet(symmetric_algorithm, key, data) {
var prefixrandom = openpgp_crypto_getPrefixRandom(symmetric_algorithm);
var prefix = prefixrandom
+ prefixrandom.charAt(prefixrandom.length - 2)
+ prefixrandom.charAt(prefixrandom.length - 1);
var tohash = data;
tohash += String.fromCharCode(0xD3);
tohash += String.fromCharCode(0x14);
util.print_debug_hexstr_dump("data to be hashed:"
, prefix + tohash);
tohash += str_sha1(prefix + tohash);
util.print_debug_hexstr_dump("hash:"
, tohash.substring(tohash.length - 20,
tohash.length));
var result = openpgp_crypto_symmetricEncrypt(prefixrandom,
symmetric_algorithm, key, tohash, false).substring(0,
prefix.length + tohash.length);
var header = openpgp_packet.write_packet_header(18, result.length + 1)
+ String.fromCharCode(1);
this.encryptedData = result;
return header + result;
}
/**
* Decrypts the encrypted data contained in this object read_packet must
* have been called before
*
* @param {integer} symmetric_algorithm_type
* the selected symmetric encryption algorithm to be used
* @param {String} key the key of cipher blocksize length to be used
* @return the decrypted data of this packet
*/
function decrypt(symmetric_algorithm_type, key) {
this.decryptedData = openpgp_crypto_symmetricDecrypt(
symmetric_algorithm_type, key, this.encryptedData, false);
// there must be a modification detection code packet as the
// last packet and everything gets hashed except the hash itself
this.hash = str_sha1(openpgp_crypto_MDCSystemBytes(
symmetric_algorithm_type, key, this.encryptedData)
+ this.decryptedData.substring(0,
this.decryptedData.length - 20));
util.print_debug_hexstr_dump("calc hash = ", this.hash);
if (this.hash == this.decryptedData.substring(
this.decryptedData.length - 20, this.decryptedData.length))
return this.decryptedData;
else
util
.print_error("Decryption stopped: discovered a modification of encrypted data.");
return null;
}
function toString() {
var data = '';
if(openpgp.config.debug)
data = ' data: Bytes ['
+ util.hexstrdump(this.encryptedData) + ']';
return '5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n'
+ ' length: '
+ this.packetLength
+ '\n'
+ ' version: '
+ this.version
+ '\n'
+ data;
}
this.write_packet = write_packet;
this.read_packet = read_packet;
this.toString = toString;
this.decrypt = decrypt;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the One-Pass Signature Packets (Tag 4)
*
* RFC4880 5.4:
* The One-Pass Signature packet precedes the signed data and contains
* enough information to allow the receiver to begin calculating any
* hashes needed to verify the signature. It allows the Signature
* packet to be placed at the end of the message, so that the signer
* can compute the entire signed message in one pass.
*/
function openpgp_packet_onepasssignature() {
this.tagType = 4;
this.version = null; // A one-octet version number. The current version is 3.
this.type = null; // A one-octet signature type. Signature types are described in RFC4880 Section 5.2.1.
this.hashAlgorithm = null; // A one-octet number describing the hash algorithm used. (See RFC4880 9.4)
this.publicKeyAlgorithm = null; // A one-octet number describing the public-key algorithm used. (See RFC4880 9.1)
this.signingKeyId = null; // An eight-octet number holding the Key ID of the signing key.
this.flags = null; // A one-octet number holding a flag showing whether the signature is nested. A zero value indicates that the next packet is another One-Pass Signature packet that describes another signature to be applied to the same message data.
/**
* parsing function for a one-pass signature packet (tag 4).
* @param {string} input payload of a tag 4 packet
* @param {integer} position position to start reading from the input string
* @param {integer} len length of the packet or the remaining length of input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
this.packetLength = len;
var mypos = position;
// A one-octet version number. The current version is 3.
this.version = input.charCodeAt(mypos++);
// A one-octet signature type. Signature types are described in
// Section 5.2.1.
this.type = input.charCodeAt(mypos++);
// A one-octet number describing the hash algorithm used.
this.hashAlgorithm = input.charCodeAt(mypos++);
// A one-octet number describing the public-key algorithm used.
this.publicKeyAlgorithm = input.charCodeAt(mypos++);
// An eight-octet number holding the Key ID of the signing key.
this.signingKeyId = new openpgp_type_keyid();
this.signingKeyId.read_packet(input,mypos);
mypos += 8;
// A one-octet number holding a flag showing whether the signature
// is nested. A zero value indicates that the next packet is
// another One-Pass Signature packet that describes another
// signature to be applied to the same message data.
this.flags = input.charCodeAt(mypos++);
return this;
}
/**
* creates a string representation of a one-pass signature packet
* @param {integer} type Signature types as described in RFC4880 Section 5.2.1.
* @param {integer} hashalgorithm the hash algorithm used within the signature
* @param {openpgp_msg_privatekey} privatekey the private key used to generate the signature
* @param {integer} length length of data to be signed
* @param {boolean} nested boolean showing whether the signature is nested.
* "true" indicates that the next packet is another One-Pass Signature packet
* that describes another signature to be applied to the same message data.
* @return {String} a string representation of a one-pass signature packet
*/
function write_packet(type, hashalgorithm, privatekey,length, nested) {
var result ="";
result += openpgp_packet.write_packet_header(4,13);
result += String.fromCharCode(3);
result += String.fromCharCode(type);
result += String.fromCharCode(hashalgorithm);
result += String.fromCharCode(privatekey.privateKeyPacket.publicKey.publicKeyAlgorithm);
result += privatekey.getKeyId();
if (nested)
result += String.fromCharCode(0);
else
result += String.fromCharCode(1);
return result;
}
/**
* generates debug output (pretty print)
* @return {string} String which gives some information about the one-pass signature packet
*/
function toString() {
return '5.4. One-Pass Signature Packets (Tag 4)\n'+
' length: '+this.packetLength+'\n'+
' type: '+this.type+'\n'+
' keyID: '+this.signingKeyId.toString()+'\n'+
' hashA: '+this.hashAlgorithm+'\n'+
' pubKeyA:'+this.publicKeyAlgorithm+'\n'+
' flags: '+this.flags+'\n'+
' version:'+this.version+'\n';
}
this.read_packet = read_packet;
this.toString = toString;
this.write_packet = write_packet;
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @class
* @classdesc Implementation of the Literal Data Packet (Tag 11)
*
* RFC4880 5.9: A Literal Data packet contains the body of a message; data that
* is not to be further interpreted.
*/
function openpgp_packet_literaldata() {
this.tagType = 11;
/**
* parsing function for a literal data packet (tag 11).
*
* @param {string} input payload of a tag 11 packet
* @param {integer} position
* position to start reading from the input string
* @param {integer} len
* length of the packet or the remaining length of
* input at position
* @return {openpgp_packet_encrypteddata} object representation
*/
function read_packet(input, position, len) {
this.packetLength = len;
// - A one-octet field that describes how the data is formatted.
this.format = input[position];
this.filename = input.substr(position + 2, input
.charCodeAt(position + 1));
this.date = new Date(parseInt(input.substr(position + 2
+ input.charCodeAt(position + 1), 4)) * 1000);
this.data = input.substring(position + 6
+ input.charCodeAt(position + 1));
return this;
}
/**
* Creates a string representation of the packet
*
* @param {String} data the data to be inserted as body
* @return {String} string-representation of the packet
*/
function write_packet(data) {
data = data.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n");
this.filename = "msg.txt";
this.date = new Date();
this.format = 't';
var result = openpgp_packet.write_packet_header(11, data.length + 6
+ this.filename.length);
result += this.format;
result += String.fromCharCode(this.filename.length);
result += this.filename;
result += String
.fromCharCode((Math.round(this.date.getTime() / 1000) >> 24) & 0xFF);
result += String
.fromCharCode((Math.round(this.date.getTime() / 1000) >> 16) & 0xFF);
result += String
.fromCharCode((Math.round(this.date.getTime() / 1000) >> 8) & 0xFF);
result += String
.fromCharCode(Math.round(this.date.getTime() / 1000) & 0xFF);
result += data;
this.data = data;
return result;
}
/**
* generates debug output (pretty print)
*
* @return {string} String which gives some information about the keymaterial
*/
function toString() {
return '5.9. Literal Data Packet (Tag 11)\n' + ' length: '
+ this.packetLength + '\n' + ' format: ' + this.format
+ '\n' + ' filename:' + this.filename + '\n'
+ ' date: ' + this.date + '\n' + ' data: |'
+ this.data + '|\n' + ' rdata: |' + this.real_data + '|\n';
}
this.read_packet = read_packet;
this.toString = toString;
this.write_packet = write_packet;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @protected
* @class
* @classdesc Top-level message object. Contains information from one or more packets
*/
function openpgp_msg_message() {
// -1 = no valid passphrase submitted
// -2 = no private key found
// -3 = decryption error
// text = valid decryption
this.text = "";
/**
* Decrypts a message and generates user interface message out of the found.
* MDC will be verified as well as message signatures
* @param {openpgp_msg_privatekey} private_key the private the message is encrypted with (corresponding to the session key)
* @param {openpgp_packet_encryptedsessionkey} sessionkey the session key to be used to decrypt the message
* @return {String} plaintext of the message or null on error
*/
function decrypt(private_key, sessionkey) {
return this.decryptAndVerifySignature(private_key, sessionkey).text;
}
/**
* Decrypts a message and generates user interface message out of the found.
* MDC will be verified as well as message signatures
* @param {openpgp_msg_privatekey} private_key the private the message is encrypted with (corresponding to the session key)
* @param {openpgp_packet_encryptedsessionkey} sessionkey the session key to be used to decrypt the message
* @param {openpgp_msg_publickey} pubkey Array of public keys to check signature against. If not provided, checks local keystore.
* @return {String} plaintext of the message or null on error
*/
function decryptAndVerifySignature(private_key, sessionkey, pubkey) {
if (private_key == null || sessionkey == null || sessionkey == "")
return null;
var decrypted = sessionkey.decrypt(this, private_key.keymaterial);
if (decrypted == null)
return null;
var packet;
var position = 0;
var len = decrypted.length;
var validSignatures = new Array();
util.print_debug_hexstr_dump("openpgp.msg.messge decrypt:\n",decrypted);
while (position != decrypted.length && (packet = openpgp_packet.read_packet(decrypted, position, len)) != null) {
if (packet.tagType == 8) {
this.text = packet.decompress();
decrypted = packet.decompress();
}
util.print_debug(packet.toString());
position += packet.headerLength+packet.packetLength;
if (position > 38)
util.print_debug_hexstr_dump("openpgp.msg.messge decrypt:\n",decrypted.substring(position));
len = decrypted.length - position;
if (packet.tagType == 11) {
this.text = packet.data;
util.print_info("message successfully decrypted");
}
if (packet.tagType == 19)
// ignore.. we checked that already in a more strict way.
continue;
if (packet.tagType == 2 && packet.signatureType < 3) {
if(!pubkey || pubkey.length == 0 ){
var pubkey = openpgp.keyring.getPublicKeysForKeyId(packet.issuerKeyId);
}
if (pubkey.length == 0) {
util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(packet.issuerKeyId)+". Public key not found in keyring.");
validSignatures[validSignatures.length] = false;
} else {
if(packet.verify(this.text.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"),pubkey[0]) && pubkey[0].obj.validate()){
util.print_info("Found Good Signature from "+pubkey[0].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")");
validSignatures[validSignatures.length] = true;
}
else{
util.print_error("Signature verification failed: Bad Signature from "+pubkey[0].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")");
validSignatures[validSignatures.length] = false;
}
}
}
}
if (this.text == "") {
this.text = decrypted;
}
return {text:this.text, validSignatures:validSignatures};
}
/**
* Verifies a message signature. This function can be called after read_message if the message was signed only.
* @param {openpgp_msg_publickey} pubkey Array of public keys to check signature against. If not provided, checks local keystore.
* @return {boolean} true if the signature was correct; otherwise false
*/
function verifySignature(pubkey) {
var result = false;
if (this.type == 2) {
if(!pubkey || pubkey.length == 0){
var pubkey;
if (this.signature.version == 4) {
pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.issuerKeyId);
} else if (this.signature.version == 3) {
pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.keyId);
} else {
util.print_error("unknown signature type on message!");
return false;
}
}
if (pubkey.length == 0)
util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(this.signature.issuerKeyId)+". Public key not found in keyring.");
else {
for (var i = 0 ; i < pubkey.length; i++) {
var tohash = this.text.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n");
if (this.signature.verify(tohash.substring(0, tohash.length -2), pubkey[i])) {
util.print_info("Found Good Signature from "+pubkey[i].obj.userIds[i].text+" (0x"+util.hexstrdump(pubkey[i].obj.getKeyId()).substring(8)+")");
result = true;
} else {
util.print_error("Signature verification failed: Bad Signature from "+pubkey[i].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")");
}
}
}
}
return result;
}
function toString() {
var result = "Session Keys:\n";
if (this.sessionKeys !=null)
for (var i = 0; i < this.sessionKeys.length; i++) {
result += this.sessionKeys[i].toString();
}
result += "\n\n EncryptedData:\n";
if(this.encryptedData != null)
result += this.encryptedData.toString();
result += "\n\n Signature:\n";
if(this.signature != null)
result += this.signature.toString();
result += "\n\n Text:\n"
if(this.signature != null)
result += this.text;
return result;
}
this.decrypt = decrypt;
this.decryptAndVerifySignature = decryptAndVerifySignature;
this.verifySignature = verifySignature;
this.toString = toString;
}
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @fileoverview The openpgp base class should provide all of the functionality
* to consume the openpgp.js library. All additional classes are documented
* for extending and developing on top of the base library.
*/
/**
* GPG4Browsers Core interface. A single instance is hold
* from the beginning. To use this library call "openpgp.init()"
* @alias openpgp
* @class
* @classdesc Main Openpgp.js class. Use this to initiate and make all calls to this library.
*/
function _openpgp () {
this.tostring = "";
/**
* initializes the library:
* - reading the keyring from local storage
* - reading the config from local storage
* @return [void]
*/
function init() {
this.config = new openpgp_config();
this.config.read();
this.keyring = new openpgp_keyring();
this.keyring.init();
}
/**
* reads several publicKey objects from a ascii armored
* representation an returns openpgp_msg_publickey packets
* @param {String} armoredText OpenPGP armored text containing
* the public key(s)
* @return {Array[openpgp_msg_publickey]} on error the function
* returns null
*/
function read_publicKey(armoredText) {
var mypos = 0;
var publicKeys = new Array();
var publicKeyCount = 0;
var input = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')).openpgp;
var l = input.length;
while (mypos != input.length) {
var first_packet = openpgp_packet.read_packet(input, mypos, l);
// public key parser
if (input[mypos].charCodeAt() == 0x99 || first_packet.tagType == 6) {
publicKeys[publicKeyCount] = new openpgp_msg_publickey();
publicKeys[publicKeyCount].header = input.substring(mypos,mypos+3);
if (input[mypos].charCodeAt() == 0x99) {
// parse the length and read a tag6 packet
mypos++;
var l = (input[mypos++].charCodeAt() << 8)
| input[mypos++].charCodeAt();
publicKeys[publicKeyCount].publicKeyPacket = new openpgp_packet_keymaterial();
publicKeys[publicKeyCount].publicKeyPacket.header = publicKeys[publicKeyCount].header;
publicKeys[publicKeyCount].publicKeyPacket.read_tag6(input, mypos, l);
mypos += publicKeys[publicKeyCount].publicKeyPacket.packetLength;
mypos += publicKeys[publicKeyCount].read_nodes(publicKeys[publicKeyCount].publicKeyPacket, input, mypos, (input.length - mypos));
} else {
publicKeys[publicKeyCount] = new openpgp_msg_publickey();
publicKeys[publicKeyCount].publicKeyPacket = first_packet;
mypos += first_packet.headerLength+first_packet.packetLength;
mypos += publicKeys[publicKeyCount].read_nodes(first_packet, input, mypos, input.length -mypos);
}
} else {
util.print_error("no public key found!");
return null;
}
publicKeys[publicKeyCount].data = input.substring(0,mypos);
publicKeyCount++;
}
return publicKeys;
}
/**
* reads several privateKey objects from a ascii armored
* representation an returns openpgp_msg_privatekey objects
* @param {String} armoredText OpenPGP armored text containing
* the private key(s)
* @return {Array[openpgp_msg_privatekey]} on error the function
* returns null
*/
function read_privateKey(armoredText) {
var privateKeys = new Array();
var privateKeyCount = 0;
var mypos = 0;
var input = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')).openpgp;
var l = input.length;
while (mypos != input.length) {
var first_packet = openpgp_packet.read_packet(input, mypos, l);
if (first_packet.tagType == 5) {
privateKeys[privateKeys.length] = new openpgp_msg_privatekey();
mypos += first_packet.headerLength+first_packet.packetLength;
mypos += privateKeys[privateKeyCount].read_nodes(first_packet, input, mypos, l);
// other blocks
} else {
util.print_error('no block packet found!');
return null;
}
privateKeys[privateKeyCount].data = input.substring(0,mypos);
privateKeyCount++;
}
return privateKeys;
}
/**
* reads message packets out of an OpenPGP armored text and
* returns an array of message objects
* @param {String} armoredText text to be parsed
* @return {Array[openpgp_msg_message]} on error the function
* returns null
*/
function read_message(armoredText) {
var dearmored;
try{
dearmored = openpgp_encoding_deArmor(armoredText.replace(/\r/g,''));
}
catch(e){
util.print_error('no message found!');
return null;
}
return read_messages_dearmored(dearmored);
}
function read_messages_dearmored(input){
var messageString = input.openpgp;
var messages = new Array();
var messageCount = 0;
var mypos = 0;
var l = messageString.length;
while (mypos < messageString.length) {
var first_packet = openpgp_packet.read_packet(messageString, mypos, l);
if (!first_packet) {
break;
}
// public key parser (definition from the standard:)
// OpenPGP Message :- Encrypted Message | Signed Message |
// Compressed Message | Literal Message.
// Compressed Message :- Compressed Data Packet.
//
// Literal Message :- Literal Data Packet.
//
// ESK :- Public-Key Encrypted Session Key Packet |
// Symmetric-Key Encrypted Session Key Packet.
//
// ESK Sequence :- ESK | ESK Sequence, ESK.
//
// Encrypted Data :- Symmetrically Encrypted Data Packet |
// Symmetrically Encrypted Integrity Protected Data Packet
//
// Encrypted Message :- Encrypted Data | ESK Sequence, Encrypted Data.
//
// One-Pass Signed Message :- One-Pass Signature Packet,
// OpenPGP Message, Corresponding Signature Packet.
// Signed Message :- Signature Packet, OpenPGP Message |
// One-Pass Signed Message.
if (first_packet.tagType == 1 ||
(first_packet.tagType == 2 && first_packet.signatureType < 16) ||
first_packet.tagType == 3 ||
first_packet.tagType == 4 ||
first_packet.tagType == 8 ||
first_packet.tagType == 9 ||
first_packet.tagType == 10 ||
first_packet.tagType == 11 ||
first_packet.tagType == 18 ||
first_packet.tagType == 19) {
messages[messages.length] = new openpgp_msg_message();
messages[messageCount].messagePacket = first_packet;
messages[messageCount].type = input.type;
// Encrypted Message
if (first_packet.tagType == 9 ||
first_packet.tagType == 1 ||
first_packet.tagType == 3 ||
first_packet.tagType == 18) {
if (first_packet.tagType == 9) {
util.print_error("unexpected openpgp packet");
break;
} else if (first_packet.tagType == 1) {
util.print_debug("session key found:\n "+first_packet.toString());
var issessionkey = true;
messages[messageCount].sessionKeys = new Array();
var sessionKeyCount = 0;
while (issessionkey) {
messages[messageCount].sessionKeys[sessionKeyCount] = first_packet;
mypos += first_packet.packetLength + first_packet.headerLength;
l -= (first_packet.packetLength + first_packet.headerLength);
first_packet = openpgp_packet.read_packet(messageString, mypos, l);
if (first_packet.tagType != 1 && first_packet.tagType != 3)
issessionkey = false;
sessionKeyCount++;
}
if (first_packet.tagType == 18 || first_packet.tagType == 9) {
util.print_debug("encrypted data found:\n "+first_packet.toString());
messages[messageCount].encryptedData = first_packet;
mypos += first_packet.packetLength+first_packet.headerLength;
l -= (first_packet.packetLength+first_packet.headerLength);
messageCount++;
} else {
util.print_debug("something is wrong: "+first_packet.tagType);
}
} else if (first_packet.tagType == 18) {
util.print_debug("symmetric encrypted data");
break;
}
} else
// Signed Message
if (first_packet.tagType == 2 && first_packet.signatureType < 3) {
messages[messageCount].text = input.text;
messages[messageCount].signature = first_packet;
break;
} else
// Signed Message
if (first_packet.tagType == 4) {
//TODO: Implement check
mypos += first_packet.packetLength + first_packet.headerLength;
l -= (first_packet.packetLength + first_packet.headerLength);
} else
// Compressed Message
// TODO: needs to be implemented. From a security perspective: this message is plaintext anyway.
// This has been implemented as part of processing. Check openpgp.packet.
if (first_packet.tagType == 8) {
util.print_error("A directly compressed message is currently not supported");
break;
} else
// Marker Packet (Obsolete Literal Packet) (Tag 10)
// "Such a packet MUST be ignored when received." see http://tools.ietf.org/html/rfc4880#section-5.8
if (first_packet.tagType == 10) {
// reset messages
messages.length = 0;
// continue with next packet
mypos += first_packet.packetLength + first_packet.headerLength;
l -= (first_packet.packetLength + first_packet.headerLength);
} else
if (first_packet.tagType == 11) {
// Literal Message -- work is already done in read_packet
mypos += first_packet.packetLength + first_packet.headerLength;
l -= (first_packet.packetLength + first_packet.headerLength);
messages[messageCount].data = first_packet.data;
messageCount++;
}
} else {
util.print_error('no message found!');
return null;
}
}
return messages;
}
/**
* creates a binary string representation of an encrypted and signed message.
* The message will be encrypted with the public keys specified and signed
* with the specified private key.
* @param {obj: [openpgp_msg_privatekey]} privatekey private key to be used to sign the message
* @param {Array {obj: [openpgp_msg_publickey]}} publickeys public keys to be used to encrypt the message
* @param {String} messagetext message text to encrypt and sign
* @return {String} a binary string representation of the message which can be OpenPGP armored
*/
function write_signed_and_encrypted_message(privatekey, publickeys, messagetext) {
var result = "";
var literal = new openpgp_packet_literaldata().write_packet(messagetext.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"));
util.print_debug_hexstr_dump("literal_packet: |"+literal+"|\n",literal);
for (var i = 0; i < publickeys.length; i++) {
var onepasssignature = new openpgp_packet_onepasssignature();
var onepasssigstr = "";
if (i == 0)
onepasssigstr = onepasssignature.write_packet(1, openpgp.config.config.prefer_hash_algorithm, privatekey, false);
else
onepasssigstr = onepasssignature.write_packet(1, openpgp.config.config.prefer_hash_algorithm, privatekey, false);
util.print_debug_hexstr_dump("onepasssigstr: |"+onepasssigstr+"|\n",onepasssigstr);
var datasignature = new openpgp_packet_signature().write_message_signature(1, messagetext.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"), privatekey);
util.print_debug_hexstr_dump("datasignature: |"+datasignature.openpgp+"|\n",datasignature.openpgp);
if (i == 0) {
result = onepasssigstr+literal+datasignature.openpgp;
} else {
result = onepasssigstr+result+datasignature.openpgp;
}
}
util.print_debug_hexstr_dump("signed packet: |"+result+"|\n",result);
// signatures done.. now encryption
var sessionkey = openpgp_crypto_generateSessionKey(openpgp.config.config.encryption_cipher);
var result2 = "";
// creating session keys for each recipient
for (var i = 0; i < publickeys.length; i++) {
var pkey = publickeys[i].getEncryptionKey();
if (pkey == null) {
util.print_error("no encryption key found! Key is for signing only.");
return null;
}
result2 += new openpgp_packet_encryptedsessionkey().
write_pub_key_packet(
pkey.getKeyId(),
pkey.MPIs,
pkey.publicKeyAlgorithm,
openpgp.config.config.encryption_cipher,
sessionkey);
}
if (openpgp.config.config.integrity_protect) {
result2 += new openpgp_packet_encryptedintegrityprotecteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result);
} else {
result2 += new openpgp_packet_encrypteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result);
}
return openpgp_encoding_armor(3,result2,null,null);
}
/**
* creates a binary string representation of an encrypted message.
* The message will be encrypted with the public keys specified
* @param {Array {obj: [openpgp_msg_publickey]}} publickeys public
* keys to be used to encrypt the message
* @param {String} messagetext message text to encrypt
* @return {String} a binary string representation of the message
* which can be OpenPGP armored
*/
function write_encrypted_message(publickeys, messagetext) {
var result = "";
var literal = new openpgp_packet_literaldata().write_packet(messagetext.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"));
util.print_debug_hexstr_dump("literal_packet: |"+literal+"|\n",literal);
result = literal;
// signatures done.. now encryption
var sessionkey = openpgp_crypto_generateSessionKey(openpgp.config.config.encryption_cipher);
var result2 = "";
// creating session keys for each recipient
for (var i = 0; i < publickeys.length; i++) {
var pkey = publickeys[i].getEncryptionKey();
if (pkey == null) {
util.print_error("no encryption key found! Key is for signing only.");
return null;
}
result2 += new openpgp_packet_encryptedsessionkey().
write_pub_key_packet(
pkey.getKeyId(),
pkey.MPIs,
pkey.publicKeyAlgorithm,
openpgp.config.config.encryption_cipher,
sessionkey);
}
if (openpgp.config.config.integrity_protect) {
result2 += new openpgp_packet_encryptedintegrityprotecteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result);
} else {
result2 += new openpgp_packet_encrypteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result);
}
return openpgp_encoding_armor(3,result2,null,null);
}
/**
* creates a binary string representation a signed message.
* The message will be signed with the specified private key.
* @param {obj: [openpgp_msg_privatekey]} privatekey private
* key to be used to sign the message
* @param {String} messagetext message text to sign
* @return {Object: text [String]}, openpgp: {String} a binary
* string representation of the message which can be OpenPGP
* armored(openpgp) and a text representation of the message (text). This can be directly used to OpenPGP armor the message
*/
function write_signed_message(privatekey, messagetext) {
var sig = new openpgp_packet_signature().write_message_signature(1, messagetext.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"), privatekey);
var result = {text: messagetext.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"), openpgp: sig.openpgp, hash: sig.hash};
return openpgp_encoding_armor(2,result, null, null)
}
/**
* generates a new key pair for openpgp. Beta stage. Currently only supports RSA keys, and no subkeys.
* @param {int} keyType to indicate what type of key to make. RSA is 1. Follows algorithms outlined in OpenPGP.
* @param {int} numBits number of bits for the key creation. (should be 1024+, generally)
* @param {string} userId assumes already in form of "User Name "
* @return {privateKey: [openpgp_msg_privatekey], privateKeyArmored: [string], publicKeyArmored: [string]}
*/
function generate_key_pair(keyType, numBits, userId, passphrase){
var userIdPacket = new openpgp_packet_userid();
var userIdString = userIdPacket.write_packet(userId);
var keyPair = openpgp_crypto_generateKeyPair(keyType,numBits, passphrase, openpgp.config.config.prefer_hash_algorithm, 3);
var privKeyString = keyPair.privateKey;
var privKeyPacket = new openpgp_packet_keymaterial().read_priv_key(privKeyString.string,3,privKeyString.string.length);
if(!privKeyPacket.decryptSecretMPIs(passphrase))
util.print_error('Issue creating key. Unable to read resulting private key');
var privKey = new openpgp_msg_privatekey();
privKey.privateKeyPacket = privKeyPacket;
privKey.getPreferredSignatureHashAlgorithm = function(){return openpgp.config.config.prefer_hash_algorithm};//need to override this to solve catch 22 to generate signature. 8 is value for SHA256
var publicKeyString = privKey.privateKeyPacket.publicKey.data;
var hashData = String.fromCharCode(0x99)+ String.fromCharCode(((publicKeyString.length) >> 8) & 0xFF)
+ String.fromCharCode((publicKeyString.length) & 0xFF) +publicKeyString+String.fromCharCode(0xB4) +
String.fromCharCode((userId.length) >> 24) +String.fromCharCode(((userId.length) >> 16) & 0xFF)
+ String.fromCharCode(((userId.length) >> 8) & 0xFF) + String.fromCharCode((userId.length) & 0xFF) + userId
var signature = new openpgp_packet_signature();
signature = signature.write_message_signature(16,hashData, privKey);
var publicArmored = openpgp_encoding_armor(4, keyPair.publicKey.string + userIdString + signature.openpgp );
var privArmored = openpgp_encoding_armor(5,privKeyString.string+userIdString+signature.openpgp);
return {privateKey : privKey, privateKeyArmored: privArmored, publicKeyArmored: publicArmored}
}
this.generate_key_pair = generate_key_pair;
this.write_signed_message = write_signed_message;
this.write_signed_and_encrypted_message = write_signed_and_encrypted_message;
this.write_encrypted_message = write_encrypted_message;
this.read_message = read_message;
this.read_messages_dearmored = read_messages_dearmored;
this.read_publicKey = read_publicKey;
this.read_privateKey = read_privateKey;
this.init = init;
}
var openpgp = new _openpgp();
JXG = {exists: (function(undefined){return function(v){return !(v===undefined || v===null);}})()};
JXG.decompress = function(str) {return unescape((new JXG.Util.Unzip(JXG.Util.Base64.decodeAsArray(str))).unzip()[0][0]);};
/*
Copyright 2008-2012
Matthias Ehmann,
Michael Gerhaeuser,
Carsten Miller,
Bianca Valentin,
Alfred Wassermann,
Peter Wilfahrt
This file is part of JSXGraph.
Dual licensed under the Apache License Version 2.0, or LGPL Version 3 licenses.
You should have received a copy of the GNU Lesser General Public License
along with JSXCompressor. If not, see .
You should have received a copy of the Apache License along with JSXCompressor.
If not, see .
*/
/**
* @class Util class
* @classdesc Utilities for uncompressing and base64 decoding
* Class for gunzipping, unzipping and base64 decoding of files.
* It is used for reading GEONExT, Geogebra and Intergeo files.
*
* Only Huffman codes are decoded in gunzip.
* The code is based on the source code for gunzip.c by Pasi Ojala
* @see http://www.cs.tut.fi/~albert/Dev/gunzip/gunzip.c
* @see http://www.cs.tut.fi/~albert
*/
JXG.Util = {};
/**
* Unzip zip files
*/
JXG.Util.Unzip = function (barray){
var outputArr = [],
output = "",
debug = false,
gpflags,
files = 0,
unzipped = [],
crc,
buf32k = new Array(32768),
bIdx = 0,
modeZIP=false,
CRC, SIZE,
bitReverse = [
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
],
cplens = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
],
cplext = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
], /* 99==invalid */
cpdist = [
0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d,
0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1,
0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01,
0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001
],
cpdext = [
0, 0, 0, 0, 1, 1, 2, 2,
3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
],
border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15],
bA = barray,
bytepos=0,
bitpos=0,
bb = 1,
bits=0,
NAMEMAX = 256,
nameBuf = [],
fileout;
function readByte(){
bits+=8;
if (bytepos");
return bA[bytepos++];
} else
return -1;
};
function byteAlign(){
bb = 1;
};
function readBit(){
var carry;
bits++;
carry = (bb & 1);
bb >>= 1;
if (bb==0){
bb = readByte();
carry = (bb & 1);
bb = (bb>>1) | 0x80;
}
return carry;
};
function readBits(a) {
var res = 0,
i = a;
while(i--) {
res = (res<<1) | readBit();
}
if(a) {
res = bitReverse[res]>>(8-a);
}
return res;
};
function flushBuffer(){
//document.write('FLUSHBUFFER:'+buf32k);
bIdx = 0;
};
function addBuffer(a){
SIZE++;
//CRC=updcrc(a,crc);
buf32k[bIdx++] = a;
outputArr.push(String.fromCharCode(a));
//output+=String.fromCharCode(a);
if(bIdx==0x8000){
//document.write('ADDBUFFER:'+buf32k);
bIdx=0;
}
};
function HufNode() {
this.b0=0;
this.b1=0;
this.jump = null;
this.jumppos = -1;
};
var LITERALS = 288;
var literalTree = new Array(LITERALS);
var distanceTree = new Array(32);
var treepos=0;
var Places = null;
var Places2 = null;
var impDistanceTree = new Array(64);
var impLengthTree = new Array(64);
var len = 0;
var fpos = new Array(17);
fpos[0]=0;
var flens;
var fmax;
function IsPat() {
while (1) {
if (fpos[len] >= fmax)
return -1;
if (flens[fpos[len]] == len)
return fpos[len]++;
fpos[len]++;
}
};
function Rec() {
var curplace = Places[treepos];
var tmp;
if (debug)
document.write("
len:"+len+" treepos:"+treepos);
if(len==17) { //war 17
return -1;
}
treepos++;
len++;
tmp = IsPat();
if (debug)
document.write("
IsPat "+tmp);
if(tmp >= 0) {
curplace.b0 = tmp; /* leaf cell for 0-bit */
if (debug)
document.write("
b0 "+curplace.b0);
} else {
/* Not a Leaf cell */
curplace.b0 = 0x8000;
if (debug)
document.write("
b0 "+curplace.b0);
if(Rec())
return -1;
}
tmp = IsPat();
if(tmp >= 0) {
curplace.b1 = tmp; /* leaf cell for 1-bit */
if (debug)
document.write("
b1 "+curplace.b1);
curplace.jump = null; /* Just for the display routine */
} else {
/* Not a Leaf cell */
curplace.b1 = 0x8000;
if (debug)
document.write("
b1 "+curplace.b1);
curplace.jump = Places[treepos];
curplace.jumppos = treepos;
if(Rec())
return -1;
}
len--;
return 0;
};
function CreateTree(currentTree, numval, lengths, show) {
var i;
/* Create the Huffman decode tree/table */
//document.write("
createtree
");
if (debug)
document.write("currentTree "+currentTree+" numval "+numval+" lengths "+lengths+" show "+show);
Places = currentTree;
treepos=0;
flens = lengths;
fmax = numval;
for (i=0;i<17;i++)
fpos[i] = 0;
len = 0;
if(Rec()) {
//fprintf(stderr, "invalid huffman tree\n");
if (debug)
alert("invalid huffman tree\n");
return -1;
}
if (debug){
document.write('
Tree: '+Places.length);
for (var a=0;a<32;a++){
document.write("Places["+a+"].b0="+Places[a].b0+"
");
document.write("Places["+a+"].b1="+Places[a].b1+"
");
}
}
/*if(show) {
var tmp;
for(tmp=currentTree;tmpjump?tmp->jump-currentTree:0,(tmp->jump?tmp->jump-currentTree:0)*6+0xcf0);
if(!(tmp.b0 & 0x8000)) {
//fprintf(stdout, " 0x%03x (%c)", tmp->b0,(tmp->b0<256 && isprint(tmp->b0))?tmp->b0:'�');
}
if(!(tmp.b1 & 0x8000)) {
if((tmp.b0 & 0x8000))
fprintf(stdout, " ");
fprintf(stdout, " 0x%03x (%c)", tmp->b1,(tmp->b1<256 && isprint(tmp->b1))?tmp->b1:'�');
}
fprintf(stdout, "\n");
}
}*/
return 0;
};
function DecodeValue(currentTree) {
var len, i,
xtreepos=0,
X = currentTree[xtreepos],
b;
/* decode one symbol of the data */
while(1) {
b=readBit();
if (debug)
document.write("b="+b);
if(b) {
if(!(X.b1 & 0x8000)){
if (debug)
document.write("ret1");
return X.b1; /* If leaf node, return data */
}
X = X.jump;
len = currentTree.length;
for (i=0;i>1);
if(j > 23) {
j = (j<<1) | readBit(); /* 48..255 */
if(j > 199) { /* 200..255 */
j -= 128; /* 72..127 */
j = (j<<1) | readBit(); /* 144..255 << */
} else { /* 48..199 */
j -= 48; /* 0..151 */
if(j > 143) {
j = j+136; /* 280..287 << */
/* 0..143 << */
}
}
} else { /* 0..23 */
j += 256; /* 256..279 << */
}
if(j < 256) {
addBuffer(j);
//document.write("out:"+String.fromCharCode(j));
/*fprintf(errfp, "@%d %02x\n", SIZE, j);*/
} else if(j == 256) {
/* EOF */
break;
} else {
var len, dist;
j -= 256 + 1; /* bytes + EOF */
len = readBits(cplext[j]) + cplens[j];
j = bitReverse[readBits(5)]>>3;
if(cpdext[j] > 8) {
dist = readBits(8);
dist |= (readBits(cpdext[j]-8)<<8);
} else {
dist = readBits(cpdext[j]);
}
dist += cpdist[j];
/*fprintf(errfp, "@%d (l%02x,d%04x)\n", SIZE, len, dist);*/
for(j=0;jparam: "+literalCodes+" "+distCodes+" "+lenCodes+"
");
for(j=0; j<19; j++) {
ll[j] = 0;
}
// Get the decode tree code lengths
//document.write("
");
for(j=0; jll:'+ll);
len = distanceTree.length;
for (i=0; idistanceTree");
for(var a=0;a"+distanceTree[a].b0+" "+distanceTree[a].b1+" "+distanceTree[a].jump+" "+distanceTree[a].jumppos);
/*if (distanceTree[a].jumppos!=-1)
document.write(" "+distanceTree[a].jump.b0+" "+distanceTree[a].jump.b1);
*/
}
}
//document.write('
tree created');
//read in literal and distance code lengths
n = literalCodes + distCodes;
i = 0;
var z=-1;
if (debug)
document.write("
n="+n+" bits: "+bits+"
");
while(i < n) {
z++;
j = DecodeValue(distanceTree);
if (debug)
document.write("
"+z+" i:"+i+" decode: "+j+" bits "+bits+"
");
if(j<16) { // length of code in bits (0..15)
ll[i++] = j;
} else if(j==16) { // repeat last length 3 to 6 times
var l;
j = 3 + readBits(2);
if(i+j > n) {
flushBuffer();
return 1;
}
l = i ? ll[i-1] : 0;
while(j--) {
ll[i++] = l;
}
} else {
if(j==17) { // 3 to 10 zero length codes
j = 3 + readBits(3);
} else { // j == 18: 11 to 138 zero length codes
j = 11 + readBits(7);
}
if(i+j > n) {
flushBuffer();
return 1;
}
while(j--) {
ll[i++] = 0;
}
}
}
/*for(j=0; jliteralTree");
outer:
while(1) {
j = DecodeValue(literalTree);
if(j >= 256) { // In C64: if carry set
var len, dist;
j -= 256;
if(j == 0) {
// EOF
break;
}
j--;
len = readBits(cplext[j]) + cplens[j];
j = DecodeValue(distanceTree);
if(cpdext[j] > 8) {
dist = readBits(8);
dist |= (readBits(cpdext[j]-8)<<8);
} else {
dist = readBits(cpdext[j]);
}
dist += cpdist[j];
while(len--) {
if(bIdx - dist < 0) {
break outer;
}
var c = buf32k[(bIdx - dist) & 0x7fff];
addBuffer(c);
}
} else {
addBuffer(j);
}
}
}
} while(!last);
flushBuffer();
byteAlign();
return 0;
};
JXG.Util.Unzip.prototype.unzipFile = function(name) {
var i;
this.unzip();
//alert(unzipped[0][1]);
for (i=0;i");
}
*/
//alert(bA);
nextFile();
return unzipped;
};
function nextFile(){
if (debug)
alert("NEXTFILE");
outputArr = [];
var tmp = [];
modeZIP = false;
tmp[0] = readByte();
tmp[1] = readByte();
if (debug)
alert("type: "+tmp[0]+" "+tmp[1]);
if (tmp[0] == parseInt("78",16) && tmp[1] == parseInt("da",16)){ //GZIP
if (debug)
alert("GEONExT-GZIP");
DeflateLoop();
if (debug)
alert(outputArr.join(''));
unzipped[files] = new Array(2);
unzipped[files][0] = outputArr.join('');
unzipped[files][1] = "geonext.gxt";
files++;
}
if (tmp[0] == parseInt("78",16) && tmp[1] == parseInt("9c",16)){ //ZLIB
if (debug)
alert("ZLIB");
DeflateLoop();
if (debug)
alert(outputArr.join(''));
unzipped[files] = new Array(2);
unzipped[files][0] = outputArr.join('');
unzipped[files][1] = "ZLIB";
files++;
}
if (tmp[0] == parseInt("1f",16) && tmp[1] == parseInt("8b",16)){ //GZIP
if (debug)
alert("GZIP");
//DeflateLoop();
skipdir();
if (debug)
alert(outputArr.join(''));
unzipped[files] = new Array(2);
unzipped[files][0] = outputArr.join('');
unzipped[files][1] = "file";
files++;
}
if (tmp[0] == parseInt("50",16) && tmp[1] == parseInt("4b",16)){ //ZIP
modeZIP = true;
tmp[2] = readByte();
tmp[3] = readByte();
if (tmp[2] == parseInt("3",16) && tmp[3] == parseInt("4",16)){
//MODE_ZIP
tmp[0] = readByte();
tmp[1] = readByte();
if (debug)
alert("ZIP-Version: "+tmp[1]+" "+tmp[0]/10+"."+tmp[0]%10);
gpflags = readByte();
gpflags |= (readByte()<<8);
if (debug)
alert("gpflags: "+gpflags);
var method = readByte();
method |= (readByte()<<8);
if (debug)
alert("method: "+method);
readByte();
readByte();
readByte();
readByte();
var crc = readByte();
crc |= (readByte()<<8);
crc |= (readByte()<<16);
crc |= (readByte()<<24);
var compSize = readByte();
compSize |= (readByte()<<8);
compSize |= (readByte()<<16);
compSize |= (readByte()<<24);
var size = readByte();
size |= (readByte()<<8);
size |= (readByte()<<16);
size |= (readByte()<<24);
if (debug)
alert("local CRC: "+crc+"\nlocal Size: "+size+"\nlocal CompSize: "+compSize);
var filelen = readByte();
filelen |= (readByte()<<8);
var extralen = readByte();
extralen |= (readByte()<<8);
if (debug)
alert("filelen "+filelen);
i = 0;
nameBuf = [];
while (filelen--){
var c = readByte();
if (c == "/" | c ==":"){
i = 0;
} else if (i < NAMEMAX-1)
nameBuf[i++] = String.fromCharCode(c);
}
if (debug)
alert("nameBuf: "+nameBuf);
//nameBuf[i] = "\0";
if (!fileout)
fileout = nameBuf;
var i = 0;
while (i < extralen){
c = readByte();
i++;
}
CRC = 0xffffffff;
SIZE = 0;
if (size = 0 && fileOut.charAt(fileout.length-1)=="/"){
//skipdir
if (debug)
alert("skipdir");
}
if (method == 8){
DeflateLoop();
if (debug)
alert(outputArr.join(''));
unzipped[files] = new Array(2);
unzipped[files][0] = outputArr.join('');
unzipped[files][1] = nameBuf.join('');
files++;
//return outputArr.join('');
}
skipdir();
}
}
};
function skipdir(){
var crc,
tmp = [],
compSize, size, os, i, c;
if ((gpflags & 8)) {
tmp[0] = readByte();
tmp[1] = readByte();
tmp[2] = readByte();
tmp[3] = readByte();
if (tmp[0] == parseInt("50",16) &&
tmp[1] == parseInt("4b",16) &&
tmp[2] == parseInt("07",16) &&
tmp[3] == parseInt("08",16))
{
crc = readByte();
crc |= (readByte()<<8);
crc |= (readByte()<<16);
crc |= (readByte()<<24);
} else {
crc = tmp[0] | (tmp[1]<<8) | (tmp[2]<<16) | (tmp[3]<<24);
}
compSize = readByte();
compSize |= (readByte()<<8);
compSize |= (readByte()<<16);
compSize |= (readByte()<<24);
size = readByte();
size |= (readByte()<<8);
size |= (readByte()<<16);
size |= (readByte()<<24);
if (debug)
alert("CRC:");
}
if (modeZIP)
nextFile();
tmp[0] = readByte();
if (tmp[0] != 8) {
if (debug)
alert("Unknown compression method!");
return 0;
}
gpflags = readByte();
if (debug){
if ((gpflags & ~(parseInt("1f",16))))
alert("Unknown flags set!");
}
readByte();
readByte();
readByte();
readByte();
readByte();
os = readByte();
if ((gpflags & 4)){
tmp[0] = readByte();
tmp[2] = readByte();
len = tmp[0] + 256*tmp[1];
if (debug)
alert("Extra field size: "+len);
for (i=0;ihttp://www.webtoolkit.info/
*/
JXG.Util.Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
// public method for encoding
encode : function (input) {
var output = [],
chr1, chr2, chr3, enc1, enc2, enc3, enc4,
i = 0;
input = JXG.Util.Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output.push([this._keyStr.charAt(enc1),
this._keyStr.charAt(enc2),
this._keyStr.charAt(enc3),
this._keyStr.charAt(enc4)].join(''));
}
return output.join('');
},
// public method for decoding
decode : function (input, utf8) {
var output = [],
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(String.fromCharCode(chr1));
if (enc3 != 64) {
output.push(String.fromCharCode(chr2));
}
if (enc4 != 64) {
output.push(String.fromCharCode(chr3));
}
}
output = output.join('');
if (utf8) {
output = JXG.Util.Base64._utf8_decode(output);
}
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = [],
i = 0,
c = 0, c2 = 0, c3 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string.push(String.fromCharCode(c));
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
i += 3;
}
}
return string.join('');
},
_destrip: function (stripped, wrap){
var lines = [], lineno, i,
destripped = [];
if (wrap==null)
wrap = 76;
stripped.replace(/ /g, "");
lineno = stripped.length / wrap;
for (i = 0; i < lineno; i++)
lines[i]=stripped.substr(i * wrap, wrap);
if (lineno != stripped.length / wrap)
lines[lines.length]=stripped.substr(lineno * wrap, stripped.length-(lineno * wrap));
for (i = 0; i < lines.length; i++)
destripped.push(lines[i]);
return destripped.join('\n');
},
decodeAsArray: function (input){
var dec = this.decode(input),
ar = [], i;
for (i=0;i255){
switch (c) {
case 8364: c=128;
break;
case 8218: c=130;
break;
case 402: c=131;
break;
case 8222: c=132;
break;
case 8230: c=133;
break;
case 8224: c=134;
break;
case 8225: c=135;
break;
case 710: c=136;
break;
case 8240: c=137;
break;
case 352: c=138;
break;
case 8249: c=139;
break;
case 338: c=140;
break;
case 381: c=142;
break;
case 8216: c=145;
break;
case 8217: c=146;
break;
case 8220: c=147;
break;
case 8221: c=148;
break;
case 8226: c=149;
break;
case 8211: c=150;
break;
case 8212: c=151;
break;
case 732: c=152;
break;
case 8482: c=153;
break;
case 353: c=154;
break;
case 8250: c=155;
break;
case 339: c=156;
break;
case 382: c=158;
break;
case 376: c=159;
break;
default:
break;
}
}
return c;
};
/**
* Decoding string into utf-8
* @param {String} string to decode
* @return {String} utf8 decoded string
*/
JXG.Util.utf8Decode = function(utftext) {
var string = [];
var i = 0;
var c = 0, c1 = 0, c2 = 0, c3;
if (!JXG.exists(utftext)) return '';
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string.push(String.fromCharCode(c));
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string.push(String.fromCharCode(((c & 31) << 6) | (c2 & 63)));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string.push(String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)));
i += 3;
}
};
return string.join('');
};
/**
* Generate a random uuid.
* http://www.broofa.com
* mailto:robert@broofa.com
*
* Copyright (c) 2010 Robert Kieffer
* Dual licensed under the MIT and GPL licenses.
*
* EXAMPLES:
* >>> Math.uuid()
* "92329D39-6F5C-4520-ABFC-AAB64544E172"
*/
JXG.Util.genUUID = function() {
// Private array of chars to use
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''),
uuid = new Array(36), rnd=0, r;
for (var i = 0; i < 36; i++) {
if (i==8 || i==13 || i==18 || i==23) {
uuid[i] = '-';
} else if (i==14) {
uuid[i] = '4';
} else {
if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
r = rnd & 0xf;
rnd = rnd >> 4;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
return uuid.join('');
};
// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
var Util = function() {
this.emailRegEx = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
this.hexdump = function(str) {
var r=[];
var e=str.length;
var c=0;
var h;
var i = 0;
while(c'
* @param str [String] string of the debug message
* @return [String] an HTML tt entity containing a paragraph with a style attribute where the debug message is HTMLencoded in.
*/
this.print_debug = function(str) {
if (openpgp.config.debug) {
str = openpgp_encoding_html_encode(str);
showMessages(""+str.replace(/\n/g,"
")+"
");
}
};
/**
* Helper function to print a debug message. Debug
* messages are only printed if
* openpgp.config.debug is set to true. The calling
* Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '
'
* Different than print_debug because will call hexstrdump iff necessary.
* @param str [String] string of the debug message
* @return [String] an HTML tt entity containing a paragraph with a style attribute where the debug message is HTMLencoded in.
*/
this.print_debug_hexstr_dump = function(str,strToHex) {
if (openpgp.config.debug) {
str = str + this.hexstrdump(strToHex);
str = openpgp_encoding_html_encode(str);
showMessages(""+str.replace(/\n/g,"
")+"
");
}
};
/**
* Helper function to print an error message.
* The calling Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '
'
* @param str [String] string of the error message
* @return [String] a HTML paragraph entity with a style attribute containing the HTML encoded error message
*/
this.print_error = function(str) {
str = openpgp_encoding_html_encode(str);
showMessages("ERROR: "+str.replace(/\n/g,"
")+"
");
};
/**
* Helper function to print an info message.
* The calling Javascript context MUST define
* a "showMessages(text)" function. Line feeds ('\n')
* are automatically converted to HTML line feeds '
'.
* @param str [String] string of the info message
* @return [String] a HTML paragraph entity with a style attribute containing the HTML encoded info message
*/
this.print_info = function(str) {
str = openpgp_encoding_html_encode(str);
showMessages("INFO: "+str.replace(/\n/g,"
")+"
");
};
this.print_warning = function(str) {
str = openpgp_encoding_html_encode(str);
showMessages("WARNING: "+str.replace(/\n/g,"
")+"
");
};
this.getLeftNBits = function (string, bitcount) {
var rest = bitcount % 8;
if (rest == 0)
return string.substring(0, bitcount / 8);
var bytes = (bitcount - rest) / 8 +1;
var result = string.substring(0, bytes);
return this.shiftRight(result, 8-rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF);
};
/**
* Shifting a string to n bits right
* @param value [String] the string to shift
* @param bitcount [Integer] amount of bits to shift (MUST be smaller than 9)
* @return [String] resulting string.
*/
this.shiftRight = function(value, bitcount) {
var temp = util.str2bin(value);
if (bitcount % 8 != 0) {
for (var i = temp.length-1; i >= 0; i--) {
temp[i] >>= bitcount % 8;
if (i > 0)
temp[i] |= (temp[i - 1] << (8 - (bitcount % 8))) & 0xFF;
}
} else {
return value;
}
return util.bin2str(temp);
};
/**
* Return the algorithm type as string
* @return [String] String representing the message type
*/
this.get_hashAlgorithmString = function(algo) {
switch(algo) {
case 1:
return "MD5";
case 2:
return "SHA1";
case 3:
return "RIPEMD160";
case 8:
return "SHA256";
case 9:
return "SHA384";
case 10:
return "SHA512";
case 11:
return "SHA224";
}
return "unknown";
};
};
/**
* an instance that should be used.
*/
var util = new Util();