diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js index fa8a4efe..3b53eeb4 100644 --- a/lang/js/src/Errors.js +++ b/lang/js/src/Errors.js @@ -1,129 +1,133 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ const err_list = { // Connection 'CONN_NO_CONNECT': { msg:'Connection with the nativeMessaging host could not be' + ' established.', type: 'error' }, 'CONN_EMPTY_GPG_ANSWER':{ msg: 'The nativeMessaging answer was empty.', type: 'error' }, 'CONN_TIMEOUT': { msg: 'A connection timeout was exceeded.', type: 'error' }, 'CONN_UNEXPECTED_ANSWER': { msg: 'The answer from gnupg was not as expected.', type: 'error' }, 'CONN_ALREADY_CONNECTED':{ msg: 'A connection was already established.', type: 'warning' }, // Message/Data 'MSG_INCOMPLETE': { msg: 'The Message did not match the minimum requirements for' + ' the interaction.', type: 'error' }, 'MSG_EMPTY' : { msg: 'The Message is empty.', type: 'error' }, 'MSG_WRONG_OP': { msg: 'The operation requested could not be found', type: 'error' }, 'MSG_NO_KEYS' : { msg: 'There were no valid keys provided.', type: 'warning' }, 'MSG_NOT_A_FPR': { msg: 'The String is not an accepted fingerprint', type: 'warning' }, 'KEY_INVALID': { msg:'Key object is invalid', type: 'error' }, 'KEY_NOKEY': { msg:'This key does not exist in GPG', type: 'error' }, + 'KEY_NO_INIT': { + msg:'This property has not been retrieved yet from GPG', + type: 'error' + } // generic 'PARAM_WRONG':{ msg: 'Invalid parameter was found', type: 'error' }, 'PARAM_IGNORED': { msg: 'An parameter was set that has no effect in gpgmejs', type: 'warning' }, 'GENERIC_ERROR': { msg: 'Unspecified error', type: 'error' } }; /** * Checks the given error code and returns an error object with some * information about meaning and origin * @param {*} code Error code. Should be in err_list or 'GNUPG_ERROR' * @param {*} info Error message passed through if code is 'GNUPG_ERROR' */ export function gpgme_error(code = 'GENERIC_ERROR', info){ if (err_list.hasOwnProperty(code)){ if (err_list[code].type === 'error'){ return new GPGME_Error(code); } if (err_list[code].type === 'warning'){ console.warn(code + ': ' + err_list[code].msg); } return null; } else if (code === 'GNUPG_ERROR'){ return new GPGME_Error(code, info); } else { return new GPGME_Error('GENERIC_ERROR'); } } class GPGME_Error extends Error{ constructor(code, msg=''){ if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){ super(msg); } else if (err_list.hasOwnProperty(code)){ super(err_list[code].msg); } else { super(err_list['GENERIC_ERROR'].msg); } this.code = code || 'GENERIC_ERROR'; } set code(value){ this._code = value; } get code(){ return this._code; } } \ No newline at end of file diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index f2a16b42..454b1912 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -1,455 +1,549 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ /** * The key class allows to query the information defined in gpgme Key Objects * (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html) * * This is a stub, as the gpgme-json side is not yet implemented * */ import { isFingerprint, isLongId } from './Helpers' import { gpgme_error } from './Errors' import { createMessage } from './Message'; import { permittedOperations } from './permittedOperations'; /** * Validates the fingerprint. * @param {String} fingerprint */ export function createKey(fingerprint){ if (!isFingerprint(fingerprint)){ return gpgme_error('PARAM_WRONG'); } else return new GPGME_Key(fingerprint); } /** * Representing the Keys as stored in GPG */ export class GPGME_Key { constructor(fingerprint){ this.fingerprint = fingerprint; } set fingerprint(fpr){ if (isFingerprint(fpr) === true) { if (this._data === undefined) { this._data = {fingerprint: fpr}; } else { if (this._data.fingerprint === undefined){ this._data.fingerprint = fpr; } } } } get fingerprint(){ if (!this._data || !this._data.fingerprint){ return gpgme_error('KEY_INVALID'); } return this._data.fingerprint; } /** * * @param {Object} data Bulk set data for this key, with the Object as sent * by gpgme-json. * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have * been set */ setKeyData(data){ if (this._data === undefined) { this._data = {}; } if ( typeof(data) !== 'object') { return gpgme_error('KEY_INVALID'); } if (!this._data.fingerprint && isFingerprint(data.fingerprint)){ if (data.fingerprint !== this.fingerprint){ return gpgme_error('KEY_INVALID'); } this._data.fingerprint = data.fingerprint; } else if (this._data.fingerprint !== data.fingerprint){ return gpgme_error('KEY_INVALID'); } - - let booleans = ['expired', 'disabled','invalid','can_encrypt', - 'can_sign','can_certify','can_authenticate','secret', - 'is_qualified']; - for (let b =0; b < booleans.length; b++) { - if ( - !data.hasOwnProperty(booleans[b]) || - typeof(data[booleans[b]]) !== 'boolean' - ){ + let dataKeys = Object.keys(data); + for (let i=0; i< dataKeys.length; i++){ + if (!validKeyProperties.hasOwnProperty(dataKeys[i])){ return gpgme_error('KEY_INVALID'); } - this._data[booleans[b]] = data[booleans[b]]; - } - if (typeof(data.protocol) !== 'string'){ - return gpgme_error('KEY_INVALID'); - } - // TODO check valid protocols? - this._data.protocol = data.protocol; - - if (typeof(data.owner_trust) !== 'string'){ - return gpgme_error('KEY_INVALID'); - } - // TODO check valid values? - this._data.owner_trust = data.owner_trust; - - // TODO: what about origin ? - if (!Number.isInteger(data.last_update)){ - return gpgme_error('KEY_INVALID'); - } - this._data.last_update = data.last_update; - - this._data.subkeys = []; - if (data.hasOwnProperty('subkeys')){ - if (!Array.isArray(data.subkeys)){ + if (validKeyProperties[dataKeys[i]](data[dataKeys[i]]) !== true ){ return gpgme_error('KEY_INVALID'); } - for (let i=0; i< data.subkeys.length; i++) { - this._data.subkeys.push( - new GPGME_Subkey(data.subkeys[i])); - } - } - - this._data.userids = []; - if (data.hasOwnProperty('userids')){ - if (!Array.isArray(data.userids)){ - return gpgme_error('KEY_INVALID'); - } - for (let i=0; i< data.userids.length; i++) { - this._data.userids.push( - new GPGME_UserId(data.userids[i])); + switch (dataKeys[i]){ + case 'subkeys': + this._data.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + this._data.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); + } + break; + case 'userids': + this._data.userids = []; + for (let i=0; i< data.userids.length; i++) { + this._data.userids.push( + new GPGME_UserId(data.userids[i])); + } + break; + default: + this._data[dataKeys[i]] = data[dataKeys[i]]; } } return this; } /** * Query any property of the Key list * (TODO: armor not in here, might be unexpected) * @param {String} property Key property to be retreived * @param {*} cached (optional) if false, the data will be directly queried * from gnupg. * @returns {*|Promise<*>} the value, or if not cached, a Promise * resolving on the value */ get(property, cached=true) { if (cached === false) { let me = this; return new Promise(function(resolve, reject) { - if (property === 'armor'){ + if (!validKeyProperties.hasOwnProperty(property)){ + reject('PARAM_WRONG'); + } else if (property === 'armored'){ resolve(me.getArmor()); } else if (property === 'hasSecret'){ resolve(me.getHasSecret()); } else { me.refreshKey().then(function(key){ resolve(key.get(property, true)); }, function(error){ reject(error); }); } }); - } else { - if (!this._data.hasOwnProperty(property)){ + } else { + if (!validKeyProperties.hasOwnProperty(property)){ return gpgme_error('PARAM_WRONG'); + } + if (!this._data.hasOwnProperty(property)){ + return gpgme_error('KEY_NO_INIT'); } else { return (this._data[property]); } } } + get armored () { + return this.get('armored'); + //TODO exception if empty + } + /** * Reloads the Key from gnupg */ refreshKey() { let me = this; return new Promise(function(resolve, reject) { if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); msg.setParameter('sigs', true); msg.setParameter('keys', me._data.fingerprint); msg.post().then(function(result){ if (result.keys.length === 1){ me.setKeyData(result.keys[0]); resolve(me); } else { reject(gpgme_error('KEY_NOKEY')); } }, function (error) { reject(gpgme_error('GNUPG_ERROR'), error); }) }); } - /** - * Get the armored block of the non- secret parts of the Key. - * @returns {String} the armored Key block. - * Notice that this may be outdated cached info. Use the async getArmor if - * you need the most current info - */ - - // get armor(){ TODO } - /** * Query the armored block of the non- secret parts of the Key directly * from gpg. * @returns {Promise} */ getArmor(){ let me = this; return new Promise(function(resolve, reject) { if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('export'); msg.setParameter('armor', true); msg.setParameter('keys', me._data.fingerprint); msg.post().then(function(result){ me._data.armor = result.data; resolve(result.data); }, function(error){ reject(error); }); }); } getHasSecret(){ let me = this; return new Promise(function(resolve, reject) { if (!me._data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); msg.setParameter('keys', me._data.fingerprint); msg.setParameter('secret', true); msg.post().then(function(result){ me._data.hasSecret = null; if (result.keys === undefined || result.keys.length < 1) { me._data.hasSecret = false; resolve(false); } else if (result.keys.length === 1){ let key = result.keys[0]; if (!key.subkeys){ me._data.hasSecret = false; resolve(false); } else { for (let i=0; i < key.subkeys.length; i++) { if (key.subkeys[i].secret === true) { me._data.hasSecret = true; resolve(true); break; } if (i === (key.subkeys.length -1)) { me._data.hasSecret = false; resolve(false); } } } } else { reject(gpgme_error('CONN_UNEXPECTED_ANSWER')) } }, function(error){ }) }); } + + /** + * Convenience function to be directly used as properties of the Key + * Notice that these rely on cached info and may be outdated. Use the async + * get(property, false) if you need the most current info + */ + + /** + * @returns {String} The armored public Key block + */ + get armored(){ + return this.get('armored', true); + } + + /** + * @returns {Boolean} If the key is considered a "private Key", + * i.e. owns a secret subkey. + */ + get hasSecret(){ + return this.get('hasSecret', true); + } + + /** + * Deletes the public Key from the GPG Keyring. Note that a deletion of a + * secret key is not supported by the native backend. + * @returns {Boolean} Success if key was deleted, rejects with a GPG error + * otherwise + */ + delete(){ + let me = this; + return new Promise(function(resolve, reject){ + if (!me._data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('delete'); + msg.setParameter('key', me._data.fingerprint); + msg.post().then(function(result){ + resolve(result.success); + }, function(error){ + reject(error); + }) + }); + } } /** * The subkeys of a Key. Currently, they cannot be refreshed separately */ class GPGME_Subkey { constructor(data){ let keys = Object.keys(data); for (let i=0; i< keys.length; i++) { this.setProperty(keys[i], data[keys[i]]); } } setProperty(property, value){ if (!this._data){ this._data = {}; } if (validSubKeyProperties.hasOwnProperty(property)){ if (validSubKeyProperties[property](value) === true) { this._data[property] = value; } } } /** * * @param {String} property Information to request * @returns {String | Number} * TODO: date properties are numbers with Date in seconds */ get(property) { if (this._data.hasOwnProperty(property)){ return (this._data[property]); } } } class GPGME_UserId { constructor(data){ let keys = Object.keys(data); for (let i=0; i< keys.length; i++) { this.setProperty(keys[i], data[keys[i]]); } } setProperty(property, value){ if (!this._data){ this._data = {}; } if (validUserIdProperties.hasOwnProperty(property)){ if (validUserIdProperties[property](value) === true) { this._data[property] = value; } } } /** * * @param {String} property Information to request * @returns {String | Number} * TODO: date properties are numbers with Date in seconds */ get(property) { if (this._data.hasOwnProperty(property)){ return (this._data[property]); } } } const validUserIdProperties = { 'revoked': function(value){ return typeof(value) === 'boolean'; }, 'invalid': function(value){ return typeof(value) === 'boolean'; }, 'uid': function(value){ if (typeof(value) === 'string' || value === ''){ return true;; } return false; }, 'validity': function(value){ if (typeof(value) === 'string'){ return true;; } return false; }, 'name': function(value){ if (typeof(value) === 'string' || value === ''){ return true;; } return false; }, 'email': function(value){ if (typeof(value) === 'string' || value === ''){ return true;; } return false; }, 'address': function(value){ if (typeof(value) === 'string' || value === ''){ return true;; } return false; }, 'comment': function(value){ if (typeof(value) === 'string' || value === ''){ return true;; } return false; }, 'origin': function(value){ return Number.isInteger(value); }, 'last_update': function(value){ return Number.isInteger(value); } }; const validSubKeyProperties = { 'invalid': function(value){ return typeof(value) === 'boolean'; }, 'can_encrypt': function(value){ return typeof(value) === 'boolean'; }, 'can_sign': function(value){ return typeof(value) === 'boolean'; }, 'can_certify': function(value){ return typeof(value) === 'boolean'; }, 'can_authenticate': function(value){ return typeof(value) === 'boolean'; }, 'secret': function(value){ return typeof(value) === 'boolean'; }, 'is_qualified': function(value){ return typeof(value) === 'boolean'; }, 'is_cardkey': function(value){ return typeof(value) === 'boolean'; }, 'is_de_vs': function(value){ return typeof(value) === 'boolean'; }, 'pubkey_algo_name': function(value){ return typeof(value) === 'string'; // TODO: check against list of known?[''] }, 'pubkey_algo_string': function(value){ return typeof(value) === 'string'; // TODO: check against list of known?[''] }, 'keyid': function(value){ return isLongId(value); }, 'pubkey_algo': function(value) { return (Number.isInteger(value) && value >= 0); }, 'length': function(value){ return (Number.isInteger(value) && value > 0); }, 'timestamp': function(value){ return (Number.isInteger(value) && value > 0); }, 'expires': function(value){ return (Number.isInteger(value) && value > 0); } } +const validKeyProperties = { + //TODO better validation? + 'fingerprint': function(value){ + return isFingerprint(value); + }, + 'armored': function(value){ + return typeof(value === 'string'); + }, + 'revoked': function(value){ + return typeof(value) === 'boolean'; + }, + 'expired': function(value){ + return typeof(value) === 'boolean'; + }, + 'disabled': function(value){ + return typeof(value) === 'boolean'; + }, + 'invalid': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_encrypt': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_sign': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_certify': function(value){ + return typeof(value) === 'boolean'; + }, + 'can_authenticate': function(value){ + return typeof(value) === 'boolean'; + }, + 'secret': function(value){ + return typeof(value) === 'boolean'; + }, + 'is_qualified': function(value){ + return typeof(value) === 'boolean'; + }, + 'protocol': function(value){ + return typeof(value) === 'string'; + //TODO check for implemented ones + }, + 'issuer_serial': function(value){ + return typeof(value) === 'string'; + }, + 'issuer_name': function(value){ + return typeof(value) === 'string'; + }, + 'chain_id': function(value){ + return typeof(value) === 'string'; + }, + 'owner_trust': function(value){ + return typeof(value) === 'string'; + }, + 'last_update': function(value){ + return (Number.isInteger(value)); + //TODO undefined/null possible? + }, + 'origin': function(value){ + return (Number.isInteger(value)); + }, + 'subkeys': function(value){ + return (Array.isArray(value)); + }, + 'userids': function(value){ + return (Array.isArray(value)); + }, + 'tofu': function(value){ + return (Array.isArray(value)); + }, + 'hasSecret': function(value){ + return typeof(value) === 'boolean'; + } + +} diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 7e13dfe2..9081cbe9 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -1,81 +1,101 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ import {createMessage} from './Message' import {GPGME_Key, createKey} from './Key' -import { isFingerprint } from './Helpers'; +import { isFingerprint, toKeyIdArray } from './Helpers'; import { gpgme_error } from './Errors'; export class GPGME_Keyring { constructor(){ } /** * @param {String} pattern (optional) pattern A pattern to search for, * in userIds or KeyIds * @param {Boolean} prepare_sync (optional, default true) if set to true, * Key.armor and Key.hasSecret will be called, so they can be used * inmediately. This allows for full synchronous use. If set to false, * these will initially only be available as Promises in getArmor() and * getHasSecret() * @returns {Promise.>} * */ getKeys(pattern, prepare_sync){ let me = this; return new Promise(function(resolve, reject) { let msg; msg = createMessage('keylist'); - if (pattern && typeof(pattern) === 'string'){ + if (pattern !== undefined){ msg.setParameter('keys', pattern); } - msg.setParameter('sigs', true); //TODO do we need this? + msg.setParameter('sigs', true); msg.post().then(function(result){ let resultset = []; let promises = []; // TODO check if result.key is not empty for (let i=0; i< result.keys.length; i++){ let k = createKey(result.keys[i].fingerprint, me); k.setKeyData(result.keys[i]); if (prepare_sync === true){ promises.push(k.getArmor()); promises.push(k.getHasSecret()); } resultset.push(k); } if (promises.length > 0) { Promise.all(promises).then(function (res){ resolve(resultset); }, function(error){ reject(error); }); } }, function(error){ reject(error); }); }); } -// TODO: - // deleteKey(key, include_secret=false) - // getKeysArmored(pattern) //just dump all armored keys + + /** + * Fetches the armored public Key blocks for all Keys matchin the pattern + * (if no pattern is given, fetches all known to gnupg) + * @param {String|Array} pattern (optional) + * @returns {Promise} Armored Key blocks + */ + getKeysArmored(pattern) { + if (pattern) + return new Promise(function(resolve, reject) { + let msg = createMessage('export'); + msg.setParameter('armor', true); + if (pattern !== undefined){ + msg.setParameter('keys', pattern); + } + msg.post().then(function(result){ + resolve(result.data); + }, function(error){ + reject(error); + }); + } + // getDefaultKey() Big TODO // importKeys(armoredKeys) + // generateKey --> TODO (Andre noch anfragen!) }; diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index 88a91a60..39f6a2f0 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -1,199 +1,204 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ import {GPGME_Message, createMessage} from './Message' import {toKeyIdArray} from "./Helpers" import { gpgme_error } from "./Errors" import { GPGME_Keyring } from "./Keyring"; export class GpgME { /** * initializes GpgME by opening a nativeMessaging port * TODO: add configuration */ constructor(config){ //TODO config not parsed this._config = config; } set Keyring(keyring){ if (keyring && keyring instanceof GPGME_Keyring){ this._Keyring = keyring; } } get Keyring(){ if (!this._Keyring){ this._Keyring = new GPGME_Keyring; } return this._Keyring; } /** - * @param {String} data text/data to be encrypted as String + * Encrypt (and optionally sign) a Message + * @param {String|Object} data text/data to be encrypted as String. Also accepts Objects with a getText method * @param {GPGME_Key|String|Array|Array} publicKeys Keys used to encrypt the message + * @param {GPGME_Key|String|Array|Array} secretKeys (optional) Keys used to sign the message + * @param {Boolean} base64 (optional) The data is already considered to be in base64 encoding + * @param {Boolean} armor (optional) Request the output as armored block * @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message + * @param {Object} additional use additional gpg options (refer to src/permittedOperations) + * @returns {Promise} Encrypted message: + * data: The encrypted message + * base64: Boolean indicating whether data is base64 encoded. + * @async */ - encrypt(data, publicKeys, base64=false, wildcard=false){ - + encrypt(data, publicKeys, secretKeys, base64=false, armor=true, + wildcard=false, additional = {} + ){ let msg = createMessage('encrypt'); if (msg instanceof Error){ return Promise.reject(msg) } - // TODO temporary - msg.setParameter('armor', true); + msg.setParameter('armor', armor); msg.setParameter('always-trust', true); if (base64 === true) { msg.setParameter('base64', true); } let pubkeys = toKeyIdArray(publicKeys); msg.setParameter('keys', pubkeys); + let sigkeys = toKeyIdArray(secretKeys); + if (sigkeys.length > 0) { + msg.setParameter('signing_keys', sigkeys); + } putData(msg, data); if (wildcard === true){ msg.setParameter('throw-keyids', true); }; + if (additional){ + let additional_Keys = Object.keys(additional); + for (let k = 0; k < additional_Keys.length; k++) { + msg.setParameter(additional_Keys[k], + additional[additional_Keys[k]]); + } + } if (msg.isComplete === true){ return msg.post(); } else { return Promise.reject(gpgme_error('MSG_INCOMPLETE')); } } /** - * @param {String} data TODO base64? Message with the encrypted data - * @param {Boolean} base64 (optional) Response should stay base64 + * Decrypt a Message + * @param {String|Object} data text/data to be decrypted. Accepts Strings and Objects with a getText method + * @param {Boolean} base64 (optional) Response is expected to be base64 encoded * @returns {Promise} decrypted message: data: The decrypted data. This may be base64 encoded. base64: Boolean indicating whether data is base64 encoded. mime: A Boolean indicating whether the data is a MIME object. - info: An optional object with extra information. + signatures: Array of signature Objects TODO not yet implemented. + // should be an object that can tell if all signatures are valid etc. * @async */ - decrypt(data, base64=false){ if (data === undefined){ return Promise.reject(gpgme_error('MSG_EMPTY')); } let msg = createMessage('decrypt'); if (base64 === true){ msg.expected = 'base64'; } if (msg instanceof Error){ return Promise.reject(msg); } putData(msg, data); return msg.post(); - } - sign(data, keys, mode='clearsign', base64=false) { //sender + /** + * Sign a Message + * @param {String|Object} data text/data to be decrypted. Accepts Strings and Objects with a gettext methos + * @param {GPGME_Key|String|Array|Array} keys The key/keys to use for signing + * @param {*} mode The signing mode. Currently supported: + * 'clearsign': (default) The Message is embedded into the signature + * 'detached': The signature is stored separately + * @param {*} base64 input is considered base64 + * @returns {Promise} + * data: The resulting data. In clearsign mode this includes the signature + * signature: The detached signature (if in detached mode) + * @async + */ + sign(data, keys, mode='clearsign', base64=false) { if (data === undefined){ return Promise.reject(gpgme_error('MSG_EMPTY')); } let key_arr = toKeyIdArray(keys); if (key_arr.length === 0){ return Promise.reject(gpgme_error('MSG_NO_KEYS')); } let msg = createMessage('sign'); msg.setParameter('keys', key_arr); if (base64 === true){ msg.setParameter('base64', true); } msg.setParameter('mode', mode); putData(msg, data); if (mode === 'detached') { msg.expected = 'base64'; } let me = this; return new Promise(function(resolve,reject) { msg.post().then( function(message) { if (mode === 'clearsign'){ resolve({ data: message.data} ); } else if (mode === 'detached') { resolve({ data: data, signature: message.data }); } }, function(error){ reject(error); }) }); } - - deleteKey(key, delete_secret = false, no_confirm = false){ - return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); - let msg = createMessage('deletekey'); - if (msg instanceof Error){ - return Promise.reject(msg); - } - let key_arr = toKeyIdArray(key); - if (key_arr.length !== 1){ - return Promise.reject( - gpgme_error('GENERIC_ERROR')); - // TBD should always be ONE key? - } - msg.setParameter('key', key_arr[0]); - if (delete_secret === true){ - msg.setParameter('allow_secret', true); - // TBD - } - if (no_confirm === true){ //TODO: Do we want this hidden deep in the code? - msg.setParameter('delete_force', true); - // TBD - } - if (msg.isComplete === true){ - return msg.post(); - } else { - return Promise.reject(gpgme_error('MSG_INCOMPLETE')); - } - } } /** - * Sets the data of the message + * Sets the data of the message, setting flags according on the data type * @param {GPGME_Message} message The message where this data will be set * @param {*} data The data to enter */ function putData(message, data){ if (!message || !message instanceof GPGME_Message ) { return gpgme_error('PARAM_WRONG'); } if (!data){ return gpgme_error('PARAM_WRONG'); } else if (typeof(data) === 'string') { message.setParameter('data', data); } else if ( typeof(data) === 'object' && typeof(data.getText) === 'function' ){ let txt = data.getText(); if (typeof(txt) === 'string'){ message.setParameter('data', txt); } else { return gpgme_error('PARAM_WRONG'); } } else { return gpgme_error('PARAM_WRONG'); } }