diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index 4270be58..5b092ab0 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -1,213 +1,214 @@ /* 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+ */ /** * A connection port will be opened for each communication between gpgmejs and * gnupg. It should be alive as long as there are additional messages to be * expected. */ import { permittedOperations } from './permittedOperations' -import { GPGMEJS_Error } from "./Errors" +import { gpgme_error } from "./Errors" import { GPGME_Message } from "./Message"; /** * A Connection handles the nativeMessaging interaction. */ export class Connection{ constructor(){ this.connect(); let me = this; } /** * (Simple) Connection check. * @returns {Boolean} true if the onDisconnect event has not been fired. * Please note that the event listener of the port takes some time * (5 ms seems enough) to react after the port is created. Then this will * return undefined */ get isConnected(){ return this._isConnected; } /** * Immediately closes the open port. */ disconnect() { if (this._connection){ this._connection.disconnect(); } } /** * Opens a nativeMessaging port. */ connect(){ if (this._isConnected === true){ - GPGMEJS_Error('CONN_ALREADY_CONNECTED'); + gpgme_error('CONN_ALREADY_CONNECTED'); } else { this._isConnected = true; this._connection = chrome.runtime.connectNative('gpgmejson'); let me = this; this._connection.onDisconnect.addListener( function(){ me._isConnected = false; } ); } } /** * Sends a message and resolves with the answer. * @param {GPGME_Message} message * @returns {Promise} the gnupg answer, or rejection with error * information. */ post(message){ if (!this.isConnected){ - return Promise.reject(GPGMEJS_Error('CONN_NO_CONNECT')); + return Promise.reject(gpgme_error('CONN_NO_CONNECT')); } if (!message || !message instanceof GPGME_Message){ - return Promise.reject(GPGMEJS_Error('PARAM_WRONG'), message); + return Promise.reject(gpgme_error('PARAM_WRONG'), message); } if (message.isComplete !== true){ - return Promise.reject(GPGMEJS_Error('MSG_INCOMPLETE')); + return Promise.reject(gpgme_error('MSG_INCOMPLETE')); } let me = this; return new Promise(function(resolve, reject){ let answer = new Answer(message.operation); let listener = function(msg) { if (!msg){ me._connection.onMessage.removeListener(listener) - reject(GPGMEJS_Error('CONN_EMPTY_GPG_ANSWER')); + reject(gpgme_error('CONN_EMPTY_GPG_ANSWER')); } else if (msg.type === "error"){ me._connection.onMessage.removeListener(listener) reject( {code: 'GNUPG_ERROR', msg: msg.msg} ); } else { let answer_result = answer.add(msg); if (answer_result !== true){ reject(answer_result); } if (msg.more === true){ me._connection.postMessage({'op': 'getmore'}); } else { me._connection.onMessage.removeListener(listener) resolve(answer.message); } } }; me._connection.onMessage.addListener(listener); - let timeout = new Promise(function(resolve, reject){ - setTimeout(function(){ - reject(GPGMEJS_Error('CONN_TIMEOUT')); - }, 5000); - }); if (permittedOperations[message.operation].pinentry){ return me._connection.postMessage(message.message); } else { - return Promise.race([timeout, - me._connection.postMessage(message.message) - ]); + return Promise.race([ + me._connection.postMessage(message.message), + function(resolve, reject){ + setTimeout(function(){ + reject(gpgme_error('CONN_TIMEOUT')); + }, 5000); + }]).then(function(result){ + return result; + }); } }); } }; /** * A class for answer objects, checking and processing the return messages of * the nativeMessaging communication. * @param {String} operation The operation, to look up validity of returning messages */ class Answer{ constructor(operation){ this.operation = operation; } /** * Add the information to the answer * @param {Object} msg The message as received with nativeMessaging - * returns true if successfull, GPGMEJS_Error otherwise + * returns true if successfull, gpgme_error otherwise */ add(msg){ if (this._response === undefined){ this._response = {}; } let messageKeys = Object.keys(msg); let poa = permittedOperations[this.operation].answer; if (messageKeys.length === 0){ - return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER'); + return gpgme_error('CONN_UNEXPECTED_ANSWER'); } for (let i= 0; i < messageKeys.length; i++){ let key = messageKeys[i]; switch (key) { case 'type': if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){ - return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER'); + return gpgme_error('CONN_UNEXPECTED_ANSWER'); } break; case 'more': break; default: //data should be concatenated if (poa.data.indexOf(key) >= 0){ if (!this._response.hasOwnProperty(key)){ this._response[key] = ''; } this._response[key] = this._response[key].concat(msg[key]); } //params should not change through the message else if (poa.params.indexOf(key) >= 0){ if (!this._response.hasOwnProperty(key)){ this._response[key] = msg[key]; } else if (this._response[key] !== msg[key]){ - return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER',msg[key]); + return gpgme_error('CONN_UNEXPECTED_ANSWER',msg[key]); } } //infos may be json objects etc. Not yet defined. // Pushing them into arrays for now else if (poa.infos.indexOf(key) >= 0){ if (!this._response.hasOwnProperty(key)){ this._response[key] = []; } this._response.push(msg[key]); } else { - return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER', key); + return gpgme_error('CONN_UNEXPECTED_ANSWER', key); } break; } } return true; } /** * @returns {Object} the assembled message. * TODO: does not care yet if completed. */ get message(){ return this._response; } } diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js index 04b13e10..2f53aa89 100644 --- a/lang/js/src/Errors.js +++ b/lang/js/src/Errors.js @@ -1,116 +1,142 @@ /* 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_DISCONNECTED': { + msg:'Connection with the nativeMessaging host was lost.', + 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_OP_PENDING': { + msg: 'There is no operation specified yet. The parameter cannot' + + ' be set', + type: 'warning' + }, + 'MSG_WRONG_OP': { + msg: 'The operation requested could not be found', + type: 'warning' + }, + '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' + }, + // 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' + }, + 'NOT_IMPLEMENTED': { + msg: 'A openpgpjs parameter was submitted that is not implemented', + type: 'error' + }, + 'NOT_YET_IMPLEMENTED': { + msg: 'Support of this is probable, but it is not implemented yet', + type: 'error' + }, + 'GENERIC_ERROR': { + msg: 'Unspecified error', + type: 'error' + } +}; + /** - * Checks the given error code and returns some information about it's meaning - * @param {String} code The error code - * @returns {Object} An object containing string properties code and msg - * TODO: error-like objects with the code 'GNUPG_ERROR' are errors sent - * directly by gnupg as answer in Connection.post() + * 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 GPGMEJS_Error(code = 'GENERIC_ERROR'){ - if (!typeof(code) === 'string'){ - code = 'GENERIC_ERROR'; - } - let errors = { //TODO: someplace else - // 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: 'warn' - }, - // 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_OP_PENDING': { - msg: 'There is no operation specified yet. The parameter cannot' - + ' be set', - type: 'warning' - }, - 'MSG_WRONG_OP': { - msg: 'The operation requested could not be found', - type: 'warning' - }, - 'MSG_NO_KEYS' : { - msg: 'There were no valid keys provided.', - type: 'warn' - }, - 'MSG_NOT_A_FPR': { - msg: 'The String is not an accepted fingerprint', - type: 'warn' - }, - - // generic - 'PARAM_WRONG':{ - msg: 'invalid parameter was found', - type: 'error' - }, - 'NOT_IMPLEMENTED': { - msg: 'A openpgpjs parameter was submitted that is not implemented', - type: 'error' - }, - 'NOT_YET_IMPLEMENTED': { - msg: 'Support of this is probable, but it is not implemented yet', - type: 'error' - }, - 'GENERIC_ERROR': { - msg: 'Unspecified error', - type: 'error' - }, - } - if (code === 'TODO'){ - alert('TODO_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 (errors.hasOwnProperty(code)){ - code = 'GENERIC_ERROR'; + if (err_list[code].type === 'warning'){ + console.log(new GPGME_Error(code)); } - if (errors.type === 'error'){ - return {code: 'code', - msg: errors[code].msg - }; - } - if (errors.type === 'warning'){ - console.log(code + ': ' + error[code].msg); - } - return undefined; + return null; + } else if (code === 'GNUPG_ERROR'){ + return new GPGME_Error(code, info.msg); + } + 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/Helpers.js b/lang/js/src/Helpers.js index d9750ba7..841c0eda 100644 --- a/lang/js/src/Helpers.js +++ b/lang/js/src/Helpers.js @@ -1,102 +1,102 @@ /* 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 { GPGMEJS_Error } from "./Errors"; +import { gpgme_error } from "./Errors"; /** * Tries to return an array of fingerprints, either from input fingerprints or * from Key objects * @param {Key |Array| GPGME_Key | Array|String|Array} input * @returns {Array} Array of fingerprints. */ export function toKeyIdArray(input, nocheck){ if (!input){ - GPGMEJS_Error('MSG_NO_KEYS'); + gpgme_error('MSG_NO_KEYS'); return []; } if (!Array.isArray(input)){ input = [input]; } let result = []; for (let i=0; i < input.length; i++){ if (typeof(input[i]) === 'string'){ if (isFingerprint(input[i]) === true){ result.push(input[i]); } else { - GPGMEJS_Error('MSG_NOT_A_FPR'); + gpgme_error('MSG_NOT_A_FPR'); } } else if (typeof(input[i]) === 'object'){ let fpr = ''; if (input[i] instanceof GPGME_Key){ fpr = input[i].fingerprint; } else if (input[i].hasOwnProperty(primaryKey) && input[i].primaryKey.hasOwnProperty(getFingerprint)){ fpr = input[i].primaryKey.getFingerprint(); } if (isFingerprint(fpr) === true){ result.push(fpr); } else { - GPGMEJS_Error('MSG_NOT_A_FPR'); + gpgme_error('MSG_NOT_A_FPR'); } } else { - return GPGMEJS_Error('PARAM_WRONG'); + return gpgme_error('PARAM_WRONG'); } } if (result.length === 0){ - GPGMEJS_Error('MSG_NO_KEYS'); + gpgme_error('MSG_NO_KEYS'); return []; } else { return result; } }; /** * check if values are valid hexadecimal values of a specified length * @param {*} key input value. * @param {int} len the expected length of the value */ function hextest(key, len){ if (!key || typeof(key) !== "string"){ return false; } if (key.length !== len){ return false; } let regexp= /^[0-9a-fA-F]*$/i; return regexp.test(key); }; /** * check if the input is a valid Hex string with a length of 40 */ export function isFingerprint(string){ return hextest(string, 40); }; /** * check if the input is a valid Hex string with a length of 16 */ function isLongId(string){ return hextest(string, 16); }; // TODO still not needed anywhere function isShortId(string){ return hextest(string, 8); }; diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 5ae80438..f6fa7ae3 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -1,203 +1,203 @@ /* 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} from './Helpers' -import {GPGMEJS_Error} from './Errors' +import {gpgme_error} from './Errors' +import { GPGME_Message } from './Message'; +import { permittedOperations } from './permittedOperations'; export class GPGME_Key { constructor(fingerprint){ this.fingerprint = fingerprint; } set fingerprint(fpr){ if (isFingerprint(fpr) === true && !this._fingerprint){ this._fingerprint = fingerprint; } } get fingerprint(){ return this._fingerprint; } /** * hasSecret returns true if a secret subkey is included in this Key */ get hasSecret(){ checkKey(this._fingerprint, 'secret').then( function(result){ return Promise.resolve(result); }); } get isRevoked(){ return checkKey(this._fingerprint, 'revoked'); } get isExpired(){ return checkKey(this._fingerprint, 'expired'); } get isDisabled(){ return checkKey(this._fingerprint, 'disabled'); } get isInvalid(){ return checkKey(this._fingerprint, 'invalid'); } get canEncrypt(){ return checkKey(this._fingerprint, 'can_encrypt'); } get canSign(){ return checkKey(this._fingerprint, 'can_sign'); } get canCertify(){ return checkKey(this._fingerprint, 'can_certify'); } get canAuthenticate(){ return checkKey(this._fingerprint, 'can_authenticate'); } get isQualified(){ return checkKey(this._fingerprint, 'is_qualified'); } get armored(){ let me = this; return new Promise(function(resolve, reject){ let conn = new Connection(); conn.setFlag('armor', true); conn.post('export',{'fpr': me._fingerprint}); }); // TODO return value not yet checked. Should result in an armored block // in correct encoding // TODO openpgpjs also returns secKey if private = true? } /** * TODO returns true if this is the default key used to sign */ get isDefault(){ throw('NOT_YET_IMPLEMENTED'); } /** * get the Key's subkeys as GPGME_Key objects * @returns {Array} */ get subkeys(){ return checkKey(this._fingerprint, 'subkeys').then(function(result){ // TBD expecting a list of fingerprints if (!Array.isArray(result)){ result = [result]; } let resultset = []; for (let i=0; i < result.length; i++){ let subkey = new GPGME_Key(result[i]); if (subkey instanceof GPGME_Key){ resultset.push(subkey); } } return Promise.resolve(resultset); }); } /** * creation time stamp of the key * @returns {Date|null} TBD */ get timestamp(){ return checkKey(this._fingerprint, 'timestamp'); //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available. } /** * The expiration timestamp of this key TBD * @returns {Date|null} TBD */ get expires(){ return checkKey(this._fingerprint, 'expires'); // TODO convert to Date; check for 0 } /** * getter name TBD * @returns {String|Array} The user ids associated with this key */ get userIds(){ return checkKey(this._fingerprint, 'uids'); } /** * @returns {String} The public key algorithm supported by this subkey */ get pubkey_algo(){ return checkKey(this._fingerprint, 'pubkey_algo'); } }; /** * generic function to query gnupg information on a key. * @param {*} fingerprint The identifier of the Keyring * @param {*} property The gpgme-json property to check * */ function checkKey(fingerprint, property){ - return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); - + return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); + if (!property || + permittedOperations[keyinfo].indexOf(property) < 0){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } return new Promise(function(resolve, reject){ if (!isFingerprint(fingerprint)){ - reject('not a fingerprint'); //TBD + reject('KEY_INVALID'); } - let conn = new Connection(); - conn.post('getkey',{ // TODO not yet implemented in gpgme - 'fingerprint': this.fingerprint}) - .then(function(result){ - if (property !== undefined){ - if (result.hasOwnProperty(key)){ - resolve(result[property]); - } - else if (property == 'secret'){ - // property undefined means "not true" in case of secret - resolve(false); - } else { - reject('ERR_INVALID_PROPERTY') //TBD - } + let msg = new GPGME_Message('keyinfo'); + msg.setParameter('fingerprint', this.fingerprint); + return (this.connection.post(msg)).then(function(result){ + if (result.hasOwnProperty(property)){ + resolve(result[property]); + } + else if (property == 'secret'){ + // TBD property undefined means "not true" in case of secret? + resolve(false); + } else { + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); } - - - resolve(result); }, function(error){ - reject(error); + reject({code: 'GNUPG_ERROR', + msg: error.msg}); }); }); }; \ No newline at end of file diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index ef8028ff..e1f0a50f 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -1,150 +1,153 @@ /* 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} from './Message' import {GPGME_Key} from './Key' import { isFingerprint, isLongId } from './Helpers'; +import { gpgme_error } from './Errors'; export class GPGME_Keyring { constructor(connection){ this.connection = connection; } set connection(connection){ if (!this._connection && connection instanceof Connection){ this._connection = connection; } } get connection(){ if (this._connection instanceof Connection){ if (this._connection.isConnected){ return this._connection; } - return undefined; //TODO: connection was lost! + return gpgme_error('CONN_DISCONNECTED'); } - return undefined; //TODO: no connection there + return gpgme_error('CONN_NO_CONNECT'); } /** * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds * @param {Boolean} (optional) Include listing of secret keys * @returns {Promise.>} * */ getKeys(pattern, include_secret){ let msg = new GPGME_Message('listkeys'); if (pattern && typeof(pattern) === 'string'){ msg.setParameter('pattern', pattern); } if (include_secret){ msg.setParameter('with-secret', true); } this.connection.post(msg).then(function(result){ let fpr_list = []; let resultset = []; if (!Array.isArray(result.keys)){ //TODO check assumption keys = Array fpr_list = [result.keys]; } else { fpr_list = result.keys; } for (let i=0; i < fpr_list.length; i++){ let newKey = new GPGME_Key(fpr_list[i]); if (newKey instanceof GPGME_Key){ resultset.push(newKey); } } return Promise.resolve(resultset); }); } /** * @param {Object} flags subset filter expecting at least one of the * filters described below. True will filter on the condition, False will * reverse the filter, if not present or undefined, the filter will not be * considered. Please note that some combination may not make sense - * @param {Boolean} flags.defaultKey Only Keys marked as Default Keys * @param {Boolean} flags.secret Only Keys containing a secret part. - * @param {Boolean} flags.valid Valid Keys only * @param {Boolean} flags.revoked revoked Keys only * @param {Boolean} flags.expired Expired Keys only * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds * @returns {Promise Array} * */ getSubset(flags, pattern){ if (flags === undefined) { throw('ERR_WRONG_PARAM'); }; let secretflag = false; if (flags.hasOwnProperty(secret) && flags.secret){ secretflag = true; } this.getKeys(pattern, secretflag).then(function(queryset){ let resultset = []; for (let i=0; i < queryset.length; i++ ){ let conditions = []; let anticonditions = []; if (secretflag === true){ conditions.push('hasSecret'); } else if (secretflag === false){ anticonditions.push('hasSecret'); } + /** if (flags.defaultKey === true){ conditions.push('isDefault'); } else if (flags.defaultKey === false){ anticonditions.push('isDefault'); } - if (flags.valid === true){ + */ + /** + * if (flags.valid === true){ anticonditions.push('isInvalid'); } else if (flags.valid === false){ conditions.push('isInvalid'); } + */ if (flags.revoked === true){ conditions.push('isRevoked'); } else if (flags.revoked === false){ anticonditions.push('isRevoked'); } if (flags.expired === true){ conditions.push('isExpired'); } else if (flags.expired === false){ anticonditions.push('isExpired'); } let decision = undefined; for (let con = 0; con < conditions.length; con ++){ if (queryset[i][conditions[con]] !== true){ decision = false; } } for (let acon = 0; acon < anticonditions.length; acon ++){ if (queryset[i][anticonditions[acon]] === true){ decision = false; } } if (decision !== false){ resultset.push(queryset[i]); } } return Promise.resolve(resultset); }); } }; diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js index 1b36f11d..06ac8db2 100644 --- a/lang/js/src/Message.js +++ b/lang/js/src/Message.js @@ -1,111 +1,111 @@ /* 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 { permittedOperations } from './permittedOperations' -import { GPGMEJS_Error } from './Errors' +import { gpgme_error } from './Errors' export class GPGME_Message { //TODO getter constructor(operation){ setOperation(this, operation); } get operation(){ return this._msg.op; } /** * Sets a parameter for the message. Note that the operation has to be set * first, to be able to check if the parameter is permittted * @param {String} param Parameter to set * @param {any} value Value to set //TODO: Some type checking * @returns {Boolean} If the parameter was set successfully */ setParameter(param,value){ if (!param || typeof(param) !== 'string'){ - return GPGMEJS_Error('PARAM_WRONG'); + return gpgme_error('PARAM_WRONG'); } if (!this._msg || !this._msg.op){ - return GPGMEJS_Error('MSG_OP_PENDING'); + return gpgme_error('MSG_OP_PENDING'); } let po = permittedOperations[this._msg.op]; if (!po){ - return GPGMEJS_Error('MSG_WRONG_OP'); + return gpgme_error('MSG_WRONG_OP'); } if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){ this._msg[param] = value; return true; } - return GPGMEJS_Error('PARAM_WRONG'); + return gpgme_error('PARAM_WRONG'); } /** * Check if the message has the minimum requirements to be sent, according * to the definitions in permittedOperations * @returns {Boolean} */ get isComplete(){ if (!this._msg.op){ return false; } let reqParams = permittedOperations[this._msg.op].required; for (let i=0; i < reqParams.length; i++){ if (!this._msg.hasOwnProperty(reqParams[i])){ console.log(reqParams[i] + 'missing'); return false; } } return true; } /** * Returns the prepared message with parameters and completeness checked * @returns {Object|null} Object to be posted to gnupg, or null if * incomplete */ get message(){ if (this.isComplete === true){ return this._msg; } else { return null; } } } /** * Defines the operation this message will have * @param {String} operation Must be defined in permittedOperations * TODO: move to constructor? */ function setOperation (scope, operation){ if (!operation || typeof(operation) !== 'string'){ - return GPGMEJS_Error('PARAM_WRONG'); + return gpgme_error('PARAM_WRONG'); } if (permittedOperations.hasOwnProperty(operation)){ if (!scope._msg){ scope._msg = {}; } scope._msg.op = operation; } else { - return GPGMEJS_Error('MSG_WRONG_OP'); + return gpgme_error('MSG_WRONG_OP'); } } \ No newline at end of file diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index b20ff0f2..b504a457 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -1,169 +1,172 @@ /* 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 {Connection} from "./Connection" import {GPGME_Message} from './Message' import {toKeyIdArray} from "./Helpers" -import {GPGMEJS_Error as Error, GPGMEJS_Error} from "./Errors" +import { gpgme_error } from "./Errors" import { GPGME_Keyring } from "./Keyring"; export class GpgME { /** * initializes GpgME by opening a nativeMessaging port * TODO: add configuration */ constructor(connection){ this.connection = connection; } set connection(connection){ if (this._connection instanceof Connection){ - //TODO Warning: Connection already established + gpgme_error('CONN_ALREADY_CONNECTED'); } if (connection instanceof Connection){ this._connection = connection; + } else { + gpgme_error('PARAM_WRONG'); } } get connection(){ if (this._connection instanceof Connection){ if (this._connection.isConnected){ return this._connection; } return undefined; //TODO: connection was lost! } return undefined; //TODO: no connection there } set Keyring(keyring){ if (ring && ring instanceof GPGME_Keyring){ - this.Keyring = ring; + this._Keyring = ring; } } get Keyring(){ + return this._Keyring; } /** * @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array * @param {GPGME_Key|String|Array|Array} publicKeys Keys used to encrypt the message * @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message */ encrypt(data, publicKeys, wildcard=false){ let msg = new GPGME_Message('encrypt'); // TODO temporary msg.setParameter('armor', true); msg.setParameter('always-trust', true); let pubkeys = toKeyIdArray(publicKeys); msg.setParameter('keys', pubkeys); putData(msg, data); if (wildcard === true){msg.setParameter('throw-keyids', true); }; return (this.connection.post(msg)); } /** * @param {String} data TODO Format: base64? String? Message with the encrypted data * @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. * @async */ decrypt(data){ if (data === undefined){ - return Promise.reject(GPGMEJS_Error('MSG_EMPTY')); + return Promise.reject(gpgme_error('MSG_EMPTY')); } let msg = new GPGME_Message('decrypt'); putData(msg, data); return this.connection.post(msg); } deleteKey(key, delete_secret = false, no_confirm = false){ - return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); let msg = new GPGME_Message('deletekey'); let key_arr = toKeyIdArray(key); if (key_arr.length !== 1){ throw('TODO'); //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 } this.connection.post(msg).then(function(success){ //TODO: it seems that there is always errors coming back: }, function(error){ switch (error.msg){ case 'ERR_NO_ERROR': return Promise.resolve('okay'); //TBD default: - return Promise.reject(GPGMEJS_Error('TODO') ); // + return Promise.reject(gpgme_error('TODO') ); // // INV_VALUE, // GPG_ERR_NO_PUBKEY, // GPG_ERR_AMBIGUOUS_NAME, // GPG_ERR_CONFLICT } }); } } /** * Sets the data of the message, converting Uint8Array to base64 and setting * the base64 flag * @param {GPGME_Message} message The message where this data will be set * @param {*} data The data to enter * @param {String} propertyname // TODO unchecked still */ function putData(message, data){ if (!message || !message instanceof GPGME_Message ) { - return GPGMEJS_Error('PARAM_WRONG'); + return gpgme_error('PARAM_WRONG'); } if (!data){ message.setParameter('data', ''); } else if (data instanceof Uint8Array){ let decoder = new TextDecoder('utf8'); message.setParameter('base64', true); message.setParameter ('data', decoder.decode(data)); } else if (typeof(data) === 'string') { message.setParameter('base64', false); message.setParameter('data', data); } else if ( typeof(data) === 'object' && data.hasOwnProperty(getText)){ let txt = data.getText(); if (txt instanceof Uint8Array){ let decoder = new TextDecoder('utf8'); message.setParameter('base64', true); message.setParameter ('data', decoder.decode(txt)); } } else { - return GPGMEJS_Error('PARAM_WRONG'); + return gpgme_error('PARAM_WRONG'); } } diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js index e32f43a3..4e5e1ea0 100644 --- a/lang/js/src/gpgmejs_openpgpjs.js +++ b/lang/js/src/gpgmejs_openpgpjs.js @@ -1,261 +1,281 @@ /* 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+ */ /** * This is a compatibility API to be used as openpgpjs syntax. * Non-implemented options will throw an error if set (not null or undefined) * TODO Some info about differences */ import { GpgME } from "./gpgmejs"; import {GPGME_Keyring} from "./Keyring" import { GPGME_Key } from "./Key"; import { isFingerprint } from "./Helpers" - import { GPGMEJS_Error } from './Errors' + import { gpgme_error } from "./Errors" export class GpgME_openpgpmode { - constructor(connection){ - this.initGpgME(connection); + constructor(connection, config = {}){ + this.initGpgME(connection, config); } get Keyring(){ if (this._keyring){ return this._keyring; } return undefined; } - initGpgME(connection){ - this._GpgME = new GpgME(connection); - this._Keyring = new GPGME_Keyring_openpgpmode(connection); + initGpgME(connection, config = {}){ + if (connection && typeof(config) ==='object'){ + this._config = config; + if (!this._GPGME){ + this._GpgME = new GpgME(connection, config); + } + if (!this._Keyring){ + this._Keyring = new GPGME_Keyring_openpgpmode(connection); + } + } } get GpgME(){ if (this._GpGME){ return this._GpGME; } } /** * Encrypt Message * Supported: * @param {String|Uint8Array} data + * //an openpgp Message also accepted here. TODO: is this wanted? * @param {Key|Array} publicKeys + * //Strings of Fingerprints * @param {Boolean} wildcard * TODO: - * @param {Key|Array} privateKeys - * @param {String} filename - * @param {module:enums.compression} compression - * @param {Boolean} armor - * @param {Boolean} detached + * @param {Key|Array} privateKeys // -> encryptsign + * @param {module:enums.compression} compression //TODO accepts integer, if 0 (no compression) it won't compress + * @param {Boolean} armor // TODO base64 switch + * @param {Boolean} detached // --> encryptsign * unsupported: * @param {String|Array} passwords * @param {Object} sessionKey * @param {Signature} signature * @param {Boolean} returnSessionKey + * @param {String} filename + * + * Can be set, but will be ignored: * * @returns {Promise} * {data: ASCII armored message, * signature: detached signature if 'detached' is true * } * @async * @static */ - encrypt({data = '', publicKeys = '', privateKeys, passwords, sessionKey, - filename, compression, armor=true, detached=false, signature=null, - returnSessionKey=null, wildcard=false, date=null}) { - if (passwords !== undefined - || sessionKey !== undefined + encrypt({data = '', publicKeys = '', privateKeys, passwords=null, + sessionKey = null, filename, compression, armor=true, detached=false, + signature=null, returnSessionKey=null, wildcard=false, date=null}) { + if (passwords !== null + || sessionKey !== null || signature !== null || returnSessionKey !== null - || date !== null){ + || date !== null + ){ return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED')); } if ( privateKeys - || filename || compression || armor === false || detached == true){ - return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); + } + if (filename){ + if (this._config.unconsidered_params === 'warn'){ + GPMGEJS_Error('PARAM_IGNORED'); + } else if (this._config.unconsidered_params === 'error'){ + return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED')); + } } return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard); } /** Decrypt Message - * supported - * TODO: @param {Message} message TODO: for now it accepts an armored string only + * supported openpgpjs parameters: + * @param {Message|Uint8Array|String} message Message object from openpgpjs * Unsupported: * @param {String|Array} passwords + * @param {Key|Array} privateKeys * @param {Object|Array} sessionKeys - * @param {Date} date - - * TODO - * @param {Key|Array} privateKey - * @param {Key|Array} publicKeys + * Not yet supported, but planned * @param {String} format (optional) return data format either as 'utf8' or 'binary' * @param {Signature} signature (optional) detached signature for verification - + * Ignored values: can be safely set, but have no effect + * @param {Date} date + * @param {Key|Array} publicKeys + * * @returns {Promise} decrypted and verified message in the form: * { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] } * @async * @static */ - decrypt({ message, privateKeys, passwords, sessionKeys, publicKeys, format='utf8', signature=null, date}) { - if (passwords !== undefined - || sessionKeys - || date){ - return Promise.reject(GPGMEJS_Error('NOT_IMPLEMENTED')); + decrypt({ message, privateKeys, passwords=null, sessionKeys, + publicKeys, format='utf8', signature=null, date= null}) { + if (passwords !== null || sessionKeys || privateKeys){ + return Promise.reject(gpgme_error('NOT_IMPLEMENTED')); } - if ( privateKeys - || publicKeys - || format !== 'utf8' - || signature - ){ - return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED')); + if ( format !== 'utf8' || signature){ + return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED')); + } + if (date !== null || publicKeys){ + if (this._config.unconsidered_params === 'warn'){ + GPMGEJS_Error('PARAM_IGNORED'); + } else if (this._config.unconsidered_params === 'reject'){ + return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED')); + } } return this.GpgME.decrypt(message); // TODO: translate between: // openpgp: // { data:Uint8Array|String, // filename:String, // signatures:[{ keyid:String, valid:Boolean }] } // and gnupg: // 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. } } /** * Translation layer offering basic Keyring API to be used in Mailvelope. * It may still be changed/expanded/merged with GPGME_Keyring */ class GPGME_Keyring_openpgpmode { constructor(connection){ this._gpgme_keyring = new GPGME_Keyring(connection); } /** * Returns a GPGME_Key Object for each Key in the gnupg Keyring. This * includes keys openpgpjs considers 'private' (usable for signing), with * the difference that Key.armored will NOT contain any secret information. * Please also note that a GPGME_Key does not offer full openpgpjs- Key * compatibility. * @returns {Array} * //TODO: Check if IsDefault is also always hasSecret * TODO Check if async is required */ getPublicKeys(){ return translateKeys( this._gpgme_keyring.getKeys(null, true)); } /** * Returns the Default Key used for crypto operation in gnupg. * Please note that the armored property does not contained secret key blocks, * despite secret blocks being part of the key itself. * @returns {Promise } */ getDefaultKey(){ this._gpgme_keyring.getSubset({defaultKey: true}).then(function(result){ if (result.length === 1){ return Promise.resolve( translateKeys(result)[0]); } else { // TODO: Can there be "no default key"? // TODO: Can there be several default keys? - return GPGMEJS_Error('TODO'); + return gpgme_error('TODO'); } }); } /** * Deletes a Key * @param {Object} Object identifying key * @param {String} key.fingerprint - fingerprint of the to be deleted key * @param {Boolean} key.secret - indicator if private key should be deleted as well * @returns {Promise., Error>} TBD: Not sure what is wanted TODO @throws {Error} error.code = ‘KEY_NOT_EXIST’ - there is no key for the given fingerprint TODO @throws {Error} error.code = ‘NO_SECRET_KEY’ - secret indicator set, but no secret key exists */ deleteKey(key){ if (typeof(key) !== "object"){ - return Promise.reject(GPGMEJS_Error('PARAM_WRONG')); + return Promise.reject(gpgme_error('PARAM_WRONG')); } if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){ - return Promise.reject(GPGMEJS_Error('PARAM_WRONG')); + return Promise.reject(gpgme_error('PARAM_WRONG')); } let key_to_delete = new GPGME_Key(key.fingerprint); return key_to_delete.deleteKey(key.secret); } } /** * TODO error handling. * Offers the Key information as the openpgpmode wants */ class GPGME_Key_openpgpmode { constructor(value){ this.init = value; } set init (value){ if (!this._GPGME_Key && value instanceof GPGME_Key){ this._GPGME_Key = value; - } else if (!this._GPGME_Key && isFingerprint(fpr)){ - this._GPGME_Key = new GPGME_Key; + } else if (!this._GPGME_Key && isFingerprint(value)){ + this._GPGME_Key = new GPGME_Key(value); } } get fingerprint(){ return this._GPGME_Key.fingerprint; } get armor(){ return this._GPGME_Key.armored; } get secret(){ return this._GPGME_Key.hasSecret; } get default(){ return this._GPGME_Key.isDefault; } } /** * creates GPGME_Key_openpgpmode from GPGME_Keys */ function translateKeys(input){ if (!Array.isArray(input)){ input = [input]; } let resultset; for (let i=0; i< input.length; i++){ resultset.push(new GPGME_Key_openpgpmode(input[i])); } return resultset; } \ No newline at end of file diff --git a/lang/js/src/index.js b/lang/js/src/index.js index a54277c2..48904316 100644 --- a/lang/js/src/index.js +++ b/lang/js/src/index.js @@ -1,58 +1,59 @@ /* 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 } from "./gpgmejs"; -import { GPGMEJS_Error } from "./Errors"; +import { gpgme_error } from "./Errors"; import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs"; import { Connection } from "./Connection"; /** * Initializes a nativeMessaging Connection and returns a GPGMEjs object * @param {*} conf Configuration. TBD */ function init( config = { api_style: 'gpgme', // | gpgme_openpgpjs - null_expire_is_never: true // Boolean + null_expire_is_never: true, // Boolean + unconsidered_params: 'warn'//'warn' || 'reject' }){ return new Promise(function(resolve, reject){ let connection = new Connection; // TODO: Delayed reaction is ugly. We need to listen to the port's // event listener in isConnected, but this takes some time (<5ms) to // disconnect if there is no successfull connection. let delayedreaction = function(){ if (connection.isConnected === true){ let gpgme = null; if (config.api_style && config.api_style === 'gpgme_openpgpjs'){ resolve( - new GpgME_openpgpmode(connection)); + new GpgME_openpgpmode(connection, config)); } else { resolve(new GpgME(connection)); } } else { - reject(GPGMEJS_Error('CONN_NO_CONNECT')); + reject(gpgme_error('CONN_NO_CONNECT')); } }; setTimeout(delayedreaction, 5); }); }; export default { init: init } \ No newline at end of file diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js index 892f4f2e..79e74223 100644 --- a/lang/js/src/permittedOperations.js +++ b/lang/js/src/permittedOperations.js @@ -1,78 +1,126 @@ /* 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+ */ /** * Definition of the possible interactions with gpgme-json. * operation: required: Array optional: Array pinentry: Boolean If a pinentry dialog is expected, and a timeout of 5000 ms would be too short answer: type: The payload property of the answer. May be partial and in need of concatenation params: Array Information that do not change throughout the message - infos: Array arbitrary information that may change + infos: Array<*> arbitrary information that may result in a list } } */ export const permittedOperations = { encrypt: { required: ['keys', 'data'], optional: [ 'protocol', 'chunksize', 'base64', 'mime', 'armor', 'always-trust', 'no-encrypt-to', 'no-compress', 'throw-keyids', 'want-address', 'wrap' ], answer: { type: ['ciphertext'], data: ['data'], params: ['base64'], infos: [] } }, decrypt: { pinentry: true, required: ['data'], optional: [ 'protocol', 'chunksize', 'base64' ], answer: { type: ['plaintext'], data: ['data'], params: ['base64', 'mime'], - infos: ['info'] + infos: [] // pending. Info about signatures and validity + //signature: [{Key Fingerprint, valid Boolean}] + } + }, + /** + keyinfo: { // querying the Key's information. + required: ['fingerprint'], + anser: { + type: ['TBD'], + data: [], + params: ['hasSecret', 'isRevoked', 'isExpired', 'armored', + 'timestamp', 'expires', 'pubkey_algo'], + infos: ['subkeys', 'userIds'] + }*/ + + /** + listkeys:{ + optional: ['with-secret', 'pattern'], + answer: { + type: ['TBD'], //Array of fingerprints? + infos: ['TBD'] //the property with infos + }, + */ + + /** + importkey: { + required: ['keyarmored'], + answer: { + type: ['TBD'], + infos: [''], // for each key if import was a success, if it was an update + } + }, + */ + + /** + deletekey: { + required: ['fingerprint'], + answer: { + type ['TBD'], + infos: [''] //success:true? in gpgme, an error NO_ERROR is returned } } + */ + + /** + *get armored secret different treatment from keyinfo! + */ + + /** + * TBD key modification requests? + */ }