Page MenuHome GnuPG

No OneTemporary

diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
index 2fe955e6..f5d2be16 100644
--- a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -1,225 +1,218 @@
/* 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+
*/
describe('Encryption and Decryption', function () {
it('Successful encrypt and decrypt simple string', 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');
context.decrypt(answer.data).then(function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(inputvalues.encrypt.good.data);
- context.connection.disconnect();
done();
});
});
});
});
it('Decrypt simple non-ascii', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
let data = encryptedData;
context.decrypt(data).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(
'¡Äußerste µ€ før ñoquis@hóme! Добрый день\n');
done();
});
});
}).timeout(3000);
it('Roundtrip does not destroy trailing whitespace',
function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
let data = 'Keks. \rKeks \n Keks \r\n';
context.encrypt(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');
context.decrypt(answer.data).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(5000);
for (let j = 0; j < inputvalues.encrypt.good.data_nonascii_32.length; j++){
it('Roundtrip with >1MB non-ascii input meeting default chunksize (' + (j + 1) + '/' + inputvalues.encrypt.good.data_nonascii_32.length + ')',
function (done) {
let input = inputvalues.encrypt.good.data_nonascii_32[j];
expect(input).to.have.length(32);
let prm = Gpgmejs.init();
prm.then(function (context) {
let data = '';
for (let i=0; i < 34 * 1024; i++){
data += input;
}
context.encrypt(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');
context.decrypt(answer.data).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(3000);
};
it('Random data, as string', function (done) {
let data = bigString(1000);
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(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');
context.decrypt(answer.data).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(3000);
it('Data, input as base64', function (done) {
let data = inputvalues.encrypt.good.data;
let b64data = btoa(data);
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(b64data,
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');
context.decrypt(answer.data).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(data).to.equal(data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(3000);
it('Random data, input as base64', function (done) {
//TODO fails. The result is
let data = bigBoringString(0.001);
let b64data = btoa(data);
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(b64data,
inputvalues.encrypt.good.fingerprint, true).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');
context.decrypt(answer.data).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(3000);
it('Random data, input and output as base64', function (done) {
let data = bigBoringString(0.0001);
let b64data = btoa(data);
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(b64data,
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');
context.decrypt(answer.data, true).then(
function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(b64data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(3000);
});
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
index 521ed276..a16f993c 100644
--- a/lang/js/BrowserTestExtension/tests/encryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -1,156 +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+
*/
describe('Encryption', function () {
it('Successful 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');
- context.connection.disconnect();
done();
});
});
});
it('Successful encrypt 5 MB', function (done) {
let prm = Gpgmejs.init();
let data = fixedLengthString(5);
prm.then(function (context) {
context.encrypt(
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');
- context.connection.disconnect();
done();
});
});
}).timeout(10000);
it('Successful encrypt 20 MB', function (done) {
let prm = Gpgmejs.init();
let data = fixedLengthString(20);
prm.then(function (context) {
context.encrypt(
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');
- context.connection.disconnect();
done();
});
});
}).timeout(20000);
it('Successful encrypt 50 MB', function (done) {
let prm = Gpgmejs.init();
let data = fixedLengthString(50);
prm.then(function (context) {
context.encrypt(
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');
- context.connection.disconnect();
done();
});
});
}).timeout(20000);
it('Sending encryption without keys fails', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(
inputvalues.encrypt.good.data,
null).then(function (answer) {
expect(answer).to.be.undefined;
}, function(error){
expect(error).to.be.an('Error');
expect(error.code).to.equal('MSG_INCOMPLETE');
- context.connection.disconnect();
done();
});
});
});
it('Sending encryption without data fails', function (done) {
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');
- context.connection.disconnect();
done();
});
});
});
it('Sending encryption with non existing keys fails', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(
inputvalues.encrypt.good.data,
inputvalues.encrypt.bad.fingerprint).then(function (answer) {
expect(answer).to.be.undefined;
}, function(error){
expect(error).to.be.an('Error');
expect(error.code).to.not.be.undefined;
expect(error.code).to.equal('GNUPG_ERROR');
- context.connection.disconnect();
done();
});
});
}).timeout(5000);;
it('Overly large message ( > 65MB) is rejected', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(
fixedLengthString(65),
inputvalues.encrypt.good.fingerprint).then(function (answer) {
expect(answer).to.be.undefined;
}, function(error){
expect(error).to.be.an.instanceof(Error);
// expect(error.code).to.equal('GNUPG_ERROR');
// TODO: there is a 64 MB hard limit at least in chrome at:
// chromium//extensions/renderer/messaging_util.cc:
// kMaxMessageLength
- context.connection.disconnect();
done();
});
});
}).timeout(8000);
// TODO check different valid parameter
});
diff --git a/lang/js/BrowserTestExtension/tests/longRunningTests.js b/lang/js/BrowserTestExtension/tests/longRunningTests.js
index 4e55fd26..5c588f27 100644
--- a/lang/js/BrowserTestExtension/tests/longRunningTests.js
+++ b/lang/js/BrowserTestExtension/tests/longRunningTests.js
@@ -1,40 +1,39 @@
describe('Long running Encryption/Decryption', function () {
for (let i=0; i < 100; i++) {
it('Successful encrypt/decrypt completely random data ' + (i+1) + '/100', function (done) {
let prm = Gpgmejs.init();
let data = bigString(2*1024*1024);
prm.then(function (context) {
context.encrypt(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');
context.decrypt(answer.data).then(
function(result){
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
if (result.data.length !== data.length) {
console.log('diff: ' + (result.data.length - data.length));
for (let i=0; i < result.data.length; i++){
if (result.data[i] !== data[i]){
console.log('position: ' + i);
console.log('result : '+ result.data.charCodeAt(i) + result.data[i-2] + result.data[i-1] + result.data[i] + result.data[i+1] + result.data[i+2]);
console.log('original: ' + data.charCodeAt(i) + data[i-2] + data[i-1] + data[i] + data[i+1] + data[i+2]);
break;
}
}
}
expect(result.data).to.equal(data);
- context.connection.disconnect();
done();
});
});
});
}).timeout(8000);
};
});
diff --git a/lang/js/BrowserTestExtension/tests/signTest.js b/lang/js/BrowserTestExtension/tests/signTest.js
index e3323721..2e5edb30 100644
--- a/lang/js/BrowserTestExtension/tests/signTest.js
+++ b/lang/js/BrowserTestExtension/tests/signTest.js
@@ -1,58 +1,56 @@
/* 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+
*/
describe('Signing', function () {
it('Sign a message', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
let data = bigString(100);
context.sign(
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 SIGNATURE');
expect(answer.data).to.include('END PGP SIGNATURE');
expect(answer.data).to.include(data);
- context.connection.disconnect();
done();
});
});
});
it('Detached sign a message', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
let data = bigString(100);
context.sign(
data,
inputvalues.encrypt.good.fingerprint,
'detached'
).then(function (answer) {
expect(answer).to.not.be.empty;
expect(answer.data).to.be.a('string');
expect(answer.data).to.include(data);
expect(answer.signature).to.be.a('string');
expect(answer.signature).to.be.a('string');
- context.connection.disconnect();
done();
});
});
});
});
diff --git a/lang/js/BrowserTestExtension/tests/startup.js b/lang/js/BrowserTestExtension/tests/startup.js
index ebecf4fb..7d13ea47 100644
--- a/lang/js/BrowserTestExtension/tests/startup.js
+++ b/lang/js/BrowserTestExtension/tests/startup.js
@@ -1,51 +1,48 @@
/* 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+
*/
describe('GPGME context', function(){
it('Starting a GpgME instance', function(done){
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();
});
});
});
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/src/Connection.js b/lang/js/src/Connection.js
index 3b442622..3480d811 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -1,263 +1,269 @@
/* 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+
*/
/**
* 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, createMessage } from "./Message";
/**
* A Connection handles the nativeMessaging interaction.
*/
export class Connection{
constructor(){
this.connect();
}
/**
* Retrieves the information about the backend.
* @param {Boolean} details (optional) If set to false, the promise will
* just return a connection status
* @returns {Promise<Object>}
* {String} The property 'gpgme': Version number of gpgme
* {Array<Object>} 'info' Further information about the backends.
* Example:
* "protocol": "OpenPGP",
* "fname": "/usr/bin/gpg",
* "version": "2.2.6",
* "req_version": "1.4.0",
* "homedir": "default"
*/
checkConnection(details = true){
if (details === true) {
return this.post(createMessage('version'));
} else {
let me = this;
return new Promise(function(resolve,reject) {
Promise.race([
me.post(createMessage('version')),
new Promise(function(resolve, reject){
setTimeout(function(){
reject(gpgme_error('CONN_TIMEOUT'));
}, 500);
})
]).then(function(result){
resolve(true);
}, function(reject){
resolve(false);
});
});
}
}
/**
* Immediately closes the open port.
*/
disconnect() {
if (this._connection){
this._connection.disconnect();
this._connection = null;
}
}
/**
* Opens a nativeMessaging port.
*/
connect(){
if (!this._connection){
this._connection = chrome.runtime.connectNative('gpgmejson');
}
}
/**
* Sends a message and resolves with the answer.
* @param {GPGME_Message} message
* @returns {Promise<Object>} the gnupg answer, or rejection with error
* information.
*/
post(message){
if (!this._connection) {
}
if (!message || !message instanceof GPGME_Message){
+ this.disconnect();
return Promise.reject(gpgme_error('PARAM_WRONG'), message);
}
if (message.isComplete !== true){
+ this.disconnect();
return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
}
let me = this;
return new Promise(function(resolve, reject){
let answer = new Answer(message);
let listener = function(msg) {
if (!msg){
me._connection.onMessage.removeListener(listener)
+ me._connection.disconnect();
reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener);
+ me._connection.disconnect();
reject(gpgme_error('GNUPG_ERROR', msg.msg));
} else {
let answer_result = answer.add(msg);
if (answer_result !== true){
me._connection.onMessage.removeListener(listener);
+ me._connection.disconnect();
reject(answer_result);
- }
- if (msg.more === true){
+ } else if (msg.more === true){
me._connection.postMessage({'op': 'getmore'});
} else {
me._connection.onMessage.removeListener(listener)
+ me._connection.disconnect();
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(){
+ me._connection.disconnect();
reject(gpgme_error('CONN_TIMEOUT'));
}, 5000);
}]).then(function(result){
- return result;
+ return result;
}, function(reject){
if(!reject instanceof Error) {
+ me._connection.disconnect();
return gpgme_error('GNUPG_ERROR', reject);
} else {
return reject;
}
});
}
});
}
};
/**
* 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(message){
this.operation = message.operation;
this.expected = message.expected;
}
/**
* 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] += 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] = [];
}
if (Array.isArray(msg[key])) {
for (let i=0; i< msg[key].length; i++) {
this._response[key].push(msg[key][i]);
}
} else {
this._response[key].push(msg[key][i]);
}
}
else {
return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
break;
}
}
return true;
}
/**
* @returns {Object} the assembled message, original data assumed to be
* (javascript-) strings
*/
get message(){
let keys = Object.keys(this._response);
let msg = {};
let poa = permittedOperations[this.operation].answer;
for (let i=0; i < keys.length; i++) {
if (poa.data.indexOf(keys[i]) >= 0
&& this._response.base64 === true
) {
msg[keys[i]] = atob(this._response[keys[i]]);
if (this.expected === 'base64'){
msg[keys[i]] = this._response[keys[i]];
} else {
msg[keys[i]] = decodeURIComponent(
atob(this._response[keys[i]]).split('').map(function(c) {
return '%' +
('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
} else {
msg[keys[i]] = this._response[keys[i]];
}
}
return msg;
}
}
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 7d3d82b1..13c99542 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -1,427 +1,397 @@
/* 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, isLongId } from './Helpers'
import { gpgme_error } from './Errors'
import { createMessage } from './Message';
import { permittedOperations } from './permittedOperations';
-import { Connection } from './Connection';
/**
- * Validates the fingerprint, and checks for tha availability of a connection.
- * If both are available, a Key will be returned.
+ * Validates the fingerprint.
* @param {String} fingerprint
- * @param {Object} parent Either a Connection, or the invoking object with a
- * Connection (e.g. Keyring)
*/
-export function createKey(fingerprint, parent){
+export function createKey(fingerprint){
if (!isFingerprint(fingerprint)){
return gpgme_error('PARAM_WRONG');
}
- if ( parent instanceof Connection){
- return new GPGME_Key(fingerprint, parent);
- } else if ( parent.hasOwnProperty('connection') &&
- parent.connection instanceof Connection){
- return new GPGME_Key(fingerprint, parent.connection);
- } else {
- return gpgme_error('PARAM_WRONG');
- }
+ else return new GPGME_Key(fingerprint);
}
/**
* Representing the Keys as stored in GPG
*/
export class GPGME_Key {
- constructor(fingerprint, connection){
+ constructor(fingerprint){
this.fingerprint = fingerprint;
- this.connection = connection;
- }
-
- set connection(conn){
- if (this._connection instanceof Connection) {
- gpgme_error('CONN_ALREADY_CONNECTED');
- } else if (conn instanceof Connection ) {
- this._connection = conn;
- }
- }
-
- get connection(){
- if (!this._data.fingerprint){
- return gpgme_error('KEY_INVALID');
- }
- if (!this._connection instanceof Connection){
- return gpgme_error('CONN_NO_CONNECT');
- } else {
- return this._connection;
- }
}
set fingerprint(fpr){
if (isFingerprint(fpr) === true) {
if (this._data === undefined) {
this._data = {fingerprint: fpr};
} else {
if (this._data.fingerprint === undefined){
this._data.fingerprint = fpr;
}
}
}
}
get fingerprint(){
if (!this._data || !this._data.fingerprint){
return gpgme_error('KEY_INVALID');
}
return this._data.fingerprint;
}
/**
*
* @param {Object} data Bulk set data for this key, with the Object as sent
* by gpgme-json.
* @returns {GPGME_Key|GPGME_Error} The Key object itself after values have
* been set
*/
setKeydata(data){
if (this._data === undefined) {
this._data = {};
}
if (
typeof(data) !== 'object') {
return gpgme_error('KEY_INVALID');
}
if (!this._data.fingerprint && isFingerprint(data.fingerprint)){
if (data.fingerprint !== this.fingerprint){
return gpgme_error('KEY_INVALID');
}
this._data.fingerprint = data.fingerprint;
} else if (this._data.fingerprint !== data.fingerprint){
return gpgme_error('KEY_INVALID');
}
let booleans = ['expired', 'disabled','invalid','can_encrypt',
'can_sign','can_certify','can_authenticate','secret',
'is_qualified'];
for (let b =0; b < booleans.length; b++) {
if (
!data.hasOwnProperty(booleans[b]) ||
typeof(data[booleans[b]]) !== 'boolean'
){
return gpgme_error('KEY_INVALID');
}
this._data[booleans[b]] = data[booleans[b]];
}
if (typeof(data.protocol) !== 'string'){
return gpgme_error('KEY_INVALID');
}
// TODO check valid protocols?
this._data.protocol = data.protocol;
if (typeof(data.owner_trust) !== 'string'){
return gpgme_error('KEY_INVALID');
}
// TODO check valid values?
this._data.owner_trust = data.owner_trust;
// TODO: what about origin ?
if (!Number.isInteger(data.last_update)){
return gpgme_error('KEY_INVALID');
}
this._data.last_update = data.last_update;
this._data.subkeys = [];
if (data.hasOwnProperty('subkeys')){
if (!Array.isArray(data.subkeys)){
return gpgme_error('KEY_INVALID');
}
for (let i=0; i< data.subkeys.length; i++) {
this._data.subkeys.push(
new GPGME_Subkey(data.subkeys[i]));
}
}
this._data.userids = [];
if (data.hasOwnProperty('userids')){
if (!Array.isArray(data.userids)){
return gpgme_error('KEY_INVALID');
}
for (let i=0; i< data.userids.length; i++) {
this._data.userids.push(
new GPGME_UserId(data.userids[i]));
}
}
return this;
}
/**
* Query any property of the Key list
* (TODO: armor not in here, might be unexpected)
* @param {String} property Key property to be retreived
* @param {*} cached (optional) if false, the data will be directly queried
* from gnupg.
* @returns {*|Promise<*>} the value, or if not cached, a Promise
* resolving on the value
*/
get(property, cached=true) {
if (cached === false) {
let me = this;
return new Promise(function(resolve, reject) {
me.refreshKey().then(function(key){
resolve(key.get(property, true));
}, function(error){
reject(error);
});
});
} else {
if (!this._data.hasOwnProperty(property)){
return gpgme_error('PARAM_WRONG');
} else {
return (this._data[property]);
}
}
}
/**
* Reloads the Key from gnupg
*/
refreshKey() {
let me = this;
return new Promise(function(resolve, reject) {
if (!me._data.fingerprint){
reject(gpgme_error('KEY_INVALID'));
}
let msg = createMessage('keylist');
msg.setParameter('sigs', true);
msg.setParameter('keys', me._data.fingerprint);
- me.connection.post(msg).then(function(result){
+ console.log(msg);
+ msg.post().then(function(result){
if (result.keys.length === 1){
me.setKeydata(result.keys[0]);
resolve(me);
} else {
reject(gpgme_error('KEY_NOKEY'));
}
}, function (error) {
reject(gpgme_error('GNUPG_ERROR'), error);
})
});
}
//TODO:
/**
* Get the armored block of the non- secret parts of the Key.
* @returns {String} the armored Key block.
* Notice that this may be outdated cached info. Use the async getArmor if
* you need the most current info
*/
// get armor(){ TODO }
/**
* Query the armored block of the non- secret parts of the Key directly
* from gpg.
* Async, returns Promise<String>
*/
// getArmor(){ TODO }
//
// get hasSecret(){TODO} // confusing difference to Key.get('secret')!
// getHasSecret(){TODO async version}
}
/**
* The subkeys of a Key. Currently, they cannot be refreshed separately
*/
class GPGME_Subkey {
constructor(data){
let keys = Object.keys(data);
for (let i=0; i< keys.length; i++) {
this.setProperty(keys[i], data[keys[i]]);
}
}
setProperty(property, value){
if (!this._data){
this._data = {};
}
if (validSubKeyProperties.hasOwnProperty(property)){
if (validSubKeyProperties[property](value) === true) {
this._data[property] = value;
}
}
}
/**
*
* @param {String} property Information to request
* @returns {String | Number}
* TODO: date properties are numbers with Date in seconds
*/
get(property) {
if (this._data.hasOwnProperty(property)){
return (this._data[property]);
}
}
}
class GPGME_UserId {
constructor(data){
let keys = Object.keys(data);
for (let i=0; i< keys.length; i++) {
this.setProperty(keys[i], data[keys[i]]);
}
}
setProperty(property, value){
if (!this._data){
this._data = {};
}
if (validUserIdProperties.hasOwnProperty(property)){
if (validUserIdProperties[property](value) === true) {
this._data[property] = value;
}
}
}
/**
*
* @param {String} property Information to request
* @returns {String | Number}
* TODO: date properties are numbers with Date in seconds
*/
get(property) {
if (this._data.hasOwnProperty(property)){
return (this._data[property]);
}
}
}
const validUserIdProperties = {
'revoked': function(value){
return typeof(value) === 'boolean';
},
'invalid': function(value){
return typeof(value) === 'boolean';
},
'uid': function(value){
if (typeof(value) === 'string' || value === ''){
return true;;
}
return false;
},
'validity': function(value){
if (typeof(value) === 'string'){
return true;;
}
return false;
},
'name': function(value){
if (typeof(value) === 'string' || value === ''){
return true;;
}
return false;
},
'email': function(value){
if (typeof(value) === 'string' || value === ''){
return true;;
}
return false;
},
'address': function(value){
if (typeof(value) === 'string' || value === ''){
return true;;
}
return false;
},
'comment': function(value){
if (typeof(value) === 'string' || value === ''){
return true;;
}
return false;
},
'origin': function(value){
return Number.isInteger(value);
},
'last_update': function(value){
return Number.isInteger(value);
}
};
const validSubKeyProperties = {
'invalid': function(value){
return typeof(value) === 'boolean';
},
'can_encrypt': function(value){
return typeof(value) === 'boolean';
},
'can_sign': function(value){
return typeof(value) === 'boolean';
},
'can_certify': function(value){
return typeof(value) === 'boolean';
},
'can_authenticate': function(value){
return typeof(value) === 'boolean';
},
'secret': function(value){
return typeof(value) === 'boolean';
},
'is_qualified': function(value){
return typeof(value) === 'boolean';
},
'is_cardkey': function(value){
return typeof(value) === 'boolean';
},
'is_de_vs': function(value){
return typeof(value) === 'boolean';
},
'pubkey_algo_name': function(value){
return typeof(value) === 'string';
// TODO: check against list of known?['']
},
'pubkey_algo_string': function(value){
return typeof(value) === 'string';
// TODO: check against list of known?['']
},
'keyid': function(value){
return isLongId(value);
},
'pubkey_algo': function(value) {
return (Number.isInteger(value) && value >= 0);
},
'length': function(value){
return (Number.isInteger(value) && value > 0);
},
'timestamp': function(value){
return (Number.isInteger(value) && value > 0);
},
'expires': function(value){
return (Number.isInteger(value) && value > 0);
}
}
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index 80792f77..9abb9ec3 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -1,158 +1,144 @@
/* 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 {createMessage} from './Message'
import {GPGME_Key} from './Key'
import { isFingerprint } from './Helpers';
import { gpgme_error } from './Errors';
-import { Connection } from './Connection';
export class GPGME_Keyring {
- constructor(connection){
- this.connection = connection;
- }
-
- set connection(connection){
- if (!this._connection && connection instanceof Connection){
- this._connection = connection;
- }
- }
- get connection(){
- if (this._connection instanceof Connection){
- return this._connection;
- }
- return gpgme_error('CONN_NO_CONNECT');
+ constructor(){
}
/**
* @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 me = this;
return new Promise(function(resolve, reject) {
let msg;
msg = createMessage('listkeys');
if (pattern && typeof(pattern) === 'string'){
msg.setParameter('pattern', pattern);
}
if (include_secret){
msg.setParameter('with-secret', true);
}
- me.connection.post(msg).then(function(result){
+ msg.post().then(function(result){
let fpr_list = [];
let resultset = [];
if (!Array.isArray(result.keys)){
//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], me._connection);
+ let newKey = new GPGME_Key(fpr_list[i]);
if (newKey instanceof GPGME_Key){
resultset.push(newKey);
}
}
resolve(resultset);
}, function(error){
reject(error);
});
});
}
/**
* @param {Object} flags subset filter expecting at least one of the
* filters described below. True will filter on the condition, False will
* reverse the filter, if not present or undefined, the filter will not be
* considered. Please note that some combination may not make sense
* @param {Boolean} flags.secret Only Keys containing a secret part.
* @param {Boolean} flags.revoked revoked Keys only
* @param {Boolean} flags.expired Expired Keys only
* @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
* @returns {Promise Array<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);
}, function(error){
//TODO error handling
});
}
};
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 932212a6..5664f723 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -1,196 +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 <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1+
*/
import { permittedOperations } from './permittedOperations'
import { gpgme_error } from './Errors'
+import { Connection } from './Connection';
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;
this._expected = 'string';
}
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;
}
set expected(string){
if (string === 'base64'){
this._expected = 'base64';
}
}
get expected() {
if (this._expected === "base64"){
return this._expected;
}
return "string";
}
/**
* 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');
}
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');
}
let checktype = function(val){
switch(typeof(val)){
case 'string':
if (poparam.allowed.indexOf(typeof(val)) >= 0
&& val.length > 0) {
return true;
}
return gpgme_error('PARAM_WRONG');
break;
case 'number':
if (
poparam.allowed.indexOf('number') >= 0
&& isNaN(value) === false){
return true;
}
return gpgme_error('PARAM_WRONG');
break;
case 'boolean':
if (poparam.allowed.indexOf('boolean') >= 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;
}
}
if (val.length > 0) {
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 = Object.keys(
permittedOperations[this._msg.op].required);
let msg_params = Object.keys(this._msg);
for (let i=0; i < reqParams.length; i++){
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;
}
}
+
+ post(){
+ let me = this;
+ return new Promise(function(resolve, reject) {
+ if (me.isComplete === true) {
+ let conn = new Connection;
+ conn.post(me).then(function(response) {
+ resolve(response);
+ }, function(reason) {
+ reject(gpgme_error('GNUPG_ERROR', reason));
+ });
+ }
+ else {
+ reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ });
+ }
}
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index c182c175..88a91a60 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -1,224 +1,199 @@
/* 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 {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;
+ constructor(config){ //TODO config not parsed
+ this._config = config;
}
- set connection(conn){
- if (this._connection instanceof Connection){
- gpgme_error('CONN_ALREADY_CONNECTED');
- } else if (conn instanceof Connection){
- this._connection = conn;
- } else {
- gpgme_error('PARAM_WRONG');
- }
- }
-
- get connection(){
- return this._connection;
- }
-
- set Keyring(keyring){
+ set Keyring(keyring){
if (keyring && keyring instanceof GPGME_Keyring){
this._Keyring = keyring;
}
}
get Keyring(){
+ if (!this._Keyring){
+ this._Keyring = new GPGME_Keyring;
+ }
return this._Keyring;
}
/**
* @param {String} data text/data to be encrypted as String
* @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} 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, base64=false, 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);
if (base64 === true) {
msg.setParameter('base64', true);
}
let pubkeys = toKeyIdArray(publicKeys);
msg.setParameter('keys', pubkeys);
putData(msg, data);
if (wildcard === true){
msg.setParameter('throw-keyids', true);
};
if (msg.isComplete === true){
- return this.connection.post(msg);
+ return msg.post();
} else {
return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
}
}
/**
* @param {String} data TODO base64? Message with the encrypted data
* @param {Boolean} base64 (optional) Response should stay base64
* @returns {Promise<Object>} 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, base64=false){
if (data === undefined){
return Promise.reject(gpgme_error('MSG_EMPTY'));
}
let msg = createMessage('decrypt');
if (base64 === true){
msg.expected = 'base64';
}
if (msg instanceof Error){
return Promise.reject(msg);
}
putData(msg, data);
- return this.connection.post(msg);
+ return msg.post();
}
sign(data, keys, mode='clearsign', base64=false) { //sender
if (data === undefined){
return Promise.reject(gpgme_error('MSG_EMPTY'));
}
let key_arr = toKeyIdArray(keys);
if (key_arr.length === 0){
return Promise.reject(gpgme_error('MSG_NO_KEYS'));
}
let msg = createMessage('sign');
msg.setParameter('keys', key_arr);
if (base64 === true){
msg.setParameter('base64', true);
}
msg.setParameter('mode', mode);
putData(msg, data);
if (mode === 'detached') {
msg.expected = 'base64';
}
let me = this;
return new Promise(function(resolve,reject) {
- me.connection.post(msg).then( function(message) {
+ msg.post().then( function(message) {
if (mode === 'clearsign'){
resolve({
data: message.data}
);
} else if (mode === 'detached') {
resolve({
data: data,
signature: message.data
});
}
}, function(error){
reject(error);
})
});
}
deleteKey(key, delete_secret = false, no_confirm = false){
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
let msg = createMessage('deletekey');
if (msg instanceof Error){
return Promise.reject(msg);
}
let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){
return Promise.reject(
gpgme_error('GENERIC_ERROR'));
// TBD should always be ONE key?
}
msg.setParameter('key', key_arr[0]);
if (delete_secret === true){
msg.setParameter('allow_secret', true);
// TBD
}
if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
msg.setParameter('delete_force', true);
// TBD
}
if (msg.isComplete === true){
- 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
- }
- });
+ return msg.post();
} else {
return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
}
}
}
/**
* Sets the data of the message
* @param {GPGME_Message} message The message where this data will be set
* @param {*} data The data to enter
*/
function putData(message, data){
if (!message || !message instanceof GPGME_Message ) {
return gpgme_error('PARAM_WRONG');
}
if (!data){
return gpgme_error('PARAM_WRONG');
} else if (typeof(data) === 'string') {
message.setParameter('data', data);
} else if (
typeof(data) === 'object' &&
typeof(data.getText) === 'function'
){
let txt = data.getText();
if (typeof(txt) === 'string'){
message.setParameter('data', txt);
} else {
return gpgme_error('PARAM_WRONG');
}
} else {
return gpgme_error('PARAM_WRONG');
}
}
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 7f969fee..220a6984 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -1,82 +1,82 @@
/* 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 } from "./gpgmejs";
import { gpgme_error } from "./Errors";
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;
connection.checkConnection(false).then(
function(result){
if (result === true) {
- resolve(new GpgME(connection, _conf));
+ resolve(new GpgME(_conf));
} else {
reject(gpgme_error('CONN_NO_CONNECT'));
}
}, function(error){
reject(gpgme_error('CONN_NO_CONNECT'));
});
});
}
function parseconfiguration(rawconfig = {}){
if ( typeof(rawconfig) !== 'object'){
return gpgme_error('PARAM_WRONG');
};
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 = 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/unittests.js b/lang/js/unittests.js
index bb06309d..9830a2c5 100644
--- a/lang/js/unittests.js
+++ b/lang/js/unittests.js
@@ -1,378 +1,332 @@
/* 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 "./node_modules/mocha/mocha";
import "./node_modules/chai/chai";
import { helper_params as hp } from "./unittest_inputvalues";
import { message_params as mp } from "./unittest_inputvalues";
import { whatever_params as wp } from "./unittest_inputvalues";
import { key_params as kp } from "./unittest_inputvalues";
import { Connection } from "./src/Connection";
import { gpgme_error } from "./src/Errors";
import { toKeyIdArray , isFingerprint } from "./src/Helpers";
import { GPGME_Key , createKey } from "./src/Key";
import { GPGME_Keyring } from "./src/Keyring";
import {GPGME_Message, createMessage} from "./src/Message";
import { setTimeout } from "timers";
mocha.setup('bdd');
var expect = chai.expect;
chai.config.includeStack = true;
function unittests (){
describe('Connection testing', function(){
it('Connecting', function(done) {
let conn0 = new Connection;
conn0.checkConnection().then(function(answer) {
expect(answer).to.not.be.empty;
expect(answer.gpgme).to.not.be.undefined;
expect(answer.gpgme).to.be.a('string');
expect(answer.info).to.be.an('Array');
expect(conn0.disconnect).to.be.a('function');
expect(conn0.post).to.be.a('function');
- conn0.disconnect();
done();
});
});
it('Disconnecting', function(done) {
let conn0 = new Connection;
conn0.checkConnection(false).then(function(answer) {
expect(answer).to.be.true;
conn0.disconnect();
conn0.checkConnection(false).then(function(result) {
expect(result).to.be.false;
done();
});
});
});
});
describe('Error Object handling', function(){
// TODO: new GPGME_Error codes
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(hp.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(hp.validFingerprint);
expect(test0).to.be.true;
});
it('isFingerprint(): invalid Fingerprints', function(){
for (let i=0; i < hp.invalidFingerprints.length; i++){
let test0 = isFingerprint(hp.invalidFingerprints[i]);
expect(test0).to.be.false;
}
});
});
describe('toKeyIdArray() (converting input to fingerprint)', function(){
it('Correct fingerprint string', function(){
let test0 = toKeyIdArray(hp.validFingerprint);
expect(test0).to.be.an('array');
expect(test0).to.include(hp.validFingerprint);
});
it('correct GPGME_Key', function(){
expect(hp.validGPGME_Key).to.be.an.instanceof(GPGME_Key);
let test0 = toKeyIdArray(hp.validGPGME_Key);
expect(test0).to.be.an('array');
expect(test0).to.include(hp.validGPGME_Key.fingerprint);
});
it('openpgpjs-like object', function(){
let test0 = toKeyIdArray(hp.valid_openpgplike);
expect(test0).to.be.an('array').with.lengthOf(1);
expect(test0).to.include(
hp.valid_openpgplike.primaryKey.getFingerprint());
});
it('Array of valid inputs', function(){
let test0 = toKeyIdArray(hp.validKeys);
expect(test0).to.be.an('array');
expect(test0).to.have.lengthOf(hp.validKeys.length);
});
it('Incorrect inputs', function(){
it('valid Long ID', function(){
let test0 = toKeyIdArray(hp.validLongId);
expect(test0).to.be.empty;
});
it('invalidFingerprint', function(){
let test0 = toKeyIdArray(hp.invalidFingerprint);
expect(test0).to.be.empty;
});
it('invalidKeyArray', function(){
let test0 = toKeyIdArray(hp.invalidKeyArray);
expect(test0).to.be.empty;
});
it('Partially invalid array', function(){
let test0 = toKeyIdArray(hp.invalidKeyArray_OneBad);
expect(test0).to.be.an('array');
expect(test0).to.have.lengthOf(
hp.invalidKeyArray_OneBad.length - 1);
});
});
});
describe('GPGME_Key', function(){
it('correct Key initialization', function(){
- let conn = new Connection;
- let key = createKey(kp.validKeyFingerprint, conn);
+ let key = createKey(kp.validKeyFingerprint);
expect(key).to.be.an.instanceof(GPGME_Key);
- expect(key.connection).to.be.an.instanceof(Connection);
- conn.disconnect();
});
it('Key has data after a first refresh', function(done) {
- let conn = new Connection;
- let key = createKey(kp.validKeyFingerprint, conn);
+ let key = createKey(kp.validKeyFingerprint);
key.refreshKey().then(function(key2){
expect(key2).to.be.an.instanceof(GPGME_Key);
expect(key2.get).to.be.a('function');
for (let i=0; i < kp.validKeyProperties.length; i++) {
let prop = key2.get(kp.validKeyProperties[i]);
expect(prop).to.not.be.undefined;
expect(prop).to.be.a('boolean');
}
expect(isFingerprint(key2.get('fingerprint'))).to.be.true;
expect(
key2.get('fingerprint')).to.equal(kp.validKeyFingerprint);
expect(
key2.get('fingerprint')).to.equal(key.fingerprint);
- conn.disconnect();
done();
});
});
it('Non-cached key async data retrieval', function (done){
- let conn = new Connection;
- let key = createKey(kp.validKeyFingerprint, conn);
+ let key = createKey(kp.validKeyFingerprint);
key.get('can_authenticate',false).then(function(result){
expect(result).to.be.a('boolean');
- conn.disconnect();
done();
});
})
it('Querying non-existing Key returns an error', function(done) {
- let conn = new Connection;
- let key = createKey(kp.invalidKeyFingerprint, conn);
+ let key = createKey(kp.invalidKeyFingerprint);
key.refreshKey().then(function(){},
function(error){
expect(error).to.be.an.instanceof(Error);
expect(error.code).to.equal('KEY_NOKEY');
- conn.disconnect();
- done();
- });
- });
-
-
- it('Key can use the connection', function(done){
- let conn = new Connection;
- let key = createKey(hp.validFingerprint, conn);
- key.connection.checkConnection(false).then(function(result){
- expect(result).to.be.true;
- key.connection.disconnect();
- key.connection.checkConnection(false).then(function(result2){
- expect(result2).to.be.false;
- conn.disconnect();
done();
- });
});
});
it('createKey returns error if parameters are wrong', function(){
- let conn = new Connection;
for (let i=0; i< 4; i++){
- let key0 = createKey(wp.four_invalid_params[i], conn);
-
+ let key0 = createKey(wp.four_invalid_params[i]);
expect(key0).to.be.an.instanceof(Error);
expect(key0.code).to.equal('PARAM_WRONG');
}
- for (let i=0; i< 4; i++){
- let key0 = createKey(
- hp.validFingerprint, wp.four_invalid_params[i]);
-
- expect(key0).to.be.an.instanceof(Error);
- expect(key0.code).to.equal('PARAM_WRONG');
- }
- conn.disconnect();
});
it('malformed GPGME_Key cannot be used', function(){
- let conn = new Connection;
for (let i=0; i < 4; i++){
- let key = new GPGME_Key(wp.four_invalid_params[i], conn);
+ let key = new GPGME_Key(wp.four_invalid_params[i]);
expect(key.fingerprint).to.be.an.instanceof(Error);
expect(key.fingerprint.code).to.equal('KEY_INVALID');
}
- conn.disconnect();
});
// TODO: tests for subkeys
// TODO: tests for userids
// TODO: some invalid tests for key/keyring
});
describe('GPGME_Keyring', function(){
- it('correct initialization', function(){
- let conn = new Connection;
- let keyring = new GPGME_Keyring(conn);
-
+ it('correct Keyring initialization', function(){
+ let keyring = new GPGME_Keyring;
expect(keyring).to.be.an.instanceof(GPGME_Keyring);
- expect(keyring.connection).to.be.an.instanceof(Connection);
expect(keyring.getKeys).to.be.a('function');
expect(keyring.getSubset).to.be.a('function');
});
- it('Keyring should return errors if not connected', function(){
+ it('correct initialization', function(){
let keyring = new GPGME_Keyring;
expect(keyring).to.be.an.instanceof(GPGME_Keyring);
- expect(keyring.connection).to.be.an.instanceof(Error);
- expect(keyring.connection.code).to.equal('CONN_NO_CONNECT');
- // not yet implemented:
- // keyring.getKeys().then(
- // function(result){},
- //function(reject){
- // expect(reject).to.be.an.instanceof(Error);
- // done();
+ expect(keyring.getKeys).to.be.a('function');
+ expect(keyring.getSubset).to.be.a('function');
});
//TODO not yet implemented:
// getKeys(pattern, include_secret) //note: pattern can be null
// getSubset(flags, pattern)
// available Boolean flags: secret revoked expired
});
describe('GPGME_Message', function(){
it('creating encrypt 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 mandatory 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('Message is not complete after mandatory data is empty', function(){
let test0 = createMessage('encrypt');
test0.setParameter('data', '');
test0.setParameter('keys', hp.validFingerprints);
expect(test0.isComplete).to.be.false;
});
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);
});
it ('Not accepting non-allowed operation', function(){
let test0 = createMessage(mp.invalid_op_action);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('MSG_WRONG_OP');
});
it('Not accepting wrong parameter type', function(){
let test0 = createMessage(mp.invalid_op_type);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('PARAM_WRONG');
});
it('Not accepting 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('Not accepting 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 {unittests};
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Mon, Feb 2, 8:25 AM (12 h, 4 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8f/e4/363fd0ec6a8b143ba6516a758df5

Event Timeline