Merge pull request #734 from twiss/key-revocation

Key revocation
This commit is contained in:
Sanjana Rajan 2018-07-13 17:46:48 +02:00 committed by GitHub
commit 53f473bfa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 563 additions and 60 deletions

View File

@ -240,6 +240,30 @@ var options = {
openpgp.generateKey(options).then(function(key) {
var privkey = key.privateKeyArmored; // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
var pubkey = key.publicKeyArmored; // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
var revocationSignature = key.revocationSignature; // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
});
```
#### Revoke a key
Using a revocation signature:
```js
var options = {
key: openpgp.key.readArmored(pubkey).keys[0],
revocationSignature: revocationSignature
};
```
Using the private key:
```js
var options = {
key: openpgp.key.readArmored(privkey).keys[0]
};
```
```js
openpgp.revokeKey(options).then(function(key) {
var pubkey = key.publicKeyArmored; // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
});
```

View File

@ -95,9 +95,10 @@ function getType(text) {
* packet block.
* @author Alex
* @version 2011-12-16
* @param {String} customComment (optional) additional comment to add to the armored string
* @returns {String} The header information
*/
function addheader() {
function addheader(customComment) {
let result = "";
if (config.show_version) {
result += "Version: " + config.versionstring + '\r\n';
@ -105,6 +106,9 @@ function addheader() {
if (config.show_comment) {
result += "Comment: " + config.commentstring + '\r\n';
}
if (customComment) {
result += "Comment: " + customComment + '\r\n';
}
result += '\r\n';
return result;
}
@ -326,22 +330,23 @@ function dearmor(text) {
* @param body
* @param {Integer} partindex
* @param {Integer} parttotal
* @param {String} customComment (optional) additional comment to add to the armored string
* @returns {String} Armored text
* @static
*/
function armor(messagetype, body, partindex, parttotal) {
function armor(messagetype, body, partindex, parttotal, customComment) {
const result = [];
switch (messagetype) {
case enums.armor.multipart_section:
result.push("-----BEGIN PGP MESSAGE, PART " + partindex + "/" + parttotal + "-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP MESSAGE, PART " + partindex + "/" + parttotal + "-----\r\n");
break;
case enums.armor.multipart_last:
result.push("-----BEGIN PGP MESSAGE, PART " + partindex + "-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP MESSAGE, PART " + partindex + "-----\r\n");
@ -351,35 +356,35 @@ function armor(messagetype, body, partindex, parttotal) {
result.push("Hash: " + body.hash + "\r\n\r\n");
result.push(body.text.replace(/^-/mg, "- -"));
result.push("\r\n-----BEGIN PGP SIGNATURE-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body.data));
result.push("\r\n=" + getCheckSum(body.data) + "\r\n");
result.push("-----END PGP SIGNATURE-----\r\n");
break;
case enums.armor.message:
result.push("-----BEGIN PGP MESSAGE-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP MESSAGE-----\r\n");
break;
case enums.armor.public_key:
result.push("-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n");
break;
case enums.armor.private_key:
result.push("-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP PRIVATE KEY BLOCK-----\r\n");
break;
case enums.armor.signature:
result.push("-----BEGIN PGP SIGNATURE-----\r\n");
result.push(addheader());
result.push(addheader(customComment));
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP SIGNATURE-----\r\n");

View File

@ -428,6 +428,23 @@ export default {
signature: 6
},
/** {@link https://tools.ietf.org/html/rfc4880#section-5.2.3.23|RFC4880, section 5.2.3.23}
* @enum {Integer}
* @readonly
*/
reasonForRevocation: {
/** No reason specified (key revocations or cert revocations) */
no_reason: 0,
/** Key is superseded (key revocations) */
key_superseded: 1,
/** Key material has been compromised (key revocations) */
key_compromised: 2,
/** Key is retired and no longer used (key revocations) */
key_retired: 3,
/** User ID information is no longer valid (cert revocations) */
userid_invalid: 32
},
/** {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-5.2.3.25|RFC4880bis-04, section 5.2.3.25}
* @enum {Integer}
* @readonly

View File

@ -19,7 +19,7 @@ export default openpgp;
*/
export {
encrypt, decrypt, sign, verify,
generateKey, reformatKey, decryptKey,
generateKey, reformatKey, revokeKey, decryptKey,
encryptSessionKey, decryptSessionKeys,
initWorker, getWorker, destroyWorker
} from './openpgp';

View File

@ -625,9 +625,71 @@ async function mergeSignatures(source, dest, attr, checkFn) {
}
}
// TODO
Key.prototype.revoke = function() {
/**
* Revokes the key
* @param {Object} reasonForRevocation optional, object indicating the reason for revocation
* @param {module:enums.reasonForRevocation} reasonForRevocation.flag optional, flag indicating the reason for revocation
* @param {String} reasonForRevocation.string optional, string explaining the reason for revocation
* @param {Date} date optional, override the creationtime of the revocation signature
* @return {module:key~Key} new key with revocation signature
*/
Key.prototype.revoke = async function({
flag: reasonForRevocationFlag=enums.reasonForRevocation.no_reason,
string: reasonForRevocationString=''
} = {}, date=new Date()) {
if (this.isPublic()) {
throw new Error('Need private key for revoking');
}
const dataToSign = { key: this.primaryKey };
const key = new Key(this.toPacketlist());
key.revocationSignatures.push(await createSignaturePacket(dataToSign, null, this.primaryKey, {
signatureType: enums.signature.key_revocation,
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
reasonForRevocationString
}, date));
return key;
};
/**
* Get revocation certificate from a revoked key.
* (To get a revocation certificate for an unrevoked key, call revoke() first.)
* @return {String} armored revocation certificate
*/
Key.prototype.getRevocationCertificate = function() {
if (this.revocationSignatures.length) {
const packetlist = new packet.List();
packetlist.push(getLatestSignature(this.revocationSignatures));
return armor.encode(enums.armor.public_key, packetlist.write(), null, null, 'This is a revocation certificate');
}
};
/**
* Applies a revocation certificate to a key
* This adds the first signature packet in the armored text to the key,
* if it is a valid revocation signature.
* @param {String} revocationCertificate armored revocation certificate
* @return {module:key~Key} new revoked key
*/
Key.prototype.applyRevocationCertificate = async function(revocationCertificate) {
const input = armor.decode(revocationCertificate);
const packetlist = new packet.List();
packetlist.read(input.data);
const revocationSignature = packetlist.findPacket(enums.packet.signature);
if (!revocationSignature || revocationSignature.signatureType !== enums.signature.key_revocation) {
throw new Error('Could not find revocation signature packet');
}
if (!revocationSignature.issuerKeyId.equals(this.primaryKey.getKeyId())) {
throw new Error('Revocation signature does not match key');
}
if (revocationSignature.isExpired()) {
throw new Error('Revocation signature is expired');
}
if (!await revocationSignature.verify(this.primaryKey, { key: this.primaryKey })) {
throw new Error('Could not verify revocation signature');
}
const key = new Key(this.toPacketlist());
key.revocationSignatures.push(revocationSignature);
return key;
};
/**
@ -763,15 +825,11 @@ User.prototype.sign = async function(primaryKey, privateKeys) {
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
const 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.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey);
signaturePacket.signingKeyId = signingKeyPacket.getKeyId();
signaturePacket.sign(signingKeyPacket, dataToSign);
return signaturePacket;
return createSignaturePacket(dataToSign, privateKey, signingKeyPacket, {
// Most OpenPGP implementations use generic certification (0x10)
signatureType: enums.signature.cert_generic,
keyFlags: [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]
});
}));
await user.update(this, primaryKey);
return user;
@ -799,6 +857,28 @@ User.prototype.isRevoked = async function(primaryKey, certificate, key, date=new
);
};
/**
* Create signature packet
* @param {Object} dataToSign Contains packets to be signed
* @param {module:packet.SecretKey|
* module:packet.SecretSubkey} signingKeyPacket secret key packet for signing
* @param {Object} signatureProperties (optional) properties to write on the signature packet before signing
* @param {Date} date (optional) override the creationtime of the signature
* @param {Object} userId (optional) user ID
* @return {module:packet/signature} signature packet
*/
export async function createSignaturePacket(dataToSign, privateKey, signingKeyPacket, signatureProperties, date, userId) {
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
const signaturePacket = new packet.Signature(date);
Object.assign(signaturePacket, signatureProperties);
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userId);
await signaturePacket.sign(signingKeyPacket, dataToSign);
return signaturePacket;
}
/**
* Verifies the user certificate
* @param {module:packet.SecretKey|
@ -1036,6 +1116,30 @@ SubKey.prototype.update = async function(subKey, primaryKey) {
});
};
/**
* Revokes the subkey
* @param {module:packet.SecretKey} primaryKey decrypted private primary key for revocation
* @param {Object} reasonForRevocation optional, object indicating the reason for revocation
* @param {module:enums.reasonForRevocation} reasonForRevocation.flag optional, flag indicating the reason for revocation
* @param {String} reasonForRevocation.string optional, string explaining the reason for revocation
* @param {Date} date optional, override the creationtime of the revocation signature
* @return {module:key~SubKey} new subkey with revocation signature
*/
SubKey.prototype.revoke = async function(primaryKey, {
flag: reasonForRevocationFlag=enums.reasonForRevocation.no_reason,
string: reasonForRevocationString=''
} = {}, date=new Date()) {
const dataToSign = { key: primaryKey, bind: this.subKey };
const subKey = new SubKey(this.subKey);
subKey.revocationSignatures.push(await createSignaturePacket(dataToSign, null, primaryKey, {
signatureType: enums.signature.subkey_revocation,
reasonForRevocationFlag: enums.write(enums.reasonForRevocation, reasonForRevocationFlag),
reasonForRevocationString
}, date));
await subKey.update(this, primaryKey);
return subKey;
};
/**
* Reads an unarmored OpenPGP key list and returns one or multiple key objects
* @param {Uint8Array} data to be parsed
@ -1189,6 +1293,7 @@ export async function generate(options) {
* @param {Date} date Override the creation date of the key and the key signatures
* @param {Array<Object>} subkeys (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
*
* @param {Boolean} [options.revoked=false] Whether the key should include a revocation signature
* @returns {Promise<module:key.Key>}
* @async
* @static
@ -1267,7 +1372,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
const signaturePacket = new packet.Signature(options.date);
signaturePacket.signatureType = enums.signature.cert_generic;
signaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(secretKeyPacket);
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(null, secretKeyPacket);
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.preferredSymmetricAlgorithms = [];
// prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support)
@ -1323,7 +1428,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
const subkeySignaturePacket = new packet.Signature(subkeyOptions.date);
subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
subkeySignaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
subkeySignaturePacket.hashAlgorithm = await getPreferredHashAlgo(secretSubkeyPacket);
subkeySignaturePacket.hashAlgorithm = await getPreferredHashAlgo(null, secretSubkeyPacket);
subkeySignaturePacket.keyFlags = subkeyOptions.sign ? enums.keyFlags.sign_data : [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
if (subkeyOptions.keyExpirationTime > 0) {
subkeySignaturePacket.keyExpirationTime = subkeyOptions.keyExpirationTime;
@ -1339,6 +1444,15 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
});
});
// Add revocation signature packet for creating a revocation certificate.
// This packet should be removed before returning the key.
const dataToSign = { key: secretKeyPacket };
packetlist.push(await createSignaturePacket(dataToSign, null, secretKeyPacket, {
signatureType: enums.signature.key_revocation,
reasonForRevocationFlag: enums.reasonForRevocation.no_reason,
reasonForRevocationString: ''
}, options.date));
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.clearPrivateParams();
@ -1416,13 +1530,14 @@ function getExpirationTime(keyPacket, signature) {
/**
* Returns the preferred signature hash algorithm of a key
* @param {object} key
* @param {module:key.Key} key (optional) the key to get preferences from
* @param {module:packet.SecretKey|module:packet.SecretSubkey} keyPacket key packet used for signing
* @param {Date} date (optional) use the given date for verification instead of the current time
* @param {Object} userId (optional) user ID
* @returns {Promise<String>}
* @async
*/
export async function getPreferredHashAlgo(key, date=new Date(), userId={}) {
export async function getPreferredHashAlgo(key, keyPacket, date=new Date(), userId={}) {
let hash_algo = config.prefer_hash_algorithm;
let pref_algo = hash_algo;
if (key instanceof Key) {
@ -1432,19 +1547,17 @@ export async function getPreferredHashAlgo(key, date=new Date(), userId={}) {
hash_algo = crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?
pref_algo : hash_algo;
}
// disable expiration checks
key = key.getSigningKeyPacket(undefined, null, userId);
}
switch (Object.getPrototypeOf(key)) {
switch (Object.getPrototypeOf(keyPacket)) {
case packet.SecretKey.prototype:
case packet.PublicKey.prototype:
case packet.SecretSubkey.prototype:
case packet.PublicSubkey.prototype:
switch (key.algorithm) {
switch (keyPacket.algorithm) {
case 'ecdh':
case 'ecdsa':
case 'eddsa':
pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(key.params[0]);
pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(keyPacket.params[0]);
}
}
return crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?

View File

@ -36,7 +36,7 @@ import enums from './enums';
import util from './util';
import packet from './packet';
import { Signature } from './signature';
import { getPreferredHashAlgo, getPreferredAlgo, isAeadSupported } from './key';
import { getPreferredHashAlgo, getPreferredAlgo, isAeadSupported, createSignaturePacket } from './key';
/**
@ -428,7 +428,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null, date=new
}
const onePassSig = new packet.OnePassSignature();
onePassSig.type = signatureType;
onePassSig.hashAlgorithm = await getPreferredHashAlgo(privateKey, date, userId);
onePassSig.hashAlgorithm = await getPreferredHashAlgo(privateKey, signingKeyPacket, date, userId);
onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm;
onePassSig.signingKeyId = signingKeyPacket.getKeyId();
if (i === privateKeys.length - 1) {
@ -499,24 +499,16 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
const signatureType = literalDataPacket.text === null ?
enums.signature.binary : enums.signature.text;
await Promise.all(privateKeys.map(async function(privateKey) {
await Promise.all(privateKeys.map(async privateKey => {
if (privateKey.isPublic()) {
throw new Error('Need private key for signing');
}
const signingKeyPacket = await privateKey.getSigningKeyPacket(undefined, date, userId);
if (!signingKeyPacket) {
throw new Error('Could not find valid key packet for signing in key ' +
privateKey.primaryKey.getKeyId().toHex());
throw new Error(`Could not find valid signing key packet in key ${
privateKey.primaryKey.getKeyId().toHex()}`);
}
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
const signaturePacket = new packet.Signature(date);
signaturePacket.signatureType = signatureType;
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(privateKey, date, userId);
await signaturePacket.sign(signingKeyPacket, literalDataPacket);
return signaturePacket;
return createSignaturePacket(literalDataPacket, privateKey, signingKeyPacket, {signatureType}, date, userId);
})).then(signatureList => {
signatureList.forEach(signaturePacket => packetlist.push(signaturePacket));
});

View File

@ -109,7 +109,7 @@ export function destroyWorker() {
* @param {Array<Object>} subkeys (optional) options for each subkey, default to main key options. e.g. [{sign: true, passphrase: '123'}]
* sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
* @returns {Promise<Object>} The generated key object in the form:
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
* { key:Key, privateKeyArmored:String, publicKeyArmored:String, revocationCertificate:String }
* @async
* @static
*/
@ -125,13 +125,19 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
return asyncProxy.delegate('generateKey', options);
}
return generate(options).then(key => ({
return generate(options).then(key => {
const revocationCertificate = key.getRevocationCertificate();
key.revocationSignatures = [];
key: key,
privateKeyArmored: key.armor(),
publicKeyArmored: key.toPublic().armor()
return {
})).catch(onError.bind(null, 'Error generating keypair'));
key: key,
privateKeyArmored: key.armor(),
publicKeyArmored: key.toPublic().armor(),
revocationCertificate: revocationCertificate
};
}).catch(onError.bind(null, 'Error generating keypair'));
}
/**
@ -140,25 +146,81 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
* @param {Array<Object>} userIds array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }]
* @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
* @param {Boolean} revocationCertificate (optional) Whether the returned object should include a revocation certificate to revoke the public key
* @returns {Promise<Object>} The generated key object in the form:
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
* { key:Key, privateKeyArmored:String, publicKeyArmored:String, revocationCertificate:String }
* @async
* @static
*/
export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date}) {
export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date, revocationCertificate=true}) {
userIds = toArray(userIds);
const options = { privateKey, userIds, passphrase, keyExpirationTime, date};
const options = { privateKey, userIds, passphrase, keyExpirationTime, date, revocationCertificate };
if (asyncProxy) {
return asyncProxy.delegate('reformatKey', options);
}
return reformat(options).then(key => ({
options.revoked = options.revocationCertificate;
key: key,
privateKeyArmored: key.armor(),
publicKeyArmored: key.toPublic().armor()
return reformat(options).then(key => {
const revocationCertificate = key.getRevocationCertificate();
key.revocationSignatures = [];
})).catch(onError.bind(null, 'Error reformatting keypair'));
return {
key: key,
privateKeyArmored: key.armor(),
publicKeyArmored: key.toPublic().armor(),
revocationCertificate: revocationCertificate
};
}).catch(onError.bind(null, 'Error reformatting keypair'));
}
/**
* Revokes a key. Requires either a private key or a revocation certificate.
* If a revocation certificate is passed, the reasonForRevocation parameters will be ignored.
* @param {Key} key (optional) public or private key to revoke
* @param {String} revocationCertificate (optional) revocation certificate to revoke the key with
* @param {Object} reasonForRevocation (optional) object indicating the reason for revocation
* @param {module:enums.reasonForRevocation} reasonForRevocation.flag (optional) flag indicating the reason for revocation
* @param {String} reasonForRevocation.string (optional) string explaining the reason for revocation
* @return {Promise<Object>} The revoked key object in the form:
* { privateKey:Key, privateKeyArmored:String, publicKey:Key, publicKeyArmored:String }
* (if private key is passed) or { publicKey:Key, publicKeyArmored:String } (otherwise)
* @static
*/
export function revokeKey({
key, revocationCertificate, reasonForRevocation
} = {}) {
const options = {
key, revocationCertificate, reasonForRevocation
};
if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported
return asyncProxy.delegate('revokeKey', options);
}
return Promise.resolve().then(() => {
if (revocationCertificate) {
return key.applyRevocationCertificate(revocationCertificate);
} else {
return key.revoke(reasonForRevocation);
}
}).then(key => {
if (key.isPrivate()) {
const publicKey = key.toPublic();
return {
privateKey: key,
privateKeyArmored: key.armor(),
publicKey: publicKey,
publicKeyArmored: publicKey.armor()
};
}
return {
publicKey: key,
publicKeyArmored: key.armor()
};
}).catch(onError.bind(null, 'Error revoking key'));
}
/**

View File

@ -50,6 +50,162 @@ describe('Key', function() {
});
function tests() {
const priv_key_arm2 =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'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',
'=lw5e',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
const pub_key_arm2 =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
'9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa',
'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag',
'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr',
'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb',
'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA',
'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP',
'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2',
'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X',
'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD',
'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY',
'hz3tYjKhoFTKEIq3y3Pp',
'=h/aX',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
const pub_key_arm4 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG 2.1.15 (GNU/Linux)
mI0EWpoNLAEEAMoO8dfnLvvCze1hjWcr8t1fMdndFQc1fAM7dm6sbqrdlaAz+Dab
zF3F9UhIOCcABRm+QHyZlgEsoQpHF/7sWflUK1FpoxdORINtIDilukUkMZ0NnIaD
+8pRutdSczPNFvSImSzZNCyLzvDCGMO3+Xeaa6pViSPEeBwhXWJUuHYtABEBAAG0
IkpvZSBVc2VyIDxqb2UudXNlckBwcm90b25tYWlsLmNvbT6ItwQTAQgAIQUCWpoN
LAIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBYtrN4WUnCtoUjBACi6qVb
noQJ1aOaOyoBZscDIO5XZHZK4L9V9uJJhD9v1qRF5s0PRiG969EwFlvjqIlLiRej
KSxvE/1rcym4GwBUndku1fMM+weTWHNtRn9+BPzN/4eKZbUbY3fHtlk+Lde3N+CZ
vrGKS/ICtbtuAfZL0LdzzqnNuBUXlO6EpG5C3riNBFqaDSwBBADDURzGkpTn/FTT
xHyheai+zTOUmy7N1ViCRPkErIeD606tZf/sKqAnEChfECeZJReYydN1B3O8QOyI
Ly/rH0DS2bt/6juhknPVGHPUAyNxHmiHYXTUgGPEX1QfusjzBcfIk6vHjYBiRm/I
u9iwrzCwypA4dWDZSTZuFrVsf4n+twARAQABiJ8EGAEIAAkFAlqaDSwCGwwACgkQ
WLazeFlJwrZQEAQAuUrp9Qp76CnKqUsUjcVxq7DJBi/lewyGGYSVAFt6/0Xyg/8Y
TEa/c4Dri/HMOtrfbgjp/doIVaZLOXZYfqRcpy3z0M6BierOPB3D+fdaTfd7gIrQ
nGHIp2NmbJZnYgl8Ps23qF+LKTa1eE+AmMQYzUHSGuka2lp6OglwWzg/dEw=
=/vbH
-----END PGP PUBLIC KEY BLOCK-----`;
const priv_key_arm4 = `-----BEGIN PGP PRIVATE KEY BLOCK-----
Version: GnuPG 2.1.15 (GNU/Linux)
lQIGBFqaDSwBBADKDvHX5y77ws3tYY1nK/LdXzHZ3RUHNXwDO3ZurG6q3ZWgM/g2
m8xdxfVISDgnAAUZvkB8mZYBLKEKRxf+7Fn5VCtRaaMXTkSDbSA4pbpFJDGdDZyG
g/vKUbrXUnMzzRb0iJks2TQsi87wwhjDt/l3mmuqVYkjxHgcIV1iVLh2LQARAQAB
/gcDAoZ8RULY7umS4fVGPmTuETCnOOTGancXT5r7chKyfFXlyVU4ULvTdLwdFtqx
Vl9tNyED31nIiRP1CTmZLeaVScNGfVLjo8nvpMZUVopw5UdaFADeVTpwVdtp7ru+
IgH4ynrRMgMGh7/dgBzIP8WN4w8uBPK5G4bS34NNiREkVoZ3oh4dA/6aeYfW7lVV
cYRl2F7++AGfqS+FpLsE8KjFU2z8POJjWMN1nYKwjNa+beEO0BFYdUFvMzU7eUHA
/G0xWAhYvNyuJHE4imgYmCy1OZeawc9h8YGeaQJCh2NTVzaD9HRu0xmz93bNF19q
bfUZJC7mC6WzKsRXHX0JmzH+9DShUqGnkRl5fMo2UhQMpSxsMT4dU/Ji4q+t96oy
K6g3DMJr5OtZML3XKxGmdy0CgepkG1aikrC9qLfBgxjqi+uTewcbrS9lAOfrZg4N
jwt1FLEK8gu7aOeczdW/pFOHOCrX1DnpF81JKJ1a7hz5JRP1m+ffqwm0IkpvZSBV
c2VyIDxqb2UudXNlckBwcm90b25tYWlsLmNvbT6ItwQTAQgAIQUCWpoNLAIbAwUL
CQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRBYtrN4WUnCtoUjBACi6qVbnoQJ1aOa
OyoBZscDIO5XZHZK4L9V9uJJhD9v1qRF5s0PRiG969EwFlvjqIlLiRejKSxvE/1r
cym4GwBUndku1fMM+weTWHNtRn9+BPzN/4eKZbUbY3fHtlk+Lde3N+CZvrGKS/IC
tbtuAfZL0LdzzqnNuBUXlO6EpG5C3p0CBgRamg0sAQQAw1EcxpKU5/xU08R8oXmo
vs0zlJsuzdVYgkT5BKyHg+tOrWX/7CqgJxAoXxAnmSUXmMnTdQdzvEDsiC8v6x9A
0tm7f+o7oZJz1Rhz1AMjcR5oh2F01IBjxF9UH7rI8wXHyJOrx42AYkZvyLvYsK8w
sMqQOHVg2Uk2bha1bH+J/rcAEQEAAf4HAwLwNvRIoBFS3OHTIYirkr4sHzSkWFJx
xDPozovXgCq7BoCXDMaSIQLwZqEfb+SabYtk7nLSnG2Y2mgwb9swZuBZEWuQjZk7
lX1MvuZ0Ih2QdQSMEJk8sEsMoBGHHdHh/MZO4a27+5B9OceDfnEZZcGSOweUuu1n
IlgWcgrM40q4S3Mt39FXFgdJWnpd93hAokKDHklUGMdMLw/02dGVRkJmvUp9qdhe
c2njq9HSeYwqbY2rYgcNsF2ZcCLt9UXA2dOG4X2c2mPfjKuTRZUPxNKh6JfL3mlu
rBdd/z8gQHoKObyaarVwN3HAbtP0+6Z8a9/wDYj1K9ZCoHuEtKq1qq5J2Ec8+Yzl
K0Zlcs760LiYUr69CninMrnbDNnAhrYAcyJS42viUADPv9g+CBbyanB4KyE4UNrZ
BCB296lOEW4v1IZVNrNvqrbka3/p0qqBJiFTh7eT3zXpRNArFZDmLCUEEm53qT1a
PO/MyYUGTTMRAzTmNTiPiJ8EGAEIAAkFAlqaDSwCGwwACgkQWLazeFlJwrZQEAQA
uUrp9Qp76CnKqUsUjcVxq7DJBi/lewyGGYSVAFt6/0Xyg/8YTEa/c4Dri/HMOtrf
bgjp/doIVaZLOXZYfqRcpy3z0M6BierOPB3D+fdaTfd7gIrQnGHIp2NmbJZnYgl8
Ps23qF+LKTa1eE+AmMQYzUHSGuka2lp6OglwWzg/dEw=
=mr3M
-----END PGP PRIVATE KEY BLOCK-----`;
const revocation_certificate_arm4 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG 2.1.15 (GNU/Linux)
Comment: This is a revocation certificate
iJ8EIAEIAAkFAlqaDT0CHQAACgkQWLazeFlJwrbOaAP/V38FhBrUy4XYgt8ZX22G
ov6IFDNoyRKafSuz7Rg+8K8cf+0MAsSi52ueKfsbPxQ+I1vPeaEuEYbwTjtbvM+M
vZcX+VNYdsc1iZeNaT4ayA+2LrCN/xgFj0nrExHqcZAjgBZ9pvKghAqdK4Zb2Ghb
7chPiLLNWJCMtL4bo7a1X84=
=HcWg
-----END PGP PUBLIC KEY BLOCK-----`;
const revoked_key_arm4 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG 2.1.15 (GNU/Linux)
mI0EWpoNLAEEAMoO8dfnLvvCze1hjWcr8t1fMdndFQc1fAM7dm6sbqrdlaAz+Dab
zF3F9UhIOCcABRm+QHyZlgEsoQpHF/7sWflUK1FpoxdORINtIDilukUkMZ0NnIaD
+8pRutdSczPNFvSImSzZNCyLzvDCGMO3+Xeaa6pViSPEeBwhXWJUuHYtABEBAAGI
nwQgAQgACQUCWpoNPQIdAAAKCRBYtrN4WUnCts5oA/9XfwWEGtTLhdiC3xlfbYai
/ogUM2jJEpp9K7PtGD7wrxx/7QwCxKLna54p+xs/FD4jW895oS4RhvBOO1u8z4y9
lxf5U1h2xzWJl41pPhrID7YusI3/GAWPSesTEepxkCOAFn2m8qCECp0rhlvYaFvt
yE+Iss1YkIy0vhujtrVfzrQiSm9lIFVzZXIgPGpvZS51c2VyQHByb3Rvbm1haWwu
Y29tPoi3BBMBCAAhBQJamg0sAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJ
EFi2s3hZScK2hSMEAKLqpVuehAnVo5o7KgFmxwMg7ldkdkrgv1X24kmEP2/WpEXm
zQ9GIb3r0TAWW+OoiUuJF6MpLG8T/WtzKbgbAFSd2S7V8wz7B5NYc21Gf34E/M3/
h4pltRtjd8e2WT4t17c34Jm+sYpL8gK1u24B9kvQt3POqc24FReU7oSkbkLeuI0E
WpoNLAEEAMNRHMaSlOf8VNPEfKF5qL7NM5SbLs3VWIJE+QSsh4PrTq1l/+wqoCcQ
KF8QJ5klF5jJ03UHc7xA7IgvL+sfQNLZu3/qO6GSc9UYc9QDI3EeaIdhdNSAY8Rf
VB+6yPMFx8iTq8eNgGJGb8i72LCvMLDKkDh1YNlJNm4WtWx/if63ABEBAAGInwQY
AQgACQUCWpoNLAIbDAAKCRBYtrN4WUnCtlAQBAC5Sun1CnvoKcqpSxSNxXGrsMkG
L+V7DIYZhJUAW3r/RfKD/xhMRr9zgOuL8cw62t9uCOn92ghVpks5dlh+pFynLfPQ
zoGJ6s48HcP591pN93uAitCcYcinY2ZslmdiCXw+zbeoX4spNrV4T4CYxBjNQdIa
6RraWno6CXBbOD90TA==
=8d2d
-----END PGP PUBLIC KEY BLOCK-----`;
const twoKeys =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
@ -1172,6 +1328,11 @@ const mergeKey2 = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
done();
});
it('Verify status of revoked primary key', function(done) {
const pubKey = openpgp.key.readArmored(pub_revoked_subkeys).keys[0];
expect(pubKey.verifyPrimaryKey()).to.eventually.equal(openpgp.enums.keyStatus.revoked).notify(done);
});
it('Verify status of revoked subkey', function(done) {
const pubKeys = openpgp.key.readArmored(pub_sig_test);
expect(pubKeys).to.exist;
@ -1359,6 +1520,71 @@ const mergeKey2 = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
});
});
it('revoke() - primary key', async function() {
const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
await privKey.decrypt('hello world');
await privKey.revoke({
flag: openpgp.enums.reasonForRevocation.key_retired,
string: 'Testing key revocation'
}).then(async revKey => {
expect(revKey.revocationSignatures).to.exist.and.have.length(1);
expect(revKey.revocationSignatures[0].signatureType).to.equal(openpgp.enums.signature.key_revocation);
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.key_retired);
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('Testing key revocation');
expect(await privKey.verifyPrimaryKey()).to.equal(openpgp.enums.keyStatus.valid);
expect(await revKey.verifyPrimaryKey()).to.equal(openpgp.enums.keyStatus.revoked);
});
});
it('revoke() - subkey', async function() {
const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
await privKey.decrypt('hello world');
const subKey = pubKey.subKeys[0];
await subKey.revoke(privKey.primaryKey, {
flag: openpgp.enums.reasonForRevocation.key_superseded
}).then(async revKey => {
expect(revKey.revocationSignatures).to.exist.and.have.length(1);
expect(revKey.revocationSignatures[0].signatureType).to.equal(openpgp.enums.signature.subkey_revocation);
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.key_superseded);
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('');
expect(await subKey.verify(pubKey.primaryKey)).to.equal(openpgp.enums.keyStatus.valid);
expect(await revKey.verify(pubKey.primaryKey)).to.equal(openpgp.enums.keyStatus.revoked);
});
});
it('applyRevocationCertificate() should produce the same revoked key as GnuPG', function() {
const pubKey = openpgp.key.readArmored(pub_key_arm4).keys[0];
return pubKey.applyRevocationCertificate(revocation_certificate_arm4).then(revKey => {
expect(revKey.armor()).to.equal(openpgp.key.readArmored(revoked_key_arm4).keys[0].armor());
});
});
it('getRevocationCertificate() should produce the same revocation certificate as GnuPG', function() {
const revKey = openpgp.key.readArmored(revoked_key_arm4).keys[0];
const revocationCertificate = revKey.getRevocationCertificate();
const input = openpgp.armor.decode(revocation_certificate_arm4);
const packetlist = new openpgp.packet.List();
packetlist.read(input.data);
const armored = openpgp.armor.encode(openpgp.enums.armor.public_key, packetlist.write());
expect(revocationCertificate.replace(/^Comment: .*$\r\n/mg, '')).to.equal(armored.replace(/^Comment: .*$\r\n/mg, ''));
});
it('getRevocationCertificate() should have an appropriate comment', function() {
const revKey = openpgp.key.readArmored(revoked_key_arm4).keys[0];
const revocationCertificate = revKey.getRevocationCertificate();
expect(revocationCertificate).to.match(/Comment: This is a revocation certificate/);
expect(revKey.armor()).not.to.match(/Comment: This is a revocation certificate/);
});
it("getPreferredAlgo('symmetric') - one key - AES256", async function() {
const key1 = openpgp.key.readArmored(twoKeys).keys[0];
const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1]);
@ -1922,6 +2148,37 @@ VYGdb3eNlV8CfoEC
});
});
it('Revoke generated key with revocation certificate', 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) {
return openpgp.revokeKey({key: original.key.toPublic(), revocationCertificate: original.revocationCertificate}).then(function(revKey) {
revKey = revKey.publicKey;
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('');
return revKey.verifyPrimaryKey().then(function(status) {
expect(status).to.equal(openpgp.enums.keyStatus.revoked);
});
});
});
});
it('Revoke generated key with private 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(async function(original) {
await original.key.decrypt('1234');
return openpgp.revokeKey({key: original.key, reasonForRevocation: {string: 'Testing key revocation'}}).then(function(revKey) {
revKey = revKey.publicKey;
expect(revKey.revocationSignatures[0].reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
expect(revKey.revocationSignatures[0].reasonForRevocationString).to.equal('Testing key revocation');
return revKey.verifyPrimaryKey().then(function(status) {
expect(status).to.equal(openpgp.enums.keyStatus.revoked);
});
});
});
});
it('Merge key with another key with non-ID user attributes', function(done) {
const key = openpgp.key.readArmored(mergeKey1).keys[0];
const updateKey = openpgp.key.readArmored(mergeKey2).keys[0];

View File

@ -484,7 +484,8 @@ describe('OpenPGP.js public api tests', function() {
return 'pub_key';
}
};
}
},
getRevocationCertificate: function() {}
};
keyGenStub = stub(openpgp.key, 'generate');
keyGenStub.returns(resolves(keyObjStub));
@ -513,7 +514,7 @@ describe('OpenPGP.js public api tests', function() {
keyExpirationTime: 0,
curve: "",
date: now,
subkeys: [],
subkeys: []
}).calledOnce).to.be.true;
expect(newKey.key).to.exist;
expect(newKey.privateKeyArmored).to.exist;
@ -1855,6 +1856,38 @@ describe('OpenPGP.js public api tests', function() {
expect(signatures[0].signature.packets.length).to.equal(1);
});
});
it('should fail to encrypt with revoked key', function() {
return openpgp.revokeKey({
key: privateKey.keys[0]
}).then(function(revKey) {
return openpgp.encrypt({
data: plaintext,
publicKeys: revKey.publicKey
}).then(function(encrypted) {
throw new Error('Should not encrypt with revoked key');
}).catch(function(error) {
expect(error.message).to.match(/Could not find valid key packet for encryption/);
});
});
});
it('should fail to encrypt with revoked subkey', async function() {
const pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0];
const privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0];
await privKeyDE.decrypt(passphrase);
return privKeyDE.subKeys[0].revoke(privKeyDE.primaryKey).then(function(revSubKey) {
pubKeyDE.subKeys[0] = revSubKey;
return openpgp.encrypt({
data: plaintext,
publicKeys: pubKeyDE
}).then(function(encrypted) {
throw new Error('Should not encrypt with revoked subkey');
}).catch(function(error) {
expect(error.message).to.match(/Could not find valid key packet for encryption/);
});
});
});
});
describe('ELG / DSA encrypt, decrypt, sign, verify', function() {