diff --git a/src/key.js b/src/key.js index 71e23c71..114915fa 100644 --- a/src/key.js +++ b/src/key.js @@ -431,7 +431,7 @@ Key.prototype.verifyPrimaryKey = async function(date=new Date()) { return enums.keyStatus.no_self_cert; } // check for valid, unrevoked, unexpired self signature - const { user, selfCertification } = await this.getPrimaryUser(date); + const { user, selfCertification } = await this.getPrimaryUser(date) || {}; if (!user) { return enums.keyStatus.invalid; } @@ -469,21 +469,41 @@ Key.prototype.getExpirationTime = async function() { * @returns {{user: Array, selfCertification: Array}|null} The primary user and the self signature */ Key.prototype.getPrimaryUser = async function(date=new Date()) { - // FIXME - await this.verifyPrimaryUser(); + const { primaryKey } = this; let primaryUsers = []; - this.users.forEach((user, i) => { + let lastCreated = null; + let lastPrimaryUserID = null; + // TODO replace when Promise.forEach is implemented + for (let i = 0; i < this.users.length; i++) { + const user = this.users[i]; if (!user.userId) { return; } - user.selfCertifications.forEach(cert => { - // only consider already validated certificates - if (!cert.verified || cert.revoked || cert.isExpired(date)) { - return; + const dataToVerify = { userid: user.userId , key: primaryKey }; + for (let j = 0; j < user.selfCertifications.length; j++) { + const cert = user.selfCertifications[j]; + // skip if certificate is not the most recent + if ((cert.isPrimaryUserID && cert.isPrimaryUserID < lastPrimaryUserID) || + (!lastPrimaryUserID && cert.created < lastCreated)) { + continue; } + // skip if certificates is invalid, revoked, or expired + // eslint-disable-next-line no-await-in-loop + if (!(cert.verified || await cert.verify(primaryKey, dataToVerify))) { + continue; + } + // eslint-disable-next-line no-await-in-loop + if (cert.revoked || await user.isRevoked(primaryKey, cert, null, date)) { + continue; + } + if (cert.isExpired(date)) { + continue; + } + lastPrimaryUserID = cert.isPrimaryUserID; + lastCreated = cert.created; primaryUsers.push({ index: i, user: user, selfCertification: cert }); - }); - }); + } + } // sort by primary user flag and signature creation time primaryUsers = primaryUsers.sort(function(a, b) { const A = a.selfCertification; @@ -630,42 +650,12 @@ Key.prototype.signAllUsers = async function(privateKeys) { * valid: Boolean}>>} List of signer's keyid and validity of signature */ Key.prototype.verifyPrimaryUser = async function(keys) { - const { primaryKey } = this; - const primaryUsers = []; - let lastCreated = null; - let lastPrimaryUserID = null; - await Promise.all(this.users.map(async function(user) { - if (!user.userId && !user.userAttribute) { - return; - } - const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey }; - // TODO replace when Promise.forEach is implemented - for (let i = 0; i < user.selfCertifications.length; i++) { - const cert = user.selfCertifications[i]; - // skip if certificate is not the most recent - if ((cert.isPrimaryUserID && cert.isPrimaryUserID < lastPrimaryUserID) || - (!lastPrimaryUserID && cert.created < lastCreated)) { - return; - } - // skip if certificates is invalid, revoked, or expired - // eslint-disable-next-line no-await-in-loop - if (!(cert.verified || await cert.verify(primaryKey, dataToVerify))) { - return; - } - // eslint-disable-next-line no-await-in-loop - if (cert.revoked || await user.isRevoked(primaryKey, cert)) { - return; - } - if (cert.isExpired()) { - return; - } - lastPrimaryUserID = cert.isPrimaryUserID; - lastCreated = cert.created; - primaryUsers.push(user); - } - })); - const user = primaryUsers.pop(); - const results = !user ? [] : keys ? await user.verifyAllCertifications(primaryKey, keys) : + const primaryKey = this.primaryKey; + const { user } = await this.getPrimaryUser() || {}; + if (!user) { + throw new Error('Could not find primary user'); + } + const results = keys ? await user.verifyAllCertifications(primaryKey, keys) : [{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey) === enums.keyStatus.valid }]; return results; }; diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index afee0f55..26f816b8 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -192,9 +192,9 @@ describe('Elliptic Curve Cryptography', function () { it('Signature generation', function () { const curve = new elliptic_curves.Curve('p256'); let key = curve.keyFromPrivate(key_data.p256.priv); - return key.sign(signature_data.message, 8).then(signature => { + return key.sign(signature_data.message, 8).then(async signature => { key = curve.keyFromPublic(key_data.p256.pub); - expect( + await expect( key.verify(signature_data.message, signature, 8) ).to.eventually.be.true; }); @@ -302,8 +302,8 @@ describe('Elliptic Curve Cryptography', function () { const keyPrivate = new Uint8Array(keyPair.getPrivate()); const oid = curve.oid; const message = p384_message; - return elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => { - expect(elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic)) + return elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(async signature => { + await expect(elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic)) .to.eventually.be.true; }); }); diff --git a/test/general/key.js b/test/general/key.js index c9d36904..4e09926d 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -810,15 +810,14 @@ describe('Key', function() { }); }); - it('update() - merge user', function(done) { + it('update() - merge user', function() { const source = openpgp.key.readArmored(pub_sig_test).keys[0]; const dest = openpgp.key.readArmored(pub_sig_test).keys[0]; expect(source.users[1]).to.exist; dest.users.pop(); - dest.update(source).then(() => { + return dest.update(source).then(() => { expect(dest.users[1]).to.exist; expect(dest.users[1].userId).to.equal(source.users[1].userId); - done(); }); }); @@ -912,18 +911,17 @@ describe('Key', function() { .to.be.rejectedWith('Cannot update public key with private key if subkey mismatch').notify(done); }); - it('update() - merge subkey binding signatures', function(done) { + it('update() - merge subkey binding signatures', async function() { const source = openpgp.key.readArmored(pgp_desktop_pub).keys[0]; const dest = openpgp.key.readArmored(pgp_desktop_priv).keys[0]; expect(source.subKeys[0].bindingSignatures[0]).to.exist; - expect(source.subKeys[0].verify(source.primaryKey)) + await expect(source.subKeys[0].verify(source.primaryKey)) .to.eventually.equal(openpgp.enums.keyStatus.valid); expect(dest.subKeys[0].bindingSignatures[0]).to.not.exist; - dest.update(source).then(() => { + return dest.update(source).then(async () => { expect(dest.subKeys[0].bindingSignatures[0]).to.exist; - expect(dest.subKeys[0].verify(source.primaryKey)) + await expect(dest.subKeys[0].verify(source.primaryKey)) .to.eventually.equal(openpgp.enums.keyStatus.valid); - done(); }); }); @@ -1217,12 +1215,12 @@ describe('Key', function() { opt.privateKey = key; opt.userIds = [userId2, userId3]; opt.passphrase = '123'; - return openpgp.reformatKey(opt).then(function(newKey) { + return openpgp.reformatKey(opt).then(async function(newKey) { newKey = newKey.key; expect(newKey.users.length).to.equal(2); expect(newKey.users[0].userId.userid).to.equal(userId2); expect(newKey.primaryKey.isDecrypted).to.be.false; - newKey.decrypt('123'); + await newKey.decrypt('123'); expect(newKey.primaryKey.isDecrypted).to.be.true; }); }); diff --git a/test/general/openpgp.js b/test/general/openpgp.js index c96746c0..72f62492 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -635,8 +635,8 @@ describe('OpenPGP.js public api tests', function() { openpgp.config.aead_protect = aead_protectVal; }); - 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 wrong passphrase rejected', async function () { + await expect(privateKey.keys[0].decrypt('wrong passphrase')).to.eventually.be.rejectedWith('Incorrect key passphrase'); }); it('Decrypting key with correct passphrase returns true', async function () { @@ -891,9 +891,9 @@ describe('OpenPGP.js public api tests', function() { }); }); - it('should encrypt then decrypt with multiple private keys', function () { + it('should encrypt then decrypt with multiple private keys', async function () { const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0]; - privKeyDE.decrypt(passphrase); + await privKeyDE.decrypt(passphrase); const encOpt = { data: plaintext, @@ -933,9 +933,9 @@ describe('OpenPGP.js public api tests', function() { }); }); - it('should encrypt then decrypt with wildcard with multiple private keys', function () { + it('should encrypt then decrypt with wildcard with multiple private keys', async function () { const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0]; - privKeyDE.decrypt(passphrase); + await privKeyDE.decrypt(passphrase); const encOpt = { data: plaintext, @@ -1123,9 +1123,9 @@ describe('OpenPGP.js public api tests', function() { }); }); - it('should encrypt and decrypt/verify with detached signature as input and detached flag not set for encryption', function () { + it('should encrypt and decrypt/verify with detached signature as input and detached flag not set for encryption', async function () { const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0]; - privKeyDE.decrypt(passphrase); + await privKeyDE.decrypt(passphrase); const pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0]; @@ -1321,9 +1321,9 @@ describe('OpenPGP.js public api tests', function() { }); }); - it('should encrypt and decrypt/verify both signatures when signed with two private keys', function () { + it('should encrypt and decrypt/verify both signatures when signed with two private keys', async function () { const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0]; - privKeyDE.decrypt(passphrase); + await privKeyDE.decrypt(passphrase); const pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0]; @@ -1376,9 +1376,9 @@ describe('OpenPGP.js public api tests', function() { }); }); - it('should sign and verify cleartext data with multiple private keys', function () { + it('should sign and verify cleartext data with multiple private keys', async function () { const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0]; - privKeyDE.decrypt(passphrase); + await privKeyDE.decrypt(passphrase); const signOpt = { data: plaintext, @@ -1688,10 +1688,10 @@ describe('OpenPGP.js public api tests', function() { describe('ELG / DSA encrypt, decrypt, sign, verify', function() { - it('round trip test', function () { + it('round trip test', async function () { const pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0]; const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0]; - privKeyDE.decrypt(passphrase); + await privKeyDE.decrypt(passphrase); return openpgp.encrypt({ publicKeys: pubKeyDE, privateKeys: privKeyDE, @@ -1765,9 +1765,9 @@ describe('OpenPGP.js public api tests', function() { '=IkKW', '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'); - it('Decrypt message', function() { + it('Decrypt message', async function() { const privKey = openpgp.key.readArmored(priv_key).keys[0]; - privKey.decrypt('1234'); + await privKey.decrypt('1234'); const message = openpgp.message.readArmored(pgp_msg); return openpgp.decrypt({ privateKeys:privKey, message:message }).then(function(decrypted) { diff --git a/test/general/packet.js b/test/general/packet.js index 1c07bd76..ec9915e0 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -95,7 +95,7 @@ describe("Packet", function() { const msg2 = new openpgp.packet.List(); msg2.read(message.write()); - expect(msg2[0].decrypt(algo, key)).to.eventually.be.rejectedWith('Decryption failed due to missing MDC in combination with modern cipher.'); + await 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', async function() { @@ -415,7 +415,7 @@ describe("Packet", function() { const payload = msg[1].packets[0].packets; - expect(payload[2].verify( + await expect(payload[2].verify( key[0], payload[1] )).to.eventually.be.true; }); @@ -473,7 +473,7 @@ describe("Packet", function() { signature.publicKeyAlgorithm = 'rsa_sign'; signature.signatureType = 'binary'; - signature.sign(key, literal).then(() => { + signature.sign(key, literal).then(async () => { signed.push(literal); signed.push(signature); @@ -483,8 +483,8 @@ describe("Packet", function() { const signed2 = new openpgp.packet.List(); signed2.read(raw); - expect(signed2[1].verify(key, signed2[0])).to.eventually.be.true; - }); + await expect(signed2[1].verify(key, signed2[0])).to.eventually.be.true; + }); }); }); }); diff --git a/test/general/signature.js b/test/general/signature.js index 0728e126..5db953e0 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -324,11 +324,11 @@ describe("Signature", function() { '=Q4tk', '-----END PGP MESSAGE-----'].join('\n'); - it('Testing signature checking on CAST5-enciphered message', function() { + it('Testing signature checking on CAST5-enciphered message', async function() { const priv_key = openpgp.key.readArmored(priv_key_arm1).keys[0]; const pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0]; const msg = openpgp.message.readArmored(msg_arm1); - priv_key.decrypt("abcd"); + await priv_key.decrypt("abcd"); return openpgp.decrypt({ privateKeys: priv_key, publicKeys:[pub_key], message:msg }).then(function(decrypted) { expect(decrypted.data).to.exist; expect(decrypted.signatures[0].valid).to.be.true; @@ -336,7 +336,7 @@ describe("Signature", function() { }); }); - it('Testing GnuPG stripped-key extensions', function() { + it('Testing GnuPG stripped-key extensions', async function() { // exercises the GnuPG s2k type 1001 extension: // the secrets on the primary key have been stripped. const priv_key_gnupg_ext = openpgp.key.readArmored( @@ -369,7 +369,7 @@ describe("Signature", function() { const pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0]; const msg = openpgp.message.readArmored(msg_arm1); - priv_key_gnupg_ext.subKeys[0].subKey.decrypt("abcd"); + await priv_key_gnupg_ext.subKeys[0].subKey.decrypt("abcd"); return msg.decrypt([priv_key_gnupg_ext]).then(function(msg) { return msg.verify([pub_key]).then(verified => { expect(verified).to.exist; @@ -426,7 +426,7 @@ describe("Signature", function() { }); }); - it('Verify signature of signed and encrypted message from GPG2 with openpgp.decrypt', function() { + it('Verify signature of signed and encrypted message from GPG2 with openpgp.decrypt', async function() { const msg_armor = ['-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', @@ -448,7 +448,7 @@ describe("Signature", function() { const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId)); + await Promise.all(esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId))); return openpgp.decrypt({ privateKeys: privKey, publicKeys:[pubKey], message:esMsg }).then(function(decrypted) { expect(decrypted.data).to.exist; @@ -459,7 +459,7 @@ describe("Signature", function() { }); }); - it('Verify signature of signed and encrypted message from PGP 10.3.0 with openpgp.decrypt', function() { + it('Verify signature of signed and encrypted message from PGP 10.3.0 with openpgp.decrypt', async function() { const msg_armor = ['-----BEGIN PGP MESSAGE-----', 'Version: Encryption Desktop 10.3.0 (Build 9307)', @@ -482,7 +482,7 @@ describe("Signature", function() { const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId)); + await Promise.all(esMsg.getEncryptionKeyIds().map(keyId => privKey.decrypt('hello world', keyId))); return openpgp.decrypt({ privateKeys: privKey, publicKeys:[pubKey], message:esMsg }).then(function(decrypted) { expect(decrypted.data).to.exist; @@ -580,11 +580,11 @@ describe("Signature", function() { }); }); - it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', function() { + it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', async function() { const plaintext = 'short message\nnext line\n한국어/조선말'; const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey.primaryKey.decrypt('hello world'); + await privKey.primaryKey.decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext }).then(function(signed) { @@ -600,11 +600,11 @@ describe("Signature", function() { }); }); - it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', function() { + it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', async function() { const plaintext = openpgp.util.str_to_Uint8Array('short message\nnext line\n한국어/조선말'); const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey.primaryKey.decrypt('hello world'); + await privKey.primaryKey.decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext }).then(function(signed) { @@ -620,11 +620,11 @@ describe("Signature", function() { }); }); - it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', function() { + it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', async function() { const plaintext = openpgp.util.str_to_Uint8Array('short message\nnext line\n한국어/조선말'); const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey.primaryKey.decrypt('hello world'); + await privKey.primaryKey.decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext, armor:false }).then(function(signed) { @@ -786,11 +786,11 @@ describe("Signature", function() { }); }); - it('Detached signature signing and verification', function() { + it('Detached signature signing and verification', async function() { const msg = openpgp.message.fromText('hello'); const pubKey2 = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey2 = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey2.decrypt('hello world'); + await privKey2.decrypt('hello world'); const opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null}; if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys diff --git a/test/general/x25519.js b/test/general/x25519.js index 1f1e62ae..c9b5031a 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -217,7 +217,7 @@ describe('X25519 Cryptography', function () { userIds: {name: "Hi", email: "hi@hel.lo"}, curve: "ed25519" }; - return openpgp.generateKey(options).then(function (firstKey) { + return openpgp.generateKey(options).then(async function (firstKey) { expect(firstKey).to.exist; expect(firstKey.privateKeyArmored).to.exist; expect(firstKey.publicKeyArmored).to.exist; @@ -236,10 +236,10 @@ describe('X25519 Cryptography', function () { // Self Certificate is valid const user = hi.users[0]; - expect(user.selfCertifications[0].verify( + await expect(user.selfCertifications[0].verify( primaryKey, { userid: user.userId, key: primaryKey } )).to.eventually.be.true; - expect(user.verifyCertificate( + await expect(user.verifyCertificate( primaryKey, user.selfCertifications[0], [hi.toPublic()] )).to.eventually.equal(openpgp.enums.keyStatus.valid); @@ -247,7 +247,7 @@ describe('X25519 Cryptography', function () { userIds: { name: "Bye", email: "bye@good.bye" }, curve: "curve25519" }; - return openpgp.generateKey(options).then(function (secondKey) { + return openpgp.generateKey(options).then(async function (secondKey) { const bye = secondKey.key; expect(bye.primaryKey.params[0].getName()).to.equal('ed25519'); expect(bye.primaryKey.algorithm).to.equal('eddsa'); @@ -256,10 +256,10 @@ describe('X25519 Cryptography', function () { // Self Certificate is valid const user = bye.users[0]; - expect(user.selfCertifications[0].verify( + await expect(user.selfCertifications[0].verify( bye.primaryKey, { userid: user.userId, key: bye.primaryKey } )).to.eventually.be.true; - expect(user.verifyCertificate( + await expect(user.verifyCertificate( bye.primaryKey, user.selfCertifications[0], [bye.toPublic()] )).to.eventually.equal(openpgp.enums.keyStatus.valid); @@ -531,17 +531,15 @@ describe('X25519 Cryptography', function () { '=xeG/', '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); const hi = openpgp.key.readArmored(pubKey).keys[0]; - return hi.verifyPrimaryUser().then(() => { - const results = hi.getPrimaryUser(); - expect(results).to.exist; - expect(results.user).to.exist; - const user = results.user; - expect(user.selfCertifications[0].verify( - hi.primaryKey, {userid: user.userId, key: hi.primaryKey} - )).to.eventually.be.true; - expect(user.verifyCertificate( - hi.primaryKey, user.selfCertifications[0], [hi] - )).to.eventually.equal(openpgp.enums.keyStatus.valid); - }); + const results = hi.getPrimaryUser(); + expect(results).to.exist; + expect(results.user).to.exist; + const user = results.user; + expect(user.selfCertifications[0].verify( + hi.primaryKey, {userid: user.userId, key: hi.primaryKey} + )).to.eventually.be.true; + expect(user.verifyCertificate( + hi.primaryKey, user.selfCertifications[0], [hi] + )).to.eventually.equal(openpgp.enums.keyStatus.valid); }); */ });