Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

openpgp

Package Overview
Dependencies
Maintainers
1
Versions
179
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openpgp - npm Package Compare versions

Comparing version 0.4.1 to 0.5.0

test/general/armor.js

8

package.json
{
"name": "openpgp",
"description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.",
"version": "0.4.1",
"version": "0.5.0",
"homepage": "http://openpgpjs.org/",

@@ -39,5 +39,5 @@ "engines": {

"grunt-browserify": "~1.2.11",
"grunt-contrib-uglify": "*",
"grunt-text-replace": "*",
"grunt-jsbeautifier": "*",
"grunt-contrib-uglify": "~0.3.2",
"grunt-text-replace": "~0.3.11",
"grunt-jsbeautifier": "~0.2.6",
"grunt-contrib-jshint": "*",

@@ -44,0 +44,0 @@ "grunt-jsdoc": "*",

@@ -42,3 +42,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

if (!(this instanceof CleartextMessage)) {
return new CleartextMessage(packetlist);
return new CleartextMessage(text, packetlist);
}

@@ -146,2 +146,3 @@ // normalize EOL to canonical form <CR><LF>

packetlist.read(input.data);
verifyHeaders(input.headers, packetlist);
var newMessage = new CleartextMessage(input.text, packetlist);

@@ -151,3 +152,48 @@ return newMessage;

/**
* Compare hash algorithm specified in the armor header with signatures
* @private
* @param {Array<String>} headers Armor headers
* @param {module:packet/packetlist} packetlist The packetlist with signature packets
*/
function verifyHeaders(headers, packetlist) {
var checkHashAlgos = function(hashAlgos) {
for (var i = 0; i < packetlist.length; i++) {
if (packetlist[i].tag === enums.packet.signature &&
!hashAlgos.some(function(algo) {
return packetlist[i].hashAlgorithm === algo;
})) {
return false;
}
}
return true;
}
var oneHeader = null;
var hashAlgos = [];
for (var i = 0; i < headers.length; i++) {
oneHeader = headers[i].match(/Hash: (.+)/); // get header value
if (oneHeader) {
oneHeader = oneHeader[1].replace(/\s/g, ''); // remove whitespace
oneHeader = oneHeader.split(',');
oneHeader = oneHeader.map(function(hash) {
hash = hash.toLowerCase();
try {
return enums.write(enums.hash, hash);
} catch (e) {
throw new Error('Unknown hash algorithm in armor header: ' + hash);
}
});
hashAlgos = hashAlgos.concat(oneHeader);
} else {
throw new Error('Only "Hash" header allowed in cleartext signed message');
}
}
if (!hashAlgos.length && !checkHashAlgos([enums.hash.md5])) {
throw new Error('If no "Hash" header in cleartext signed message, then only MD5 signatures allowed');
} else if (!checkHashAlgos(hashAlgos)) {
throw new Error('Hash algorithm mismatch in armor header and signature');
}
}
exports.CleartextMessage = CleartextMessage;
exports.readArmored = readArmored;

@@ -38,10 +38,11 @@ // GPG4Browsers - An OpenPGP implementation in javascript

compression: enums.compression.zip,
integrity_protect: true,
rsa_blinding: true,
show_version: true,
show_comment: true,
integrity_protect: true,
keyserver: "keyserver.linux.it", // "pgp.mit.edu:11371"
versionstring: "OpenPGP.js VERSION",
commentstring: "http://openpgpjs.org",
keyserver: "keyserver.linux.it", // "pgp.mit.edu:11371"
node_store: './openpgp.store',

@@ -48,0 +49,0 @@

@@ -460,23 +460,18 @@ /* Rijndael (AES) Encryption

function AESencrypt(block, ctx) {
var r;
var t0, t1, t2, t3;
function AESencrypt(block, ctx, t) {
var r, rounds, b;
var b = packBytes(block);
var rounds = ctx.rounds;
var b0 = b[0];
var b1 = b[1];
var b2 = b[2];
var b3 = b[3];
b = packBytes(block);
rounds = ctx.rounds;
for (r = 0; r < rounds - 1; r++) {
t0 = b0 ^ ctx.rk[r][0];
t1 = b1 ^ ctx.rk[r][1];
t2 = b2 ^ ctx.rk[r][2];
t3 = b3 ^ ctx.rk[r][3];
t[0] = b[0] ^ ctx.rk[r][0];
t[1] = b[1] ^ ctx.rk[r][1];
t[2] = b[2] ^ ctx.rk[r][2];
t[3] = b[3] ^ ctx.rk[r][3];
b0 = T1[t0 & 255] ^ T2[(t1 >> 8) & 255] ^ T3[(t2 >> 16) & 255] ^ T4[t3 >>> 24];
b1 = T1[t1 & 255] ^ T2[(t2 >> 8) & 255] ^ T3[(t3 >> 16) & 255] ^ T4[t0 >>> 24];
b2 = T1[t2 & 255] ^ T2[(t3 >> 8) & 255] ^ T3[(t0 >> 16) & 255] ^ T4[t1 >>> 24];
b3 = T1[t3 & 255] ^ T2[(t0 >> 8) & 255] ^ T3[(t1 >> 16) & 255] ^ T4[t2 >>> 24];
b[0] = T1[t[0] & 255] ^ T2[(t[1] >> 8) & 255] ^ T3[(t[2] >> 16) & 255] ^ T4[t[3] >>> 24];
b[1] = T1[t[1] & 255] ^ T2[(t[2] >> 8) & 255] ^ T3[(t[3] >> 16) & 255] ^ T4[t[0] >>> 24];
b[2] = T1[t[2] & 255] ^ T2[(t[3] >> 8) & 255] ^ T3[(t[0] >> 16) & 255] ^ T4[t[1] >>> 24];
b[3] = T1[t[3] & 255] ^ T2[(t[0] >> 8) & 255] ^ T3[(t[1] >> 16) & 255] ^ T4[t[2] >>> 24];
}

@@ -487,11 +482,11 @@

t0 = b0 ^ ctx.rk[r][0];
t1 = b1 ^ ctx.rk[r][1];
t2 = b2 ^ ctx.rk[r][2];
t3 = b3 ^ ctx.rk[r][3];
t[0] = b[0] ^ ctx.rk[r][0];
t[1] = b[1] ^ ctx.rk[r][1];
t[2] = b[2] ^ ctx.rk[r][2];
t[3] = b[3] ^ ctx.rk[r][3];
b[0] = F1(t0, t1, t2, t3) ^ ctx.rk[rounds][0];
b[1] = F1(t1, t2, t3, t0) ^ ctx.rk[rounds][1];
b[2] = F1(t2, t3, t0, t1) ^ ctx.rk[rounds][2];
b[3] = F1(t3, t0, t1, t2) ^ ctx.rk[rounds][3];
b[0] = F1(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0];
b[1] = F1(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1];
b[2] = F1(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2];
b[3] = F1(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3];

@@ -505,5 +500,6 @@ return unpackBytes(b);

this.key = keyExpansion(key);
this._temp = new Uint32Array(this.blockSize / 4);
this.encrypt = function(block) {
return AESencrypt(block, this.key);
return AESencrypt(block, this.key, this._temp);
};

@@ -510,0 +506,0 @@ };

@@ -21,3 +21,7 @@ /**

/** @see module:crypto/cipher/blowfish */
blowfish: require('./blowfish.js')
blowfish: require('./blowfish.js'),
/** Not implemented */
idea: function() {
throw new Error('IDEA symmetric-key algorithm not implemented');
}
};

@@ -24,0 +28,0 @@

@@ -33,6 +33,2 @@ /* Modified by Recurity Labs GmbH

function rotb(b, n) {
return (b << n | b >>> (8 - n)) & 0xFF;
}
function rotw(w, n) {

@@ -58,20 +54,2 @@ return (w << n | w >>> (32 - n)) & MAXINT;

function getNrBits(i) {
var n = 0;
while (i > 0) {
n++;
i >>>= 1;
}
return n;
}
function getMask(n) {
return (1 << n) - 1;
}
//added 2008/11/13 XXX MUST USE ONE-WAY HASH FUNCTION FOR SECURITY REASON
function randByte() {
return Math.floor(Math.random() * 256);
}
// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@@ -78,0 +56,0 @@ // Twofish

@@ -96,2 +96,5 @@ // GPG4Browsers - An OpenPGP implementation in javascript

// 0 and 1 are the public key.
var n = keyIntegers[0].toBigInteger();
var e = keyIntegers[1].toBigInteger();
// 2 to 5 are the private key.
var d = keyIntegers[2].toBigInteger();

@@ -102,3 +105,3 @@ p = keyIntegers[3].toBigInteger();

var m = dataIntegers[0].toBigInteger();
return rsa.decrypt(m, d, p, q, u);
return rsa.decrypt(m, n, e, d, p, q, u);
case 'elgamal':

@@ -105,0 +108,0 @@ var elgamal = new publicKey.elgamal();

@@ -88,3 +88,3 @@ /*

default:
document.write("Bogus round number");
throw new Error("Bogus round number");
break;

@@ -91,0 +91,0 @@ }

@@ -90,3 +90,3 @@ /* A JavaScript implementation of the SHA family of hashes, as defined in FIPS

} else {
return "INVALID HEX STRING";
throw new Error("INVALID HEX STRING");
}

@@ -874,3 +874,3 @@ }

/* This should never be reached */
return [];
throw new Error('Unknown SHA variant');
}

@@ -901,3 +901,3 @@ },

if (0 !== (srcString.length % 2)) {
return "TEXT MUST BE IN BYTE INCREMENTS";
throw new Error("TEXT MUST BE IN BYTE INCREMENTS");
}

@@ -911,3 +911,3 @@ this.strBinLen = srcString.length * 4;

} else {
return "UNKNOWN TEXT INPUT TYPE";
throw new Error("UNKNOWN TEXT INPUT TYPE");
}

