Base functionality moved to User.prototype

This commit is contained in:
Aydar Zartdinov 2017-02-10 12:12:38 +03:00
parent 715f98bb38
commit 3fa4c0c760
3 changed files with 237 additions and 75 deletions

View File

@ -639,83 +639,61 @@ Key.prototype.revoke = function() {
}; };
/** /**
* Signs the public key * Signs primary user of key
* @param {Array<module:key~Key>} privateKey decrypted private keys for signing * @param {Array<module:key~Key>} privateKey decrypted private keys for signing
* @return {module:key~Key} new public key with new certificate signature * @return {module:key~Key} new public key with new certificate signature
*/ */
Key.prototype.sign = function(privateKeys) { Key.prototype.signPrimaryUser = function(privateKeys) {
var primaryUser, primaryKey, dataToSign, signingKeyPacket, signaturePacket, user, key; var {index, user} = this.getPrimaryUser() || {};
if (this.isPrivate()) { if (!user) {
throw new Error('Only public key can be signed');
}
primaryUser = this.getPrimaryUser();
if (!primaryUser) {
throw new Error('Could not find primary user'); throw new Error('Could not find primary user');
} }
primaryKey = this.primaryKey; user = user.sign(this.primaryKey, privateKeys);
dataToSign = {}; var key = new Key(this.toPacketlist());
dataToSign.userid = primaryUser.user.userId || primaryUser.user.userAttribute; key.users[index] = user;
dataToSign.key = primaryKey;
user = new User(primaryUser.user.userId || primaryUser.user.userAttribute);
user.otherCertifications = [];
privateKeys.forEach(function(privateKey) {
if (privateKey.isPublic()) {
throw new Error('Need private key for signing');
}
if (privateKey.primaryKey.getKeyId().equals(primaryKey.getKeyId())) {
throw new Error('No need to self signinig');
}
signingKeyPacket = privateKey.getSigningKeyPacket();
if (!signingKeyPacket) {
throw new Error('Could not find valid signing key packet');
}
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
signaturePacket = new packet.Signature();
// Most OpenPGP implementations use generic certification (0x10)
signaturePacket.signatureType = enums.write(enums.signature, enums.signature.cert_generic);
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.hashAlgorithm = privateKey.getPreferredHashAlgorithm();
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.signingKeyId = signingKeyPacket.getKeyId();
signaturePacket.sign(signingKeyPacket, dataToSign);
user.otherCertifications.push(signaturePacket);
});
user.update(primaryUser.user, this.primaryKey);
key = new Key(this.toPacketlist());
key.users[primaryUser.index] = user;
return key; return key;
}; };
/** /**
* Verifies public key * 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
*/
Key.prototype.signAllUsers = function(privateKeys) {
var users = this.users.map(user => user.sign(this.primaryKey, privateKeys));
var key = new Key(this.toPacketlist());
key.users = users;
return key;
};
/**
* Verifies primary user of key
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures * @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 * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
*/ */
Key.prototype.verify = function(keys) { Key.prototype.verifyPrimaryUser = function(keys) {
var primaryKey, primaryUser, user; var {user} = this.getPrimaryUser() || {};
if (this.isPrivate()) { if (!user) {
throw new Error('Only public key can be verified');
}
primaryKey = this.primaryKey;
primaryUser = this.getPrimaryUser();
if (!primaryUser) {
throw new Error('Could not find primary user'); throw new Error('Could not find primary user');
} }
user = primaryUser.user; return user.verifyAllSignatures(this.primaryKey, keys);
if (!user.otherCertifications) { };
return [];
} /**
return user.otherCertifications.map(function(signaturePacket) { * Verifies all users of key
var keyPacket = keys.find(function(key) { * @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
return key.getSigningKeyPacket(signaturePacket.issuerKeyId); * @return {Array<({userid: String, keyid: module:type/keyid, valid: Boolean})>} list of userid, signer's keyid and validity of signature
}) || null; */
return { Key.prototype.verifyAllUsers = function(keys) {
keyid: signaturePacket.issuerKeyId, return this.users.reduce((signatures, user) => {
valid: keyPacket && signaturePacket.verify(keyPacket.primaryKey, {userid: user.userId || user.userAttribute, key: primaryKey}) return signatures.concat(
}; user.verifyAllSignatures(this.primaryKey, keys).map(signature => ({
}); userid: user.userId.userid,
keyid: signature.keyid,
valid: signature.valid
}))
);
}, []);
}; };
/** /**
@ -807,6 +785,64 @@ User.prototype.isValidSelfCertificate = function(primaryKey, selfCertificate) {
return false; return false;
}; };
/**
* 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
*/
User.prototype.sign = function(primaryKey, privateKeys) {
var user, dataToSign, signingKeyPacket, signaturePacket;
dataToSign = {};
dataToSign.key = primaryKey;
dataToSign.userid = this.userId || this.userAttribute;
user = new User(this.userId || this.userAttribute);
user.otherCertifications = [];
privateKeys.forEach(privateKey => {
if (privateKey.isPublic()) {
throw new Error('Need private key for signing');
}
if (privateKey.primaryKey.getKeyId().equals(primaryKey.getKeyId())) {
throw new Error('Not implemented for self signing');
}
signingKeyPacket = privateKey.getSigningKeyPacket();
if (!signingKeyPacket) {
throw new Error('Could not find valid signing key packet');
}
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
signaturePacket = new packet.Signature();
// Most OpenPGP implementations use generic certification (0x10)
signaturePacket.signatureType = enums.write(enums.signature, enums.signature.cert_generic);
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.hashAlgorithm = privateKey.getPreferredHashAlgorithm();
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.signingKeyId = signingKeyPacket.getKeyId();
signaturePacket.sign(signingKeyPacket, dataToSign);
user.otherCertifications.push(signaturePacket);
});
user.update(this, primaryKey);
return user;
};
/**
* Verifies all user signatures
* @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
*/
User.prototype.verifyAllSignatures = function(primaryKey, keys) {
var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
return this.selfCertifications.concat(this.otherCertifications).map(signaturePacket => {
var keyPacket = keys.find(key => key.getSigningKeyPacket(signaturePacket.issuerKeyId)) || null;
return {
keyid: signaturePacket.issuerKeyId,
valid: keyPacket && signaturePacket.verify(keyPacket.primaryKey, dataToVerify)
};
});
};
/** /**
* Verify User. Checks for existence of self signatures, revocation signatures * Verify User. Checks for existence of self signatures, revocation signatures
* and validity of self signature * and validity of self signature

View File

@ -303,7 +303,7 @@ describe('Key', function() {
'=MVfN', '=MVfN',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var pgp_desktop_pub = var pgp_desktop_pub =
['-----BEGIN PGP PUBLIC KEY BLOCK-----', ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: Encryption Desktop 10.3.0 (Build 9307)', 'Version: Encryption Desktop 10.3.0 (Build 9307)',
'', '',
@ -342,7 +342,7 @@ var pgp_desktop_pub =
'=dVeR', '=dVeR',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var pgp_desktop_priv = var pgp_desktop_priv =
['-----BEGIN PGP PRIVATE KEY BLOCK-----', ['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: Encryption Desktop 10.3.0 (Build 9307)', 'Version: Encryption Desktop 10.3.0 (Build 9307)',
'', '',
@ -536,6 +536,57 @@ var pgp_desktop_priv =
done(); done();
}); });
var multi_uid_key =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v1',
'',
'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',
'ljIYQms3pCv1ja6bLlNKpPII',
'=qxBI',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var wrong_key =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: OpenPGP.js v0.9.0',
'',
'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5',
'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg',
'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM',
'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq',
'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1',
'=6XMW',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
it('Parsing armored text with two keys', function(done) { it('Parsing armored text with two keys', function(done) {
var pubKeys = openpgp.key.readArmored(twoKeys); var pubKeys = openpgp.key.readArmored(twoKeys);
expect(pubKeys).to.exist; expect(pubKeys).to.exist;
@ -890,6 +941,80 @@ var pgp_desktop_priv =
done(); done();
}).catch(done); }).catch(done);
}); });
it('Sign and verify key - primary user', function(done) {
var key = openpgp.key.readArmored(pub_sig_test).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
privateKey.decrypt('hello world');
key = key.signPrimaryUser([privateKey]);
var signatures = key.verifyPrimaryUser([privateKey]);
expect(signatures.length).to.equal(2);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.true;
done();
});
it('Sign key and verify with wrong key - primary user', function(done) {
var key = openpgp.key.readArmored(pub_sig_test).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
var wrongKey = openpgp.key.readArmored(wrong_key).keys[0];
privateKey.decrypt('hello world');
key = key.signPrimaryUser([privateKey]);
var signatures = key.verifyPrimaryUser([wrongKey]);
expect(signatures.length).to.equal(2);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.null;
done();
});
it('Sign and verify key - all users', function(done) {
var key = openpgp.key.readArmored(multi_uid_key).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
privateKey.decrypt('hello world');
key = key.signAllUsers([privateKey]);
var signatures = key.verifyAllUsers([privateKey]);
expect(signatures.length).to.equal(4);
expect(signatures[0].userid).to.equal(key.users[0].userId.userid);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].userid).to.equal(key.users[0].userId.userid);
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.true;
expect(signatures[2].userid).to.equal(key.users[1].userId.userid);
expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[2].valid).to.be.null;
expect(signatures[3].userid).to.equal(key.users[1].userId.userid);
expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[3].valid).to.be.true;
done();
});
it('Sign key and verify with wrong key - all users', function(done) {
var key = openpgp.key.readArmored(multi_uid_key).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
var wrongKey = openpgp.key.readArmored(wrong_key).keys[0];
privateKey.decrypt('hello world');
key = key.signAllUsers([privateKey]);
var signatures = key.verifyAllUsers([wrongKey]);
expect(signatures.length).to.equal(4);
expect(signatures[0].userid).to.equal(key.users[0].userId.userid);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].userid).to.equal(key.users[0].userId.userid);
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.null;
expect(signatures[2].userid).to.equal(key.users[1].userId.userid);
expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[2].valid).to.be.null;
expect(signatures[3].userid).to.equal(key.users[1].userId.userid);
expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[3].valid).to.be.null;
done();
});
it('Reformat key without passphrase', function(done) { it('Reformat key without passphrase', function(done) {
var userId1 = 'test1 <a@b.com>'; var userId1 = 'test1 <a@b.com>';
var userId2 = 'test2 <b@a.com>'; var userId2 = 'test2 <b@a.com>';

View File

@ -654,8 +654,8 @@ describe("Signature", function() {
done(); done();
}); });
}); });
it('Verify signed public key', function(done) { it('Verify signed key', function(done) {
var signedArmor = [ var signedArmor = [
'-----BEGIN PGP PUBLIC KEY BLOCK-----', '-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v1', 'Version: GnuPG v1',
@ -682,14 +682,15 @@ describe("Signature", function() {
'=fvK7', '=fvK7',
'-----END PGP PUBLIC KEY BLOCK-----' '-----END PGP PUBLIC KEY BLOCK-----'
].join('\n'); ].join('\n');
var sig_key = openpgp.key.readArmored(signedArmor).keys[0]; var signedKey = openpgp.key.readArmored(signedArmor).keys[0];
var pub_key = openpgp.key.readArmored(priv_key_arm1).keys[0].toPublic(); var signerKey = openpgp.key.readArmored(priv_key_arm1).keys[0];
openpgp.verifyPublicKey({ publicKey: sig_key, publicKeys: [pub_key] }).then(function(verified) { var signatures = signedKey.verifyPrimaryUser([signerKey]);
expect(verified.signatures[0].valid).to.be.true; expect(signatures[0].valid).to.be.null;
expect(verified.signatures[0].keyid.toHex()).to.equal(pub_key.primaryKey.getKeyId().toHex()); expect(signatures[0].keyid.toHex()).to.equal(signedKey.primaryKey.getKeyId().toHex());
done(); expect(signatures[1].valid).to.be.true;
}); expect(signatures[1].keyid.toHex()).to.equal(signerKey.primaryKey.getKeyId().toHex());
done();
}); });
}); });