Create openpgp.revokeKey

This commit is contained in:
Daniel Huigens 2018-02-27 22:55:06 +01:00
parent 368d80245a
commit 1ed7943bf9
6 changed files with 357 additions and 17 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

@ -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

@ -665,6 +665,56 @@ Key.prototype.revoke = async function(privateKey, {
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.revocationSignature) {
const commentstring = config.commentstring;
config.commentstring = 'This is a revocation certificate';
try {
const packetlist = new packet.List();
packetlist.push(this.revocationSignature);
return armor.encode(enums.armor.public_key, packetlist.write());
} finally {
// Restore comment string. armor.encode() shouldn't throw, but just to be sure it's wrapped in a try/finally
config.commentstring = commentstring;
}
}
};
/**
* Applies a revocation certificate to a key
* @param {String} revocationCertificate armored revocation certificate
* @return {module:key~Key} new revoked key
*/
Key.prototype.applyRevocationCertificate = async function(revocationCertificate) {
const input = armor.decode(revocationCertificate);
if (input.type !== enums.armor.public_key) {
throw new Error('Armored text not of type public key');
}
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.revocationSignature = revocationSignature;
return key;
};
/**
* Signs primary user of key
* @param {Array<module:key.Key>} privateKey decrypted private keys for signing
@ -1185,6 +1235,7 @@ export function readArmored(armoredText) {
* @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'}]
* sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
* @param {Boolean} [options.revoked=false] Whether the key should include a revocation signature
* @returns {Promise<module:key.Key>}
* @async
* @static
@ -1266,6 +1317,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
@ -1416,6 +1468,19 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
});
});
if (options.revoked) {
const dataToSign = {};
dataToSign.key = secretKeyPacket;
const revocationSignaturePacket = new packet.Signature();
revocationSignaturePacket.signatureType = enums.signature.key_revocation;
revocationSignaturePacket.reasonForRevocationFlag = enums.reasonForRevocation.no_reason;
revocationSignaturePacket.reasonForRevocationString = '';
revocationSignaturePacket.publicKeyAlgorithm = options.keyType;
revocationSignaturePacket.hashAlgorithm = getPreferredHashAlgo(secretKeyPacket);
await revocationSignaturePacket.sign(secretKeyPacket, dataToSign);
packetlist.push(revocationSignaturePacket);
}
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.clearPrivateParams();

View File

@ -108,15 +108,16 @@ export function destroyWorker() {
* @param {Date} date (optional) 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'}]
* sign parameter defaults to false, and indicates whether the subkey should sign rather than encrypt
* @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 generateKey({ userIds=[], passphrase="", numBits=2048, keyExpirationTime=0, curve="", date=new Date(), subkeys=[{}] }) {
export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpirationTime=0, curve="", date=new Date(), subkeys=[{}], revocationCertificate=true }) {
userIds = toArray(userIds);
const options = { userIds, passphrase, numBits, keyExpirationTime, curve, date, subkeys };
const options = { userIds, passphrase, numBits, keyExpirationTime, curve, date, subkeys, revocationCertificate };
if (util.getWebCryptoAll() && numBits < 2048) {
throw new Error('numBits should be 2048 or 4096, found: ' + numBits);
}
@ -125,13 +126,21 @@ export function generateKey({ userIds=[], passphrase="", numBits=2048, keyExpira
return asyncProxy.delegate('generateKey', options);
}
return generate(options).then(key => ({
options.revoked = options.revocationCertificate;
key: key,
privateKeyArmored: key.armor(),
publicKeyArmored: key.toPublic().armor()
return generate(options).then(key => {
const revocationCertificate = key.getRevocationCertificate();
key.revocationSignature = null;
})).catch(onError.bind(null, 'Error generating keypair'));
return {
key: key,
privateKeyArmored: key.armor(),
publicKeyArmored: key.toPublic().armor(),
revocationCertificate: revocationCertificate
};
}).catch(onError.bind(null, 'Error generating keypair'));
}
/**
@ -140,25 +149,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=true};
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.revocationSignature = null;
})).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(key, 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

@ -114,6 +114,97 @@ function tests() {
'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-----',
@ -1467,6 +1558,34 @@ const mergeKey2 = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
});
});
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: .*$/m, '')).to.equal(armored.replace(/^Comment: .*$/m, ''));
});
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]);
@ -2012,6 +2131,37 @@ const mergeKey2 = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
});
});
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.revocationSignature.reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
expect(revKey.revocationSignature.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(function(original) {
original.key.decrypt('1234');
return openpgp.revokeKey({key: original.key, reasonForRevocation: {string: 'Testing key revocation'}}).then(function(revKey) {
revKey = revKey.publicKey;
expect(revKey.revocationSignature.reasonForRevocationFlag).to.equal(openpgp.enums.reasonForRevocation.no_reason);
expect(revKey.revocationSignature.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,6 +484,10 @@ describe('OpenPGP.js public api tests', function() {
return 'pub_key';
}
};
},
getRevocationCertificate: function() {},
removeRevocationCertificate: function() {
return this;
}
};
keyGenStub = stub(openpgp.key, 'generate');
@ -514,6 +518,8 @@ describe('OpenPGP.js public api tests', function() {
curve: "",
date: now,
subkeys: [],
revocationCertificate: true,
revoked: true,
}).calledOnce).to.be.true;
expect(newKey.key).to.exist;
expect(newKey.privateKeyArmored).to.exist;
@ -1855,6 +1861,36 @@ describe('OpenPGP.js public api tests', function() {
expect(signatures[0].signature.packets.length).to.equal(1);
});
});
it.skip('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.skip('should fail to encrypt with revoked subkey', function() {
let clonedKey = privateKey.keys[0].toPublic();
return clonedKey.subKeys[0].revoke(clonedKey.primaryKey, privateKey.keys[0]).then(function(revSubKey) {
clonedKey.subKeys[0] = revSubKey;
return openpgp.encrypt({
data: plaintext,
publicKeys: clonedKey
}).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() {