@@ -941,3 +941,3 @@ };

default:
return "FORMAT NOT RECOGNIZED";
throw new Error("FORMAT NOT RECOGNIZED");
}

@@ -972,3 +972,3 @@

default:
return "HASH NOT RECOGNIZED";
throw new Error("HASH NOT RECOGNIZED");
}

@@ -1007,3 +1007,3 @@ },

default:
return "FORMAT NOT RECOGNIZED";
throw new Error("FORMAT NOT RECOGNIZED");
}

@@ -1034,3 +1034,3 @@

default:
return "HASH NOT RECOGNIZED";
throw new Error("HASH NOT RECOGNIZED");
}

@@ -1042,3 +1042,3 @@

if (0 !== (key.length % 2)) {
return "KEY MUST BE IN BYTE INCREMENTS";
throw new Error("KEY MUST BE IN BYTE INCREMENTS");
}

@@ -1051,3 +1051,3 @@ keyToUse = hex2binb(key);

} else {
return "UNKNOWN KEY INPUT TYPE";
throw new Error("UNKNOWN KEY INPUT TYPE");
}

@@ -1054,0 +1054,0 @@

@@ -31,3 +31,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

*/
hash_headers = [];
var hash_headers = [];
hash_headers[1] = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04,

@@ -47,3 +47,3 @@ 0x10

];
hash_headers[11] = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
0x00, 0x04, 0x1C

@@ -58,2 +58,21 @@ ];

/**
* Create padding with secure random data
* @private
* @param {Integer} length Length of the padding in bytes
* @return {String} Padding as string
*/
function getPkcs1Padding(length) {
var result = '';
var randomByte;
while (result.length < length) {
randomByte = random.getSecureRandomOctet();
if (randomByte !== 0) {
result += String.fromCharCode(randomByte);
}
}
return result;
}
module.exports = {

@@ -63,34 +82,47 @@ eme: {

* create a EME-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1})
* @param {String} message message to be padded
* @param {Integer} length Length to the resulting message
* @param {String} M message to be encoded
* @param {Integer} k the length in octets of the key modulus
* @return {String} EME-PKCS1 padded message
*/
encode: function(message, length) {
if (message.length > length - 11)
return -1;
var result = "";
result += String.fromCharCode(0);
result += String.fromCharCode(2);
for (var i = 0; i < length - message.length - 3; i++) {
result += String.fromCharCode(random.getPseudoRandom(1, 255));
encode: function(M, k) {
var mLen = M.length;
// length checking
if (mLen > k - 11) {
throw new Error('Message too long');
}
result += String.fromCharCode(0);
result += message;
return result;
// Generate an octet string PS of length k - mLen - 3 consisting of
// pseudo-randomly generated nonzero octets
var PS = getPkcs1Padding(k - mLen - 3);
// Concatenate PS, the message M, and other padding to form an
// encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M.
var EM = String.fromCharCode(0) +
String.fromCharCode(2) +
PS +
String.fromCharCode(0) +
M;
return EM;
},
/**
* decodes a EME-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2})
* @param {String} message EME-PKCS1 padded message
* @return {String} decoded message
* @param {String} EM encoded message, an octet string
* @return {String} message, an octet string
*/
decode: function(message, len) {
if (message.length < len)
message = String.fromCharCode(0) + message;
if (message.length < 12 || message.charCodeAt(0) !== 0 || message.charCodeAt(1) != 2)
return -1;
decode: function(EM) {
// leading zeros truncated by jsbn
if (EM.charCodeAt(0) !== 0) {
EM = String.fromCharCode(0) + EM;
}
var firstOct = EM.charCodeAt(0);
var secondOct = EM.charCodeAt(1);
var i = 2;
while (message.charCodeAt(i) !== 0 && message.length > i)
while (EM.charCodeAt(i) !== 0 && i < EM.length) {
i++;
return message.substring(i + 1, message.length);
}
var psLen = i - 2;
var separator = EM.charCodeAt(i++);
if (firstOct === 0 && secondOct === 2 && psLen >= 8 && separator === 0) {
return EM.substr(i);
} else {
throw new Error('Decryption error');
}
}

@@ -100,51 +132,45 @@ },

emsa: {
/**
* create a EMSA-PKCS1-v1_5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3})
* @param {Integer} algo Hash algorithm type used
* @param {String} data Data to be hashed
* @param {Integer} keylength Key size of the public mpi in bytes
* @returns {String} Hashcode with pkcs1padding as string
* @param {String} M message to be encoded
* @param {Integer} emLen intended length in octets of the encoded message
* @returns {String} encoded message
*/
encode: function(algo, data, keylength) {
var data2 = "";
data2 += String.fromCharCode(0x00);
data2 += String.fromCharCode(0x01);
encode: function(algo, M, emLen) {
var i;
for (i = 0; i < (keylength - hash_headers[algo].length - 3 -
hash.getHashByteLength(algo)); i++)
data2 += String.fromCharCode(0xff);
data2 += String.fromCharCode(0x00);
for (i = 0; i < hash_headers[algo].length; i++)
data2 += String.fromCharCode(hash_headers[algo][i]);
data2 += hash.digest(algo, data);
return new BigInteger(util.hexstrdump(data2), 16);
},
/**
* extract the hash out of an EMSA-PKCS1-v1.5 padding (See {@link http://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3})
* @param {String} data Hash in pkcs1 encoding
* @returns {String} The hash as string
*/
decode: function(algo, data) {
var i = 0;
if (data.charCodeAt(0) === 0) i++;
else if (data.charCodeAt(0) != 1) return -1;
else i++;
while (data.charCodeAt(i) == 0xFF) i++;
if (data.charCodeAt(i++) !== 0) return -1;
var j = 0;
for (j = 0; j < hash_headers[algo].length && j + i < data.length; j++) {
if (data.charCodeAt(j + i) != hash_headers[algo][j]) return -1;
// Apply the hash function to the message M to produce a hash value H
var H = hash.digest(algo, M);
if (H.length !== hash.getHashByteLength(algo)) {
throw new Error('Invalid hash length');
}
i += j;
if (data.substring(i).length < hash.getHashByteLength(algo)) return -1;
return data.substring(i);
// produce an ASN.1 DER value for the hash function used.
// Let T be the full hash prefix
var T = '';
for (i = 0; i < hash_headers[algo].length; i++) {
T += String.fromCharCode(hash_headers[algo][i]);
}
// add hash value to prefix
T += H;
// and let tLen be the length in octets of T
var tLen = T.length;
if (emLen < tLen + 11) {
throw new Error('Intended encoded message length too short');
}
// an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xFF
// The length of PS will be at least 8 octets
var PS = '';
for (i = 0; i < (emLen - tLen - 3); i++) {
PS += String.fromCharCode(0xff);
}
// Concatenate PS, the hash prefix T, and other padding to form the
// encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T.
var EM = String.fromCharCode(0x00) +
String.fromCharCode(0x01) +
PS +
String.fromCharCode(0x00) +
T;
return new BigInteger(util.hexstrdump(EM), 16);
}
}
};

@@ -45,5 +45,16 @@ // GPG4Browsers - An OpenPGP implementation in javascript

var hash = new BigInteger(util.hexstrdump(hashed_data), 16);
var k = random.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);
// FIPS-186-4, section 4.6:
// The values of r and s shall be checked to determine if r = 0 or s = 0.
// If either r = 0 or s = 0, a new value of k shall be generated, and the
// signature shall be recalculated. It is extremely unlikely that r = 0
// or s = 0 if signatures are generated properly.
var k, s1, s2;
while (true) {
k = random.getRandomBigIntegerInRange(BigInteger.ONE, q.subtract(BigInteger.ONE));
s1 = (g.modPow(k, p)).mod(q);
s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q);
if (s1 != 0 && s2 != 0) {
break;
}
}
var result = [];

@@ -50,0 +61,0 @@ result[0] = s1.toMPI();

@@ -35,5 +35,4 @@ // GPG4Browsers - An OpenPGP implementation in javascript

// choose k in {2,...,p-2}
var two = BigInteger.ONE.add(BigInteger.ONE);
var pMinus2 = p.subtract(two);
var k = random.getRandomBigIntegerInRange(two, pMinus2);
var pMinus2 = p.subtract(BigInteger.TWO);
var k = random.getRandomBigIntegerInRange(BigInteger.ONE, pMinus2);
k = k.mod(pMinus2).add(BigInteger.ONE);

@@ -40,0 +39,0 @@ var c = [];

@@ -173,3 +173,3 @@ /*

if (x > 0) this[0] = x;
else if (x < -1) this[0] = x + DV;
else if (x < -1) this[0] = x + this.DV;
else this.t = 0;

@@ -297,3 +297,3 @@ }

r = i - a.t;
if (r != 0) return r;
if (r != 0) return (this.s < 0) ? -r : r;
while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r;

@@ -735,2 +735,3 @@ return 0;

BigInteger.ONE = nbv(1);
BigInteger.TWO = nbv(2);

@@ -737,0 +738,0 @@ module.exports = BigInteger;

@@ -29,3 +29,4 @@ // GPG4Browsers - An OpenPGP implementation in javascript

util = require('../../util.js'),
random = require('../random.js');
random = require('../random.js'),
config = require('../../config');

@@ -41,2 +42,19 @@ function SecureRandom() {

var blinder = BigInteger.ZERO;
var unblinder = BigInteger.ZERO;
function blind(m, n, e) {
if (unblinder.bitLength() === n.bitLength()) {
unblinder = unblinder.square().mod(n);
} else {
unblinder = random.getRandomBigIntegerInRange(BigInteger.TWO, n);
}
blinder = unblinder.modInverse(n).modPow(e, n);
return m.multiply(blinder).mod(n);
}
function unblind(t, n) {
return t.multiply(unblinder).mod(n);
}
function RSA() {

@@ -47,2 +65,6 @@ /**

* message
* @param n
* RSA public modulus n as BigInteger
* @param e
* RSA public exponent as BigInteger
* @param d

@@ -58,3 +80,6 @@ * RSA d as BigInteger

*/
function decrypt(m, d, p, q, u) {
function decrypt(m, n, e, d, p, q, u) {
if (config.rsa_blinding) {
m = blind(m, n, e);
}
var xp = m.mod(p).modPow(d.mod(p.subtract(BigInteger.ONE)), p);

@@ -72,3 +97,7 @@ var xq = m.mod(q).modPow(d.mod(q.subtract(BigInteger.ONE)), q);

}
return t.multiply(p).add(xp);
t = t.multiply(p).add(xp);
if (config.rsa_blinding) {
t = unblind(t, n);
}
return t;
}

@@ -75,0 +104,0 @@

@@ -47,12 +47,2 @@ // GPG4Browsers - An OpenPGP implementation in javascript

/**
* Return a pseudo-random number in the specified range
* @param {Integer} from Min of the random number
* @param {Integer} to Max of the random number (max 32bit)
* @return {Integer} A pseudo random number
*/
getPseudoRandom: function(from, to) {
return Math.round(Math.random() * (to - from)) + from;
},
/**
* Return a secure random number in the specified range

@@ -90,2 +80,5 @@ * @param {Integer} from Min of the random number

getRandomValues: function(buf) {
if (!(buf instanceof Uint8Array)) {
throw new Error('Invalid type: buf not an Uint8Array');
}
if (typeof window !== 'undefined' && window.crypto) {

@@ -109,4 +102,4 @@ window.crypto.getRandomValues(buf);

getRandomBigInteger: function(bits) {
if (bits < 0) {
return null;
if (bits < 1) {
throw new Error('Illegal parameter value: bits < 1');
}

@@ -130,3 +123,3 @@ var numBytes = Math.floor((bits + 7) / 8);

if (max.compareTo(min) <= 0) {
return;
throw new Error('Illegal parameter value: max <= min');
}

@@ -159,3 +152,3 @@

RandomBuffer.prototype.init = function(size) {
this.buffer = new Uint32Array(size);
this.buffer = new Uint8Array(size);
this.size = 0;

@@ -166,3 +159,3 @@ };

* Concat array of secure random numbers to buffer
* @param {Uint32Array} buf
* @param {Uint8Array} buf
*/

@@ -173,2 +166,5 @@ RandomBuffer.prototype.set = function(buf) {

}
if (!(buf instanceof Uint8Array)) {
throw new Error('Invalid type: buf not an Uint8Array');
}
var freeSpace = this.buffer.length - this.size;

@@ -178,2 +174,3 @@ if (buf.length > freeSpace) {

}
// set buf with offset old size of buffer
this.buffer.set(buf, this.size);

@@ -185,3 +182,3 @@ this.size += buf.length;

* Take numbers out of buffer and copy to array
* @param {Uint32Array} buf the destination array
* @param {Uint8Array} buf the destination array
*/

@@ -192,8 +189,13 @@ RandomBuffer.prototype.get = function(buf) {

}
if (!(buf instanceof Uint8Array)) {
throw new Error('Invalid type: buf not an Uint8Array');
}
if (this.size < buf.length) {
throw new Error('Random number buffer depleted.')
throw new Error('Random number buffer depleted');
}
for (var i = 0; i < buf.length; i++) {
buf[i] = this.buffer[--this.size];
// clear buffer value
this.buffer[this.size] = 0;
}
};

