diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
new file mode 100644
index 00000000..e6000003
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -0,0 +1,71 @@
+/* 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+
+ */
+describe('Encryption', function(){
+
+ it('Successfull encrypt', function(done){
+ let prm = Gpgmejs.init();
+ prm.then(function(context){
+ context.encrypt(
+ inputvalues.encrypt.good.data,
+ inputvalues.encrypt.good.fingerprint).then(function(answer){
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ done();
+ }, function(err){
+ expect(err).to.be.undefined;
+ done();
+ });
+ });
+ });
+
+ it('Sending encryption without keys fails', function(){
+ let prm = Gpgmejs.init();
+ prm.then(function(context){
+ context.encrypt(
+ inputvalues.encrypt.good.data,
+ null).then(function(answer){
+ expect(answer).to.be.undefined;
+ done();
+ }, function(error){
+ expect(error).to.be.an('Error');
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ done()
+ });
+ });
+ });
+
+ it('Sending encryption without data fails', function(){
+ let prm = Gpgmejs.init();
+ prm.then(function(context){
+ context.encrypt(
+ null,inputvalues.encrypt.good.keyid).then(function(answer){
+ expect(answer).to.be.undefined;
+ }, function(error){
+ expect(error).to.be.an.instanceof(Error);
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ done();
+ });
+ });
+ });
+
+ // TODO check different valid parameter
+});
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
index 47600c84..1761c82f 100644
--- a/lang/js/BrowserTestExtension/tests/inputvalues.js
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -1,28 +1,32 @@
/* 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+
*/
var inputvalues = {
encrypt: {
good:{
data : 'Hello World.',
- keyid : 'CDC3A2B2860625CCBFC5A5A9FC6D1B604967FC40'
+ fingerprint : 'CDC3A2B2860625CCBFC5A5A9FC6D1B604967FC40'
}
+ },
+ init: {
+ invalid_startups: [{all_passwords: true}, 'openpgpmode', {api_style:"frankenstein"}]
}
+
};
diff --git a/lang/js/BrowserTestExtension/tests/startup.js b/lang/js/BrowserTestExtension/tests/startup.js
index 14d12c0a..a5614a83 100644
--- a/lang/js/BrowserTestExtension/tests/startup.js
+++ b/lang/js/BrowserTestExtension/tests/startup.js
@@ -1,51 +1,70 @@
/* 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+
*/
describe('GPGME context', function(){
it('Starting a GpgME instance', function(done){
- Gpgmejs.init().then(
+ let prm = Gpgmejs.init();
+ prm.then(
function(context){
- expect(context.connection).to.not.be.undefined;
- expect(context).to.be.an('object');
- expect(context.connection).to.be.an('object');
- expect(context.Keyring).to.be.undefined;
- expect(context.encrypt).to.be.a('function');
- expect(context.decrypt).to.be.a('function');
- done();
- }, function(err){
- done(err);
+ expect(context.connection).to.not.be.undefined;
+ expect(context).to.be.an('object');
+ expect(context.connection).to.be.an('object');
+ expect(context.Keyring).to.be.undefined;
+ expect(context.encrypt).to.be.a('function');
+ expect(context.decrypt).to.be.a('function');
+ done();
+ }, function(errorr){
+ expect(error).to.be.undefined;
+ done();
});
});
- it('Starting an openpgp mode GPGME instance', function(done){
- Gpgmejs.init({api_style:"gpgme_openpgpjs"}).then(
- function(context){
- console.log(context);
- done();
- // expect(context).to.be.an('object');
- // expect(context.connection).to.be.undefined;
- // expect(context.Keyring).to.be.an('object');
- // expect(context.encrypt).to.be.a('function');
- // expect(context.decrypt).to.be.a('function');
- // done();
- }, function(err){
- done(err);
+});
+describe('openpgp mode', function(){
+ it('startup of openpgp mode returns the correct parameters', function(done){
+ let prm = Gpgmejs.init({api_style:"gpgme_openpgpjs"});
+ prm.then(function(context){
+ expect(context).to.be.an('object');
+ expect(context.connection).to.be.undefined;
+ expect(context.Keyring).to.be.an('object');
+ expect(context.encrypt).to.be.a('function');
+ expect(context.decrypt).to.be.a('function');
+ done();
+ }, function(error){
+ expect(error).to.be.undefined;
+ done();
});
});
- });
+});
+
+describe('GPGME does not start with invalid parameters', function(){
+ for (let i=0; i < inputvalues.init.invalid_startups.length; i++){
+ it('Parameter '+ i, function(done){
+ let prm = Gpgmejs.init(inputvalues.init.invalid_startups[i]);
+ prm.then(function(context){
+ expect(context).to.be.undefined;
+ done();
+ }, function(error){
+ expect(error).to.be.an.instanceof(Error);
+ expect(error.code).to.equal('PARAM_WRONG');
+ done();
+ });
+ })
+ }
+});
\ No newline at end of file
diff --git a/lang/js/build_extensions.sh b/lang/js/build_extensions.sh
index be7b0584..b99a362c 100755
--- a/lang/js/build_extensions.sh
+++ b/lang/js/build_extensions.sh
@@ -1,14 +1,15 @@
#/!bin/bash
npx webpack --config webpack.conf.js
mkdir -p BrowserTestExtension/libs
cp node_modules/chai/chai.js \
node_modules/mocha/mocha.css \
node_modules/mocha/mocha.js \
build/gpgmejs.bundle.js BrowserTestExtension/libs
+rm -rf build/extensions
mkdir -p build/extensions
zip -r build/extensions/browsertest.zip BrowserTestExtension
mkdir -p DemoExtension/libs
cp build/gpgmejs.bundle.js DemoExtension/libs
zip -r build/extensions/demoextension.zip DemoExtension
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index a10f9d9a..a198bdc6 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -1,214 +1,214 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
/**
* A connection port will be opened for each communication between gpgmejs and
* gnupg. It should be alive as long as there are additional messages to be
* expected.
*/
import { permittedOperations } from './permittedOperations'
import { gpgme_error } from "./Errors"
import { GPGME_Message } from "./Message";
/**
* A Connection handles the nativeMessaging interaction.
*/
export class Connection{
constructor(){
this.connect();
let me = this;
}
/**
* (Simple) Connection check.
* @returns {Boolean} true if the onDisconnect event has not been fired.
* Please note that the event listener of the port takes some time
* (5 ms seems enough) to react after the port is created. Then this will
* return undefined
*/
get isConnected(){
return this._isConnected;
}
/**
* Immediately closes the open port.
*/
disconnect() {
if (this._connection){
this._connection.disconnect();
}
}
/**
* Opens a nativeMessaging port.
*/
connect(){
if (this._isConnected === true){
gpgme_error('CONN_ALREADY_CONNECTED');
} else {
this._isConnected = true;
this._connection = chrome.runtime.connectNative('gpgmejson');
let me = this;
this._connection.onDisconnect.addListener(
function(){
me._isConnected = false;
}
);
}
}
/**
* Sends a message and resolves with the answer.
* @param {GPGME_Message} message
* @returns {Promise} the gnupg answer, or rejection with error
* information.
*/
post(message){
if (!this.isConnected){
return Promise.reject(gpgme_error('CONN_DISCONNECTED'));
}
if (!message || !message instanceof GPGME_Message){
return Promise.reject(gpgme_error('PARAM_WRONG'), message);
}
if (message.isComplete !== true){
return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
}
let me = this;
return new Promise(function(resolve, reject){
let answer = new Answer(message.operation);
let listener = function(msg) {
if (!msg){
me._connection.onMessage.removeListener(listener)
reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener)
- reject(
- {code: 'GNUPG_ERROR',
- msg: msg.msg} );
+ reject(gpgme_error('GNUPG_ERROR', msg.msg));
} else {
let answer_result = answer.add(msg);
if (answer_result !== true){
reject(answer_result);
}
if (msg.more === true){
me._connection.postMessage({'op': 'getmore'});
} else {
me._connection.onMessage.removeListener(listener)
resolve(answer.message);
}
}
};
me._connection.onMessage.addListener(listener);
if (permittedOperations[message.operation].pinentry){
return me._connection.postMessage(message.message);
} else {
return Promise.race([
me._connection.postMessage(message.message),
function(resolve, reject){
setTimeout(function(){
reject(gpgme_error('CONN_TIMEOUT'));
}, 5000);
}]).then(function(result){
return result;
+ }, function(error){
+ return error;
});
}
});
}
};
/**
* A class for answer objects, checking and processing the return messages of
* the nativeMessaging communication.
* @param {String} operation The operation, to look up validity of returning messages
*/
class Answer{
constructor(operation){
this.operation = operation;
}
/**
* Add the information to the answer
* @param {Object} msg The message as received with nativeMessaging
* returns true if successfull, gpgme_error otherwise
*/
add(msg){
if (this._response === undefined){
this._response = {};
}
let messageKeys = Object.keys(msg);
let poa = permittedOperations[this.operation].answer;
if (messageKeys.length === 0){
return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
for (let i= 0; i < messageKeys.length; i++){
let key = messageKeys[i];
switch (key) {
case 'type':
if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
break;
case 'more':
break;
default:
//data should be concatenated
if (poa.data.indexOf(key) >= 0){
if (!this._response.hasOwnProperty(key)){
this._response[key] = '';
}
this._response[key] = this._response[key].concat(msg[key]);
}
//params should not change through the message
else if (poa.params.indexOf(key) >= 0){
if (!this._response.hasOwnProperty(key)){
this._response[key] = msg[key];
}
else if (this._response[key] !== msg[key]){
return gpgme_error('CONN_UNEXPECTED_ANSWER',msg[key]);
}
}
//infos may be json objects etc. Not yet defined.
// Pushing them into arrays for now
else if (poa.infos.indexOf(key) >= 0){
if (!this._response.hasOwnProperty(key)){
this._response[key] = [];
}
this._response.push(msg[key]);
}
else {
return gpgme_error('CONN_UNEXPECTED_ANSWER', key);
}
break;
}
}
return true;
}
/**
* @returns {Object} the assembled message.
* TODO: does not care yet if completed.
*/
get message(){
return this._response;
}
}
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 0b44b245..30449d63 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -1,206 +1,205 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
/**
* The key class allows to query the information defined in gpgme Key Objects
* (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)
*
* This is a stub, as the gpgme-json side is not yet implemented
*
*/
import { isFingerprint } from './Helpers'
import { gpgme_error } from './Errors'
import { createMessage } from './Message';
import { permittedOperations } from './permittedOperations';
export class GPGME_Key {
constructor(fingerprint){
this.fingerprint = fingerprint;
}
set fingerprint(fpr){
if (isFingerprint(fpr) === true && !this._fingerprint){
this._fingerprint = fpr;
}
}
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);
- });
-
+ return checkKey(this._fingerprint, 'secret');
}
get isRevoked(){
return checkKey(this._fingerprint, 'revoked');
}
get isExpired(){
return checkKey(this._fingerprint, 'expired');
}
get isDisabled(){
return checkKey(this._fingerprint, 'disabled');
}
get isInvalid(){
return checkKey(this._fingerprint, 'invalid');
}
get canEncrypt(){
return checkKey(this._fingerprint, 'can_encrypt');
}
get canSign(){
return checkKey(this._fingerprint, 'can_sign');
}
get canCertify(){
return checkKey(this._fingerprint, 'can_certify');
}
get canAuthenticate(){
return checkKey(this._fingerprint, 'can_authenticate');
}
get isQualified(){
return checkKey(this._fingerprint, 'is_qualified');
}
get armored(){
let me = this;
return new Promise(function(resolve, reject){
let conn = new Connection();
conn.setFlag('armor', true);
conn.post('export',{'fpr': me._fingerprint});
});
// TODO return value not yet checked. Should result in an armored block
// in correct encoding
// TODO openpgpjs also returns secKey if private = true?
}
/**
* TODO returns true if this is the default key used to sign
*/
get isDefault(){
throw('NOT_YET_IMPLEMENTED');
}
/**
* get the Key's subkeys as GPGME_Key objects
* @returns {Array}
*/
get subkeys(){
return checkKey(this._fingerprint, 'subkeys').then(function(result){
// TBD expecting a list of fingerprints
if (!Array.isArray(result)){
result = [result];
}
let resultset = [];
for (let i=0; i < result.length; i++){
let subkey = new GPGME_Key(result[i]);
if (subkey instanceof GPGME_Key){
resultset.push(subkey);
}
}
return Promise.resolve(resultset);
+ }, function(error){
+ //TODO checkKey fails
});
}
/**
* creation time stamp of the key
* @returns {Date|null} TBD
*/
get timestamp(){
return checkKey(this._fingerprint, 'timestamp');
//TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available.
}
/**
* The expiration timestamp of this key TBD
* @returns {Date|null} TBD
*/
get expires(){
return checkKey(this._fingerprint, 'expires');
// TODO convert to Date; check for 0
}
/**
* getter name TBD
* @returns {String|Array} The user ids associated with this key
*/
get userIds(){
return checkKey(this._fingerprint, 'uids');
}
/**
* @returns {String} The public key algorithm supported by this subkey
*/
get pubkey_algo(){
return checkKey(this._fingerprint, 'pubkey_algo');
}
};
/**
* generic function to query gnupg information on a key.
* @param {*} fingerprint The identifier of the Keyring
* @param {*} property The gpgme-json property to check
*
*/
function checkKey(fingerprint, property){
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
- if (!property ||
- permittedOperations[keyinfo].indexOf(property) < 0){
+ if (!property || !permittedOperations[keyinfo].hasOwnProperty(property)){
return Promise.reject(gpgme_error('PARAM_WRONG'));
}
return new Promise(function(resolve, reject){
if (!isFingerprint(fingerprint)){
reject(gpgme_error('KEY_INVALID'));
}
let msg = createMessage ('keyinfo');
if (msg instanceof Error){
reject(gpgme_error('PARAM_WRONG'));
}
msg.setParameter('fingerprint', this.fingerprint);
- return (this.connection.post(msg)).then(function(result){
- if (result.hasOwnProperty(property)){
+ return (this.connection.post(msg)).then(function(result, error){
+ if (error){
+ reject(gpgme_error('GNUPG_ERROR',error.msg));
+ } else if (result.hasOwnProperty(property)){
resolve(result[property]);
}
else if (property == 'secret'){
- // TBD property undefined means "not true" in case of secret?
- resolve(false);
+ // TBD property undefined means "not true" in case of secret?
+ resolve(false);
} else {
reject(gpgme_error('CONN_UNEXPECTED_ANSWER'));
}
}, function(error){
- reject({code: 'GNUPG_ERROR',
- msg: error.msg});
+ //TODO error handling
});
});
};
\ No newline at end of file
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
@@ -1,157 +1,161 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
import {createMessage} from './Message'
import {GPGME_Key} from './Key'
import { isFingerprint, isLongId } 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){
if (this._connection.isConnected){
return this._connection;
}
return gpgme_error('CONN_DISCONNECTED');
}
return gpgme_error('CONN_NO_CONNECT');
}
/**
* @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
* @param {Boolean} (optional) Include listing of secret keys
* @returns {Promise.>}
*
*/
getKeys(pattern, include_secret){
let msg = 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);
}
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);
+ }, function(error){
+ //TODO error handling
});
}
/**
* @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
});
}
};
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 95d043ba..c42480f2 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -1,117 +1,167 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
import { permittedOperations } from './permittedOperations'
import { gpgme_error } from './Errors'
export function createMessage(operation){
if (typeof(operation) !== 'string'){
return gpgme_error('PARAM_WRONG');
}
if (permittedOperations.hasOwnProperty(operation)){
return new GPGME_Message(operation);
} else {
return gpgme_error('MSG_WRONG_OP');
}
}
/**
* Prepares a communication request. It checks operations and parameters in
* ./permittedOperations.
* @param {String} operation
*/
export class GPGME_Message {
//TODO getter
constructor(operation){
this.operation = operation;
}
set operation (op){
if (typeof(op) === "string"){
if (!this._msg){
this._msg = {};
}
if (!this._msg.op & permittedOperations.hasOwnProperty(op)){
this._msg.op = 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
* @param {String} param Parameter to set
* @param {any} value Value to set //TODO: Some type checking
* @returns {Boolean} If the parameter was set successfully
*/
setParameter(param,value){
if (!param || typeof(param) !== 'string'){
return gpgme_error('PARAM_WRONG');
}
let po = permittedOperations[this._msg.op];
if (!po){
return gpgme_error('MSG_WRONG_OP');
}
- if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
- this._msg[param] = value;
- return true;
+ let poparam = null;
+ if (po.required.hasOwnProperty(param)){
+ poparam = po.required[param];
+ } else if (po.optional.hasOwnProperty(param)){
+ poparam = po.optional[param];
+ } else {
+ return gpgme_error('PARAM_WRONG');
}
- return gpgme_error('PARAM_WRONG');
+ let checktype = function(val){
+ switch(typeof(val)){
+ case 'string':
+ case 'number':
+ case 'boolean':
+ if (poparam.allowed.indexOf(typeof(val)) >= 0){
+ return true;
+ }
+ return gpgme_error('PARAM_WRONG');
+ break;
+ case 'object':
+ if (Array.isArray(val)){
+ if (poparam.array_allowed !== true){
+ return gpgme_error('PARAM_WRONG');
+ }
+ for (let i=0; i < val.length; i++){
+ let res = checktype(val[i]);
+ if (res !== true){
+ return res;
+ }
+ }
+ return true;
+ } else if (val instanceof Uint8Array){
+ if (poparam.allowed.indexOf('Uint8Array') >= 0){
+ return true;
+ }
+ return gpgme_error('PARAM_WRONG');
+ } else {
+ return gpgme_error('PARAM_WRONG');
+ }
+ break;
+ default:
+ return gpgme_error('PARAM_WRONG');
+ }
+ };
+ let typechecked = checktype(value);
+ if (typechecked !== true){
+ return typechecked;
+ }
+ if (poparam.hasOwnProperty('allowed_data')){
+ if (poparam.allowed_data.indexOf(value) < 0){
+ return gpgme_error('PARAM_WRONG');
+ }
+ }
+ this._msg[param] = value;
+ return true;
}
/**
* Check if the message has the minimum requirements to be sent, according
* to the definitions in permittedOperations
* @returns {Boolean}
*/
get isComplete(){
if (!this._msg.op){
return false;
}
- let reqParams = permittedOperations[this._msg.op].required;
+ let reqParams = Object.keys(
+ permittedOperations[this._msg.op].required);
+ let msg_params = Object.keys(this._msg);
for (let i=0; i < reqParams.length; i++){
-
- if (!this._msg.hasOwnProperty(reqParams[i])){
- console.log(reqParams[i] + 'missing');
+ if (msg_params.indexOf(reqParams[i]) < 0){
+ console.log(reqParams[i] + ' missing');
return false;
}
}
return true;
}
/**
* Returns the prepared message with parameters and completeness checked
* @returns {Object|null} Object to be posted to gnupg, or null if
* incomplete
*/
get message(){
if (this.isComplete === true){
return this._msg;
}
else {
return null;
}
}
}
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index 2ddf2964..9475b2b0 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -1,183 +1,189 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
import {Connection} from "./Connection"
import {GPGME_Message, createMessage} from './Message'
import {toKeyIdArray} from "./Helpers"
import { gpgme_error } from "./Errors"
import { GPGME_Keyring } from "./Keyring";
export class GpgME {
/**
* initializes GpgME by opening a nativeMessaging port
* TODO: add configuration
*/
constructor(connection){
this.connection = connection;
}
- set connection(connection){
+ set connection(conn){
if (this._connection instanceof Connection){
gpgme_error('CONN_ALREADY_CONNECTED');
- }
- if (connection instanceof Connection){
- this._connection = connection;
+ } else if (conn instanceof Connection){
+ this._connection = conn;
} else {
gpgme_error('PARAM_WRONG');
}
}
get connection(){
- if (this._connection instanceof Connection){
- if (this._connection.isConnected){
+ if (this._connection){
+ if (this._connection.isConnected === true){
return this._connection;
}
- return undefined; //TODO: connection was lost!
+ return undefined;
}
- return undefined; //TODO: no connection there
+ return undefined;
}
set Keyring(keyring){
if (ring && ring instanceof GPGME_Keyring){
this._Keyring = ring;
}
}
get Keyring(){
return this._Keyring;
}
/**
* @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array
* @param {GPGME_Key|String|Array|Array} publicKeys Keys used to encrypt the message
* @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message
*/
encrypt(data, publicKeys, wildcard=false){
let msg = createMessage('encrypt');
if (msg instanceof Error){
return Promise.reject(msg)
}
// TODO temporary
msg.setParameter('armor', true);
msg.setParameter('always-trust', true);
let pubkeys = toKeyIdArray(publicKeys);
msg.setParameter('keys', pubkeys);
putData(msg, data);
if (wildcard === true){msg.setParameter('throw-keyids', true);
};
-
- return (this.connection.post(msg));
+ if (msg.isComplete === true){
+ return this.connection.post(msg);
+ } else {
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
}
/**
* @param {String} data TODO Format: base64? String? Message with the encrypted data
* @returns {Promise} decrypted message:
data: The decrypted data. This may be base64 encoded.
base64: Boolean indicating whether data is base64 encoded.
mime: A Boolean indicating whether the data is a MIME object.
info: An optional object with extra information.
* @async
*/
decrypt(data){
if (data === undefined){
return Promise.reject(gpgme_error('MSG_EMPTY'));
}
let msg = createMessage('decrypt');
if (msg instanceof Error){
return Promise.reject(msg);
}
putData(msg, data);
return this.connection.post(msg);
}
deleteKey(key, delete_secret = false, no_confirm = false){
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
let msg = createMessage('deletekey');
if (msg instanceof Error){
return Promise.reject(msg);
}
let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){
return Promise.reject(
gpgme_error('GENERIC_ERROR'));
// TBD should always be ONE key?
}
msg.setParameter('key', key_arr[0]);
if (delete_secret === true){
msg.setParameter('allow_secret', true);
// TBD
}
if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
msg.setParameter('delete_force', true);
// TBD
}
- 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(gpgme_error('TODO') ); //
- // INV_VALUE,
- // GPG_ERR_NO_PUBKEY,
- // GPG_ERR_AMBIGUOUS_NAME,
- // GPG_ERR_CONFLICT
- }
- });
+ if (msg.isComplete === true){
+ 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(gpgme_error('TODO') ); //
+ // INV_VALUE,
+ // GPG_ERR_NO_PUBKEY,
+ // GPG_ERR_AMBIGUOUS_NAME,
+ // GPG_ERR_CONFLICT
+ }
+ });
+ } else {
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
}
}
/**
* Sets the data of the message, converting Uint8Array to base64 and setting
* the base64 flag
* @param {GPGME_Message} message The message where this data will be set
* @param {*} data The data to enter
* @param {String} propertyname // TODO unchecked still
*/
function putData(message, data){
if (!message || !message instanceof GPGME_Message ) {
return gpgme_error('PARAM_WRONG');
}
if (!data){
- message.setParameter('data', '');
+ return gpgme_error('PARAM_WRONG');
} else if (data instanceof Uint8Array){
let decoder = new TextDecoder('utf8');
message.setParameter('base64', true);
message.setParameter ('data', decoder.decode(data));
} else if (typeof(data) === 'string') {
message.setParameter('base64', false);
message.setParameter('data', data);
} else if ( typeof(data) === 'object' && data.hasOwnProperty(getText)){
let txt = data.getText();
if (txt instanceof Uint8Array){
let decoder = new TextDecoder('utf8');
message.setParameter('base64', true);
message.setParameter ('data', decoder.decode(txt));
}
} else {
return gpgme_error('PARAM_WRONG');
}
}
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index cc2afde1..c80d5a86 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -1,275 +1,280 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
/**
* This is a compatibility API to be used as openpgpjs syntax.
* Non-implemented options will throw an error if set (not null or undefined)
* TODO Some info about differences
*/
import { GpgME } from "./gpgmejs";
import {GPGME_Keyring} from "./Keyring"
import { GPGME_Key } from "./Key";
import { isFingerprint } from "./Helpers"
import { gpgme_error } from "./Errors"
export class GpgME_openpgpmode {
constructor(connection, config = {}){
this.initGpgME(connection, config);
}
get Keyring(){
if (this._keyring){
return this._keyring;
}
return undefined;
}
initGpgME(connection, config = {}){
if (connection && typeof(config) ==='object'){
this._config = config;
if (!this._GPGME){
this._GpgME = new GpgME(connection, config);
}
if (!this._keyring){
this._keyring = new GPGME_Keyring_openpgpmode(connection);
}
}
}
/**
* Encrypt Message
* Supported:
* @param {String|Uint8Array} data
* //an openpgp Message also accepted here. TODO: is this wanted?
* @param {Key|Array} publicKeys
* //Strings of Fingerprints
* @param {Boolean} wildcard
* TODO:
* @param {Key|Array} privateKeys // -> encryptsign
* @param {module:enums.compression} compression //TODO accepts integer, if 0 (no compression) it won't compress
* @param {Boolean} armor // TODO base64 switch
* @param {Boolean} detached // --> encryptsign
* unsupported:
* @param {String|Array} passwords
* @param {Object} sessionKey
* @param {Signature} signature
* @param {Boolean} returnSessionKey
* @param {String} filename
*
* Can be set, but will be ignored:
*
* @returns {Promise}
* {data: ASCII armored message,
* signature: detached signature if 'detached' is true
* }
* @async
* @static
*/
encrypt({data = '', publicKeys = '', privateKeys, passwords=null,
sessionKey = null, filename, compression, armor=true, detached=false,
signature=null, returnSessionKey=null, wildcard=false, date=null}) {
if (passwords !== null
|| sessionKey !== null
|| signature !== null
|| returnSessionKey !== null
|| date !== null
){
return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| compression
|| armor === false
|| detached == true){
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
}
if (filename){
if (this._config.unconsidered_params === 'warn'){
GPMGEJS_Error('PARAM_IGNORED');
} else if (this._config.unconsidered_params === 'error'){
return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
}
- return this._GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
+ return this._GpgME.encrypt(data, translateKeys(publicKeys), wildcard);
}
/** Decrypt Message
* supported openpgpjs parameters:
* @param {Message|Uint8Array|String} message Message object from openpgpjs
* Unsupported:
* @param {String|Array} passwords
* @param {Key|Array} privateKeys
* @param {Object|Array} sessionKeys
* Not yet supported, but planned
* @param {String} format (optional) return data format either as 'utf8' or 'binary'
* @param {Signature} signature (optional) detached signature for verification
* Ignored values: can be safely set, but have no effect
* @param {Date} date
* @param {Key|Array} publicKeys
*
* @returns {Promise} decrypted and verified message in the form:
* { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] }
* @async
* @static
*/
decrypt({ message, privateKeys, passwords=null, sessionKeys,
publicKeys, format='utf8', signature=null, date= null}) {
if (passwords !== null || sessionKeys || privateKeys){
return Promise.reject(gpgme_error('NOT_IMPLEMENTED'));
}
if ( format !== 'utf8' || signature){
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
}
if (date !== null || publicKeys){
if (this._config.unconsidered_params === 'warn'){
GPMGEJS_Error('PARAM_IGNORED');
} else if (this._config.unconsidered_params === 'reject'){
return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
}
return this._GpgME.decrypt(message);
// TODO: translate between:
// openpgp:
// { data:Uint8Array|String,
// filename:String,
// signatures:[{ keyid:String, valid:Boolean }] }
// and gnupg:
// data: The decrypted data. This may be base64 encoded.
// base64: Boolean indicating whether data is base64 encoded.
// mime: A Boolean indicating whether the data is a MIME object.
// info: An optional object with extra information.
}
}
/**
* Translation layer offering basic Keyring API to be used in Mailvelope.
* It may still be changed/expanded/merged with GPGME_Keyring
*/
class GPGME_Keyring_openpgpmode {
constructor(connection){
this._gpgme_keyring = new GPGME_Keyring(connection);
}
/**
* Returns a GPGME_Key Object for each Key in the gnupg Keyring. This
* includes keys openpgpjs considers 'private' (usable for signing), with
* the difference that Key.armored will NOT contain any secret information.
* Please also note that a GPGME_Key does not offer full openpgpjs- Key
* compatibility.
* @returns {Array}
* //TODO: Check if IsDefault is also always hasSecret
* TODO Check if async is required
*/
getPublicKeys(){
return translateKeys(
this._gpgme_keyring.getKeys(null, true));
}
/**
* Returns the Default Key used for crypto operation in gnupg.
* Please note that the armored property does not contained secret key blocks,
* despite secret blocks being part of the key itself.
* @returns {Promise }
*/
getDefaultKey(){
this._gpgme_keyring.getSubset({defaultKey: true}).then(function(result){
if (result.length === 1){
return Promise.resolve(
translateKeys(result)[0]);
}
else {
// TODO: Can there be "no default key"?
// TODO: Can there be several default keys?
return gpgme_error('TODO');
}
+ }, function(error){
+ //TODO
});
}
/**
* Deletes a Key
* @param {Object} Object identifying key
* @param {String} key.fingerprint - fingerprint of the to be deleted key
* @param {Boolean} key.secret - indicator if private key should be deleted as well
* @returns {Promise., Error>} TBD: Not sure what is wanted
TODO @throws {Error} error.code = ‘KEY_NOT_EXIST’ - there is no key for the given fingerprint
TODO @throws {Error} error.code = ‘NO_SECRET_KEY’ - secret indicator set, but no secret key exists
*/
deleteKey(key){
if (typeof(key) !== "object"){
return Promise.reject(gpgme_error('PARAM_WRONG'));
}
if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
return Promise.reject(gpgme_error('PARAM_WRONG'));
}
let key_to_delete = new GPGME_Key(key.fingerprint);
return key_to_delete.deleteKey(key.secret);
}
}
/**
* TODO error handling.
* Offers the Key information as the openpgpmode wants
*/
class GPGME_Key_openpgpmode {
constructor(value){
this.init = value;
}
set init (value){
if (!this._GPGME_Key && value instanceof GPGME_Key){
this._GPGME_Key = value;
} else if (!this._GPGME_Key && isFingerprint(value)){
this._GPGME_Key = new GPGME_Key(value);
}
}
get fingerprint(){
return this._GPGME_Key.fingerprint;
}
get armor(){
return this._GPGME_Key.armored;
}
get secret(){
return this._GPGME_Key.hasSecret;
}
get default(){
return this._GPGME_Key.isDefault;
}
}
/**
* creates GPGME_Key_openpgpmode from GPGME_Keys
*/
function translateKeys(input){
+ if (!input){
+ return null;
+ }
if (!Array.isArray(input)){
input = [input];
}
let resultset;
for (let i=0; i< input.length; i++){
resultset.push(new GPGME_Key_openpgpmode(input[i]));
}
return resultset;
}
\ No newline at end of file
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 4de98457..90fe99e3 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -1,83 +1,88 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
import { GpgME } from "./gpgmejs";
import { gpgme_error } from "./Errors";
import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs";
import { Connection } from "./Connection";
import { defaultConf, availableConf } from "./Config";
/**
* Initializes a nativeMessaging Connection and returns a GPGMEjs object
* @param {Object} config Configuration. See Config.js for available parameters. Still TODO
*/
function init(config){
let _conf = parseconfiguration(config);
if (_conf instanceof Error){
return Promise.reject(_conf);
}
return new Promise(function(resolve, reject){
let connection = new Connection;
// TODO: Delayed reaction is ugly. We need to listen to the port's
// event listener in isConnected, but this takes some time (<5ms) to
// disconnect if there is no successfull connection.
let delayedreaction = function(){
if (connection.isConnected === true){
if (_conf.api_style && _conf.api_style === 'gpgme_openpgpjs'){
resolve(new GpgME_openpgpmode(connection, _conf));
} else {
resolve(new GpgME(connection));
}
} else {
reject(gpgme_error('CONN_NO_CONNECT'));
}
};
setTimeout(delayedreaction, 5);
});
}
-function parseconfiguration(config){
- if (!config){
- return defaultConf;
- }
- if ( typeof(config) !== 'object'){
+function parseconfiguration(rawconfig = {}){
+ if ( typeof(rawconfig) !== 'object'){
return gpgme_error('PARAM_WRONG');
};
- let result_config = defaultConf;
- let conf_keys = Object.keys(config);
- for (let i=0; i < conf_keys; i++){
+ let result_config = {};
+ let conf_keys = Object.keys(rawconfig);
+
+ for (let i=0; i < conf_keys.length; i++){
+
if (availableConf.hasOwnProperty(conf_keys[i])){
- let value = config[conf_keys[i]];
+ let value = rawconfig[conf_keys[i]];
if (availableConf[conf_keys[i]].indexOf(value) < 0){
return gpgme_error('PARAM_WRONG');
} else {
result_config[conf_keys[i]] = value;
}
}
else {
return gpgme_error('PARAM_WRONG');
}
}
+ let default_keys = Object.keys(defaultConf);
+ for (let j=0; j < default_keys.length; j++){
+ if (!result_config.hasOwnProperty(default_keys[j])){
+ result_config[default_keys[j]] = defaultConf[default_keys[j]];
+ }
+ }
return result_config;
};
export default {
init: init
}
\ No newline at end of file
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index 79e74223..274e037e 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -1,126 +1,176 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
/**
* Definition of the possible interactions with gpgme-json.
* operation:
- required: Array
- optional: Array
- pinentry: Boolean If a pinentry dialog is expected, and a timeout of
+ required: Array
+ name The name of the property
+ allowed: Array of allowed types. Currently accepted values:
+ ['number', 'string', 'boolean', 'Uint8Array']
+ array_allowed: Boolean. If the value can be an array of the above
+ allowed_data: If present, restricts to the given value
+ optional: Array
+ see 'required', with these parameters not being mandatory for a
+ complete message
+ pinentry: boolean If a pinentry dialog is expected, and a timeout of
5000 ms would be too short
answer:
type: The payload property of the answer. May be
partial and in need of concatenation
params: Array Information that do not change throughout
the message
infos: Array<*> arbitrary information that may result in a list
}
}
*/
export const permittedOperations = {
encrypt: {
- required: ['keys', 'data'],
- optional: [
- 'protocol',
- 'chunksize',
- 'base64',
- 'mime',
- 'armor',
- 'always-trust',
- 'no-encrypt-to',
- 'no-compress',
- 'throw-keyids',
- 'want-address',
- 'wrap'
- ],
+ required: {
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ 'data': {
+ allowed: ['string', 'Uint8Array']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'chunksize': {
+ allowed: ['number']
+ },
+ 'base64': {
+ allowed: ['boolean']
+ },
+ 'mime': {
+ allowed: ['boolean']
+ },
+ 'armor': {
+ allowed: ['boolean']
+ },
+ 'always-trust': {
+ allowed: ['boolean']
+ },
+ 'no-encrypt-to': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ 'no-compress': {
+ allowed: ['boolean']
+ },
+ 'throw-keyids': {
+ allowed: ['boolean']
+ },
+ 'want-address': {
+ allowed: ['boolean']
+ },
+ 'wrap': {
+ allowed: ['boolean']
+ },
+ },
answer: {
type: ['ciphertext'],
data: ['data'],
params: ['base64'],
infos: []
}
},
decrypt: {
pinentry: true,
- required: ['data'],
- optional: [
- 'protocol',
- 'chunksize',
- 'base64'
- ],
+ required: {
+ 'data': {
+ allowed: ['string', 'Uint8Array']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'chunksize': {
+ allowed: ['number'],
+ },
+ 'base64': {
+ allowed: ['boolean']
+ }
+ },
answer: {
type: ['plaintext'],
data: ['data'],
params: ['base64', 'mime'],
infos: [] // pending. Info about signatures and validity
- //signature: [{Key Fingerprint, valid Boolean}]
+ //signature: [{Key Fingerprint, valid boolean}]
}
},
/**
keyinfo: { // querying the Key's information.
required: ['fingerprint'],
anser: {
type: ['TBD'],
data: [],
params: ['hasSecret', 'isRevoked', 'isExpired', 'armored',
'timestamp', 'expires', 'pubkey_algo'],
infos: ['subkeys', 'userIds']
}*/
/**
listkeys:{
optional: ['with-secret', 'pattern'],
answer: {
type: ['TBD'], //Array of fingerprints?
infos: ['TBD'] //the property with infos
},
*/
/**
importkey: {
required: ['keyarmored'],
answer: {
type: ['TBD'],
infos: [''], // for each key if import was a success, if it was an update
}
},
*/
/**
deletekey: {
required: ['fingerprint'],
answer: {
type ['TBD'],
infos: [''] //success:true? in gpgme, an error NO_ERROR is returned
}
}
*/
/**
*get armored secret different treatment from keyinfo!
*/
/**
* TBD key modification requests?
*/
}
diff --git a/lang/js/test/Helpers.js b/lang/js/test/Helpers.js
index 5d8909f9..6b5a5382 100644
--- a/lang/js/test/Helpers.js
+++ b/lang/js/test/Helpers.js
@@ -1,97 +1,97 @@
/* 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 { expect } from "../node_modules/chai/chai";
import { gpgme_error} from "../src/Errors";
import { GPGME_Key } from "../src/Key";
import { isLongId, isFingerprint, toKeyIdArray } from "../src/Helpers"
import { helper_params } from "./inputvalues";
-function Helpertest(){
+export function Helpertest(){
describe('Error Object handling', function(){
it('check the Timeout error', function(){
let test0 = gpgme_error('CONN_TIMEOUT');
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('CONN_TIMEOUT');
});
it('Error Object returns generic code if code is not listed', function(){
let test0 = gpgme_error(helper_params.invalidErrorCode);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('GENERIC_ERROR');
});
it('Warnings like PARAM_IGNORED should not return errors', function(){
let test0 = gpgme_error('PARAM_IGNORED');
expect(test0).to.be.null;
});
});
describe('Fingerprint checking', function(){
it('isFingerprint(): valid Fingerprint', function(){
let test0 = isFingerprint(helper_params.validFingerprint);
expect(test0).to.be.true;
});
it('isFingerprint(): invalid Fingerprint', function(){
let test0 = isFingerprint(helper_params.invalidFingerprint);
expect(test0).to.be.false;
});
});
describe('Converting to Fingerprint', function(){
it('Correct Inputs', function(){
it('Fingerprint string', function(){
let test0 = toKeyIdArray(helper_params.validFingerprint);
expect(test0).to.be.an('array');
expect(test0).to.include(helper_params.validFingerprint);
});
it('GPGME_Key', function(){
expect(helper_params.validGPGME_Key).to.be.an.instanceof(GPGME_Key);
let test0 = toKeyIdArray(helper_params.validGPGME_Key);
expect(test0).to.be.an('array');
expect(test0).to.include(helper_params.validGPGME_Key.fingerprint);
});
it('Array of valid inputs', function(){
let test0 = toKeyIdArray(helper_params.validKeys);
expect(test0).to.be.an('array');
expect(test0).to.have.lengthOf(helper_params.validKeys.length);
});
});
describe('Incorrect inputs', function(){
it('valid Long ID', function(){
let test0 = toKeyIdArray(helper_params.validLongId);
expect(test0).to.be.empty;
});
it('invalidFingerprint', function(){
let test0 = toKeyIdArray(helper_params.invalidFingerprint);
expect(test0).to.be.empty;
});
it('invalidKeyArray', function(){
let test0 = toKeyIdArray(helper_params.invalidKeyArray);
expect(test0).to.be.empty;
});
it('Partially invalid array', function(){
let test0 = toKeyIdArray(helper_params.invalidKeyArray_OneBad);
expect(test0).to.be.an('array');
expect(test0).to.have.lengthOf(
helper_params.invalidKeyArray_OneBad.length - 1);
});
});
});
};
export default Helpertest;
\ No newline at end of file
diff --git a/lang/js/test/Message.js b/lang/js/test/Message.js
index 44206fba..a7dd3af5 100644
--- a/lang/js/test/Message.js
+++ b/lang/js/test/Message.js
@@ -1,43 +1,93 @@
/* 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 { expect } from "../node_modules/chai/chai";
import { GPGME_Message, createMessage } from "../src/Message";
-import { message_params } from "./inputvalues";
+import { message_params as mp, helper_params as hp} from "./inputvalues";
-function Messagetest(){
+export function Messagetest(){
describe('Message Object', function(){
- describe('incorrect initialization', function(){
+ describe('correct initialization of an encrypt Message', function(){
+ it('creating Message', function(){
+ let test0 = createMessage('encrypt');
+ expect(test0).to.be.an.instanceof(GPGME_Message);
+ expect(test0.isComplete).to.be.false;
+ });
+ it('Message is complete after setting mandatoy data', function(){
+ let test0 = createMessage('encrypt');
+ test0.setParameter('data', mp.valid_encrypt_data);
+ test0.setParameter('keys', hp.validFingerprints);
+ expect(test0.isComplete).to.be.true;
+ });
+ it('Complete Message contains the data that was set', function(){
+ let test0 = createMessage('encrypt');
+ test0.setParameter('data', mp.valid_encrypt_data);
+ test0.setParameter('keys', hp.validFingerprints);
+
+ expect(test0.message).to.not.be.null;
+ expect(test0.message).to.have.keys('op', 'data', 'keys');
+ expect(test0.message.op).to.equal('encrypt');
+ expect(test0.message.data).to.equal(
+ mp.valid_encrypt_data);
+ });
+ });
+
+ describe('Incorrect initialization', function(){
it('non-allowed operation', function(){
- let test0 = createMessage(message_params.invalid_op_action);
+ let test0 = createMessage(mp.invalid_op_action);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('MSG_WRONG_OP');
});
it('wrong parameter type in constructor', function(){
- let test0 = createMessage(message_params.invalid_op_type);
+ let test0 = createMessage(mp.invalid_op_type);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('PARAM_WRONG');
});
});
+
+ describe('Setting wrong parameters', function(){
+ it('Wrong parameter name', function(){
+ let test0 = createMessage(mp.invalid_param_test.valid_op);
+ for (let i=0; i < mp.invalid_param_test.invalid_param_names.length; i++){
+ let ret = test0.setParameter(
+ mp.invalid_param_test.invalid_param_names[i],
+ 'Somevalue');
+ expect(ret).to.be.an.instanceof(Error);
+ expect(ret.code).to.equal('PARAM_WRONG');
+ }
+ });
+ it('Wrong parameter value', function(){
+ let test0 = createMessage(mp.invalid_param_test.valid_op);
+ for (let j=0;
+ j < mp.invalid_param_test.invalid_values_0.length;
+ j++){
+ let ret = test0.setParameter(
+ mp.invalid_param_test.validparam_name_0,
+ mp.invalid_param_test.invalid_values_0[j]);
+ expect(ret).to.be.an.instanceof(Error);
+ expect(ret.code).to.equal('PARAM_WRONG');
+ }
+ });
+ });
});
-};
+}
export default Messagetest;
\ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/test/index.js
similarity index 83%
copy from lang/js/BrowserTestExtension/tests/inputvalues.js
copy to lang/js/test/index.js
index 47600c84..b8520515 100644
--- a/lang/js/BrowserTestExtension/tests/inputvalues.js
+++ b/lang/js/test/index.js
@@ -1,28 +1,28 @@
/* 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 { Helpertest } from "./Helpers";
+import { Messagetest } from "./Message";
-var inputvalues = {
- encrypt: {
- good:{
- data : 'Hello World.',
- keyid : 'CDC3A2B2860625CCBFC5A5A9FC6D1B604967FC40'
- }
- }
-};
+/**
+ * Unit tests.
+ */
+
+Helpertest();
+Messagetest();
diff --git a/lang/js/test/inputvalues.js b/lang/js/test/inputvalues.js
index a50c8162..f6cd75aa 100644
--- a/lang/js/test/inputvalues.js
+++ b/lang/js/test/inputvalues.js
@@ -1,29 +1,39 @@
import {GPGME_Key} from "../src/Key"
export const helper_params = {
validLongId: '0A0A0A0A0A0A0A0A',
validGPGME_Key: new GPGME_Key('ADDBC303B6D31026F5EB4591A27EABDF283121BB'),
validKeys: [new GPGME_Key('A1E3BC45BDC8E87B74F4392D53B151A1368E50F3'),
'ADDBC303B6D31026F5EB4591A27EABDF283121BB',
new GPGME_Key('EE17AEE730F88F1DE7713C54BBE0A4FF7851650A')],
validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ '9AAE7A338A9A9A7A7A8A9A9A7A7A8A9A9A7A7DDA'],
invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
invalidFingerprint: [{hello:'World'}],
invalidKeyArray: {curiosity:'uncat'},
invalidKeyArray_OneBad: [
new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'),
'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
'3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
invalidErrorCode: 'Please type in all your passwords.'
}
export const message_params = {
invalid_op_action : 'dance',
invalid_op_type : [234, 34, '<>'],
+ valid_encrypt_data: "مرحبا بالعالم",
+ invalid_param_test: {
+ valid_op: 'encrypt',
+ invalid_param_names: [22,'dance', {}],
+ validparam_name_0: 'mime',
+ invalid_values_0: [2134, 'All your passwords', new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'), null]
+ }
}
+
export default {
helper_params: helper_params,
message_params: message_params
}
\ No newline at end of file