Merge pull request #534 from openpgpjs/signature_input
Accept signature object as an input to encrypt function
This commit is contained in:
commit
62c24ed227
|
@ -292,10 +292,11 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign the message (the literal data packet of the message)
|
* Sign the message (the literal data packet of the message)
|
||||||
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
|
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
|
||||||
* @return {module:message~Message} new message with signed content
|
* @param {Signature} signature (optional) any existing detached signature to add to the message
|
||||||
|
* @return {module:message~Message} new message with signed content
|
||||||
*/
|
*/
|
||||||
Message.prototype.sign = function(privateKeys) {
|
Message.prototype.sign = function(privateKeys=[], signature=null) {
|
||||||
|
|
||||||
var packetlist = new packet.List();
|
var packetlist = new packet.List();
|
||||||
|
|
||||||
|
@ -307,12 +308,30 @@ Message.prototype.sign = function(privateKeys) {
|
||||||
var literalFormat = enums.write(enums.literal, literalDataPacket.format);
|
var literalFormat = enums.write(enums.literal, literalDataPacket.format);
|
||||||
var signatureType = literalFormat === enums.literal.binary ?
|
var signatureType = literalFormat === enums.literal.binary ?
|
||||||
enums.signature.binary : enums.signature.text;
|
enums.signature.binary : enums.signature.text;
|
||||||
var i, signingKeyPacket;
|
var i, signingKeyPacket, existingSigPacketlist, onePassSig;
|
||||||
|
|
||||||
|
if (signature) {
|
||||||
|
existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
|
||||||
|
if (existingSigPacketlist.length) {
|
||||||
|
for (i = existingSigPacketlist.length - 1; i >= 0; i--) {
|
||||||
|
var sigPacket = existingSigPacketlist[i];
|
||||||
|
onePassSig = new packet.OnePassSignature();
|
||||||
|
onePassSig.type = signatureType;
|
||||||
|
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
|
||||||
|
onePassSig.publicKeyAlgorithm = sigPacket.publicKeyAlgorithm;
|
||||||
|
onePassSig.signingKeyId = sigPacket.issuerKeyId;
|
||||||
|
if (!privateKeys.length && i === 0) {
|
||||||
|
onePassSig.flags = 1;
|
||||||
|
}
|
||||||
|
packetlist.push(onePassSig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (i = 0; i < privateKeys.length; i++) {
|
for (i = 0; i < privateKeys.length; i++) {
|
||||||
if (privateKeys[i].isPublic()) {
|
if (privateKeys[i].isPublic()) {
|
||||||
throw new Error('Need private key for signing');
|
throw new Error('Need private key for signing');
|
||||||
}
|
}
|
||||||
var onePassSig = new packet.OnePassSignature();
|
onePassSig = new packet.OnePassSignature();
|
||||||
onePassSig.type = signatureType;
|
onePassSig.type = signatureType;
|
||||||
//TODO get preferred hashg algo from key signature
|
//TODO get preferred hashg algo from key signature
|
||||||
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
|
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
|
||||||
|
@ -342,15 +361,20 @@ Message.prototype.sign = function(privateKeys) {
|
||||||
packetlist.push(signaturePacket);
|
packetlist.push(signaturePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signature) {
|
||||||
|
packetlist.concat(existingSigPacketlist);
|
||||||
|
}
|
||||||
|
|
||||||
return new Message(packetlist);
|
return new Message(packetlist);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a detached signature for the message (the literal data packet of the message)
|
* Create a detached signature for the message (the literal data packet of the message)
|
||||||
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
|
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
|
||||||
|
* @param {Signature} signature (optional) any existing detached signature
|
||||||
* @return {module:signature~Signature} new detached signature of message content
|
* @return {module:signature~Signature} new detached signature of message content
|
||||||
*/
|
*/
|
||||||
Message.prototype.signDetached = function(privateKeys) {
|
Message.prototype.signDetached = function(privateKeys=[], signature=null) {
|
||||||
|
|
||||||
var packetlist = new packet.List();
|
var packetlist = new packet.List();
|
||||||
|
|
||||||
|
@ -375,6 +399,10 @@ Message.prototype.signDetached = function(privateKeys) {
|
||||||
signaturePacket.sign(signingKeyPacket, literalDataPacket);
|
signaturePacket.sign(signingKeyPacket, literalDataPacket);
|
||||||
packetlist.push(signaturePacket);
|
packetlist.push(signaturePacket);
|
||||||
}
|
}
|
||||||
|
if (signature) {
|
||||||
|
var existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
|
||||||
|
packetlist.concat(existingSigPacketlist);
|
||||||
|
}
|
||||||
|
|
||||||
return new sigModule.Signature(packetlist);
|
return new sigModule.Signature(packetlist);
|
||||||
};
|
};
|
||||||
|
|
|
@ -180,31 +180,35 @@ export function decryptKey({ privateKey, passphrase }) {
|
||||||
* @param {String} filename (optional) a filename for the literal data packet
|
* @param {String} filename (optional) a filename for the literal data packet
|
||||||
* @param {Boolean} armor (optional) if the return values should be ascii armored or the message/signature objects
|
* @param {Boolean} armor (optional) if the return values should be ascii armored or the message/signature objects
|
||||||
* @param {Boolean} detached (optional) if the signature should be detached (if true, signature will be added to returned object)
|
* @param {Boolean} detached (optional) if the signature should be detached (if true, signature will be added to returned object)
|
||||||
|
* @param {Signature} signature (optional) a detached signature to add to the encrypted message
|
||||||
* @return {Promise<Object>} encrypted (and optionally signed message) in the form:
|
* @return {Promise<Object>} encrypted (and optionally signed message) in the form:
|
||||||
* {data: ASCII armored message if 'armor' is true,
|
* {data: ASCII armored message if 'armor' is true,
|
||||||
* message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true}
|
* message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true}
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true, detached=false }) {
|
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true, detached=false, signature=null }) {
|
||||||
checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords);
|
checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords);
|
||||||
|
|
||||||
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported
|
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported
|
||||||
return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor, detached });
|
return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor, detached, signature });
|
||||||
}
|
}
|
||||||
var result = {};
|
var result = {};
|
||||||
return Promise.resolve().then(() => {
|
return Promise.resolve().then(() => {
|
||||||
|
|
||||||
let message = createMessage(data, filename);
|
let message = createMessage(data, filename);
|
||||||
if (privateKeys) { // sign the message only if private keys are specified
|
if (!privateKeys) {
|
||||||
|
privateKeys = [];
|
||||||
|
}
|
||||||
|
if (privateKeys.length || signature) { // sign the message only if private keys or signature is specified
|
||||||
if (detached) {
|
if (detached) {
|
||||||
var signature = message.signDetached(privateKeys);
|
var detachedSignature = message.signDetached(privateKeys, signature);
|
||||||
if (armor) {
|
if (armor) {
|
||||||
result.signature = signature.armor();
|
result.signature = detachedSignature.armor();
|
||||||
} else {
|
} else {
|
||||||
result.signature = signature;
|
result.signature = detachedSignature;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message = message.sign(privateKeys);
|
message = message.sign(privateKeys, signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return message.encrypt(publicKeys, passwords);
|
return message.encrypt(publicKeys, passwords);
|
||||||
|
|
|
@ -689,6 +689,147 @@ describe('OpenPGP.js public api tests', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should encrypt and decrypt/verify with detached signature input and detached flag set for encryption', function(done) {
|
||||||
|
var signOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
privateKeys: privateKey.keys[0],
|
||||||
|
detached: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var encOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
publicKeys: publicKey.keys,
|
||||||
|
detached: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var decOpt = {
|
||||||
|
privateKey: privateKey.keys[0],
|
||||||
|
publicKeys: publicKey.keys[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
openpgp.sign(signOpt).then(function(signed) {
|
||||||
|
encOpt.signature = openpgp.signature.readArmored(signed.signature);
|
||||||
|
return openpgp.encrypt(encOpt);
|
||||||
|
}).then(function(encrypted) {
|
||||||
|
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||||
|
decOpt.signature = openpgp.signature.readArmored(encrypted.signature);
|
||||||
|
return openpgp.decrypt(decOpt);
|
||||||
|
}).then(function(decrypted) {
|
||||||
|
expect(decrypted.data).to.equal(plaintext);
|
||||||
|
expect(decrypted.signatures[0].valid).to.be.true;
|
||||||
|
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
|
||||||
|
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should encrypt and decrypt/verify with detached signature as input and detached flag not set for encryption', function(done) {
|
||||||
|
var privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0];
|
||||||
|
privKeyDE.decrypt(passphrase);
|
||||||
|
|
||||||
|
var pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0];
|
||||||
|
|
||||||
|
var signOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
privateKeys: privKeyDE,
|
||||||
|
detached: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var encOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
publicKeys: publicKey.keys,
|
||||||
|
privateKeys: privateKey.keys[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
var decOpt = {
|
||||||
|
privateKey: privateKey.keys[0],
|
||||||
|
publicKeys: [publicKey.keys[0], pubKeyDE]
|
||||||
|
};
|
||||||
|
|
||||||
|
openpgp.sign(signOpt).then(function(signed) {
|
||||||
|
encOpt.signature = openpgp.signature.readArmored(signed.signature);
|
||||||
|
return openpgp.encrypt(encOpt);
|
||||||
|
}).then(function(encrypted) {
|
||||||
|
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||||
|
return openpgp.decrypt(decOpt);
|
||||||
|
}).then(function(decrypted) {
|
||||||
|
expect(decrypted.data).to.equal(plaintext);
|
||||||
|
expect(decrypted.signatures[0].valid).to.be.true;
|
||||||
|
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
|
||||||
|
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
expect(decrypted.signatures[1].valid).to.be.true;
|
||||||
|
expect(decrypted.signatures[1].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex());
|
||||||
|
expect(decrypted.signatures[1].signature.packets.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to encrypt and decrypt/verify with detached signature input and detached flag set for encryption with wrong public key', function(done) {
|
||||||
|
var signOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
privateKeys: privateKey.keys,
|
||||||
|
detached: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var encOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
publicKeys: publicKey.keys,
|
||||||
|
detached: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var decOpt = {
|
||||||
|
privateKey: privateKey.keys[0],
|
||||||
|
publicKeys: openpgp.key.readArmored(wrong_pubkey).keys
|
||||||
|
};
|
||||||
|
|
||||||
|
openpgp.sign(signOpt).then(function(signed) {
|
||||||
|
encOpt.signature = openpgp.signature.readArmored(signed.signature);
|
||||||
|
return openpgp.encrypt(encOpt);
|
||||||
|
}).then(function(encrypted) {
|
||||||
|
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||||
|
decOpt.signature = openpgp.signature.readArmored(encrypted.signature);
|
||||||
|
return openpgp.decrypt(decOpt);
|
||||||
|
}).then(function(decrypted) {
|
||||||
|
expect(decrypted.data).to.equal(plaintext);
|
||||||
|
expect(decrypted.signatures[0].valid).to.be.null;
|
||||||
|
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
|
||||||
|
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to encrypt and decrypt/verify with detached signature as input and detached flag not set for encryption with wrong public key', function(done) {
|
||||||
|
var signOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
privateKeys: privateKey.keys,
|
||||||
|
detached: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var encOpt = {
|
||||||
|
data: plaintext,
|
||||||
|
publicKeys: publicKey.keys
|
||||||
|
};
|
||||||
|
|
||||||
|
var decOpt = {
|
||||||
|
privateKey: privateKey.keys[0],
|
||||||
|
publicKeys: openpgp.key.readArmored(wrong_pubkey).keys
|
||||||
|
};
|
||||||
|
|
||||||
|
openpgp.sign(signOpt).then(function(signed) {
|
||||||
|
encOpt.signature = openpgp.signature.readArmored(signed.signature);
|
||||||
|
return openpgp.encrypt(encOpt);
|
||||||
|
}).then(function(encrypted) {
|
||||||
|
decOpt.message = openpgp.message.readArmored(encrypted.data);
|
||||||
|
return openpgp.decrypt(decOpt);
|
||||||
|
}).then(function(decrypted) {
|
||||||
|
expect(decrypted.data).to.equal(plaintext);
|
||||||
|
expect(decrypted.signatures[0].valid).to.be.null;
|
||||||
|
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
|
||||||
|
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should fail to verify decrypted data with wrong public pgp key', function(done) {
|
it('should fail to verify decrypted data with wrong public pgp key', function(done) {
|
||||||
var encOpt = {
|
var encOpt = {
|
||||||
data: plaintext,
|
data: plaintext,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user