Merge pull request #661 from openpgpjs/random_workers
Eliminate running out of random bytes in workers, allow N workers
This commit is contained in:
commit
3c420312a0
|
@ -107,9 +107,12 @@ module.exports = function(grunt) {
|
|||
transform: [
|
||||
["babelify", {
|
||||
global: true,
|
||||
// Only babelify chai-as-promised in node_modules
|
||||
only: /^(?:.*\/node_modules\/chai-as-promised\/|(?!.*\/node_modules\/)).*$/,
|
||||
plugins: ["transform-async-to-generator",
|
||||
"syntax-async-functions",
|
||||
"transform-regenerator",
|
||||
"transform-runtime",
|
||||
"transform-remove-strict-mode"],
|
||||
ignore: ['*.min.js'],
|
||||
presets: ["env"]
|
||||
|
|
|
@ -50,13 +50,15 @@ hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
|
|||
* @param {Integer} length Length of the padding in bytes
|
||||
* @return {String} Padding as string
|
||||
*/
|
||||
function getPkcs1Padding(length) {
|
||||
async function getPkcs1Padding(length) {
|
||||
let result = '';
|
||||
let randomByte;
|
||||
while (result.length < length) {
|
||||
randomByte = random.getSecureRandomOctet();
|
||||
if (randomByte !== 0) {
|
||||
result += String.fromCharCode(randomByte);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const randomBytes = await random.getRandomBytes(length - result.length);
|
||||
for (let i = 0; i < randomBytes.length; i++) {
|
||||
if (randomBytes[i] !== 0) {
|
||||
result += String.fromCharCode(randomBytes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -69,9 +71,9 @@ export default {
|
|||
* create a EME-PKCS1-v1_5 padding (See {@link https://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1})
|
||||
* @param {String} M message to be encoded
|
||||
* @param {Integer} k the length in octets of the key modulus
|
||||
* @return {String} EME-PKCS1 padded message
|
||||
* @return {Promise<String>} EME-PKCS1 padded message
|
||||
*/
|
||||
encode: function(M, k) {
|
||||
encode: async function(M, k) {
|
||||
const mLen = M.length;
|
||||
// length checking
|
||||
if (mLen > k - 11) {
|
||||
|
@ -79,15 +81,14 @@ export default {
|
|||
}
|
||||
// Generate an octet string PS of length k - mLen - 3 consisting of
|
||||
// pseudo-randomly generated nonzero octets
|
||||
const PS = getPkcs1Padding(k - mLen - 3);
|
||||
const PS = await 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.
|
||||
const EM = String.fromCharCode(0) +
|
||||
String.fromCharCode(2) +
|
||||
PS +
|
||||
String.fromCharCode(0) +
|
||||
M;
|
||||
return EM;
|
||||
return String.fromCharCode(0) +
|
||||
String.fromCharCode(2) +
|
||||
PS +
|
||||
String.fromCharCode(0) +
|
||||
M;
|
||||
},
|
||||
/**
|
||||
* decodes a EME-PKCS1-v1_5 padding (See {@link https://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2})
|
||||
|
|
|
@ -48,7 +48,7 @@ export default {
|
|||
* g, p, q, x are all BN
|
||||
* returns { r: BN, s: BN }
|
||||
*/
|
||||
sign: function(hash_algo, m, g, p, q, x) {
|
||||
sign: async function(hash_algo, m, g, p, q, x) {
|
||||
let k;
|
||||
let r;
|
||||
let s;
|
||||
|
@ -73,7 +73,8 @@ export default {
|
|||
// or s = 0 if signatures are generated properly.
|
||||
while (true) {
|
||||
// See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
||||
k = random.getRandomBN(one, q); // returns in [1, q-1]
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
k = await random.getRandomBN(one, q); // returns in [1, q-1]
|
||||
r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q
|
||||
if (zero.cmp(r) === 0) {
|
||||
continue;
|
||||
|
@ -96,7 +97,7 @@ export default {
|
|||
* p, q, g, y are all BN
|
||||
* returns BN
|
||||
*/
|
||||
verify: function(hash_algo, r, s, m, p, q, g, y) {
|
||||
verify: async function(hash_algo, r, s, m, p, q, g, y) {
|
||||
if (zero.ucmp(r) >= 0 || r.ucmp(q) >= 0 ||
|
||||
zero.ucmp(s) >= 0 || s.ucmp(q) >= 0) {
|
||||
util.print_debug("invalid DSA Signature");
|
||||
|
|
|
@ -33,13 +33,13 @@ export default {
|
|||
* m, p, g, y are all BN
|
||||
* returns { c1: BN, c2: BN }
|
||||
*/
|
||||
encrypt: function(m, p, g, y) {
|
||||
encrypt: async function(m, p, g, y) {
|
||||
const redp = new BN.red(p);
|
||||
const mred = m.toRed(redp);
|
||||
const gred = g.toRed(redp);
|
||||
const yred = y.toRed(redp);
|
||||
// See Section 11.5 here: https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf
|
||||
const k = random.getRandomBN(zero, p); // returns in [0, p-1]
|
||||
const k = await random.getRandomBN(zero, p); // returns in [0, p-1]
|
||||
return {
|
||||
c1: gred.redPow(k).fromRed(),
|
||||
c2: yred.redPow(k).redMul(mred).fromRed()
|
||||
|
@ -50,7 +50,7 @@ export default {
|
|||
* c1, c2, p, x are all BN
|
||||
* returns BN
|
||||
*/
|
||||
decrypt: function(c1, c2, p, x) {
|
||||
decrypt: async function(c1, c2, p, x) {
|
||||
const redp = new BN.red(p);
|
||||
const c1red = c1.toRed(redp);
|
||||
const c2red = c2.toRed(redp);
|
||||
|
|
|
@ -178,7 +178,7 @@ Curve.prototype.genKeyPair = async function () {
|
|||
if (!keyPair || !keyPair.priv) {
|
||||
// elliptic fallback
|
||||
const r = await this.curve.genKeyPair({
|
||||
entropy: util.Uint8Array_to_str(random.getRandomBytes(32))
|
||||
entropy: util.Uint8Array_to_str(await random.getRandomBytes(32))
|
||||
});
|
||||
const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont';
|
||||
if (this.keyType === enums.publicKey.eddsa) {
|
||||
|
|
|
@ -37,15 +37,16 @@ export default {
|
|||
* @param {Integer} k Optional number of iterations of Miller-Rabin test
|
||||
* @return BN
|
||||
*/
|
||||
function randomProbablePrime(bits, e, k) {
|
||||
async function randomProbablePrime(bits, e, k) {
|
||||
const min = new BN(1).shln(bits - 1);
|
||||
|
||||
let n = random.getRandomBN(min, min.shln(1));
|
||||
let n = await random.getRandomBN(min, min.shln(1));
|
||||
if (n.isEven()) {
|
||||
n.iaddn(1); // force odd
|
||||
}
|
||||
|
||||
while (!isProbablePrime(n, e, k)) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
while (!await isProbablePrime(n, e, k)) {
|
||||
n.iaddn(2);
|
||||
// If reached the maximum, go back to the minimum.
|
||||
if (n.bitLength() > bits) {
|
||||
|
@ -62,17 +63,17 @@ function randomProbablePrime(bits, e, k) {
|
|||
* @param {Integer} k Optional number of iterations of Miller-Rabin test
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isProbablePrime(n, e, k) {
|
||||
async function isProbablePrime(n, e, k) {
|
||||
if (e && !n.subn(1).gcd(e).eqn(1)) {
|
||||
return false;
|
||||
}
|
||||
if (!fermat(n)) {
|
||||
return false;
|
||||
}
|
||||
if (!millerRabin(n, k, () => new BN(lowprimes[Math.random() * lowprimes.length | 0]))) {
|
||||
if (!await millerRabin(n, k, () => new BN(lowprimes[Math.random() * lowprimes.length | 0]))) {
|
||||
return false;
|
||||
}
|
||||
if (!millerRabin(n, k)) {
|
||||
if (!await millerRabin(n, k)) {
|
||||
return false;
|
||||
}
|
||||
// TODO implement the Lucas test
|
||||
|
@ -138,7 +139,7 @@ const lowprimes = [
|
|||
* @param {Function} rand Optional function to generate potential witnesses
|
||||
* @return {boolean}
|
||||
*/
|
||||
function millerRabin(n, k, rand) {
|
||||
async function millerRabin(n, k, rand) {
|
||||
const len = n.bitLength();
|
||||
const red = BN.mont(n);
|
||||
const rone = new BN(1).toRed(red);
|
||||
|
@ -155,7 +156,8 @@ function millerRabin(n, k, rand) {
|
|||
const d = n.shrn(s);
|
||||
|
||||
for (; k > 0; k--) {
|
||||
let a = rand ? rand() : random.getRandomBN(new BN(2), n1);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
let a = rand ? rand() : await random.getRandomBN(new BN(2), n1);
|
||||
|
||||
let x = a.toRed(red).redPow(d);
|
||||
if (x.eq(rone) || x.eq(rn1))
|
||||
|
|
|
@ -55,7 +55,7 @@ export default {
|
|||
* @param d private MPI part as BN
|
||||
* @return BN
|
||||
*/
|
||||
sign: function(m, n, e, d) {
|
||||
sign: async function(m, n, e, d) {
|
||||
if (n.cmp(m) <= 0) {
|
||||
throw new Error('Data too large.');
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export default {
|
|||
* @param e public MPI part as BN
|
||||
* @return BN
|
||||
*/
|
||||
verify: function(s, n, e) {
|
||||
verify: async function(s, n, e) {
|
||||
if (n.cmp(s) <= 0) {
|
||||
throw new Error('Data too large.');
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ export default {
|
|||
* @param e public MPI part as BN
|
||||
* @return BN
|
||||
*/
|
||||
encrypt: function(m, n, e) {
|
||||
encrypt: async function(m, n, e) {
|
||||
if (n.cmp(m) <= 0) {
|
||||
throw new Error('Data too large.');
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ export default {
|
|||
* @param u RSA u as BN
|
||||
* @return {BN} The decrypted value of the message
|
||||
*/
|
||||
decrypt: function(m, n, e, d, p, q, u) {
|
||||
decrypt: async function(m, n, e, d, p, q, u) {
|
||||
if (n.cmp(m) <= 0) {
|
||||
throw new Error('Data too large.');
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ export default {
|
|||
let blinder;
|
||||
let unblinder;
|
||||
if (config.rsa_blinding) {
|
||||
unblinder = random.getRandomBN(new BN(2), n).toRed(nred);
|
||||
unblinder = (await random.getRandomBN(new BN(2), n)).toRed(nred);
|
||||
blinder = unblinder.redInvm().redPow(e);
|
||||
m = m.toRed(nred).redMul(blinder).fromRed();
|
||||
}
|
||||
|
@ -204,8 +204,8 @@ export default {
|
|||
// RSA keygen fallback using 40 iterations of the Miller-Rabin test
|
||||
// See https://stackoverflow.com/a/6330138 for justification
|
||||
// Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST
|
||||
let p = prime.randomProbablePrime(B - (B >> 1), E, 40);
|
||||
let q = prime.randomProbablePrime(B >> 1, E, 40);
|
||||
let p = await prime.randomProbablePrime(B - (B >> 1), E, 40);
|
||||
let q = await prime.randomProbablePrime(B >> 1, E, 40);
|
||||
|
||||
if (p.cmp(q) < 0) {
|
||||
[p, q] = [q, p];
|
||||
|
|
|
@ -37,50 +37,8 @@ export default {
|
|||
* @param {Integer} length Length in bytes to generate
|
||||
* @return {Uint8Array} Random byte array
|
||||
*/
|
||||
getRandomBytes: function(length) {
|
||||
const result = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
result[i] = this.getSecureRandomOctet();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a secure 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 secure random number
|
||||
*/
|
||||
getSecureRandom: function(from, to) {
|
||||
let randUint = this.getSecureRandomUint();
|
||||
const bits = ((to - from)).toString(2).length;
|
||||
while ((randUint & ((2 ** bits) - 1)) > (to - from)) {
|
||||
randUint = this.getSecureRandomUint();
|
||||
}
|
||||
return from + (Math.abs(randUint & ((2 ** bits) - 1)));
|
||||
},
|
||||
|
||||
getSecureRandomOctet: function() {
|
||||
const buf = new Uint8Array(1);
|
||||
this.getRandomValues(buf);
|
||||
return buf[0];
|
||||
},
|
||||
|
||||
getSecureRandomUint: function() {
|
||||
const buf = new Uint8Array(4);
|
||||
const dv = new DataView(buf.buffer);
|
||||
this.getRandomValues(buf);
|
||||
return dv.getUint32(0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper routine which calls platform specific crypto random generator
|
||||
* @param {Uint8Array} buf
|
||||
*/
|
||||
getRandomValues: function(buf) {
|
||||
if (!(buf instanceof Uint8Array)) {
|
||||
throw new Error('Invalid type: buf not an Uint8Array');
|
||||
}
|
||||
getRandomBytes: async function(length) {
|
||||
const buf = new Uint8Array(length);
|
||||
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
|
||||
window.crypto.getRandomValues(buf);
|
||||
} else if (typeof window !== 'undefined' && typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') {
|
||||
|
@ -89,7 +47,7 @@ export default {
|
|||
const bytes = nodeCrypto.randomBytes(buf.length);
|
||||
buf.set(bytes);
|
||||
} else if (this.randomBuffer.buffer) {
|
||||
this.randomBuffer.get(buf);
|
||||
await this.randomBuffer.get(buf);
|
||||
} else {
|
||||
throw new Error('No secure random number generator available.');
|
||||
}
|
||||
|
@ -102,7 +60,7 @@ export default {
|
|||
* @param {module:type/mpi} max Upper bound, excluded
|
||||
* @return {module:BN} Random MPI
|
||||
*/
|
||||
getRandomBN: function(min, max) {
|
||||
getRandomBN: async function(min, max) {
|
||||
if (max.cmp(min) <= 0) {
|
||||
throw new Error('Illegal parameter value: max <= min');
|
||||
}
|
||||
|
@ -113,7 +71,7 @@ export default {
|
|||
// Using a while loop is necessary to avoid bias introduced by the mod operation.
|
||||
// However, we request 64 extra random bits so that the bias is negligible.
|
||||
// Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
||||
const r = new BN(this.getRandomBytes(bytes + 8));
|
||||
const r = new BN(await this.getRandomBytes(bytes + 8));
|
||||
return r.mod(modulus).add(min);
|
||||
},
|
||||
|
||||
|
@ -126,15 +84,17 @@ export default {
|
|||
function RandomBuffer() {
|
||||
this.buffer = null;
|
||||
this.size = null;
|
||||
this.callback = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize buffer
|
||||
* @param {Integer} size size of buffer
|
||||
*/
|
||||
RandomBuffer.prototype.init = function(size) {
|
||||
RandomBuffer.prototype.init = function(size, callback) {
|
||||
this.buffer = new Uint8Array(size);
|
||||
this.size = 0;
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -161,7 +121,7 @@ RandomBuffer.prototype.set = function(buf) {
|
|||
* Take numbers out of buffer and copy to array
|
||||
* @param {Uint8Array} buf the destination array
|
||||
*/
|
||||
RandomBuffer.prototype.get = function(buf) {
|
||||
RandomBuffer.prototype.get = async function(buf) {
|
||||
if (!this.buffer) {
|
||||
throw new Error('RandomBuffer is not initialized');
|
||||
}
|
||||
|
@ -169,7 +129,12 @@ RandomBuffer.prototype.get = function(buf) {
|
|||
throw new Error('Invalid type: buf not an Uint8Array');
|
||||
}
|
||||
if (this.size < buf.length) {
|
||||
throw new Error('Random number buffer depleted');
|
||||
if (!this.callback) {
|
||||
throw new Error('Random number buffer depleted');
|
||||
}
|
||||
// Wait for random bytes from main context, then try again
|
||||
await this.callback();
|
||||
return this.get(buf);
|
||||
}
|
||||
for (let i = 0; i < buf.length; i++) {
|
||||
buf[i] = this.buffer[--this.size];
|
||||
|
|
|
@ -34,7 +34,7 @@ export default {
|
|||
const m = msg_MPIs[0].toBN();
|
||||
const n = pub_MPIs[0].toBN();
|
||||
const e = pub_MPIs[1].toBN();
|
||||
const EM = publicKey.rsa.verify(m, n, e);
|
||||
const EM = await publicKey.rsa.verify(m, n, e);
|
||||
const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array_to_str(data), n.byteLength());
|
||||
return util.Uint8Array_to_hex(EM) === EM2;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export default {
|
|||
const d = key_params[2].toBN();
|
||||
data = util.Uint8Array_to_str(data);
|
||||
const m = new BN(pkcs1.emsa.encode(hash_algo, data, n.byteLength()), 16);
|
||||
const signature = publicKey.rsa.sign(m, n, e, d);
|
||||
const signature = await publicKey.rsa.sign(m, n, e, d);
|
||||
return util.Uint8Array_to_MPI(signature);
|
||||
}
|
||||
case enums.publicKey.dsa: {
|
||||
|
@ -96,7 +96,7 @@ export default {
|
|||
const q = key_params[1].toBN();
|
||||
const g = key_params[2].toBN();
|
||||
const x = key_params[4].toBN();
|
||||
const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x);
|
||||
const signature = await publicKey.dsa.sign(hash_algo, data, g, p, q, x);
|
||||
return util.concatUint8Array([
|
||||
util.Uint8Array_to_MPI(signature.r),
|
||||
util.Uint8Array_to_MPI(signature.s)
|
||||
|
|
162
src/key.js
162
src/key.js
|
@ -153,7 +153,7 @@ Key.prototype.packetlist2structure = function(packetlist) {
|
|||
|
||||
/**
|
||||
* Transforms structured key data to packetlist
|
||||
* @return {module:packet/packetlist} The packets that form a key
|
||||
* @returns {module:packet/packetlist} The packets that form a key
|
||||
*/
|
||||
Key.prototype.toPacketlist = function() {
|
||||
const packetlist = new packet.List();
|
||||
|
@ -210,7 +210,7 @@ Key.prototype.getKeyIds = function() {
|
|||
/**
|
||||
* Returns array containing first key packet for given key ID or all key packets in the case of a wildcard ID
|
||||
* @param {type/keyid} keyId
|
||||
* @return {(module:packet/public_subkey|module:packet/public_key|
|
||||
* @returns {(module:packet/public_subkey|module:packet/public_key|
|
||||
* module:packet/secret_subkey|module:packet/secret_key|null)}
|
||||
*/
|
||||
Key.prototype.getKeyPackets = function(packetKeyId) {
|
||||
|
@ -229,7 +229,7 @@ Key.prototype.getKeyPackets = function(packetKeyId) {
|
|||
|
||||
/**
|
||||
* Returns userids
|
||||
* @return {Array<string>} array of userids
|
||||
* @returns {Array<string>} array of userids
|
||||
*/
|
||||
Key.prototype.getUserIds = function() {
|
||||
const userids = [];
|
||||
|
@ -243,7 +243,7 @@ Key.prototype.getUserIds = function() {
|
|||
|
||||
/**
|
||||
* Returns true if this is a public key
|
||||
* @return {Boolean}
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Key.prototype.isPublic = function() {
|
||||
return this.primaryKey.tag === enums.packet.publicKey;
|
||||
|
@ -251,7 +251,7 @@ Key.prototype.isPublic = function() {
|
|||
|
||||
/**
|
||||
* Returns true if this is a private key
|
||||
* @return {Boolean}
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
Key.prototype.isPrivate = function() {
|
||||
return this.primaryKey.tag === enums.packet.secretKey;
|
||||
|
@ -259,7 +259,7 @@ Key.prototype.isPrivate = function() {
|
|||
|
||||
/**
|
||||
* Returns key as public key (shallow copy)
|
||||
* @return {module:key~Key} new public Key
|
||||
* @returns {module:key~Key} new public Key
|
||||
*/
|
||||
Key.prototype.toPublic = function() {
|
||||
const packetlist = new packet.List();
|
||||
|
@ -290,7 +290,7 @@ Key.prototype.toPublic = function() {
|
|||
|
||||
/**
|
||||
* Returns ASCII armored text of key
|
||||
* @return {String} ASCII armor
|
||||
* @returns {String} ASCII armor
|
||||
*/
|
||||
Key.prototype.armor = function() {
|
||||
const type = this.isPublic() ? enums.armor.public_key : enums.armor.private_key;
|
||||
|
@ -301,7 +301,7 @@ Key.prototype.armor = function() {
|
|||
* Returns first key packet or key packet by given keyId that is available for signing or signature verification
|
||||
* @param {module:type/keyid} keyId, optional
|
||||
* @param {Date} date use the given date for verification instead of the current time
|
||||
* @return {(module:packet/secret_subkey|module:packet/secret_key|null)} key packet or null if no signing key has been found
|
||||
* @returns {(module:packet/secret_subkey|module:packet/secret_key|null)} key packet or null if no signing key has been found
|
||||
*/
|
||||
Key.prototype.getSigningKeyPacket = function (keyId=null, date=new Date()) {
|
||||
const primaryUser = this.getPrimaryUser(date);
|
||||
|
@ -379,36 +379,34 @@ Key.prototype.getEncryptionKeyPacket = function(keyId, date=new Date()) {
|
|||
/**
|
||||
* Encrypts all secret key and subkey packets
|
||||
* @param {String} passphrase
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
Key.prototype.encrypt = function(passphrase) {
|
||||
Key.prototype.encrypt = async function(passphrase) {
|
||||
if (!this.isPrivate()) {
|
||||
throw new Error("Nothing to encrypt in a public key");
|
||||
}
|
||||
|
||||
const keys = this.getAllKeyPackets();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
keys[i].encrypt(passphrase);
|
||||
keys[i].clearPrivateParams();
|
||||
}
|
||||
await Promise.all(keys.map(async function(packet) {
|
||||
await packet.encrypt(passphrase);
|
||||
await packet.clearPrivateParams();
|
||||
return packet;
|
||||
}));
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypts all secret key and subkey packets
|
||||
* @param {String} passphrase
|
||||
* @return {Boolean} true if all key and subkey packets decrypted successfully
|
||||
* @returns {Promise<Boolean>} true if all key and subkey packets decrypted successfully
|
||||
*/
|
||||
Key.prototype.decrypt = function(passphrase) {
|
||||
if (this.isPrivate()) {
|
||||
const keys = this.getAllKeyPackets();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const success = keys[i].decrypt(passphrase);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Key.prototype.decrypt = async function(passphrase) {
|
||||
if (!this.isPrivate()) {
|
||||
throw new Error("Nothing to decrypt in a public key");
|
||||
}
|
||||
|
||||
const keys = this.getAllKeyPackets();
|
||||
await Promise.all(keys.map(packet => packet.decrypt(passphrase)));
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -416,7 +414,7 @@ Key.prototype.decrypt = function(passphrase) {
|
|||
* Decrypts specific key packets by key ID
|
||||
* @param {Array<module:type/keyid>} keyIds
|
||||
* @param {String} passphrase
|
||||
* @return {Boolean} true if all key packets decrypted successfully
|
||||
* @returns {Boolean} true if all key packets decrypted successfully
|
||||
*/
|
||||
Key.prototype.decryptKeyPacket = function(keyIds, passphrase) {
|
||||
if (this.isPrivate()) {
|
||||
|
@ -442,7 +440,7 @@ Key.prototype.decryptKeyPacket = function(keyIds, passphrase) {
|
|||
* Verify primary key. Checks for revocation signatures, expiration time
|
||||
* and valid self signature
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @return {module:enums.keyStatus} The status of the primary key
|
||||
* @returns {Promise{module:enums.keyStatus}} The status of the primary key
|
||||
*/
|
||||
Key.prototype.verifyPrimaryKey = async function(date=new Date()) {
|
||||
// TODO clarify OpenPGP's behavior given an expired revocation signature
|
||||
|
@ -484,7 +482,7 @@ Key.prototype.verifyPrimaryKey = async function(date=new Date()) {
|
|||
|
||||
/**
|
||||
* Returns the expiration time of the primary key or null if key does not expire
|
||||
* @return {Date|null}
|
||||
* @returns {Date|null}
|
||||
*/
|
||||
Key.prototype.getExpirationTime = function() {
|
||||
if (this.primaryKey.version === 3) {
|
||||
|
@ -519,7 +517,7 @@ function getExpirationTime(keyPacket, selfCertificate, defaultValue=null) {
|
|||
*
|
||||
* NOTE: call verifyPrimaryUser before calling this function.
|
||||
* @param {Date} date use the given date for verification instead of the current time
|
||||
* @return {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}|null} The primary user and the self signature
|
||||
* @returns {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}|null} The primary user and the self signature
|
||||
*/
|
||||
Key.prototype.getPrimaryUser = function(date=new Date()) {
|
||||
let primaryUsers = [];
|
||||
|
@ -652,7 +650,7 @@ Key.prototype.revoke = function() {
|
|||
/**
|
||||
* Signs primary user of key
|
||||
* @param {Array<module:key~Key>} privateKey decrypted private keys for signing
|
||||
* @return {module:key~Key} new public key with new certificate signature
|
||||
* @returns {Promise<module:key~Key>} new public key with new certificate signature
|
||||
*/
|
||||
Key.prototype.signPrimaryUser = async function(privateKeys) {
|
||||
await this.verifyPrimaryUser();
|
||||
|
@ -669,7 +667,7 @@ Key.prototype.signPrimaryUser = async function(privateKeys) {
|
|||
/**
|
||||
* Signs all users of key
|
||||
* @param {Array<module:key~Key>} privateKeys decrypted private keys for signing
|
||||
* @return {module:key~Key} new public key with new certificate signature
|
||||
* @returns {Promise<module:key~Key>} new public key with new certificate signature
|
||||
*/
|
||||
Key.prototype.signAllUsers = async function(privateKeys) {
|
||||
const that = this;
|
||||
|
@ -685,7 +683,7 @@ Key.prototype.signAllUsers = async function(privateKeys) {
|
|||
* - if no arguments are given, verifies the self certificates;
|
||||
* - otherwise, verifies all certificates signed with given keys.
|
||||
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
|
||||
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
* @returns {Promise<Array<({keyid: module:type/keyid, valid: Boolean})>>} list of signer's keyid and validity of signature
|
||||
*/
|
||||
Key.prototype.verifyPrimaryUser = async function(keys) {
|
||||
const { primaryKey } = this;
|
||||
|
@ -735,7 +733,7 @@ Key.prototype.verifyPrimaryUser = async function(keys) {
|
|||
* - if no arguments are given, verifies the self certificates;
|
||||
* - otherwise, verifies all certificates signed with given keys.
|
||||
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
|
||||
* @return {Array<({userid: String, keyid: module:type/keyid, valid: Boolean})>} list of userid, signer's keyid and validity of signature
|
||||
* @returns {Promise<Array<({userid: String, keyid: module:type/keyid, valid: Boolean})>>} list of userid, signer's keyid and validity of signature
|
||||
*/
|
||||
Key.prototype.verifyAllUsers = async function(keys) {
|
||||
const results = [];
|
||||
|
@ -771,7 +769,7 @@ function User(userPacket) {
|
|||
|
||||
/**
|
||||
* Transforms structured user data to packetlist
|
||||
* @return {module:packet/packetlist}
|
||||
* @returns {module:packet/packetlist}
|
||||
*/
|
||||
User.prototype.toPacketlist = function() {
|
||||
const packetlist = new packet.List();
|
||||
|
@ -788,7 +786,7 @@ User.prototype.toPacketlist = function() {
|
|||
* @param {module:packet/signature} certificate The certificate to verify
|
||||
* @param {module:packet/public_subkey|module:packet/public_key|
|
||||
* module:packet/secret_subkey|module:packet/secret_key} key, optional The key to verify the signature
|
||||
* @return {Boolean} True if the certificate is revoked
|
||||
* @returns {Promise<Boolean>} True if the certificate is revoked
|
||||
*/
|
||||
User.prototype.isRevoked = async function(primaryKey, certificate, key) {
|
||||
certificate.revoked = null;
|
||||
|
@ -810,7 +808,7 @@ User.prototype.isRevoked = async function(primaryKey, certificate, key) {
|
|||
* Signs user
|
||||
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
|
||||
* @param {Array<module:key~Key>} privateKeys decrypted private keys for signing
|
||||
* @return {module:key~Key} new user with new certificate signatures
|
||||
* @returns {Promise<module:key~Key>} new user with new certificate signatures
|
||||
*/
|
||||
User.prototype.sign = async function(primaryKey, privateKeys) {
|
||||
const dataToSign = { userid: this.userId || this.userAttribute, key: primaryKey };
|
||||
|
@ -851,7 +849,7 @@ User.prototype.sign = async function(primaryKey, privateKeys) {
|
|||
* @param {module:packet/signature} certificate A certificate of this user
|
||||
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
|
||||
* @param {Date} date use the given date for verification instead of the current time
|
||||
* @return {module:enums.keyStatus} status of the certificate
|
||||
* @returns {Promise<module:enums.keyStatus>} status of the certificate
|
||||
*/
|
||||
User.prototype.verifyCertificate = async function(primaryKey, certificate, keys, date=new Date()) {
|
||||
const that = this;
|
||||
|
@ -879,7 +877,7 @@ User.prototype.verifyCertificate = async function(primaryKey, certificate, keys,
|
|||
* Verifies all user certificates
|
||||
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
|
||||
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
|
||||
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
* @returns {Promise<Array<({keyid: module:type/keyid, valid: Boolean})>>} list of signer's keyid and validity of signature
|
||||
*/
|
||||
User.prototype.verifyAllCertifications = async function(primaryKey, keys) {
|
||||
const that = this;
|
||||
|
@ -897,7 +895,7 @@ User.prototype.verifyAllCertifications = async function(primaryKey, keys) {
|
|||
* Verify User. Checks for existence of self signatures, revocation signatures
|
||||
* and validity of self signature
|
||||
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
|
||||
* @return {module:enums.keyStatus} status of user
|
||||
* @returns {Promise<module:enums.keyStatus>} status of user
|
||||
*/
|
||||
User.prototype.verify = async function(primaryKey) {
|
||||
if (!this.selfCertifications) {
|
||||
|
@ -925,7 +923,7 @@ User.prototype.verify = async function(primaryKey) {
|
|||
/**
|
||||
* 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
|
||||
* @param {Promise<module:packet/signature>} primaryKey primary key used for validation
|
||||
*/
|
||||
User.prototype.update = async function(user, primaryKey) {
|
||||
const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
|
||||
|
@ -954,7 +952,7 @@ function SubKey(subKeyPacket) {
|
|||
|
||||
/**
|
||||
* Transforms structured subkey data to packetlist
|
||||
* @return {module:packet/packetlist}
|
||||
* @returns {module:packet/packetlist}
|
||||
*/
|
||||
SubKey.prototype.toPacketlist = function() {
|
||||
const packetlist = new packet.List();
|
||||
|
@ -970,7 +968,7 @@ SubKey.prototype.toPacketlist = function() {
|
|||
* Returns true if the subkey can be used for encryption
|
||||
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
|
||||
* @param {Date} date use the given date for verification instead of the current time
|
||||
* @return {Boolean}
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
SubKey.prototype.isValidEncryptionKey = async function(primaryKey, date=new Date()) {
|
||||
if (await this.verify(primaryKey, date) !== enums.keyStatus.valid) {
|
||||
|
@ -988,7 +986,7 @@ SubKey.prototype.isValidEncryptionKey = async function(primaryKey, date=new Date
|
|||
* Returns true if the subkey can be used for signing of data
|
||||
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
|
||||
* @param {Date} date use the given date for verification instead of the current time
|
||||
* @return {Boolean}
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
SubKey.prototype.isValidSigningKey = async function(primaryKey, date=new Date()) {
|
||||
if (await this.verify(primaryKey, date) !== enums.keyStatus.valid) {
|
||||
|
@ -1007,7 +1005,7 @@ SubKey.prototype.isValidSigningKey = async function(primaryKey, date=new Date())
|
|||
* and valid binding signature
|
||||
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
|
||||
* @param {Date} date use the given date for verification instead of the current time
|
||||
* @return {module:enums.keyStatus} The status of the subkey
|
||||
* @returns {Promise<module:enums.keyStatus>} The status of the subkey
|
||||
*/
|
||||
SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
|
||||
const that = this;
|
||||
|
@ -1054,7 +1052,7 @@ SubKey.prototype.verify = async function(primaryKey, date=new Date()) {
|
|||
|
||||
/**
|
||||
* Returns the expiration time of the subkey or null if key does not expire
|
||||
* @return {Date|null}
|
||||
* @returns {Date|null}
|
||||
*/
|
||||
SubKey.prototype.getExpirationTime = function() {
|
||||
let highest;
|
||||
|
@ -1073,7 +1071,7 @@ SubKey.prototype.getExpirationTime = function() {
|
|||
/**
|
||||
* 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
|
||||
* @param {Promise<module:packet/signature>} primaryKey primary key used for validation
|
||||
*/
|
||||
SubKey.prototype.update = async function(subKey, primaryKey) {
|
||||
if (await subKey.verify(primaryKey) === enums.keyStatus.invalid) {
|
||||
|
@ -1115,7 +1113,7 @@ SubKey.prototype.update = async function(subKey, primaryKey) {
|
|||
/**
|
||||
* Reads an unarmored OpenPGP key list and returns one or multiple key objects
|
||||
* @param {Uint8Array} data to be parsed
|
||||
* @return {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
|
||||
* @returns {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
|
||||
* @static
|
||||
*/
|
||||
export function read(data) {
|
||||
|
@ -1148,7 +1146,7 @@ export function read(data) {
|
|||
/**
|
||||
* Reads an OpenPGP armored text and returns one or multiple key objects
|
||||
* @param {String} armoredText text to be parsed
|
||||
* @return {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
|
||||
* @returns {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
|
||||
* @static
|
||||
*/
|
||||
export function readArmored(armoredText) {
|
||||
|
@ -1176,7 +1174,7 @@ export function readArmored(armoredText) {
|
|||
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
|
||||
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
|
||||
* @param {Number} [options.keyExpirationTime=0] The number of seconds after the key creation time that the key expires
|
||||
* @return {module:key~Key}
|
||||
* @returns {module:key~Key}
|
||||
* @static
|
||||
*/
|
||||
export function generate(options) {
|
||||
|
@ -1251,49 +1249,51 @@ export function generate(options) {
|
|||
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
|
||||
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
|
||||
* @param {Number} [options.keyExpirationTime=0] The number of seconds after the key creation time that the key expires
|
||||
* @return {module:key~Key}
|
||||
* @returns {Promise<module:key~Key>}
|
||||
* @static
|
||||
*/
|
||||
export function reformat(options) {
|
||||
export async function reformat(options) {
|
||||
let secretKeyPacket;
|
||||
let secretSubkeyPacket;
|
||||
return Promise.resolve().then(() => {
|
||||
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
|
||||
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
||||
throw new Error('Only RSA Encrypt or Sign supported');
|
||||
}
|
||||
|
||||
if (!options.privateKey.decrypt()) {
|
||||
throw new Error('Key not decrypted');
|
||||
}
|
||||
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
|
||||
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
|
||||
throw new Error('Only RSA Encrypt or Sign supported');
|
||||
}
|
||||
|
||||
if (!options.passphrase) { // Key without passphrase is unlocked by definition
|
||||
options.unlocked = true;
|
||||
try {
|
||||
await options.privateKey.decrypt();
|
||||
}
|
||||
catch(err) {
|
||||
throw new Error('Key not decrypted');
|
||||
}
|
||||
|
||||
if (!options.passphrase) { // Key without passphrase is unlocked by definition
|
||||
options.unlocked = true;
|
||||
}
|
||||
if (util.isString(options.userIds)) {
|
||||
options.userIds = [options.userIds];
|
||||
}
|
||||
const packetlist = options.privateKey.toPacketlist();
|
||||
for (let i = 0; i < packetlist.length; i++) {
|
||||
if (packetlist[i].tag === enums.packet.secretKey) {
|
||||
secretKeyPacket = packetlist[i];
|
||||
options.keyType = secretKeyPacket.algorithm;
|
||||
} else if (packetlist[i].tag === enums.packet.secretSubkey) {
|
||||
secretSubkeyPacket = packetlist[i];
|
||||
options.subkeyType = secretSubkeyPacket.algorithm;
|
||||
}
|
||||
if (util.isString(options.userIds)) {
|
||||
options.userIds = [options.userIds];
|
||||
}
|
||||
const packetlist = options.privateKey.toPacketlist();
|
||||
for (let i = 0; i < packetlist.length; i++) {
|
||||
if (packetlist[i].tag === enums.packet.secretKey) {
|
||||
secretKeyPacket = packetlist[i];
|
||||
options.keyType = secretKeyPacket.algorithm;
|
||||
} else if (packetlist[i].tag === enums.packet.secretSubkey) {
|
||||
secretSubkeyPacket = packetlist[i];
|
||||
options.subkeyType = secretSubkeyPacket.algorithm;
|
||||
}
|
||||
}
|
||||
if (!secretKeyPacket) {
|
||||
throw new Error('Key does not contain a secret key packet');
|
||||
}
|
||||
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
|
||||
});
|
||||
}
|
||||
if (!secretKeyPacket) {
|
||||
throw new Error('Key does not contain a secret key packet');
|
||||
}
|
||||
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
|
||||
}
|
||||
|
||||
async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
|
||||
// set passphrase protection
|
||||
if (options.passphrase) {
|
||||
secretKeyPacket.encrypt(options.passphrase);
|
||||
await secretKeyPacket.encrypt(options.passphrase);
|
||||
if (secretSubkeyPacket) {
|
||||
secretSubkeyPacket.encrypt(options.passphrase);
|
||||
}
|
||||
|
@ -1383,7 +1383,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
|
|||
/**
|
||||
* Returns the preferred signature hash algorithm of a key
|
||||
* @param {object} key
|
||||
* @return {String}
|
||||
* @returns {String}
|
||||
*/
|
||||
export function getPreferredHashAlgo(key) {
|
||||
let hash_algo = config.prefer_hash_algorithm;
|
||||
|
@ -1417,7 +1417,7 @@ export function getPreferredHashAlgo(key) {
|
|||
/**
|
||||
* 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
|
||||
* @returns {enums.symmetric} Preferred symmetric algorithm
|
||||
*/
|
||||
export function getPreferredSymAlgo(keys) {
|
||||
const prioMap = {};
|
||||
|
|
351
src/message.js
351
src/message.js
|
@ -55,7 +55,7 @@ export function Message(packetlist) {
|
|||
|
||||
/**
|
||||
* Returns the key IDs of the keys to which the session key is encrypted
|
||||
* @return {Array<module:type/keyid>} array of keyid objects
|
||||
* @returns {Array<module:type/keyid>} array of keyid objects
|
||||
*/
|
||||
Message.prototype.getEncryptionKeyIds = function() {
|
||||
const keyIds = [];
|
||||
|
@ -68,7 +68,7 @@ Message.prototype.getEncryptionKeyIds = function() {
|
|||
|
||||
/**
|
||||
* Returns the key IDs of the keys that signed the message
|
||||
* @return {Array<module:type/keyid>} array of keyid objects
|
||||
* @returns {Array<module:type/keyid>} array of keyid objects
|
||||
*/
|
||||
Message.prototype.getSigningKeyIds = function() {
|
||||
const keyIds = [];
|
||||
|
@ -93,7 +93,7 @@ Message.prototype.getSigningKeyIds = function() {
|
|||
* @param {Array<Key>} privateKeys (optional) private keys with decrypted secret data
|
||||
* @param {Array<String>} passwords (optional) passwords used to decrypt
|
||||
* @param {Array<Object>} sessionKeys (optional) session keys in the form: { data:Uint8Array, algorithm:String }
|
||||
* @return {Message} new message with decrypted content
|
||||
* @returns {Promise<Message>} new message with decrypted content
|
||||
*/
|
||||
Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys) {
|
||||
const keyObjs = sessionKeys || await this.decryptSessionKeys(privateKeys, passwords);
|
||||
|
@ -105,7 +105,7 @@ Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys)
|
|||
);
|
||||
|
||||
if (symEncryptedPacketlist.length === 0) {
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
|
||||
const symEncryptedPacket = symEncryptedPacketlist[0];
|
||||
|
@ -138,74 +138,73 @@ Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys)
|
|||
* Decrypt encrypted session keys either with private keys or passwords.
|
||||
* @param {Array<Key>} privateKeys (optional) private keys with decrypted secret data
|
||||
* @param {Array<String>} passwords (optional) passwords used to decrypt
|
||||
* @return {Array<{ data:Uint8Array, algorithm:String }>} array of object with potential sessionKey, algorithm pairs
|
||||
* @returns {Promise{Array<{ data:Uint8Array, algorithm:String }>}} array of object with potential sessionKey, algorithm pairs
|
||||
*/
|
||||
Message.prototype.decryptSessionKeys = function(privateKeys, passwords) {
|
||||
Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
||||
let keyPackets = [];
|
||||
return Promise.resolve().then(async () => {
|
||||
if (passwords) {
|
||||
const symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey);
|
||||
if (!symESKeyPacketlist) {
|
||||
throw new Error('No symmetrically encrypted session key packet found.');
|
||||
}
|
||||
await Promise.all(symESKeyPacketlist.map(async function(packet) {
|
||||
await Promise.all(passwords.map(async function(password) {
|
||||
try {
|
||||
await packet.decrypt(password);
|
||||
keyPackets.push(packet);
|
||||
} catch (err) {}
|
||||
}));
|
||||
}));
|
||||
|
||||
} else if (privateKeys) {
|
||||
const pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey);
|
||||
if (!pkESKeyPacketlist) {
|
||||
throw new Error('No public key encrypted session key packet found.');
|
||||
}
|
||||
await Promise.all(pkESKeyPacketlist.map(async function(packet) {
|
||||
const privateKeyPackets = privateKeys.reduce(function(acc, privateKey) {
|
||||
return acc.concat(privateKey.getKeyPackets(packet.publicKeyId));
|
||||
}, []);
|
||||
await Promise.all(privateKeyPackets.map(async function(privateKeyPacket) {
|
||||
if (!privateKeyPacket) {
|
||||
return;
|
||||
}
|
||||
if (!privateKeyPacket.isDecrypted) {
|
||||
throw new Error('Private key is not decrypted.');
|
||||
}
|
||||
try {
|
||||
await packet.decrypt(privateKeyPacket);
|
||||
keyPackets.push(packet);
|
||||
} catch (err) {}
|
||||
}));
|
||||
if (passwords) {
|
||||
const symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey);
|
||||
if (!symESKeyPacketlist) {
|
||||
throw new Error('No symmetrically encrypted session key packet found.');
|
||||
}
|
||||
await Promise.all(symESKeyPacketlist.map(async function(packet) {
|
||||
await Promise.all(passwords.map(async function(password) {
|
||||
try {
|
||||
await packet.decrypt(password);
|
||||
keyPackets.push(packet);
|
||||
} catch (err) {}
|
||||
}));
|
||||
} else {
|
||||
throw new Error('No key or password specified.');
|
||||
}
|
||||
}).then(() => {
|
||||
if (keyPackets.length) {
|
||||
// Return only unique session keys
|
||||
if (keyPackets.length > 1) {
|
||||
const seen = {};
|
||||
keyPackets = keyPackets.filter(function(item) {
|
||||
const k = item.sessionKeyAlgorithm + util.Uint8Array_to_str(item.sessionKey);
|
||||
if (seen.hasOwnProperty(k)) {
|
||||
return false;
|
||||
}
|
||||
seen[k] = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
return keyPackets.map(packet => ({ data: packet.sessionKey, algorithm: packet.sessionKeyAlgorithm }));
|
||||
} else if (privateKeys) {
|
||||
const pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey);
|
||||
if (!pkESKeyPacketlist) {
|
||||
throw new Error('No public key encrypted session key packet found.');
|
||||
}
|
||||
throw new Error('Session key decryption failed.');
|
||||
});
|
||||
await Promise.all(pkESKeyPacketlist.map(async function(packet) {
|
||||
const privateKeyPackets = privateKeys.reduce(function(acc, privateKey) {
|
||||
return acc.concat(privateKey.getKeyPackets(packet.publicKeyId));
|
||||
}, []);
|
||||
await Promise.all(privateKeyPackets.map(async function(privateKeyPacket) {
|
||||
if (!privateKeyPacket) {
|
||||
return;
|
||||
}
|
||||
if (!privateKeyPacket.isDecrypted) {
|
||||
throw new Error('Private key is not decrypted.');
|
||||
}
|
||||
try {
|
||||
await packet.decrypt(privateKeyPacket);
|
||||
keyPackets.push(packet);
|
||||
} catch (err) {}
|
||||
}));
|
||||
}));
|
||||
} else {
|
||||
throw new Error('No key or password specified.');
|
||||
}
|
||||
|
||||
if (keyPackets.length) {
|
||||
// Return only unique session keys
|
||||
if (keyPackets.length > 1) {
|
||||
const seen = {};
|
||||
keyPackets = keyPackets.filter(function(item) {
|
||||
const k = item.sessionKeyAlgorithm + util.Uint8Array_to_str(item.sessionKey);
|
||||
if (seen.hasOwnProperty(k)) {
|
||||
return false;
|
||||
}
|
||||
seen[k] = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return keyPackets.map(packet => ({ data: packet.sessionKey, algorithm: packet.sessionKeyAlgorithm }));
|
||||
}
|
||||
throw new Error('Session key decryption failed.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get literal data that is the body of the message
|
||||
* @return {(Uint8Array|null)} literal body of the message as Uint8Array
|
||||
* @returns {(Uint8Array|null)} literal body of the message as Uint8Array
|
||||
*/
|
||||
Message.prototype.getLiteralData = function() {
|
||||
const literal = this.packets.findPacket(enums.packet.literal);
|
||||
|
@ -214,7 +213,7 @@ Message.prototype.getLiteralData = function() {
|
|||
|
||||
/**
|
||||
* Get filename from literal data packet
|
||||
* @return {(String|null)} filename of literal data packet as string
|
||||
* @returns {(String|null)} filename of literal data packet as string
|
||||
*/
|
||||
Message.prototype.getFilename = function() {
|
||||
const literal = this.packets.findPacket(enums.packet.literal);
|
||||
|
@ -223,7 +222,7 @@ Message.prototype.getFilename = function() {
|
|||
|
||||
/**
|
||||
* Get literal data as text
|
||||
* @return {(String|null)} literal body of the message interpreted as text
|
||||
* @returns {(String|null)} literal body of the message interpreted as text
|
||||
*/
|
||||
Message.prototype.getText = function() {
|
||||
const literal = this.packets.findPacket(enums.packet.literal);
|
||||
|
@ -240,54 +239,52 @@ Message.prototype.getText = function() {
|
|||
* @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String }
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
* @param {Date} date (optional) override the creation date of the literal package
|
||||
* @return {Message} new message with encrypted content
|
||||
* @returns {Promise<Message>} new message with encrypted content
|
||||
*/
|
||||
Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard=false, date=new Date()) {
|
||||
Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard=false, date=new Date()) {
|
||||
let symAlgo;
|
||||
let msg;
|
||||
let symEncryptedPacket;
|
||||
return Promise.resolve().then(async () => {
|
||||
if (sessionKey) {
|
||||
if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) {
|
||||
throw new Error('Invalid session key for encryption.');
|
||||
}
|
||||
symAlgo = sessionKey.algorithm;
|
||||
sessionKey = sessionKey.data;
|
||||
} else if (keys && keys.length) {
|
||||
symAlgo = enums.read(enums.symmetric, getPreferredSymAlgo(keys));
|
||||
} else if (passwords && passwords.length) {
|
||||
symAlgo = enums.read(enums.symmetric, config.encryption_cipher);
|
||||
} else {
|
||||
throw new Error('No keys, passwords, or session key provided.');
|
||||
|
||||
if (sessionKey) {
|
||||
if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) {
|
||||
throw new Error('Invalid session key for encryption.');
|
||||
}
|
||||
symAlgo = sessionKey.algorithm;
|
||||
sessionKey = sessionKey.data;
|
||||
} else if (keys && keys.length) {
|
||||
symAlgo = enums.read(enums.symmetric, getPreferredSymAlgo(keys));
|
||||
} else if (passwords && passwords.length) {
|
||||
symAlgo = enums.read(enums.symmetric, config.encryption_cipher);
|
||||
} else {
|
||||
throw new Error('No keys, passwords, or session key provided.');
|
||||
}
|
||||
|
||||
if (!sessionKey) {
|
||||
sessionKey = crypto.generateSessionKey(symAlgo);
|
||||
if (!sessionKey) {
|
||||
sessionKey = await crypto.generateSessionKey(symAlgo);
|
||||
}
|
||||
|
||||
const msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, wildcard, date);
|
||||
|
||||
if (config.aead_protect) {
|
||||
symEncryptedPacket = new packet.SymEncryptedAEADProtected();
|
||||
} else if (config.integrity_protect) {
|
||||
symEncryptedPacket = new packet.SymEncryptedIntegrityProtected();
|
||||
} else {
|
||||
symEncryptedPacket = new packet.SymmetricallyEncrypted();
|
||||
}
|
||||
symEncryptedPacket.packets = this.packets;
|
||||
|
||||
await symEncryptedPacket.encrypt(symAlgo, sessionKey);
|
||||
|
||||
msg.packets.push(symEncryptedPacket);
|
||||
symEncryptedPacket.packets = new packet.List(); // remove packets after encryption
|
||||
return {
|
||||
message: msg,
|
||||
sessionKey: {
|
||||
data: sessionKey,
|
||||
algorithm: symAlgo
|
||||
}
|
||||
|
||||
msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, wildcard, date);
|
||||
|
||||
if (config.aead_protect) {
|
||||
symEncryptedPacket = new packet.SymEncryptedAEADProtected();
|
||||
} else if (config.integrity_protect) {
|
||||
symEncryptedPacket = new packet.SymEncryptedIntegrityProtected();
|
||||
} else {
|
||||
symEncryptedPacket = new packet.SymmetricallyEncrypted();
|
||||
}
|
||||
symEncryptedPacket.packets = this.packets;
|
||||
|
||||
return symEncryptedPacket.encrypt(symAlgo, sessionKey);
|
||||
}).then(() => {
|
||||
msg.packets.push(symEncryptedPacket);
|
||||
symEncryptedPacket.packets = new packet.List(); // remove packets after encryption
|
||||
return {
|
||||
message: msg,
|
||||
sessionKey: {
|
||||
data: sessionKey,
|
||||
algorithm: symAlgo
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -298,64 +295,64 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard=false
|
|||
* @param {Array<String>} passwords (optional) for message encryption
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
* @param {Date} date (optional) override the creation date signature
|
||||
* @return {Message} new message with encrypted content
|
||||
* @returns {Promise<Message>} new message with encrypted content
|
||||
*/
|
||||
export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wildcard=false, date=new Date()) {
|
||||
export async function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wildcard=false, date=new Date()) {
|
||||
const packetlist = new packet.List();
|
||||
|
||||
return Promise.resolve().then(async () => {
|
||||
if (publicKeys) {
|
||||
const results = await Promise.all(publicKeys.map(async function(key) {
|
||||
await key.verifyPrimaryUser();
|
||||
const encryptionKeyPacket = key.getEncryptionKeyPacket(undefined, date);
|
||||
if (!encryptionKeyPacket) {
|
||||
throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex());
|
||||
if (publicKeys) {
|
||||
const results = await Promise.all(publicKeys.map(async function(key) {
|
||||
await key.verifyPrimaryUser();
|
||||
const encryptionKeyPacket = key.getEncryptionKeyPacket(undefined, date);
|
||||
if (!encryptionKeyPacket) {
|
||||
throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex());
|
||||
}
|
||||
const pkESKeyPacket = new packet.PublicKeyEncryptedSessionKey();
|
||||
pkESKeyPacket.publicKeyId = wildcard ? type_keyid.wildcard() : encryptionKeyPacket.getKeyId();
|
||||
pkESKeyPacket.publicKeyAlgorithm = encryptionKeyPacket.algorithm;
|
||||
pkESKeyPacket.sessionKey = sessionKey;
|
||||
pkESKeyPacket.sessionKeyAlgorithm = symAlgo;
|
||||
await pkESKeyPacket.encrypt(encryptionKeyPacket);
|
||||
delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption
|
||||
return pkESKeyPacket;
|
||||
}));
|
||||
packetlist.concat(results);
|
||||
}
|
||||
|
||||
if (passwords) {
|
||||
const testDecrypt = async function(keyPacket, password) {
|
||||
try {
|
||||
await keyPacket.decrypt(password);
|
||||
return 1;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const sum = (accumulator, currentValue) => accumulator + currentValue;
|
||||
|
||||
const encryptPassword = async function(sessionKey, symAlgo, password) {
|
||||
const symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey();
|
||||
symEncryptedSessionKeyPacket.sessionKey = sessionKey;
|
||||
symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo;
|
||||
await symEncryptedSessionKeyPacket.encrypt(password);
|
||||
|
||||
if (config.password_collision_check) {
|
||||
const results = await Promise.all(passwords.map(pwd => testDecrypt(symEncryptedSessionKeyPacket, pwd)));
|
||||
if (results.reduce(sum) !== 1) {
|
||||
return encryptPassword(sessionKey, symAlgo, password);
|
||||
}
|
||||
const pkESKeyPacket = new packet.PublicKeyEncryptedSessionKey();
|
||||
pkESKeyPacket.publicKeyId = wildcard ? type_keyid.wildcard() : encryptionKeyPacket.getKeyId();
|
||||
pkESKeyPacket.publicKeyAlgorithm = encryptionKeyPacket.algorithm;
|
||||
pkESKeyPacket.sessionKey = sessionKey;
|
||||
pkESKeyPacket.sessionKeyAlgorithm = symAlgo;
|
||||
await pkESKeyPacket.encrypt(encryptionKeyPacket);
|
||||
delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption
|
||||
return pkESKeyPacket;
|
||||
}));
|
||||
packetlist.concat(results);
|
||||
}
|
||||
}
|
||||
|
||||
if (passwords) {
|
||||
const testDecrypt = async function(keyPacket, password) {
|
||||
try {
|
||||
await keyPacket.decrypt(password);
|
||||
return 1;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
delete symEncryptedSessionKeyPacket.sessionKey; // delete plaintext session key after encryption
|
||||
return symEncryptedSessionKeyPacket;
|
||||
};
|
||||
|
||||
const sum = (accumulator, currentValue) => accumulator + currentValue;
|
||||
const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symAlgo, pwd)));
|
||||
packetlist.concat(results);
|
||||
}
|
||||
|
||||
const encryptPassword = async function(sessionKey, symAlgo, password) {
|
||||
const symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey();
|
||||
symEncryptedSessionKeyPacket.sessionKey = sessionKey;
|
||||
symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo;
|
||||
await symEncryptedSessionKeyPacket.encrypt(password);
|
||||
|
||||
if (config.password_collision_check) {
|
||||
const results = await Promise.all(passwords.map(pwd => testDecrypt(symEncryptedSessionKeyPacket, pwd)));
|
||||
if (results.reduce(sum) !== 1) {
|
||||
return encryptPassword(sessionKey, symAlgo, password);
|
||||
}
|
||||
}
|
||||
|
||||
delete symEncryptedSessionKeyPacket.sessionKey; // delete plaintext session key after encryption
|
||||
return symEncryptedSessionKeyPacket;
|
||||
};
|
||||
|
||||
const results = await Promise.all(passwords.map(pwd => encryptPassword(sessionKey, symAlgo, pwd)));
|
||||
packetlist.concat(results);
|
||||
}
|
||||
}).then(() => new Message(packetlist));
|
||||
return new Message(packetlist);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -363,7 +360,7 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords, wi
|
|||
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
|
||||
* @param {Signature} signature (optional) any existing detached signature to add to the message
|
||||
* @param {Date} date} (optional) override the creation time of the signature
|
||||
* @return {module:message~Message} new message with signed content
|
||||
* @returns {Promise<Message>} new message with signed content
|
||||
*/
|
||||
Message.prototype.sign = async function(privateKeys=[], signature=null, date=new Date()) {
|
||||
const packetlist = new packet.List();
|
||||
|
@ -427,7 +424,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, date=new
|
|||
/**
|
||||
* Compresses the message (the literal and -if signed- signature data packets of the message)
|
||||
* @param {module:enums.compression} compression compression algorithm to be used
|
||||
* @return {module:message~Message} new message with compressed content
|
||||
* @returns {module:message~Message} new message with compressed content
|
||||
*/
|
||||
Message.prototype.compress = function(compression) {
|
||||
if (compression === enums.compression.uncompressed) {
|
||||
|
@ -446,10 +443,10 @@ Message.prototype.compress = function(compression) {
|
|||
|
||||
/**
|
||||
* Create a detached signature for the message (the literal data packet of the message)
|
||||
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
|
||||
* @param {Signature} signature (optional) any existing detached signature
|
||||
* @param {Date} date (optional) override the creation time of the signature
|
||||
* @return {module:signature~Signature} new detached signature of message content
|
||||
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
|
||||
* @param {Signature} signature (optional) any existing detached signature
|
||||
* @param {Date} date (optional) override the creation time of the signature
|
||||
* @returns {Promise<module:signature~Signature>} new detached signature of message content
|
||||
*/
|
||||
Message.prototype.signDetached = async function(privateKeys=[], signature=null, date=new Date()) {
|
||||
const literalDataPacket = this.packets.findPacket(enums.packet.literal);
|
||||
|
@ -461,11 +458,11 @@ Message.prototype.signDetached = async function(privateKeys=[], signature=null,
|
|||
|
||||
/**
|
||||
* Create signature packets for the message
|
||||
* @param {module:packet/literal} literalDataPacket the literal data packet to sign
|
||||
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
|
||||
* @param {Signature} signature (optional) any existing detached signature to append
|
||||
* @param {Date} date (optional) override the creationtime of the signature
|
||||
* @return {module:packet/packetlist} list of signature packets
|
||||
* @param {module:packet/literal} literalDataPacket the literal data packet to sign
|
||||
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
|
||||
* @param {Signature} signature (optional) any existing detached signature to append
|
||||
* @param {Date} date (optional) override the creationtime of the signature
|
||||
* @returns {Promise<module:packet/packetlist>} list of signature packets
|
||||
*/
|
||||
export async function createSignaturePackets(literalDataPacket, privateKeys, signature=null, date=new Date()) {
|
||||
const packetlist = new packet.List();
|
||||
|
@ -507,7 +504,7 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
|
|||
* Verify message signatures
|
||||
* @param {Array<module:key~Key>} keys array of keys to verify signatures
|
||||
* @param {Date} date (optional) Verify the signature against the given date, i.e. check signature creation time < date < expiration time
|
||||
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
* @returns {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
*/
|
||||
Message.prototype.verify = function(keys, date=new Date()) {
|
||||
const msg = this.unwrapCompressed();
|
||||
|
@ -524,7 +521,7 @@ Message.prototype.verify = function(keys, date=new Date()) {
|
|||
* @param {Array<module:key~Key>} keys array of keys to verify signatures
|
||||
* @param {Signature} signature
|
||||
* @param {Date} date Verify the signature against the given date, i.e. check signature creation time < date < expiration time
|
||||
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
* @returns {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
*/
|
||||
Message.prototype.verifyDetached = function(signature, keys, date=new Date()) {
|
||||
const msg = this.unwrapCompressed();
|
||||
|
@ -542,7 +539,7 @@ Message.prototype.verifyDetached = function(signature, keys, date=new Date()) {
|
|||
* @param {Array<module:packet/literal>} literalDataList array of literal data packets
|
||||
* @param {Array<module:key~Key>} keys array of keys to verify signatures
|
||||
* @param {Date} date Verify the signature against the given date, i.e. check signature creation time < date < expiration time
|
||||
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
|
||||
* @returns {Promise{Array<({keyid: module:type/keyid, valid: Boolean})>}} list of signer's keyid and validity of signature
|
||||
*/
|
||||
export async function createVerificationObjects(signatureList, literalDataList, keys, date=new Date()) {
|
||||
return Promise.all(signatureList.map(async function(signature) {
|
||||
|
@ -572,7 +569,7 @@ export async function createVerificationObjects(signatureList, literalDataList,
|
|||
|
||||
/**
|
||||
* Unwrap compressed message
|
||||
* @return {module:message~Message} message Content of compressed message
|
||||
* @returns {module:message~Message} message Content of compressed message
|
||||
*/
|
||||
Message.prototype.unwrapCompressed = function() {
|
||||
const compressed = this.packets.filterByTag(enums.packet.compressed);
|
||||
|
@ -592,7 +589,7 @@ Message.prototype.appendSignature = function(detachedSignature) {
|
|||
|
||||
/**
|
||||
* Returns ASCII armored text of message
|
||||
* @return {String} ASCII armor
|
||||
* @returns {String} ASCII armor
|
||||
*/
|
||||
Message.prototype.armor = function() {
|
||||
return armor.encode(enums.armor.message, this.packets.write());
|
||||
|
@ -601,7 +598,7 @@ Message.prototype.armor = function() {
|
|||
/**
|
||||
* reads an OpenPGP armored message and returns a message object
|
||||
* @param {String} armoredText text to be parsed
|
||||
* @return {module:message~Message} new message object
|
||||
* @returns {module:message~Message} new message object
|
||||
* @static
|
||||
*/
|
||||
export function readArmored(armoredText) {
|
||||
|
@ -614,7 +611,7 @@ export function readArmored(armoredText) {
|
|||
/**
|
||||
* reads an OpenPGP message as byte array and returns a message object
|
||||
* @param {Uint8Array} input binary message
|
||||
* @return {Message} new message object
|
||||
* @returns {Message} new message object
|
||||
* @static
|
||||
*/
|
||||
export function read(input) {
|
||||
|
@ -628,7 +625,7 @@ export function read(input) {
|
|||
* @param {String} text
|
||||
* @param {String} filename (optional)
|
||||
* @param {Date} date (optional)
|
||||
* @return {module:message~Message} new message object
|
||||
* @returns {module:message~Message} new message object
|
||||
* @static
|
||||
*/
|
||||
export function fromText(text, filename, date=new Date()) {
|
||||
|
@ -648,7 +645,7 @@ export function fromText(text, filename, date=new Date()) {
|
|||
* @param {Uint8Array} bytes
|
||||
* @param {String} filename (optional)
|
||||
* @param {Date} date (optional)
|
||||
* @return {module:message~Message} new message object
|
||||
* @returns {module:message~Message} new message object
|
||||
* @static
|
||||
*/
|
||||
export function fromBinary(bytes, filename, date=new Date()) {
|
||||
|
|
|
@ -55,19 +55,20 @@ let asyncProxy; // instance of the asyncproxy
|
|||
|
||||
/**
|
||||
* Set the path for the web worker script and create an instance of the async proxy
|
||||
* @param {String} path relative path to the worker scripts, default: 'openpgp.worker.js'
|
||||
* @param {Object} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js'
|
||||
* @param {String} path relative path to the worker scripts, default: 'openpgp.worker.js'
|
||||
* @param {Number} n number of workers to initialize
|
||||
* @param {Array<Object>} workers alternative to path parameter: web workers initialized with 'openpgp.worker.js'
|
||||
*/
|
||||
export function initWorker({ path='openpgp.worker.js', worker } = {}) {
|
||||
if (worker || (typeof window !== 'undefined' && window.Worker)) {
|
||||
asyncProxy = new AsyncProxy({ path, worker, config });
|
||||
export function initWorker({ path='openpgp.worker.js', n = 1, workers = [] } = {}) {
|
||||
if (workers.length || (typeof window !== 'undefined' && window.Worker)) {
|
||||
asyncProxy = new AsyncProxy({ path, n, workers, config });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the async proxy if the worker was initialized with openpgp.initWorker()
|
||||
* @return {module:worker/async_proxy~AsyncProxy|null} the async proxy or null if not initialized
|
||||
* @returns {module:worker/async_proxy~AsyncProxy|null} the async proxy or null if not initialized
|
||||
*/
|
||||
export function getWorker() {
|
||||
return asyncProxy;
|
||||
|
@ -96,7 +97,7 @@ export function destroyWorker() {
|
|||
* @param {String} curve (optional) elliptic curve for ECC keys: curve25519, p256, p384, p521, or secp256k1
|
||||
* @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked
|
||||
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
|
||||
* @return {Promise<Object>} The generated key object in the form:
|
||||
* @returns {Promise<Object>} The generated key object in the form:
|
||||
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
|
||||
* @static
|
||||
*/
|
||||
|
@ -133,7 +134,7 @@ export function generateKey({
|
|||
* @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key
|
||||
* @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked
|
||||
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
|
||||
* @return {Promise<Object>} The generated key object in the form:
|
||||
* @returns {Promise<Object>} The generated key object in the form:
|
||||
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
|
||||
* @static
|
||||
*/
|
||||
|
@ -163,7 +164,7 @@ export function reformatKey({
|
|||
* Unlock a private key with your passphrase.
|
||||
* @param {Key} privateKey the private key that is to be decrypted
|
||||
* @param {String} passphrase the user's passphrase chosen during key generation
|
||||
* @return {Key} the unlocked private key
|
||||
* @returns {Promise<Object>} the unlocked key object in the form: { key:Key }
|
||||
*/
|
||||
export function decryptKey({ privateKey, passphrase }) {
|
||||
if (asyncProxy) { // use web worker if available
|
||||
|
@ -179,6 +180,26 @@ export function decryptKey({ privateKey, passphrase }) {
|
|||
}).catch(onError.bind(null, 'Error decrypting private key'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock a private key with your passphrase.
|
||||
* @param {Key} privateKey the private key that is to be decrypted
|
||||
* @param {String} passphrase the user's passphrase chosen during key generation
|
||||
* @returns {Promise<Object>} the locked key object in the form: { key:Key }
|
||||
*/
|
||||
export function encryptKey({ privateKey, passphrase }) {
|
||||
if (asyncProxy) { // use web worker if available
|
||||
return asyncProxy.delegate('encryptKey', { privateKey, passphrase });
|
||||
}
|
||||
|
||||
return Promise.resolve().then(async function() {
|
||||
await privateKey.encrypt(passphrase);
|
||||
|
||||
return {
|
||||
key: privateKey
|
||||
};
|
||||
}).catch(onError.bind(null, 'Error decrypting private key'));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
// //
|
||||
|
@ -203,7 +224,7 @@ export function decryptKey({ privateKey, passphrase }) {
|
|||
* @param {Boolean} returnSessionKey (optional) if the unencrypted session key should be added to returned object
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
* @param {Date} date (optional) override the creation date of the message and the message signature
|
||||
* @return {Promise<Object>} encrypted (and optionally signed message) in the form:
|
||||
* @returns {Promise<Object>} encrypted (and optionally signed message) in the form:
|
||||
* {data: ASCII armored message if 'armor' is true,
|
||||
* message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true}
|
||||
* @static
|
||||
|
@ -255,7 +276,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey,
|
|||
* @param {String} format (optional) return data format either as 'utf8' or 'binary'
|
||||
* @param {Signature} signature (optional) detached signature for verification
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @return {Promise<Object>} decrypted and verified message in the form:
|
||||
* @returns {Promise<Object>} decrypted and verified message in the form:
|
||||
* { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] }
|
||||
* @static
|
||||
*/
|
||||
|
@ -294,7 +315,7 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe
|
|||
* @param {Boolean} armor (optional) if the return value should be ascii armored or the message object
|
||||
* @param {Boolean} detached (optional) if the return value should contain a detached signature
|
||||
* @param {Date} date (optional) override the creation date signature
|
||||
* @return {Promise<Object>} signed cleartext in the form:
|
||||
* @returns {Promise<Object>} signed cleartext in the form:
|
||||
* {data: ASCII armored message if 'armor' is true,
|
||||
* message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true}
|
||||
* @static
|
||||
|
@ -336,7 +357,7 @@ export function sign({
|
|||
* @param {CleartextMessage} message cleartext message object with signatures
|
||||
* @param {Signature} signature (optional) detached signature for verification
|
||||
* @param {Date} date (optional) use the given date for verification instead of the current time
|
||||
* @return {Promise<Object>} cleartext with status of verified signatures in the form of:
|
||||
* @returns {Promise<Object>} cleartext with status of verified signatures in the form of:
|
||||
* { data:String, signatures: [{ keyid:String, valid:Boolean }] }
|
||||
* @static
|
||||
*/
|
||||
|
@ -372,7 +393,7 @@ export function verify({ message, publicKeys, signature=null, date=new Date() })
|
|||
* @param {Key|Array<Key>} publicKeys (optional) array of public keys or single key, used to encrypt the key
|
||||
* @param {String|Array<String>} passwords (optional) passwords for the message
|
||||
* @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs
|
||||
* @return {Promise<Message>} the encrypted session key packets contained in a message object
|
||||
* @returns {Promise<Message>} the encrypted session key packets contained in a message object
|
||||
* @static
|
||||
*/
|
||||
export function encryptSessionKey({ data, algorithm, publicKeys, passwords, wildcard=false }) {
|
||||
|
@ -395,7 +416,7 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords, wild
|
|||
* @param {Message} message a message object containing the encrypted session key packets
|
||||
* @param {Key|Array<Key>} privateKeys (optional) private keys with decrypted secret key data
|
||||
* @param {String|Array<String>} passwords (optional) passwords to decrypt the session key
|
||||
* @return {Promise<Object|undefined>} Array of decrypted session key, algorithm pairs in form:
|
||||
* @returns {Promise<Object|undefined>} Array of decrypted session key, algorithm pairs in form:
|
||||
* { data:Uint8Array, algorithm:String }
|
||||
* or 'undefined' if no key packets found
|
||||
* @static
|
||||
|
@ -484,7 +505,7 @@ function formatUserIds(userIds) {
|
|||
/**
|
||||
* Normalize parameter to an array if it is not undefined.
|
||||
* @param {Object} param the parameter to be normalized
|
||||
* @return {Array<Object>|undefined} the resulting array or undefined
|
||||
* @returns {Array<Object>|undefined} the resulting array or undefined
|
||||
*/
|
||||
function toArray(param) {
|
||||
if (param && !util.isArray(param)) {
|
||||
|
@ -498,7 +519,7 @@ function toArray(param) {
|
|||
* @param {String|Uint8Array} data the payload for the message
|
||||
* @param {String} filename the literal data packet's filename
|
||||
* @param {Date} date the creation date of the package
|
||||
* @return {Message} a message object
|
||||
* @returns {Message} a message object
|
||||
*/
|
||||
function createMessage(data, filename, date=new Date()) {
|
||||
let msg;
|
||||
|
@ -516,7 +537,7 @@ function createMessage(data, filename, date=new Date()) {
|
|||
* Parse the message given a certain format.
|
||||
* @param {Message} message the message object to be parse
|
||||
* @param {String} format the output format e.g. 'utf8' or 'binary'
|
||||
* @return {Object} the parse data in the respective format
|
||||
* @returns {Object} the parse data in the respective format
|
||||
*/
|
||||
function parseMessage(message, format) {
|
||||
if (format === 'binary') {
|
||||
|
@ -551,7 +572,7 @@ function onError(message, error) {
|
|||
/**
|
||||
* Check for AES-GCM support and configuration by the user. Only browsers that
|
||||
* implement the current WebCrypto specification support native AES-GCM.
|
||||
* @return {Boolean} If authenticated encryption should be used
|
||||
* @returns {Boolean} If authenticated encryption should be used
|
||||
*/
|
||||
function nativeAEAD() {
|
||||
return util.getWebCrypto() && config.aead_protect;
|
||||
|
|
|
@ -91,7 +91,7 @@ Packetlist.prototype.push = function (packet) {
|
|||
|
||||
/**
|
||||
* Remove a packet from the list and return it.
|
||||
* @return {Object} The packet that was removed
|
||||
* @returns {Object} The packet that was removed
|
||||
*/
|
||||
Packetlist.prototype.pop = function() {
|
||||
if (this.length === 0) {
|
||||
|
@ -164,6 +164,8 @@ Packetlist.prototype.map = function (callback) {
|
|||
/**
|
||||
* Executes the callback function once for each element
|
||||
* until it finds one where callback returns a truthy value
|
||||
* @param {Function} callback
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
Packetlist.prototype.some = async function (callback) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
|
@ -178,7 +180,7 @@ Packetlist.prototype.some = async function (callback) {
|
|||
/**
|
||||
* Traverses packet tree and returns first matching packet
|
||||
* @param {module:enums.packet} type The packet type
|
||||
* @return {module:packet/packet|null}
|
||||
* @returns {module:packet/packet|null}
|
||||
*/
|
||||
Packetlist.prototype.findPacket = function (type) {
|
||||
const packetlist = this.filterByTag(type);
|
||||
|
|
|
@ -67,7 +67,7 @@ export default function PublicKeyEncryptedSessionKey() {
|
|||
* @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 {module:packet/public_key_encrypted_session_key} Object representation
|
||||
* @returns {module:packet/public_key_encrypted_session_key} Object representation
|
||||
*/
|
||||
PublicKeyEncryptedSessionKey.prototype.read = function (bytes) {
|
||||
this.version = bytes[0];
|
||||
|
@ -88,7 +88,7 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) {
|
|||
/**
|
||||
* Create a string representation of a tag 1 packet
|
||||
*
|
||||
* @return {Uint8Array} The Uint8Array representation
|
||||
* @returns {Uint8Array} The Uint8Array representation
|
||||
*/
|
||||
PublicKeyEncryptedSessionKey.prototype.write = function () {
|
||||
const arr = [new Uint8Array([this.version]), this.publicKeyId.write(), new Uint8Array([enums.write(enums.publicKey, this.publicKeyAlgorithm)])];
|
||||
|
@ -100,6 +100,11 @@ PublicKeyEncryptedSessionKey.prototype.write = function () {
|
|||
return util.concatUint8Array(arr);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt session key packet
|
||||
* @param {module:packet/public_key} key Public key
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
|
||||
let data = String.fromCharCode(enums.write(enums.symmetric, this.sessionKeyAlgorithm));
|
||||
|
||||
|
@ -112,11 +117,12 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
|
|||
if (algo === enums.publicKey.ecdh) {
|
||||
toEncrypt = new type_mpi(crypto.pkcs5.encode(data));
|
||||
} else {
|
||||
toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength()));
|
||||
toEncrypt = new type_mpi(await crypto.pkcs1.eme.encode(data, key.params[0].byteLength()));
|
||||
}
|
||||
|
||||
this.encrypted = await crypto.publicKeyEncrypt(
|
||||
algo, key.params, toEncrypt, key.fingerprint);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -125,7 +131,7 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
|
|||
*
|
||||
* @param {module:packet/secret_key} key
|
||||
* Private key with secret params unlocked
|
||||
* @return {String} The unencrypted session key
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
|
||||
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
||||
|
@ -150,6 +156,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
|
|||
this.sessionKey = key;
|
||||
this.sessionKeyAlgorithm = enums.read(enums.symmetric, decoded.charCodeAt(0));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,7 +81,7 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
|
|||
const hash = util.Uint8Array_to_str(hashfn(cleartext));
|
||||
|
||||
if (hash !== hashtext) {
|
||||
return new Error("Hash mismatch.");
|
||||
return new Error("Incorrect key passphrase");
|
||||
}
|
||||
|
||||
const algo = enums.write(enums.publicKey, algorithm);
|
||||
|
@ -151,7 +151,7 @@ SecretKey.prototype.read = function (bytes) {
|
|||
};
|
||||
|
||||
/** Creates an OpenPGP key packet for the given key.
|
||||
* @return {String} A string of bytes containing the secret key OpenPGP packet
|
||||
* @returns {String} A string of bytes containing the secret key OpenPGP packet
|
||||
*/
|
||||
SecretKey.prototype.write = function () {
|
||||
const arr = [this.writePublicKey()];
|
||||
|
@ -172,21 +172,23 @@ SecretKey.prototype.write = function () {
|
|||
* and the passphrase is empty or undefined, the key will be set as not encrypted.
|
||||
* This can be used to remove passphrase protection after calling decrypt().
|
||||
* @param {String} passphrase
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
SecretKey.prototype.encrypt = function (passphrase) {
|
||||
SecretKey.prototype.encrypt = async function (passphrase) {
|
||||
if (this.isDecrypted && !passphrase) {
|
||||
this.encrypted = null;
|
||||
return;
|
||||
return false;
|
||||
} else if (!passphrase) {
|
||||
throw new Error('The key must be decrypted before removing passphrase protection.');
|
||||
}
|
||||
|
||||
const s2k = new type_s2k();
|
||||
s2k.salt = await crypto.random.getRandomBytes(8);
|
||||
const symmetric = 'aes256';
|
||||
const cleartext = write_cleartext_params('sha1', this.algorithm, this.params);
|
||||
const key = produceEncryptionKey(s2k, passphrase, symmetric);
|
||||
const blockLen = crypto.cipher[symmetric].blockSize;
|
||||
const iv = crypto.random.getRandomBytes(blockLen);
|
||||
const iv = await crypto.random.getRandomBytes(blockLen);
|
||||
|
||||
const arr = [new Uint8Array([254, enums.write(enums.symmetric, symmetric)])];
|
||||
arr.push(s2k.write());
|
||||
|
@ -194,6 +196,7 @@ SecretKey.prototype.encrypt = function (passphrase) {
|
|||
arr.push(crypto.cfb.normalEncrypt(symmetric, key, cleartext, iv));
|
||||
|
||||
this.encrypted = util.concatUint8Array(arr);
|
||||
return true;
|
||||
};
|
||||
|
||||
function produceEncryptionKey(s2k, passphrase, algorithm) {
|
||||
|
@ -208,12 +211,10 @@ function produceEncryptionKey(s2k, passphrase, algorithm) {
|
|||
* @link module:packet/secret_key.isDecrypted should be
|
||||
* false otherwise a call to this function is not needed
|
||||
*
|
||||
* @param {String} str_passphrase The passphrase for this private key
|
||||
* as string
|
||||
* @return {Boolean} True if the passphrase was correct or param already
|
||||
* decrypted; false if not
|
||||
* @param {String} passphrase The passphrase for this private key as string
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
SecretKey.prototype.decrypt = function (passphrase) {
|
||||
SecretKey.prototype.decrypt = async function (passphrase) {
|
||||
if (this.isDecrypted) {
|
||||
return true;
|
||||
}
|
||||
|
@ -261,11 +262,12 @@ SecretKey.prototype.decrypt = function (passphrase) {
|
|||
|
||||
const privParams = parse_cleartext_params(hash, cleartext, this.algorithm);
|
||||
if (privParams instanceof Error) {
|
||||
return false;
|
||||
throw privParams;
|
||||
}
|
||||
this.params = this.params.concat(privParams);
|
||||
this.isDecrypted = true;
|
||||
this.encrypted = null;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
|
|
@ -211,6 +211,7 @@ Signature.prototype.write = function () {
|
|||
* Signs provided data. This needs to be done prior to serialization.
|
||||
* @param {module:packet/secret_key} key private key used to sign the message.
|
||||
* @param {Object} data Contains packets to be signed.
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
Signature.prototype.sign = async function (key, data) {
|
||||
const signatureType = enums.write(enums.signature, this.signatureType);
|
||||
|
@ -247,6 +248,7 @@ Signature.prototype.sign = async function (key, data) {
|
|||
this.signature = await crypto.signature.sign(
|
||||
publicKeyAlgorithm, hashAlgorithm, key.params, toHash
|
||||
);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -615,7 +617,7 @@ Signature.prototype.calculateTrailer = function () {
|
|||
* @param {String|Object} data data which on the signature applies
|
||||
* @param {module:packet/public_subkey|module:packet/public_key|
|
||||
* module:packet/secret_subkey|module:packet/secret_key} key the public key to verify the signature
|
||||
* @return {boolean} True if message is verified, else false.
|
||||
* @return {Promise<Boolean>} True if message is verified, else false.
|
||||
*/
|
||||
Signature.prototype.verify = async function (key, data) {
|
||||
const signatureType = enums.write(enums.signature, this.signatureType);
|
||||
|
|
|
@ -64,23 +64,21 @@ SymEncryptedAEADProtected.prototype.write = function () {
|
|||
* Decrypt the encrypted payload.
|
||||
* @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128'
|
||||
* @param {Uint8Array} key The session key used to encrypt the payload
|
||||
* @return {Promise<undefined>} Nothing is returned
|
||||
* @return {Promise<Boolean>}
|
||||
*/
|
||||
SymEncryptedAEADProtected.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
||||
return crypto.gcm.decrypt(sessionKeyAlgorithm, this.encrypted, key, this.iv).then(decrypted => {
|
||||
this.packets.read(decrypted);
|
||||
});
|
||||
SymEncryptedAEADProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
|
||||
this.packets.read(await crypto.gcm.decrypt(sessionKeyAlgorithm, this.encrypted, key, this.iv));
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt the packet list payload.
|
||||
* @param {String} sessionKeyAlgorithm The session key's cipher algorithm e.g. 'aes128'
|
||||
* @param {Uint8Array} key The session key used to encrypt the payload
|
||||
* @return {Promise<undefined>} Nothing is returned
|
||||
* @return {Promise<Boolean>}
|
||||
*/
|
||||
SymEncryptedAEADProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) {
|
||||
this.iv = crypto.random.getRandomValues(new Uint8Array(IV_LEN)); // generate new random IV
|
||||
return crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv).then(encrypted => {
|
||||
this.encrypted = encrypted;
|
||||
});
|
||||
SymEncryptedAEADProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key) {
|
||||
this.iv = await crypto.random.getRandomBytes(IV_LEN); // generate new random IV
|
||||
this.encrypted = await crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv);
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -79,11 +79,11 @@ SymEncryptedIntegrityProtected.prototype.write = function () {
|
|||
* Encrypt the payload in the packet.
|
||||
* @param {String} sessionKeyAlgorithm The selected symmetric encryption algorithm to be used e.g. 'aes128'
|
||||
* @param {Uint8Array} key The key of cipher blocksize length to be used
|
||||
* @return {Promise}
|
||||
* @return {Promise<Boolean>}
|
||||
*/
|
||||
SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) {
|
||||
SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key) {
|
||||
const bytes = this.packets.write();
|
||||
const prefixrandom = crypto.getPrefixRandom(sessionKeyAlgorithm);
|
||||
const prefixrandom = await crypto.getPrefixRandom(sessionKeyAlgorithm);
|
||||
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
|
||||
const prefix = util.concatUint8Array([prefixrandom, repeat]);
|
||||
const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet
|
||||
|
@ -98,17 +98,16 @@ SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm
|
|||
this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false);
|
||||
this.encrypted = this.encrypted.subarray(0, prefix.length + tohash.length);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypts the encrypted data contained in the packet.
|
||||
* @param {String} sessionKeyAlgorithm The selected symmetric encryption algorithm to be used e.g. 'aes128'
|
||||
* @param {Uint8Array} key The key of cipher blocksize length to be used
|
||||
* @return {Promise}
|
||||
* @return {Promise<Boolean>}
|
||||
*/
|
||||
SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
||||
SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
|
||||
let decrypted;
|
||||
if (sessionKeyAlgorithm.substr(0, 3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
|
||||
decrypted = aesDecrypt(sessionKeyAlgorithm, this.encrypted, key);
|
||||
|
|
|
@ -52,7 +52,7 @@ export default function SymEncryptedSessionKey() {
|
|||
this.sessionKeyEncryptionAlgorithm = null;
|
||||
this.sessionKeyAlgorithm = 'aes256';
|
||||
this.encrypted = null;
|
||||
this.s2k = new type_s2k();
|
||||
this.s2k = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,6 +73,7 @@ SymEncryptedSessionKey.prototype.read = function(bytes) {
|
|||
const algo = enums.read(enums.symmetric, bytes[1]);
|
||||
|
||||
// A string-to-key (S2K) specifier, length as defined above.
|
||||
this.s2k = new type_s2k();
|
||||
const s2klength = this.s2k.read(bytes.subarray(2, bytes.length));
|
||||
|
||||
// Optionally, the encrypted session key itself, which is decrypted
|
||||
|
@ -101,12 +102,11 @@ SymEncryptedSessionKey.prototype.write = function() {
|
|||
};
|
||||
|
||||
/**
|
||||
* Decrypts the session key (only for public key encrypted session key
|
||||
* packets (tag 1)
|
||||
*
|
||||
* @return {Uint8Array} The unencrypted session key
|
||||
* Decrypts the session key
|
||||
* @param {String} passphrase The passphrase in string form
|
||||
* @return {Promise<Boolean}
|
||||
*/
|
||||
SymEncryptedSessionKey.prototype.decrypt = function(passphrase) {
|
||||
SymEncryptedSessionKey.prototype.decrypt = async function(passphrase) {
|
||||
const algo = this.sessionKeyEncryptionAlgorithm !== null ?
|
||||
this.sessionKeyEncryptionAlgorithm :
|
||||
this.sessionKeyAlgorithm;
|
||||
|
@ -122,26 +122,36 @@ SymEncryptedSessionKey.prototype.decrypt = function(passphrase) {
|
|||
this.sessionKeyAlgorithm = enums.read(enums.symmetric, decrypted[0]);
|
||||
this.sessionKey = decrypted.subarray(1, decrypted.length);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
SymEncryptedSessionKey.prototype.encrypt = function(passphrase) {
|
||||
/**
|
||||
* Encrypts the session key
|
||||
* @param {String} passphrase The passphrase in string form
|
||||
* @return {Promise<Boolean}
|
||||
*/
|
||||
SymEncryptedSessionKey.prototype.encrypt = async function(passphrase) {
|
||||
const algo = this.sessionKeyEncryptionAlgorithm !== null ?
|
||||
this.sessionKeyEncryptionAlgorithm :
|
||||
this.sessionKeyAlgorithm;
|
||||
|
||||
this.sessionKeyEncryptionAlgorithm = algo;
|
||||
|
||||
this.s2k = new type_s2k();
|
||||
this.s2k.salt = await crypto.random.getRandomBytes(8);
|
||||
|
||||
const length = crypto.cipher[algo].keySize;
|
||||
const key = this.s2k.produce_key(passphrase, length);
|
||||
|
||||
const algo_enum = new Uint8Array([enums.write(enums.symmetric, this.sessionKeyAlgorithm)]);
|
||||
|
||||
if (this.sessionKey === null) {
|
||||
this.sessionKey = crypto.getRandomBytes(crypto.cipher[this.sessionKeyAlgorithm].keySize);
|
||||
this.sessionKey = await crypto.generateSessionKey(this.sessionKeyAlgorithm);
|
||||
}
|
||||
const private_key = util.concatUint8Array([algo_enum, this.sessionKey]);
|
||||
|
||||
this.encrypted = crypto.cfb.normalEncrypt(algo, key, private_key, null);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,15 +54,13 @@ SymmetricallyEncrypted.prototype.write = function () {
|
|||
};
|
||||
|
||||
/**
|
||||
* Symmetrically decrypt the packet data
|
||||
*
|
||||
* Decrypt the symmetrically-encrypted packet data
|
||||
* @param {module:enums.symmetric} sessionKeyAlgorithm
|
||||
* Symmetric key algorithm to use // See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC4880 9.2}
|
||||
* @param {String} key
|
||||
* Key as string with the corresponding length to the
|
||||
* algorithm
|
||||
* @param {Uint8Array} key The key of cipher blocksize length to be used
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
||||
SymmetricallyEncrypted.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
|
||||
const decrypted = crypto.cfb.decrypt(sessionKeyAlgorithm, key, this.encrypted, true);
|
||||
// for modern cipher (blocklength != 64 bit, except for Twofish) MDC is required
|
||||
if (!this.ignore_mdc_error &&
|
||||
|
@ -73,13 +71,20 @@ SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
|||
}
|
||||
this.packets.read(decrypted);
|
||||
|
||||
return Promise.resolve();
|
||||
return true;
|
||||
};
|
||||
|
||||
SymmetricallyEncrypted.prototype.encrypt = function (algo, key) {
|
||||
/**
|
||||
* Encrypt the symmetrically-encrypted packet data
|
||||
* @param {module:enums.symmetric} sessionKeyAlgorithm
|
||||
* Symmetric key algorithm to use // See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC4880 9.2}
|
||||
* @param {Uint8Array} key The key of cipher blocksize length to be used
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
SymmetricallyEncrypted.prototype.encrypt = async function (algo, key) {
|
||||
const data = this.packets.write();
|
||||
|
||||
this.encrypted = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, data, key, true);
|
||||
this.encrypted = crypto.cfb.encrypt(await crypto.getPrefixRandom(algo), algo, data, key, true);
|
||||
|
||||
return Promise.resolve();
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@ export default function S2K() {
|
|||
/** Eight bytes of salt in a binary string.
|
||||
* @type {String}
|
||||
*/
|
||||
this.salt = crypto.random.getRandomBytes(8);
|
||||
this.salt = null;
|
||||
}
|
||||
|
||||
S2K.prototype.get_count = function () {
|
||||
|
|
|
@ -19,28 +19,68 @@ import util from '../util.js';
|
|||
import crypto from '../crypto';
|
||||
import packet from '../packet';
|
||||
|
||||
const INITIAL_RANDOM_SEED = 50000; // random bytes seeded to worker
|
||||
const RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request
|
||||
/**
|
||||
* Message handling
|
||||
*/
|
||||
function handleMessage(workerId) {
|
||||
return function(event) {
|
||||
const msg = event.data;
|
||||
switch (msg.event) {
|
||||
case 'method-return':
|
||||
if (msg.err) {
|
||||
// fail
|
||||
const err = new Error(msg.err);
|
||||
// add worker stack
|
||||
err.workerStack = msg.stack;
|
||||
this.tasks[msg.id].reject(err);
|
||||
} else {
|
||||
// success
|
||||
this.tasks[msg.id].resolve(msg.data);
|
||||
}
|
||||
delete this.tasks[msg.id];
|
||||
this.workers[workerId].requests--;
|
||||
break;
|
||||
case 'request-seed':
|
||||
this.seedRandom(workerId, msg.amount);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown Worker Event.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new proxy and loads the web worker
|
||||
* @constructor
|
||||
* @param {String} path The path to the worker or 'openpgp.worker.js' by default
|
||||
* @param {Object} config config The worker configuration
|
||||
* @param {Object} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js'
|
||||
* @return {Promise}
|
||||
* @param {String} path The path to the worker or 'openpgp.worker.js' by default
|
||||
* @param {Number} n number of workers to initialize if path given
|
||||
* @param {Object} config config The worker configuration
|
||||
* @param {Array<Object>} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js'
|
||||
*/
|
||||
export default function AsyncProxy({ path='openpgp.worker.js', worker, config } = {}) {
|
||||
this.worker = worker || new Worker(path);
|
||||
this.worker.onmessage = this.onMessage.bind(this);
|
||||
this.worker.onerror = e => {
|
||||
throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
|
||||
};
|
||||
this.seedRandom(INITIAL_RANDOM_SEED);
|
||||
export default function AsyncProxy({ path='openpgp.worker.js', n = 1, workers = [], config } = {}) {
|
||||
|
||||
if (config) {
|
||||
this.worker.postMessage({ event:'configure', config });
|
||||
if (workers.length) {
|
||||
this.workers = workers;
|
||||
}
|
||||
else {
|
||||
this.workers = [];
|
||||
while (this.workers.length < n) {
|
||||
this.workers.push(new Worker(path));
|
||||
}
|
||||
}
|
||||
|
||||
let workerId = 0;
|
||||
this.workers.forEach(worker => {
|
||||
worker.requests = 0;
|
||||
worker.onmessage = handleMessage(workerId++).bind(this);
|
||||
worker.onerror = e => {
|
||||
throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
|
||||
};
|
||||
|
||||
if (config) {
|
||||
worker.postMessage({ event:'configure', config });
|
||||
}
|
||||
});
|
||||
|
||||
// Cannot rely on task order being maintained, use object keyed by request ID to track tasks
|
||||
this.tasks = {};
|
||||
|
@ -55,61 +95,22 @@ AsyncProxy.prototype.getID = function() {
|
|||
return this.currentID++;
|
||||
};
|
||||
|
||||
/**
|
||||
* Message handling
|
||||
*/
|
||||
AsyncProxy.prototype.onMessage = function(event) {
|
||||
const msg = event.data;
|
||||
switch (msg.event) {
|
||||
case 'method-return':
|
||||
if (msg.err) {
|
||||
// fail
|
||||
const err = new Error(msg.err);
|
||||
// add worker stack
|
||||
err.workerStack = msg.stack;
|
||||
this.tasks[msg.id].reject(err);
|
||||
} else {
|
||||
// success
|
||||
this.tasks[msg.id].resolve(msg.data);
|
||||
}
|
||||
delete this.tasks[msg.id];
|
||||
break;
|
||||
case 'request-seed':
|
||||
this.seedRandom(RANDOM_SEED_REQUEST);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown Worker Event.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send message to worker with random data
|
||||
* @param {Integer} size Number of bytes to send
|
||||
*/
|
||||
AsyncProxy.prototype.seedRandom = function(size) {
|
||||
const buf = this.getRandomBuffer(size);
|
||||
this.worker.postMessage({ event:'seed-random', buf }, util.getTransferables(buf));
|
||||
AsyncProxy.prototype.seedRandom = async function(workerId, size) {
|
||||
const buf = await crypto.random.getRandomBytes(size);
|
||||
this.workers[workerId].postMessage({ event:'seed-random', buf }, util.getTransferables(buf));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Uint8Array with random numbers
|
||||
* @param {Integer} size Length of buffer
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
AsyncProxy.prototype.getRandomBuffer = function(size) {
|
||||
if (!size) {
|
||||
return null;
|
||||
}
|
||||
const buf = new Uint8Array(size);
|
||||
crypto.random.getRandomValues(buf);
|
||||
return buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminates the worker
|
||||
* Terminates the workers
|
||||
*/
|
||||
AsyncProxy.prototype.terminate = function() {
|
||||
this.worker.terminate();
|
||||
this.workers.forEach(worker => {
|
||||
worker.terminate();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -119,11 +120,21 @@ AsyncProxy.prototype.terminate = function() {
|
|||
* @return {Promise} see the corresponding public api functions for their return types
|
||||
*/
|
||||
AsyncProxy.prototype.delegate = function(method, options) {
|
||||
|
||||
const id = this.getID();
|
||||
const requests = this.workers.map(worker => worker.requests);
|
||||
const minRequests = Math.min(requests);
|
||||
let workerId = 0;
|
||||
for(; workerId < this.workers.length; workerId++) {
|
||||
if (this.workers[workerId].requests === minRequests) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// clone packets (for web worker structured cloning algorithm)
|
||||
this.worker.postMessage({ id:id, event:method, options:packet.clone.clonePackets(options) }, util.getTransferables(options));
|
||||
this.workers[workerId].postMessage({ id:id, event:method, options:packet.clone.clonePackets(options) }, util.getTransferables(options));
|
||||
this.workers[workerId].requests++;
|
||||
|
||||
// remember to handle parsing cloned packets from worker
|
||||
this.tasks[id] = { resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject };
|
||||
|
|
|
@ -24,10 +24,29 @@ self.window = {}; // to make UMD bundles work
|
|||
importScripts('openpgp.js');
|
||||
var openpgp = window.openpgp;
|
||||
|
||||
var randomQueue = [];
|
||||
var randomRequested = false;
|
||||
var MIN_SIZE_RANDOM_BUFFER = 40000;
|
||||
var MAX_SIZE_RANDOM_BUFFER = 60000;
|
||||
var MIN_SIZE_RANDOM_REQUEST = 20000;
|
||||
|
||||
openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER);
|
||||
/**
|
||||
* Handle random buffer exhaustion by requesting more random bytes from the main window
|
||||
* @return {Promise<Object>} Empty promise whose resolution indicates that the buffer has been refilled
|
||||
*/
|
||||
function randomCallback() {
|
||||
|
||||
if (!randomRequested) {
|
||||
self.postMessage({ event: 'request-seed', amount: MAX_SIZE_RANDOM_BUFFER });
|
||||
}
|
||||
randomRequested = true;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
randomQueue.push(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER, randomCallback);
|
||||
|
||||
/**
|
||||
* Handle messages from the main window.
|
||||
|
@ -43,6 +62,13 @@ self.onmessage = function(event) {
|
|||
|
||||
case 'seed-random':
|
||||
seedRandom(msg.buf);
|
||||
|
||||
var queueCopy = randomQueue;
|
||||
randomQueue = [];
|
||||
for (var i = 0; i < queueCopy.length; i++) {
|
||||
queueCopy[i]();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -99,8 +125,8 @@ function delegate(id, method, options) {
|
|||
* @param {Object} event Contains event type and data
|
||||
*/
|
||||
function response(event) {
|
||||
if (openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) {
|
||||
self.postMessage({ event: 'request-seed' });
|
||||
if (!randomRequested && openpgp.crypto.random.randomBuffer.size < MIN_SIZE_RANDOM_BUFFER) {
|
||||
self.postMessage({ event: 'request-seed', amount: MIN_SIZE_RANDOM_REQUEST });
|
||||
}
|
||||
self.postMessage(event, openpgp.util.getTransferables(event.data));
|
||||
}
|
||||
|
|
|
@ -278,19 +278,19 @@ describe('API functional testing', function() {
|
|||
});
|
||||
|
||||
function testCFB(plaintext, resync) {
|
||||
symmAlgos.forEach(function(algo) {
|
||||
const symmKey = crypto.generateSessionKey(algo);
|
||||
const symmencData = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, util.str_to_Uint8Array(plaintext), symmKey, resync);
|
||||
symmAlgos.forEach(async function(algo) {
|
||||
const symmKey = await crypto.generateSessionKey(algo);
|
||||
const symmencData = crypto.cfb.encrypt(await crypto.getPrefixRandom(algo), algo, util.str_to_Uint8Array(plaintext), symmKey, resync);
|
||||
const text = util.Uint8Array_to_str(crypto.cfb.decrypt(algo, symmKey, symmencData, resync));
|
||||
expect(text).to.equal(plaintext);
|
||||
});
|
||||
}
|
||||
|
||||
function testAESCFB(plaintext) {
|
||||
symmAlgos.forEach(function(algo) {
|
||||
symmAlgos.forEach(async function(algo) {
|
||||
if(algo.substr(0,3) === 'aes') {
|
||||
const symmKey = crypto.generateSessionKey(algo);
|
||||
const rndm = crypto.getPrefixRandom(algo);
|
||||
const symmKey = await crypto.generateSessionKey(algo);
|
||||
const rndm = await crypto.getPrefixRandom(algo);
|
||||
|
||||
const repeat = new Uint8Array([rndm[rndm.length - 2], rndm[rndm.length - 1]]);
|
||||
const prefix = util.concatUint8Array([rndm, repeat]);
|
||||
|
@ -307,9 +307,9 @@ describe('API functional testing', function() {
|
|||
function testAESGCM(plaintext) {
|
||||
symmAlgos.forEach(function(algo) {
|
||||
if(algo.substr(0,3) === 'aes') {
|
||||
it(algo, function() {
|
||||
const key = crypto.generateSessionKey(algo);
|
||||
const iv = crypto.random.getRandomValues(new Uint8Array(crypto.gcm.ivLength));
|
||||
it(algo, async function() {
|
||||
const key = await crypto.generateSessionKey(algo);
|
||||
const iv = await crypto.random.getRandomBytes(crypto.gcm.ivLength);
|
||||
|
||||
return crypto.gcm.encrypt(
|
||||
algo, util.str_to_Uint8Array(plaintext), key, iv
|
||||
|
@ -373,11 +373,10 @@ describe('API functional testing', function() {
|
|||
|
||||
it('Asymmetric using RSA with eme_pkcs1 padding', function () {
|
||||
const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256'));
|
||||
const RSAUnencryptedData = crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())
|
||||
const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData);
|
||||
return crypto.publicKeyEncrypt(
|
||||
1, RSApubMPIs, RSAUnencryptedMPI
|
||||
).then(RSAEncryptedData => {
|
||||
return crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength()).then(RSAUnencryptedData => {
|
||||
const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData);
|
||||
return crypto.publicKeyEncrypt(1, RSApubMPIs, RSAUnencryptedMPI);
|
||||
}).then(RSAEncryptedData => {
|
||||
|
||||
return crypto.publicKeyDecrypt(
|
||||
1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
|
||||
|
@ -393,12 +392,10 @@ describe('API functional testing', function() {
|
|||
|
||||
it('Asymmetric using Elgamal with eme_pkcs1 padding', function () {
|
||||
const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256'));
|
||||
const ElgamalUnencryptedData = crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength());
|
||||
const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData);
|
||||
|
||||
return crypto.publicKeyEncrypt(
|
||||
16, ElgamalpubMPIs, ElgamalUnencryptedMPI
|
||||
).then(ElgamalEncryptedData => {
|
||||
return crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength()).then(ElgamalUnencryptedData => {
|
||||
const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData);
|
||||
return crypto.publicKeyEncrypt(16, ElgamalpubMPIs, ElgamalUnencryptedMPI);
|
||||
}).then(ElgamalEncryptedData => {
|
||||
|
||||
return crypto.publicKeyDecrypt(
|
||||
16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData
|
||||
|
|
|
@ -12,9 +12,9 @@ describe('Random Buffer', function() {
|
|||
expect(randomBuffer).to.exist;
|
||||
});
|
||||
|
||||
it('Throw error if not initialized', function () {
|
||||
it('Throw error if not initialized', async function () {
|
||||
expect(randomBuffer.set.bind(randomBuffer)).to.throw('RandomBuffer is not initialized');
|
||||
expect(randomBuffer.get.bind(randomBuffer)).to.throw('RandomBuffer is not initialized');
|
||||
await expect(randomBuffer.get(new Uint8Array(1))).to.eventually.be.rejectedWith('RandomBuffer is not initialized');
|
||||
});
|
||||
|
||||
it('Initialization', function () {
|
||||
|
@ -56,13 +56,13 @@ describe('Random Buffer', function() {
|
|||
expect(randomBuffer.size).to.equal(1);
|
||||
});
|
||||
|
||||
it('Get Method', function () {
|
||||
it('Get Method', async function () {
|
||||
randomBuffer.init(5);
|
||||
let buf = new Uint8Array(5);
|
||||
buf[0] = 1; buf[1] = 2; buf[2] = 5; buf[3] = 7; buf[4] = 8;
|
||||
randomBuffer.set(buf);
|
||||
buf = new Uint32Array(2);
|
||||
expect(randomBuffer.get.bind(randomBuffer, buf)).to.throw('Invalid type: buf not an Uint8Array');
|
||||
await expect(randomBuffer.get(buf)).to.eventually.be.rejectedWith('Invalid type: buf not an Uint8Array');
|
||||
buf = new Uint8Array(2);
|
||||
randomBuffer.get(buf);
|
||||
expect(equal(randomBuffer.buffer, [1,2,5,0,0])).to.be.true;
|
||||
|
@ -74,6 +74,6 @@ describe('Random Buffer', function() {
|
|||
expect(buf).to.to.have.property('1', 2);
|
||||
expect(equal(randomBuffer.buffer, [1,0,0,0,0])).to.be.true;
|
||||
expect(randomBuffer.size).to.equal(1);
|
||||
expect(function() { randomBuffer.get(buf); }).to.throw('Random number buffer depleted');
|
||||
await expect(randomBuffer.get(buf)).to.eventually.be.rejectedWith('Random number buffer depleted');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -142,7 +142,7 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
data[name].pub_key = pub.keys[0];
|
||||
return data[name].pub_key;
|
||||
}
|
||||
function load_priv_key(name) {
|
||||
async function load_priv_key(name) {
|
||||
if (data[name].priv_key) {
|
||||
return data[name].priv_key;
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
expect(pk.err).to.not.exist;
|
||||
expect(pk.keys).to.have.length(1);
|
||||
expect(pk.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id);
|
||||
expect(pk.keys[0].decrypt(data[name].pass)).to.be.true;
|
||||
expect(await pk.keys[0].decrypt(data[name].pass)).to.be.true;
|
||||
data[name].priv_key = pk.keys[0];
|
||||
return data[name].priv_key;
|
||||
}
|
||||
|
@ -160,10 +160,10 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
load_pub_key('juliet');
|
||||
done();
|
||||
});
|
||||
it('Load private key', function (done) {
|
||||
load_priv_key('romeo');
|
||||
load_priv_key('juliet');
|
||||
done();
|
||||
it('Load private key', async function () {
|
||||
await load_priv_key('romeo');
|
||||
await load_priv_key('juliet');
|
||||
return true;
|
||||
});
|
||||
it('Verify clear signed message', function () {
|
||||
const pub = load_pub_key('juliet');
|
||||
|
@ -175,52 +175,45 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
});
|
||||
it('Sign message', function () {
|
||||
const romeo = load_priv_key('romeo');
|
||||
return openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) {
|
||||
const romeo = load_pub_key('romeo');
|
||||
const msg = openpgp.cleartext.readArmored(signed.data);
|
||||
return openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) {
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data.romeo.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
});
|
||||
it('Sign message', async function () {
|
||||
const romeoPrivate = await load_priv_key('romeo');
|
||||
const signed = await openpgp.sign({privateKeys: [romeoPrivate], data: data.romeo.message + "\n"});
|
||||
const romeoPublic = load_pub_key('romeo');
|
||||
const msg = openpgp.cleartext.readArmored(signed.data);
|
||||
const result = await openpgp.verify({publicKeys: [romeoPublic], message: msg});
|
||||
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data.romeo.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
it('Decrypt and verify message', function () {
|
||||
it('Decrypt and verify message', async function () {
|
||||
const juliet = load_pub_key('juliet');
|
||||
const romeo = load_priv_key('romeo');
|
||||
const romeo = await load_priv_key('romeo');
|
||||
const msg = openpgp.message.readArmored(data.juliet.message_encrypted);
|
||||
return openpgp.decrypt(
|
||||
{privateKeys: romeo, publicKeys: [juliet], message: msg}
|
||||
).then(function (result) {
|
||||
expect(result).to.exist;
|
||||
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
|
||||
expect(result.data.trim()).to.equal(data.juliet.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
const result = await openpgp.decrypt({privateKeys: romeo, publicKeys: [juliet], message: msg});
|
||||
|
||||
expect(result).to.exist;
|
||||
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
|
||||
expect(result.data.trim()).to.equal(data.juliet.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
it('Encrypt and sign message', function () {
|
||||
const romeo = load_priv_key('romeo');
|
||||
const juliet = load_pub_key('juliet');
|
||||
expect(romeo.decrypt(data.romeo.pass)).to.be.true;
|
||||
return openpgp.encrypt(
|
||||
{publicKeys: [juliet], privateKeys: [romeo], data: data.romeo.message + "\n"}
|
||||
).then(function (encrypted) {
|
||||
const message = openpgp.message.readArmored(encrypted.data);
|
||||
const romeo = load_pub_key('romeo');
|
||||
const juliet = load_priv_key('juliet');
|
||||
return openpgp.decrypt(
|
||||
{privateKeys: juliet, publicKeys: [romeo], message: message}
|
||||
).then(function (result) {
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data.romeo.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
});
|
||||
it('Encrypt and sign message', async function () {
|
||||
const romeoPrivate = await load_priv_key('romeo');
|
||||
const julietPublic = load_pub_key('juliet');
|
||||
expect(await romeoPrivate.decrypt(data.romeo.pass)).to.be.true;
|
||||
const encrypted = await openpgp.encrypt({publicKeys: [julietPublic], privateKeys: [romeoPrivate], data: data.romeo.message + "\n"});
|
||||
|
||||
const message = openpgp.message.readArmored(encrypted.data);
|
||||
const romeoPublic = load_pub_key('romeo');
|
||||
const julietPrivate = await load_priv_key('juliet');
|
||||
const result = await openpgp.decrypt({privateKeys: julietPrivate, publicKeys: [romeoPublic], message: message});
|
||||
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data.romeo.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
it('Generate key', function () {
|
||||
const options = {
|
||||
|
|
|
@ -1037,26 +1037,24 @@ describe('Key', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('Encrypt key with new passphrase', function() {
|
||||
it('Encrypt key with new passphrase', async function() {
|
||||
const userId = 'test <a@b.com>';
|
||||
const opt = {numBits: 512, userIds: userId, passphrase: 'passphrase'};
|
||||
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||
return openpgp.generateKey(opt).then(function(key) {
|
||||
key = key.key;
|
||||
const armor1 = key.armor();
|
||||
const armor2 = key.armor();
|
||||
expect(armor1).to.equal(armor2);
|
||||
expect(key.decrypt('passphrase')).to.be.true;
|
||||
expect(key.primaryKey.isDecrypted).to.be.true;
|
||||
key.encrypt('new_passphrase');
|
||||
expect(key.primaryKey.isDecrypted).to.be.false;
|
||||
expect(key.decrypt('passphrase')).to.be.false;
|
||||
expect(key.primaryKey.isDecrypted).to.be.false;
|
||||
expect(key.decrypt('new_passphrase')).to.be.true;
|
||||
expect(key.primaryKey.isDecrypted).to.be.true;
|
||||
const armor3 = key.armor();
|
||||
expect(armor3).to.not.equal(armor1);
|
||||
});
|
||||
const key = (await openpgp.generateKey(opt)).key;
|
||||
const armor1 = key.armor();
|
||||
const armor2 = key.armor();
|
||||
expect(armor1).to.equal(armor2);
|
||||
expect(await key.decrypt('passphrase')).to.be.true;
|
||||
expect(key.primaryKey.isDecrypted).to.be.true;
|
||||
await key.encrypt('new_passphrase');
|
||||
expect(key.primaryKey.isDecrypted).to.be.false;
|
||||
await expect(key.decrypt('passphrase')).to.eventually.be.rejectedWith('Incorrect key passphrase');
|
||||
expect(key.primaryKey.isDecrypted).to.be.false;
|
||||
expect(await key.decrypt('new_passphrase')).to.be.true;
|
||||
expect(key.primaryKey.isDecrypted).to.be.true;
|
||||
const armor3 = key.armor();
|
||||
expect(armor3).to.not.equal(armor1);
|
||||
});
|
||||
|
||||
it('Generate key - ensure keyExpirationTime works', function() {
|
||||
|
@ -1268,7 +1266,7 @@ describe('Key', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('Throw user friendly error when reformatting encrypted key', function() {
|
||||
it('Reject with user-friendly error when reformatting encrypted key', function() {
|
||||
const opt = {numBits: 512, userIds: 'test1 <a@b.com>', passphrase: '1234'};
|
||||
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||
return openpgp.generateKey(opt).then(function(original) {
|
||||
|
|
|
@ -363,7 +363,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
postMessage: function() {}
|
||||
};
|
||||
openpgp.initWorker({
|
||||
worker: workerStub
|
||||
workers: [workerStub]
|
||||
});
|
||||
expect(openpgp.getWorker()).to.exist;
|
||||
openpgp.destroyWorker();
|
||||
|
@ -522,7 +522,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
postMessage: function() {}
|
||||
};
|
||||
openpgp.initWorker({
|
||||
worker: workerStub
|
||||
workers: [workerStub]
|
||||
});
|
||||
const proxyGenStub = stub(openpgp.getWorker(), 'delegate');
|
||||
getWebCryptoAllStub.returns();
|
||||
|
@ -635,12 +635,12 @@ describe('OpenPGP.js public api tests', function() {
|
|||
openpgp.config.aead_protect = aead_protectVal;
|
||||
});
|
||||
|
||||
it('Decrypting key with wrong passphrase returns false', function () {
|
||||
expect(privateKey.keys[0].decrypt('wrong passphrase')).to.be.false;
|
||||
it('Decrypting key with wrong passphrase rejected', function () {
|
||||
expect(privateKey.keys[0].decrypt('wrong passphrase')).to.eventually.be.rejectedWith('Incorrect key passphrase');
|
||||
});
|
||||
|
||||
it('Decrypting key with correct passphrase returns true', function () {
|
||||
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||
it('Decrypting key with correct passphrase returns true', async function () {
|
||||
expect(await privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||
});
|
||||
|
||||
tryTests('CFB mode (asm.js)', tests, {
|
||||
|
@ -719,7 +719,7 @@ describe('OpenPGP.js public api tests', function() {
|
|||
privateKey: privateKey.keys[0],
|
||||
passphrase: 'incorrect'
|
||||
}).catch(function(error){
|
||||
expect(error.message).to.match(/Invalid passphrase/);
|
||||
expect(error.message).to.match(/Incorrect key passphrase/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -727,9 +727,10 @@ describe('OpenPGP.js public api tests', function() {
|
|||
describe('encryptSessionKey, decryptSessionKeys', function() {
|
||||
const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]);
|
||||
|
||||
beforeEach(function(done) {
|
||||
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||
privateKey.keys[0].verifyPrimaryUser().then(() => done());
|
||||
beforeEach(async function() {
|
||||
expect(await privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||
await privateKey.keys[0].verifyPrimaryUser();
|
||||
return true;
|
||||
});
|
||||
|
||||
it('should encrypt with public key', function() {
|
||||
|
@ -867,33 +868,13 @@ describe('OpenPGP.js public api tests', function() {
|
|||
'=6XMW\r\n' +
|
||||
'-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n';
|
||||
|
||||
beforeEach(function (done) {
|
||||
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||
Promise.all([
|
||||
privateKey.keys[0].verifyPrimaryUser(),
|
||||
privateKey_2000_2008.keys[0].verifyPrimaryUser(),
|
||||
privateKey_1337.keys[0].verifyPrimaryUser(),
|
||||
privateKey_2038_2045.keys[0].verifyPrimaryUser()
|
||||
]).then(() => done());
|
||||
});
|
||||
|
||||
it('should encrypt then decrypt', function () {
|
||||
const encOpt = {
|
||||
data: plaintext,
|
||||
publicKeys: publicKey.keys
|
||||
};
|
||||
const decOpt = {
|
||||
privateKeys: privateKey.keys
|
||||
};
|
||||
return openpgp.encrypt(encOpt).then(function (encrypted) {
|
||||
expect(encrypted.data).to.match(/^-----BEGIN PGP MESSAGE/);
|
||||
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||
return openpgp.decrypt(decOpt);
|
||||
}).then(function (decrypted) {
|
||||
expect(decrypted.data).to.equal(plaintext);
|
||||
expect(decrypted.signatures).to.exist;
|
||||
expect(decrypted.signatures.length).to.equal(0);
|
||||
});
|
||||
beforeEach( async function () {
|
||||
expect(await privateKey.keys[0].decrypt(passphrase)).to.be.true;
|
||||
await privateKey.keys[0].verifyPrimaryUser();
|
||||
await privateKey_2000_2008.keys[0].verifyPrimaryUser();
|
||||
await privateKey_1337.keys[0].verifyPrimaryUser();
|
||||
await privateKey_2038_2045.keys[0].verifyPrimaryUser();
|
||||
return true;
|
||||
});
|
||||
|
||||
it('should encrypt then decrypt', function () {
|
||||
|
@ -1001,9 +982,9 @@ describe('OpenPGP.js public api tests', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should encrypt using custom session key and decrypt using session key', function () {
|
||||
it('should encrypt using custom session key and decrypt using session key', async function () {
|
||||
const sessionKey = {
|
||||
data: openpgp.crypto.generateSessionKey('aes256'),
|
||||
data: await openpgp.crypto.generateSessionKey('aes256'),
|
||||
algorithm: 'aes256'
|
||||
};
|
||||
const encOpt = {
|
||||
|
|
|
@ -55,7 +55,7 @@ describe("Packet", function() {
|
|||
'=KXkj\n' +
|
||||
'-----END PGP PRIVATE KEY BLOCK-----';
|
||||
|
||||
it('Symmetrically encrypted packet', function(done) {
|
||||
it('Symmetrically encrypted packet', async function() {
|
||||
const message = new openpgp.packet.List();
|
||||
|
||||
const literal = new openpgp.packet.Literal();
|
||||
|
@ -68,18 +68,17 @@ describe("Packet", function() {
|
|||
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);
|
||||
const algo = 'aes256';
|
||||
|
||||
enc.encrypt(algo, key);
|
||||
await enc.encrypt(algo, key);
|
||||
|
||||
const msg2 = new openpgp.packet.List();
|
||||
msg2.read(message.write());
|
||||
msg2[0].ignore_mdc_error = true;
|
||||
msg2[0].decrypt(algo, key);
|
||||
await msg2[0].decrypt(algo, key);
|
||||
|
||||
expect(stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
|
||||
done();
|
||||
});
|
||||
|
||||
it('Symmetrically encrypted packet - MDC error for modern cipher', function() {
|
||||
it('Symmetrically encrypted packet - MDC error for modern cipher', async function() {
|
||||
const message = new openpgp.packet.List();
|
||||
|
||||
const literal = new openpgp.packet.Literal();
|
||||
|
@ -87,19 +86,19 @@ describe("Packet", function() {
|
|||
|
||||
const enc = new openpgp.packet.SymmetricallyEncrypted();
|
||||
message.push(enc);
|
||||
enc.packets.push(literal);
|
||||
await enc.packets.push(literal);
|
||||
|
||||
const key = '12345678901234567890123456789012';
|
||||
const algo = 'aes256';
|
||||
|
||||
enc.encrypt(algo, key);
|
||||
await enc.encrypt(algo, key);
|
||||
|
||||
const msg2 = new openpgp.packet.List();
|
||||
msg2.read(message.write());
|
||||
expect(msg2[0].decrypt.bind(msg2[0], algo, key)).to.throw('Decryption failed due to missing MDC in combination with modern cipher.');
|
||||
expect(msg2[0].decrypt(algo, key)).to.eventually.be.rejectedWith('Decryption failed due to missing MDC in combination with modern cipher.');
|
||||
});
|
||||
|
||||
it('Sym. encrypted integrity protected packet', function(done) {
|
||||
it('Sym. encrypted integrity protected packet', async function() {
|
||||
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);
|
||||
const algo = 'aes256';
|
||||
|
||||
|
@ -110,15 +109,14 @@ describe("Packet", function() {
|
|||
msg.push(enc);
|
||||
literal.setText('Hello world!');
|
||||
enc.packets.push(literal);
|
||||
enc.encrypt(algo, key);
|
||||
await enc.encrypt(algo, key);
|
||||
|
||||
const msg2 = new openpgp.packet.List();
|
||||
msg2.read(msg.write());
|
||||
|
||||
msg2[0].decrypt(algo, key);
|
||||
await msg2[0].decrypt(algo, key);
|
||||
|
||||
expect(stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
|
||||
done();
|
||||
});
|
||||
|
||||
it('Sym. encrypted AEAD protected packet', function() {
|
||||
|
@ -310,7 +308,7 @@ describe("Packet", function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('Sym. encrypted session key reading/writing', function(done) {
|
||||
it('Sym. encrypted session key reading/writing', async function() {
|
||||
const passphrase = 'hello';
|
||||
const algo = 'aes256';
|
||||
|
||||
|
@ -323,23 +321,22 @@ describe("Packet", function() {
|
|||
msg.push(enc);
|
||||
|
||||
key_enc.sessionKeyAlgorithm = algo;
|
||||
key_enc.decrypt(passphrase);
|
||||
await key_enc.encrypt(passphrase);
|
||||
|
||||
const key = key_enc.sessionKey;
|
||||
|
||||
literal.setText('Hello world!');
|
||||
enc.packets.push(literal);
|
||||
enc.encrypt(algo, key);
|
||||
await enc.encrypt(algo, key);
|
||||
|
||||
const msg2 = new openpgp.packet.List();
|
||||
msg2.read(msg.write());
|
||||
|
||||
msg2[0].decrypt(passphrase);
|
||||
await msg2[0].decrypt(passphrase);
|
||||
const key2 = msg2[0].sessionKey;
|
||||
msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2);
|
||||
await msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2);
|
||||
|
||||
expect(stringify(msg2[1].packets[0].data)).to.equal(stringify(literal.data));
|
||||
done();
|
||||
});
|
||||
|
||||
it('Secret key encryption/decryption test', function() {
|
||||
|
|
|
@ -127,7 +127,7 @@ describe('X25519 Cryptography', function () {
|
|||
return data[name].pub_key;
|
||||
}
|
||||
|
||||
function load_priv_key(name) {
|
||||
async function load_priv_key(name) {
|
||||
if (data[name].priv_key) {
|
||||
return data[name].priv_key;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ describe('X25519 Cryptography', function () {
|
|||
expect(pk.err).to.not.exist;
|
||||
expect(pk.keys).to.have.length(1);
|
||||
expect(pk.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id);
|
||||
expect(pk.keys[0].decrypt(data[name].pass)).to.be.true;
|
||||
expect(await pk.keys[0].decrypt(data[name].pass)).to.be.true;
|
||||
data[name].priv_key = pk.keys[0];
|
||||
return data[name].priv_key;
|
||||
}
|
||||
|
@ -149,10 +149,10 @@ describe('X25519 Cryptography', function () {
|
|||
|
||||
// This test is slow because the keys are generated by GPG2, which
|
||||
// by default chooses a larger number for S2K iterations than we do.
|
||||
it('Load private key', function (done) {
|
||||
load_priv_key('light');
|
||||
load_priv_key('night');
|
||||
done();
|
||||
it('Load private key', async function () {
|
||||
await load_priv_key('light');
|
||||
await load_priv_key('night');
|
||||
return true;
|
||||
});
|
||||
|
||||
it('Verify clear signed message', function () {
|
||||
|
@ -167,56 +167,49 @@ describe('X25519 Cryptography', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Sign message', function () {
|
||||
it('Sign message', async function () {
|
||||
const name = 'light';
|
||||
const priv = load_priv_key(name);
|
||||
return openpgp.sign({ privateKeys: [priv], data: data[name].message + "\n" }).then(function (signed) {
|
||||
const pub = load_pub_key(name);
|
||||
const msg = openpgp.cleartext.readArmored(signed.data);
|
||||
return openpgp.verify({ publicKeys: [pub], message: msg}).then(function (result) {
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data[name].message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
});
|
||||
const priv = await load_priv_key(name);
|
||||
const signed = await openpgp.sign({ privateKeys: [priv], data: data[name].message + "\n" });
|
||||
const pub = load_pub_key(name);
|
||||
const msg = openpgp.cleartext.readArmored(signed.data);
|
||||
const result = await openpgp.verify({ publicKeys: [pub], message: msg});
|
||||
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data[name].message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
|
||||
it('Decrypt and verify message', function () {
|
||||
it('Decrypt and verify message', async function () {
|
||||
const light = load_pub_key('light');
|
||||
const night = load_priv_key('night');
|
||||
expect(night.decrypt(data.night.pass)).to.be.true;
|
||||
const night = await load_priv_key('night');
|
||||
expect(await night.decrypt(data.night.pass)).to.be.true;
|
||||
const msg = openpgp.message.readArmored(data.night.message_encrypted);
|
||||
return openpgp.decrypt(
|
||||
{ privateKeys: night, publicKeys: [light], message: msg }
|
||||
).then(function (result) {
|
||||
expect(result).to.exist;
|
||||
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
|
||||
expect(result.data.trim()).to.equal(data.night.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
const result = await openpgp.decrypt({ privateKeys: night, publicKeys: [light], message: msg });
|
||||
|
||||
expect(result).to.exist;
|
||||
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
|
||||
expect(result.data.trim()).to.equal(data.night.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
|
||||
it('Encrypt and sign message', function () {
|
||||
const night = load_pub_key('night');
|
||||
const light = load_priv_key('light');
|
||||
expect(light.decrypt(data.light.pass)).to.be.true;
|
||||
openpgp.encrypt(
|
||||
{ publicKeys: [night], privateKeys: [light], data: data.light.message + "\n" }
|
||||
).then(function (encrypted) {
|
||||
const message = openpgp.message.readArmored(encrypted.data);
|
||||
const light = load_pub_key('light');
|
||||
const night = load_priv_key('night');
|
||||
return openpgp.decrypt(
|
||||
{ privateKeys: night, publicKeys: [light], message: message }
|
||||
).then(function (result) {
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data.light.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
});
|
||||
it('Encrypt and sign message', async function () {
|
||||
const nightPublic = load_pub_key('night');
|
||||
const lightPrivate = await load_priv_key('light');
|
||||
expect(await lightPrivate.decrypt(data.light.pass)).to.be.true;
|
||||
const encrypted = await openpgp.encrypt({ publicKeys: [nightPublic], privateKeys: [lightPrivate], data: data.light.message + "\n" });
|
||||
|
||||
const message = openpgp.message.readArmored(encrypted.data);
|
||||
const lightPublic = load_pub_key('light');
|
||||
const nightPrivate = await load_priv_key('night');
|
||||
const result = await openpgp.decrypt({ privateKeys: nightPrivate, publicKeys: [lightPublic], message: message });
|
||||
|
||||
expect(result).to.exist;
|
||||
expect(result.data.trim()).to.equal(data.light.message);
|
||||
expect(result.signatures).to.have.length(1);
|
||||
expect(result.signatures[0].valid).to.be.true;
|
||||
});
|
||||
|
||||
// TODO export, then reimport key and validate
|
||||
|
|
|
@ -47,15 +47,12 @@ tryTests('Async Proxy', tests, {
|
|||
|
||||
function tests() {
|
||||
|
||||
describe('Error handling', function() {
|
||||
it('Depleted random buffer in worker gives error', function() {
|
||||
const wProxy = new openpgp.AsyncProxy({ path:'../dist/openpgp.worker.js' });
|
||||
wProxy.worker = new Worker('../dist/openpgp.worker.js');
|
||||
wProxy.worker.onmessage = wProxy.onMessage.bind(wProxy);
|
||||
wProxy.seedRandom(10);
|
||||
return wProxy.delegate('encrypt', { publicKeys:[pubKey], data:plaintext }).catch(function(err) {
|
||||
expect(err.message).to.match(/Random number buffer depleted/);
|
||||
});
|
||||
describe('Random number pipeline', function() {
|
||||
it('Random number buffer automatically reseeded', function() {
|
||||
const worker = new Worker('../dist/openpgp.worker.js');
|
||||
const wProxy = new openpgp.AsyncProxy({ path:'../dist/openpgp.worker.js', workers: [worker] });
|
||||
|
||||
return wProxy.delegate('encrypt', { publicKeys:[pubKey], data:plaintext });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user