[git] GPGME - branch, javascript-binding, updated. gpgme-1.11.1-6-gd62f66b
by Maximilian Krambach
cvs at cvs.gnupg.org
Mon Apr 23 17:28:55 CEST 2018
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG Made Easy".
The branch, javascript-binding has been updated
via d62f66b1fb47f2075770d896f672748a4136e70b (commit)
from 6ab25e40d904007755c5d999bf66ae264236e745 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit d62f66b1fb47f2075770d896f672748a4136e70b
Author: Maximilian Krambach <maximilian.krambach at intevation.de>
Date: Mon Apr 23 17:18:46 2018 +0200
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.
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 784929e..87ec8cf 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -26,31 +26,19 @@ import { GPGME_Message } from "./Message";
* expected.
*/
import { permittedOperations } from './permittedOperations'
+import { GPGMEJS_Error} from "./Errors"
+/**
+ * A Connection handles the nativeMessaging interaction.
+ */
export class Connection{
- /**
- * Opens and closes a port. Thus, it is made sure that the connection can
- * be used.
- * THIS BEHAVIOUR MAY CHANGE!
- * discussion is to keep a port alive as long as the context stays the same
- *
- * TODO returns nothing, but triggers exceptions if not successfull
- */
constructor(){
- this._connection = chrome.runtime.connectNative('gpgmejson');
- if (!this._connection){
- if (chrome.runtime.lastError){
- throw('NO_CONNECT_RLE');
- } else {
- throw('NO_CONNECT');
- }
- }
- this._flags = {}; // TODO general config
+ this.connect();
}
/**
- * Immediately closes the open port
+ * Immediately closes the open port.
*/
disconnect() {
if (this._connection){
@@ -59,26 +47,55 @@ export class Connection{
}
/**
+ * Opens a nativeMessaging port.
+ * returns nothing, but triggers errors if not successfull:
+ * NO_CONNECT: connection not successfull, chrome.runtime.lastError may be
+ * available
+ * ALREADY_CONNECTED: There is already a connection present.
+ */
+ connect(){
+ if (this._connection){
+ return new GPGMEJS_Error('ALREADY_CONNECTED');
+ }
+ this._connection = chrome.runtime.connectNative('gpgmejson');
+ if (!this._connection){
+ return new GPGMEJS_Error('NO_CONNECT');
+ }
+ }
+
+ /**
+ * checks if the connection is established
+ * TODO: some kind of ping to see if the other side responds as expected?
+ * @returns {Boolean}
+ */
+ get connected(){
+ return this._connection ? true: false;
+ }
+
+ /**
* Sends a message and resolves with the answer.
* @param {GPGME_Message} message
* @returns {Promise<Object>} the gnupg answer, or rejection with error
- * information
- * TODO: better/more consistent error information
+ * information.
*/
post(message){
if (!message || !message instanceof GPGME_Message){
- return Promise.reject('ERR_NO_MSG');
+ return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ }
+ if (message.isComplete !== true){
+ return Promise.reject(new GPGMEJS_Error('MSG_INCOMPLETE'));
}
// let timeout = 5000; //TODO config
let me = this;
return new Promise(function(resolve, reject){
- let answer = new Answer(message.op);
+ let answer = new Answer(message.operation);
let listener = function(msg) {
if (!msg){
me._connection.onMessage.removeListener(listener)
- reject('EMPTY_ANSWER');
+ reject(new GPGMEJS_Error('EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener)
+ //TODO: GPGMEJS_Error?
reject(msg.msg);
} else {
answer.add(msg);
@@ -92,12 +109,12 @@ export class Connection{
};
me._connection.onMessage.addListener(listener);
- me._connection.postMessage(message);
+ me._connection.postMessage(message.message);
//TBD: needs to be aware if there is a pinentry pending
// setTimeout(
// function(){
// me.disconnect();
- // reject('TIMEOUT');
+ // reject(new GPGMEJS_Error('TIMEOUT', 5000));
// }, timeout);
});
}
@@ -105,8 +122,8 @@ export class Connection{
/**
* A class for answer objects, checking and processing the return messages of
- * the nativeMessaging communication
- * @param {String} operation The operation, to look up validity of return keys
+ * the nativeMessaging communication.
+ * @param {String} operation The operation, to look up validity of returning messages
*/
class Answer{
@@ -115,9 +132,8 @@ class Answer{
}
/**
- *
+ * Add the information to the answer
* @param {Object} msg The message as received with nativeMessaging
- * TODO: "error" and "more" handling are not in here, but in post()
*/
add(msg){
if (this._response === undefined){
@@ -130,9 +146,7 @@ class Answer{
switch (key) {
case 'type':
if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
- console.log( 'unexpected answer type: ' + msg.type);
- throw('UNEXPECTED_TYPE');
-
+ return new GPGMEJS_Error('UNEXPECTED_ANSWER');
}
break;
case 'more':
@@ -151,7 +165,7 @@ class Answer{
this._response[key] = msg[key];
}
else if (this._response[key] !== msg[key]){
- throw('UNEXPECTED_TYPE');
+ return new GPGMEJS_Error('UNEXPECTED_ANSWER',msg[key]);
}
}
//infos may be json objects etc. Not yet defined.
@@ -163,8 +177,7 @@ class Answer{
this._response.push(msg[key]);
}
else {
- console.log('unexpected answer parameter: ' + key);
- throw('UNEXPECTED_PARAM');
+ return new GPGMEJS_Error('UNEXPECTED_ANSWER', key);
}
break;
}
@@ -172,7 +185,8 @@ class Answer{
}
/**
- * Returns the assembled message. TODO: does not care yet if completed.
+ * @returns {Object} the assembled message.
+ * TODO: does not care yet if completed.
*/
get message(){
return this._response;
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
new file mode 100644
index 0000000..c2356f7
--- /dev/null
+++ b/lang/js/src/Errors.js
@@ -0,0 +1,148 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+// This is a preliminary collection of erors and warnings to be thrown and implemented.
+
+// general idea: if throw , throw the NAME
+// return false || 'return' property
+
+//TODO: Connection.NOCONNECT promise
+//connection.timeout: Be aware of pinentry
+
+export class GPGMEJS_Error {
+
+ constructor(code = 'GENERIC_ERROR', details){
+ let config = { //TODO TEMP
+ debug: 'console', // |'alert'
+ throw: 'default' // | 'always' | 'never'
+ };
+ let errors = { //TODO: someplace else
+ //Connection errors
+ 'ALREADY_CONNECTED':{
+ msg: 'The connection was already established. The action would overwrite the context',
+ throw: true
+ },
+ 'NO_CONNECT': {
+ msg:'Connection with the nativeMessaging host could not be established.',
+ throw: true
+ },
+ 'EMPTY_GPG_ANSWER':{
+ msg: 'The nativeMesaging answer was empty',
+ throw: true
+ },
+ 'TIMEOUT': {
+ msg: 'A timeout was exceeded.',
+ throw: false
+ },
+
+ 'UNEXPECTED_ANSWER': {
+ msg: 'The answer from gnupg was not as expected',
+ throw: true
+ },
+
+ // Message/Data Errors
+
+ 'NO_KEYS' : {
+ msg: 'There were no valid keys provided.',
+ throw: true
+ },
+ 'NOT_A_FPR': {
+ msg: 'The String is not an accepted fingerprint',
+ throw: false
+ },
+ 'MSG_INCOMPLETE': {
+ msg: 'The Message did not match the minimum requirements for the interaction',
+ throw: true
+ },
+ 'EMPTY_MSG' : {
+ msg: 'The Message has no data.',
+ throw: true
+ },
+ 'MSG_NODATA':{
+ msg: 'The data sent is empty. This may be unintentional.',
+ throw: false
+ },
+ 'MSG_OP_PENDING': {
+ msg: 'There is no operation specified yet. The parameter cannot be set',
+ throw: false
+ },
+ 'WRONG_OP': {
+ msg: "The operation requested could not be found",
+ throw: true
+ },
+
+ //generic errors
+
+ 'WRONGPARAM':{
+ msg: 'invalid parameter was found',
+ throw: true
+ },
+ 'WRONGTYPE':{
+ msg: 'invalid parameter type was found',
+ throw: true
+ },
+ 'NOT_IMPLEMENTED': {
+ msg: 'A openpgpjs parameter was submitted that is not implemented',
+ throw: true
+ },
+ 'GENERIC_ERROR': {
+ msg: 'Unspecified error',
+ throw: true
+ },
+
+ // hopefully temporary errors
+
+ 'NOT_YET_IMPLEMENTED': {
+ msg: 'Support of this is probable, but it is not implemented yet',
+ throw: false
+ }
+ }
+ if (!errors.hasOwnProperty(code)){
+ throw('GENERIC_ERROR');
+ }
+ let msg = code;
+ if (errors[code].msg !== undefined){
+ msg = msg + ': ' + errors[code].msg;
+ }
+ if (details){
+ msg = msg + ' ' + details;
+ }
+ if (config.debug === 'console'){
+ console.log(msg);
+ } else if (config.debug === 'alert'){
+ alert(msg);
+ }
+ switch (config.throw) {
+ case 'default':
+ if (errors[code].throw === true){
+ throw(code);
+ }
+ break;
+ case 'always':
+ throw(code);
+ break;
+
+ case 'never':
+ break;
+ default:
+ throw('GENERIC_ERROR');
+ }
+ }
+}
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index eeb7a3c..922ca06 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -1,3 +1,5 @@
+import { GPGMEJS_Error } from "./Errors";
+
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
@@ -21,33 +23,46 @@
/**
* Tries to return an array of fingerprints, either from input fingerprints or
* from Key objects
- * @param {String|Array<String>} input Input value.
+ * @param {Key |Array<Key>| GPGME_Key | Array<GPGME_Key>|String|Array<String>} input
+ * @param {Boolean} nocheck if set, an empty result is acceptable
* @returns {Array<String>} Array of fingerprints.
*/
-export function toKeyIdArray(input){
+
+export function toKeyIdArray(input, nocheck){
if (!input){
- return [];
- // TODO: Warning or error here? Did we expect something or is "nothing" okay?
+ return (nocheck ===true)? [] : new GPGMEJS_Error('NO_KEYS');
+ }
+ if (!Array.isArray(input)){
+ input = [input];
}
- if (input instanceof Array){
- let result = [];
- for (let i=0; i < input.length; i++){
+ let result = [];
+ for (let i=0; i < input.length; i++){
+ if (typeof(input[i]) === 'string'){
if (isFingerprint(input[i]) === true){
result.push(input[i]);
} else {
- //TODO error?
- console.log('gpgmejs/Helpers.js Warning: '+
- input[i] +
- ' is not a valid key fingerprint and will not be used');
+ GPGMEJS_Error
+ }
+ } else if (typeof(input[i]) === 'object'){
+ let fpr = '';
+ if (input[i] instanceof GPGME_Key){
+ fpr = input[i].fingerprint;
+ } else if (input[i].hasOwnProperty(primaryKey) &&
+ input[i].primaryKey.hasOwnProperty(getFingerprint)){
+ fpr = input[i].primaryKey.getFingerprint();
+ }
+ if (isFingerprint(fpr) === true){
+ result.push(fpr);
}
+ } else {
+ return new GPGMEJS_Error('WRONGTYPE');
}
+ }
+ if (result.length === 0){
+ return (nocheck===true)? [] : new GPGMEJS_Error('NO_KEYS');
+ } else {
return result;
- } else if (isFingerprint(input) === true) {
- return [input];
}
- console.log('gpgmejs/Helpers.js Warning: ' + input +
- ' is not a valid key fingerprint and will not be used');
- return [];
};
/**
@@ -72,13 +87,14 @@ function hextest(key, len){
export function isFingerprint(string){
return hextest(string, 40);
};
-
-//TODO needed anywhere?
+/**
+ * check if the input is a valid Hex string with a length of 16
+ */
function isLongId(string){
return hextest(string, 16);
};
-//TODO needed anywhere?
+// TODO still not needed anywhere
function isShortId(string){
return hextest(string, 8);
};
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
new file mode 100644
index 0000000..d8f16c5
--- /dev/null
+++ b/lang/js/src/Key.js
@@ -0,0 +1,201 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+/**
+ * The key class allows to query the information defined in gpgme Key Objects
+ * (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)
+ *
+ * This is a stub, as the gpgme-json side is not yet implemented
+ *
+ */
+
+import {isFingerprint} from './Helpers'
+import {GPGMEJS_Error} from './Errors'
+
+export class GPGME_Key {
+
+ constructor(fingerprint){
+ if (isFingerprint(fingerprint) === true){
+ this._fingerprint = fingerprint;
+ } else {
+ return new GPGMEJS_Error('WRONGPARAM', 'Key.js: invalid fingerprint');
+ }
+ }
+
+ get fingerprint(){
+ return this._fingerprint;
+ }
+
+ /**
+ * hasSecret returns true if a secret subkey is included in this Key
+ */
+ get hasSecret(){
+ checkKey(this._fingerprint, 'secret').then( function(result){
+ return Promise.resolve(result);
+ });
+
+ }
+
+ get isRevoked(){
+ return checkKey(this._fingerprint, 'revoked');
+ }
+
+ get isExpired(){
+ return checkKey(this._fingerprint, 'expired');
+ }
+
+ get isDisabled(){
+ return checkKey(this._fingerprint, 'disabled');
+ }
+
+ get isInvalid(){
+ return checkKey(this._fingerprint, 'invalid');
+ }
+
+ get canEncrypt(){
+ return checkKey(this._fingerprint, 'can_encrypt');
+ }
+
+ get canSign(){
+ return checkKey(this._fingerprint, 'can_sign');
+ }
+
+ get canCertify(){
+ return checkKey(this._fingerprint, 'can_certify');
+ }
+
+ get canAuthenticate(){
+ return checkKey(this._fingerprint, 'can_authenticate');
+ }
+
+ get isQualified(){
+ return checkKey(this._fingerprint, 'is_qualified');
+ }
+
+ get armored(){
+ let me = this;
+ return new Promise(function(resolve, reject){
+ let conn = new Connection();
+ conn.setFlag('armor', true);
+ conn.post('export',{'fpr': me._fingerprint});
+ });
+ // TODO return value not yet checked. Should result in an armored block
+ // in correct encoding
+ // TODO openpgpjs also returns secKey if private = true?
+ }
+
+ /**
+ * TODO returns true if this is the default key used to sign
+ */
+ get isDefault(){
+ throw('NOT_YET_IMPLEMENTED');
+ }
+
+ /**
+ * get the Key's subkeys as GPGME_Key objects
+ * @returns {Array<GPGME_Key>}
+ */
+ get subkeys(){
+ return checkKey(this._fingerprint, 'subkeys').then(function(result){
+ // TBD expecting a list of fingerprints
+ if (!Array.isArray(result)){
+ result = [result];
+ }
+ let resultset = [];
+ for (let i=0; i < result.length; i++){
+ let subkey = new GPGME_Key(result[i]);
+ if (subkey instanceof GPGME_Key){
+ resultset.push(subkey);
+ }
+ }
+ return Promise.resolve(resultset);
+ });
+ }
+
+ /**
+ * creation time stamp of the key
+ * @returns {Date|null} TBD
+ */
+ get timestamp(){
+ return checkKey(this._fingerprint, 'timestamp');
+ //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available.
+ }
+
+ /**
+ * The expiration timestamp of this key TBD
+ * @returns {Date|null} TBD
+ */
+ get expires(){
+ return checkKey(this._fingerprint, 'expires');
+ // TODO convert to Date; check for 0
+ }
+
+ /**
+ * getter name TBD
+ * @returns {String|Array<String>} The user ids associated with this key
+ */
+ get userIds(){
+ return checkKey(this._fingerprint, 'uids');
+ }
+
+ /**
+ * @returns {String} The public key algorithm supported by this subkey
+ */
+ get pubkey_algo(){
+ return checkKey(this._fingerprint, 'pubkey_algo');
+ }
+};
+
+/**
+ * generic function to query gnupg information on a key.
+ * @param {*} fingerprint The identifier of the Keyring
+ * @param {*} property The gpgme-json property to check
+ *
+ */
+function checkKey(fingerprint, property){
+ return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+
+ return new Promise(function(resolve, reject){
+ if (!isFingerprint(fingerprint)){
+ reject('not a fingerprint'); //TBD
+ }
+ let conn = new Connection();
+ conn.post('getkey',{ // TODO not yet implemented in gpgme
+ 'fingerprint': this.fingerprint})
+ .then(function(result){
+ if (property !== undefined){
+ if (result.hasOwnProperty(key)){
+ resolve(result[property]);
+ }
+ else if (property == 'secret'){
+ // property undefined means "not true" in case of secret
+ resolve(false);
+ } else {
+ reject('ERR_INVALID_PROPERTY') //TBD
+ }
+ }
+
+
+ resolve(result);
+ }, function(error){
+ reject(error);
+ });
+ });
+};
\ No newline at end of file
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
new file mode 100644
index 0000000..52fa7f7
--- /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 <http://www.gnu.org/licenses/>.
+ * 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.<Array<GPGME_Key>>}
+ *
+ */
+ 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<String fingerprints>
+ 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<GPGME_Key>}
+ *
+ */
+ 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);
+ });
+ }
+
+};
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 90b554a..6a93b6f 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -18,21 +18,24 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
import { permittedOperations } from './permittedOperations'
-
+import { GPGMEJS_Error } from './Errors'
export class GPGME_Message {
//TODO getter
- constructor(){
+ constructor(operation){
+ if (operation){
+ this.operation(operation);
+ }
}
/**
* Defines the operation this message will have
- * @param {String} operation Mus be defined in permittedOperations
+ * @param {String} operation Must be defined in permittedOperations
* TODO: move to constructor?
*/
set operation (operation){
if (!operation || typeof(operation) !== 'string'){
- throw('ERR_WRONG_PARAM');
+ return new GPGMEJS_Error('WRONGPARAM');
}
if (operation in permittedOperations){
if (!this._msg){
@@ -40,10 +43,14 @@ export class GPGME_Message {
}
this._msg.op = operation;
} else {
- throw('ERR_NOT_IMPLEMENTED');
+ return new GPGMEJS_Error('WRONG_OP');
}
}
+ get operation(){
+ return this._msg.op;
+ }
+
/**
* Sets a parameter for the message. Note that the operation has to be set
* first, to be able to check if the parameter is permittted
@@ -53,25 +60,20 @@ export class GPGME_Message {
*/
setParameter(param,value){
if (!param || typeof(param) !== 'string'){
- throw('ERR_WRONG_PARAM');
+ return new GPGMEJS_Error('WRONGPARAM', 'type check failed');
}
if (!this._msg || !this._msg.op){
- console.log('There is no operation specified yet. '+
- 'The parameter cannot be set');
- return false;
+ return new GPGMEJS_Error('MSG_OP_PENDING');
}
let po = permittedOperations[this._msg.op];
if (!po){
- throw('LAZY_PROGRAMMER');
- //TODO
- return false;
+ return new GPGMEJS_Error('WRONG_OP', param);
}
if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
this._msg[param] = value;
return true;
}
- console.log('' + param + ' is invalid and could not be set');
- return false;
+ return new GPGMEJS_Error('WRONGPARAM', param);
}
/**
@@ -85,7 +87,9 @@ export class GPGME_Message {
}
let reqParams = permittedOperations[this._msg.op].required;
for (let i=0; i < reqParams.length; i++){
- if (!reqParams[i] in this._msg){
+
+ if (!this._msg.hasOwnProperty(reqParams[i])){
+ console.log(reqParams[i] + 'missing');
return false;
}
}
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index 8323ac3..c23a356 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -21,26 +21,54 @@
import {Connection} from "./Connection"
import {GPGME_Message} from './Message'
import {toKeyIdArray} from "./Helpers"
+import {GPGMEJS_Error as Error, GPGMEJS_Error} from "./Errors"
export class GpgME {
/**
- * initial check if connection si successfull. Will throw ERR_NO_CONNECT or
- * ERR_NO_CONNECT_RLE (if chrome.runtime.lastError is available) if the
- * connection fails.
- * TODO The connection to the nativeMessaging host will, for now, be closed
- * after each interaction. Session management with gpg_agent is TBD.
+ * initializes GpgME by opening a nativeMessaging port
* TODO: add configuration
*/
- constructor(){
- let conn = new Connection();
- // this.keyring = new Keyring(); TBD
- // TODO config, e.g.
- this.configuration = {
- null_expire_is_never: true
- };
- conn.disconnect();
+ constructor(configuration = {
+ null_expire_is_never: false
+ }){
+ this._connection = new Connection;
+ }
+
+ /**
+ * refreshes the nativeApp connection
+ */
+ reconnect(){
+ if (!this._connection || ! this._connection instanceof Connection){
+ this._connection = new Connection;
+ } else {
+ this._connection.disconnect();
+ this._connection.connect();
+ }
+ }
+
+ /**
+ * inmediately tries to destroy the nativeMessaging connection.
+ * TODO: may not be included in final API, as it is redundant.
+ * For now, it just serves paranoia
+ */
+ disconnect(){
+ if (this._connection){
+ this._connection.disconnect();
+ this._connection = null;
+ }
+ }
+
+ /**
+ * tests the nativeApp connection
+ */
+ get connected(){
+ if (!this._connection || ! this._connection instanceof Connection){
+ return false;
+ }
+ return this._connection.connected;
}
+
/**
* @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array
* @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message
@@ -62,14 +90,7 @@ export class GpgME {
if (wildcard === true){msg.setParameter('throw-keyids', true);
};
- if (msg.isComplete === true) {
- let conn = new Connection();
- return (conn.post(msg.message));
- }
- else {
- return Promise.reject('NO_CONNECT');
- //TODO
- }
+ return (this._connection.post(msg));
}
/**
@@ -85,22 +106,47 @@ export class GpgME {
decrypt(data){
if (data === undefined){
- throw('ERR_EMPTY_MSG');
+ return Promise.reject(new GPGMEJS_Error ('EMPTY_MSG'));
}
let msg = new GPGME_Message;
msg.operation = 'decrypt';
putData(msg, data);
- // TODO: needs proper EOL to be decrypted.
+ return this._connection.post(msg);
+
+ }
- if (msg.isComplete === true){
- let conn = new Connection();
- return conn.post(msg.message);
+ deleteKey(key, delete_secret = false, no_confirm = false){
+ return Promise.reject(new GPGMEJS_Error ('NOT_YET_IMPLEMENTED'));
+ let msg = new GPGME_Message;
+ msg.operation = 'deletekey';
+ let key_arr = toKeyIdArray(key);
+ if (key_arr.length !== 1){
+ throw('TODO');
+ //should always be ONE key
+ }
+ msg.setParameter('key', key_arr[0]);
+ if (delete_secret === true){
+ msg.setParameter('allow_secret', true); //TBD
}
- else {
- return Promise.reject('NO_CONNECT');
- //TODO
+ if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
+ msg.setParameter('delete_force', true); //TBD
}
+ this._connection.post(msg).then(function(success){
+ //TODO: it seems that there is always errors coming back:
+ }, function(error){
+ switch (error.msg){
+ case 'ERR_NO_ERROR':
+ return Promise.resolve('okay'); //TBD
+ default:
+ return Promise.reject(new GPGMEJS_Error);
+ // INV_VALUE,
+ // GPG_ERR_NO_PUBKEY,
+ // GPG_ERR_AMBIGUOUS_NAME,
+ // GPG_ERR_CONFLICT
+ }
+ });
}
+
}
/**
@@ -112,7 +158,7 @@ export class GpgME {
*/
function putData(message, data){
if (!message || !message instanceof GPGME_Message ) {
- throw('NO_MESSAGE_OBJECT');
+ return new GPGMEJS_Error('WRONGPARAM');
}
if (!data){
//TODO Debug only! No data is legitimate
@@ -126,6 +172,6 @@ function putData(message, data){
message.setParameter('base64', false);
message.setParameter('data', data);
} else {
- throw('ERR_WRONG_TYPE');
+ return new GPGMEJS_Error('WRONGPARAM');
}
}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index 1eec4da..54b9dd4 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -25,13 +25,18 @@
*/
import { GpgME } from "./gpgmejs";
-// import {Keyring} from "./Keyring" TODO
-
+ import {GPGME_Keyring} from "./Keyring"
+ import { GPGME_Key } from "./Key";
+ import { isFingerprint } from "./Helpers"
+ import { GPGMEJS_Error } from './Errors'
export class GpgME_openPGPCompatibility {
constructor(){
- this._gpgme = new GpgME;
+ this._gpgme = new GpgME({
+ null_expire_is_never: false
+ });
+ this.Keyring = this.initKeyring();
}
/**
@@ -67,15 +72,14 @@ export class GpgME_openPGPCompatibility {
|| signature !== null
|| returnSessionKey !== null
|| date !== null){
- throw('NOT_IMPLEMENTED');
+ return Promise.reject(new GPMGEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| filename
|| compression
|| armor === false
|| detached == true){
- console.log('may be implemented later');
- throw('NOT_YET_IMPLEMENTED');
+ return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
}
return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
}
@@ -103,16 +107,14 @@ export class GpgME_openPGPCompatibility {
if (passwords !== undefined
|| sessionKeys
|| date){
-
- throw('NOT_IMPLEMENTED');
+ return Promise.reject(new GPGMEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| publicKeys
|| format !== 'utf8'
|| signature
){
- console.log('may be implemented later');
- throw('NOT_YET_IMPLEMENTED');
+ return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
}
return this.GpgME.decrypt(message);
// TODO: translate between:
@@ -126,31 +128,74 @@ export class GpgME_openPGPCompatibility {
// mime: A Boolean indicating whether the data is a MIME object.
// info: An optional object with extra information.
}
+ initKeyring(){
+ return new GPGME_Keyring_openPGPCompatibility;
+ }
}
/**
- *
- * @param {Object | String} Key Either a (presumably openpgp Key) Object with a
- * primaryKeyproperty and a method getFingerprint, or a string.
- * @returns {String} Unchecked string value claiming to be a fingerprint
- * TODO: gpgmejs checks again, so it's okay here.
+ * Translation layer offering basic Keyring API to be used in Mailvelope.
+ * It may still be changed/expanded/merged with GPGME_Keyring
*/
-function translateKeyInput(Key){
- if (!Key){
- return [];
+class GPGME_Keyring_openPGPCompatibility {
+ constructor(){
+ this._gpgme_keyring = new GPGME_Keyring;
}
- if (!Array.isArray(Key)){
- Key = [Key];
+
+ /**
+ * Returns a GPGME_Key Object for each Key in the gnupg Keyring. This
+ * includes keys openpgpjs considers 'private' (usable for signing), with
+ * the difference that Key.armored will NOT contain any secret information.
+ * Please also note that a GPGME_Key does not offer full openpgpjs- Key
+ * compatibility.
+ * @returns {Array<GPGME_Key>} with the objects offering at least:
+ * @property {String} armored The armored key block (does not include secret blocks)
+ * @property {Boolean} hasSecret Indicator if a private/secret key exists
+ * @property {Boolean} isDefault Indicator if private key exists and is the default key in this keyring
+ * @property {String} fingerprint The fingerprint identifying this key
+ * //TODO: Check if IsDefault is also always hasSecret
+ */
+ getPublicKeys(){
+ return this._gpgme_keyring.getKeys(null, true);
}
- let resultslist = [];
- for (let i=0; i < Key.length; i++){
- if (typeof(Key[i]) === 'string'){
- resultslist.push(Key);
- } else if (
- Key[i].hasOwnProperty(primaryKey) &&
- Key[i].primaryKey.hasOwnProperty(getFingerprint)){
- resultslist.push(Key[i].primaryKey.getFingerprint());
+
+ /**
+ * Returns the Default Key used for crypto operation in gnupg.
+ * Please note that the armored property does not contained secret key blocks,
+ * despite secret blocks being part of the key itself.
+ * @returns {Promise <GPGME_Key>}
+ */
+ getDefaultKey(){
+ this._gpgme_keyring.getSubset({defaultKey: true}).then(function(result){
+ if (result.length === 1){
+ return Promise.resolve(result[0]);
+ }
+ else {
+ // TODO: Can there be "no default key"?
+ // TODO: Can there be several default keys?
+ return new GPGMEJS_Error; //TODO
+ }
+ });
+ }
+
+ /**
+ * Deletes a Key
+ * @param {Object} Object identifying key
+ * @param {String} key.fingerprint - fingerprint of the to be deleted key
+ * @param {Boolean} key.secret - indicator if private key should be deleted as well
+
+ * @returns {Promise.<Array.<undefined>, Error>} TBD: Not sure what is wanted
+ TODO @throws {Error} error.code = ‘KEY_NOT_EXIST’ - there is no key for the given fingerprint
+ TODO @throws {Error} error.code = ‘NO_SECRET_KEY’ - secret indicator set, but no secret key exists
+ */
+ deleteKey(key){
+ if (typeof(key) !== "object"){
+ return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ }
+ if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
+ return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
}
+ let key_to_delete = new GPGME_Key(key.fingerprint);
+ return key_to_delete.deleteKey(key.secret);
}
- return resultslist;
-}
\ No newline at end of file
+}
-----------------------------------------------------------------------
Summary of changes:
lang/js/src/Connection.js | 86 ++++++++++-------
lang/js/src/Errors.js | 148 ++++++++++++++++++++++++++++
lang/js/src/Helpers.js | 54 +++++++----
lang/js/src/Key.js | 201 +++++++++++++++++++++++++++++++++++++++
lang/js/src/Keyring.js | 151 +++++++++++++++++++++++++++++
lang/js/src/Message.js | 34 ++++---
lang/js/src/gpgmejs.js | 108 +++++++++++++++------
lang/js/src/gpgmejs_openpgpjs.js | 105 ++++++++++++++------
8 files changed, 756 insertions(+), 131 deletions(-)
create mode 100644 lang/js/src/Errors.js
create mode 100644 lang/js/src/Key.js
create mode 100644 lang/js/src/Keyring.js
hooks/post-receive
--
GnuPG Made Easy
http://git.gnupg.org
More information about the Gnupg-commits
mailing list