@@ -22,4 +22,2 @@ /**

verify: function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) {
var calc_hash = hashModule.digest(hash_algo, data);
var dopublic;

@@ -35,11 +33,8 @@ switch (algo) {

var n = publickey_MPIs[0].toBigInteger();
var k = publickey_MPIs[0].byteLength();
var e = publickey_MPIs[1].toBigInteger();
var x = msg_MPIs[0].toBigInteger();
dopublic = rsa.verify(x, e, n);
var hash = pkcs1.emsa.decode(hash_algo, dopublic.toMPI().substring(2));
if (hash == -1) {
throw new Error('PKCS1 padding in message or key incorrect. Aborting...');
}
return hash == calc_hash;
var m = msg_MPIs[0].toBigInteger();
var EM = rsa.verify(m, e, n);
var EM2 = pkcs1.emsa.encode(hash_algo, data, k);
return EM.compareTo(EM2) === 0;
case 16:

@@ -58,3 +53,3 @@ // Elgamal (Encrypt-Only) [ELGAMAL] [HAC]

var m = data;
dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y);
var dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y);
return dopublic.compareTo(s1) === 0;

@@ -61,0 +56,0 @@ default:

@@ -30,3 +30,4 @@ // GPG4Browsers - An OpenPGP implementation in javascript

/**
* Finds out which Ascii Armoring type is used. This is an internal function
* Finds out which Ascii Armoring type is used. Throws error if unknown type.
* @private
* @param {String} text [String] ascii armored text

@@ -39,13 +40,16 @@ * @returns {Integer} 0 = MESSAGE PART n of m

* 5 = PRIVATE KEY BLOCK
* null = unknown
*/
function getType(text) {
var reHeader = /^-----([^-]+)-----$\n/m;
var reHeader = /^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK)-----$\n/m;
var header = text.match(reHeader);
if (!header) {
throw new Error('Unknow ASCII armor type');
}
// BEGIN PGP MESSAGE, PART X/Y
// Used for multi-part messages, where the armor is split amongst Y
// parts, and this is the Xth part out of Y.
if (header[1].match(/BEGIN PGP MESSAGE, PART \d+\/\d+/)) {
if (header[1].match(/MESSAGE, PART \d+\/\d+/)) {
return enums.armor.multipart_section;

@@ -57,3 +61,3 @@ } else

// Header to be used.
if (header[1].match(/BEGIN PGP MESSAGE, PART \d+/)) {
if (header[1].match(/MESSAGE, PART \d+/)) {
return enums.armor.multipart_last;

@@ -66,3 +70,3 @@

// for detached signatures.
if (header[1].match(/BEGIN PGP SIGNED MESSAGE/)) {
if (header[1].match(/SIGNED MESSAGE/)) {
return enums.armor.signed;

@@ -73,3 +77,3 @@

// Used for signed, encrypted, or compressed files.
if (header[1].match(/BEGIN PGP MESSAGE/)) {
if (header[1].match(/MESSAGE/)) {
return enums.armor.message;

@@ -80,3 +84,3 @@

// Used for armoring public keys.
if (header[1].match(/BEGIN PGP PUBLIC KEY BLOCK/)) {
if (header[1].match(/PUBLIC KEY BLOCK/)) {
return enums.armor.public_key;

@@ -87,3 +91,3 @@

// Used for armoring private keys.
if (header[1].match(/BEGIN PGP PRIVATE KEY BLOCK/)) {
if (header[1].match(/PRIVATE KEY BLOCK/)) {
return enums.armor.private_key;

@@ -137,3 +141,3 @@ }

var d = checksum;
return c[0] == d[0] && c[1] == d[1] && c[2] == d[2];
return c[0] == d[0] && c[1] == d[1] && c[2] == d[2] && c[3] == d[3];
}

@@ -218,4 +222,4 @@ /**

function splitHeaders(text) {
var reEmptyLine = /^[\t ]*\n/m;
var headers = "";
var reEmptyLine = /^\s*\n/m;
var headers = '';
var body = text;

@@ -228,4 +232,10 @@

body = text.slice(matchResult.index + matchResult[0].length);
} else {
throw new Error('Mandatory blank line missing between armor headers and armor data');
}
headers = headers.split('\n');
// remove empty entry
headers.pop();
return { headers: headers, body: body };

@@ -235,2 +245,16 @@ }

/**
* Verify armored headers. RFC4880, section 6.3: "OpenPGP should consider improperly formatted
* Armor Headers to be corruption of the ASCII Armor."
* @private
* @param {Array<String>} headers Armor headers
*/
function verifyHeaders(headers) {
for (var i = 0; i < headers.length; i++) {
if (!headers[i].match(/^(Version|Comment|MessageID|Hash|Charset): .+$/)) {
throw new Error('Improperly formatted armor header: ' + headers[i]);;
}
}
}
/**
* Splits a message into two parts, the body and the checksum. This is an internal function

@@ -271,5 +295,2 @@ * @param {String} text OpenPGP armored message part

var type = getType(text);
if (!type) {
throw new Error('Unknow ASCII armor type');
}

@@ -295,2 +316,3 @@ var splittext = text.split(reSplit);

data: base64.decode(msg_sum.body),
headers: msg.headers,
type: type

@@ -301,5 +323,6 @@ };

} else {
// Reverse dash-escaping for msg and remove trailing whitespace at end of line
// Reverse dash-escaping for msg and remove trailing whitespace (0x20) and tabs (0x09) at end of line
msg = splitHeaders(splittext[indexBase].replace(/^- /mg, '').replace(/[\t ]+\n/g, "\n"));
var sig = splitHeaders(splittext[indexBase + 1].replace(/^- /mg, ''));
verifyHeaders(sig.headers);
var sig_sum = splitChecksum(sig.body);

@@ -310,2 +333,3 @@

data: base64.decode(sig_sum.body),
headers: msg.headers,
type: type

@@ -317,2 +341,4 @@ };

checksum = checksum.substr(0, 4);
if (!verifyCheckSum(result.data, checksum)) {

@@ -322,6 +348,8 @@ throw new Error("Ascii armor integrity check on message failed: '" +

"' should be '" +
getCheckSum(result) + "'");
} else {
return result;
getCheckSum(result.data) + "'");
}
verifyHeaders(result.headers);
return result;
}

@@ -328,0 +356,0 @@

@@ -477,3 +477,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

return null;
};
}

@@ -484,3 +484,3 @@ /**

* - if no primary user is found returns the user with the latest self signature
* @return {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}} The primary user and the self signature
* @return {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}|null} The primary user and the self signature
*/

@@ -498,5 +498,5 @@ Key.prototype.getPrimaryUser = function() {

}
if (!user ||
!userSelfCert.isPrimaryUserID && selfCert.isPrimaryUserID ||
userSelfCert.created < selfCert.created) {
if (!user ||
(!userSelfCert.isPrimaryUserID || selfCert.isPrimaryUserID) &&
userSelfCert.created > selfCert.created) {
user = this.users[i];

@@ -509,2 +509,98 @@ userSelfCert = selfCert;

/**
* Update key with new components from specified key with same key ID:
* users, subkeys, certificates are merged into the destination key,
* duplicates are ignored.
* If the specified key is a private key and the destination key is public,
* the destination key is tranformed to a private key.
* @param {module:key~Key} key source key to merge
*/
Key.prototype.update = function(key) {
var that = this;
if (key.verifyPrimaryKey() === enums.keyStatus.invalid) {
return;
}
if (this.primaryKey.getFingerprint() !== key.primaryKey.getFingerprint()) {
throw new Error('Key update method: fingerprints of keys not equal');
}
if (this.isPublic() && key.isPrivate()) {
// check for equal subkey packets
var equal = ((this.subKeys && this.subKeys.length) === (key.subKeys && key.subKeys.length)) &&
(!this.subKeys || this.subKeys.every(function(destSubKey) {
return key.subKeys.some(function(srcSubKey) {
return destSubKey.subKey.getFingerprint() === srcSubKey.subKey.getFingerprint();
});
}));
if (!equal) {
throw new Error('Cannot update public key with private key if subkey mismatch');
}
this.primaryKey = key.primaryKey;
}
// revocation signature
if (!this.revocationSignature && key.revocationSignature && !key.revocationSignature.isExpired() &&
(key.revocationSignature.verified ||
key.revocationSignature.verify(key.primaryKey, {key: key.primaryKey}))) {
this.revocationSignature = key.revocationSignature;
}
// direct signatures
mergeSignatures(key, this, 'directSignatures');
// users
key.users.forEach(function(srcUser) {
var found = false;
for (var i = 0; i < that.users.length; i++) {
if (srcUser.userId && (srcUser.userId.userid === that.users[i].userId.userid) ||
srcUser.userAttribute && (srcUser.userAttribute.equals(that.users[i].userAttribute))) {
that.users[i].update(srcUser, that.primaryKey);
found = true;
break;
}
}
if (!found) {
that.users.push(srcUser);
}
});
// subkeys
if (key.subKeys) {
key.subKeys.forEach(function(srcSubKey) {
var found = false;
for (var i = 0; i < that.subKeys.length; i++) {
if (srcSubKey.subKey.getFingerprint() === that.subKeys[i].subKey.getFingerprint()) {
that.subKeys[i].update(srcSubKey, that.primaryKey);
found = true;
break;
}
}
if (!found) {
that.subKeys.push(srcSubKey);
}
});
}
};
/**
* Merges signatures from source[attr] to dest[attr]
* @private
* @param {Object} source
* @param {Object} dest
* @param {String} attr
* @param {Function} checkFn optional, signature only merged if true
*/
function mergeSignatures(source, dest, attr, checkFn) {
source = source[attr];
if (source) {
if (!dest[attr]) {
dest[attr] = source;
} else {
source.forEach(function(sourceSig) {
if (!sourceSig.isExpired() && (!checkFn || checkFn(sourceSig)) &&
!dest[attr].some(function(destSig) {
return destSig.signature === sourceSig.signature;
})) {
dest[attr].push(sourceSig);
}
});
}
}
}
// TODO

@@ -624,2 +720,20 @@ Key.prototype.revoke = function() {

/**
* Update user with new components from specified user
* @param {module:key~User} user source user to merge
* @param {module:packet/signature} primaryKey primary key used for validation
*/
User.prototype.update = function(user, primaryKey) {
var that = this;
// self signatures
mergeSignatures(user, this, 'selfCertifications', function(srcSelfSig) {
return srcSelfSig.verified ||
srcSelfSig.verify(primaryKey, {userid: that.userId || that.userAttribute, key: primaryKey});
});
// other signatures
mergeSignatures(user, this, 'otherCertifications');
// revocation signatures
mergeSignatures(user, this, 'revocationCertifications');
};
/**
* @class

@@ -715,2 +829,26 @@ * @classdesc Class that represents a subkey packet and the relevant signatures.

/**
* Update subkey with new components from specified subkey
* @param {module:key~SubKey} subKey source subkey to merge
* @param {module:packet/signature} primaryKey primary key used for validation
*/
SubKey.prototype.update = function(subKey, primaryKey) {
if (this.verify(primaryKey) === enums.keyStatus.invalid) {
return;
}
if (this.subKey.getFingerprint() !== subKey.subKey.getFingerprint()) {
throw new Error('SubKey update method: fingerprints of subkeys not equal');
}
if (this.subKey.tag === enums.packet.publicSubkey &&
subKey.subKey.tag === enums.packet.secretSubkey) {
this.subKey = subKey.subKey;
}
// revocation signature
if (!this.revocationSignature && subKey.revocationSignature && !subKey.revocationSignature.isExpired() &&
(subKey.revocationSignature.verified ||
subKey.revocationSignature.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) {
this.revocationSignature = subKey.revocationSignature;
}
};
/**
* Reads an OpenPGP armored text and returns one or multiple key objects

@@ -764,2 +902,7 @@ * @param {String} armoredText text to be parsed

function generate(keyType, numBits, userId, passphrase) {
// RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
if (keyType !== enums.publicKey.rsa_encrypt_sign) {
throw new Error('Only RSA Encrypt or Sign supported');
}
var packetlist = new packet.List();

@@ -781,5 +924,21 @@

signaturePacket.publicKeyAlgorithm = keyType;
//TODO we should load preferred hash from config, or as input to this function
signaturePacket.hashAlgorithm = enums.hash.sha256;
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.preferredSymmetricAlgorithms = [];
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
signaturePacket.preferredHashAlgorithms = [];
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256);
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha1);
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha512);
signaturePacket.preferredCompressionAlgorithms = [];
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zlib);
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zip);
if (config.integrity_protect) {
signaturePacket.features = [];
signaturePacket.features.push(1); // Modification Detection
}
signaturePacket.sign(secretKeyPacket, dataToSign);

@@ -798,4 +957,3 @@

subkeySignaturePacket.publicKeyAlgorithm = keyType;
//TODO we should load preferred hash from config, or as input to this function
subkeySignaturePacket.hashAlgorithm = enums.hash.sha256;
subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];

@@ -813,4 +971,38 @@ subkeySignaturePacket.sign(secretKeyPacket, dataToSign);

/**
* Returns the preferred symmetric algorithm for a set of keys
* @param {Array<module:key~Key>} keys Set of keys
* @return {enums.symmetric} Preferred symmetric algorithm
*/
function getPreferredSymAlgo(keys) {
var prioMap = {};
for (var i = 0; i < keys.length; i++) {
var primaryUser = keys[i].getPrimaryUser();
if (!primaryUser || !primaryUser.selfCertificate.preferredSymmetricAlgorithms) {
return config.encryption_cipher;
}
primaryUser.selfCertificate.preferredSymmetricAlgorithms.forEach(function(algo, index) {
var entry = prioMap[algo] || (prioMap[algo] = {prio: 0, count: 0, algo: algo});
entry.prio += 64 >> index;
entry.count++;
});
}
var prefAlgo = {prio: 0, algo: config.encryption_cipher};
for (var algo in prioMap) {
try {
if (algo !== enums.symmetric.plaintext &&
algo !== enums.symmetric.idea && // not implemented
enums.read(enums.symmetric, algo) && // known algorithm
prioMap[algo].count === keys.length && // available for all keys
prioMap[algo].prio > prefAlgo.prio) {
prefAlgo = prioMap[algo];
}
} catch (e) {}
}
return prefAlgo.algo;
}
exports.Key = Key;
exports.readArmored = readArmored;
exports.generate = generate;
exports.getPreferredSymAlgo = getPreferredSymAlgo;

@@ -30,10 +30,2 @@ // GPG4Browsers - An OpenPGP implementation in javascript

/**
* Callback to check if a key matches the input
* @callback module:keyring/keyring.checkCallback
* @param {String} input input to search for
* @param {module:key~Key} key The key to be checked.
* @return {Boolean} True if the input matches the specified key
*/
module.exports = Keyring;

@@ -49,4 +41,5 @@

this.storeHandler = storeHandler || new (require('./localstore.js'))();
this.keys = this.storeHandler.load();
};
this.publicKeys = new KeyArray(this.storeHandler.loadPublic());
this.privateKeys = new KeyArray(this.storeHandler.loadPrivate());
}

@@ -57,3 +50,4 @@ /**

Keyring.prototype.store = function () {
this.storeHandler.store(this.keys);
this.storeHandler.storePublic(this.publicKeys.keys);
this.storeHandler.storePrivate(this.privateKeys.keys);
};

@@ -65,7 +59,67 @@

Keyring.prototype.clear = function() {
this.keys = [];
this.publicKeys.keys = [];
this.privateKeys.keys = [];
};
/**
* Searches the keyring for keys having the specified key id
* @param {String} keyId provided as string of lowercase hex number
* withouth 0x prefix (can be 16-character key ID or fingerprint)
* @param {Boolean} deep if true search also in subkeys
* @return {Array<module:key~Key>|null} keys found or null
*/
Keyring.prototype.getKeysForId = function (keyId, deep) {
var result = [];
result = result.concat(this.publicKeys.getForId(keyId, deep) || []);
result = result.concat(this.privateKeys.getForId(keyId, deep) || []);
return result.length ? result : null;
};
/**
* Removes keys having the specified key id from the keyring
* @param {String} keyId provided as string of lowercase hex number
* withouth 0x prefix (can be 16-character key ID or fingerprint)
* @return {Array<module:key~Key>|null} keys found or null
*/
Keyring.prototype.removeKeysForId = function (keyId) {
var result = [];
result = result.concat(this.publicKeys.removeForId(keyId) || []);
result = result.concat(this.privateKeys.removeForId(keyId) || []);
return result.length ? result : null;
};
/**
* Get all public and private keys
* @return {Array<module:key~Key>} all keys
*/
Keyring.prototype.getAllKeys = function () {
return this.publicKeys.keys.concat(this.privateKeys.keys);
};
/**
* Array of keys
* @param {Array<module:key~Key>} keys The keys to store in this array
*/
function KeyArray(keys) {
this.keys = keys;
}
/**
* Searches all keys in the KeyArray matching the address or address part of the user ids
* @param {String} email email address to search for
* @return {Array<module:key~Key>} The public keys associated with provided email address.
*/
KeyArray.prototype.getForAddress = function(email) {
var results = [];
for (var i = 0; i < this.keys.length; i++) {
if (emailCheck(email, this.keys[i])) {
results.push(this.keys[i]);
}
}
return results;
};
/**
* Checks a key to see if it matches the specified email address
* @private
* @param {String} email email address to search for

@@ -90,109 +144,82 @@ * @param {module:key~Key} key The key to be checked.

* Checks a key to see if it matches the specified keyid
* @param {String} id hex string keyid to search for
* @param {module:key~Key} key the key to be checked.
* @return {Boolean} true if the email address is defined in the specified key
* @inner
* @private
* @param {String} keyId provided as string of lowercase hex number
* withouth 0x prefix (can be 16-character key ID or fingerprint)
* @param {module:packet/secret_key|public_key|public_subkey|secret_subkey} keypacket The keypacket to be checked
* @return {Boolean} True if keypacket has the specified keyid
*/
function idCheck(id, key) {
var keyids = key.getKeyIds();
for (var i = 0; i < keyids.length; i++) {
if (util.hexstrdump(keyids[i].write()) == id) {
return true;
}
function keyIdCheck(keyId, keypacket) {
if (keyId.length === 16) {
return keyId === keypacket.getKeyId().toHex();
} else {
return keyId === keypacket.getFingerprint();
}
return false;
}
/**
* searches all public keys in the keyring matching the address or address part of the user ids
* @param {Array<module:key~Key>} keys array of keys to search
* @param {module:keyring/keyring.checkCallback} identityFunction callback function which checks for a match
* @param {String} identityInput input to check against
* @param {module:enums.packet} keyType packet types of keys to check
* @return {Array<module:key~Key>} array of keys which match
* Searches the KeyArray for a key having the specified key id
* @param {String} keyId provided as string of lowercase hex number
* withouth 0x prefix (can be 16-character key ID or fingerprint)
* @param {Boolean} deep if true search also in subkeys
* @return {module:key~Key|null} key found or null
*/
function checkForIdentityAndKeyTypeMatch(keys, identityFunction, identityInput, keyType) {
var results = [];
for (var p = 0; p < keys.length; p++) {
var key = keys[p];
switch (keyType) {
case enums.packet.publicKey:
if (key.isPublic() && identityFunction(identityInput, key)) {
results.push(key);
KeyArray.prototype.getForId = function (keyId, deep) {
for (var i = 0; i < this.keys.length; i++) {
if (keyIdCheck(keyId, this.keys[i].primaryKey)) {
return this.keys[i];
}
if (deep && this.keys[i].subKeys) {
for (var j = 0; j < this.keys[i].subKeys.length; j++) {
if (keyIdCheck(keyId, this.keys[i].subKeys[j].subKey)) {
return this.keys[i];
}
break;
case enums.packet.secretKey:
if (key.isPrivate() && identityFunction(identityInput, key)) {
results.push(key);
}
break;
}
}
}
return results;
}
/**
* searches all public keys in the keyring matching the address or address part of the user ids
* @param {String} email email address to search for
* @return {Array<module:key~Key>} The public keys associated with provided email address.
*/
Keyring.prototype.getPublicKeyForAddress = function (email) {
return checkForIdentityAndKeyTypeMatch(this.keys, emailCheck, email, enums.packet.publicKey);
return null;
};
/**
* Searches the keyring for a private key containing the specified email address
* @param {String} email email address to search for
* @return {Array<module:key~Key>} private keys found
*/
Keyring.prototype.getPrivateKeyForAddress = function (email) {
return checkForIdentityAndKeyTypeMatch(this.keys, emailCheck, email, enums.packet.secretKey);
};
/**
* Searches the keyring for public keys having the specified key id
* @param {String} keyId provided as string of hex number (lowercase)
* @return {Array<module:key~Key>} public keys found
*/
Keyring.prototype.getKeysForKeyId = function (keyId) {
return checkForIdentityAndKeyTypeMatch(this.keys, idCheck, keyId, enums.packet.publicKey);
};
/**
* Imports a key from an ascii armored message
* @param {String} armored message to read the keys/key from
* @return {Array<Error>|null} array of error objects or null
*/
Keyring.prototype.importKey = function (armored) {
this.keys = this.keys.concat(keyModule.readArmored(armored).keys);
return true;
KeyArray.prototype.importKey = function (armored) {
var imported = keyModule.readArmored(armored);
var that = this;
imported.keys.forEach(function(key) {
// check if key already in key array
var keyidHex = key.primaryKey.getKeyId().toHex();
var keyFound = that.getForId(keyidHex);
if (keyFound) {
keyFound.update(key);
} else {
that.push(key);
}
});
return imported.err ? imported.err : null;
};
/**
* returns the armored message representation of the key at key ring index
* @param {Integer} index the index of the key within the array
* @return {String} armored message representing the key object
* Add key to KeyArray
* @param {module:key~Key} key The key that will be added to the keyring
* @return {Number} The new length of the KeyArray
*/
Keyring.prototype.exportKey = function (index) {
return this.keys[index].armor();
KeyArray.prototype.push = function (key) {
return this.keys.push(key);
};
/**
* 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 {module:key~Key} The public key object which has been removed
* Removes a key with the specified keyid from the keyring
* @param {String} keyId provided as string of lowercase hex number
* withouth 0x prefix (can be 16-character key ID or fingerprint)
* @return {module:key~Key|null} The key object which has been removed or null
*/
Keyring.prototype.removeKey = function (index) {
var removed = this.keys.splice(index, 1);
return removed;
KeyArray.prototype.removeForId = function (keyId) {
for (var i = 0; i < this.keys.length; i++) {
if (keyIdCheck(keyId, this.keys[i].primaryKey)) {
return this.keys.splice(i, 1)[0];
}
}
return null;
};
/**
* returns the armored message representation of the public key portion of the key at key ring index
* @param {Integer} index the index of the key within the array
* @return {String} armored message representing the public key object
*/
Keyring.prototype.exportPublicKey = function (index) {
return this.keys[index].toPublic().armor();
};

@@ -20,32 +20,46 @@ // GPG4Browsers - An OpenPGP implementation in javascript

* The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage.
* @requires openpgp
* @requires config
* @module keyring/localstore
* @param {String} item itemname in localstore
* @param {String} prefix prefix for itemnames in localstore
*/
module.exports = LocalStore;
var openpgp = require('../');
var config = require('../config'),
keyModule = require('../key.js');
function LocalStore(item) {
function LocalStore(prefix) {
prefix = prefix || 'openpgp-';
this.publicKeysItem = prefix + this.publicKeysItem;
this.privateKeysItem = prefix + this.privateKeysItem;
if (typeof window != 'undefined' && window.localStorage) {
this.storage = window.localStorage;
} else {
this.storage = new (require('node-localstorage').LocalStorage)(openpgp.config.node_store);
this.storage = new (require('node-localstorage').LocalStorage)(config.node_store);
}
if(typeof item == 'string') {
this.item = item;
}
}
/*
* Declare the localstore itemname
* Declare the localstore itemnames
*/
LocalStore.prototype.item = 'armoredKeys';
LocalStore.prototype.publicKeysItem = 'public-keys';
LocalStore.prototype.privateKeysItem = 'private-keys';
/**
* Load the keyring from HTML5 local storage and initializes this instance.
* Load the public keys from HTML5 local storage.
* @return {Array<module:key~Key>} array of keys retrieved from localstore
*/
LocalStore.prototype.load = function () {
var armoredKeys = JSON.parse(this.storage.getItem(this.item));
LocalStore.prototype.loadPublic = function () {
return loadKeys(this.storage, this.publicKeysItem);
};
/**
* Load the private keys from HTML5 local storage.
* @return {Array<module:key~Key>} array of keys retrieved from localstore
*/
LocalStore.prototype.loadPrivate = function () {
return loadKeys(this.storage, this.privateKeysItem);
};
function loadKeys(storage, itemname) {
var armoredKeys = JSON.parse(storage.getItem(itemname));
var keys = [];

@@ -55,3 +69,3 @@ if (armoredKeys !== null && armoredKeys.length !== 0) {

for (var i = 0; i < armoredKeys.length; i++) {
key = openpgp.key.readArmored(armoredKeys[i]).keys[0];
key = keyModule.readArmored(armoredKeys[i]).keys[0];
keys.push(key);

@@ -61,10 +75,23 @@ }

return keys;
}
/**
* Saves the current state of the public keys to HTML5 local storage.
* The key array gets stringified using JSON
* @param {Array<module:key~Key>} keys array of keys to save in localstore
*/
LocalStore.prototype.storePublic = function (keys) {
storeKeys(this.storage, this.publicKeysItem, keys);
};
/**
* Saves the current state of the keyring to HTML5 local storage.
* The privateKeys array and publicKeys array gets Stringified using JSON
* Saves the current state of the private keys to HTML5 local storage.
* The key array gets stringified using JSON
* @param {Array<module:key~Key>} keys array of keys to save in localstore
*/
LocalStore.prototype.store = function (keys) {
LocalStore.prototype.storePrivate = function (keys) {
storeKeys(this.storage, this.privateKeysItem, keys);
};
function storeKeys(storage, itemname, keys) {
var armoredKeys = [];

@@ -74,3 +101,3 @@ for (var i = 0; i < keys.length; i++) {

}
this.storage.setItem(this.item, JSON.stringify(armoredKeys));
};
storage.setItem(itemname, JSON.stringify(armoredKeys));
}

@@ -31,3 +31,4 @@ // GPG4Browsers - An OpenPGP implementation in javascript

config = require('./config'),
crypto = require('./crypto');
crypto = require('./crypto'),
keyModule = require('./key.js');

@@ -111,3 +112,6 @@ /**

symEncryptedPacket.decrypt(pkESKeyPacket.sessionKeyAlgorithm, pkESKeyPacket.sessionKey);
return new Message(symEncryptedPacket.packets);
var resultMsg = new Message(symEncryptedPacket.packets);
// remove packets after decryption
symEncryptedPacket.packets = new packet.List();
return resultMsg;
}

@@ -146,4 +150,4 @@ }

var packetlist = new packet.List();
//TODO get preferred algo from signature
var sessionKey = crypto.generateSessionKey(enums.read(enums.symmetric, config.encryption_cipher));
var symAlgo = keyModule.getPreferredSymAlgo(keys);
var sessionKey = crypto.generateSessionKey(enums.read(enums.symmetric, symAlgo));
keys.forEach(function(key) {

@@ -156,4 +160,3 @@ var encryptionKeyPacket = key.getEncryptionKeyPacket();

pkESKeyPacket.sessionKey = sessionKey;
//TODO get preferred algo from signature
pkESKeyPacket.sessionKeyAlgorithm = enums.read(enums.symmetric, config.encryption_cipher);
pkESKeyPacket.sessionKeyAlgorithm = enums.read(enums.symmetric, symAlgo);
pkESKeyPacket.encrypt(encryptionKeyPacket);

@@ -172,4 +175,3 @@ packetlist.push(pkESKeyPacket);

symEncryptedPacket.packets = this.packets;
//TODO get preferred algo from signature
symEncryptedPacket.encrypt(enums.read(enums.symmetric, config.encryption_cipher), sessionKey);
symEncryptedPacket.encrypt(enums.read(enums.symmetric, symAlgo), sessionKey);
packetlist.push(symEncryptedPacket);

@@ -176,0 +178,0 @@ // remove packets after encryption

@@ -207,3 +207,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

* @param {function} callback (optional) callback(error, result) for async style
* @return {Object} {key: Array<module:key~Key>, privateKeyArmored: Array<String>, publicKeyArmored: Array<String>}
* @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String}
* @static

@@ -210,0 +210,0 @@ */

@@ -41,2 +41,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

this.date = new Date();
this.filename = 'msg.txt';
}

@@ -89,2 +90,20 @@

/**
* Sets the filename of the literal packet data
* @param {String} filename Any native javascript string
*/
Literal.prototype.setFilename = function (filename) {
this.filename = filename;
};
/**
* Get the filename of the literal packet data
* @returns {String} filename
*/
Literal.prototype.getFilename = function() {
return this.filename;
};
/**
* Parsing function for a literal data packet (tag 11).

@@ -122,3 +141,3 @@ *

Literal.prototype.write = function () {
var filename = util.encode_utf8("msg.txt");
var filename = util.encode_utf8(this.filename);

@@ -125,0 +144,0 @@ var data = this.getBytes();

@@ -238,3 +238,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

}
real_packet_length = mypos2;
real_packet_length = mypos2 - mypos;
// 4.2.2.3. Five-Octet Lengths

@@ -241,0 +241,0 @@ } else {

@@ -172,5 +172,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

var decoded = crypto.pkcs1.eme.decode(
result,
key.mpi[0].byteLength());
var decoded = crypto.pkcs1.eme.decode(result);

@@ -177,0 +175,0 @@ key = decoded.substring(1, decoded.length - 2);

@@ -58,2 +58,12 @@ // GPG4Browsers - An OpenPGP implementation in javascript

this.expirationTimeV3 = 0;
/**
* Fingerprint in lowercase hex
* @type {String}
*/
this.fingerprint = null;
/**
* Keyid
* @type {module:type/keyid}
*/
this.keyid = null;
}

@@ -162,9 +172,12 @@

PublicKey.prototype.getKeyId = function () {
var keyid = new type_keyid();
if (this.keyid) {
return this.keyid;
}
this.keyid = new type_keyid();
if (this.version == 4) {
keyid.read(this.getFingerprint().substr(12, 8));
this.keyid.read(util.hex2bin(this.getFingerprint()).substr(12, 8));
} else if (this.version == 3) {
keyid.read(this.mpi[0].write().substr(-8));
this.keyid.read(this.mpi[0].write().substr(-8));
}
return keyid;
return this.keyid;
};

@@ -174,9 +187,12 @@

* Calculates the fingerprint of the key
* @return {String} A string containing the fingerprint
* @return {String} A string containing the fingerprint in lowercase hex
*/
PublicKey.prototype.getFingerprint = function () {
if (this.fingerprint) {
return this.fingerprint;
}
var toHash = '';
if (this.version == 4) {
toHash = this.writeOld();
return crypto.hash.sha1(toHash);
this.fingerprint = crypto.hash.sha1(toHash);
} else if (this.version == 3) {

@@ -187,4 +203,6 @@ var mpicount = crypto.getPublicMpiCount(this.algorithm);

}
return crypto.hash.md5(toHash);
this.fingerprint = crypto.hash.md5(toHash);
}
this.fingerprint = util.hexstrdump(this.fingerprint);
return this.fingerprint;
};

@@ -207,2 +225,5 @@

}
if (this.keyid) {
this.keyid = type_keyid.fromClone(this.keyid);
}
};

@@ -54,2 +54,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

this.signatureData = null;
this.unhashedSubpackets = null;
this.signedHashValue = null;

@@ -170,5 +171,7 @@

this.signatureData = bytes.substr(0, i);
var sigDataLength = i;
// unhashed subpackets
i += subpackets.call(this, bytes.substr(i), false);
this.unhashedSubpackets = bytes.substr(sigDataLength, i - sigDataLength);

@@ -189,3 +192,4 @@ break;

return this.signatureData +
util.writeNumber(0, 2) + // Number of unsigned subpackets.
// unhashed subpackets or two octets for zero
(this.unhashedSubpackets ? this.unhashedSubpackets : util.writeNumber(0, 2)) +
this.signedHashValue +

@@ -296,3 +300,3 @@ this.signature;

bytes = util.bin2str(this.preferredCompressionAlgorithms);
result += write_sub_packet(sub.preferred_hash_algorithms, bytes);
result += write_sub_packet(sub.preferred_compression_algorithms, bytes);
}

@@ -414,8 +418,3 @@ if (this.keyServerPreferences !== null) {

// Preferred Symmetric Algorithms
this.preferredSymmetricAlgorithms = [];
while (mypos != bytes.length) {
this.preferredSymmetricAlgorithms.push(bytes.charCodeAt(mypos++));
}
read_array.call(this, 'preferredSymmetricAlgorithms', bytes.substr(mypos));
break;

@@ -454,3 +453,5 @@ case 12:

this.notation[name] = value;
} else throw new Error("Unsupported notation flag.");
} else {
util.print_debug("Unsupported notation flag "+bytes.charCodeAt(mypos));
}
break;

@@ -463,3 +464,3 @@ case 21:

// Preferred Compression Algorithms
read_array.call(this, 'preferredCompressionAlgorithms ', bytes.substr(mypos));
read_array.call(this, 'preferredCompressionAlgorithms', bytes.substr(mypos));
break;

@@ -515,3 +516,3 @@ case 23:

default:
throw new Error("Unknown signature subpacket type " + type + " @:" + mypos);
util.print_debug("Unknown signature subpacket type " + type + " @:" + mypos);
}

@@ -570,3 +571,3 @@ };

if (data.key === undefined)
throw new Error('Key packet is required for this sigtature.');
throw new Error('Key packet is required for this signature.');

@@ -573,0 +574,0 @@ return data.key.writeOld();

@@ -67,1 +67,15 @@ // GPG4Browsers - An OpenPGP implementation in javascript

};
/**
* Compare for equality
* @param {module:user_attribute~UserAttribute} usrAttr
* @return {Boolean} true if equal
*/
UserAttribute.prototype.equals = function(usrAttr) {
if (!usrAttr || !(usrAttr instanceof UserAttribute)) {
return false;
}
return this.attributes.every(function(attr, index) {
return attr === usrAttr.attributes[index];
});
};

@@ -145,2 +145,5 @@ // GPG4Browsers - An OpenPGP implementation in javascript

decode_utf8: function (utf8) {
if (typeof utf8 !== 'string') {
throw new Error('Parameter "utf8" is not of type string');
}
try {

@@ -147,0 +150,0 @@ return decodeURIComponent(escape(utf8));

@@ -44,2 +44,5 @@ // GPG4Browsers - An OpenPGP implementation in javascript

this.worker.onmessage = this.onMessage.bind(this);
this.worker.onerror = function(e) {
throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
};
this.seedRandom(INITIAL_RANDOM_SEED);

@@ -46,0 +49,0 @@ // FIFO

@@ -20,3 +20,3 @@ // GPG4Browsers - An OpenPGP implementation in javascript

importScripts('openpgp.min.js');
importScripts('openpgp.js');

@@ -35,2 +35,5 @@ var MIN_SIZE_RANDOM_BUFFER = 40000;

case 'seed-random':
if (!(msg.buf instanceof Uint8Array)) {
msg.buf = new Uint8Array(msg.buf);
}
window.openpgp.crypto.random.randomBuffer.set(msg.buf);

@@ -37,0 +40,0 @@ break;

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var chai = require('chai'),

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var util = openpgp.util,

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -91,3 +91,8 @@ var chai = require('chai'),

var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message);
// sign and encrypt
var msg, encrypted;
msg = openpgp.message.fromBinary(message);
msg = msg.sign([privKey]);
msg = msg.encrypt([pubKey]);
encrypted = openpgp.armor.encode(openpgp.enums.armor.message, msg.packets.write());

@@ -98,3 +103,3 @@ if (console.profileEnd) {

var msg = openpgp.message.readArmored(encrypted);
msg = openpgp.message.readArmored(encrypted);

@@ -119,3 +124,2 @@ var keyids = msg.getEncryptionKeyIds();

'Version: GnuPG v2.0.19 (GNU/Linux)',
'Type: RSA/RSA',
'',

@@ -146,4 +150,2 @@ 'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',

'Version: GnuPG v2.0.19 (GNU/Linux)',
'Type: RSA/RSA',
'Pwd: hello world',
'',

@@ -259,2 +261,9 @@ 'lQH+BFJhL04BBADclrUEDDsm0PSZbQ6pml9FpzTyXiyCyDN+rMOsy9J300Oc10kt',

});
it('Decrypt message 2x', function() {
decrypted = openpgp.decryptMessage(privKey, message);
var decrypted2 = openpgp.decryptMessage(privKey, message);
expect(decrypted).to.equal(decrypted2);
});
});

@@ -327,2 +336,11 @@

describe("Misc.", function() {
it('util.decode_utf8 throws error if invalid parameter type', function () {
var test = openpgp.util.decode_utf8.bind(null, {chameleon: true});
expect(test).to.throw(Error, /Parameter "utf8" is not of type string/);
});
});
});
describe('General', function () {
require('./basic.js');
require('./armor.js');
require('./key.js');

@@ -4,0 +5,0 @@ require('./keyring.js');

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -224,2 +224,42 @@ var chai = require('chai'),

var priv_key_rsa =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'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',
'=lw5e',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
it('Parsing armored text with two keys', function(done) {

@@ -254,5 +294,5 @@ var pubKeys = openpgp.key.readArmored(twoKeys);

expect(pubKeyV4.getKeyPacket().getKeyId().toHex()).to.equal('4a63613a4d6e4094');
expect(openpgp.util.hexstrdump(pubKeyV4.getKeyPacket().getFingerprint())).to.equal('f470e50dcb1ad5f1e64e08644a63613a4d6e4094');
expect(pubKeyV4.getKeyPacket().getFingerprint()).to.equal('f470e50dcb1ad5f1e64e08644a63613a4d6e4094');
expect(pubKeyV3.getKeyPacket().getKeyId().toHex()).to.equal('e5b7a014a237ba9d');
expect(openpgp.util.hexstrdump(pubKeyV3.getKeyPacket().getFingerprint())).to.equal('a44fcee620436a443bc4913640ab3e49');
expect(pubKeyV3.getKeyPacket().getFingerprint()).to.equal('a44fcee620436a443bc4913640ab3e49');
done();

@@ -327,3 +367,138 @@ });

it('update() - throw error if fingerprints not equal', function() {
var keys = openpgp.key.readArmored(twoKeys).keys;
expect(keys[0].update.bind(keys[0], keys[1])).to.throw('Key update method: fingerprints of keys not equal');
});
it('update() - merge revocation signature', function() {
var source = openpgp.key.readArmored(pub_revoked).keys[0];
var dest = openpgp.key.readArmored(pub_revoked).keys[0];
expect(source.revocationSignature).to.exist;
dest.revocationSignature = null;
dest.update(source);
expect(dest.revocationSignature).to.exist.and.be.an.instanceof(openpgp.packet.Signature);
});
it('update() - merge user', function() {
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
expect(source.users[1]).to.exist;
dest.users.pop();
dest.update(source);
expect(dest.users[1]).to.exist;
expect(dest.users[1].userId).to.equal(source.users[1].userId);
});
it('update() - merge user - other and revocation certification', function() {
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
expect(source.users[1].otherCertifications).to.exist;
expect(source.users[1].revocationCertifications).to.exist;
dest.users[1].otherCertifications = null;
dest.users[1].revocationCertifications.pop();
dest.update(source);
expect(dest.users[1].otherCertifications).to.exist.and.to.have.length(1);
expect(dest.users[1].otherCertifications[0].signature).to.equal(source.users[1].otherCertifications[0].signature);
expect(dest.users[1].revocationCertifications).to.exist.and.to.have.length(2);
expect(dest.users[1].revocationCertifications[1].signature).to.equal(source.users[1].revocationCertifications[1].signature);
});
it('update() - merge subkey', function() {
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
expect(source.subKeys[1]).to.exist;
dest.subKeys.pop();
dest.update(source);
expect(dest.subKeys[1]).to.exist;
expect(dest.subKeys[1].subKey.getKeyId().toHex()).to.equal(source.subKeys[1].subKey.getKeyId().toHex());
});
it('update() - merge subkey - revocation signature', function() {
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
expect(source.subKeys[0].revocationSignature).to.exist;
dest.subKeys[0].revocationSignature = null;
dest.update(source);
expect(dest.subKeys[0].revocationSignature).to.exist;
expect(dest.subKeys[0].revocationSignature.signature).to.equal(dest.subKeys[0].revocationSignature.signature);
});
it('update() - merge private key into public key', function() {
var source = openpgp.key.readArmored(priv_key_rsa).keys[0];
var dest = openpgp.key.readArmored(twoKeys).keys[0];
expect(dest.isPublic()).to.be.true;
dest.update(source);
expect(dest.isPrivate()).to.be.true;
expect(source.verifyPrimaryKey()).to.equal(dest.verifyPrimaryKey());
expect(source.users[0].verify(source.primaryKey)).to.equal(dest.users[0].verify(dest.primaryKey));
expect(source.subKeys[0].verify(source.primaryKey)).to.equal(dest.subKeys[0].verify(dest.primaryKey));
});
it('update() - merge private key into public key - no subkeys', function() {
var source = openpgp.key.readArmored(priv_key_rsa).keys[0];
var dest = openpgp.key.readArmored(twoKeys).keys[0];
source.subKeys = null;
dest.subKeys = null;
expect(dest.isPublic()).to.be.true;
dest.update(source);
expect(dest.isPrivate()).to.be.true;
expect(source.verifyPrimaryKey()).to.equal(dest.verifyPrimaryKey());
expect(source.users[0].verify(source.primaryKey)).to.equal(dest.users[0].verify(dest.primaryKey));
});
it('update() - merge private key into public key - mismatch throws error', function() {
var source = openpgp.key.readArmored(priv_key_rsa).keys[0];
var dest = openpgp.key.readArmored(twoKeys).keys[0];
source.subKeys = null;
expect(dest.subKeys).to.exist;
expect(dest.isPublic()).to.be.true;
expect(dest.update.bind(dest, source)).to.throw('Cannot update public key with private key if subkey mismatch');
});
it('getPreferredSymAlgo() - one key - AES256', function() {
var key1 = openpgp.key.readArmored(twoKeys).keys[0];
var prefAlgo = openpgp.key.getPreferredSymAlgo([key1]);
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256);
});
it('getPreferredSymAlgo() - two key - AES192', function() {
var keys = openpgp.key.readArmored(twoKeys).keys;
var key1 = keys[0];
var key2 = keys[1];
key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = [6,8,3];
var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]);
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes192);
});
it('getPreferredSymAlgo() - two key - one without pref', function() {
var keys = openpgp.key.readArmored(twoKeys).keys;
var key1 = keys[0];
var key2 = keys[1];
key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = null;
var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]);
expect(prefAlgo).to.equal(openpgp.config.encryption_cipher);
});
it('Preferences of generated key', function() {
var testPref = function(key) {
// key flags
var keyFlags = openpgp.enums.keyFlags;
expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.certify_keys).to.equal(keyFlags.certify_keys);
expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.sign_data).to.equal(keyFlags.sign_data);
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_communication).to.equal(keyFlags.encrypt_communication);
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
var sym = openpgp.enums.symmetric;
expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes192, sym.aes128, sym.cast5, sym.tripledes]);
var hash = openpgp.enums.hash;
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha1, hash.sha512]);
var compr = openpgp.enums.compression;
expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zlib, compr.zip]);
expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection
}
var key = openpgp.generateKeyPair(openpgp.enums.publicKey.rsa_encrypt_sign, 512, 'test', 'hello');
testPref(key.key);
testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]);
});
});
'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -13,3 +13,4 @@ var keyring = new openpgp.Keyring(),

