Create openpgp.revokeKey
This commit is contained in:
parent
368d80245a
commit
1ed7943bf9
24
README.md
24
README.md
|
@ -240,6 +240,30 @@ var options = {
|
||||||
openpgp.generateKey(options).then(function(key) {
|
openpgp.generateKey(options).then(function(key) {
|
||||||
var privkey = key.privateKeyArmored; // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
|
var privkey = key.privateKeyArmored; // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
|
||||||
var pubkey = key.publicKeyArmored; // '-----BEGIN PGP PUBLIC 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 ... '
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ export default openpgp;
|
||||||
*/
|
*/
|
||||||
export {
|
export {
|
||||||
encrypt, decrypt, sign, verify,
|
encrypt, decrypt, sign, verify,
|
||||||
generateKey, reformatKey, decryptKey,
|
generateKey, reformatKey, revokeKey, decryptKey,
|
||||||
encryptSessionKey, decryptSessionKeys,
|
encryptSessionKey, decryptSessionKeys,
|
||||||
initWorker, getWorker, destroyWorker
|
initWorker, getWorker, destroyWorker
|
||||||
} from './openpgp';
|
} from './openpgp';
|
||||||
|
|
65
src/key.js
65
src/key.js
|
@ -665,6 +665,56 @@ Key.prototype.revoke = async function(privateKey, {
|
||||||
return key;
|
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
|
* Signs primary user of key
|
||||||
* @param {Array<module:key.Key>} privateKey decrypted private keys for signing
|
* @param {Array<module:key.Key>} privateKey decrypted private keys for signing
|
||||||
|
@ -1185,6 +1235,7 @@ export function readArmored(armoredText) {
|
||||||
* @param {Date} date Override the creation date of the key and the key signatures
|
* @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 {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
|
* 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>}
|
* @returns {Promise<module:key.Key>}
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @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 {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 {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>}
|
* @returns {Promise<module:key.Key>}
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @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
|
// set passphrase protection
|
||||||
if (options.passphrase) {
|
if (options.passphrase) {
|
||||||
secretKeyPacket.clearPrivateParams();
|
secretKeyPacket.clearPrivateParams();
|
||||||
|
|
|
@ -108,15 +108,16 @@ export function destroyWorker() {
|
||||||
* @param {Date} date (optional) override the creation date of the key and the key signatures
|
* @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'}]
|
* @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
|
* 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:
|
* @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
|
* @async
|
||||||
* @static
|
* @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);
|
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) {
|
if (util.getWebCryptoAll() && numBits < 2048) {
|
||||||
throw new Error('numBits should be 2048 or 4096, found: ' + numBits);
|
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 asyncProxy.delegate('generateKey', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return generate(options).then(key => ({
|
options.revoked = options.revocationCertificate;
|
||||||
|
|
||||||
|
return generate(options).then(key => {
|
||||||
|
const revocationCertificate = key.getRevocationCertificate();
|
||||||
|
key.revocationSignature = null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
key: key,
|
key: key,
|
||||||
privateKeyArmored: key.armor(),
|
privateKeyArmored: key.armor(),
|
||||||
publicKeyArmored: key.toPublic().armor()
|
publicKeyArmored: key.toPublic().armor(),
|
||||||
|
revocationCertificate: revocationCertificate
|
||||||
|
|
||||||
})).catch(onError.bind(null, 'Error generating keypair'));
|
};
|
||||||
|
}).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 {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 {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 {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:
|
* @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
|
* @async
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date}) {
|
export function reformatKey({privateKey, userIds=[], passphrase="", keyExpirationTime=0, date, revocationCertificate=true}) {
|
||||||
userIds = toArray(userIds);
|
userIds = toArray(userIds);
|
||||||
const options = { privateKey, userIds, passphrase, keyExpirationTime, date};
|
const options = { privateKey, userIds, passphrase, keyExpirationTime, date, revocationCertificate=true};
|
||||||
if (asyncProxy) {
|
if (asyncProxy) {
|
||||||
return asyncProxy.delegate('reformatKey', options);
|
return asyncProxy.delegate('reformatKey', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reformat(options).then(key => ({
|
options.revoked = options.revocationCertificate;
|
||||||
|
|
||||||
|
return reformat(options).then(key => {
|
||||||
|
const revocationCertificate = key.getRevocationCertificate();
|
||||||
|
key.revocationSignature = null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
key: key,
|
key: key,
|
||||||
privateKeyArmored: key.armor(),
|
privateKeyArmored: key.armor(),
|
||||||
publicKeyArmored: key.toPublic().armor()
|
publicKeyArmored: key.toPublic().armor(),
|
||||||
|
revocationCertificate: revocationCertificate
|
||||||
|
|
||||||
})).catch(onError.bind(null, 'Error reformatting keypair'));
|
};
|
||||||
|
}).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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -115,6 +115,97 @@ function tests() {
|
||||||
'=h/aX',
|
'=h/aX',
|
||||||
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
'-----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)
|
||||||
|
|
||||||
|
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
|
||||||
|
=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 =
|
const twoKeys =
|
||||||
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||||
|
@ -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() {
|
it("getPreferredAlgo('symmetric') - one key - AES256", async function() {
|
||||||
const key1 = openpgp.key.readArmored(twoKeys).keys[0];
|
const key1 = openpgp.key.readArmored(twoKeys).keys[0];
|
||||||
const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1]);
|
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) {
|
it('Merge key with another key with non-ID user attributes', function(done) {
|
||||||
const key = openpgp.key.readArmored(mergeKey1).keys[0];
|
const key = openpgp.key.readArmored(mergeKey1).keys[0];
|
||||||
const updateKey = openpgp.key.readArmored(mergeKey2).keys[0];
|
const updateKey = openpgp.key.readArmored(mergeKey2).keys[0];
|
||||||
|
|
|
@ -484,6 +484,10 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
return 'pub_key';
|
return 'pub_key';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
getRevocationCertificate: function() {},
|
||||||
|
removeRevocationCertificate: function() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
keyGenStub = stub(openpgp.key, 'generate');
|
keyGenStub = stub(openpgp.key, 'generate');
|
||||||
|
@ -514,6 +518,8 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
curve: "",
|
curve: "",
|
||||||
date: now,
|
date: now,
|
||||||
subkeys: [],
|
subkeys: [],
|
||||||
|
revocationCertificate: true,
|
||||||
|
revoked: true,
|
||||||
}).calledOnce).to.be.true;
|
}).calledOnce).to.be.true;
|
||||||
expect(newKey.key).to.exist;
|
expect(newKey.key).to.exist;
|
||||||
expect(newKey.privateKeyArmored).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);
|
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() {
|
describe('ELG / DSA encrypt, decrypt, sign, verify', function() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user