From d62f66b1fb47f2075770d896f672748a4136e70b Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 23 Apr 2018 17:18:46 +0200 Subject: js: Key handling stubs, Error handling, refactoring -- * Error handling: introduced GPGMEJS_Error class that handles errors at a more centralized and consistent position * src/Connection.js: The nativeMessaging port now opens per session instead of per message. Some methods were added that reflect this change - added methods disconnect() and reconnect() - added connection status query * src/gpgmejs.js - stub for key deletion - error handling - high level API for changing connection status * src/gpgmejs_openpgpjs.js - added stubs for Key/Keyring handling according to current state of discussion. It is still subject to change * src/Helpers.js - toKeyIdArray creates an array of KeyIds, now accepting fingerprints, GPGMEJS_Key objects and openpgp Key objects. * Key objects (src/Key.js) Querying information about a key directly from gnupg. Currently a stub, only the Key.fingerprint is functional. * Keyring queries (src/Keyring.js): Listing and searching keys. Currently a stub. --- lang/js/src/Keyring.js | 151 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 lang/js/src/Keyring.js (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js new file mode 100644 index 00000000..52fa7f71 --- /dev/null +++ b/lang/js/src/Keyring.js @@ -0,0 +1,151 @@ +/* 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 {Connection} from './Connection' +import {GPGME_Key} from './Key' +import { isFingerprint, isLongId } from './Helpers'; + +export class GPGME_Keyring { + constructor(){ + this.reconnect(); + } + + /** + * (Re)-establishes the connection + * TODO TEMP: should we better use the connection of our parent, + * which we do not control? + */ + reconnect(){ + if (!this._connection || ! this._connection instanceof Connection){ + this._connection = new Connection; + } else { + this._connection.disconnect(); + this._connection.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; + msg.operation = '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){ + 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); + }); + } + +}; -- cgit v1.2.3 From 727340b295f25e04cb595022ba143cda48364697 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 23 Apr 2018 19:15:40 +0200 Subject: js: don't allow message operation changes -- Once an operation is changed, their set of allowed/required parameters will change. So we shouldn't set/change the operation later. --- lang/js/src/Keyring.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 52fa7f71..d8cb84b2 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -49,8 +49,7 @@ export class GPGME_Keyring { * */ getKeys(pattern, include_secret){ - let msg = new GPGME_Message; - msg.operation = 'listkeys'; + let msg = new GPGME_Message('listkeys'); if (pattern && typeof(pattern) === 'string'){ msg.setParameter('pattern', pattern); } -- cgit v1.2.3 From 461dd0c8b41683a91073b362d100ee5217ec53f6 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 24 Apr 2018 18:44:30 +0200 Subject: js: change in initialization ancd connection handling -- * The Connection will now be started before an object is created, to better account for failures. * index.js: now exposes an init(), which returns a Promise of configurable with an established connection. * TODO: There is currently no way to recover from a "connection lost" * Connection.js offers Connection.isConnected, which toggles on port closing. --- lang/js/src/Keyring.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d8cb84b2..ef8028ff 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -19,27 +19,27 @@ */ import {GPGME_Message} from './Message' -import {Connection} from './Connection' import {GPGME_Key} from './Key' import { isFingerprint, isLongId } from './Helpers'; export class GPGME_Keyring { - constructor(){ - this.reconnect(); + constructor(connection){ + this.connection = connection; } - /** - * (Re)-establishes the connection - * TODO TEMP: should we better use the connection of our parent, - * which we do not control? - */ - reconnect(){ - if (!this._connection || ! this._connection instanceof Connection){ - this._connection = new Connection; - } else { - this._connection.disconnect(); - this._connection.connect(); + 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 undefined; //TODO: no connection there } /** @@ -57,7 +57,7 @@ export class GPGME_Keyring { msg.setParameter('with-secret', true); } - this._connection.post(msg).then(function(result){ + this.connection.post(msg).then(function(result){ let fpr_list = []; let resultset = []; if (!Array.isArray(result.keys)){ -- cgit v1.2.3 From 1fb310cabe578625f96fce5d84ff6f0092c08d24 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 25 Apr 2018 15:59:36 +0200 Subject: js: Configuration and Error handling -- * gpgmejs_openpgpjs - unsuported values with no negative consequences can now reject, warn or be ignored, according to config.unconsidered_params - cleanup of unsupported/supported parameters and TODOS * A src/index.js init() now accepts a configuration object * Errors will now be derived from Error, offering more info and a stacktrace. * Fixed Connection.post() timeout triggering on wrong cases * Added comments in permittedOperations.js, which gpgme interactions are still unimplemented and should be added next --- lang/js/src/Keyring.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'lang/js/src/Keyring.js') 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 @@ -21,6 +21,7 @@ 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){ @@ -37,9 +38,9 @@ export class GPGME_Keyring { 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'); } /** @@ -81,9 +82,7 @@ export class GPGME_Keyring { * 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 @@ -108,16 +107,20 @@ export class GPGME_Keyring { } 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){ -- cgit v1.2.3 From 3685913bf510a14b8cb324d980217d90489e6453 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 25 Apr 2018 19:45:39 +0200 Subject: js: First testing and improvements -- * Introduced Mocha/chai as testsuite. After development build 'npm test' should run the unit tests. Functionality exclusive to Browsers/WebExtensions cannot be run this way, so some other testing is still needed. - package.json: Added required development packages - .babelrc indirect configuration for mocha. ES6 transpiling needs some babel configuration, but mocha has no setting for it. - test/mocha.opts Vonfiguration for mocha runs * Fixed errors: - Helpers.js toKeyIdArray; isLongId is now exported - Key.js Key constructor failed - Message.js will not throw an Error during construction, a new message is now created with createMessage, which can return an Error or a GPGME_Message object * Tests: - test/Helpers: exports from Helpers.js, GPGME_Error handling - test/Message: first init test with bad parameters --- lang/js/src/Keyring.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index e1f0a50f..470eeeec 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -18,7 +18,7 @@ * SPDX-License-Identifier: LGPL-2.1+ */ -import {GPGME_Message} from './Message' +import {createMessage} from './Message' import {GPGME_Key} from './Key' import { isFingerprint, isLongId } from './Helpers'; import { gpgme_error } from './Errors'; @@ -50,7 +50,10 @@ export class GPGME_Keyring { * */ getKeys(pattern, include_secret){ - let msg = new GPGME_Message('listkeys'); + let msg = createMessage('listkeys'); + if (msg instanceof Error){ + return Promise.reject(msg); + } if (pattern && typeof(pattern) === 'string'){ msg.setParameter('pattern', pattern); } -- cgit v1.2.3 From 1f7b19512cfa7e1b153b99d6a2b40bad82a5496e Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 26 Apr 2018 17:13:34 +0200 Subject: js: created TestExtension and smaller fixes -- * Extensions: - Moved testapplication to Demoextension - Created BrowserTestExtension. Includes mocha and chai. For running tests that cannot be run outside a WebExtension Both Extensions can be found zipped in build/extensions after running build_extensions.sh * Code changes: - src/Config: Place for the configuration - small fixes raised during testing in Keyring.js, Message.js, - src/gpgmejs_openpgpjs.js don't offer direct GpgME object to the outside, as it only causes confusion - index.js init() now checks the config for validity * Tests: - Reordered tests in test/. - Input values are now in a separate file which may be of use for bulk testing * moved the build directory from dist to build --- lang/js/src/Keyring.js | 1 + 1 file changed, 1 insertion(+) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 470eeeec..364bfb46 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -22,6 +22,7 @@ import {createMessage} from './Message' import {GPGME_Key} from './Key' import { isFingerprint, isLongId } from './Helpers'; import { gpgme_error } from './Errors'; +import { Connection } from './Connection'; export class GPGME_Keyring { constructor(connection){ -- cgit v1.2.3 From fda7b13f1b673962ce34b6f429158a7eb9cef47b Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Apr 2018 20:03:09 +0200 Subject: js: more testing -- * Tests: initialization of the two modes, encryption * gpgme.js: reintroduced message check before calling Connection.post() * gpgmejs_openpgp.js: Fixed openpgp mode not passing keys * index.js: fixed some confusion in parseconfig() * Inserted some TODO stubs for missing error handling --- lang/js/src/Keyring.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 364bfb46..d1f4122e 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -78,6 +78,8 @@ export class GPGME_Keyring { } } return Promise.resolve(resultset); + }, function(error){ + //TODO error handling }); } @@ -151,6 +153,8 @@ export class GPGME_Keyring { } } return Promise.resolve(resultset); + }, function(error){ + //TODO error handling }); } -- cgit v1.2.3 From 6f67814eb45725bc7f3736a2638bad0a7470f17a Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 3 May 2018 14:12:10 +0200 Subject: js: changed Key class stub -- * src/Key.js: A Key object cannot offer more than basic functionality outside a connection, so it now requires a connection to be present. --- lang/js/src/Keyring.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d1f4122e..2cf87c24 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -61,6 +61,7 @@ export class GPGME_Keyring { if (include_secret){ msg.setParameter('with-secret', true); } + let me = this; this.connection.post(msg).then(function(result){ let fpr_list = []; @@ -72,7 +73,7 @@ export class GPGME_Keyring { fpr_list = result.keys; } for (let i=0; i < fpr_list.length; i++){ - let newKey = new GPGME_Key(fpr_list[i]); + let newKey = new GPGME_Key(fpr_list[i], me._connection); if (newKey instanceof GPGME_Key){ resultset.push(newKey); } -- cgit v1.2.3 From c755287ba845c4cbbf1d50e5aafecb2e687c7ac9 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 3 May 2018 18:03:22 +0200 Subject: js: Added browser testing for unit tests -- * Added unittests to be run inside a Browser. To be able to access the non-exposed functions and classes, a testing bundle will be created, containing the tests (unittests.js) and the items to be tested. * src/Helpelpers, src/Key, src/Keyring: fixed some errors found during testing. --- lang/js/src/Keyring.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 2cf87c24..4596035a 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -20,7 +20,7 @@ import {createMessage} from './Message' import {GPGME_Key} from './Key' -import { isFingerprint, isLongId } from './Helpers'; +import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; import { Connection } from './Connection'; -- cgit v1.2.3 From eff27d6387b1cad2ef9901fa03dbee2ea86c786a Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 25 May 2018 11:53:24 +0200 Subject: js: use version operation for connection checks -- * src/Connection.js: isConnected was renamed to checkConnection, that returns a promise with either version information or Boolean * Connection checks have been adapted to reflect that checkConnection returns a Promise * BrowsertestExtension: tests/signTest.js was missing from my last commit --- lang/js/src/Keyring.js | 58 +++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 4596035a..80792f77 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -36,10 +36,7 @@ export class GPGME_Keyring { } get connection(){ if (this._connection instanceof Connection){ - if (this._connection.isConnected){ - return this._connection; - } - return gpgme_error('CONN_DISCONNECTED'); + return this._connection; } return gpgme_error('CONN_NO_CONNECT'); } @@ -51,36 +48,35 @@ export class GPGME_Keyring { * */ getKeys(pattern, include_secret){ - let msg = createMessage('listkeys'); - if (msg instanceof Error){ - return Promise.reject(msg); - } - if (pattern && typeof(pattern) === 'string'){ - msg.setParameter('pattern', pattern); - } - if (include_secret){ - msg.setParameter('with-secret', true); - } let me = this; - - 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; + return new Promise(function(resolve, reject) { + let msg; + msg = createMessage('listkeys'); + if (pattern && typeof(pattern) === 'string'){ + msg.setParameter('pattern', pattern); } - for (let i=0; i < fpr_list.length; i++){ - let newKey = new GPGME_Key(fpr_list[i], me._connection); - if (newKey instanceof GPGME_Key){ - resultset.push(newKey); - } + if (include_secret){ + msg.setParameter('with-secret', true); } - return Promise.resolve(resultset); - }, function(error){ - //TODO error handling + me.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], me._connection); + if (newKey instanceof GPGME_Key){ + resultset.push(newKey); + } + } + resolve(resultset); + }, function(error){ + reject(error); + }); }); } -- cgit v1.2.3 From d4adbf453d39659eee378b2be1d7125315d76083 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 28 May 2018 16:52:50 +0200 Subject: js: Treat a connection as a gpgme Context -- * After an operation a connection should be disconnected again. The "end of operation" is now assumed to be either an error as answer, or a message not including a "more" * GPGME, GPGME_Key, GPGME_Keyring don't require a connection anymore * Message.js: The Message.post() method will open a connection as required --- lang/js/src/Keyring.js | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 80792f77..9abb9ec3 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -22,23 +22,9 @@ import {createMessage} from './Message' import {GPGME_Key} from './Key' import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; -import { Connection } from './Connection'; 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){ - return this._connection; - } - return gpgme_error('CONN_NO_CONNECT'); + constructor(){ } /** @@ -58,7 +44,7 @@ export class GPGME_Keyring { if (include_secret){ msg.setParameter('with-secret', true); } - me.connection.post(msg).then(function(result){ + msg.post().then(function(result){ let fpr_list = []; let resultset = []; if (!Array.isArray(result.keys)){ @@ -68,7 +54,7 @@ export class GPGME_Keyring { fpr_list = result.keys; } for (let i=0; i < fpr_list.length; i++){ - let newKey = new GPGME_Key(fpr_list[i], me._connection); + let newKey = new GPGME_Key(fpr_list[i]); if (newKey instanceof GPGME_Key){ resultset.push(newKey); } -- cgit v1.2.3 From 53ce2b94bc35243710dec9b7972c7aaaa79dbc75 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 28 May 2018 17:26:56 +0200 Subject: js: Keyring listing keys -- * implementing Keyring methods: - Keyring.getKeys: has an additional option that retrieves the armor and secret state once at the beginning. This is power hungry, but allows for Keys to be used directly (without querying gpgme-json each call) * permittedOperations.js: reflect recent changes in the native counterpart, adding more options * Key: adding two methods for retrieving the armored Key block and for finding out if the Key includes a secret subkey. --- lang/js/src/Keyring.js | 129 +++++++++++++------------------------------------ 1 file changed, 33 insertions(+), 96 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 9abb9ec3..7e13dfe2 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -19,7 +19,7 @@ */ import {createMessage} from './Message' -import {GPGME_Key} from './Key' +import {GPGME_Key, createKey} from './Key' import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; @@ -28,117 +28,54 @@ export class GPGME_Keyring { } /** - * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds - * @param {Boolean} (optional) Include listing of secret keys + * @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, include_secret){ + getKeys(pattern, prepare_sync){ let me = this; return new Promise(function(resolve, reject) { let msg; - msg = createMessage('listkeys'); + msg = createMessage('keylist'); if (pattern && typeof(pattern) === 'string'){ - msg.setParameter('pattern', pattern); - } - if (include_secret){ - msg.setParameter('with-secret', true); + msg.setParameter('keys', pattern); } + msg.setParameter('sigs', true); //TODO do we need this? msg.post().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); + 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); + }); } - resolve(resultset); }, function(error){ reject(error); }); }); } - - /** - * @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.secret Only Keys containing a secret part. - * @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){ - 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); - }, function(error){ - //TODO error handling - }); - } +// TODO: + // deleteKey(key, include_secret=false) + // getKeysArmored(pattern) //just dump all armored keys + // getDefaultKey() Big TODO + // importKeys(armoredKeys) }; -- cgit v1.2.3 From 332b4adbcc52ccf337cbc1943d5abef500769e10 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 30 May 2018 17:05:54 +0200 Subject: js: more Keyring/Key handling -- * src/Keys.js - made setKeyData more consistent with other methods - added convenience methods (Key.armored, Key.hasSecret) - Added a Key delete function * src/Keyring.js: - added a getkeysArmored which allows for bulk export of public Keys gpgmejs: - removed deleteKey. It is now a method of the Key itself - Encrypt: Added some common options as parameter, and the possibility to set all allowed flags via an additional Object --- lang/js/src/Keyring.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'lang/js/src/Keyring.js') 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 @@ -20,7 +20,7 @@ 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 { @@ -43,10 +43,10 @@ export class GPGME_Keyring { 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 = []; @@ -72,10 +72,30 @@ export class GPGME_Keyring { }); }); } -// 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!) }; -- cgit v1.2.3 From 0356a667c5a8b4fdb4404cebb57475ed3f39ade9 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 6 Jun 2018 11:57:41 +0200 Subject: js: implement import/delete Key, some fixes -- * Keyring.js - implemented importKey: importing one or more armored public key blocks. - implemented deleteKey: deleting a public Key from gpg. * Key.js renamed property Key.armor to Key.armored * Helpers.js: toKeyIDArray does not complain anymore if there are no keys. Not having Keys in e.g. signing keys in encrypt is legitimate and common, the complaints were getting spammy * Errors.js: gpgme_errors now always pass an optional additional message, for easier debugging in minified code * Connection.js: Fix in gpgme-json responses containing objects * eslintrc.json: Start using eslint. A cleanup to conform to it is not done yet * Added further tests for the new functionality --- lang/js/src/Keyring.js | 144 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 26 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 9081cbe9..c5e613ec 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -18,9 +18,9 @@ * SPDX-License-Identifier: LGPL-2.1+ */ -import {createMessage} from './Message' -import {GPGME_Key, createKey} from './Key' -import { isFingerprint, toKeyIdArray } from './Helpers'; +import {createMessage} from './Message'; +import {createKey} from './Key'; +import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; export class GPGME_Keyring { @@ -39,10 +39,8 @@ export class GPGME_Keyring { * */ getKeys(pattern, prepare_sync){ - let me = this; return new Promise(function(resolve, reject) { - let msg; - msg = createMessage('keylist'); + let msg = createMessage('keylist'); if (pattern !== undefined){ msg.setParameter('keys', pattern); } @@ -50,25 +48,28 @@ export class GPGME_Keyring { 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()); + if (result.keys.length === 0){ + resolve([]); + } else { + for (let i=0; i< result.keys.length; i++){ + let k = createKey(result.keys[i].fingerprint); + k.setKeyData(result.keys[i]); + if (prepare_sync === true){ + promises.push(k.getArmor()); + promises.push(k.getHasSecret()); + } + resultset.push(k); } - resultset.push(k); - } - if (promises.length > 0) { - Promise.all(promises).then(function (res){ + if (promises.length > 0) { + Promise.all(promises).then(function() { + resolve(resultset); + }, function(error){ + reject(error); + }); + } else { resolve(resultset); - }, function(error){ - reject(error); - }); + } } - }, function(error){ - reject(error); }); }); } @@ -80,7 +81,6 @@ export class GPGME_Keyring { * @returns {Promise} Armored Key blocks */ getKeysArmored(pattern) { - if (pattern) return new Promise(function(resolve, reject) { let msg = createMessage('export'); msg.setParameter('armor', true); @@ -92,10 +92,102 @@ export class GPGME_Keyring { }, function(error){ reject(error); }); + }); } // getDefaultKey() Big TODO - // importKeys(armoredKeys) - // generateKey --> TODO (Andre noch anfragen!) -}; + /** + * + * @param {String} armored Armored Key block of the Kex(s) to be imported into gnupg + * @param {Boolean} prepare_sync prepare the keys for synched use (see getKeys()). + * @returns {Promise>} An array of objects for the Keys considered. + * Key.key The key itself as a GPGME_Key + * Key.status String: + * 'nochange' if the Key was not changed, + * 'newkey' if the Key was imported in gpg, and did not exist previously, + * 'change' if the key existed, but details were updated. For details, + * Key.changes is available. + * Key.changes.userId: Boolean userIds changed + * Key.changes.signature: Boolean signatures changed + * Key.changes.subkey: Boolean subkeys changed + * // TODO: not yet implemented: Information about Keys that failed + * (e.g. malformed Keys, secretKeys are not accepted) + */ + importKey(armored, prepare_sync) { + if (!armored || typeof(armored) !== 'string'){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('import'); + msg.setParameter('data', armored); + msg.post().then(function(response){ + let infos = {}; + let fprs = []; + for (var res=0; res < response.result[0].imports.length; res++) { + let result = response.result[0].imports[res]; + let status = ''; + if (result.status === 0){ + status = 'nochange'; + } else if ((result.status & 1) === 1){ + status = 'newkey'; + } else { + status = 'change'; + } + let changes = {}; + changes.userId = (result.status & 2) === 2; + changes.signature = (result.status & 4) === 4; + changes.subkey = (result.status & 8) === 8; + //16 new secret key: not implemented + + fprs.push(result.fingerprint); + infos[result.fingerprint] = { + changes: changes, + status: status + }; + } + let resultset = []; + if (prepare_sync === true){ + me.getKeys(fprs, true).then(function(result){ + for (let i=0; i < result.length; i++) { + resultset.push({ + key: result[i], + changes: infos[result[i].fingerprint].changes, + status: infos[result[i].fingerprint].status + }); + } + resolve(resultset); + }, function(error){ + reject(error); + }); + } else { + for (let i=0; i < fprs.length; i++) { + resultset.push({ + key: createKey(fprs[i]), + changes: infos[fprs[i]].changes, + status: infos[fprs[i]].status + }); + } + resolve(resultset); + } + + }, function(error){ + reject(error); + }); + + + }); + + + } + + deleteKey(fingerprint){ + if (isFingerprint(fingerprint) === true) { + let key = createKey(fingerprint); + key.delete(); + } + } + + // generateKey +} -- cgit v1.2.3 From bfd3799d39df265882deedeee083fd5246a2f35d Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 6 Jun 2018 13:05:53 +0200 Subject: js: code cleanup (eslint) -- * trying to stick to eslint from now on for readability * As some attribution was lost in previous git confusions, I added my name into some of the licence headers --- lang/js/src/Keyring.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index c5e613ec..71585878 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -16,8 +16,12 @@ * 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+ + * + * Author(s): + * Maximilian Krambach */ + import {createMessage} from './Message'; import {createKey} from './Key'; import { isFingerprint } from './Helpers'; @@ -99,18 +103,22 @@ export class GPGME_Keyring { /** * - * @param {String} armored Armored Key block of the Kex(s) to be imported into gnupg - * @param {Boolean} prepare_sync prepare the keys for synched use (see getKeys()). - * @returns {Promise>} An array of objects for the Keys considered. - * Key.key The key itself as a GPGME_Key - * Key.status String: + * @param {String} armored Armored Key block of the Kex(s) to be imported + * into gnupg + * @param {Boolean} prepare_sync prepare the keys for synched use + * (see getKeys()). + * @returns {Promise>} An array of objects for the Keys + * considered: + * Key.key : The key itself as a GPGME_Key + * Key.status : * 'nochange' if the Key was not changed, - * 'newkey' if the Key was imported in gpg, and did not exist previously, - * 'change' if the key existed, but details were updated. For details, - * Key.changes is available. - * Key.changes.userId: Boolean userIds changed - * Key.changes.signature: Boolean signatures changed - * Key.changes.subkey: Boolean subkeys changed + * 'newkey' if the Key was imported in gpg, and did not exist + * previously, + * 'change' if the key existed, but details were updated. For + * details, Key.changes is available. + * Key.changes.userId: userIds changed + * Key.changes.signature: signatures changed + * Key.changes.subkey: subkeys changed * // TODO: not yet implemented: Information about Keys that failed * (e.g. malformed Keys, secretKeys are not accepted) */ @@ -125,7 +133,7 @@ export class GPGME_Keyring { msg.post().then(function(response){ let infos = {}; let fprs = []; - for (var res=0; res < response.result[0].imports.length; res++) { + for (let res=0; res < response.result[0].imports.length; res++){ let result = response.result[0].imports[res]; let status = ''; if (result.status === 0){ -- cgit v1.2.3 From e97e6c06e950cfad424e120f4f3752b594214c94 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 11 Jun 2018 12:08:50 +0200 Subject: js: Add key creation to Keyring -- * src/Keyring.js: Added method generateKey for new Keys Still TODO: Key length and some further testing. Automated testing does not work in this case, and gpgmejs will not be able to delete test keys again. * src/permittedOperations.js Added new method's definitions according to gpgme-json --- lang/js/src/Keyring.js | 60 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 71585878..0d4e3c52 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -197,5 +197,63 @@ export class GPGME_Keyring { } } - // generateKey + /** + * Generates a new Key pair directly in gpg, and returns a GPGME_Key + * representing that Key. Please note that due to security concerns, secret + * Keys can not be _deleted_ from inside gpgmejs. + * + * @param {String} userId The user Id, e.g. "Foo Bar " + * @param {*} algo (optional) algorithm to be used. See + * {@link supportedKeyAlgos } below for supported values. + * @param {Number} keyLength (optional) TODO + * @param {Date} expires (optional) Expiration date. If not set, expiration + * will be set to 'never' + * + * @returns{Promise} + */ + generateKey(userId, algo = 'default', keyLength, expires){ + if ( + typeof(userId) !== 'string' || + supportedKeyAlgos.indexOf(algo) < 0 || + (expires && !(expires instanceof Date)) + // TODO keylength + // TODO check for completeness of algos + ){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('createkey'); + msg.setParameter('userid', userId); + msg.setParameter('algo', algo); + if (expires){ + msg.setParameter('expires', + Math.floor(expires.valueOf()/1000)); + } + // TODO append keylength to algo + msg.post().then(function(response){ + me.getKeys(response.fingerprint, true).then( + // TODO make prepare_sync (second parameter) optional here. + function(result){ + resolve(result); + }, function(error){ + reject(error); + }); + }, function(error) { + reject(error); + }); + }); + } } + +/** + * A list of algorithms supported for key generation. + */ +const supportedKeyAlgos = [ + 'default', + 'rsa', + 'dsa', + 'elg', + 'ed25519', + 'cv25519' +]; \ No newline at end of file -- cgit v1.2.3 From aed402c5d572b60246f1f8e57ae269f8c91b0b7c Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 13 Jun 2018 15:22:03 +0200 Subject: js: getDefaultKey and verify fix -- * DemoExtension/maindemo.js - added a Demo for retrieving the default signing key * src/Errors.js - add a new Error if no default key can be determined * src/Key.js added documentation and a TODO marker for hasSecret. * src/Keyring.js implemented getDefaultKey * src/permittedOperations.js: Added missing entry for verify, added config_opt --- lang/js/src/Keyring.js | 56 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 0d4e3c52..e07a5934 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -99,7 +99,61 @@ export class GPGME_Keyring { }); } - // getDefaultKey() Big TODO + /** + * Returns the Key to be used by default for signing operations, + * looking up the gpg configuration, or returning the first key that + * contains a secret key. + * @returns {Promise} + * + * @async + * TODO: getHasSecret always returns false at this moment, so this fucntion + * still does not fully work as intended. + * + */ + getDefaultKey() { + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('config_opt'); + msg.setParameter('component', 'gpg'); + msg.setParameter('option', 'default-key'); + msg.post().then(function(response){ + if (response.value !== undefined + && response.value.hasOwnProperty('string') + && typeof(response.value.string) === 'string' + ){ + me.getKeys(response.value.string,true).then(function(keys){ + if(keys.length === 1){ + resolve(keys[0]); + } else { + reject(gpgme_error('KEY_NO_DEFAULT')); + } + }, function(error){ + reject(error); + }); + } else { + // TODO: this is overly 'expensive' in communication + // and probably performance, too + me.getKeys(null,true).then(function(keys){ + for (let i=0; i < keys.length; i++){ + console.log(keys[i]); + console.log(keys[i].get('hasSecret')); + if (keys[i].get('hasSecret') === true){ + resolve(keys[i]); + break; + } + if (i === keys.length -1){ + reject(gpgme_error('KEY_NO_DEFAULT')); + } + } + }, function(error){ + reject(error); + }); + } + }, function(error){ + reject(error); + }); + }); + } /** * -- cgit v1.2.3 From 3c783bd09ce54b0d50dc3bea201e70e4fcbbf6a3 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 14 Jun 2018 12:15:51 +0200 Subject: js: add verify and signature parsing -- * src/gpgmejs.js: - Added verify method - Added verification results in decrypt (if signatures are present in the message) - Added a base64 option to decrypt * src/Signature.js: Convenience class for verification results. Used for e.g. converting timestamps to javascript time, quick overall validity checks * src/Keyring.js: removed debug code * src/Errors.js add two new Signature errors --- lang/js/src/Keyring.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index e07a5934..451f936a 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -135,8 +135,6 @@ export class GPGME_Keyring { // and probably performance, too me.getKeys(null,true).then(function(keys){ for (let i=0; i < keys.length; i++){ - console.log(keys[i]); - console.log(keys[i].get('hasSecret')); if (keys[i].get('hasSecret') === true){ resolve(keys[i]); break; -- cgit v1.2.3 From 3cd428ba442f43e470b977e27e18ff52567baba5 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 14 Jun 2018 14:50:25 +0200 Subject: js: import result feedback -- * src/Keyring.js: Changed and documented the import result feedback towards the javascript side --- lang/js/src/Keyring.js | 54 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 18 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 451f936a..43d257d2 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -155,26 +155,36 @@ export class GPGME_Keyring { /** * - * @param {String} armored Armored Key block of the Kex(s) to be imported + * @param {String} armored Armored Key block of the Key(s) to be imported * into gnupg * @param {Boolean} prepare_sync prepare the keys for synched use * (see getKeys()). - * @returns {Promise>} An array of objects for the Keys - * considered: - * Key.key : The key itself as a GPGME_Key - * Key.status : - * 'nochange' if the Key was not changed, - * 'newkey' if the Key was imported in gpg, and did not exist - * previously, - * 'change' if the key existed, but details were updated. For - * details, Key.changes is available. - * Key.changes.userId: userIds changed - * Key.changes.signature: signatures changed - * Key.changes.subkey: subkeys changed - * // TODO: not yet implemented: Information about Keys that failed - * (e.g. malformed Keys, secretKeys are not accepted) + * + * @returns {Promise} result: A summary and an array of Keys + * considered + * + * @returns result.summary: Numerical summary of the result. See the + * feedbackValues variable for available values and the gnupg documentation + * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html + * for details on their meaning. + * @returns {Array} result.Keys: Array of objects containing: + * @returns {GPGME_Key} Key.key The resulting key + * @returns {String} Key.status: + * 'nochange' if the Key was not changed, + * 'newkey' if the Key was imported in gpg, and did not exist + * previously, + * 'change' if the key existed, but details were updated. For + * details, Key.changes is available. + * @returns {Boolean} Key.changes.userId: userIds changed + * @returns {Boolean} Key.changes.signature: signatures changed + * @returns {Boolean} Key.changes.subkey: subkeys changed */ importKey(armored, prepare_sync) { + let feedbackValues = ['considered', 'no_user_id', 'imported', + 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', + 'new_signatures', 'new_revocations', 'secret_read', + 'secret_imported', 'secret_unchanged', 'skipped_new_keys', + 'not_imported', 'skipped_v3_keys']; if (!armored || typeof(armored) !== 'string'){ return Promise.reject(gpgme_error('PARAM_WRONG')); } @@ -185,8 +195,8 @@ export class GPGME_Keyring { msg.post().then(function(response){ let infos = {}; let fprs = []; - for (let res=0; res < response.result[0].imports.length; res++){ - let result = response.result[0].imports[res]; + for (let res=0; res Date: Tue, 19 Jun 2018 09:26:01 +0200 Subject: js: getDefaultKey and GenerateKey improvements -- * src/Keyring.js: added more options for key generation. * src/Key.js: GetDefaultKey now relies on the info associated with the key, as the approach of relying on a secret subkey did not work as intended * DemoExtension: Added a button for retrieval of the subkey, to test this functionality. --- lang/js/src/Keyring.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 43d257d2..8bec1cea 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -273,21 +273,18 @@ export class GPGME_Keyring { * Keys can not be _deleted_ from inside gpgmejs. * * @param {String} userId The user Id, e.g. "Foo Bar " - * @param {*} algo (optional) algorithm to be used. See - * {@link supportedKeyAlgos } below for supported values. - * @param {Number} keyLength (optional) TODO + * @param {*} algo (optional) algorithm (and optionally key size to be + * used. See {@link supportedKeyAlgos } below for supported values. * @param {Date} expires (optional) Expiration date. If not set, expiration * will be set to 'never' * * @returns{Promise} */ - generateKey(userId, algo = 'default', keyLength, expires){ + generateKey(userId, algo = 'default', expires){ if ( typeof(userId) !== 'string' || supportedKeyAlgos.indexOf(algo) < 0 || (expires && !(expires instanceof Date)) - // TODO keylength - // TODO check for completeness of algos ){ return Promise.reject(gpgme_error('PARAM_WRONG')); } @@ -295,12 +292,11 @@ export class GPGME_Keyring { return new Promise(function(resolve, reject){ let msg = createMessage('createkey'); msg.setParameter('userid', userId); - msg.setParameter('algo', algo); + msg.setParameter('algo', algo ); if (expires){ msg.setParameter('expires', Math.floor(expires.valueOf()/1000)); } - // TODO append keylength to algo msg.post().then(function(response){ me.getKeys(response.fingerprint, true).then( // TODO make prepare_sync (second parameter) optional here. @@ -321,9 +317,11 @@ export class GPGME_Keyring { */ const supportedKeyAlgos = [ 'default', - 'rsa', - 'dsa', - 'elg', + 'rsa', 'rsa2048', 'rsa3072', 'rsa4096', + 'dsa', 'dsa2048', 'dsa3072', 'dsa4096', + 'elg', 'elg2048', 'elg3072', 'elg4096', 'ed25519', - 'cv25519' + 'cv25519', + 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', + 'NIST P-256', 'NIST P-384', 'NIST P-521' ]; \ No newline at end of file -- cgit v1.2.3 From 1105fc87a3bd3e1152aff578b7b84871558418e6 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 4 Jul 2018 12:11:35 +0200 Subject: js: add Key lookup -- * src/Keyring.js: getKeys() now has the option "search", which will trigger a remote lookup (as configured in gpg) for the string given as pattern. * src/permittedOperations: make use of the new 'locate' option in keylist * DemoExtension: Add a button for lookup, to demonstrate the functionality --- lang/js/src/Keyring.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 8bec1cea..0d7643f0 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -39,16 +39,21 @@ export class GPGME_Keyring { * inmediately. This allows for full synchronous use. If set to false, * these will initially only be available as Promises in getArmor() and * getHasSecret() + * @param {Boolean} search (optional) retrieve the Keys from servers with + * the method(s) defined in gnupg (e.g. WKD/HKP lookup) * @returns {Promise.>} * */ - getKeys(pattern, prepare_sync){ + getKeys(pattern, prepare_sync, search){ return new Promise(function(resolve, reject) { let msg = createMessage('keylist'); if (pattern !== undefined){ msg.setParameter('keys', pattern); } msg.setParameter('sigs', true); + if (search === true){ + msg.setParameter('locate', true); + } msg.post().then(function(result){ let resultset = []; let promises = []; -- cgit v1.2.3 From 1919fa41b6da4dfd4f69e776caa6e6b1883eb208 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 4 Jul 2018 13:38:54 +0200 Subject: js: Add jsdoc, update webpack-cli dependency -- * package.json: - the old webpack-cli version depended on two packages with vulnerabilities, set to minimum version 3.0.8 to fix this (nodesecurity.io/advisories/157, nodesecurity.io/advisories/612) - added License identifier * README: Updated documentation * jsdoc.conf: Added a configuration file for jsdoc * some minor documentation changes, indentations --- lang/js/src/Keyring.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 0d7643f0..358757b0 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -110,10 +110,10 @@ export class GPGME_Keyring { * contains a secret key. * @returns {Promise} * - * @async + * * TODO: getHasSecret always returns false at this moment, so this fucntion * still does not fully work as intended. - * + * * @async */ getDefaultKey() { let me = this; @@ -283,7 +283,7 @@ export class GPGME_Keyring { * @param {Date} expires (optional) Expiration date. If not set, expiration * will be set to 'never' * - * @returns{Promise} + * @return {Promise} */ generateKey(userId, algo = 'default', expires){ if ( -- cgit v1.2.3 From 67b6fa5a2948deed6a914c638f923fb9ad2eac66 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 9 Jul 2018 11:24:46 +0200 Subject: js: reduce request spam at getKeys() -- * Don't make a secret-Key request for each Key retrieved, use one request for all of them instead, and assemble the info later. This should reduce the traffic with large Keyrings. The bulk retrieval for the public armored Keys for each of these Keys is still up to discussion * unittests: disabled assertion for the armored key (as it currently doesn't work) * encryptTest: clarified the mechanism/reason of rejection for Messages >64 MB. This is still a TODO, as this error comes from a different place (the browser itself) and behaves different from the other errors. --- lang/js/src/Keyring.js | 52 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 358757b0..09c43f73 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -56,28 +56,46 @@ export class GPGME_Keyring { } msg.post().then(function(result){ let resultset = []; - let promises = []; if (result.keys.length === 0){ resolve([]); } else { - for (let i=0; i< result.keys.length; i++){ - let k = createKey(result.keys[i].fingerprint); - 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() { - resolve(resultset); - }, function(error){ - reject(error); - }); + let secondrequest; + if (prepare_sync === true) { + secondrequest = function() { + msg.setParameter('secret', true); + return msg.post(); + }; } else { - resolve(resultset); + secondrequest = function() { + return Promise.resolve(true); + }; } + secondrequest().then(function(answer) { + for (let i=0; i < result.keys.length; i++){ + if (prepare_sync === true){ + result.keys[i].hasSecret = false; + if (answer && answer.keys) { + for (let j=0; j < answer.keys.length; j++ ){ + if (result.keys[i].fingerprint === + answer.keys[j].fingerprint + ) { + if (answer.keys[j].secret === true){ + result.keys[i].hasSecret = true; + } + break; + } + } + // TODO getArmor() to be used in sync + } + } + let k = createKey(result.keys[i].fingerprint); + k.setKeyData(result.keys[i]); + resultset.push(k); + } + resolve(resultset); + }, function(error){ + reject(error); + }); } }); }); -- cgit v1.2.3 From 4015f5b4983c8a4590aa71776880d8bc42c7918d Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 10 Jul 2018 14:32:26 +0200 Subject: js: documentation -- * Fixed errors: - src/Message.js post(): Set chunksize to defined default value instead of hardcoded - src/Keys.js: added getHasSecret() to refreshKey operation. * Reviewed and updated the documentation * non-documentation changes which do not affect functionality: - src/Errors: disabled a console.warn that is only useful for debugging - helpers.js: renamed "string" to "value" in isFingerprint and isLongId to avoid confusion - src/Keyring: prepare_sync, search are both explicitly set to false by default --- lang/js/src/Keyring.js | 132 ++++++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 50 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 09c43f73..a0bdfcb2 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -27,24 +27,30 @@ import {createKey} from './Key'; import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; +/** + * This class offers access to the gnupg keyring + */ 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() - * @param {Boolean} search (optional) retrieve the Keys from servers with - * the method(s) defined in gnupg (e.g. WKD/HKP lookup) - * @returns {Promise.>} + * Queries Keys (all Keys or a subset) from gnupg. * + * @param {String | Array} pattern (optional) A pattern to search + * for in userIds or KeyIds. + * @param {Boolean} prepare_sync (optional) if set to true, the 'hasSecret' + * and 'armored' properties will be fetched for the Keys as well. These + * require additional calls to gnupg, resulting in a performance hungry + * operation. Calling them here enables direct, synchronous use of these + * properties for all keys, without having to resort to a refresh() first. + * @param {Boolean} search (optional) retrieve Keys from external servers + * with the method(s) defined in gnupg (e.g. WKD/HKP lookup) + * @returns {Promise.|GPGME_Error>} + * @static + * @async */ - getKeys(pattern, prepare_sync, search){ + getKeys(pattern, prepare_sync=false, search=false){ return new Promise(function(resolve, reject) { let msg = createMessage('keylist'); if (pattern !== undefined){ @@ -102,10 +108,15 @@ export class GPGME_Keyring { } /** - * 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 + * Fetches the armored public Key blocks for all Keys matching the pattern + * (if no pattern is given, fetches all keys known to gnupg). Note that the + * result may be one big armored block, instead of several smaller armored + * blocks + * @param {String|Array} pattern (optional) The Pattern to search + * for + * @returns {Promise} Armored Key blocks + * @static + * @async */ getKeysArmored(pattern) { return new Promise(function(resolve, reject) { @@ -123,15 +134,14 @@ export class GPGME_Keyring { } /** - * Returns the Key to be used by default for signing operations, - * looking up the gpg configuration, or returning the first key that - * contains a secret key. - * @returns {Promise} - * + * Returns the Key used by default in gnupg. + * (a.k.a. 'primary Key or 'main key'). + * It looks up the gpg configuration if set, or the first key that contains + * a secret key. * - * TODO: getHasSecret always returns false at this moment, so this fucntion - * still does not fully work as intended. - * * @async + * @returns {Promise} + * @async + * @static */ getDefaultKey() { let me = this; @@ -177,30 +187,40 @@ export class GPGME_Keyring { } /** + * @typedef {Object} importResult The result of a Key update + * @property {Object} summary Numerical summary of the result. See the + * feedbackValues variable for available Keys values and the gnupg + * documentation. + * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html + * for details on their meaning. + * @property {Array} Keys Array of Object containing + * GPGME_Keys with additional import information * + */ + + /** + * @typedef {Object} importedKeyResult + * @property {GPGME_Key} key The resulting key + * @property {String} status: + * 'nochange' if the Key was not changed, + * 'newkey' if the Key was imported in gpg, and did not exist previously, + * 'change' if the key existed, but details were updated. For details, + * Key.changes is available. + * @property {Boolean} changes.userId Changes in userIds + * @property {Boolean} changes.signature Changes in signatures + * @property {Boolean} changes.subkey Changes in subkeys + */ + + /** + * Import an armored Key block into gnupg. Note that this currently will + * not succeed on private Key blocks. * @param {String} armored Armored Key block of the Key(s) to be imported * into gnupg * @param {Boolean} prepare_sync prepare the keys for synched use - * (see getKeys()). - * - * @returns {Promise} result: A summary and an array of Keys - * considered - * - * @returns result.summary: Numerical summary of the result. See the - * feedbackValues variable for available values and the gnupg documentation - * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html - * for details on their meaning. - * @returns {Array} result.Keys: Array of objects containing: - * @returns {GPGME_Key} Key.key The resulting key - * @returns {String} Key.status: - * 'nochange' if the Key was not changed, - * 'newkey' if the Key was imported in gpg, and did not exist - * previously, - * 'change' if the key existed, but details were updated. For - * details, Key.changes is available. - * @returns {Boolean} Key.changes.userId: userIds changed - * @returns {Boolean} Key.changes.signature: signatures changed - * @returns {Boolean} Key.changes.subkey: subkeys changed + * (see {@link getKeys}). + * @returns {Promise} A summary and Keys considered. + * @async + * @static */ importKey(armored, prepare_sync) { let feedbackValues = ['considered', 'no_user_id', 'imported', @@ -283,25 +303,36 @@ export class GPGME_Keyring { } + /** + * Convenience function for deleting a Key. See {@link Key.delete} for + * further information about the return values. + * @param {String} fingerprint + * @returns {Promise} + * @async + * @static + */ deleteKey(fingerprint){ if (isFingerprint(fingerprint) === true) { let key = createKey(fingerprint); - key.delete(); + return key.delete(); + } else { + return Promise.reject(gpgme_error('KEY_INVALID')); } } /** * Generates a new Key pair directly in gpg, and returns a GPGME_Key * representing that Key. Please note that due to security concerns, secret - * Keys can not be _deleted_ from inside gpgmejs. + * Keys can not be deleted or exported from inside gpgme.js. * - * @param {String} userId The user Id, e.g. "Foo Bar " - * @param {*} algo (optional) algorithm (and optionally key size to be - * used. See {@link supportedKeyAlgos } below for supported values. + * @param {String} userId The user Id, e.g. 'Foo Bar ' + * @param {String} algo (optional) algorithm (and optionally key size) to + * be used. See {@link supportedKeyAlgos} below for supported values. * @param {Date} expires (optional) Expiration date. If not set, expiration * will be set to 'never' * - * @return {Promise} + * @return {Promise} + * @async */ generateKey(userId, algo = 'default', expires){ if ( @@ -336,7 +367,8 @@ export class GPGME_Keyring { } /** - * A list of algorithms supported for key generation. + * List of algorithms supported for key generation. Please refer to the gnupg + * documentation for details */ const supportedKeyAlgos = [ 'default', -- cgit v1.2.3 From 30bb5490466119b66eeac255d71fb7bdc79149fa Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 12 Jul 2018 11:36:55 +0200 Subject: js: add with-sec-fprs to getKeysArmored -- * Reflects the changes made to gpgme-json in commit 6cc842c9aa76d19448141e5117ac59452d7a1ff3. - getKeysArmored now returns an object with property 'armored' being the exported armored block, and an (optional) array of fingerprint strings for those keys that can be used in sign/encrypt operations as property 'secret_fprs'. With this, extensions such as mailvelope will be able to bulk fetch all necessary key information in one request. --- lang/js/src/Keyring.js | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index a0bdfcb2..7a33be98 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -107,26 +107,46 @@ export class GPGME_Keyring { }); } + /** + * @typedef {Object} exportResult The result of a getKeysArmored operation. + * @property {String} armored The public Key(s) as armored block. Note that + * the result is one armored block, and not a block per key. + * @property {Array} secret_fprs (optional) list of fingerprints + * for those Keys that also have a secret Key available in gnupg. The + * secret key will not be exported, but the fingerprint can be used in + * operations needing a secret key. + */ + /** * Fetches the armored public Key blocks for all Keys matching the pattern - * (if no pattern is given, fetches all keys known to gnupg). Note that the - * result may be one big armored block, instead of several smaller armored - * blocks + * (if no pattern is given, fetches all keys known to gnupg). * @param {String|Array} pattern (optional) The Pattern to search * for - * @returns {Promise} Armored Key blocks + * @param {Boolean} with_secret_fpr (optional) also return a list of + * fingerprints for the keys that have a secret key available + * @returns {Promise} Object containing the + * armored Key(s) and additional information. * @static * @async */ - getKeysArmored(pattern) { + getKeysArmored(pattern, with_secret_fpr) { return new Promise(function(resolve, reject) { let msg = createMessage('export'); msg.setParameter('armor', true); + if (with_secret_fpr === true) { + msg.setParameter('with-sec-fprs', true); + } if (pattern !== undefined){ msg.setParameter('keys', pattern); } - msg.post().then(function(result){ - resolve(result.data); + msg.post().then(function(answer){ + const result = {armored: answer.data}; + if (with_secret_fpr === true + && answer.hasOwnProperty('sec-fprs') + ) { + result.secret_fprs = answer['sec-fprs']; + } + resolve(result); }, function(error){ reject(error); }); -- cgit v1.2.3 From 5213a599fea0da64560f935dffbf6b27a39d4850 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 12 Jul 2018 11:48:17 +0200 Subject: js: allow optional Key retrieve pattern to be null -- * src/Keyring.js: If the optional "pattern" parameter is not to be used, but another, following parameter is, null is more of a convention in javascript, thus both null and undefined are interpreted as "this parameter is not meant to be set". --- lang/js/src/Keyring.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 7a33be98..cefc8123 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -53,7 +53,7 @@ export class GPGME_Keyring { getKeys(pattern, prepare_sync=false, search=false){ return new Promise(function(resolve, reject) { let msg = createMessage('keylist'); - if (pattern !== undefined){ + if (pattern !== undefined && pattern !== null){ msg.setParameter('keys', pattern); } msg.setParameter('sigs', true); @@ -136,7 +136,7 @@ export class GPGME_Keyring { if (with_secret_fpr === true) { msg.setParameter('with-sec-fprs', true); } - if (pattern !== undefined){ + if (pattern !== undefined && pattern !== null){ msg.setParameter('keys', pattern); } msg.post().then(function(answer){ -- cgit v1.2.3 From ce0379d999039131c86dc0bb83e9b8edfee1c7d4 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 17 Jul 2018 11:07:49 +0200 Subject: js: fix getkeys with locate option -- * src/Keyring.js: As locate will not work with the "secret" option, the first message cannot be reused, thus a new one must be created here --- lang/js/src/Keyring.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index cefc8123..f2a71389 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -68,8 +68,10 @@ export class GPGME_Keyring { let secondrequest; if (prepare_sync === true) { secondrequest = function() { - msg.setParameter('secret', true); - return msg.post(); + let msg2 = createMessage('keylist'); + msg2.setParameter('keys', pattern); + msg2.setParameter('secret', true); + return msg2.post(); }; } else { secondrequest = function() { -- cgit v1.2.3 From 94ee0988d4eaac27785de6efb7c19ca9976e1e9c Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Jul 2018 20:36:21 +0200 Subject: js: change the write access for js class methods -- * src/ [Connection, Error, Key, Keyring, MEssage, Signature, gpgmejs]: Functions and values that are not meant to be overwritten are now moved into their constructors, thus eliminating the possibility of overwrites after initialization. * Key: The mode of use (synchronous cached, or async promises) ivs now determined at initialization of that Key. The property Key.isAsync reflects this state. * unittests: fixed old Key syntax for testing. * Message.js isComplete is now a method and not a getter anymore. * Added some startup tests. --- lang/js/src/Keyring.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index f2a71389..d863fe73 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -32,7 +32,6 @@ import { gpgme_error } from './Errors'; */ export class GPGME_Keyring { constructor(){ - } /** * Queries Keys (all Keys or a subset) from gnupg. @@ -50,7 +49,7 @@ export class GPGME_Keyring { * @static * @async */ - getKeys(pattern, prepare_sync=false, search=false){ + this.getKeys = function(pattern, prepare_sync=false, search=false){ return new Promise(function(resolve, reject) { let msg = createMessage('keylist'); if (pattern !== undefined && pattern !== null){ @@ -107,7 +106,7 @@ export class GPGME_Keyring { } }); }); - } + }; /** * @typedef {Object} exportResult The result of a getKeysArmored operation. @@ -131,7 +130,7 @@ export class GPGME_Keyring { * @static * @async */ - getKeysArmored(pattern, with_secret_fpr) { + this.getKeysArmored = function(pattern, with_secret_fpr) { return new Promise(function(resolve, reject) { let msg = createMessage('export'); msg.setParameter('armor', true); @@ -153,7 +152,7 @@ export class GPGME_Keyring { reject(error); }); }); - } + }; /** * Returns the Key used by default in gnupg. @@ -165,7 +164,7 @@ export class GPGME_Keyring { * @async * @static */ - getDefaultKey() { + this.getDefaultKey = function() { let me = this; return new Promise(function(resolve, reject){ let msg = createMessage('config_opt'); @@ -206,7 +205,7 @@ export class GPGME_Keyring { reject(error); }); }); - } + }; /** * @typedef {Object} importResult The result of a Key update @@ -244,7 +243,7 @@ export class GPGME_Keyring { * @async * @static */ - importKey(armored, prepare_sync) { + this.importKey = function (armored, prepare_sync) { let feedbackValues = ['considered', 'no_user_id', 'imported', 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', 'new_signatures', 'new_revocations', 'secret_read', @@ -323,7 +322,7 @@ export class GPGME_Keyring { }); - } + }; /** * Convenience function for deleting a Key. See {@link Key.delete} for @@ -333,14 +332,14 @@ export class GPGME_Keyring { * @async * @static */ - deleteKey(fingerprint){ + this.deleteKey = function(fingerprint){ if (isFingerprint(fingerprint) === true) { let key = createKey(fingerprint); return key.delete(); } else { return Promise.reject(gpgme_error('KEY_INVALID')); } - } + }; /** * Generates a new Key pair directly in gpg, and returns a GPGME_Key @@ -356,7 +355,7 @@ export class GPGME_Keyring { * @return {Promise} * @async */ - generateKey(userId, algo = 'default', expires){ + this.generateKey = function (userId, algo = 'default', expires){ if ( typeof(userId) !== 'string' || supportedKeyAlgos.indexOf(algo) < 0 || @@ -385,7 +384,8 @@ export class GPGME_Keyring { reject(error); }); }); - } + }; +} } /** -- cgit v1.2.3 From 522121ea7e105acc22795b1997ca500c7b227b4f Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Jul 2018 20:56:11 +0200 Subject: js: fix indentaion -- * doing the indentation changes that became neccesary in the last commit. --- lang/js/src/Keyring.js | 653 +++++++++++++++++++++++++------------------------ 1 file changed, 331 insertions(+), 322 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d863fe73..31c4f92b 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -33,359 +33,368 @@ import { gpgme_error } from './Errors'; export class GPGME_Keyring { constructor(){ - /** - * Queries Keys (all Keys or a subset) from gnupg. - * - * @param {String | Array} pattern (optional) A pattern to search - * for in userIds or KeyIds. - * @param {Boolean} prepare_sync (optional) if set to true, the 'hasSecret' - * and 'armored' properties will be fetched for the Keys as well. These - * require additional calls to gnupg, resulting in a performance hungry - * operation. Calling them here enables direct, synchronous use of these - * properties for all keys, without having to resort to a refresh() first. - * @param {Boolean} search (optional) retrieve Keys from external servers - * with the method(s) defined in gnupg (e.g. WKD/HKP lookup) - * @returns {Promise.|GPGME_Error>} - * @static - * @async - */ - this.getKeys = function(pattern, prepare_sync=false, search=false){ - return new Promise(function(resolve, reject) { - let msg = createMessage('keylist'); - if (pattern !== undefined && pattern !== null){ - msg.setParameter('keys', pattern); - } - msg.setParameter('sigs', true); - if (search === true){ - msg.setParameter('locate', true); - } - msg.post().then(function(result){ - let resultset = []; - if (result.keys.length === 0){ - resolve([]); - } else { - let secondrequest; - if (prepare_sync === true) { - secondrequest = function() { - let msg2 = createMessage('keylist'); - msg2.setParameter('keys', pattern); - msg2.setParameter('secret', true); - return msg2.post(); - }; + /** + * Queries Keys (all Keys or a subset) from gnupg. + * + * @param {String | Array} pattern (optional) A pattern to + * search for in userIds or KeyIds. + * @param {Boolean} prepare_sync (optional) if set to true, the + * 'hasSecret' and 'armored' properties will be fetched for the Keys as + * well. These require additional calls to gnupg, resulting in a + * performance hungry operation. Calling them here enables direct, + * synchronous use of these properties for all keys, without having to + * resort to a refresh() first. + * @param {Boolean} search (optional) retrieve Keys from external + * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup) + * @returns {Promise.|GPGME_Error>} + * @static + * @async + */ + this.getKeys = function(pattern, prepare_sync=false, search=false){ + return new Promise(function(resolve, reject) { + let msg = createMessage('keylist'); + if (pattern !== undefined && pattern !== null){ + msg.setParameter('keys', pattern); + } + msg.setParameter('sigs', true); + if (search === true){ + msg.setParameter('locate', true); + } + msg.post().then(function(result){ + let resultset = []; + if (result.keys.length === 0){ + resolve([]); } else { - secondrequest = function() { - return Promise.resolve(true); - }; - } - secondrequest().then(function(answer) { - for (let i=0; i < result.keys.length; i++){ - if (prepare_sync === true){ - result.keys[i].hasSecret = false; - if (answer && answer.keys) { - for (let j=0; j < answer.keys.length; j++ ){ - if (result.keys[i].fingerprint === - answer.keys[j].fingerprint - ) { - if (answer.keys[j].secret === true){ - result.keys[i].hasSecret = true; + let secondrequest; + if (prepare_sync === true) { + secondrequest = function() { + let msg2 = createMessage('keylist'); + msg2.setParameter('keys', pattern); + msg2.setParameter('secret', true); + return msg2.post(); + }; + } else { + secondrequest = function() { + return Promise.resolve(true); + }; + } + secondrequest().then(function(answer) { + for (let i=0; i < result.keys.length; i++){ + if (prepare_sync === true){ + result.keys[i].hasSecret = false; + if (answer && answer.keys) { + for (let j=0; + j < answer.keys.length; j++ ){ + const a = answer.keys[j]; + const b = result.keys[i]; + if ( + a.fingerprint === b.fingerprint + ) { + if (a.secret === true){ + a.hasSecret = true; + } + break; } - break; } + // TODO getArmor() to be used in sync } - // TODO getArmor() to be used in sync } + let k = createKey(result.keys[i].fingerprint); + k.setKeyData(result.keys[i]); + resultset.push(k); } - let k = createKey(result.keys[i].fingerprint); - k.setKeyData(result.keys[i]); - resultset.push(k); - } - resolve(resultset); - }, function(error){ - reject(error); - }); - } + resolve(resultset); + }, function(error){ + reject(error); + }); + } + }); }); - }); - }; + }; - /** - * @typedef {Object} exportResult The result of a getKeysArmored operation. - * @property {String} armored The public Key(s) as armored block. Note that - * the result is one armored block, and not a block per key. - * @property {Array} secret_fprs (optional) list of fingerprints - * for those Keys that also have a secret Key available in gnupg. The - * secret key will not be exported, but the fingerprint can be used in - * operations needing a secret key. - */ + /** + * @typedef {Object} exportResult The result of a getKeysArmored + * operation. + * @property {String} armored The public Key(s) as armored block. Note + * that the result is one armored block, and not a block per key. + * @property {Array} secret_fprs (optional) list of + * fingerprints for those Keys that also have a secret Key available in + * gnupg. The secret key will not be exported, but the fingerprint can + * be used in operations needing a secret key. + */ - /** - * Fetches the armored public Key blocks for all Keys matching the pattern - * (if no pattern is given, fetches all keys known to gnupg). - * @param {String|Array} pattern (optional) The Pattern to search - * for - * @param {Boolean} with_secret_fpr (optional) also return a list of - * fingerprints for the keys that have a secret key available - * @returns {Promise} Object containing the - * armored Key(s) and additional information. - * @static - * @async - */ - this.getKeysArmored = function(pattern, with_secret_fpr) { - return new Promise(function(resolve, reject) { - let msg = createMessage('export'); - msg.setParameter('armor', true); - if (with_secret_fpr === true) { - msg.setParameter('with-sec-fprs', true); - } - if (pattern !== undefined && pattern !== null){ - msg.setParameter('keys', pattern); - } - msg.post().then(function(answer){ - const result = {armored: answer.data}; - if (with_secret_fpr === true - && answer.hasOwnProperty('sec-fprs') - ) { - result.secret_fprs = answer['sec-fprs']; + /** + * Fetches the armored public Key blocks for all Keys matching the + * pattern (if no pattern is given, fetches all keys known to gnupg). + * @param {String|Array} pattern (optional) The Pattern to + * search for + * @param {Boolean} with_secret_fpr (optional) also return a list of + * fingerprints for the keys that have a secret key available + * @returns {Promise} Object containing the + * armored Key(s) and additional information. + * @static + * @async + */ + this.getKeysArmored = function(pattern, with_secret_fpr) { + return new Promise(function(resolve, reject) { + let msg = createMessage('export'); + msg.setParameter('armor', true); + if (with_secret_fpr === true) { + msg.setParameter('with-sec-fprs', true); } - resolve(result); - }, function(error){ - reject(error); + if (pattern !== undefined && pattern !== null){ + msg.setParameter('keys', pattern); + } + msg.post().then(function(answer){ + const result = {armored: answer.data}; + if (with_secret_fpr === true + && answer.hasOwnProperty('sec-fprs') + ) { + result.secret_fprs = answer['sec-fprs']; + } + resolve(result); + }, function(error){ + reject(error); + }); }); - }); - }; + }; - /** - * Returns the Key used by default in gnupg. - * (a.k.a. 'primary Key or 'main key'). - * It looks up the gpg configuration if set, or the first key that contains - * a secret key. - * - * @returns {Promise} - * @async - * @static - */ - this.getDefaultKey = function() { - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('config_opt'); - msg.setParameter('component', 'gpg'); - msg.setParameter('option', 'default-key'); - msg.post().then(function(response){ - if (response.value !== undefined - && response.value.hasOwnProperty('string') - && typeof(response.value.string) === 'string' - ){ - me.getKeys(response.value.string,true).then(function(keys){ - if(keys.length === 1){ - resolve(keys[0]); - } else { - reject(gpgme_error('KEY_NO_DEFAULT')); - } - }, function(error){ - reject(error); - }); - } else { - // TODO: this is overly 'expensive' in communication - // and probably performance, too - me.getKeys(null,true).then(function(keys){ - for (let i=0; i < keys.length; i++){ - if (keys[i].get('hasSecret') === true){ - resolve(keys[i]); - break; - } - if (i === keys.length -1){ - reject(gpgme_error('KEY_NO_DEFAULT')); + /** + * Returns the Key used by default in gnupg. + * (a.k.a. 'primary Key or 'main key'). + * It looks up the gpg configuration if set, or the first key that + * contains a secret key. + * + * @returns {Promise} + * @async + * @static + */ + this.getDefaultKey = function() { + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('config_opt'); + msg.setParameter('component', 'gpg'); + msg.setParameter('option', 'default-key'); + msg.post().then(function(response){ + if (response.value !== undefined + && response.value.hasOwnProperty('string') + && typeof(response.value.string) === 'string' + ){ + me.getKeys(response.value.string,true).then( + function(keys){ + if(keys.length === 1){ + resolve(keys[0]); + } else { + reject(gpgme_error('KEY_NO_DEFAULT')); + } + }, function(error){ + reject(error); + }); + } else { + // TODO: this is overly 'expensive' in communication + // and probably performance, too + me.getKeys(null,true).then(function(keys){ + for (let i=0; i < keys.length; i++){ + if (keys[i].get('hasSecret') === true){ + resolve(keys[i]); + break; + } + if (i === keys.length -1){ + reject(gpgme_error('KEY_NO_DEFAULT')); + } } - } - }, function(error){ - reject(error); - }); - } - }, function(error){ - reject(error); + }, function(error){ + reject(error); + }); + } + }, function(error){ + reject(error); + }); }); - }); - }; + }; - /** - * @typedef {Object} importResult The result of a Key update - * @property {Object} summary Numerical summary of the result. See the - * feedbackValues variable for available Keys values and the gnupg - * documentation. - * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html - * for details on their meaning. - * @property {Array} Keys Array of Object containing - * GPGME_Keys with additional import information - * - */ + /** + * @typedef {Object} importResult The result of a Key update + * @property {Object} summary Numerical summary of the result. See the + * feedbackValues variable for available Keys values and the gnupg + * documentation. + * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html + * for details on their meaning. + * @property {Array} Keys Array of Object containing + * GPGME_Keys with additional import information + * + */ - /** - * @typedef {Object} importedKeyResult - * @property {GPGME_Key} key The resulting key - * @property {String} status: - * 'nochange' if the Key was not changed, - * 'newkey' if the Key was imported in gpg, and did not exist previously, - * 'change' if the key existed, but details were updated. For details, - * Key.changes is available. - * @property {Boolean} changes.userId Changes in userIds - * @property {Boolean} changes.signature Changes in signatures - * @property {Boolean} changes.subkey Changes in subkeys - */ + /** + * @typedef {Object} importedKeyResult + * @property {GPGME_Key} key The resulting key + * @property {String} status: + * 'nochange' if the Key was not changed, + * 'newkey' if the Key was imported in gpg, and did not exist + * previously, + * 'change' if the key existed, but details were updated. For details, + * Key.changes is available. + * @property {Boolean} changes.userId Changes in userIds + * @property {Boolean} changes.signature Changes in signatures + * @property {Boolean} changes.subkey Changes in subkeys + */ - /** - * Import an armored Key block into gnupg. Note that this currently will - * not succeed on private Key blocks. - * @param {String} armored Armored Key block of the Key(s) to be imported - * into gnupg - * @param {Boolean} prepare_sync prepare the keys for synched use - * (see {@link getKeys}). - * @returns {Promise} A summary and Keys considered. - * @async - * @static - */ - this.importKey = function (armored, prepare_sync) { - let feedbackValues = ['considered', 'no_user_id', 'imported', - 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', - 'new_signatures', 'new_revocations', 'secret_read', - 'secret_imported', 'secret_unchanged', 'skipped_new_keys', - 'not_imported', 'skipped_v3_keys']; - if (!armored || typeof(armored) !== 'string'){ - return Promise.reject(gpgme_error('PARAM_WRONG')); - } - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('import'); - msg.setParameter('data', armored); - msg.post().then(function(response){ - let infos = {}; - let fprs = []; - for (let res=0; res} A summary and Keys considered. + * @async + * @static + */ + this.importKey = function (armored, prepare_sync) { + let feedbackValues = ['considered', 'no_user_id', 'imported', + 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', + 'new_signatures', 'new_revocations', 'secret_read', + 'secret_imported', 'secret_unchanged', 'skipped_new_keys', + 'not_imported', 'skipped_v3_keys']; + if (!armored || typeof(armored) !== 'string'){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('import'); + msg.setParameter('data', armored); + msg.post().then(function(response){ + let infos = {}; + let fprs = []; + for (let res=0; res} - * @async - * @static - */ - this.deleteKey = function(fingerprint){ - if (isFingerprint(fingerprint) === true) { - let key = createKey(fingerprint); - return key.delete(); - } else { - return Promise.reject(gpgme_error('KEY_INVALID')); - } - }; + /** + * Convenience function for deleting a Key. See {@link Key.delete} for + * further information about the return values. + * @param {String} fingerprint + * @returns {Promise} + * @async + * @static + */ + this.deleteKey = function(fingerprint){ + if (isFingerprint(fingerprint) === true) { + let key = createKey(fingerprint); + return key.delete(); + } else { + return Promise.reject(gpgme_error('KEY_INVALID')); + } + }; - /** - * Generates a new Key pair directly in gpg, and returns a GPGME_Key - * representing that Key. Please note that due to security concerns, secret - * Keys can not be deleted or exported from inside gpgme.js. - * - * @param {String} userId The user Id, e.g. 'Foo Bar ' - * @param {String} algo (optional) algorithm (and optionally key size) to - * be used. See {@link supportedKeyAlgos} below for supported values. - * @param {Date} expires (optional) Expiration date. If not set, expiration - * will be set to 'never' - * - * @return {Promise} - * @async - */ - this.generateKey = function (userId, algo = 'default', expires){ - if ( - typeof(userId) !== 'string' || - supportedKeyAlgos.indexOf(algo) < 0 || - (expires && !(expires instanceof Date)) - ){ - return Promise.reject(gpgme_error('PARAM_WRONG')); - } - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('createkey'); - msg.setParameter('userid', userId); - msg.setParameter('algo', algo ); - if (expires){ - msg.setParameter('expires', - Math.floor(expires.valueOf()/1000)); + /** + * Generates a new Key pair directly in gpg, and returns a GPGME_Key + * representing that Key. Please note that due to security concerns, + * secret Keys can not be deleted or exported from inside gpgme.js. + * + * @param {String} userId The user Id, e.g. 'Foo Bar ' + * @param {String} algo (optional) algorithm (and optionally key size) + * to be used. See {@link supportedKeyAlgos} below for supported + * values. + * @param {Date} expires (optional) Expiration date. If not set, + * expiration will be set to 'never' + * + * @return {Promise} + * @async + */ + this.generateKey = function (userId, algo = 'default', expires){ + if ( + typeof(userId) !== 'string' || + supportedKeyAlgos.indexOf(algo) < 0 || + (expires && !(expires instanceof Date)) + ){ + return Promise.reject(gpgme_error('PARAM_WRONG')); } - msg.post().then(function(response){ - me.getKeys(response.fingerprint, true).then( - // TODO make prepare_sync (second parameter) optional here. - function(result){ - resolve(result); - }, function(error){ - reject(error); - }); - }, function(error) { - reject(error); + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('createkey'); + msg.setParameter('userid', userId); + msg.setParameter('algo', algo ); + if (expires){ + msg.setParameter('expires', + Math.floor(expires.valueOf()/1000)); + } + msg.post().then(function(response){ + me.getKeys(response.fingerprint, true).then( + // TODO prepare_sync? + function(result){ + resolve(result); + }, function(error){ + reject(error); + }); + }, function(error) { + reject(error); + }); }); - }); - }; -} + }; + } } /** -- cgit v1.2.3 From 9d247b7fd5edd11fb5710a057baec671276f5034 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 31 Jul 2018 16:54:43 +0200 Subject: js: Fix Key.hasSecret answer -- * The comparision result between Keyring and Keyring with secrets was set to the wrong Object which was not returned at all. --- lang/js/src/Keyring.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 31c4f92b..8715a47d 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -46,7 +46,7 @@ export class GPGME_Keyring { * resort to a refresh() first. * @param {Boolean} search (optional) retrieve Keys from external * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup) - * @returns {Promise.|GPGME_Error>} + * @returns {Promise>} * @static * @async */ @@ -79,9 +79,9 @@ export class GPGME_Keyring { }; } secondrequest().then(function(answer) { - for (let i=0; i < result.keys.length; i++){ + for (let i=0; i < answer.keys.length; i++){ if (prepare_sync === true){ - result.keys[i].hasSecret = false; + result.keys[i].hasSecret = undefined; if (answer && answer.keys) { for (let j=0; j < answer.keys.length; j++ ){ @@ -91,7 +91,9 @@ export class GPGME_Keyring { a.fingerprint === b.fingerprint ) { if (a.secret === true){ - a.hasSecret = true; + b.hasSecret = true; + } else { + b.hasSecret = false; } break; } -- cgit v1.2.3 From 6313a2de9ee84a9321292f775e4d6c790486d3dc Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 31 Jul 2018 17:35:52 +0200 Subject: js: fix confusion about loop in last commit -- * The aim is to iterate through the results of the first request (all keys), and then add the propert 'hasSecret' to those that are in the second request (secret Keysring) as well. I messed this up in a recent change, and it escaped testing. --- lang/js/src/Keyring.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 8715a47d..d25216c6 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -79,9 +79,8 @@ export class GPGME_Keyring { }; } secondrequest().then(function(answer) { - for (let i=0; i < answer.keys.length; i++){ + for (let i=0; i < result.keys.length; i++){ if (prepare_sync === true){ - result.keys[i].hasSecret = undefined; if (answer && answer.keys) { for (let j=0; j < answer.keys.length; j++ ){ -- cgit v1.2.3 From aeb065acc91a22b6548ebf0a558951ed26398214 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 11:29:10 +0200 Subject: js: simplify getDefaultKey -- * src/Keyring.js: In case no default Key is set in configuration, only Keys reported as having a secret part should be considered for default Keys, avoiding some extra requests. --- lang/js/src/Keyring.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d25216c6..c4b89b2e 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -192,17 +192,13 @@ export class GPGME_Keyring { reject(error); }); } else { - // TODO: this is overly 'expensive' in communication - // and probably performance, too - me.getKeys(null,true).then(function(keys){ - for (let i=0; i < keys.length; i++){ - if (keys[i].get('hasSecret') === true){ - resolve(keys[i]); - break; - } - if (i === keys.length -1){ - reject(gpgme_error('KEY_NO_DEFAULT')); - } + let msg = createMessage('keylist'); + msg.setParameter('secret', true); + msg.post().then(function(result){ + if (result.keys.length === 0){ + reject(gpgme_error('KEY_NO_DEFAULT')); + } else { + resolve(result.keys[0]); } }, function(error){ reject(error); -- cgit v1.2.3 From 715cdc0d7d5bc8d39ff3cc49774c59e5db01c1b6 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 12:03:30 +0200 Subject: js: get default key fixes -- * src/Keyring.js: The answer was not parsed correctly, so a config was being ignored. * If no config is set, we return the first non-invalid key with a secret, instead of the first key (which may be e.g. an expired one) --- lang/js/src/Keyring.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index c4b89b2e..9fdd53b9 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -176,12 +176,13 @@ export class GPGME_Keyring { let msg = createMessage('config_opt'); msg.setParameter('component', 'gpg'); msg.setParameter('option', 'default-key'); - msg.post().then(function(response){ - if (response.value !== undefined - && response.value.hasOwnProperty('string') - && typeof(response.value.string) === 'string' - ){ - me.getKeys(response.value.string,true).then( + msg.post().then(function(resp){ + if (resp.option !== undefined + && resp.option.hasOwnProperty('value') + && resp.option.value.length === 1 + && resp.option.value[0].hasOwnProperty('string') + && typeof(resp.option.value[0].string) === 'string'){ + me.getKeys(resp.option.value[0].string, true).then( function(keys){ if(keys.length === 1){ resolve(keys[0]); @@ -198,7 +199,14 @@ export class GPGME_Keyring { if (result.keys.length === 0){ reject(gpgme_error('KEY_NO_DEFAULT')); } else { - resolve(result.keys[0]); + for (let i=0; i< result.keys.length; i++ ) { + if (result.keys[i].get('invalid') === false) { + resolve(result.keys[i]); + break; + } else if (i === result.keys.length - 1){ + reject(gpgme_error('KEY_NO_DEFAULT')); + } + } } }, function(error){ reject(error); -- cgit v1.2.3 From 43cff5136459c5bca4dca66772eb815f5761c6cd Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 12:13:10 +0200 Subject: js: wrong object assumed in recent commit -- * src/Keyring.js I wrongly assumed an object to be a GPGME_Key, it was the raw answer from nativeMessaging instead. Now it returns a GPGME_Key again. --- lang/js/src/Keyring.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 9fdd53b9..93923c6f 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -200,8 +200,11 @@ export class GPGME_Keyring { reject(gpgme_error('KEY_NO_DEFAULT')); } else { for (let i=0; i< result.keys.length; i++ ) { - if (result.keys[i].get('invalid') === false) { - resolve(result.keys[i]); + if (result.keys[i].invalid === false) { + let k = createKey( + result.keys[i].fingerprint); + k.setKeyData(result.keys[i]); + resolve(k); break; } else if (i === result.keys.length - 1){ reject(gpgme_error('KEY_NO_DEFAULT')); -- cgit v1.2.3 From d65a392670888f86071ca629266ec14b7afb0e46 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 17:07:29 +0200 Subject: js: fix import feedback -- * src/Keyring.js For Key imports without prepare_sync the import feedback was lacking the summary --- lang/js/src/Keyring.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 93923c6f..eb4f60f5 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -327,7 +327,15 @@ export class GPGME_Keyring { status: infos[fprs[i]].status }); } - resolve(resultset); + let summary = {}; + for (let i=0; i < feedbackValues.length; i++ ){ + summary[feedbackValues[i]] = + response[feedbackValues[i]]; + } + resolve({ + Keys:resultset, + summary:summary + }); } }, function(error){ -- cgit v1.2.3 From 90cb4a684211fe5630f209ba61510e8be3129eae Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 16 Aug 2018 17:58:11 +0200 Subject: js: importKey feedback refactor -- * src/Keyring.js: An empty result should no longer cause an error, the import feedback summary has been refactored slightly * Browsertests to reflect import feedback change --- lang/js/src/Keyring.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index eb4f60f5..a6787986 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -274,6 +274,17 @@ export class GPGME_Keyring { msg.post().then(function(response){ let infos = {}; let fprs = []; + let summary = {}; + for (let i=0; i < feedbackValues.length; i++ ){ + summary[feedbackValues[i]] = + response.result[feedbackValues[i]]; + } + if (!response.result.hasOwnProperty('imports') || + response.result.imports.length === 0 + ){ + resolve({Keys:[],summary: summary}); + return; + } for (let res=0; res Date: Fri, 17 Aug 2018 14:40:27 +0200 Subject: js: disallow bulk set data on key from outside -- * src/Key.js Key class is not exported anymore, as it should not be used directly anywhere. setKeyData is no more a method of the Key, (optional) data are now validated and set on Key creation and on updates, both from within this module, thus no longer exposing setKeyData to the outside. * createKey now gained an optional parameter which allows to set Key data at this point. --- lang/js/src/Keyring.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index a6787986..43ab96c8 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -100,8 +100,8 @@ export class GPGME_Keyring { // TODO getArmor() to be used in sync } } - let k = createKey(result.keys[i].fingerprint); - k.setKeyData(result.keys[i]); + let k = createKey(result.keys[i].fingerprint, + !prepare_sync, result.keys[i]); resultset.push(k); } resolve(resultset); @@ -170,7 +170,7 @@ export class GPGME_Keyring { * @async * @static */ - this.getDefaultKey = function() { + this.getDefaultKey = function(prepare_sync = false) { let me = this; return new Promise(function(resolve, reject){ let msg = createMessage('config_opt'); @@ -202,8 +202,9 @@ export class GPGME_Keyring { for (let i=0; i< result.keys.length; i++ ) { if (result.keys[i].invalid === false) { let k = createKey( - result.keys[i].fingerprint); - k.setKeyData(result.keys[i]); + result.keys[i].fingerprint, + !prepare_sync, + result.keys[i]); resolve(k); break; } else if (i === result.keys.length - 1){ -- cgit v1.2.3 From ad39d54d192864b54a155bf5f94d5b6bb3e8612a Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 17 Aug 2018 16:57:41 +0200 Subject: js: removed Key.armor property in synchronous use -- * src/Key.js The synchronous mode for a Key does not offer an armor/ armored property anymore. This frees up a lot of performance issues, also the armored expoort is expected to change quite often, so a cached version is not advisable. * hasSecret/getHasSecret is now refactored, to reflect their uses. With get('hasSecret') there is a method that fetches the result. * src/Key.js also some refactoring --- lang/js/src/Keyring.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 43ab96c8..766bab15 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -38,12 +38,11 @@ export class GPGME_Keyring { * * @param {String | Array} pattern (optional) A pattern to * search for in userIds or KeyIds. - * @param {Boolean} prepare_sync (optional) if set to true, the - * 'hasSecret' and 'armored' properties will be fetched for the Keys as - * well. These require additional calls to gnupg, resulting in a - * performance hungry operation. Calling them here enables direct, - * synchronous use of these properties for all keys, without having to - * resort to a refresh() first. + * @param {Boolean} prepare_sync (optional) if set to true, most data + * (with the exception of armored Key blocks) will be cached for the + * Keys. This enables direct, synchronous use of these properties for + * all keys. It does not check for changes on the backend. The cached + * information can be updated with the {@link Key.refresh} method. * @param {Boolean} search (optional) retrieve Keys from external * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup) * @returns {Promise>} @@ -97,7 +96,6 @@ export class GPGME_Keyring { break; } } - // TODO getArmor() to be used in sync } } let k = createKey(result.keys[i].fingerprint, -- cgit v1.2.3 From 8e87790db3499b1625fd65f3272192df47b5dfd0 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 17 Aug 2018 17:20:35 +0200 Subject: js: don't expire new keys if no date is set -- * src/Keyring.js A new Key without expiration is documented as 'never expire' here, and should behave accordingly. This requires sending '0' here. --- lang/js/src/Keyring.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 766bab15..d18fb649 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -390,6 +390,8 @@ export class GPGME_Keyring { if (expires){ msg.setParameter('expires', Math.floor(expires.valueOf()/1000)); + } else { + msg.setParameter('expires', 0); } msg.post().then(function(response){ me.getKeys(response.fingerprint, true).then( -- cgit v1.2.3 From 1954d27be86b8e4eb801ca6ddcb670f8cfb149f5 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 20 Aug 2018 12:12:43 +0200 Subject: js: revert changes to class read/write restriction -- * undoes 94ee0988d4eaac27785de6efb7c19ca9976e1e9c and e16a87e83910ebb6bfdc4148369165f121f0997e. I do not fully understand why my approach was bad, but I am not in a position to argue. This revert was requested to me after a review, and I'm doing it in the assumption that more experienced people know better than me. * unittests: Also changed some outdated tests that stopped working since 754e799d35fd62d7a979452f44342934659908c7 (as GPGME_Key is not exported, one cannot check for instanceof in the tests anymore) --- lang/js/src/Keyring.js | 675 +++++++++++++++++++++++++------------------------ 1 file changed, 338 insertions(+), 337 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d18fb649..de21736e 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -32,383 +32,384 @@ import { gpgme_error } from './Errors'; */ export class GPGME_Keyring { constructor(){ + } - /** - * Queries Keys (all Keys or a subset) from gnupg. - * - * @param {String | Array} pattern (optional) A pattern to - * search for in userIds or KeyIds. - * @param {Boolean} prepare_sync (optional) if set to true, most data - * (with the exception of armored Key blocks) will be cached for the - * Keys. This enables direct, synchronous use of these properties for - * all keys. It does not check for changes on the backend. The cached - * information can be updated with the {@link Key.refresh} method. - * @param {Boolean} search (optional) retrieve Keys from external - * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup) - * @returns {Promise>} - * @static - * @async - */ - this.getKeys = function(pattern, prepare_sync=false, search=false){ - return new Promise(function(resolve, reject) { - let msg = createMessage('keylist'); - if (pattern !== undefined && pattern !== null){ - msg.setParameter('keys', pattern); - } - msg.setParameter('sigs', true); - if (search === true){ - msg.setParameter('locate', true); - } - msg.post().then(function(result){ - let resultset = []; - if (result.keys.length === 0){ - resolve([]); + /** + * Queries Keys (all Keys or a subset) from gnupg. + * + * @param {String | Array} pattern (optional) A pattern to + * search for in userIds or KeyIds. + * @param {Boolean} prepare_sync (optional) if set to true, most data + * (with the exception of armored Key blocks) will be cached for the + * Keys. This enables direct, synchronous use of these properties for + * all keys. It does not check for changes on the backend. The cached + * information can be updated with the {@link Key.refresh} method. + * @param {Boolean} search (optional) retrieve Keys from external + * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup) + * @returns {Promise>} + * @static + * @async + */ + getKeys (pattern, prepare_sync=false, search=false){ + return new Promise(function(resolve, reject) { + let msg = createMessage('keylist'); + if (pattern !== undefined && pattern !== null){ + msg.setParameter('keys', pattern); + } + msg.setParameter('sigs', true); + if (search === true){ + msg.setParameter('locate', true); + } + msg.post().then(function(result){ + let resultset = []; + if (result.keys.length === 0){ + resolve([]); + } else { + let secondrequest; + if (prepare_sync === true) { + secondrequest = function() { + let msg2 = createMessage('keylist'); + msg2.setParameter('keys', pattern); + msg2.setParameter('secret', true); + return msg2.post(); + }; } else { - let secondrequest; - if (prepare_sync === true) { - secondrequest = function() { - let msg2 = createMessage('keylist'); - msg2.setParameter('keys', pattern); - msg2.setParameter('secret', true); - return msg2.post(); - }; - } else { - secondrequest = function() { - return Promise.resolve(true); - }; - } - secondrequest().then(function(answer) { - for (let i=0; i < result.keys.length; i++){ - if (prepare_sync === true){ - if (answer && answer.keys) { - for (let j=0; - j < answer.keys.length; j++ ){ - const a = answer.keys[j]; - const b = result.keys[i]; - if ( - a.fingerprint === b.fingerprint - ) { - if (a.secret === true){ - b.hasSecret = true; - } else { - b.hasSecret = false; - } - break; + secondrequest = function() { + return Promise.resolve(true); + }; + } + secondrequest().then(function(answer) { + for (let i=0; i < result.keys.length; i++){ + if (prepare_sync === true){ + if (answer && answer.keys) { + for (let j=0; + j < answer.keys.length; j++ ){ + const a = answer.keys[j]; + const b = result.keys[i]; + if ( + a.fingerprint === b.fingerprint + ) { + if (a.secret === true){ + b.hasSecret = true; + } else { + b.hasSecret = false; } + break; } } } - let k = createKey(result.keys[i].fingerprint, - !prepare_sync, result.keys[i]); - resultset.push(k); } - resolve(resultset); - }, function(error){ - reject(error); - }); - } - }); + let k = createKey(result.keys[i].fingerprint, + !prepare_sync, result.keys[i]); + resultset.push(k); + } + resolve(resultset); + }, function(error){ + reject(error); + }); + } }); - }; + }); + } - /** - * @typedef {Object} exportResult The result of a getKeysArmored - * operation. - * @property {String} armored The public Key(s) as armored block. Note - * that the result is one armored block, and not a block per key. - * @property {Array} secret_fprs (optional) list of - * fingerprints for those Keys that also have a secret Key available in - * gnupg. The secret key will not be exported, but the fingerprint can - * be used in operations needing a secret key. - */ + /** + * @typedef {Object} exportResult The result of a getKeysArmored + * operation. + * @property {String} armored The public Key(s) as armored block. Note + * that the result is one armored block, and not a block per key. + * @property {Array} secret_fprs (optional) list of + * fingerprints for those Keys that also have a secret Key available in + * gnupg. The secret key will not be exported, but the fingerprint can + * be used in operations needing a secret key. + */ - /** - * Fetches the armored public Key blocks for all Keys matching the - * pattern (if no pattern is given, fetches all keys known to gnupg). - * @param {String|Array} pattern (optional) The Pattern to - * search for - * @param {Boolean} with_secret_fpr (optional) also return a list of - * fingerprints for the keys that have a secret key available - * @returns {Promise} Object containing the - * armored Key(s) and additional information. - * @static - * @async - */ - this.getKeysArmored = function(pattern, with_secret_fpr) { - return new Promise(function(resolve, reject) { - let msg = createMessage('export'); - msg.setParameter('armor', true); - if (with_secret_fpr === true) { - msg.setParameter('with-sec-fprs', true); - } - if (pattern !== undefined && pattern !== null){ - msg.setParameter('keys', pattern); + /** + * Fetches the armored public Key blocks for all Keys matching the + * pattern (if no pattern is given, fetches all keys known to gnupg). + * @param {String|Array} pattern (optional) The Pattern to + * search for + * @param {Boolean} with_secret_fpr (optional) also return a list of + * fingerprints for the keys that have a secret key available + * @returns {Promise} Object containing the + * armored Key(s) and additional information. + * @static + * @async + */ + getKeysArmored (pattern, with_secret_fpr) { + return new Promise(function(resolve, reject) { + let msg = createMessage('export'); + msg.setParameter('armor', true); + if (with_secret_fpr === true) { + msg.setParameter('with-sec-fprs', true); + } + if (pattern !== undefined && pattern !== null){ + msg.setParameter('keys', pattern); + } + msg.post().then(function(answer){ + const result = {armored: answer.data}; + if (with_secret_fpr === true + && answer.hasOwnProperty('sec-fprs') + ) { + result.secret_fprs = answer['sec-fprs']; } - msg.post().then(function(answer){ - const result = {armored: answer.data}; - if (with_secret_fpr === true - && answer.hasOwnProperty('sec-fprs') - ) { - result.secret_fprs = answer['sec-fprs']; - } - resolve(result); - }, function(error){ - reject(error); - }); + resolve(result); + }, function(error){ + reject(error); }); - }; + }); + } - /** - * Returns the Key used by default in gnupg. - * (a.k.a. 'primary Key or 'main key'). - * It looks up the gpg configuration if set, or the first key that - * contains a secret key. - * - * @returns {Promise} - * @async - * @static - */ - this.getDefaultKey = function(prepare_sync = false) { - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('config_opt'); - msg.setParameter('component', 'gpg'); - msg.setParameter('option', 'default-key'); - msg.post().then(function(resp){ - if (resp.option !== undefined - && resp.option.hasOwnProperty('value') - && resp.option.value.length === 1 - && resp.option.value[0].hasOwnProperty('string') - && typeof(resp.option.value[0].string) === 'string'){ - me.getKeys(resp.option.value[0].string, true).then( - function(keys){ - if(keys.length === 1){ - resolve(keys[0]); - } else { - reject(gpgme_error('KEY_NO_DEFAULT')); - } - }, function(error){ - reject(error); - }); - } else { - let msg = createMessage('keylist'); - msg.setParameter('secret', true); - msg.post().then(function(result){ - if (result.keys.length === 0){ - reject(gpgme_error('KEY_NO_DEFAULT')); + /** + * Returns the Key used by default in gnupg. + * (a.k.a. 'primary Key or 'main key'). + * It looks up the gpg configuration if set, or the first key that + * contains a secret key. + * + * @returns {Promise} + * @async + * @static + */ + getDefaultKey(prepare_sync = false) { + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('config_opt'); + msg.setParameter('component', 'gpg'); + msg.setParameter('option', 'default-key'); + msg.post().then(function(resp){ + if (resp.option !== undefined + && resp.option.hasOwnProperty('value') + && resp.option.value.length === 1 + && resp.option.value[0].hasOwnProperty('string') + && typeof(resp.option.value[0].string) === 'string'){ + me.getKeys(resp.option.value[0].string, true).then( + function(keys){ + if(keys.length === 1){ + resolve(keys[0]); } else { - for (let i=0; i< result.keys.length; i++ ) { - if (result.keys[i].invalid === false) { - let k = createKey( - result.keys[i].fingerprint, - !prepare_sync, - result.keys[i]); - resolve(k); - break; - } else if (i === result.keys.length - 1){ - reject(gpgme_error('KEY_NO_DEFAULT')); - } - } + reject(gpgme_error('KEY_NO_DEFAULT')); } }, function(error){ reject(error); }); - } - }, function(error){ - reject(error); - }); + } else { + let msg = createMessage('keylist'); + msg.setParameter('secret', true); + msg.post().then(function(result){ + if (result.keys.length === 0){ + reject(gpgme_error('KEY_NO_DEFAULT')); + } else { + for (let i=0; i< result.keys.length; i++ ) { + if (result.keys[i].invalid === false) { + let k = createKey( + result.keys[i].fingerprint, + !prepare_sync, + result.keys[i]); + resolve(k); + break; + } else if (i === result.keys.length - 1){ + reject(gpgme_error('KEY_NO_DEFAULT')); + } + } + } + }, function(error){ + reject(error); + }); + } + }, function(error){ + reject(error); }); - }; + }); + } - /** - * @typedef {Object} importResult The result of a Key update - * @property {Object} summary Numerical summary of the result. See the - * feedbackValues variable for available Keys values and the gnupg - * documentation. - * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html - * for details on their meaning. - * @property {Array} Keys Array of Object containing - * GPGME_Keys with additional import information - * - */ + /** + * @typedef {Object} importResult The result of a Key update + * @property {Object} summary Numerical summary of the result. See the + * feedbackValues variable for available Keys values and the gnupg + * documentation. + * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html + * for details on their meaning. + * @property {Array} Keys Array of Object containing + * GPGME_Keys with additional import information + * + */ - /** - * @typedef {Object} importedKeyResult - * @property {GPGME_Key} key The resulting key - * @property {String} status: - * 'nochange' if the Key was not changed, - * 'newkey' if the Key was imported in gpg, and did not exist - * previously, - * 'change' if the key existed, but details were updated. For details, - * Key.changes is available. - * @property {Boolean} changes.userId Changes in userIds - * @property {Boolean} changes.signature Changes in signatures - * @property {Boolean} changes.subkey Changes in subkeys - */ + /** + * @typedef {Object} importedKeyResult + * @property {GPGME_Key} key The resulting key + * @property {String} status: + * 'nochange' if the Key was not changed, + * 'newkey' if the Key was imported in gpg, and did not exist + * previously, + * 'change' if the key existed, but details were updated. For details, + * Key.changes is available. + * @property {Boolean} changes.userId Changes in userIds + * @property {Boolean} changes.signature Changes in signatures + * @property {Boolean} changes.subkey Changes in subkeys + */ - /** - * Import an armored Key block into gnupg. Note that this currently - * will not succeed on private Key blocks. - * @param {String} armored Armored Key block of the Key(s) to be - * imported into gnupg - * @param {Boolean} prepare_sync prepare the keys for synched use - * (see {@link getKeys}). - * @returns {Promise} A summary and Keys considered. - * @async - * @static - */ - this.importKey = function (armored, prepare_sync) { - let feedbackValues = ['considered', 'no_user_id', 'imported', - 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', - 'new_signatures', 'new_revocations', 'secret_read', - 'secret_imported', 'secret_unchanged', 'skipped_new_keys', - 'not_imported', 'skipped_v3_keys']; - if (!armored || typeof(armored) !== 'string'){ - return Promise.reject(gpgme_error('PARAM_WRONG')); - } - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('import'); - msg.setParameter('data', armored); - msg.post().then(function(response){ - let infos = {}; - let fprs = []; - let summary = {}; - for (let i=0; i < feedbackValues.length; i++ ){ - summary[feedbackValues[i]] = - response.result[feedbackValues[i]]; - } - if (!response.result.hasOwnProperty('imports') || - response.result.imports.length === 0 - ){ - resolve({Keys:[],summary: summary}); - return; + /** + * Import an armored Key block into gnupg. Note that this currently + * will not succeed on private Key blocks. + * @param {String} armored Armored Key block of the Key(s) to be + * imported into gnupg + * @param {Boolean} prepare_sync prepare the keys for synched use + * (see {@link getKeys}). + * @returns {Promise} A summary and Keys considered. + * @async + * @static + */ + importKey (armored, prepare_sync) { + let feedbackValues = ['considered', 'no_user_id', 'imported', + 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', + 'new_signatures', 'new_revocations', 'secret_read', + 'secret_imported', 'secret_unchanged', 'skipped_new_keys', + 'not_imported', 'skipped_v3_keys']; + if (!armored || typeof(armored) !== 'string'){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('import'); + msg.setParameter('data', armored); + msg.post().then(function(response){ + let infos = {}; + let fprs = []; + let summary = {}; + for (let i=0; i < feedbackValues.length; i++ ){ + summary[feedbackValues[i]] = + response.result[feedbackValues[i]]; + } + if (!response.result.hasOwnProperty('imports') || + response.result.imports.length === 0 + ){ + resolve({Keys:[],summary: summary}); + return; + } + for (let res=0; res} - * @async - * @static - */ - this.deleteKey = function(fingerprint){ - if (isFingerprint(fingerprint) === true) { - let key = createKey(fingerprint); - return key.delete(); - } else { - return Promise.reject(gpgme_error('KEY_INVALID')); - } - }; + /** + * Convenience function for deleting a Key. See {@link Key.delete} for + * further information about the return values. + * @param {String} fingerprint + * @returns {Promise} + * @async + * @static + */ + deleteKey(fingerprint){ + if (isFingerprint(fingerprint) === true) { + let key = createKey(fingerprint); + return key.delete(); + } else { + return Promise.reject(gpgme_error('KEY_INVALID')); + } + } - /** - * Generates a new Key pair directly in gpg, and returns a GPGME_Key - * representing that Key. Please note that due to security concerns, - * secret Keys can not be deleted or exported from inside gpgme.js. - * - * @param {String} userId The user Id, e.g. 'Foo Bar ' - * @param {String} algo (optional) algorithm (and optionally key size) - * to be used. See {@link supportedKeyAlgos} below for supported - * values. - * @param {Date} expires (optional) Expiration date. If not set, - * expiration will be set to 'never' - * - * @return {Promise} - * @async - */ - this.generateKey = function (userId, algo = 'default', expires){ - if ( - typeof(userId) !== 'string' || - supportedKeyAlgos.indexOf(algo) < 0 || - (expires && !(expires instanceof Date)) - ){ - return Promise.reject(gpgme_error('PARAM_WRONG')); + /** + * Generates a new Key pair directly in gpg, and returns a GPGME_Key + * representing that Key. Please note that due to security concerns, + * secret Keys can not be deleted or exported from inside gpgme.js. + * + * @param {String} userId The user Id, e.g. 'Foo Bar ' + * @param {String} algo (optional) algorithm (and optionally key size) + * to be used. See {@link supportedKeyAlgos} below for supported + * values. + * @param {Date} expires (optional) Expiration date. If not set, + * expiration will be set to 'never' + * + * @return {Promise} + * @async + */ + generateKey(userId, algo = 'default', expires){ + if ( + typeof(userId) !== 'string' || + supportedKeyAlgos.indexOf(algo) < 0 || + (expires && !(expires instanceof Date)) + ){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('createkey'); + msg.setParameter('userid', userId); + msg.setParameter('algo', algo ); + if (expires){ + msg.setParameter('expires', + Math.floor(expires.valueOf()/1000)); + } else { + msg.setParameter('expires', 0); } - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('createkey'); - msg.setParameter('userid', userId); - msg.setParameter('algo', algo ); - if (expires){ - msg.setParameter('expires', - Math.floor(expires.valueOf()/1000)); - } else { - msg.setParameter('expires', 0); - } - msg.post().then(function(response){ - me.getKeys(response.fingerprint, true).then( - // TODO prepare_sync? - function(result){ - resolve(result); - }, function(error){ - reject(error); - }); - }, function(error) { - reject(error); - }); + msg.post().then(function(response){ + me.getKeys(response.fingerprint, true).then( + // TODO prepare_sync? + function(result){ + resolve(result); + }, function(error){ + reject(error); + }); + }, function(error) { + reject(error); }); - }; + }); } } + /** * List of algorithms supported for key generation. Please refer to the gnupg * documentation for details -- cgit v1.2.3 From dd32daad0bb21e3d5567326d0b2e548ff8510431 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 20 Aug 2018 15:12:01 +0200 Subject: js: add and apply eslint rules -- * mainly spacing, see .eslintrc.json for details --- lang/js/src/Keyring.js | 83 +++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 42 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index de21736e..90d267db 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -22,8 +22,8 @@ */ -import {createMessage} from './Message'; -import {createKey} from './Key'; +import { createMessage } from './Message'; +import { createKey } from './Key'; import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; @@ -31,8 +31,6 @@ import { gpgme_error } from './Errors'; * This class offers access to the gnupg keyring */ export class GPGME_Keyring { - constructor(){ - } /** * Queries Keys (all Keys or a subset) from gnupg. @@ -51,7 +49,7 @@ export class GPGME_Keyring { * @async */ getKeys (pattern, prepare_sync=false, search=false){ - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { let msg = createMessage('keylist'); if (pattern !== undefined && pattern !== null){ msg.setParameter('keys', pattern); @@ -60,25 +58,25 @@ export class GPGME_Keyring { if (search === true){ msg.setParameter('locate', true); } - msg.post().then(function(result){ + msg.post().then(function (result){ let resultset = []; if (result.keys.length === 0){ resolve([]); } else { let secondrequest; if (prepare_sync === true) { - secondrequest = function() { + secondrequest = function () { let msg2 = createMessage('keylist'); msg2.setParameter('keys', pattern); msg2.setParameter('secret', true); return msg2.post(); }; } else { - secondrequest = function() { + secondrequest = function () { return Promise.resolve(true); }; } - secondrequest().then(function(answer) { + secondrequest().then(function (answer) { for (let i=0; i < result.keys.length; i++){ if (prepare_sync === true){ if (answer && answer.keys) { @@ -104,7 +102,7 @@ export class GPGME_Keyring { resultset.push(k); } resolve(resultset); - }, function(error){ + }, function (error){ reject(error); }); } @@ -136,7 +134,7 @@ export class GPGME_Keyring { * @async */ getKeysArmored (pattern, with_secret_fpr) { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { let msg = createMessage('export'); msg.setParameter('armor', true); if (with_secret_fpr === true) { @@ -145,15 +143,15 @@ export class GPGME_Keyring { if (pattern !== undefined && pattern !== null){ msg.setParameter('keys', pattern); } - msg.post().then(function(answer){ - const result = {armored: answer.data}; + msg.post().then(function (answer){ + const result = { armored: answer.data }; if (with_secret_fpr === true && answer.hasOwnProperty('sec-fprs') ) { result.secret_fprs = answer['sec-fprs']; } resolve(result); - }, function(error){ + }, function (error){ reject(error); }); }); @@ -169,32 +167,32 @@ export class GPGME_Keyring { * @async * @static */ - getDefaultKey(prepare_sync = false) { + getDefaultKey (prepare_sync = false) { let me = this; - return new Promise(function(resolve, reject){ + return new Promise(function (resolve, reject){ let msg = createMessage('config_opt'); msg.setParameter('component', 'gpg'); msg.setParameter('option', 'default-key'); - msg.post().then(function(resp){ + msg.post().then(function (resp){ if (resp.option !== undefined && resp.option.hasOwnProperty('value') && resp.option.value.length === 1 && resp.option.value[0].hasOwnProperty('string') - && typeof(resp.option.value[0].string) === 'string'){ + && typeof (resp.option.value[0].string) === 'string'){ me.getKeys(resp.option.value[0].string, true).then( - function(keys){ - if(keys.length === 1){ + function (keys){ + if (keys.length === 1){ resolve(keys[0]); } else { reject(gpgme_error('KEY_NO_DEFAULT')); } - }, function(error){ + }, function (error){ reject(error); }); } else { let msg = createMessage('keylist'); msg.setParameter('secret', true); - msg.post().then(function(result){ + msg.post().then(function (result){ if (result.keys.length === 0){ reject(gpgme_error('KEY_NO_DEFAULT')); } else { @@ -211,11 +209,11 @@ export class GPGME_Keyring { } } } - }, function(error){ + }, function (error){ reject(error); }); } - }, function(error){ + }, function (error){ reject(error); }); }); @@ -264,14 +262,14 @@ export class GPGME_Keyring { 'new_signatures', 'new_revocations', 'secret_read', 'secret_imported', 'secret_unchanged', 'skipped_new_keys', 'not_imported', 'skipped_v3_keys']; - if (!armored || typeof(armored) !== 'string'){ + if (!armored || typeof (armored) !== 'string'){ return Promise.reject(gpgme_error('PARAM_WRONG')); } let me = this; - return new Promise(function(resolve, reject){ + return new Promise(function (resolve, reject){ let msg = createMessage('import'); msg.setParameter('data', armored); - msg.post().then(function(response){ + msg.post().then(function (response){ let infos = {}; let fprs = []; let summary = {}; @@ -282,7 +280,7 @@ export class GPGME_Keyring { if (!response.result.hasOwnProperty('imports') || response.result.imports.length === 0 ){ - resolve({Keys:[],summary: summary}); + resolve({ Keys:[],summary: summary }); return; } for (let res=0; res} * @async */ - generateKey(userId, algo = 'default', expires){ + generateKey (userId, algo = 'default', expires){ if ( - typeof(userId) !== 'string' || + typeof (userId) !== 'string' || + // eslint-disable-next-line no-use-before-define supportedKeyAlgos.indexOf(algo) < 0 || (expires && !(expires instanceof Date)) ){ return Promise.reject(gpgme_error('PARAM_WRONG')); } let me = this; - return new Promise(function(resolve, reject){ + return new Promise(function (resolve, reject){ let msg = createMessage('createkey'); msg.setParameter('userid', userId); msg.setParameter('algo', algo ); @@ -394,15 +393,15 @@ export class GPGME_Keyring { } else { msg.setParameter('expires', 0); } - msg.post().then(function(response){ + msg.post().then(function (response){ me.getKeys(response.fingerprint, true).then( // TODO prepare_sync? - function(result){ + function (result){ resolve(result); - }, function(error){ + }, function (error){ reject(error); }); - }, function(error) { + }, function (error) { reject(error); }); }); -- cgit v1.2.3 From 91c2362550f787cc28d764c0e571e911c740f74f Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 20 Aug 2018 17:46:29 +0200 Subject: js: set expiry date on generateKey -- * on the javascript side a Date is expected, gpggme-json expects seconds from 'now' --- lang/js/src/Keyring.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 90d267db..7d9b370a 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -388,8 +388,9 @@ export class GPGME_Keyring { msg.setParameter('userid', userId); msg.setParameter('algo', algo ); if (expires){ + const now = new Date(); msg.setParameter('expires', - Math.floor(expires.valueOf()/1000)); + Math.floor((expires - now) /1000)); } else { msg.setParameter('expires', 0); } -- cgit v1.2.3 From d77a1c887d6a5e892329534c94f95eaf8bb88492 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Mon, 20 Aug 2018 18:05:34 +0200 Subject: js: add option "subkey-algo" to generateKey -- * The option was recently added to gpgme-json; this reflects this on javascript side --- lang/js/src/Keyring.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 7d9b370a..81a047ca 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -366,14 +366,16 @@ export class GPGME_Keyring { * @param {String} userId The user Id, e.g. 'Foo Bar ' * @param {String} algo (optional) algorithm (and optionally key size) * to be used. See {@link supportedKeyAlgos} below for supported - * values. + * values. If ommitted, 'default' is used. * @param {Date} expires (optional) Expiration date. If not set, * expiration will be set to 'never' + * @param {String} subkey_algo (optional) algorithm of the encryption + * subkey. If ommited the same as algo is used. * * @return {Promise} * @async */ - generateKey (userId, algo = 'default', expires){ + generateKey (userId, algo = 'default', expires, subkey_algo){ if ( typeof (userId) !== 'string' || // eslint-disable-next-line no-use-before-define @@ -382,11 +384,18 @@ export class GPGME_Keyring { ){ return Promise.reject(gpgme_error('PARAM_WRONG')); } + // eslint-disable-next-line no-use-before-define + if (subkey_algo && supportedKeyAlgos.indexOf(subkey_algo) < 0 ){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } let me = this; return new Promise(function (resolve, reject){ let msg = createMessage('createkey'); msg.setParameter('userid', userId); msg.setParameter('algo', algo ); + if (subkey_algo) { + msg.setParameter('subkey-algo', subkey_algo ); + } if (expires){ const now = new Date(); msg.setParameter('expires', -- cgit v1.2.3 From 8b8c009dee8ae5493e7f888ee518468dbfcf5375 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Tue, 21 Aug 2018 11:42:11 +0200 Subject: js: set expiry of generatedKey to seconds from now -- * src/Keyring.js: Changed key ecpiration from Date to seconds from creation, as in gpgme. The Date parameter used before was due to a misunderstanding in documentation and requests from potential users. --- lang/js/src/Keyring.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 81a047ca..ab0144ef 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -367,8 +367,8 @@ export class GPGME_Keyring { * @param {String} algo (optional) algorithm (and optionally key size) * to be used. See {@link supportedKeyAlgos} below for supported * values. If ommitted, 'default' is used. - * @param {Date} expires (optional) Expiration date. If not set, - * expiration will be set to 'never' + * @param {Number} expires (optional) Expiration time in seconds from now. + * If not set or set to 0, expiration will be 'never' * @param {String} subkey_algo (optional) algorithm of the encryption * subkey. If ommited the same as algo is used. * @@ -380,7 +380,7 @@ export class GPGME_Keyring { typeof (userId) !== 'string' || // eslint-disable-next-line no-use-before-define supportedKeyAlgos.indexOf(algo) < 0 || - (expires && !(expires instanceof Date)) + (expires && !( Number.isInteger(expires) || expires < 0 )) ){ return Promise.reject(gpgme_error('PARAM_WRONG')); } @@ -397,9 +397,7 @@ export class GPGME_Keyring { msg.setParameter('subkey-algo', subkey_algo ); } if (expires){ - const now = new Date(); - msg.setParameter('expires', - Math.floor((expires - now) /1000)); + msg.setParameter('expires', expires); } else { msg.setParameter('expires', 0); } -- cgit v1.2.3 From 93f674d33d4dacb115398196a7218c28323fd708 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Wed, 22 Aug 2018 12:18:55 +0200 Subject: js: throw errors in sync functions -- * synchronous functions should throw errors if something goes wrong, Promises should reject. This commit changes some error cases that returned Error objects instead of throwing them - src/Key.js: createKey() and sync Key.get() throw errors - src/Error.js: Exporting the list of errors to be able to test and compare against these strings - src/Keyring.js: Setting a null value in pattern is not useful, and now caused an error with the new changes. - src/Message.js: createMessage and Message.setParameter now throw errors --- lang/js/src/Keyring.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lang/js/src/Keyring.js') diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index ab0144ef..cb053ba1 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -67,7 +67,9 @@ export class GPGME_Keyring { if (prepare_sync === true) { secondrequest = function () { let msg2 = createMessage('keylist'); - msg2.setParameter('keys', pattern); + if (pattern){ + msg2.setParameter('keys', pattern); + } msg2.setParameter('secret', true); return msg2.post(); }; -- cgit v1.2.3