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
//
// 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
//
// 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
//
// 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
// The GPG4Browsers crypto interface
/**
* Encrypts data using the specified public key multiprecision integers
* and the specified algorithm.
* @param algo [Integer] Algorithm to be used (See RFC4880 9.1)
* @param publicMPIs [Array[openpgp_type_mpi]] algorithm dependent multiprecision integers
* @param data [openpgp_type_mpi] data to be encrypted as MPI
* @return [Object] if RSA an openpgp_type_mpi; if elgamal encryption an array of two
* openpgp_type_mpi is returned; otherwise null
*/
function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) {
switch(algo) {
case 1: // RSA (Encrypt or Sign) [HAC]
case 2: // RSA Encrypt-Only [HAC]
case 3: // RSA Sign-Only [HAC]
var rsa = new RSA();
var n = publicMPIs[0].toBigInteger();
var e = publicMPIs[1].toBigInteger();
var m = data.toBigInteger();
return rsa.encrypt(m,e,n).toMPI();
case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
var elgamal = new Elgamal();
var p = publicMPIs[0].toBigInteger();
var g = publicMPIs[1].toBigInteger();
var y = publicMPIs[2].toBigInteger();
var m = data.toBigInteger();
return elgamal.encrypt(m,g,p,y);
default:
return null;
}
}
/**
* Decrypts data using the specified public key multiprecision integers of the private key,
* the specified secretMPIs of the private key and the specified algorithm.
* @param algo [Integer] Algorithm to be used (See RFC4880 9.1)
* @param publicMPIs [Array[openpgp_type_mpi]] algorithm dependent multiprecision integers of the public key part of the private key
* @param secretMPIs [Array[openpgp_type_mpi]] algorithm dependent multiprecision integers of the private key used
* @param data [openpgp_type_mpi] data to be encrypted as MPI
* @return [BigInteger] returns a big integer containing the decrypted data; otherwise null
*/
function openpgp_crypto_asymetricDecrypt(algo, publicMPIs, secretMPIs, dataMPIs) {
switch(algo) {
case 1: // RSA (Encrypt or Sign) [HAC]
case 2: // RSA Encrypt-Only [HAC]
case 3: // RSA Sign-Only [HAC]
var rsa = new RSA();
var d = secretMPIs[0].toBigInteger();
var p = secretMPIs[1].toBigInteger();
var q = secretMPIs[2].toBigInteger();
var u = secretMPIs[3].toBigInteger();
var m = dataMPIs[0].toBigInteger();
return rsa.decrypt(m, d, p, q, u);
case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
var elgamal = new Elgamal();
var x = secretMPIs[0].toBigInteger();
var c1 = dataMPIs[0].toBigInteger();
var c2 = dataMPIs[1].toBigInteger();
var p = publicMPIs[0].toBigInteger();
return elgamal.decrypt(c1,c2,p,x);
default:
return null;
}
}
/**
* generate random byte prefix as string for the specified algorithm
* @param algo [Integer] algorithm to use (see RFC4880 9.2)
* @return [String] random bytes with length equal to the block
* size of the cipher
*/
function openpgp_crypto_getPrefixRandom(algo) {
switch(algo) {
case 2:
case 3:
case 4:
return openpgp_crypto_getRandomBytes(8);
case 7:
case 8:
case 9:
case 10:
return openpgp_crypto_getRandomBytes(16);
default:
return null;
}
}
/**
* retrieve the MDC prefixed bytes by decrypting them
* @param algo [Integer] algorithm to use (see RFC4880 9.2)
* @param key [String] key as string. length is depending on the algorithm used
* @param data [String] encrypted data where the prefix is decrypted from
* @return [String] plain text data of the prefixed data
*/
function openpgp_crypto_MDCSystemBytes(algo, key, data) {
util.print_debug_hexstr_dump("openpgp_crypto_symmetricDecrypt:\nencrypteddata:",data);
switch(algo) {
case 0: // Plaintext or unencrypted data
return data;
case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
return openpgp_cfb_mdc(desede, 8, key, data, openpgp_cfb);
case 3: // CAST5 (128 bit key, as per [RFC2144])
return openpgp_cfb_mdc(cast5_encrypt, 8, key, data);
case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH]
return openpgp_cfb_mdc(BFencrypt, 8, key, data);
case 7: // AES with 128-bit key [AES]
case 8: // AES with 192-bit key
case 9: // AES with 256-bit key
return openpgp_cfb_mdc(AESencrypt, 16, keyExpansion(key), data);
case 10:
return openpgp_cfb_mdc(TFencrypt, 16, key, data);
case 1: // IDEA [IDEA]
util.print_error(""+ (algo == 1 ? "IDEA Algorithm not implemented" : "Twofish Algorithm not implemented"));
return null;
default:
}
return null;
}
/**
* Generating a session key for the specified symmetric algorithm
* @param algo [Integer] algorithm to use (see RFC4880 9.2)
* @return [String] random bytes as a string to be used as a key
*/
function openpgp_crypto_generateSessionKey(algo) {
switch (algo) {
case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192)
case 8: // AES with 192-bit key
return openpgp_crypto_getRandomBytes(24);
case 3: // CAST5 (128 bit key, as per [RFC2144])
case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH]
case 7: // AES with 128-bit key [AES]
util.print_debug("length = 16:\n"+util.hexstrdump(openpgp_crypto_getRandomBytes(16)));
return openpgp_crypto_getRandomBytes(16);
case 9: // AES with 256-bit key
case 10:// Twofish with 256-bit key [TWOFISH]
return openpgp_crypto_getRandomBytes(32);
}
return null;
}
/**
*
* @param algo [Integer] public key algorithm
* @param hash_algo [Integer] hash algorithm
* @param msg_MPIs [Array[openpgp_type_mpi]] signature multiprecision integers
* @param publickey_MPIs [Array[openpgp_type_mpi]] public key multiprecision integers
* @param data [String] data on where the signature was computed on.
* @return true if signature (sig_data was equal to data over hash)
*/
function openpgp_crypto_verifySignature(algo, hash_algo, msg_MPIs, publickey_MPIs, data) {
var calc_hash = openpgp_crypto_hashData(hash_algo, data);
switch(algo) {
case 1: // RSA (Encrypt or Sign) [HAC]
case 2: // RSA Encrypt-Only [HAC]
case 3: // RSA Sign-Only [HAC]
var rsa = new RSA();
var n = publickey_MPIs[0].toBigInteger();
var e = publickey_MPIs[1].toBigInteger();
var x = msg_MPIs[0].toBigInteger();
var dopublic = rsa.verify(x,e,n);
var hash = openpgp_encoding_emsa_pkcs1_decode(hash_algo,dopublic.toMPI().substring(2));
if (hash == -1) {
util.print_error("PKCS1 padding in message or key incorrect. Aborting...");
return false;
}
return hash == calc_hash;
case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
util.print_error("signing with Elgamal is not defined in the OpenPGP standard.");
return null;
case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC]
var dsa = new DSA();
var s1 = msg_MPIs[0].toBigInteger();
var s2 = msg_MPIs[1].toBigInteger();
var p = publickey_MPIs[0].toBigInteger();
var q = publickey_MPIs[1].toBigInteger();
var g = publickey_MPIs[2].toBigInteger();
var y = publickey_MPIs[3].toBigInteger();
var m = data;
var dopublic = dsa.verify(hash_algo,s1,s2,m,p,q,g,y);
return dopublic.compareTo(s1) == 0;
default:
return null;
}
}
/**
* Create a signature on data using the specified algorithm
* @param hash_algo [Integer] hash algorithm to use (See RFC4880 9.4)
* @param algo [Integer] asymmetric cipher algorithm to use (See RFC4880 9.1)
* @param publicMPIs [Array[openpgp_type_mpi]] public key multiprecision integers of the private key
* @param secretMPIs [Array[openpgp_type_mpi]] private key multiprecision integers which is used to sign the data
* @param data [String] data to be signed
* @return [String or openpgp_type_mpi]
*/
function openpgp_crypto_signData(hash_algo, algo, publicMPIs, secretMPIs, data) {
switch(algo) {
case 1: // RSA (Encrypt or Sign) [HAC]
case 2: // RSA Encrypt-Only [HAC]
case 3: // RSA Sign-Only [HAC]
var rsa = new RSA();
var d = secretMPIs[0].toBigInteger();
var n = publicMPIs[0].toBigInteger();
var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, data,publicMPIs[0].mpiByteLength);
util.print_debug("signing using RSA");
return rsa.sign(m, d, n).toMPI();
case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC]
var dsa = new DSA();
util.print_debug("DSA Sign: q size in Bytes:"+publicMPIs[1].getByteLength());
var p = publicMPIs[0].toBigInteger();
var q = publicMPIs[1].toBigInteger();
var g = publicMPIs[2].toBigInteger();
var y = publicMPIs[3].toBigInteger();
var x = secretMPIs[0].toBigInteger();
var m = data;
var result = dsa.sign(hash_algo,m, g, p, q, x);
util.print_debug("signing using DSA\n result:"+util.hexstrdump(result[0])+"|"+util.hexstrdump(result[1]));
return result[0]+result[1];
case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
util.print_debug("signing with Elgamal is not defined in the OpenPGP standard.");
return null;
default:
return null;
}
}
/**
* create a hash on the specified data using the specified algorithm
* @param algo [Integer] hash algorithm type (see RFC4880 9.4)
* @param data [String] data to be hashed
* @return [String] hash value
*/
function openpgp_crypto_hashData(algo, data) {
var hash = null;
switch(algo) {
case 1: // - MD5 [HAC]
hash = MD5(data);
break;
case 2: // - SHA-1 [FIPS180]
hash = str_sha1(data);
break;
case 3: // - RIPE-MD/160 [HAC]
hash = RMDstring(data);
break;
case 8: // - SHA256 [FIPS180]
hash = str_sha256(data);
break;
case 9: // - SHA384 [FIPS180]
hash = str_sha384(data);
break;
case 10:// - SHA512 [FIPS180]
hash = str_sha512(data);
break;
case 11:// - SHA224 [FIPS180]
hash = str_sha224(data);
default:
break;
}
return hash;
}
/**
* returns the hash size in bytes of the specified hash algorithm type
* @param algo [Integer] hash algorithm type (See RFC4880 9.4)
* @return [Integer] size in bytes of the resulting hash
*/
function openpgp_crypto_getHashByteLength(algo) {
var hash = null;
switch(algo) {
case 1: // - MD5 [HAC]
return 16;
case 2: // - SHA-1 [FIPS180]
case 3: // - RIPE-MD/160 [HAC]
return 20;
case 8: // - SHA256 [FIPS180]
return 32;
case 9: // - SHA384 [FIPS180]
return 48
case 10:// - SHA512 [FIPS180]
return 64;
case 11:// - SHA224 [FIPS180]
return 28;
}
return null;
}
/**
* retrieve secure random byte string of the specified length
* @param length [Integer] length in bytes to generate
* @return [String] random byte string
*/
function openpgp_crypto_getRandomBytes(length) {
var result = '';
for (var i = 0; i < length; i++) {
result += String.fromCharCode(openpgp_crypto_getSecureRandomOctet());
}
return result;
}
/**
* return a pseudo-random number in the specified range
* @param from [Integer] min of the random number
* @param to [Integer] max of the random number (max 32bit)
* @return [Integer] a pseudo random number
*/
function openpgp_crypto_getPseudoRandom(from, to) {
return Math.round(Math.random()*(to-from))+from;
}
/**
* return a secure random number in the specified range
* @param from [Integer] min of the random number
* @param to [Integer] max of the random number (max 32bit)
* @return [Integer] a secure random number
*/
function openpgp_crypto_getSecureRandom(from, to) {
var buf = new Uint32Array(1);
window.crypto.getRandomValues(buf);
var bits = ((to-from)).toString(2).length;
while ((buf[0] & (Math.pow(2, bits) -1)) > (to-from))
window.crypto.getRandomValues(buf);
return from+(Math.abs(buf[0] & (Math.pow(2, bits) -1)));
}
function openpgp_crypto_getSecureRandomOctet() {
var buf = new Uint32Array(1);
window.crypto.getRandomValues(buf);
return buf[0] & 0xFF;
}
/**
* create a secure random big integer of bits length
* @param bits [Integer] bit length of the MPI to create
* @return [BigInteger] resulting big integer
*/
function openpgp_crypto_getRandomBigInteger(bits) {
if (bits < 0)
return null;
var numBytes = Math.floor((bits+7)/8);
var randomBits = openpgp_crypto_getRandomBytes(numBytes);
if (bits % 8 > 0) {
randomBits = String.fromCharCode(
(Math.pow(2,bits % 8)-1) &
randomBits.charCodeAt(0)) +
randomBits.substring(1);
}
return new openpgp_type_mpi().create(randomBits).toBigInteger();
}
function openpgp_crypto_getRandomBigIntegerInRange(min, max) {
if (max.compareTo(min) <= 0)
return;
var range = max.subtract(min);
var r = openpgp_crypto_getRandomBigInteger(range.bitLength());
while (r > range) {
r = openpgp_crypto_getRandomBigInteger(range.bitLength());
}
return min.add(r);
}
//This is a test method to ensure that encryption/decryption with a given 1024bit RSAKey object functions as intended
function openpgp_crypto_testRSA(key){
debugger;
var rsa = new RSA();
var mpi = new openpgp_type_mpi();
mpi.create(openpgp_encoding_eme_pkcs1_encode('ABABABAB', 128));
var msg = rsa.encrypt(mpi.toBigInteger(),key.ee,key.n);
var result = rsa.decrypt(msg, key.d, key.p, key.q, key.u);
}
/**
* calls the necessary crypto functions to generate a keypair. Called directly by openpgp.js
* @keyType [int] follows OpenPGP algorithm convention.
* @numBits [int] number of bits to make the key to be generated
* @return {privateKey: [openpgp_packet_keymaterial] , publicKey: [openpgp_packet_keymaterial]}
*/
function openpgp_crypto_generateKeyPair(keyType, numBits, passphrase, s2kHash, symmetricEncryptionAlgorithm){
var privKeyPacket;
var publicKeyPacket;
var d = new Date();
d = d.getTime()/1000;
var timePacket = String.fromCharCode(Math.floor(d/0x1000000%0x100)) + String.fromCharCode(Math.floor(d/0x10000%0x100)) + String.fromCharCode(Math.floor(d/0x100%0x100)) + String.fromCharCode(Math.floor(d%0x100));
switch(keyType){
case 1:
var rsa = new RSA();
var key = rsa.generate(numBits,"10001");
privKeyPacket = new openpgp_packet_keymaterial().write_private_key(keyType, key, passphrase, s2kHash, symmetricEncryptionAlgorithm, timePacket);
publicKeyPacket = new openpgp_packet_keymaterial().write_public_key(keyType, key, timePacket);
break;
default:
util.print_error("Unknown keytype "+keyType)
}
return {privateKey: privKeyPacket, publicKey: publicKeyPacket};
}
// 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 The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage.
*/
function openpgp_keyring() {
/**
* Initialization routine for the keyring. This method reads the
* keyring from HTML5 local storage and initializes this instance.
* This method is called by openpgp.init().
* @return {null} undefined
*/
function init() {
var sprivatekeys = JSON.parse(window.localStorage.getItem("privatekeys"));
var spublickeys = JSON.parse(window.localStorage.getItem("publickeys"));
if (sprivatekeys == null || sprivatekeys.length == 0) {
sprivatekeys = new Array();
}
if (spublickeys == null || spublickeys.length == 0) {
spublickeys = new Array();
}
this.publicKeys = new Array();
this.privateKeys = new Array();
var k = 0;
for (var i =0; i < sprivatekeys.length; i++) {
var r = openpgp.read_privateKey(sprivatekeys[i]);
this.privateKeys[k] = { armored: sprivatekeys[i], obj: r[0], keyId: r[0].getKeyId()};
k++;
}
k = 0;
for (var i =0; i < spublickeys.length; i++) {
var r = openpgp.read_publicKey(spublickeys[i]);
if (r[0] != null) {
this.publicKeys[k] = { armored: spublickeys[i], obj: r[0], keyId: r[0].getKeyId()};
k++;
}
}
}
this.init = init;
/**
* Checks if at least one private key is in the keyring
* @return {boolean} True if there are private keys, else false.
*/
function hasPrivateKey() {
return this.privateKeys.length > 0;
}
this.hasPrivateKey = hasPrivateKey;
/**
* Saves the current state of the keyring to HTML5 local storage.
* The privateKeys array and publicKeys array gets Stringified using JSON
* @return {null} undefined
*/
function store() {
var priv = new Array();
for (var i = 0; i < this.privateKeys.length; i++) {
priv[i] = this.privateKeys[i].armored;
}
var pub = new Array();
for (var i = 0; i < this.publicKeys.length; i++) {
pub[i] = this.publicKeys[i].armored;
}
window.localStorage.setItem("privatekeys",JSON.stringify(priv));
window.localStorage.setItem("publickeys",JSON.stringify(pub));
}
this.store = store;
/**
* searches all public keys in the keyring matching the address or address part of the user ids
* @param email_address
* @return {array[openpgp_msg_publickey]} the public keys associated with provided email address.
*/
function getPublicKeyForAddress(email_address) {
var results = new Array();
var spl = email_address.split("<");
var email = "";
if (spl.length > 1) {
email = spl[1].split(">")[0];
} else {
email = email_address.trim();
}
email = email.toLowerCase();
if(!util.emailRegEx.test(email)){
return results;
}
for (var i =0; i < this.publicKeys.length; i++) {
for (var j = 0; j < this.publicKeys[i].obj.userIds.length; j++) {
if (this.publicKeys[i].obj.userIds[j].text.toLowerCase().indexOf(email) >= 0)
results[results.length] = this.publicKeys[i];
}
}
return results;
}
this.getPublicKeyForAddress = getPublicKeyForAddress;
/**
* Searches the keyring for a private key containing the specified email address
* @param {String} email_address email address to search for
* @return {Array[openpgp_msg_privatekey} private keys found
*/
function getPrivateKeyForAddress(email_address) {
var results = new Array();
var spl = email_address.split("<");
var email = "";
if (spl.length > 1) {
email = spl[1].split(">")[0];
} else {
email = email_address.trim();
}
email = email.toLowerCase();
if(!util.emailRegEx.test(email)){
return results;
}
for (var i =0; i < this.privateKeys.length; i++) {
for (var j = 0; j < this.privateKeys[i].obj.userIds.length; j++) {
if (this.privateKeys[i].obj.userIds[j].text.toLowerCase().indexOf(email) >= 0)
results[results.length] = this.privateKeys[i];
}
}
return results;
}
this.getPrivateKeyForAddress = getPrivateKeyForAddress;
/**
* Searches the keyring for public keys having the specified key id
* @param keyId provided as string of hex number (lowercase)
* @return {Array[openpgp_msg_privatekey]} public keys found
*/
function getPublicKeysForKeyId(keyId) {
var result = new Array();
for (var i=0; i < this.publicKeys.length; i++) {
var key = this.publicKeys[i];
if (keyId == key.obj.getKeyId())
result[result.length] = key;
else if (key.obj.subKeys != null) {
for (var j=0; j < key.obj.subKeys.length; j++) {
var subkey = key.obj.subKeys[j];
if (keyId == subkey.getKeyId()) {
result[result.length] = {
obj: key.obj.getSubKeyAsKey(j),
keyId: subkey.getKeyId()
}
}
}
}
}
return result;
}
this.getPublicKeysForKeyId = getPublicKeysForKeyId;
/**
* Searches the keyring for private keys having the specified key id
* @param {String} keyId 8 bytes as string containing the key id to look for
* @return {Array[openpgp_msg_privatekey]} private keys found
*/
function getPrivateKeyForKeyId(keyId) {
var result = new Array();
for (var i=0; i < this.privateKeys.length; i++) {
if (keyId == this.privateKeys[i].obj.getKeyId()) {
result[result.length] = { key: this.privateKeys[i], keymaterial: this.privateKeys[i].obj.privateKeyPacket};
}
if (this.privateKeys[i].obj.subKeys != null) {
var subkeyids = this.privateKeys[i].obj.getSubKeyIds();
for (var j=0; j < subkeyids.length; j++)
if (keyId == util.hexstrdump(subkeyids[j])) {
result[result.length] = { key: this.privateKeys[i], keymaterial: this.privateKeys[i].obj.subKeys[j]};
}
}
}
return result;
}
this.getPrivateKeyForKeyId = getPrivateKeyForKeyId;
/**
* Imports a public key from an exported ascii armored message
* @param {String} armored_text PUBLIC KEY BLOCK message to read the public key from
* @return {null} nothing
*/
function importPublicKey (armored_text) {
var result = openpgp.read_publicKey(armored_text);
for (var i = 0; i < result.length; i++) {
this.publicKeys[this.publicKeys.length] = {armored: armored_text, obj: result[i], keyId: result[i].getKeyId()};
}
return true;
}
/**
* Imports a private key from an exported ascii armored message
* @param {String} armored_text PRIVATE KEY BLOCK message to read the private key from
* @return {null} nothing
*/
function importPrivateKey (armored_text, password) {
var result = openpgp.read_privateKey(armored_text);
if(!result[0].decryptSecretMPIs(password))
return false;
for (var i = 0; i < result.length; i++) {
this.privateKeys[this.privateKeys.length] = {armored: armored_text, obj: result[i], keyId: result[i].getKeyId()};
}
return true;
}
this.importPublicKey = importPublicKey;
this.importPrivateKey = importPrivateKey;
/**
* returns the openpgp_msg_privatekey representation of the public key at public key ring index
* @param {Integer} index the index of the public key within the publicKeys array
* @return {openpgp_msg_privatekey} the public key object
*/
function exportPublicKey(index) {
return this.publicKey[index];
}
this.exportPublicKey = exportPublicKey;
/**
* Removes a public key from the public key keyring at the specified index
* @param {Integer} index the index of the public key within the publicKeys array
* @return {openpgp_msg_privatekey} The public key object which has been removed
*/
function removePublicKey(index) {
var removed = this.publicKeys.splice(index,1);
this.store();
return removed;
}
this.removePublicKey = removePublicKey;
/**
* returns the openpgp_msg_privatekey representation of the private key at private key ring index
* @param {Integer} index the index of the private key within the privateKeys array
* @return {openpgp_msg_privatekey} the private key object
*/
function exportPrivateKey(index) {
return this.privateKeys[index];
}
this.exportPrivateKey = exportPrivateKey;
/**
* Removes a private key from the private key keyring at the specified index
* @param {Integer} index the index of the private key within the privateKeys array
* @return {openpgp_msg_privatekey} The private key object which has been removed
*/
function removePrivateKey(index) {
var removed = this.privateKeys.splice(index,1);
this.store();
return removed;
}
this.removePrivateKey = removePrivateKey;
}
// 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 Decoded public key object for internal openpgp.js use
*/
function openpgp_msg_publickey() {
this.data;
this.position;
this.len;
this.tostring = "OPENPGP PUBLIC KEY\n";
this.bindingSignature = null;
this.publicKeyPacket = null;
this.userIds = new Array();
this.userAttributes = new Array();
this.revocationSignatures = new Array();
this.subKeys = new Array();
this.arbitraryPacket = new Array();
this.directSignatures = new Array();
/**
*
* @return last position
*/
function read_nodes(parent_node, input, position, len) {
this.publicKeyPacket = parent_node;
var exit = false;
var pos = position;
var l = len;
while (input.length != pos) {
var result = openpgp_packet.read_packet(input, pos, input.length - pos);
if (result == null) {
util.print_error("openpgp.msg.publickey read_nodes:\n"+'[pub_key]parsing ends here @:' + pos + " l:" + l);
break;
} else {
switch (result.tagType) {
case 2: // public key revocation signature
if (result.signatureType == 32)
this.revocationSignatures[this.revocationSignatures.length] = result;
else if (result.signatureType == 16 || result.signatureType == 17 || result.signatureType == 18 || result.signatureType == 19)
this.certificationSignature = result;
else if (result.signatureType == 25) {
this.bindingSignature = result;
} else if (result.signatureType == 31) {
this.directSignatures[this.directSignatures.length] = result;
} else
util.print_error("openpgp.msg.publickey read_nodes:\n"+"unknown signature type directly on key "+result.signatureType);
pos += result.packetLength + result.headerLength;
break;
case 14: // Public-Subkey Packet
this.subKeys[this.subKeys.length] = result;
pos += result.packetLength + result.headerLength;
pos += result.read_nodes(this.publicKeyPacket,input, pos, input.length - pos);
break;
case 17: // User Attribute Packet
this.userAttributes[this.userAttributes.length] = result;
pos += result.packetLength + result.headerLength;
pos += result.read_nodes(this.publicKeyPacket,input, pos, input.length - pos);
break;
case 13: // User ID Packet
this.userIds[this.userIds.length] = result;
pos += result.packetLength + result.headerLength;
pos += result.read_nodes(this.publicKeyPacket, input, pos, input.length - pos);
break;
default:
this.data = input;
this.position = position - this.publicKeyPacket.packetLength - this.publicKeyPacket.headerLength;
this.len = pos - position;
return this.len;
}
}
}
this.data = input;
this.position = position - (this.publicKeyPacket.packetLength - this.publicKeyPacket.headerLength);
this.len = pos - position;
return this.len;
}
function write() {
}
function getKeyId() {
return this.publicKeyPacket.getKeyId();
}
function getFingerprint() {
return this.publicKeyPacket.getFingerprint();
}
function validate() {
// check revocation keys
for (var i = 0; i < this.revocationSignatures.length; i++) {
var tohash = this.publicKeyPacket.header+this.publicKeyPacket.data;
if (this.revocationSignatures[i].verify(tohash, this.publicKeyPacket))
return false;
}
if (this.subKeys.length != 0) {
// search for a valid subkey
var found = false;
for (var i = 0; i < this.subKeys.length; i++)
if (this.subKeys[i].verifyKey() == 3) {
found = true;
break;
}
if (!found)
return false;
}
// search for one valid userid
found = false;
for (var i = 0; i < this.userIds.length; i++)
if (this.userIds[i].verify(this.publicKeyPacket) == 0) {
found = true;
break;
}
if (!found)
return false;
return true;
}
/**
* verifies all signatures
* @return a 2 dimensional array. the first dimension corresponds to the userids available
*/
function verifyCertificationSignatures() {
var result = new Array();
for (var i = 0; i < this.userIds.length; i++) {
result[i] = this.userIds[i].verifyCertificationSignatures(this.publicKeyPacket);
}
return result;
}
this.verifyCertificationSignatures = verifyCertificationSignatures;
/**
* verifies:
* - revocation certificates directly on key
* - self signatures
* - subkey binding and revocation certificates
*
* This is useful for validating the key
* @returns true if the basic signatures are all valid
*/
function verifyBasicSignatures() {
for (var i = 0; i < this.revocationSignatures.length; i++) {
var tohash = this.publicKeyPacket.header+this.publicKeyPacket.data;
if (this.revocationSignatures[i].verify(tohash, this.publicKeyPacket))
return false;
}
if (this.subKeys.length != 0) {
// search for a valid subkey
var found = false;
for (var i = 0; i < this.subKeys.length; i++) {
if (this.subKeys[i] == null)
continue;
var result = this.subKeys[i].verifyKey();
if (result == 3) {
found = true;
break;
}
}
if (!found)
return false;
}
var keyId = this.getKeyId();
for (var i = 0; i < this.userIds.length; i++) {
for (var j = 0; j < this.userIds[i].certificationRevocationSignatures.length; j++) {
if (this.userIds[i].certificationSignatures[j].getIssuer == keyId &&
this.userIds[i].certificationSignatures[j].verifyBasic(this.publicKeyPacket) != 4)
return false;
}
}
return true;
}
function toString() {
var result = " OPENPGP Public Key\n length: "+this.len+"\n";
result += " Revocation Signatures:\n"
for (var i=0; i < this.revocationSignatures.length; i++) {
result += " "+this.revocationSignatures[i].toString();
}
result += " User Ids:\n";
for (var i=0; i < this.userIds.length; i++) {
result += " "+this.userIds[i].toString();
}
result += " User Attributes:\n";
for (var i=0; i < this.userAttributes.length; i++) {
result += " "+this.userAttributes[i].toString();
}
result += " Public Key SubKeys:\n";
for (var i=0; i < this.subKeys.length; i++) {
result += " "+this.subKeys[i].toString();
}
return result;
}
/**
* finds an encryption key for this public key
* @returns null if no encryption key has been found
*/
function getEncryptionKey() {
if (this.publicKeyPacket.publicKeyAlgorithm != 17 && this.publicKeyPacket.publicKeyAlgorithm != 3
&& this.publicKeyPacket.verifyKey())
return this.publicKeyPacket;
else if (this.publicKeyPacket.version == 4) // V3 keys MUST NOT have subkeys.
for (var j = 0; j < this.subKeys.length; j++)
if (this.subKeys[j].publicKeyAlgorithm != 17 &&
this.subKeys[j].publicKeyAlgorithm != 3 &&
this.subKeys[j].verifyKey()) {
return this.subKeys[j];
}
return null;
}
function getSigningKey() {
if ((this.publicKeyPacket.publicKeyAlgorithm == 17 ||
this.publicKeyPacket.publicKeyAlgorithm != 2))
return this.publicKeyPacket;
else if (this.publicKeyPacket.version == 4) // V3 keys MUST NOT have subkeys.
for (var j = 0; j < this.subKeys.length; j++) {
if ((this.subKeys[j].publicKeyAlgorithm == 17 ||
this.subKeys[j].publicKeyAlgorithm != 2) &&
this.subKeys[j].verifyKey())
return this.subKeys[j];
}
return null;
}
/* Returns the i-th subKey as a openpgp_msg_publickey object */
function getSubKeyAsKey(i) {
var ret = new openpgp_msg_publickey();
ret.userIds = this.userIds;
ret.userAttributes = this.userAttributes;
ret.publicKeyPacket = this.subKeys[i];
return ret;
}
this.getEncryptionKey = getEncryptionKey;
this.getSigningKey = getSigningKey;
this.read_nodes = read_nodes;
this.write = write;
this.toString = toString;
this.validate = validate;
this.getFingerprint = getFingerprint;
this.getKeyId = getKeyId;
this.verifyBasicSignatures = verifyBasicSignatures;
this.getSubKeyAsKey = getSubKeyAsKey;
}
// 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 = "";
this.messagePacket = null;
this.type = null;
/**
* 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);
var messages = openpgp.read_messages_dearmored({text: decrypted, openpgp: decrypted});
for(var m in messages){
if(messages[m].data){
this.text = messages[m].data;
}
if(messages[m].signature){
validSignatures.push(messages[m].verifySignature(pubkey));
}
}
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.signature.tagType == 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, pubkey[i])) {
util.print_info("Found Good Signature from "+pubkey[i].obj.userIds[0].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
/**
* @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 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 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;
this.verified = false;
/**
* 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) {
// calculating the trailer
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);
switch(this.signatureType) {
case 0: // 0x00: Signature of a binary document.
if (this.version == 4) {
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer);
}
break;
case 1: // 0x01: Signature of a canonical text document.
if (this.version == 4) {
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer);
return this.verified;
}
break;
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) {
this.verified = false;
break;
}
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.obj.publicKeyPacket.MPIs, this.signatureData+trailer);
break;
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.
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.MPIs, data+this.signatureData+trailer);
break;
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) {
this.verified = false;
break;
}
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.MPIs, data+this.signatureData+trailer);
break;
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.
this.verified = openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm,
this.MPIs, key.MPIs, data+this.signatureData+trailer);
break;
// 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;
}
return this.verified;
}
/**
* 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 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 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 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 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 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;
this.decompressedData = null;
/**
* 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]
util.print_info('Decompressed packet [Type 1-ZIP]: ' + this.toString());
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));
this.decompressedData = unescape(jxg_obj.deflate()[0][0]);
break;
case 2: // - ZLIB [RFC1950]
util.print_info('Decompressed packet [Type 2-ZLIB]: ' + this.toString());
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,"");
//TODO check ADLER32 checksum
this.decompressedData = JXG.decompress(radix);
break;
} 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 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.s2k.type != 1001) {
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.s2k.type == 1001) {
this.secMPIs = null;
this.encryptedMPIData = null;
} else 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[this.subKeyRevocationSignature.length] = 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.length] = 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.getKeyId() == this.subKeyRevocationSignature[i].keyId){
return 2;
}
}
}
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 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
/**
* @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;
};