keySize = 512,
keyId = 'F6F60E9B42CDFF4C',
keyId = 'f6f60e9b42cdff4c',
keyFingerP = '5856cef789c3a307e8a1b976f6f60e9b42cdff4c',
pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +

@@ -41,63 +42,241 @@ 'Version: OpenPGP.js v.1.20131011\n' +

'=ULta\n' +
'-----END PGP PRIVATE KEY BLOCK-----';
'-----END PGP PRIVATE KEY BLOCK-----',
keyId2 = 'ba993fc2aee18a3a',
keyFingerP2 = '560b7a7f3f9ab516b233b299ba993fc2aee18a3a',
subkeyId2 = 'f47c5210a8cc2740',
subkeyFingerP2 = '2a20c371141e000833848d85f47c5210a8cc2740',
pubkey2 =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.22 (GNU/Linux)',
'',
'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',
'og2umGfGng==',
'=v3+L',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
it('Import key pair', function(done) {
it('Import key pair', function() {
// clear any keys already in the keychain
keyring.clear();
keyring.importKey(privkey);
keyring.importKey(pubkey);
done();
keyring.store();
keyring.publicKeys.importKey(pubkey);
keyring.publicKeys.importKey(pubkey2);
keyring.privateKeys.importKey(privkey);
});
it('getPublicKeyForAddress() - unknown address', function(done) {
var key = keyring.getPublicKeyForAddress('nobody@example.com');
expect(key).to.be.empty;
done();
it('getKeysForId() - unknown id', function() {
var keys = keyring.getKeysForId('01234567890123456');
expect(keys).to.be.null;
});
it('getPublicKeyForAddress() - valid address', function(done) {
var key = keyring.getPublicKeyForAddress(user);
expect(key).to.exist.and.have.length(1);
done();
it('getKeysForId() - valid id', function() {
var keys = keyring.getKeysForId(keyId);
// we get public and private key
expect(keys).to.exist.and.have.length(2);
expect(keys[0].primaryKey.getKeyId().toHex()).equals(keyId);
});
it('getPrivateKeyForAddress() - unknown address', function(done) {
var key = keyring.getPrivateKeyForAddress('nobody@example.com');
expect(key).to.be.empty;
done();
it('publicKeys.getForId() - unknown id', function() {
var key = keyring.publicKeys.getForId('01234567890123456');
expect(key).to.be.null;
});
it('getPrivateKeyForAddress() - valid address', function(done) {
var key = keyring.getPrivateKeyForAddress(user);
expect(key).to.exist.and.have.length(1);
done();
it('publicKeys.getForId() - valid id', function() {
var key = keyring.publicKeys.getForId(keyId);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId);
});
it('getKeysForKeyId() - unknown id', function(done) {
var keys = keyring.getKeysForKeyId('000102030405060708');
it('privateKeys.getForId() - unknown id', function() {
var key = keyring.privateKeys.getForId('01234567890123456');
expect(key).to.be.null;
});
it('privateKeys.getForId() - valid id', function() {
var key = keyring.privateKeys.getForId(keyId);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId);
});
it('publicKeys.getForId() - subkey id', function() {
var key = keyring.publicKeys.getForId(subkeyId2);
expect(key).to.be.null;
});
it('publicKeys.getForId() - deep, including subkeys - subkey id', function() {
var key = keyring.publicKeys.getForId(subkeyId2, true);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
});
it('getKeysForId() - unknown fingerprint', function() {
var keys = keyring.getKeysForId('71130e8383bef9526e062600d5e9f93acbbc7275');
expect(keys).to.be.null;
});
it('getKeysForId() - valid fingerprint', function() {
var keys = keyring.getKeysForId(keyFingerP2);
expect(keys).to.exist.and.have.length(1);
expect(keys[0].primaryKey.getKeyId().toHex()).equals(keyId2);
});
it('publicKeys.getForId() - unknown fingerprint', function() {
var key = keyring.publicKeys.getForId('71130e8383bef9526e062600d5e9f93acbbc7275');
expect(key).to.be.null;
});
it('publicKeys.getForId() - valid fingerprint', function() {
var key = keyring.publicKeys.getForId(keyFingerP2);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
});
it('publicKeys.getForId() - subkey fingerprint', function() {
var key = keyring.publicKeys.getForId(subkeyFingerP2);
expect(key).to.be.null;
});
it('publicKeys.getForId() - deep, including subkeys - subkey fingerprint', function() {
var key = keyring.publicKeys.getForId(subkeyFingerP2, true);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
});
it('publicKeys.getForAddress() - unknown address', function() {
var keys = keyring.publicKeys.getForAddress('nobody@example.com');
expect(keys).to.be.empty;
done();
});
it('getKeysForKeyId() - valid id', function(done) {
var keys = keyring.getKeysForKeyId(keyId.toLowerCase());
it('publicKeys.getForAddress() - valid address', function() {
var keys = keyring.publicKeys.getForAddress(user);
expect(keys).to.exist.and.have.length(1);
done();
});
it('store keys in localstorage', function(done){
it('privateKeys.getForAddress() - unknown address', function() {
var key = keyring.privateKeys.getForAddress('nobody@example.com');
expect(key).to.be.empty;
});
it('privateKeys.getForAddress() - valid address', function() {
var key = keyring.privateKeys.getForAddress(user);
expect(key).to.exist.and.have.length(1);
});
it('store keys in localstorage', function(){
keyring.store();
done();
});
it('after loading from localstorage: getKeysForKeyId() - valid id', function(done) {
it('after loading from localstorage: getKeysForKeyId() - valid id', function() {
var keyring = new openpgp.Keyring(),
keys = keyring.getKeysForKeyId(keyId.toLowerCase());
expect(keys).to.exist.and.have.length(1);
done();
keys = keyring.getKeysForId(keyId);
// we expect public and private key
expect(keys).to.exist.and.have.length(2);
});
it('publicKeys.removeForId() - unknown id', function() {
var key = keyring.publicKeys.removeForId('01234567890123456');
expect(key).to.be.null;
});
it('publicKeys.removeForId() - valid id', function() {
var key = keyring.publicKeys.removeForId(keyId);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId);
expect(keyring.publicKeys.keys).to.exist.and.have.length(1);
});
it('publicKeys.removeForId() - unknown fingerprint', function() {
var key = keyring.publicKeys.removeForId('71130e8383bef9526e062600d5e9f93acbbc7275');
expect(key).to.be.null;
expect(keyring.publicKeys.keys).to.exist.and.have.length(1);
});
it('publicKeys.removeForId() - valid fingerprint', function() {
var key = keyring.publicKeys.removeForId(keyFingerP2);
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
expect(keyring.publicKeys.keys).to.be.empty;
});
it('customize localstorage itemname', function() {
var localstore1 = new openpgp.Keyring.localstore('my-custom-name');
var localstore2 = new openpgp.Keyring.localstore('my-custom-name');
var localstore1 = new openpgp.Keyring.localstore('my-custom-prefix-');
var localstore2 = new openpgp.Keyring.localstore('my-custom-prefix-');
var localstore3 = new openpgp.Keyring.localstore();
localstore3.store([]);
localstore3.storePublic([]);
var key = openpgp.key.readArmored(pubkey).keys[0];
localstore1.store([key]);
expect(localstore2.load()[0].primaryKey.getKeyId().equals(key.primaryKey.getKeyId())).to.be.true;
expect(localstore3.load()).to.have.length(0);
localstore1.storePublic([key]);
expect(localstore2.loadPublic()[0].primaryKey.getKeyId().equals(key.primaryKey.getKeyId())).to.be.true;
expect(localstore3.loadPublic()).to.have.length(0);
});
it('removeKeysForId() - unknown id', function() {
keyring.publicKeys.importKey(pubkey);
keyring.publicKeys.importKey(pubkey2);
keyring.privateKeys.importKey(privkey);
expect(keyring.publicKeys.keys).to.have.length(2);
expect(keyring.privateKeys.keys).to.have.length(1);
var keys = keyring.removeKeysForId('01234567890123456');
expect(keys).to.be.null;
expect(keyring.publicKeys.keys).to.have.length(2);
expect(keyring.privateKeys.keys).to.have.length(1);
});
it('removeKeysForId() - valid id', function() {
var keys = keyring.removeKeysForId(keyId);
expect(keys).to.have.length(2);
expect(keyring.publicKeys.keys).to.have.length(1);
expect(keyring.privateKeys.keys).to.have.length(0);
});
it('removeKeysForId() - unknown fingerprint', function() {
keyring.publicKeys.importKey(pubkey);
keyring.publicKeys.importKey(pubkey2);
keyring.privateKeys.importKey(privkey);
expect(keyring.publicKeys.keys).to.have.length(2);
expect(keyring.privateKeys.keys).to.have.length(1);
var keys = keyring.removeKeysForId('71130e8383bef9526e062600d5e9f93acbbc7275');
expect(keys).to.be.null;
expect(keyring.publicKeys.keys).to.have.length(2);
expect(keyring.privateKeys.keys).to.have.length(1);
});
it('removeKeysForId() - valid fingerprint', function() {
var keys = keyring.removeKeysForId(keyFingerP);
expect(keys).to.have.length(2);
expect(keyring.publicKeys.keys).to.have.length(1);
expect(keyring.privateKeys.keys).to.have.length(0);
});
});
'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -5,0 +5,0 @@ var chai = require('chai'),

'use strict';
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('../../src/index');
var openpgp = typeof window != 'undefined' && window.openpgp ? window.openpgp : require('openpgp');

@@ -80,4 +80,2 @@ var chai = require('chai'),

'Version: GnuPG v2.0.19 (GNU/Linux)',
'Type: RSA/RSA',
'Pwd: hello world',
'',

@@ -124,3 +122,2 @@ 'lQH+BFJhL04BBADclrUEDDsm0PSZbQ6pml9FpzTyXiyCyDN+rMOsy9J300Oc10kt',

'Version: GnuPG v2.0.19 (GNU/Linux)',
'Type: RSA/RSA',
'',

@@ -576,2 +573,10 @@ 'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',

});
it('Write unhashed subpackets', function() {
var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
expect(pubKey.users[0].selfCertifications).to.exist;
pubKey = openpgp.key.readArmored(pubKey.armor()).keys[0]
expect(pubKey.users[0].selfCertifications).to.exist;
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc