Refactoring Key class to use structured data. Transform key from-to packetlist.

Add User and SubKey class. Verification methods for key components.
This commit is contained in:
Thomas Oberndörfer 2013-12-20 18:19:11 +01:00
parent 1ca90a980c
commit b27b01905b
8 changed files with 1213 additions and 208 deletions

File diff suppressed because one or more lines are too long

View File

@ -255,6 +255,14 @@ var enums = {
shared_private_key: 128
},
keyStatus: {
invalid: 0,
expired: 1,
revoked: 2,
valid: 3,
no_self_cert: 4
},
armor: {
multipart_section: 0,
multipart_last: 1,

View File

@ -31,21 +31,112 @@ function Key(packetlist) {
if (!(this instanceof Key)) {
return new Key(packetlist);
}
this.packets = packetlist || new packet.list();
// same data as in packetlist but in structured form
this.primaryKey = null;
this.revocationSignature = null;
this.directSignatures = null;
this.users = null;
this.subKeys = null;
this.packetlist2structure(packetlist);
if (!this.primaryKey || !this.users) {
throw new Error('Invalid key: need at least key and user ID packet');
}
}
/**
* Transforms packetlist to structured key data
* @param {packetlist} packetlist The packets that form a key
*/
Key.prototype.packetlist2structure = function(packetlist) {
var user, primaryKeyId, subKey;
for (var i = 0; i < packetlist.length; i++) {
switch (packetlist[i].tag) {
case enums.packet.public_key:
case enums.packet.secret_key:
this.primaryKey = packetlist[i];
primaryKeyId = this.primaryKey.getKeyId();
break;
case enums.packet.userid:
case enums.packet.user_attribute:
user = new User(packetlist[i]);
if (!this.users) this.users = [];
this.users.push(user);
break;
case enums.packet.public_subkey:
case enums.packet.secret_subkey:
user = null;
if (!this.subKeys) this.subKeys = [];
subKey = new SubKey(packetlist[i]);
this.subKeys.push(subKey);
break;
case enums.packet.signature:
switch (packetlist[i].signatureType) {
case enums.signature.cert_generic:
case enums.signature.cert_persona:
case enums.signature.cert_casual:
case enums.signature.cert_positive:
if (packetlist[i].issuerKeyId.equals(primaryKeyId)) {
if (!user.selfCertifications) user.selfCertifications = [];
user.selfCertifications.push(packetlist[i]);
} else {
if (!user.otherCertifications) user.otherCertifications = [];
user.otherCertifications.push(packetlist[i]);
}
break;
case enums.signature.cert_revocation:
if (user) {
if (!user.revocationCertifications) user.revocationCertifications = [];
user.revocationCertifications.push(packetlist[i]);
} else {
if (!this.directSignatures) this.directSignatures = [];
this.directSignatures.push(packetlist[i]);
}
break;
case enums.signature.key:
if (!this.directSignatures) this.directSignatures = [];
this.directSignatures.push(packetlist[i]);
break;
case enums.signature.subkey_binding:
subKey.bindingSignature = packetlist[i];
break;
case enums.signature.key_revocation:
this.revocationSignature = packetlist[i];
break;
case enums.signature.subkey_revocation:
subKey.revocationSignature = packetlist[i];
break;
}
break;
}
}
};
/**
* Transforms structured key data to packetlist
* @return {packetlist} The packets that form a key
*/
Key.prototype.toPacketlist = function() {
var packetlist = new packet.list();
packetlist.push(this.primaryKey);
packetlist.push(this.revocationSignature);
packetlist.concat(this.directSignatures);
for (var i = 0; i < this.users.length; i++) {
packetlist.concat(this.users[i].toPacketlist());
}
if (this.subKeys) {
for (var i = 0; i < this.subKeys.length; i++) {
packetlist.concat(this.subKeys[i].toPacketlist());
}
}
return packetlist;
};
/**
* Returns the primary key packet (secret or public)
* @returns {packet_secret_key|packet_public_key|null}
*/
Key.prototype.getKeyPacket = function() {
for (var i = 0; i < this.packets.length; i++) {
if (this.packets[i].tag == enums.packet.public_key ||
this.packets[i].tag == enums.packet.secret_key) {
return this.packets[i];
}
}
return null;
return this.primaryKey;
};
/**
@ -53,17 +144,13 @@ Key.prototype.getKeyPacket = function() {
* @returns {[public_subkey|secret_subkey]}
*/
Key.prototype.getSubkeyPackets = function() {
var subkeys = [];
for (var i = 0; i < this.packets.length; i++) {
if (this.packets[i].tag == enums.packet.public_subkey ||
this.packets[i].tag == enums.packet.secret_subkey) {
subkeys.push(this.packets[i]);
var subKeys = [];
if (this.subKeys) {
for (var i = 0; i < this.subKeys.length; i++) {
subKeys.push(this.subKeys[i].subKey);
}
}
return subkeys;
return subKeys;
};
/**
@ -105,8 +192,11 @@ function findKey(keys, keyIds) {
* @return {public_subkey|packet_public_key|null}
*/
Key.prototype.getPublicKeyPacket = function(keyIds) {
var keys = this.packets.filterByTag(enums.packet.public_key, enums.packet.public_subkey);
return findKey(keys, keyIds);
if (this.primaryKey.tag == enums.packet.public_key) {
return findKey(this.getAllKeyPackets(), keyIds);
} else {
return null;
}
};
/**
@ -115,8 +205,11 @@ Key.prototype.getPublicKeyPacket = function(keyIds) {
* @return {secret_subkey|packet_secret_key|null}
*/
Key.prototype.getPrivateKeyPacket = function(keyIds) {
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
return findKey(keys, keyIds);
if (this.primaryKey.tag == enums.packet.secret_key) {
return findKey(this.getAllKeyPackets(), keyIds);
} else {
return null;
}
};
/**
@ -125,9 +218,10 @@ Key.prototype.getPrivateKeyPacket = function(keyIds) {
*/
Key.prototype.getUserIds = function() {
var userids = [];
var useridPackets = this.packets.filterByTag(enums.packet.userid);
for (var i = 0; i < useridPackets.length; i++) {
userids.push(useridPackets[i].write());
for (var i = 0; i < this.users.length; i++) {
if (this.users[i].userId) {
userids.push(this.users[i].userId.write());
}
}
return userids;
};
@ -137,8 +231,7 @@ Key.prototype.getUserIds = function() {
* @return {Boolean}
*/
Key.prototype.isPublic = function() {
var publicKeyPackets = this.packets.filterByTag(enums.packet.public_key);
return publicKeyPackets.length ? true : false;
return this.primaryKey.tag == enums.packet.public_key;
};
/**
@ -146,32 +239,32 @@ Key.prototype.isPublic = function() {
* @return {Boolean}
*/
Key.prototype.isPrivate = function() {
var privateKeyPackets = this.packets.filterByTag(enums.packet.private_key);
return privateKeyPackets.length ? true : false;
return this.primaryKey.tag == enums.packet.secret_key;
};
/**
* Returns key as public key
* @return {key} public key
* Returns key as public key (shallow copy)
* @return {key} new public Key
*/
Key.prototype.toPublic = function() {
var packetlist = new packet.list();
for (var i = 0; i < this.packets.length; i++) {
switch (this.packets[i].tag) {
var keyPackets = this.toPacketlist();
for (var i = 0; i < keyPackets.length; i++) {
switch (keyPackets[i].tag) {
case enums.packet.secret_key:
var bytes = this.packets[i].writePublicKey();
var bytes = keyPackets[i].writePublicKey();
var pubKeyPacket = new packet.public_key();
pubKeyPacket.read(bytes);
packetlist.push(pubKeyPacket);
break;
case enums.packet.secret_subkey:
var bytes = this.packets[i].writePublicKey();
var bytes = keyPackets[i].writePublicKey();
var pubSubkeyPacket = new packet.public_subkey();
pubSubkeyPacket.read(bytes);
packetlist.push(pubSubkeyPacket);
break;
default:
packetlist.push(this.packets[i]);
packetlist.push(keyPackets[i]);
}
}
return new Key(packetlist);
@ -183,29 +276,29 @@ Key.prototype.toPublic = function() {
*/
Key.prototype.armor = function() {
var type = this.isPublic() ? enums.armor.public_key : enums.armor.private_key;
return armor.encode(type, this.packets.write());
return armor.encode(type, this.toPacketlist().write());
};
/**
* Returns first key packet that is available for signing
* @return {public_subkey|secret_subkey|packet_secret_key|packet_public_key|null}
* @return {secret_subkey|packet_secret_key|null} key packet or null if no signing key has been found
*/
Key.prototype.getSigningKeyPacket = function() {
var signing = [ enums.publicKey.rsa_encrypt_sign, enums.publicKey.rsa_sign, enums.publicKey.dsa];
signing = signing.map(function(s) {
return enums.read(enums.publicKey, s);
});
var keys = this.getAllKeyPackets();
for (var i = 0; i < keys.length; i++) {
if (signing.indexOf(keys[i].algorithm) !== -1) {
return keys[i];
if (this.isPublic()) {
throw new Error('Need private key for signing');
}
var primaryUser = this.getPrimaryUser();
if (primaryUser &&
isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate)) {
return this.primaryKey;
}
if (this.subKeys) {
for (var i = 0; i < this.subKeys.length; i++) {
if (this.subKeys[i].isValidSigningKey(this.primaryKey)) {
return this.subKeys[i].subKey;
}
}
}
return null;
};
@ -213,12 +306,30 @@ Key.prototype.getSigningKeyPacket = function() {
* Returns preferred signature hash algorithm of this key
* @return {String}
*/
Key.prototype.getPreferredSignatureHashAlgorithm = function() {
//TODO implement: https://tools.ietf.org/html/rfc4880#section-5.2.3.8
//separate private key preference from digest preferences
Key.prototype.getPreferredHashAlgorithm = function() {
var primaryUser = this.getPrimaryUser();
if (primaryUser && primaryUser.selfCertificate.preferredHashAlgorithms) {
return primaryUser.selfCertificate.preferredHashAlgorithms[0];
}
return config.prefer_hash_algorithm;
};
function isValidEncryptionKeyPacket(keyPacket, signature) {
return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.dsa) &&
keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_sign) &&
((signature.keyFlags & enums.keyFlags.encrypt_communication) !== 0 ||
(signature.keyFlags & enums.keyFlags.encrypt_storage) !== 0 ||
!signature.keyFlags);
};
function isValidSigningKeyPacket(keyPacket, signature) {
return (keyPacket.algorithm == enums.read(enums.publicKey, enums.publicKey.dsa) ||
keyPacket.algorithm == enums.read(enums.publicKey, enums.publicKey.rsa_sign) ||
keyPacket.algorithm == enums.read(enums.publicKey, enums.publicKey.rsa_encrypt_sign)) &&
((signature.keyFlags & enums.keyFlags.sign_data) !== 0 ||
!signature.keyFlags);
};
/**
* Returns the first valid encryption key packet for this key
* @returns {public_subkey|secret_subkey|packet_secret_key|packet_public_key|null} key packet or null if no encryption key has been found
@ -226,25 +337,18 @@ Key.prototype.getPreferredSignatureHashAlgorithm = function() {
Key.prototype.getEncryptionKeyPacket = function() {
// V4: by convention subkeys are prefered for encryption service
// V3: keys MUST NOT have subkeys
var isValidEncryptionKey = function(key) {
//TODO evaluate key flags: http://tools.ietf.org/html/rfc4880#section-5.2.3.21
return key.algorithm != enums.read(enums.publicKey, enums.publicKey.dsa) && key.algorithm != enums.read(enums.publicKey,
enums.publicKey.rsa_sign);
//TODO verify key
//&& keys.verifyKey()
};
var subkeys = this.getSubkeyPackets();
for (var j = 0; j < subkeys.length; j++) {
if (isValidEncryptionKey(subkeys[j])) {
return subkeys[j];
if (this.subKeys) {
for (var i = 0; i < this.subKeys.length; i++) {
if (this.subKeys[i].isValidEncryptionKey(this.primaryKey)) {
return this.subKeys[i].subKey;
}
}
}
// if no valid subkey for encryption, use primary key
var primaryKey = this.getKeyPacket();
if (isValidEncryptionKey(primaryKey)) {
return primaryKey;
// if no valid subkey for encryption, evaluate primary key
var primaryUser = this.getPrimaryUser();
if (primaryUser &&
isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate)) {
return this.primaryKey;
}
return null;
};
@ -255,10 +359,14 @@ Key.prototype.getEncryptionKeyPacket = function() {
* @return {Boolean} true if all key and subkey packets decrypted successfully
*/
Key.prototype.decrypt = function(passphrase) {
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
for (var i = 0; i < keys.length; i++) {
var success = keys[i].decrypt(passphrase);
if (!success) return false;
if (this.isPrivate()) {
var keys = this.getAllKeyPackets();
for (var i = 0; i < keys.length; i++) {
var success = keys[i].decrypt(passphrase);
if (!success) return false;
}
} else {
throw new Error("Nothing to decrypt in a public key");
}
return true;
};
@ -270,29 +378,286 @@ Key.prototype.decrypt = function(passphrase) {
* @return {Boolean} true if all key packets decrypted successfully
*/
Key.prototype.decryptKeyPacket = function(keyIds, passphrase) {
//TODO return value
var keys = this.packets.filterByTag(enums.packet.secret_key, enums.packet.secret_subkey);
for (var i = 0; i < keys.length; i++) {
var keyId = keys[i].getKeyId();
for (var j = 0; j < keyIds.length; j++) {
if (keyId.equals(keyIds[j])) {
var success = keys[i].decrypt(passphrase);
if (!success) return false;
if (this.isPrivate()) {
var keys = this.getAllKeyPackets();
for (var i = 0; i < keys.length; i++) {
var keyId = keys[i].getKeyId();
for (var j = 0; j < keyIds.length; j++) {
if (keyId.equals(keyIds[j])) {
var success = keys[i].decrypt(passphrase);
if (!success) return false;
}
}
}
} else {
throw new Error("Nothing to decrypt in a public key");
}
return true;
};
// TODO
Key.prototype.verify = function() {
/**
* Verify primary key. Checks for revocation signatures, expiration time
* and valid self signature
* @return {enums.keyStatus} The status of the primary key
*/
Key.prototype.verifyPrimaryKey = function() {
// check revocation signature
if (this.revocationSignature && !this.revocationSignature.isExpired() &&
(this.revocationSignature.verified ||
this.revocationSignature.verify(this.primaryKey, {key: this.primaryKey}))) {
return enums.keyStatus.revoked;
}
// check V3 expiration time
if (this.primaryKey.version == 3 && this.primaryKey.expirationTimeV3 !== 0 &&
Date.now() > (this.primaryKey.created.getTime() + this.primaryKey.expirationTimeV3*24*3600*1000)) {
return enums.keyStatus.expired;
}
// check for at least one self signature. Self signature of user ID not mandatory
// See http://tools.ietf.org/html/rfc4880#section-11.1
var selfSigned = false;
for (var i = 0; i < this.users.length; i++) {
if (this.users[i].userId && this.users[i].selfCertifications) {
selfSigned = true;
}
}
if (!selfSigned) {
return enums.keyStatus.no_self_cert;
}
// check for valid self signature
var primaryUser = this.getPrimaryUser();
if (!primaryUser) {
return enums.keyStatus.invalid;
}
// check V4 expiration time
if (this.primaryKey.version == 4 && primaryUser.selfCertificate.keyNeverExpires === false &&
Date.now() > (primaryUser.selfCertificate.created.getTime() + primaryUser.selfCertificate.keyExpirationTime*1000)) {
return enums.keyStatus.expired;
}
return enums.keyStatus.valid;
};
/**
* Returns primary user and most significant (latest valid) self signature
* - if multiple users are marked as primary users returns the one with the latest self signature
* - if no primary user is found returns the user with the latest self signature
* @return {{user: [User], selfCertificate: [packet_signature]}} The primary user and the self signature
*/
Key.prototype.getPrimaryUser = function() {
var user = null;
var userSelfCert;
for (var i = 0; i < this.users.length; i++) {
if (!this.users[i].userId) {
continue;
}
var selfCert = this.users[i].getValidSelfCertificate(this.primaryKey);
if (!selfCert) {
continue;
}
if (!user ||
!userSelfCert.isPrimaryUserID && selfCert.isPrimaryUserID ||
userSelfCert.created < selfCert.created) {
user = this.users[i];
userSelfCert = selfCert;
}
}
return user ? {user: user, selfCertificate: userSelfCert} : null;
}
// TODO
Key.prototype.revoke = function() {
};
/**
* @class
* @classdesc Class that represents an user ID or attribute packet and the relevant signatures.
*/
function User(userPacket) {
if (!(this instanceof User)) {
return new User(userPacket);
}
this.userId = userPacket.tag == enums.packet.userid ? userPacket : null;
this.userAttribute = userPacket.tag == enums.packet.user_attribute ? userPacket : null
this.selfCertifications = null;
this.otherCertifications = null;
this.revocationCertifications = null;
}
/**
* Transforms structured user data to packetlist
* @return {packetlist}
*/
User.prototype.toPacketlist = function() {
var packetlist = new packet.list();
packetlist.push(this.userId || this.userAttribute);
packetlist.concat(this.revocationCertifications);
packetlist.concat(this.selfCertifications);
packetlist.concat(this.otherCertifications);
return packetlist;
};
/**
* Checks if a self signature of the user is revoked
* @param {packet_signature} certificate
* @param {packet_secret_key|packet_public_key} primaryKey The primary key packet
* @return {Boolean} True if the certificate is revoked
*/
User.prototype.isRevoked = function(certificate, primaryKey) {
if (this.revocationCertifications) {
var that = this;
return this.revocationCertifications.some(function(revCert) {
return revCert.issuerKeyId.equals(certificate.issuerKeyId) &&
!revCert.isExpired() &&
(revCert.verified ||
revCert.verify(primaryKey, {userid: that.userId || that.userAttribute, key: primaryKey}));
});
} else {
return false;
}
};
/**
* Returns the most significant (latest valid) self signature of the user
* @param {packet_secret_key|packet_public_key} primaryKey The primary key packet
* @return {packet_signature} The self signature
*/
User.prototype.getValidSelfCertificate = function(primaryKey) {
if (!this.selfCertifications) {
return null;
}
var validCert = [];
for (var i = 0; i < this.selfCertifications.length; i++) {
if (this.isRevoked(this.selfCertifications[i], primaryKey)) {
continue;
}
if (!this.selfCertifications[i].isExpired() &&
(this.selfCertifications[i].verified ||
this.selfCertifications[i].verify(primaryKey, {userid: this.userId || this.userAttribute, key: primaryKey}))) {
validCert.push(this.selfCertifications[i]);
}
}
// most recent first
validCert = validCert.sort(function(a, b) {
a = a.created;
b = b.created;
return a>b ? -1 : a<b ? 1 : 0;
});
return validCert[0];
};
/**
* Verify User. Checks for existence of self signatures, revocation signatures
* and validity of self signature
* @param {packet_secret_key|packet_public_key} primaryKey The primary key packet
* @return {enums.keyStatus} status of user
*/
User.prototype.verify = function(primaryKey) {
if (!this.selfCertifications) {
return enums.keyStatus.no_self_cert;
}
var status;
for (var i = 0; i < this.selfCertifications.length; i++) {
if (this.isRevoked(this.selfCertifications[i], primaryKey)) {
status = enums.keyStatus.revoked;
continue;
}
if (!(this.selfCertifications[i].verified ||
this.selfCertifications[i].verify(primaryKey, {userid: this.userId || this.userAttribute, key: primaryKey}))) {
status = enums.keyStatus.invalid;
continue;
}
if (this.selfCertifications[i].isExpired()) {
status = enums.keyStatus.expired;
continue;
}
status = enums.keyStatus.valid;
break;
}
return status;
};
/**
* @class
* @classdesc Class that represents a subkey packet and the relevant signatures.
*/
function SubKey(subKeyPacket) {
if (!(this instanceof SubKey)) {
return new SubKey(subKeyPacket);
}
this.subKey = subKeyPacket;
this.bindingSignature = null;
this.revocationSignature = null;
}
/**
* Transforms structured subkey data to packetlist
* @return {packetlist}
*/
SubKey.prototype.toPacketlist = function() {
var packetlist = new packet.list();
packetlist.push(this.subKey);
packetlist.push(this.revocationSignature);
packetlist.push(this.bindingSignature);
return packetlist;
};
/**
* Returns true if the subkey can be used for encryption
* @param {packet_secret_key|packet_public_key} primaryKey The primary key packet
* @return {Boolean}
*/
SubKey.prototype.isValidEncryptionKey = function(primaryKey) {
return this.verify(primaryKey) == enums.keyStatus.valid &&
isValidEncryptionKeyPacket(this.subKey, this.bindingSignature);
};
/**
* Returns true if the subkey can be used for signing of data
* @param {packet_secret_key|packet_public_key} primaryKey The primary key packet
* @return {Boolean}
*/
SubKey.prototype.isValidSigningKey = function(primaryKey) {
return this.verify(primaryKey) == enums.keyStatus.valid &&
isValidSigningKeyPacket(this.subKey, this.bindingSignature);
};
/**
* Verify subkey. Checks for revocation signatures, expiration time
* and valid binding signature
* @return {enums.keyStatus} The status of the subkey
*/
SubKey.prototype.verify = function(primaryKey) {
// check subkey revocation signature
if (this.revocationSignature && !this.revocationSignature.isExpired() &&
(this.revocationSignature.verified ||
this.revocationSignature.verify(primaryKey, {key: this.subKey}))) {
return enums.keyStatus.revoked;
}
// check V3 expiration time
if (this.subKey.version == 3 && this.subKey.expirationTimeV3 !== 0 &&
Date.now() > (this.subKey.created.getTime() + this.subKey.expirationTimeV3*24*3600*1000)) {
return enums.keyStatus.expired;
}
// check subkey binding signature
if (!this.bindingSignature) {
return enums.keyStatus.invalid;
}
if (this.bindingSignature.isExpired()) {
return enums.keyStatus.expired;
}
if (!(this.bindingSignature.verified ||
this.bindingSignature.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) {
return enums.keyStatus.invalid;
}
// check V4 expiration time
if (this.subKey.version == 4 &&
this.bindingSignature.keyNeverExpires === false &&
Date.now() > (this.subKey.created.getTime() + this.bindingSignature.keyExpirationTime*1000)) {
return enums.keyStatus.expired;
}
return enums.keyStatus.valid;
};
/**
* Reads an OpenPGP armored text and returns one or multiple key objects
* @param {String} armoredText text to be parsed

View File

@ -146,6 +146,8 @@ Message.prototype.encrypt = function(keys) {
pkESKeyPacket.sessionKeyAlgorithm = enums.read(enums.symmetric, config.encryption_cipher);
pkESKeyPacket.encrypt(encryptionKeyPacket);
packetlist.push(pkESKeyPacket);
} else {
throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex());
}
});
var symEncryptedPacket;
@ -183,6 +185,9 @@ Message.prototype.sign = function(privateKeys) {
//TODO get preferred hashg algo from key signature
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
var signingKeyPacket = privateKeys[i].getSigningKeyPacket();
if (!signingKeyPacket) {
throw new Error('Could not find valid key packet for signing in key ' + privateKeys[i].primaryKey.getKeyId().toHex());
}
onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm;
onePassSig.signingKeyId = signingKeyPacket.getKeyId();
packetlist.push(onePassSig);

View File

@ -56,6 +56,8 @@ module.exports = function packetlist() {
* writing to packetlist[i] directly will result in an error.
*/
this.push = function(packet) {
if (!packet) return;
packet.packets = packet.packets || new packetlist();
this[this.length] = packet;
@ -154,4 +156,15 @@ module.exports = function packetlist() {
return part;
}
/**
* Concatenates packetlist or array of packets
*/
this.concat = function(packetlist) {
if (packetlist) {
for (var i = 0; i < packetlist.length; i++) {
this.push(packetlist[i]);
}
}
}
}

View File

@ -41,6 +41,7 @@ module.exports = function packet_public_key() {
/** Public key algorithm
* @type {openpgp.publickey} */
this.algorithm = 'rsa_sign';
// time in days (V3 only)
this.expirationTimeV3 = 0;
@ -139,7 +140,11 @@ module.exports = function packet_public_key() {
*/
this.getKeyId = function() {
var keyid = new type_keyid();
keyid.read(this.getFingerprint().substr(12, 8));
if (this.version == 4) {
keyid.read(this.getFingerprint().substr(12, 8));
} else if (this.version == 3) {
keyid.read(this.mpi[0].write().substr(-8));
}
return keyid;
}
@ -148,8 +153,17 @@ module.exports = function packet_public_key() {
* @return {String} A string containing the fingerprint
*/
this.getFingerprint = function() {
var toHash = this.writeOld();
return crypto.hash.sha1(toHash, toHash.length);
var toHash = '';
if (this.version == 4) {
toHash = this.writeOld();
return crypto.hash.sha1(toHash);
} else if (this.version == 3) {
var mpicount = crypto.getPublicMpiCount(this.algorithm);
for (var i = 0; i < mpicount; i++) {
toHash += this.mpi[i].toBytes();
}
return crypto.hash.md5(toHash)
}
}
}

View File

@ -3,7 +3,7 @@ var unit = require('../unit.js');
unit.register("Key testing", function() {
var openpgp = require('openpgp');
var twoKeys =
var twoKeys =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
@ -53,6 +53,172 @@ unit.register("Key testing", function() {
'=w6wd',
'-----END PGP PUBLIC KEY BLOCK-----'].join("\n");
var pub_revoked =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'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',
'pMiJM+NJAQ==',
'=ok+o',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var pub_v3 =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: SKS 1.1.3',
'',
'mQENAy9J/w4AAAEIALBDDD4vWqG/Jg59ghhMYAa+E7ECCTv2At8hxsM5cMP8P9sMLjs+GMfD',
'IdQSOqlQXbunYADvM1l/h2fOuUMoYFIIGaUsO5Daxvd9uWceM4DVzhXMeJZb9wc5jEJEF21+',
'qidKj5OGsMyTrg++mn4Gh/aFXvvy3N3KWaQpPfNi3NRZUpNLz0IlfbXVBQGD6reLoxPptJun',
'NqpClyRiesgq8HCscmB2oQo+b9KzSSgzU9qQJA4SljMYVmJ2sDE/sjREI8iKL8lIgUMhJG9q',
'NggWjuxFTpVcGKkuQFJIvdL+UhTVvEBuqw6n4cmFAzfZ/AInJM032qLtsaIf5begFKI3up0A',
'BRGJARUDBSAxm7HC5begFKI3up0BAbdDB/0TOcI0ec+OPxC5RTZAltgIgyUc0yOjHoTD/yBh',
'WjZdQ9YVrLGMWTW4fjhm4rFnppVZKS/N71bwI76SnN9zO4pPfx86aQPR7StmSLJxB+cfh2GL',
'gudJoG9ifhJWdNYMUD/yhA0TpJkdHMD5yTDE5Ce/PqKLviiX9C5MPW0AT1MDvafQlzeUXfb5',
'1a71vQNPw7W1NBAVZRwztm7TNUaxWMFuOmUtOJpq4F/qDQTIHW2zGPJvl47rpf6JSiyIyU70',
'l0deiQcZOXPC80tgInhNoBrz3zbEXhXRJo1fHkr2YSLclpJaoUOHsPxoyrNB28ASL5ZknPwI',
'Zx3+cFxaGpRprfSdtCFKb2huIEEuIFBlcnJ5IDxwZXJyeUBwaG9lbml4Lm5ldD6JARUDBRAv',
'Sf8k5begFKI3up0BAcbGB/0eLod2qrQxoE2/RUWQtqklOPUj/p/ZTmvZm8BgsdIflb0AMeey',
'9o8AbxyAgA3pcrcCjcye79M1Ma2trEvRksvs8hViuq3BXXjDbjPZi3wTtKSvbAC022OV52Sb',
'8/sgiTGp7xC8QMqS8w4ZeKoxJGh1TVMYrevUA8a2Rr5aDqrR3EA4rifSHwkVjJWOPF69xiKt',
'IVA0LcYJvGsPOQCf2ag+nOcnDrF4dvcmg6XZ/RyLepve+1qkhXsA/oq+yHoaqWfe+bwgssk/',
'qw1aEUk7Di8x7vY+cfjvWaazcYGw8kkIwSSqqIq0pkKFz2xDDfSaDJl6OW/2GUK0wDpJmYZo',
'PN40iJUDBRAvSgDsU5OkROGu2G8BAeUqBACbC45t4+wYxWCxxp81pkFRb8RWBvEvbXI+Spwd',
'4NcKs8jc5OVC8V02yiq4KbKFDRxdw2OWpUCSRAJe1gjsfFrZ+2RivpKk06kbAYthES03MjXg',
'cfcV3z2d7IWanJzdcOlzsHzPe1+RoUAaqBjvcqPRCGRlk0ogkYHyWYxElc6574iVAwUQL9iL',
'CXr7ES8bepftAQGPywP/d9GSpEmS7LLIqazl4rgN1nkXN5KqduiH8Whu3xcBrdOAn7IYnGTp',
'O+Ag4qwKKH+y/ke9CeZL6AnrU9c0pux150dHsDeHtpTPyInkjgKI7BofprydvpiFNd0nlAi4',
'J4SAEYr3q92Qn/IiKpnLgo6Ls/GFb7q6y1O/2LL8PC2zrYU=',
'=eoGb',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var pub_sig_test =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'mQENBFKgqXUBCADC4c6sSBnBU+15Y32/a8IXqO2WxKxSHj7I5hv1OdSTmSZes7nZ',
'5V96qsk0k5/ka3C2In+GfTKfuAJ0oVkTZVi5tHP9D+PcZngrIFX56OZ2P5PtTU7U',
'jh0C78JwCVnv6Eg57JnIMwdbL3ZLqmogLhw5q15Hie5btCIQnuuKfrwxnsox4i3q',
'dYCHYB1HBGzpvflS07r3Y3IRFJaP8hUhx7PsvpD1s+9DU8AuMNZTXAqRI/bms5hC',
'BpVejXLj/vlNeQil99MoI7s14L+dHogYuUOUbsXim5EyIFfF/1v+o5l0wmueWrE8',
'mYQfj5ZvURlGVFah9ECaW9/ResCyJ1Yh975xABEBAAG0I1NpZ25hdHVyZSBUZXN0',
'IDxzaWduYXR1cmVAdGVzdC5jb20+iQE8BBMBAgAmAhsDBwsJCAcDAgEGFQgCCQoL',
'BBYCAwECHgECF4AFAlKgq80CGQEACgkQwHbmNNMrSY3KKQf/UGnuc6LbVyhkFQKo',
'USTVDFg/42CVmIGOG+aZBo0VZuzNYARwDKyoZ5okKqZi5VSfdDaBXuW4VIYepvux',
'AV8eJV6GIsLRv/wJcKPABIXDIK1tdNetiYbd+2/Fb2/YqAX5wOKIxd3Ggzyx5X4F',
'WhA6fIBIXyShUWoadkX7S87z5hryhII9281rW2mOsLC5fy/SUQUWM1YmsZ1owvY9',
'q6W8xRnHDmY+Ko91xex7fikDLBofsWbTUc0/O/1o9miIZfp2nXLKQus2H1WdZVOe',
'H9zFiy54x7+zTov94SJE3xXppoQnIpeOTlFjTP2mjxm0VW1Dn9lGE3IFgWolpNPy',
'Rv6dnLQdU2Vjb25kIFVzZXIgPHNlY29uZEB1c2VyLmNvbT6IowQwAQIADQUCUrF1',
'hwYdIEh1cnoACgkQSmNhOk1uQJRVeQP9GQoLvan5FMYcPPY4a9dNlkvtheRXcoif',
'oYdQoEyy9zAFCqmg2pC6RrHaMwNINw534JDh2vgWQ0MU3ktMJjSvGBBHayQc6ov8',
'i4I6rUPBlYoSDKyFnhCCXWF56bHMGyEGJhcQLv1hrGPVv6PTKj3hyR+2n50Impwo',
'UrlFIwYZNyWJAS8EMAECABkFAlKgqqYSHSBUZXN0aW5nIHB1cnBvc2VzAAoJEMB2',
'5jTTK0mNvKAH/Rgu+I12Fb7S8axNwzp5m/jl1iscYbjgOrdUEI7bc2yo0KhGwYOV',
'U3Zj68Ogj6gkLkVwfhvJYZJgfYBG7nTxkC5/MTABQrAI5ZX89Hh9y0tLh2wKr5iK',
'MH6Mi9xxJmVJ+IiAKx/02f+sKWh4tv3TFNNxnp24LPHWz7RMd/o4m8itmzQxFmaZ',
'yEPd/CD6hYqSMP5Y7zMN4gTB+tHsawB9PWkrF/dW8bk3PtZanDlBMUSVrPH15bIZ',
'lFO1NKEN39WagmNe5wezKxWmHBcuISQHxCIX3Hf4dYyexndX25fMphF93YgQnNE+',
'zQeBJyNBKRpMXzGndELo5KFaA1YyC07GKKyJATkEEwECACMFAlKgqeYCGwMHCwkI',
'BwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRDAduY00ytJjagNCACGQMQPl6foIVcz',
'OzLf8npGihIjiIYARQz4+yg6ze9TG2hjIpWLiwGNJ0uEG22cFiN7OeFnUADFi131',
'oEtZzIXcBd0A1S87ooH+86YjpvLj5PMlviVKGsGmdqtWpQN5fII8brydNLwSHlLV',
'+JolvyMlA2Ao/sePopR0aSKIPfD108YIIiZztE4pHgDzE5G66zAw3zWn/dzLuGln',
'Mp4nrY8Rxb68MaZFhVq0A5QFzlOjQ/iDJWrPM6vy/U8TQxmaYGMjcEyEEil+3+OJ',
'OFqfB4byISOIxL9LqFVRndbgOw7ICi+qE2e7+9G2koCtEkjpPg3ZCF4mfZiaLT9p',
'QhoFS4yxiJwEEAECAAYFAlKgqhYACgkQSmNhOk1uQJSJ0gP9F5RRwGBbXD4Mg4gq',
'wcQYrzw9ZAapLKZ2vuco6gHknQAM1YuaOpKQu1rd6eFzKE4M11CLmoS/CalDhg9f',
'aN6fvTZG7lbUnSZKl/tgvG7qeneA919/b1RtMNDkHmRxvHysiyDYmkJYlmZlwXZB',
'5FBoRvv5b2oXfWLLEcNvUvbetuC5AQ0EUqCpdQEIAOMvycVLkIKm9EMbxFqGc019',
'yjCB3xiK+hF0PwdfWBXF8KskJ4hfybd19LdO6EGnKfAVGaeVEt6RtUJMsgfhqAhE',
'BwaeHLLfjXjd7PetBdzybh0u2kfaGDBQshdEuLcfqTqp4+R+ha1epdXAPDP+lb9E',
'5OXIOU2EWLSY+62fyGw3kvUSYNQKufDoKuq5vzltW1uYVq3aeA7e/yTqEoWSoRGo',
'25f/xaY6u6sYIyLpkZ6IX1n1BzLirfJSkJ8svNX+hNihCDshKJUDoMwAPcRdICkr',
'vFbrO3k24OylQA6dpQqHUWD9kVu8sEZH/eiHZ5YBo/hgwNH7UMaFSBAYQZrSZjcA',
'EQEAAYkBHwQoAQIACQUCUqCrcgIdAwAKCRDAduY00ytJjeO9B/9O/A6idEMy6cEG',
'PAYv0U/SJW0RcM54/Ptryg3jiros+qkLQD+Hp2q/xxpXKFPByGWkkGZnNIIxaA1j',
'SPvOJXrK728b/OXKB3IaMknKTB7gLGH4oA9/dmzHgbeqNWXYok5GSwPxLSUoeIrZ',
'j+6DkUz2ebDx1FO797eibeL1Dn15iyWh/l3QMT+1fLjJyVDnEtNhZibMlDPohVuS',
'suJfoKbQJkT6mRy4nDWsPLzFOt3VreJKXo9MMrrHV44XeOKo5nqCK3KsfCoeoqft',
'G7e/NP4DgcfkgNrU/XnBmR9ZVn9/o3EbDENniOVlNH2JaSQskspv5fv7k6dRWn4Q',
'NRhN5uMWiQEfBBgBAgAJBQJSoKl1AhsMAAoJEMB25jTTK0mNgaEIAKBkMGTSexox',
'zy6EWtSR+XfA+LxXjhwOgJWrRKvLUqssGbhQNRfY3gy7dEGiSKdnIV+d/xSLgm7i',
'zAyE4SjmDDOFRfxpyEsxhw2738OyEenEvO70A2J6RLj91Rfg9+vhT7WWyxBKdU1b',
'zM2ZORHCBUmbqjYAiLUbz0E589YwJR3a7osjCC8Lstf2C62ttAAAcKks2+wt4kUQ',
'Zm7WAUi1kG26VvOXVg9Tnj00mnBWmWlLPG7Qjudf2RBMJ/S8gg9OZWpBN29NEl6X',
'SU+DbbDHw3G97gRNE7QcHZPGyRtjbKv3nV2mJ8DMKrTzLuPUUcFqd7AlpdrFeDx/',
'8YM3DBS79eW5Ay4EUqCq0hEIAMIgqJsi3uTPzJw4b4c1Oue+O98jWaacrk7M57+y',
'Ol209yRUDyLgojs8ZmEZWdvjBG1hr15FIYI4BmusVXHCokVDGv8KNP4pvbf5wljM',
'2KG1FAxvxZ38/VXTDVH8dOERTf8JPLKlSLbF6rNqfePIL/1wto47b6oRCdawIC25',
'ft6XX18WlE+dgIefbYcmc0BOgHTHf8YY04IIg67904/RRE6yAWS42Ibx4h1J/haP',
'95SdthKg5J4HQ2lhudC2NJS3p+QBEieavSFuYTXgJwEeLs6gobwpZ7B0IWqAFCYH',
'rUOxA35MIg39TfZ4VAC+QZRjoDlp+NAM6tP9HfzsiTi5IecBAOEsOESNYr4ifBkw',
'StjpU6GuGydZf8MP/Ab/EHDAgYTlB/9VLpplMKMVCJLfYIOxEPkhYCfu30kxzsAL',
'dLmatluP33Zxv0YMnin6lY4Wii0G56ZovbuKDnGR1JcJT4Rr6ZUdd5dZzGqaP7Aj',
'J/thLQbIJdC1cGntd2V4lyMSly03ENXxYklzWm7S7xgS+uYsE36s1nctytBqxJYl',
'8e/7y+Zg4DxgrA2RM9+5R5neciiPGJIx16tBjOq/CM+R2d2+998YN7rKLxZ3w12t',
'RXHdGt2DZBVkH7bWxy8/2nTxwRmMiEcmeHfOsMz8BiEdgAU+E8YvuIYb2hL2Vdly',
'ie9boAnoy0fvVMOpcexw/DQHQcPba5OlfTQJwhTxnfaVd8jaxxJmCAC3PljfH9+/',
'MZrI2ApzC/xTP64t1ERJ7KP50eu53D+w2IpBOLJwnxMIxjtePRSdbF/0EEEL/0jF',
'GPSGNEw95/QZAyvbhkCTHuo2Sz3f0M2hCCzReo+t+et13h/7nQhEeNEJtOFFu/t+',
'nX9BrqNLCjH/6TCpQOkiZC3JQGzJxLU15P0LT+/20Rd8ysym0kPg2SrJCnyOrWwZ',
'wj+1hEHR9pfNtPIZx2LodtRF//Qo9KMSv9G6Tw3a60x7+18siHxTO9wzOxJxRnqN',
'LgguiQYq//N6LxF1MeQSxxmNr6kNalafp+pwRwNV4G2L7QWPYn3Axe5oEbjKfnoF',
'pwhalEs4PCnNiQGFBBgBAgAPBQJSoKrSAhsCBQkAAVGAAGoJEMB25jTTK0mNXyAE',
'GREIAAYFAlKgqtIACgkQqxSB4x5Bj2igHQD+JYra3ESBrVwurLq4n8mm4bq5Wujm',
'Da5k6Vf7F7ytbDAA/jb47AhgcDXQRcMw0ElTap5AP/JgtuglW/fO4cJxJfa8Yf0H',
'/i95k6w/MOn5CIwgpZyHc/F4bAVyaZmZ8gAT4lhn03ZDehFNrGJ0IhQH/QfqqNSp',
'NqG8h7GQIH6ovJlLIcolszIL3khI7LhMsIS6Yi8xpPPB9QcqNmjYkuYAtPE2KyL+',
'2yBt+f4AJ/VFnBygcUf+AC6YxBS3cYclGKUAE9j6StRGj3kPNJPF7M5dZi+1+1Tu',
'yJ5ucX3iq+3GKLq98Lv7SPUxIqkxuZbkZIoX99Wqz8of9BUV2wTDvVXB7TEPC5Ho',
'1y9Mb82aDrqPCq3DXvw5nz3EwxYqIXoKvLW5zsScBg9N3gmMeukXr2FCREKP5oht',
'yeSTTh8ZnzRiwuUH1t90E7w=',
'=e8xo',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var tests = [function() {
var pubKey = openpgp.key.readArmored(twoKeys);
@ -61,6 +227,31 @@ unit.register("Key testing", function() {
pubKey.keys[1].getKeyPacket().getKeyId().toHex() == 'dbf223e870534df4';
return new unit.result("Parsing armored text with two keys", verified);
},function() {
var pubKeyV4 = openpgp.key.readArmored(twoKeys).keys[0];
var pubKeyV3 = openpgp.key.readArmored(pub_v3).keys[0];
var verified = pubKeyV4.getKeyPacket().getKeyId().toHex() == '4a63613a4d6e4094' &&
openpgp.util.hexstrdump(pubKeyV4.getKeyPacket().getFingerprint()) == 'f470e50dcb1ad5f1e64e08644a63613a4d6e4094' &&
pubKeyV3.getKeyPacket().getKeyId().toHex() == 'e5b7a014a237ba9d' &&
openpgp.util.hexstrdump(pubKeyV3.getKeyPacket().getFingerprint()) == 'a44fcee620436a443bc4913640ab3e49';
return new unit.result("Testing key ID and fingerprint for V3 and V4 keys", verified);
},function() {
var pubKey = openpgp.key.readArmored(pub_sig_test).keys[0];
var packetlist = new openpgp.packet.list();
packetlist.read(openpgp.armor.decode(pub_sig_test).data);
var subkeys = pubKey.getSubkeyPackets();
var verified = subkeys.length == 2 &&
subkeys[0].getKeyId().equals(packetlist[8].getKeyId()) &&
subkeys[1].getKeyId().equals(packetlist[11].getKeyId());
return new unit.result("Testing key method getSubkeyPackets", verified);
},function() {
var pubKey = openpgp.key.readArmored(pub_sig_test).keys[0];
var status = pubKey.subKeys[0].verify(pubKey.primaryKey);
return new unit.result("Verify status of revoked subkey", status == openpgp.enums.keyStatus.revoked);
}];
var results = [];

View File

@ -252,17 +252,14 @@ var pub_v3 =
var tests = [function() {
var priv_key = openpgp.key.readArmored(priv_key_arm1).keys[0].packets;
var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0].packets;
var msg = openpgp.message.readArmored(msg_arm1).packets;
//TODO need both?
priv_key[0].decrypt("abcd");
priv_key[3].decrypt("abcd");
msg[0].decrypt(priv_key[3]);
msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
msg[1].packets[2].verify(pub_key[0], msg[1].packets[1]);
var priv_key = openpgp.key.readArmored(priv_key_arm1).keys[0];
var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0];
var msg = openpgp.message.readArmored(msg_arm1);
priv_key.decrypt("abcd");
var decrypted = openpgp.decryptAndVerifyMessage(priv_key, [pub_key], msg);
var verified = decrypted && decrypted.signatures[0].valid;
return new unit.result("Testing signature checking on CAST5-enciphered message",
msg[1].packets[2].verified === true);
verified);
}, function() {
@ -295,16 +292,15 @@ var pub_v3 =
'iY3UT9QkV9d0sMgyLkug',
'=GQsY',
'-----END PGP PRIVATE KEY BLOCK-----',
].join("\n")).keys[0].packets;
var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0].packets;
var msg = openpgp.message.readArmored(msg_arm1).packets;
].join("\n")).keys[0];
var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0];
var msg = openpgp.message.readArmored(msg_arm1);
priv_key_gnupg_ext[3].decrypt("abcd");
msg[0].decrypt(priv_key_gnupg_ext[3]);
msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
msg[1].packets[2].verify(pub_key[0], msg[1].packets[1]);
priv_key_gnupg_ext.subKeys[0].subKey.decrypt("abcd");
msg = msg.decrypt(priv_key_gnupg_ext);
var verified = msg.verify([pub_key]);
return new unit.result("Testing GnuPG stripped-key extensions",
msg[1].packets[2].verified === true);
verified[0].valid);
}, function() {
@ -320,10 +316,10 @@ var pub_v3 =
'=VH8F',
'-----END PGP MESSAGE-----'].join('\n');
var sMsg = openpgp.message.readArmored(signedArmor).packets;
var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0].packets;
sMsg[0].packets[2].verify(pub_key[3], sMsg[0].packets[1]);
return new unit.result("Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)", sMsg[0].packets[2].verified);
var sMsg = openpgp.message.readArmored(signedArmor);
var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0];
var verified = sMsg.verify([pub_key]);
return new unit.result("Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)", verified[0].valid);
}, function() {
var signedArmor =
@ -338,10 +334,10 @@ var pub_v3 =
'=pa6B',
'-----END PGP MESSAGE-----'].join('\n');
var sMsg = openpgp.message.readArmored(signedArmor).packets;
var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0].packets;
sMsg[0].packets[2].verify(pub_key[3], sMsg[0].packets[1]);
return new unit.result("Verify V3 signature. Hash: MD5. PK: RSA. Signature Type: 0x01 (text document)", sMsg[0].packets[2].verified);
var sMsg = openpgp.message.readArmored(signedArmor);
var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0];
var verified = sMsg.verify([pub_key]);
return new unit.result("Verify V3 signature. Hash: MD5. PK: RSA. Signature Type: 0x01 (text document)", verified[0].valid);
}, function() {
var msg_armor =
@ -502,21 +498,29 @@ var pub_v3 =
var pubKey = openpgp.key.readArmored(pub_revoked).keys[0];
var verified = pubKey.packets[1].verify(pubKey.packets[0], {key: pubKey.packets[0]});
var verified = pubKey.revocationSignature.verify(pubKey.primaryKey, {key: pubKey.primaryKey});
return new unit.result("Verify revocation signature", verified);
return new unit.result("Verify primary key revocation signature", verified);
}, function() {
var pubKey = openpgp.key.readArmored(pub_revoked).keys[0];
var verified = !pubKey.packets[4].keyNeverExpires && pubKey.packets[4].keyExpirationTime == 5*365*24*60*60;
var verified = pubKey.subKeys[0].revocationSignature.verify(pubKey.primaryKey, {key: pubKey.subKeys[0].subKey});
return new unit.result("Verify subkey revocation signature", verified);
}, function() {
var pubKey = openpgp.key.readArmored(pub_revoked).keys[0];
var verified = !pubKey.users[0].selfCertifications[0].keyNeverExpires &&
pubKey.users[0].selfCertifications[0].keyExpirationTime == 5*365*24*60*60;
return new unit.result("Verify key expiration date", verified);
}, function() {
var pubKey = openpgp.key.readArmored(pub_v3).keys[0];
var verified = pubKey.packets[3].verify(pubKey.packets[0], {key: pubKey.packets[0], userid: pubKey.packets[2]});
var verified = pubKey.users[0].selfCertifications[0].verify(pubKey.primaryKey, {key: pubKey.primaryKey, userid: pubKey.users[0].userId});
return new unit.result("Verify V3 certification signature", verified);
}];