Added PGP Sign/Verify operations
This commit is contained in:
parent
6a67fe09de
commit
7554cbda72
@ -3951,12 +3951,20 @@ const OperationConfig = {
|
|||||||
"PGP Encrypt": {
|
"PGP Encrypt": {
|
||||||
module: "PGP",
|
module: "PGP",
|
||||||
manualBake: true,
|
manualBake: true,
|
||||||
description: "",
|
description: [
|
||||||
|
"Input: the message you want to encrypt.",
|
||||||
|
"<br><br>",
|
||||||
|
"Arguments: the ASCII-armoured PGP public key of the recipient.",
|
||||||
|
"<br><br>",
|
||||||
|
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||||
|
"<br><br>",
|
||||||
|
"This function relies on kbpgp.js for the implementation of PGP.",
|
||||||
|
].join("\n"),
|
||||||
inputType: "string",
|
inputType: "string",
|
||||||
outputType: "string",
|
outputType: "string",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "Public key",
|
name: "Public key of recipient",
|
||||||
type: "text",
|
type: "text",
|
||||||
value: ""
|
value: ""
|
||||||
},
|
},
|
||||||
@ -3965,20 +3973,98 @@ const OperationConfig = {
|
|||||||
"PGP Decrypt": {
|
"PGP Decrypt": {
|
||||||
module: "PGP",
|
module: "PGP",
|
||||||
manualBake: true,
|
manualBake: true,
|
||||||
description: "",
|
description: [
|
||||||
|
"Input: the ASCII-armoured PGP message you want to decrypt.",
|
||||||
|
"<br><br>",
|
||||||
|
"Arguments: the ASCII-armoured PGP private key of the recipient, ",
|
||||||
|
"(and the private key password if necessary).",
|
||||||
|
"<br><br>",
|
||||||
|
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||||
|
"<br><br>",
|
||||||
|
"This function relies on kbpgp.js for the implementation of PGP.",
|
||||||
|
].join("\n"),
|
||||||
inputType: "string",
|
inputType: "string",
|
||||||
outputType: "string",
|
outputType: "string",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
name: "Private key",
|
name: "Private key of recipient",
|
||||||
type: "text",
|
type: "text",
|
||||||
value: ""
|
value: ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Passphrase",
|
name: "Private key passphrase",
|
||||||
|
type: "string",
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PGP Sign": {
|
||||||
|
module: "PGP",
|
||||||
|
manualBake: true,
|
||||||
|
description: [
|
||||||
|
"Input: the cleartext you want to sign.",
|
||||||
|
"<br><br>",
|
||||||
|
"Arguments: the ASCII-armoured private key of the signer (plus the private key password if necessary)",
|
||||||
|
"and the ASCII-armoured PGP public key of the recipient.",
|
||||||
|
"<br><br>",
|
||||||
|
"This operation uses PGP to produce an encrypted digital signature.",
|
||||||
|
"<br><br>",
|
||||||
|
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||||
|
"<br><br>",
|
||||||
|
"This function relies on kbpgp.js for the implementation of PGP.",
|
||||||
|
].join("\n"),
|
||||||
|
inputType: "string",
|
||||||
|
outputType: "string",
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "Private key of signer",
|
||||||
type: "text",
|
type: "text",
|
||||||
value: ""
|
value: ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Private key passphrase",
|
||||||
|
type: "string",
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Public key of recipient",
|
||||||
|
type: "text",
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"PGP Verify": {
|
||||||
|
module: "PGP",
|
||||||
|
description: [
|
||||||
|
"Input: the ASCII-armoured encrypted PGP message you want to verify.",
|
||||||
|
"<br><br>",
|
||||||
|
"Arguments: the ASCII-armoured PGP public key of the signer, ",
|
||||||
|
"the ASCII-armoured private key of the recipient (and the private key password if necessary).",
|
||||||
|
"<br><br>",
|
||||||
|
"This operation uses PGP to decrypt and verify an encrypted digital signature.",
|
||||||
|
"<br><br>",
|
||||||
|
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
|
||||||
|
"<br><br>",
|
||||||
|
"This function relies on kbpgp.js for the implementation of PGP.",
|
||||||
|
].join("\n"),
|
||||||
|
inputType: "string",
|
||||||
|
outputType: "string",
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "Public key of signer",
|
||||||
|
type: "text",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Private key of recipient",
|
||||||
|
type: "text",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Private key password",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import PGP from "../../operations/PGP.js";
|
|||||||
* - kbpgp
|
* - kbpgp
|
||||||
*
|
*
|
||||||
* @author tlwr [toby@toby.codes]
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
* @copyright Crown Copyright 2017
|
* @copyright Crown Copyright 2017
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -17,6 +18,8 @@ OpModules.PGP = {
|
|||||||
"Generate PGP Key Pair": PGP.runGenerateKeyPair,
|
"Generate PGP Key Pair": PGP.runGenerateKeyPair,
|
||||||
"PGP Encrypt": PGP.runEncrypt,
|
"PGP Encrypt": PGP.runEncrypt,
|
||||||
"PGP Decrypt": PGP.runDecrypt,
|
"PGP Decrypt": PGP.runDecrypt,
|
||||||
|
"PGP Sign": PGP.runSign,
|
||||||
|
"PGP Verify": PGP.runVerify,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OpModules;
|
export default OpModules;
|
||||||
|
@ -11,6 +11,7 @@ const KEY_TYPES = ["RSA", "ECC"];
|
|||||||
* PGP operations.
|
* PGP operations.
|
||||||
*
|
*
|
||||||
* @author tlwr [toby@toby.codes]
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [matt@artemisbot.uk]
|
||||||
* @copyright Crown Copyright 2016
|
* @copyright Crown Copyright 2016
|
||||||
* @license Apache-2.0
|
* @license Apache-2.0
|
||||||
*
|
*
|
||||||
@ -21,10 +22,12 @@ const PGP = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate PGP Key Size
|
* Validate PGP Key Size
|
||||||
|
*
|
||||||
|
* @private
|
||||||
* @param {string} keySize
|
* @param {string} keySize
|
||||||
* @returns {Integer}
|
* @returns {Integer}
|
||||||
*/
|
*/
|
||||||
validateKeySize(keySize, keyType) {
|
_validateKeySize(keySize, keyType) {
|
||||||
if (KEY_SIZES.indexOf(keySize) < 0) {
|
if (KEY_SIZES.indexOf(keySize) < 0) {
|
||||||
throw `Invalid key size ${keySize}, must be in ${JSON.stringify(KEY_SIZES)}`;
|
throw `Invalid key size ${keySize}, must be in ${JSON.stringify(KEY_SIZES)}`;
|
||||||
}
|
}
|
||||||
@ -46,10 +49,12 @@ const PGP = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get size of subkey
|
* Get size of subkey
|
||||||
|
*
|
||||||
|
* @private
|
||||||
* @param {Integer} keySize
|
* @param {Integer} keySize
|
||||||
* @returns {Integer}
|
* @returns {Integer}
|
||||||
*/
|
*/
|
||||||
getSubkeySize(keySize) {
|
_getSubkeySize(keySize) {
|
||||||
return {
|
return {
|
||||||
1024: 1024,
|
1024: 1024,
|
||||||
2048: 1024,
|
2048: 1024,
|
||||||
@ -64,18 +69,65 @@ const PGP = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate PGP Key Type
|
* Validate PGP Key Type
|
||||||
|
*
|
||||||
|
* @private
|
||||||
* @param {string} keyType
|
* @param {string} keyType
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
validateKeyType(keyType) {
|
_validateKeyType(keyType) {
|
||||||
if (KEY_TYPES.indexOf(keyType) >= 0) return keyType.toLowerCase();
|
if (KEY_TYPES.indexOf(keyType) >= 0) return keyType.toLowerCase();
|
||||||
throw `Invalid key type ${keyType}, must be in ${JSON.stringify(KEY_TYPES)}`;
|
throw `Invalid key type ${keyType}, must be in ${JSON.stringify(KEY_TYPES)}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import private key and unlock if necessary
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} privateKey
|
||||||
|
* @param {string} [passphrase]
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
async _importPrivateKey (privateKey, passphrase) {
|
||||||
|
try {
|
||||||
|
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||||
|
armored: privateKey,
|
||||||
|
});
|
||||||
|
if (key.is_pgp_locked() && passphrase) {
|
||||||
|
if (passphrase) {
|
||||||
|
await promisify(key.unlock_pgp, key)({
|
||||||
|
passphrase
|
||||||
|
});
|
||||||
|
} else if (!passphrase) {
|
||||||
|
throw "Did not provide passphrase with locked private key.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
} catch (err) {
|
||||||
|
throw `Could not import private key: ${err}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import public key
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} publicKey
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
async _importPublicKey (publicKey) {
|
||||||
|
try {
|
||||||
|
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
||||||
|
armored: publicKey,
|
||||||
|
});
|
||||||
|
return key;
|
||||||
|
} catch (err) {
|
||||||
|
throw `Could not import public key: ${err}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate PGP Key Pair operation.
|
* Generate PGP Key Pair operation.
|
||||||
*
|
*
|
||||||
* @author tlwr [toby@toby.codes]
|
|
||||||
* @param {string} input
|
* @param {string} input
|
||||||
* @param {Object[]} args
|
* @param {Object[]} args
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
@ -87,8 +139,8 @@ const PGP = {
|
|||||||
name = args[3],
|
name = args[3],
|
||||||
email = args[4];
|
email = args[4];
|
||||||
|
|
||||||
keyType = PGP.validateKeyType(keyType);
|
keyType = PGP._validateKeyType(keyType);
|
||||||
keySize = PGP.validateKeySize(keySize, keyType);
|
keySize = PGP._validateKeySize(keySize, keyType);
|
||||||
|
|
||||||
let userIdentifier = "";
|
let userIdentifier = "";
|
||||||
if (name) userIdentifier += name;
|
if (name) userIdentifier += name;
|
||||||
@ -109,11 +161,11 @@ const PGP = {
|
|||||||
expire_in: 0
|
expire_in: 0
|
||||||
},
|
},
|
||||||
subkeys: [{
|
subkeys: [{
|
||||||
nbits: PGP.getSubkeySize(keySize),
|
nbits: PGP._getSubkeySize(keySize),
|
||||||
flags: kbpgp.const.openpgp.sign_data,
|
flags: kbpgp.const.openpgp.sign_data,
|
||||||
expire_in: 86400 * 365 * 8
|
expire_in: 86400 * 365 * 8
|
||||||
}, {
|
}, {
|
||||||
nbits: PGP.getSubkeySize(keySize),
|
nbits: PGP._getSubkeySize(keySize),
|
||||||
flags: kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
|
flags: kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
|
||||||
expire_in: 86400 * 365 * 2
|
expire_in: 86400 * 365 * 2
|
||||||
}],
|
}],
|
||||||
@ -134,6 +186,13 @@ const PGP = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PGP Encrypt operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
async runEncrypt(input, args) {
|
async runEncrypt(input, args) {
|
||||||
let plaintextMessage = input,
|
let plaintextMessage = input,
|
||||||
plainPubKey = args[0];
|
plainPubKey = args[0];
|
||||||
@ -160,31 +219,21 @@ const PGP = {
|
|||||||
return encryptedMessage.toString();
|
return encryptedMessage.toString();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PGP Decrypt operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
async runDecrypt(input, args) {
|
async runDecrypt(input, args) {
|
||||||
let encryptedMessage = input,
|
let encryptedMessage = input,
|
||||||
privateKey = args[0],
|
privateKey = args[0],
|
||||||
passphrase = args[1],
|
passphrase = args[1],
|
||||||
keyring = new kbpgp.keyring.KeyRing();
|
keyring = new kbpgp.keyring.KeyRing();
|
||||||
|
|
||||||
let key, plaintextMessage;
|
let plaintextMessage;
|
||||||
|
const key = await PGP._importPrivateKey(privateKey, passphrase);
|
||||||
try {
|
|
||||||
key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
|
|
||||||
armored: privateKey,
|
|
||||||
});
|
|
||||||
if (key.is_pgp_locked() && passphrase) {
|
|
||||||
if (passphrase) {
|
|
||||||
await promisify(key.unlock_pgp, key)({
|
|
||||||
passphrase
|
|
||||||
});
|
|
||||||
} else if (!passphrase) {
|
|
||||||
throw "Did not provide passphrase with locked private key.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
throw `Could not import private key: ${err}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyring.add_key_manager(key);
|
keyring.add_key_manager(key);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -198,6 +247,93 @@ const PGP = {
|
|||||||
|
|
||||||
return plaintextMessage.toString();
|
return plaintextMessage.toString();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PGP Sign Message operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
async runSign(input, args) {
|
||||||
|
let message = input,
|
||||||
|
privateKey = args[0],
|
||||||
|
passphrase = args[1],
|
||||||
|
publicKey = args[2];
|
||||||
|
|
||||||
|
let signedMessage;
|
||||||
|
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
|
||||||
|
const pubKey = await PGP._importPublicKey(publicKey);
|
||||||
|
|
||||||
|
try {
|
||||||
|
signedMessage = await promisify(kbpgp.box)({
|
||||||
|
msg: message,
|
||||||
|
encrypt_for: pubKey,
|
||||||
|
sign_with: privKey
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
throw `Couldn't sign message: ${err}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return signedMessage;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PGP Verify Message operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
async runVerify(input, args) {
|
||||||
|
let signedMessage = input,
|
||||||
|
publicKey = args[0],
|
||||||
|
privateKey = args[1],
|
||||||
|
passphrase = args[2],
|
||||||
|
keyring = new kbpgp.keyring.KeyRing();
|
||||||
|
|
||||||
|
let unboxedLiterals;
|
||||||
|
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
|
||||||
|
const pubKey = await PGP._importPublicKey(publicKey);
|
||||||
|
keyring.add_key_manager(privKey);
|
||||||
|
keyring.add_key_manager(pubKey);
|
||||||
|
|
||||||
|
try {
|
||||||
|
unboxedLiterals = await promisify(kbpgp.unbox)({
|
||||||
|
armored: signedMessage,
|
||||||
|
keyfetch: keyring
|
||||||
|
});
|
||||||
|
const ds = unboxedLiterals[0].get_data_signer();
|
||||||
|
if (ds) {
|
||||||
|
const km = ds.get_key_manager();
|
||||||
|
if (km) {
|
||||||
|
const signer = km.get_userids_mark_primary()[0].components;
|
||||||
|
let text = "Signed by ";
|
||||||
|
if (signer.email || signer.username || signer.comment) {
|
||||||
|
if (signer.username) {
|
||||||
|
text += `${signer.username} `;
|
||||||
|
}
|
||||||
|
if (signer.comment) {
|
||||||
|
text += `${signer.comment} `;
|
||||||
|
}
|
||||||
|
if (signer.email) {
|
||||||
|
text += `<${signer.email}>`;
|
||||||
|
}
|
||||||
|
text += "\n";
|
||||||
|
}
|
||||||
|
text += [
|
||||||
|
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
|
||||||
|
`Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
|
||||||
|
"----------------------------------"
|
||||||
|
].join("\n");
|
||||||
|
text += unboxedLiterals.toString();
|
||||||
|
return text.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw `Couldn't verify message: ${err}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PGP;
|
export default PGP;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user