From eef3a509fa5744e5f09ec8084985e6070b78226b Mon Sep 17 00:00:00 2001
From: "raimund.renkert@intevation.de"
Date: Tue, 10 Apr 2018 11:33:14 +0200
Subject: js: Initial commit for JavaScript Native Messaging API
--
Note this code misses all the legal boilerplate; please add this as
soon as possible and provide a DCO so we can merge it into master.
I also removed the dist/ directory because that was not source code.
---
lang/README | 2 +-
lang/js/CHECKLIST | 30 ++++++++
lang/js/CHECKLIST_build | 9 +++
lang/js/README | 52 +++++++++++++
lang/js/manifest.json | 18 +++++
lang/js/package.json | 17 +++++
lang/js/src/Connection.js | 76 ++++++++++++++++++
lang/js/src/gpgmejs.js | 187 +++++++++++++++++++++++++++++++++++++++++++++
lang/js/src/index.js | 14 ++++
lang/js/testapplication.js | 21 +++++
lang/js/testicon.png | Bin 0 -> 16192 bytes
lang/js/ui.css | 10 +++
lang/js/ui.html | 24 ++++++
lang/js/webpack.conf.js | 13 ++++
14 files changed, 472 insertions(+), 1 deletion(-)
create mode 100644 lang/js/CHECKLIST
create mode 100644 lang/js/CHECKLIST_build
create mode 100644 lang/js/README
create mode 100644 lang/js/manifest.json
create mode 100644 lang/js/package.json
create mode 100644 lang/js/src/Connection.js
create mode 100644 lang/js/src/gpgmejs.js
create mode 100644 lang/js/src/index.js
create mode 100644 lang/js/testapplication.js
create mode 100644 lang/js/testicon.png
create mode 100644 lang/js/ui.css
create mode 100644 lang/js/ui.html
create mode 100644 lang/js/webpack.conf.js
diff --git a/lang/README b/lang/README
index ee99f0f1..afd7b083 100644
--- a/lang/README
+++ b/lang/README
@@ -13,4 +13,4 @@ cl Common Lisp
cpp C++
qt Qt-Framework API
python Python 2 and 3 (module name: gpg)
-javascript Native messaging client for the gpgme-json server.
+js Native messaging client for the gpgme-json server.
diff --git a/lang/js/CHECKLIST b/lang/js/CHECKLIST
new file mode 100644
index 00000000..79a35cb7
--- /dev/null
+++ b/lang/js/CHECKLIST
@@ -0,0 +1,30 @@
+NativeConnection:
+
+ [X] nativeConnection: successfully sending an encrypt request,
+receiving an answer
+ [X] nativeConnection successfull on Chromium, chrome and firefox
+ [ ] nativeConnection successfull on Windows, macOS, Linux
+ [ ] nativeConnection with delayed, multipart (> 1MB) answer
+
+replicating Openpgpjs API:
+
+ [*] Message handling (encrypt, verify, sign)
+ [ ] Key handling (import/export, modifying, status queries)
+ [ ] Configuration handling
+ [ ] check for completeness
+ [ ] handling of differences to openpgpjs
+
+Communication with other implementations
+
+ [ ] option to export SECRET Key into localstore used by e.g. mailvelope
+
+Management:
+ [*] Define the gpgme interface
+ [ ] check Permissions (e.g. csp) for the different envs
+ [ ] agree on license
+ [ ] tests
+
+
+Problems:
+ [X] gpgme-json: interactive mode vs. bytelength; filename
+ [X] nativeApp chokes on arrays. We will get rid of that bnativeapp anyhow
diff --git a/lang/js/CHECKLIST_build b/lang/js/CHECKLIST_build
new file mode 100644
index 00000000..fa162a10
--- /dev/null
+++ b/lang/js/CHECKLIST_build
@@ -0,0 +1,9 @@
+- Checklist for build/install:
+
+browsers' manifests (see README) need allowedextension added, and the path set
+
+manifest.json/ csp needs adaption
+
+/dist contains a current build which is used by example app.
+We may either want to update it on every commit, or never at all, but not
+inconsistently.
diff --git a/lang/js/README b/lang/js/README
new file mode 100644
index 00000000..3ca07439
--- /dev/null
+++ b/lang/js/README
@@ -0,0 +1,52 @@
+This is an example app for gpgme-json.
+As of now, it only encrypts a given text.
+
+Installation
+-------------
+
+gpgmejs uses webpack, the builds can be found in dist/
+(the testapplication uses that script at that location). To create a new
+package, the command is npx webpack --config webpack.conf.js.
+If you want a more debuggable (i.e. not minified) build, just change the mode
+in webpack.conf.js.
+
+Demo WebExtension:
+As soon as a bundled webpack is in dist/ (TODO: .gitignore or not?),
+the gpgmejs folder can just be included in the extensions tab of the browser in
+questions (extension debug mode needs to be active). For chrome, selecting the
+folder is sufficient, for firefox, the manifest.json needs to be selected.
+
+In the browsers' nativeMessaging configuration folder a file 'gpgmejs.json'
+is needed, with the following content:
+
+(The path to the native app gpgme-json may need adaption)
+
+Chromium:
+~/.config/chromium/NativeMessagingHosts/gpgmejson.json
+
+{
+ "name": "gpgmejson",
+ "description": "This is a test application for gpgmejs",
+ "path": "/usr/bin/gpgme-json",
+ "type": "stdio",
+ "allowed_origins": ["chrome-extension://ExtensionIdentifier/"]
+}
+The ExtensionIdentifier can be seen on the chrome://extensions page, and
+changes on each reinstallation. Note the slashes in allowed_origins.
+
+
+Firefox:
+~/.mozilla/native-messaging-hosts/gpgmejson.json
+{
+ "name": "gpgmejson",
+ "description": "This is a test application for gpgmejs",
+ "path": "/usr/bin/gpgme-json",
+ "type": "stdio",
+ "allowed_extensions": ["ExtensionIdentifier@temporary-addon"]
+}
+The ExtensionIdentifier can be seen as Extension ID on the about:addons page if
+addon-debugging is active. In firefox, the temporary addon is removed once
+firefox exits, and the identifier will need to be changed more often.
+
+For testing purposes, it could be a good idea to change the keyID in the
+ui.html, to not having to type it every time.
diff --git a/lang/js/manifest.json b/lang/js/manifest.json
new file mode 100644
index 00000000..8bb5c58d
--- /dev/null
+++ b/lang/js/manifest.json
@@ -0,0 +1,18 @@
+{
+ "manifest_version": 2,
+
+ "name": "gpgme-json with native Messaging",
+ "description": "This should be able to encrypt a text using gpgme-json",
+ "version": "0.1",
+ "content_security_policy": "default-src 'self' 'unsafe-eval' filesystem",
+ "browser_action": {
+ "default_icon": "testicon.png",
+ "default_title": "gpgme.js",
+ "default_popup": "ui.html"
+ },
+ "permissions": ["nativeMessaging", "activeTab"],
+
+ "background": {
+ "scripts": [ "dist/gpgmejs.bundle.js"]
+ }
+}
diff --git a/lang/js/package.json b/lang/js/package.json
new file mode 100644
index 00000000..46b60fd2
--- /dev/null
+++ b/lang/js/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "gpgmejs",
+ "version": "0.0.1",
+ "description": "javascript part of a nativeMessaging gnupg integration",
+ "main": "src/gpgmejs.js",
+ "private": true,
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "",
+ "devDependencies": {
+ "webpack": "^4.3.0",
+ "webpack-cli": "^2.0.13"
+ }
+}
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
new file mode 100644
index 00000000..e8fea542
--- /dev/null
+++ b/lang/js/src/Connection.js
@@ -0,0 +1,76 @@
+/**
+ * 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.
+ */
+
+export function Connection(){
+ if (!this.connection){
+ this.connection = connect();
+ this._msg = {
+ 'always-trust': true,
+ // 'no-encrypt-to': false,
+ // 'no-compress': true,
+ // 'throw-keyids': false,
+ // 'wrap': false,
+ 'armor': true,
+ 'base64': false
+ };
+ };
+
+ this.disconnect = function () {
+ if (this.connection){
+ this.connection.disconnect();
+ }
+ };
+
+ /**
+ * Sends a message and resolves with the answer.
+ * @param {*} operation The interaction requested from gpgme
+ * @param {*} message A json-capable object to pass the operation details.
+ * TODO: _msg should contain configurable parameters
+ */
+ this.post = function(operation, message){
+ let timeout = 5000;
+ let me = this;
+ if (!message || !operation){
+ return Promise.reject('no message'); // TBD
+ }
+
+ let keys = Object.keys(message);
+ for (let i=0; i < keys.length; i++){
+ let property = keys[i];
+ me._msg[property] = message[property];
+ }
+ me._msg['op'] = operation;
+ // TODO fancier checks if what we want is consistent with submitted content
+ return new Promise(function(resolve, reject){
+ me.connection.onMessage.addListener(function(msg) {
+ if (!msg){
+ reject('empty answer.');
+ }
+ if (msg.type === "error"){
+ reject(msg.msg);
+ }
+ resolve(msg);
+ });
+
+ me.connection.postMessage(me._msg);
+ setTimeout(
+ function(){
+ me.disconnect();
+ reject('Timeout');
+ }, timeout);
+ });
+ };
+};
+
+
+function connect(){
+ let connection = chrome.runtime.connectNative('gpgmejson');
+ if (!connection){
+ let msg = chrome.runtime.lastError || 'no message'; //TBD
+ throw(msg);
+ }
+ return connection;
+};
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
new file mode 100644
index 00000000..dedbf809
--- /dev/null
+++ b/lang/js/src/gpgmejs.js
@@ -0,0 +1,187 @@
+import {Connection} from "./Connection"
+
+export function encrypt(data, publicKeys, privateKeys, passwords=null,
+ sessionKey, filename, compression, armor=true, detached=false,
+ signature=null, returnSessionKey=false, wildcard=false, date=new Date()){
+ // gpgme_op_encrypt ( <-gpgme doc on this operation
+ // gpgme_ctx_t ctx,
+ // gpgme_key_t recp[],
+ // gpgme_encrypt_flags_t flags,
+ // gpgme_data_t plain,
+ // gpgme_data_t cipher)
+ // flags:
+ // GPGME_ENCRYPT_ALWAYS_TRUST
+ // GPGME_ENCRYPT_NO_ENCRYPT_TO
+ // GPGME_ENCRYPT_NO_COMPRESS
+ // GPGME_ENCRYPT_PREPARE
+ // GPGME_ENCRYPT_EXPECT_SIGN
+ // GPGME_ENCRYPT_SYMMETRIC
+ // GPGME_ENCRYPT_THROW_KEYIDS
+ // GPGME_ENCRYPT_WRAP
+ if (passwords !== null){
+ throw('Password!'); // TBD
+ }
+
+ let pubkeys = toKeyIdArray(publicKeys);
+ let privkeys = toKeyIdArray(privateKeys);
+
+ // TODO filename: data is supposed to be empty, file is provided
+ // TODO config compression detached signature
+ // TODO signature to add to the encrypted message (?) || privateKeys: signature is desired
+ // gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher)
+
+ // TODO sign date overwriting implemented in gnupg?
+
+ let conn = new Connection();
+ if (wildcard){
+ // Connection.set('throw-keyids', true); TODO Connection.set not yet existant
+ }
+ return conn.post('encrypt', {
+ 'data': data,
+ 'keys': publicKeys,
+ 'armor': armor});
+};
+
+export function decrypt(message, privateKeys, passwords, sessionKeys, publicKeys,
+ format='utf8', signature=null, date=new Date()) {
+ if (passwords !== null){
+ throw('Password!'); // TBD
+ }
+ if (format === 'binary'){
+ // Connection.set('base64', true);
+ }
+ if (publicKeys || signature){
+ // Connection.set('signature', signature);
+ // request verification, too
+ }
+ //privateKeys optionally if keyId was thrown?
+ // gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
+ // response is gpgme_op_decrypt_result (gpgme_ctx_t ctx) (next available?)
+ return conn.post('decrypt', {
+ 'data': message
+ });
+}
+
+// BIG TODO.
+export function generateKey({userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve="", date=new Date()}){
+ throw('not implemented here');
+ // gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t extrakey, unsigned int flags);
+ return false;
+}
+
+export function sign({ data, privateKeys, armor=true, detached=false, date=new Date() }) {
+ //TODO detached GPGME_SIG_MODE_DETACH | GPGME_SIG_MODE_NORMAL
+ // gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode)
+ // TODO date not supported
+
+ let conn = new Connection();
+ let privkeys = toKeyIdArray(privateKeys);
+ return conn.post('sign', {
+ 'data': data,
+ 'keys': privkeys,
+ 'armor': armor});
+};
+
+export function verify({ message, publicKeys, signature=null, date=new Date() }) {
+ //TODO extra signature: sig, signed_text, plain: null
+ // inline sig: signed_text:null, plain as writable (?)
+ // date not supported
+ //gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plain)
+ let conn = new Connection();
+ let privkeys = toKeyIdArray(privateKeys);
+ return conn.post('sign', {
+ 'data': data,
+ 'keys': privkeys,
+ 'armor': armor});
+}
+
+
+export function reformatKey(privateKey, userIds=[], passphrase="", unlocked=false, keyExpirationTime=0){
+ let privKey = toKeyIdArray(privateKey);
+ if (privKey.length !== 1){
+ return false; //TODO some error handling. There is not exactly ONE key we are editing
+ }
+ let conn = new Connection();
+ // TODO key management needs to be changed somewhat
+ return conn.post('TODO', {
+ 'key': privKey[0],
+ 'keyExpirationTime': keyExpirationTime, //TODO check if this is 0 or a positive and plausible number
+ 'userIds': userIds //TODO check if empty or plausible strings
+ });
+ // unlocked will be ignored
+}
+
+export function decryptKey({ privateKey, passphrase }) {
+ throw('not implemented here');
+ return false;
+};
+
+export function encryptKey({ privateKey, passphrase }) {
+ throw('not implemented here');
+ return false;
+};
+
+export function encryptSessionKey({data, algorithm, publicKeys, passwords, wildcard=false }) {
+ //openpgpjs:
+ // Encrypt a symmetric session key with public keys, passwords, or both at
+ // once. At least either public keys or passwords must be specified.
+ throw('not implemented here');
+ return false;
+};
+
+export function decryptSessionKeys({ message, privateKeys, passwords }) {
+ throw('not implemented here');
+ return false;
+};
+
+// //TODO worker handling
+
+// //TODO key representation
+// //TODO: keyring handling
+
+
+/**
+ * Helper functions and checks
+ */
+
+/**
+ * Checks if the submitted value is a keyID.
+ * TODO: should accept all strings that are accepted as keyID by gnupg
+ * TODO: See if Key becomes an object later on
+ * @param {*} key input value. Is expected to be a string of 8,16 or 40 chars
+ * representing hex values. Will return false if that expectation is not met
+ */
+function isKeyId(key){
+ if (!key || typeof(key) !== "string"){
+ return false;
+ }
+ if ([8,16,40].indexOf(key.length) < 0){
+ return false;
+ }
+ let regexp= /^[0-9a-fA-F]*$/i;
+ return regexp.test(key);
+};
+
+/**
+ * Tries to return an array of keyID values, either from a string or an array.
+ * Filters out those that do not meet the criteria. (TODO: silently for now)
+ * @param {*} array Input value.
+ */
+function toKeyIdArray(array){
+ let result = [];
+ if (!array){
+ return result;
+ }
+ if (!Array.isArray(array)){
+ if (isKeyId(array) === true){
+ return [keyId];
+ }
+ return result;
+ }
+ for (let i=0; i < array.length; i++){
+ if (isKeyId(array[i]) === true){
+ result.push(array[i]);
+ }
+ }
+ return result;
+};
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
new file mode 100644
index 00000000..02dc919d
--- /dev/null
+++ b/lang/js/src/index.js
@@ -0,0 +1,14 @@
+import * as gpgmejs from'./gpgmejs'
+export default gpgmejs;
+
+/**
+ * Export each high level api function separately.
+ * Usage:
+ *
+ * import { encryptMessage } from 'gpgme.js'
+ * encryptMessage(keys, text)
+ */
+export {
+ encrypt, decrypt, sign, verify,
+ generateKey, reformatKey
+ } from './gpgmejs';
diff --git a/lang/js/testapplication.js b/lang/js/testapplication.js
new file mode 100644
index 00000000..d01aca99
--- /dev/null
+++ b/lang/js/testapplication.js
@@ -0,0 +1,21 @@
+/**
+* Testing nativeMessaging. This is a temporary plugin using the gpgmejs
+ implemetation as contained in src/
+*/
+function buttonclicked(event){
+ let data = document.getElementById("text0").value;
+ let keyId = document.getElementById("key").value;
+ let enc = Gpgmejs.encrypt(data, [keyId]).then(function(answer){
+ console.log(answer);
+ console.log(answer.type);
+ console.log(answer.data);
+ alert(answer.data);
+ }, function(errormsg){
+ alert('Error: '+ errormsg);
+ });
+};
+
+document.addEventListener('DOMContentLoaded', function() {
+ document.getElementById("button0").addEventListener("click",
+ buttonclicked);
+ });
diff --git a/lang/js/testicon.png b/lang/js/testicon.png
new file mode 100644
index 00000000..12c3f5df
Binary files /dev/null and b/lang/js/testicon.png differ
diff --git a/lang/js/ui.css b/lang/js/ui.css
new file mode 100644
index 00000000..9c88698b
--- /dev/null
+++ b/lang/js/ui.css
@@ -0,0 +1,10 @@
+ul {
+ list-style-type: none;
+ padding-left: 0px;
+}
+
+ul li span {
+ float: left;
+ width: 120px;
+ margin-top: 6px;
+}
diff --git a/lang/js/ui.html b/lang/js/ui.html
new file mode 100644
index 00000000..9c56c2e5
--- /dev/null
+++ b/lang/js/ui.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+ Encrypt
+
+
+
diff --git a/lang/js/webpack.conf.js b/lang/js/webpack.conf.js
new file mode 100644
index 00000000..71b71161
--- /dev/null
+++ b/lang/js/webpack.conf.js
@@ -0,0 +1,13 @@
+const path = require('path');
+
+module.exports = {
+ entry: './src/index.js',
+ // mode: 'development',
+ mode: 'production',
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'gpgmejs.bundle.js',
+ libraryTarget: 'var',
+ library: 'Gpgmejs'
+ }
+};
--
cgit v1.2.3
From 6ab25e40d904007755c5d999bf66ae264236e745 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Wed, 18 Apr 2018 16:38:06 +0200
Subject: js: encrypt improvement and decrypt method
* Compatibility class gpgme_openpgpjs offers an API that should accept
openpgpjs syntax, throwing errors if a parameter is unexpected/not
implemented
* tried to be more generic in methods
* waiting for multiple answers if 'more' is in the answer
* more consistency checking on sending and receiving
* updated the example extension
--
---
lang/js/CHECKLIST | 18 ++-
lang/js/CHECKLIST_build | 6 +-
lang/js/README | 23 +--
lang/js/manifest.json | 10 +-
lang/js/package.json | 6 +-
lang/js/src/Connection.js | 208 ++++++++++++++++++++-------
lang/js/src/Helpers.js | 84 +++++++++++
lang/js/src/Message.js | 109 ++++++++++++++
lang/js/src/gpgmejs.js | 282 +++++++++++++++----------------------
lang/js/src/gpgmejs_openpgpjs.js | 156 ++++++++++++++++++++
lang/js/src/index.js | 33 +++--
lang/js/src/permittedOperations.js | 75 ++++++++++
lang/js/test_index.js | 25 ++++
lang/js/testapplication.js | 70 ++++++---
lang/js/testapplication_index.html | 9 ++
lang/js/ui.html | 24 ----
lang/js/ui2.html | 33 +++++
lang/js/webpack.conf.js | 22 +++
18 files changed, 887 insertions(+), 306 deletions(-)
create mode 100644 lang/js/src/Helpers.js
create mode 100644 lang/js/src/Message.js
create mode 100644 lang/js/src/gpgmejs_openpgpjs.js
create mode 100644 lang/js/src/permittedOperations.js
create mode 100644 lang/js/test_index.js
create mode 100644 lang/js/testapplication_index.html
delete mode 100644 lang/js/ui.html
create mode 100644 lang/js/ui2.html
diff --git a/lang/js/CHECKLIST b/lang/js/CHECKLIST
index 79a35cb7..49b17265 100644
--- a/lang/js/CHECKLIST
+++ b/lang/js/CHECKLIST
@@ -3,16 +3,19 @@ NativeConnection:
[X] nativeConnection: successfully sending an encrypt request,
receiving an answer
[X] nativeConnection successfull on Chromium, chrome and firefox
- [ ] nativeConnection successfull on Windows, macOS, Linux
- [ ] nativeConnection with delayed, multipart (> 1MB) answer
+ [*] nativeConnection successfull on Windows, macOS, Linux
+ [*] nativeConnection with delayed, multipart (> 1MB) answer
replicating Openpgpjs API:
[*] Message handling (encrypt, verify, sign)
- [ ] Key handling (import/export, modifying, status queries)
+ [x] encrypt
+ [ ] verify
+ [ ] sign
+ [*] Key handling (import/export, modifying, status queries)
[ ] Configuration handling
[ ] check for completeness
- [ ] handling of differences to openpgpjs
+ [*] handling of differences to openpgpjs
Communication with other implementations
@@ -21,10 +24,5 @@ Communication with other implementations
Management:
[*] Define the gpgme interface
[ ] check Permissions (e.g. csp) for the different envs
- [ ] agree on license
+ [X] agree on license
[ ] tests
-
-
-Problems:
- [X] gpgme-json: interactive mode vs. bytelength; filename
- [X] nativeApp chokes on arrays. We will get rid of that bnativeapp anyhow
diff --git a/lang/js/CHECKLIST_build b/lang/js/CHECKLIST_build
index fa162a10..19eb2146 100644
--- a/lang/js/CHECKLIST_build
+++ b/lang/js/CHECKLIST_build
@@ -4,6 +4,6 @@ browsers' manifests (see README) need allowedextension added, and the path set
manifest.json/ csp needs adaption
-/dist contains a current build which is used by example app.
-We may either want to update it on every commit, or never at all, but not
-inconsistently.
+/dist should be built which is used by the example app.
+
+csp in manifest.json MUST NOT contain "unsafe-eval" in production!
diff --git a/lang/js/README b/lang/js/README
index 3ca07439..5dc3f50b 100644
--- a/lang/js/README
+++ b/lang/js/README
@@ -1,20 +1,28 @@
-This is an example app for gpgme-json.
-As of now, it only encrypts a given text.
+gpgmejs, as contained in this directory, is a javascript library for direct use
+of gnupg in browsers, with the help of nativeMessaging.
Installation
-------------
+gpgmejs uses webpack, and thus depends on nodejs for building. Webpack can be
+installed by running
+`npm install webpack webpack-cli --save-dev`.
-gpgmejs uses webpack, the builds can be found in dist/
-(the testapplication uses that script at that location). To create a new
-package, the command is npx webpack --config webpack.conf.js.
+To create a current version of the package, the command is
+`npx webpack --config webpack.conf.js`.
If you want a more debuggable (i.e. not minified) build, just change the mode
in webpack.conf.js.
+TODO: gpgme_openpgpjs aims to offer an API similar to openpgpjs, throwing errors
+if some part of the API is not implemented, 'translating' objects if possible.
+This will be incorporated into the build process later, for now it is a line to
+uncomment in src/index.js
+
Demo WebExtension:
-As soon as a bundled webpack is in dist/ (TODO: .gitignore or not?),
+As soon as a bundled webpack is in dist/
the gpgmejs folder can just be included in the extensions tab of the browser in
questions (extension debug mode needs to be active). For chrome, selecting the
folder is sufficient, for firefox, the manifest.json needs to be selected.
+Please note that it is just for demonstration/debug purposes!
In the browsers' nativeMessaging configuration folder a file 'gpgmejs.json'
is needed, with the following content:
@@ -47,6 +55,3 @@ Firefox:
The ExtensionIdentifier can be seen as Extension ID on the about:addons page if
addon-debugging is active. In firefox, the temporary addon is removed once
firefox exits, and the identifier will need to be changed more often.
-
-For testing purposes, it could be a good idea to change the keyID in the
-ui.html, to not having to type it every time.
diff --git a/lang/js/manifest.json b/lang/js/manifest.json
index 8bb5c58d..e5e17aa5 100644
--- a/lang/js/manifest.json
+++ b/lang/js/manifest.json
@@ -4,15 +4,11 @@
"name": "gpgme-json with native Messaging",
"description": "This should be able to encrypt a text using gpgme-json",
"version": "0.1",
- "content_security_policy": "default-src 'self' 'unsafe-eval' filesystem",
+ "content_security_policy": "default-src 'self' 'unsafe-eval' filesystem:",
"browser_action": {
"default_icon": "testicon.png",
"default_title": "gpgme.js",
- "default_popup": "ui.html"
+ "default_popup": "testapplication_index.html"
},
- "permissions": ["nativeMessaging", "activeTab"],
-
- "background": {
- "scripts": [ "dist/gpgmejs.bundle.js"]
- }
+ "permissions": ["nativeMessaging", "activeTab"]
}
diff --git a/lang/js/package.json b/lang/js/package.json
index 46b60fd2..2b7dd7ee 100644
--- a/lang/js/package.json
+++ b/lang/js/package.json
@@ -2,7 +2,7 @@
"name": "gpgmejs",
"version": "0.0.1",
"description": "javascript part of a nativeMessaging gnupg integration",
- "main": "src/gpgmejs.js",
+ "main": "src/index.js",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
@@ -11,7 +11,7 @@
"author": "",
"license": "",
"devDependencies": {
- "webpack": "^4.3.0",
- "webpack-cli": "^2.0.13"
+ "webpack": "^4.5.0",
+ "webpack-cli": "^2.0.14"
}
}
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index e8fea542..784929e9 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -1,76 +1,180 @@
+import { GPGME_Message } from "./Message";
+
+/* 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'
-export function Connection(){
- if (!this.connection){
- this.connection = connect();
- this._msg = {
- 'always-trust': true,
- // 'no-encrypt-to': false,
- // 'no-compress': true,
- // 'throw-keyids': false,
- // 'wrap': false,
- 'armor': true,
- 'base64': false
- };
- };
+export class Connection{
- this.disconnect = function () {
- if (this.connection){
- this.connection.disconnect();
+ /**
+ * Opens and closes a port. Thus, it is made sure that the connection can
+ * be used.
+ * THIS BEHAVIOUR MAY CHANGE!
+ * discussion is to keep a port alive as long as the context stays the same
+ *
+ * TODO returns nothing, but triggers exceptions if not successfull
+ */
+ constructor(){
+ this._connection = chrome.runtime.connectNative('gpgmejson');
+ if (!this._connection){
+ if (chrome.runtime.lastError){
+ throw('NO_CONNECT_RLE');
+ } else {
+ throw('NO_CONNECT');
+ }
}
- };
+ this._flags = {}; // TODO general config
+ }
/**
- * Sends a message and resolves with the answer.
- * @param {*} operation The interaction requested from gpgme
- * @param {*} message A json-capable object to pass the operation details.
- * TODO: _msg should contain configurable parameters
+ * Immediately closes the open port
*/
- this.post = function(operation, message){
- let timeout = 5000;
- let me = this;
- if (!message || !operation){
- return Promise.reject('no message'); // TBD
+ disconnect() {
+ if (this._connection){
+ this._connection.disconnect();
}
+ }
- let keys = Object.keys(message);
- for (let i=0; i < keys.length; i++){
- let property = keys[i];
- me._msg[property] = message[property];
+ /**
+ * Sends a message and resolves with the answer.
+ * @param {GPGME_Message} message
+ * @returns {Promise} the gnupg answer, or rejection with error
+ * information
+ * TODO: better/more consistent error information
+ */
+ post(message){
+ if (!message || !message instanceof GPGME_Message){
+ return Promise.reject('ERR_NO_MSG');
}
- me._msg['op'] = operation;
- // TODO fancier checks if what we want is consistent with submitted content
+ // let timeout = 5000; //TODO config
+ let me = this;
return new Promise(function(resolve, reject){
- me.connection.onMessage.addListener(function(msg) {
+ let answer = new Answer(message.op);
+ let listener = function(msg) {
if (!msg){
- reject('empty answer.');
- }
- if (msg.type === "error"){
+ me._connection.onMessage.removeListener(listener)
+ reject('EMPTY_ANSWER');
+ } else if (msg.type === "error"){
+ me._connection.onMessage.removeListener(listener)
reject(msg.msg);
+ } else {
+ answer.add(msg);
+ if (msg.more === true){
+ me._connection.postMessage({'op': 'getmore'});
+ } else {
+ me._connection.onMessage.removeListener(listener)
+ resolve(answer.message);
+ }
}
- resolve(msg);
- });
+ };
- me.connection.postMessage(me._msg);
- setTimeout(
- function(){
- me.disconnect();
- reject('Timeout');
- }, timeout);
+ me._connection.onMessage.addListener(listener);
+ me._connection.postMessage(message);
+ //TBD: needs to be aware if there is a pinentry pending
+ // setTimeout(
+ // function(){
+ // me.disconnect();
+ // reject('TIMEOUT');
+ // }, timeout);
});
- };
+ }
};
+/**
+ * A class for answer objects, checking and processing the return messages of
+ * the nativeMessaging communication
+ * @param {String} operation The operation, to look up validity of return keys
+ */
+class Answer{
-function connect(){
- let connection = chrome.runtime.connectNative('gpgmejson');
- if (!connection){
- let msg = chrome.runtime.lastError || 'no message'; //TBD
- throw(msg);
+ constructor(operation){
+ this.operation = operation;
}
- return connection;
-};
+
+ /**
+ *
+ * @param {Object} msg The message as received with nativeMessaging
+ * TODO: "error" and "more" handling are not in here, but in post()
+ */
+ add(msg){
+ if (this._response === undefined){
+ this._response = {};
+ }
+ let messageKeys = Object.keys(msg);
+ let poa = permittedOperations[this.operation].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){
+ console.log( 'unexpected answer type: ' + msg.type);
+ throw('UNEXPECTED_TYPE');
+
+ }
+ 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]){
+ throw('UNEXPECTED_TYPE');
+ }
+ }
+ //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 {
+ console.log('unexpected answer parameter: ' + key);
+ throw('UNEXPECTED_PARAM');
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns the assembled message. TODO: does not care yet if completed.
+ */
+ get message(){
+ return this._response;
+ }
+}
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
new file mode 100644
index 00000000..eeb7a3c4
--- /dev/null
+++ b/lang/js/src/Helpers.js
@@ -0,0 +1,84 @@
+/* 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+
+ */
+
+/**
+ * Tries to return an array of fingerprints, either from input fingerprints or
+ * from Key objects
+ * @param {String|Array} input Input value.
+ * @returns {Array} Array of fingerprints.
+ */
+export function toKeyIdArray(input){
+ if (!input){
+ return [];
+ // TODO: Warning or error here? Did we expect something or is "nothing" okay?
+ }
+ if (input instanceof Array){
+ let result = [];
+ for (let i=0; i < input.length; i++){
+ if (isFingerprint(input[i]) === true){
+ result.push(input[i]);
+ } else {
+ //TODO error?
+ console.log('gpgmejs/Helpers.js Warning: '+
+ input[i] +
+ ' is not a valid key fingerprint and will not be used');
+ }
+ }
+ return result;
+ } else if (isFingerprint(input) === true) {
+ return [input];
+ }
+ console.log('gpgmejs/Helpers.js Warning: ' + input +
+ ' is not a valid key fingerprint and will not be used');
+ return [];
+};
+
+/**
+ * check if values are valid hexadecimal values of a specified length
+ * @param {*} key input value.
+ * @param {int} len the expected length of the value
+ */
+function hextest(key, len){
+ if (!key || typeof(key) !== "string"){
+ return false;
+ }
+ if (key.length !== len){
+ return false;
+ }
+ let regexp= /^[0-9a-fA-F]*$/i;
+ return regexp.test(key);
+};
+
+/**
+ * check if the input is a valid Hex string with a length of 40
+ */
+export function isFingerprint(string){
+ return hextest(string, 40);
+};
+
+//TODO needed anywhere?
+function isLongId(string){
+ return hextest(string, 16);
+};
+
+//TODO needed anywhere?
+function isShortId(string){
+ return hextest(string, 8);
+};
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
new file mode 100644
index 00000000..90b554a1
--- /dev/null
+++ b/lang/js/src/Message.js
@@ -0,0 +1,109 @@
+/* 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'
+
+export class GPGME_Message {
+ //TODO getter
+
+ constructor(){
+ }
+
+ /**
+ * Defines the operation this message will have
+ * @param {String} operation Mus be defined in permittedOperations
+ * TODO: move to constructor?
+ */
+ set operation (operation){
+ if (!operation || typeof(operation) !== 'string'){
+ throw('ERR_WRONG_PARAM');
+ }
+ if (operation in permittedOperations){
+ if (!this._msg){
+ this._msg = {};
+ }
+ this._msg.op = operation;
+ } else {
+ throw('ERR_NOT_IMPLEMENTED');
+ }
+ }
+
+ /**
+ * 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'){
+ throw('ERR_WRONG_PARAM');
+ }
+ if (!this._msg || !this._msg.op){
+ console.log('There is no operation specified yet. '+
+ 'The parameter cannot be set');
+ return false;
+ }
+ let po = permittedOperations[this._msg.op];
+ if (!po){
+ throw('LAZY_PROGRAMMER');
+ //TODO
+ return false;
+ }
+ if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
+ this._msg[param] = value;
+ return true;
+ }
+ console.log('' + param + ' is invalid and could not be set');
+ return false;
+ }
+
+ /**
+ * Check if the message has the minimum requirements to be sent, according
+ * to the definitions in permittedOperations
+ * @returns {Boolean}
+ */
+ get isComplete(){
+ if (!this._msg.op){
+ return false;
+ }
+ let reqParams = permittedOperations[this._msg.op].required;
+ for (let i=0; i < reqParams.length; i++){
+ if (!reqParams[i] in this._msg){
+ 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;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index dedbf809..8323ac3b 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -1,187 +1,131 @@
-import {Connection} from "./Connection"
-
-export function encrypt(data, publicKeys, privateKeys, passwords=null,
- sessionKey, filename, compression, armor=true, detached=false,
- signature=null, returnSessionKey=false, wildcard=false, date=new Date()){
- // gpgme_op_encrypt ( <-gpgme doc on this operation
- // gpgme_ctx_t ctx,
- // gpgme_key_t recp[],
- // gpgme_encrypt_flags_t flags,
- // gpgme_data_t plain,
- // gpgme_data_t cipher)
- // flags:
- // GPGME_ENCRYPT_ALWAYS_TRUST
- // GPGME_ENCRYPT_NO_ENCRYPT_TO
- // GPGME_ENCRYPT_NO_COMPRESS
- // GPGME_ENCRYPT_PREPARE
- // GPGME_ENCRYPT_EXPECT_SIGN
- // GPGME_ENCRYPT_SYMMETRIC
- // GPGME_ENCRYPT_THROW_KEYIDS
- // GPGME_ENCRYPT_WRAP
- if (passwords !== null){
- throw('Password!'); // TBD
- }
-
- let pubkeys = toKeyIdArray(publicKeys);
- let privkeys = toKeyIdArray(privateKeys);
-
- // TODO filename: data is supposed to be empty, file is provided
- // TODO config compression detached signature
- // TODO signature to add to the encrypted message (?) || privateKeys: signature is desired
- // gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher)
-
- // TODO sign date overwriting implemented in gnupg?
-
- let conn = new Connection();
- if (wildcard){
- // Connection.set('throw-keyids', true); TODO Connection.set not yet existant
- }
- return conn.post('encrypt', {
- 'data': data,
- 'keys': publicKeys,
- 'armor': armor});
-};
+/* 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+
+ */
-export function decrypt(message, privateKeys, passwords, sessionKeys, publicKeys,
- format='utf8', signature=null, date=new Date()) {
- if (passwords !== null){
- throw('Password!'); // TBD
- }
- if (format === 'binary'){
- // Connection.set('base64', true);
- }
- if (publicKeys || signature){
- // Connection.set('signature', signature);
- // request verification, too
+import {Connection} from "./Connection"
+import {GPGME_Message} from './Message'
+import {toKeyIdArray} from "./Helpers"
+
+export class GpgME {
+ /**
+ * initial check if connection si successfull. Will throw ERR_NO_CONNECT or
+ * ERR_NO_CONNECT_RLE (if chrome.runtime.lastError is available) if the
+ * connection fails.
+ * TODO The connection to the nativeMessaging host will, for now, be closed
+ * after each interaction. Session management with gpg_agent is TBD.
+ * TODO: add configuration
+ */
+ constructor(){
+ let conn = new Connection();
+ // this.keyring = new Keyring(); TBD
+ // TODO config, e.g.
+ this.configuration = {
+ null_expire_is_never: true
+ };
+ conn.disconnect();
}
- //privateKeys optionally if keyId was thrown?
- // gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
- // response is gpgme_op_decrypt_result (gpgme_ctx_t ctx) (next available?)
- return conn.post('decrypt', {
- 'data': message
- });
-}
-// BIG TODO.
-export function generateKey({userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve="", date=new Date()}){
- throw('not implemented here');
- // gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t extrakey, unsigned int flags);
- return false;
-}
+ /**
+ * @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){
-export function sign({ data, privateKeys, armor=true, detached=false, date=new Date() }) {
- //TODO detached GPGME_SIG_MODE_DETACH | GPGME_SIG_MODE_NORMAL
- // gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode)
- // TODO date not supported
+ let msg = new GPGME_Message;
+ msg.operation = 'encrypt';
- let conn = new Connection();
- let privkeys = toKeyIdArray(privateKeys);
- return conn.post('sign', {
- 'data': data,
- 'keys': privkeys,
- 'armor': armor});
-};
+ // TODO temporary
+ msg.setParameter('armor', true);
+ msg.setParameter('always-trust', true);
-export function verify({ message, publicKeys, signature=null, date=new Date() }) {
- //TODO extra signature: sig, signed_text, plain: null
- // inline sig: signed_text:null, plain as writable (?)
- // date not supported
- //gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plain)
- let conn = new Connection();
- let privkeys = toKeyIdArray(privateKeys);
- return conn.post('sign', {
- 'data': data,
- 'keys': privkeys,
- 'armor': armor});
-}
+ let pubkeys = toKeyIdArray(publicKeys);
+ msg.setParameter('keys', pubkeys);
+ putData(msg, data);
+ if (wildcard === true){msg.setParameter('throw-keyids', true);
+ };
-export function reformatKey(privateKey, userIds=[], passphrase="", unlocked=false, keyExpirationTime=0){
- let privKey = toKeyIdArray(privateKey);
- if (privKey.length !== 1){
- return false; //TODO some error handling. There is not exactly ONE key we are editing
+ if (msg.isComplete === true) {
+ let conn = new Connection();
+ return (conn.post(msg.message));
+ }
+ else {
+ return Promise.reject('NO_CONNECT');
+ //TODO
+ }
}
- let conn = new Connection();
- // TODO key management needs to be changed somewhat
- return conn.post('TODO', {
- 'key': privKey[0],
- 'keyExpirationTime': keyExpirationTime, //TODO check if this is 0 or a positive and plausible number
- 'userIds': userIds //TODO check if empty or plausible strings
- });
- // unlocked will be ignored
-}
-export function decryptKey({ privateKey, passphrase }) {
- throw('not implemented here');
- return false;
-};
+ /**
+ * @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
+ */
-export function encryptKey({ privateKey, passphrase }) {
- throw('not implemented here');
- return false;
-};
+ decrypt(data){
-export function encryptSessionKey({data, algorithm, publicKeys, passwords, wildcard=false }) {
- //openpgpjs:
- // Encrypt a symmetric session key with public keys, passwords, or both at
- // once. At least either public keys or passwords must be specified.
- throw('not implemented here');
- return false;
-};
-
-export function decryptSessionKeys({ message, privateKeys, passwords }) {
- throw('not implemented here');
- return false;
-};
-
-// //TODO worker handling
-
-// //TODO key representation
-// //TODO: keyring handling
-
-
-/**
- * Helper functions and checks
- */
-
-/**
- * Checks if the submitted value is a keyID.
- * TODO: should accept all strings that are accepted as keyID by gnupg
- * TODO: See if Key becomes an object later on
- * @param {*} key input value. Is expected to be a string of 8,16 or 40 chars
- * representing hex values. Will return false if that expectation is not met
- */
-function isKeyId(key){
- if (!key || typeof(key) !== "string"){
- return false;
- }
- if ([8,16,40].indexOf(key.length) < 0){
- return false;
+ if (data === undefined){
+ throw('ERR_EMPTY_MSG');
+ }
+ let msg = new GPGME_Message;
+ msg.operation = 'decrypt';
+ putData(msg, data);
+ // TODO: needs proper EOL to be decrypted.
+
+ if (msg.isComplete === true){
+ let conn = new Connection();
+ return conn.post(msg.message);
+ }
+ else {
+ return Promise.reject('NO_CONNECT');
+ //TODO
+ }
}
- let regexp= /^[0-9a-fA-F]*$/i;
- return regexp.test(key);
-};
+}
/**
- * Tries to return an array of keyID values, either from a string or an array.
- * Filters out those that do not meet the criteria. (TODO: silently for now)
- * @param {*} array Input value.
+ * 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 toKeyIdArray(array){
- let result = [];
- if (!array){
- return result;
- }
- if (!Array.isArray(array)){
- if (isKeyId(array) === true){
- return [keyId];
- }
- return result;
+function putData(message, data){
+ if (!message || !message instanceof GPGME_Message ) {
+ throw('NO_MESSAGE_OBJECT');
}
- for (let i=0; i < array.length; i++){
- if (isKeyId(array[i]) === true){
- result.push(array[i]);
- }
+ if (!data){
+ //TODO Debug only! No data is legitimate
+ console.log('Warning. no data in message');
+ message.setParameter('data', '');
+ } else if (data instanceof Uint8Array){
+ let decoder = new TextDecoder('utf8');
+ message.setParameter('base64', true);
+ message.setParameter ('data', decoder.decode(data));
+ } else if (typeof(data) === 'string') {
+ message.setParameter('base64', false);
+ message.setParameter('data', data);
+ } else {
+ throw('ERR_WRONG_TYPE');
}
- return result;
-};
+}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
new file mode 100644
index 00000000..1eec4da4
--- /dev/null
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -0,0 +1,156 @@
+/* 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 {Keyring} from "./Keyring" TODO
+
+
+export class GpgME_openPGPCompatibility {
+
+ constructor(){
+ this._gpgme = new GpgME;
+ }
+
+ /**
+ * Encrypt Message
+ * Supported:
+ * @param {String|Uint8Array} data
+ * @param {Key|Array} publicKeys
+ * @param {Boolean} wildcard
+ * TODO:
+ * @param {Key|Array} privateKeys
+ * @param {String} filename
+ * @param {module:enums.compression} compression
+ * @param {Boolean} armor
+ * @param {Boolean} detached
+ * unsupported:
+ * @param {String|Array} passwords
+ * @param {Object} sessionKey
+ * @param {Signature} signature
+ * @param {Boolean} returnSessionKey
+ *
+ * @returns {Promise}
+ * {data: ASCII armored message,
+ * signature: detached signature if 'detached' is true
+ * }
+ * @async
+ * @static
+ */
+ encrypt({data = '', publicKeys = '', privateKeys, passwords, sessionKey,
+ filename, compression, armor=true, detached=false, signature=null,
+ returnSessionKey=null, wildcard=false, date=null}) {
+ if (passwords !== undefined
+ || sessionKey !== undefined
+ || signature !== null
+ || returnSessionKey !== null
+ || date !== null){
+ throw('NOT_IMPLEMENTED');
+ }
+ if ( privateKeys
+ || filename
+ || compression
+ || armor === false
+ || detached == true){
+ console.log('may be implemented later');
+ throw('NOT_YET_IMPLEMENTED');
+ }
+ return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
+ }
+
+ /** Decrypt Message
+ * supported
+ * TODO: @param {Message} message TODO: for now it accepts an armored string only
+ * Unsupported:
+ * @param {String|Array} passwords
+ * @param {Object|Array} sessionKeys
+ * @param {Date} date
+
+ * TODO
+ * @param {Key|Array} privateKey
+ * @param {Key|Array} publicKeys
+ * @param {String} format (optional) return data format either as 'utf8' or 'binary'
+ * @param {Signature} signature (optional) detached signature for verification
+
+ * @returns {Promise} decrypted and verified message in the form:
+ * { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] }
+ * @async
+ * @static
+ */
+ decrypt({ message, privateKeys, passwords, sessionKeys, publicKeys, format='utf8', signature=null, date}) {
+ if (passwords !== undefined
+ || sessionKeys
+ || date){
+
+ throw('NOT_IMPLEMENTED');
+ }
+ if ( privateKeys
+ || publicKeys
+ || format !== 'utf8'
+ || signature
+ ){
+ console.log('may be implemented later');
+ throw('NOT_YET_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.
+ }
+}
+
+/**
+ *
+ * @param {Object | String} Key Either a (presumably openpgp Key) Object with a
+ * primaryKeyproperty and a method getFingerprint, or a string.
+ * @returns {String} Unchecked string value claiming to be a fingerprint
+ * TODO: gpgmejs checks again, so it's okay here.
+ */
+function translateKeyInput(Key){
+ if (!Key){
+ return [];
+ }
+ if (!Array.isArray(Key)){
+ Key = [Key];
+ }
+ let resultslist = [];
+ for (let i=0; i < Key.length; i++){
+ if (typeof(Key[i]) === 'string'){
+ resultslist.push(Key);
+ } else if (
+ Key[i].hasOwnProperty(primaryKey) &&
+ Key[i].primaryKey.hasOwnProperty(getFingerprint)){
+ resultslist.push(Key[i].primaryKey.getFingerprint());
+ }
+ }
+ return resultslist;
+}
\ No newline at end of file
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 02dc919d..f70bd2d8 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -1,14 +1,23 @@
-import * as gpgmejs from'./gpgmejs'
-export default gpgmejs;
-
-/**
- * Export each high level api function separately.
- * Usage:
+/* 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.
*
- * import { encryptMessage } from 'gpgme.js'
- * encryptMessage(keys, text)
+ * 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+
*/
-export {
- encrypt, decrypt, sign, verify,
- generateKey, reformatKey
- } from './gpgmejs';
+
+import { GpgME as gpgmejs } from "./gpgmejs";
+// import { GpgME_openPGPCompatibility as gpgmejs } from "./gpgmejs_openpgpjs";
+export default gpgmejs;
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
new file mode 100644
index 00000000..3c11b8e0
--- /dev/null
+++ b/lang/js/src/permittedOperations.js
@@ -0,0 +1,75 @@
+/* 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
+ answer:
+ type: The payload property of the answer. May be
+ partial and in need of concatenation
+ params: Array Information that do not change throughout
+ the message
+ infos: Array arbitrary information that may change
+ }
+ }
+ */
+
+export const permittedOperations = {
+ encrypt: {
+ required: ['keys', 'data'],
+ optional: [
+ 'protocol',
+ 'chunksize',
+ 'base64',
+ 'mime',
+ 'armor',
+ 'always-trust',
+ 'no-encrypt-to',
+ 'no-compress',
+ 'throw-keyids',
+ 'want-address',
+ 'wrap'
+ ],
+ answer: {
+ type: ['ciphertext'],
+ data: ['data'],
+ params: ['base64'],
+ infos: []
+ }
+ },
+
+ decrypt: {
+ required: ['data'],
+ optional: [
+ 'protocol',
+ 'chunksize',
+ 'base64'
+ ],
+ answer: {
+ type: ['plaintext'],
+ data: ['data'],
+ params: ['base64', 'mime'],
+ infos: ['info']
+ }
+ }
+}
diff --git a/lang/js/test_index.js b/lang/js/test_index.js
new file mode 100644
index 00000000..9119d271
--- /dev/null
+++ b/lang/js/test_index.js
@@ -0,0 +1,25 @@
+/* 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+
+ *
+ */
+document.addEventListener('DOMContentLoaded', function() {
+ chrome.tabs.create({
+ url: './ui2.html'
+ });
+});
diff --git a/lang/js/testapplication.js b/lang/js/testapplication.js
index d01aca99..97b35527 100644
--- a/lang/js/testapplication.js
+++ b/lang/js/testapplication.js
@@ -1,21 +1,57 @@
-/**
-* Testing nativeMessaging. This is a temporary plugin using the gpgmejs
- implemetation as contained in src/
-*/
-function buttonclicked(event){
- let data = document.getElementById("text0").value;
- let keyId = document.getElementById("key").value;
- let enc = Gpgmejs.encrypt(data, [keyId]).then(function(answer){
- console.log(answer);
- console.log(answer.type);
- console.log(answer.data);
- alert(answer.data);
- }, function(errormsg){
- alert('Error: '+ errormsg);
+/* 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+
+ *
+ */
+
+function encryptbuttonclicked(event){
+ let data = document.getElementById('cleartext').value;
+ let keyId = document.getElementById('pubkey').value;
+ let communication = new Gpgmejs;
+ let enc = communication.encrypt(data, keyId).then(
+ function(answer){
+ console.log(answer);
+ if (answer.data){
+ console.log(answer.data);
+ document.getElementById('answer').value = answer.data;
+ }
+ }, function(errormsg){
+ alert('Error: '+ errormsg);
+ });
+};
+
+function decryptbuttonclicked(event){
+ let data = document.getElementById("ciphertext").value;
+ let communication = new Gpgmejs;
+ let enc = communication.decrypt(data).then(
+ function(answer){
+ console.log(answer);
+ if (answer.data){
+ document.getElementById('answer').value = answer.data;
+ }
+ }, function(errormsg){
+ alert('Error: '+ errormsg);
});
};
document.addEventListener('DOMContentLoaded', function() {
- document.getElementById("button0").addEventListener("click",
- buttonclicked);
- });
+ document.getElementById("buttonencrypt").addEventListener("click",
+ encryptbuttonclicked);
+ document.getElementById("buttondecrypt").addEventListener("click",
+ decryptbuttonclicked);
+});
diff --git a/lang/js/testapplication_index.html b/lang/js/testapplication_index.html
new file mode 100644
index 00000000..866b1135
--- /dev/null
+++ b/lang/js/testapplication_index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lang/js/ui.html b/lang/js/ui.html
deleted file mode 100644
index 9c56c2e5..00000000
--- a/lang/js/ui.html
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Encrypt
-
-
-
diff --git a/lang/js/ui2.html b/lang/js/ui2.html
new file mode 100644
index 00000000..8d0abd97
--- /dev/null
+++ b/lang/js/ui2.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+ Encrypt
+
+
+
+ Encrypted armored Text:
+
+
+
+ Decrypt
+
+ Result data:
+
+
+
diff --git a/lang/js/webpack.conf.js b/lang/js/webpack.conf.js
index 71b71161..7a5392ee 100644
--- a/lang/js/webpack.conf.js
+++ b/lang/js/webpack.conf.js
@@ -1,3 +1,24 @@
+/* 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 the configuration file for building the gpgmejs-Library with webpack
+ */
const path = require('path');
module.exports = {
@@ -8,6 +29,7 @@ module.exports = {
path: path.resolve(__dirname, 'dist'),
filename: 'gpgmejs.bundle.js',
libraryTarget: 'var',
+ libraryExport: 'default',
library: 'Gpgmejs'
}
};
--
cgit v1.2.3
From d62f66b1fb47f2075770d896f672748a4136e70b Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Mon, 23 Apr 2018 17:18:46 +0200
Subject: js: Key handling stubs, Error handling, refactoring
--
* Error handling: introduced GPGMEJS_Error class that handles errors
at a more centralized and consistent position
* src/Connection.js:
The nativeMessaging port now opens per session instead of per
message. Some methods were added that reflect this change
- added methods disconnect() and reconnect()
- added connection status query
* src/gpgmejs.js
- stub for key deletion
- error handling
- high level API for changing connection status
* src/gpgmejs_openpgpjs.js
- added stubs for Key/Keyring handling according to current
state of discussion. It is still subject to change
* src/Helpers.js
- toKeyIdArray creates an array of KeyIds, now accepting
fingerprints, GPGMEJS_Key objects and openpgp Key objects.
* Key objects (src/Key.js) Querying information about a key
directly from gnupg. Currently a stub, only the Key.fingerprint is
functional.
* Keyring queries (src/Keyring.js): Listing and searching keys.
Currently a stub.
---
lang/js/src/Connection.js | 86 ++++++++++-------
lang/js/src/Errors.js | 148 ++++++++++++++++++++++++++++
lang/js/src/Helpers.js | 54 +++++++----
lang/js/src/Key.js | 201 +++++++++++++++++++++++++++++++++++++++
lang/js/src/Keyring.js | 151 +++++++++++++++++++++++++++++
lang/js/src/Message.js | 34 ++++---
lang/js/src/gpgmejs.js | 108 +++++++++++++++------
lang/js/src/gpgmejs_openpgpjs.js | 105 ++++++++++++++------
8 files changed, 756 insertions(+), 131 deletions(-)
create mode 100644 lang/js/src/Errors.js
create mode 100644 lang/js/src/Key.js
create mode 100644 lang/js/src/Keyring.js
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 784929e9..87ec8cf7 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -26,31 +26,19 @@ import { GPGME_Message } from "./Message";
* expected.
*/
import { permittedOperations } from './permittedOperations'
+import { GPGMEJS_Error} from "./Errors"
+/**
+ * A Connection handles the nativeMessaging interaction.
+ */
export class Connection{
- /**
- * Opens and closes a port. Thus, it is made sure that the connection can
- * be used.
- * THIS BEHAVIOUR MAY CHANGE!
- * discussion is to keep a port alive as long as the context stays the same
- *
- * TODO returns nothing, but triggers exceptions if not successfull
- */
constructor(){
- this._connection = chrome.runtime.connectNative('gpgmejson');
- if (!this._connection){
- if (chrome.runtime.lastError){
- throw('NO_CONNECT_RLE');
- } else {
- throw('NO_CONNECT');
- }
- }
- this._flags = {}; // TODO general config
+ this.connect();
}
/**
- * Immediately closes the open port
+ * Immediately closes the open port.
*/
disconnect() {
if (this._connection){
@@ -58,27 +46,56 @@ export class Connection{
}
}
+ /**
+ * Opens a nativeMessaging port.
+ * returns nothing, but triggers errors if not successfull:
+ * NO_CONNECT: connection not successfull, chrome.runtime.lastError may be
+ * available
+ * ALREADY_CONNECTED: There is already a connection present.
+ */
+ connect(){
+ if (this._connection){
+ return new GPGMEJS_Error('ALREADY_CONNECTED');
+ }
+ this._connection = chrome.runtime.connectNative('gpgmejson');
+ if (!this._connection){
+ return new GPGMEJS_Error('NO_CONNECT');
+ }
+ }
+
+ /**
+ * checks if the connection is established
+ * TODO: some kind of ping to see if the other side responds as expected?
+ * @returns {Boolean}
+ */
+ get connected(){
+ return this._connection ? true: false;
+ }
+
/**
* Sends a message and resolves with the answer.
* @param {GPGME_Message} message
* @returns {Promise} the gnupg answer, or rejection with error
- * information
- * TODO: better/more consistent error information
+ * information.
*/
post(message){
if (!message || !message instanceof GPGME_Message){
- return Promise.reject('ERR_NO_MSG');
+ return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ }
+ if (message.isComplete !== true){
+ return Promise.reject(new GPGMEJS_Error('MSG_INCOMPLETE'));
}
// let timeout = 5000; //TODO config
let me = this;
return new Promise(function(resolve, reject){
- let answer = new Answer(message.op);
+ let answer = new Answer(message.operation);
let listener = function(msg) {
if (!msg){
me._connection.onMessage.removeListener(listener)
- reject('EMPTY_ANSWER');
+ reject(new GPGMEJS_Error('EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener)
+ //TODO: GPGMEJS_Error?
reject(msg.msg);
} else {
answer.add(msg);
@@ -92,12 +109,12 @@ export class Connection{
};
me._connection.onMessage.addListener(listener);
- me._connection.postMessage(message);
+ me._connection.postMessage(message.message);
//TBD: needs to be aware if there is a pinentry pending
// setTimeout(
// function(){
// me.disconnect();
- // reject('TIMEOUT');
+ // reject(new GPGMEJS_Error('TIMEOUT', 5000));
// }, timeout);
});
}
@@ -105,8 +122,8 @@ export class Connection{
/**
* A class for answer objects, checking and processing the return messages of
- * the nativeMessaging communication
- * @param {String} operation The operation, to look up validity of return keys
+ * the nativeMessaging communication.
+ * @param {String} operation The operation, to look up validity of returning messages
*/
class Answer{
@@ -115,9 +132,8 @@ class Answer{
}
/**
- *
+ * Add the information to the answer
* @param {Object} msg The message as received with nativeMessaging
- * TODO: "error" and "more" handling are not in here, but in post()
*/
add(msg){
if (this._response === undefined){
@@ -130,9 +146,7 @@ class Answer{
switch (key) {
case 'type':
if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
- console.log( 'unexpected answer type: ' + msg.type);
- throw('UNEXPECTED_TYPE');
-
+ return new GPGMEJS_Error('UNEXPECTED_ANSWER');
}
break;
case 'more':
@@ -151,7 +165,7 @@ class Answer{
this._response[key] = msg[key];
}
else if (this._response[key] !== msg[key]){
- throw('UNEXPECTED_TYPE');
+ return new GPGMEJS_Error('UNEXPECTED_ANSWER',msg[key]);
}
}
//infos may be json objects etc. Not yet defined.
@@ -163,8 +177,7 @@ class Answer{
this._response.push(msg[key]);
}
else {
- console.log('unexpected answer parameter: ' + key);
- throw('UNEXPECTED_PARAM');
+ return new GPGMEJS_Error('UNEXPECTED_ANSWER', key);
}
break;
}
@@ -172,7 +185,8 @@ class Answer{
}
/**
- * Returns the assembled message. TODO: does not care yet if completed.
+ * @returns {Object} the assembled message.
+ * TODO: does not care yet if completed.
*/
get message(){
return this._response;
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
new file mode 100644
index 00000000..c2356f7c
--- /dev/null
+++ b/lang/js/src/Errors.js
@@ -0,0 +1,148 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see .
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+// This is a preliminary collection of erors and warnings to be thrown and implemented.
+
+// general idea: if throw , throw the NAME
+// return false || 'return' property
+
+//TODO: Connection.NOCONNECT promise
+//connection.timeout: Be aware of pinentry
+
+export class GPGMEJS_Error {
+
+ constructor(code = 'GENERIC_ERROR', details){
+ let config = { //TODO TEMP
+ debug: 'console', // |'alert'
+ throw: 'default' // | 'always' | 'never'
+ };
+ let errors = { //TODO: someplace else
+ //Connection errors
+ 'ALREADY_CONNECTED':{
+ msg: 'The connection was already established. The action would overwrite the context',
+ throw: true
+ },
+ 'NO_CONNECT': {
+ msg:'Connection with the nativeMessaging host could not be established.',
+ throw: true
+ },
+ 'EMPTY_GPG_ANSWER':{
+ msg: 'The nativeMesaging answer was empty',
+ throw: true
+ },
+ 'TIMEOUT': {
+ msg: 'A timeout was exceeded.',
+ throw: false
+ },
+
+ 'UNEXPECTED_ANSWER': {
+ msg: 'The answer from gnupg was not as expected',
+ throw: true
+ },
+
+ // Message/Data Errors
+
+ 'NO_KEYS' : {
+ msg: 'There were no valid keys provided.',
+ throw: true
+ },
+ 'NOT_A_FPR': {
+ msg: 'The String is not an accepted fingerprint',
+ throw: false
+ },
+ 'MSG_INCOMPLETE': {
+ msg: 'The Message did not match the minimum requirements for the interaction',
+ throw: true
+ },
+ 'EMPTY_MSG' : {
+ msg: 'The Message has no data.',
+ throw: true
+ },
+ 'MSG_NODATA':{
+ msg: 'The data sent is empty. This may be unintentional.',
+ throw: false
+ },
+ 'MSG_OP_PENDING': {
+ msg: 'There is no operation specified yet. The parameter cannot be set',
+ throw: false
+ },
+ 'WRONG_OP': {
+ msg: "The operation requested could not be found",
+ throw: true
+ },
+
+ //generic errors
+
+ 'WRONGPARAM':{
+ msg: 'invalid parameter was found',
+ throw: true
+ },
+ 'WRONGTYPE':{
+ msg: 'invalid parameter type was found',
+ throw: true
+ },
+ 'NOT_IMPLEMENTED': {
+ msg: 'A openpgpjs parameter was submitted that is not implemented',
+ throw: true
+ },
+ 'GENERIC_ERROR': {
+ msg: 'Unspecified error',
+ throw: true
+ },
+
+ // hopefully temporary errors
+
+ 'NOT_YET_IMPLEMENTED': {
+ msg: 'Support of this is probable, but it is not implemented yet',
+ throw: false
+ }
+ }
+ if (!errors.hasOwnProperty(code)){
+ throw('GENERIC_ERROR');
+ }
+ let msg = code;
+ if (errors[code].msg !== undefined){
+ msg = msg + ': ' + errors[code].msg;
+ }
+ if (details){
+ msg = msg + ' ' + details;
+ }
+ if (config.debug === 'console'){
+ console.log(msg);
+ } else if (config.debug === 'alert'){
+ alert(msg);
+ }
+ switch (config.throw) {
+ case 'default':
+ if (errors[code].throw === true){
+ throw(code);
+ }
+ break;
+ case 'always':
+ throw(code);
+ break;
+
+ case 'never':
+ break;
+ default:
+ throw('GENERIC_ERROR');
+ }
+ }
+}
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index eeb7a3c4..922ca06c 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -1,3 +1,5 @@
+import { GPGMEJS_Error } from "./Errors";
+
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
@@ -21,33 +23,46 @@
/**
* Tries to return an array of fingerprints, either from input fingerprints or
* from Key objects
- * @param {String|Array} input Input value.
+ * @param {Key |Array| GPGME_Key | Array|String|Array} input
+ * @param {Boolean} nocheck if set, an empty result is acceptable
* @returns {Array} Array of fingerprints.
*/
-export function toKeyIdArray(input){
+
+export function toKeyIdArray(input, nocheck){
if (!input){
- return [];
- // TODO: Warning or error here? Did we expect something or is "nothing" okay?
+ return (nocheck ===true)? [] : new GPGMEJS_Error('NO_KEYS');
+ }
+ if (!Array.isArray(input)){
+ input = [input];
}
- if (input instanceof Array){
- let result = [];
- for (let i=0; i < input.length; i++){
+ let result = [];
+ for (let i=0; i < input.length; i++){
+ if (typeof(input[i]) === 'string'){
if (isFingerprint(input[i]) === true){
result.push(input[i]);
} else {
- //TODO error?
- console.log('gpgmejs/Helpers.js Warning: '+
- input[i] +
- ' is not a valid key fingerprint and will not be used');
+ GPGMEJS_Error
+ }
+ } else if (typeof(input[i]) === 'object'){
+ let fpr = '';
+ if (input[i] instanceof GPGME_Key){
+ fpr = input[i].fingerprint;
+ } else if (input[i].hasOwnProperty(primaryKey) &&
+ input[i].primaryKey.hasOwnProperty(getFingerprint)){
+ fpr = input[i].primaryKey.getFingerprint();
+ }
+ if (isFingerprint(fpr) === true){
+ result.push(fpr);
}
+ } else {
+ return new GPGMEJS_Error('WRONGTYPE');
}
+ }
+ if (result.length === 0){
+ return (nocheck===true)? [] : new GPGMEJS_Error('NO_KEYS');
+ } else {
return result;
- } else if (isFingerprint(input) === true) {
- return [input];
}
- console.log('gpgmejs/Helpers.js Warning: ' + input +
- ' is not a valid key fingerprint and will not be used');
- return [];
};
/**
@@ -72,13 +87,14 @@ function hextest(key, len){
export function isFingerprint(string){
return hextest(string, 40);
};
-
-//TODO needed anywhere?
+/**
+ * check if the input is a valid Hex string with a length of 16
+ */
function isLongId(string){
return hextest(string, 16);
};
-//TODO needed anywhere?
+// TODO still not needed anywhere
function isShortId(string){
return hextest(string, 8);
};
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
new file mode 100644
index 00000000..d8f16c55
--- /dev/null
+++ b/lang/js/src/Key.js
@@ -0,0 +1,201 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see .
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+/**
+ * The key class allows to query the information defined in gpgme Key Objects
+ * (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)
+ *
+ * This is a stub, as the gpgme-json side is not yet implemented
+ *
+ */
+
+import {isFingerprint} from './Helpers'
+import {GPGMEJS_Error} from './Errors'
+
+export class GPGME_Key {
+
+ constructor(fingerprint){
+ if (isFingerprint(fingerprint) === true){
+ this._fingerprint = fingerprint;
+ } else {
+ return new GPGMEJS_Error('WRONGPARAM', 'Key.js: invalid fingerprint');
+ }
+ }
+
+ get fingerprint(){
+ return this._fingerprint;
+ }
+
+ /**
+ * hasSecret returns true if a secret subkey is included in this Key
+ */
+ get hasSecret(){
+ checkKey(this._fingerprint, 'secret').then( function(result){
+ return Promise.resolve(result);
+ });
+
+ }
+
+ get isRevoked(){
+ return checkKey(this._fingerprint, 'revoked');
+ }
+
+ get isExpired(){
+ return checkKey(this._fingerprint, 'expired');
+ }
+
+ get isDisabled(){
+ return checkKey(this._fingerprint, 'disabled');
+ }
+
+ get isInvalid(){
+ return checkKey(this._fingerprint, 'invalid');
+ }
+
+ get canEncrypt(){
+ return checkKey(this._fingerprint, 'can_encrypt');
+ }
+
+ get canSign(){
+ return checkKey(this._fingerprint, 'can_sign');
+ }
+
+ get canCertify(){
+ return checkKey(this._fingerprint, 'can_certify');
+ }
+
+ get canAuthenticate(){
+ return checkKey(this._fingerprint, 'can_authenticate');
+ }
+
+ get isQualified(){
+ return checkKey(this._fingerprint, 'is_qualified');
+ }
+
+ get armored(){
+ let me = this;
+ return new Promise(function(resolve, reject){
+ let conn = new Connection();
+ conn.setFlag('armor', true);
+ conn.post('export',{'fpr': me._fingerprint});
+ });
+ // TODO return value not yet checked. Should result in an armored block
+ // in correct encoding
+ // TODO openpgpjs also returns secKey if private = true?
+ }
+
+ /**
+ * TODO returns true if this is the default key used to sign
+ */
+ get isDefault(){
+ throw('NOT_YET_IMPLEMENTED');
+ }
+
+ /**
+ * get the Key's subkeys as GPGME_Key objects
+ * @returns {Array}
+ */
+ get subkeys(){
+ return checkKey(this._fingerprint, 'subkeys').then(function(result){
+ // TBD expecting a list of fingerprints
+ if (!Array.isArray(result)){
+ result = [result];
+ }
+ let resultset = [];
+ for (let i=0; i < result.length; i++){
+ let subkey = new GPGME_Key(result[i]);
+ if (subkey instanceof GPGME_Key){
+ resultset.push(subkey);
+ }
+ }
+ return Promise.resolve(resultset);
+ });
+ }
+
+ /**
+ * creation time stamp of the key
+ * @returns {Date|null} TBD
+ */
+ get timestamp(){
+ return checkKey(this._fingerprint, 'timestamp');
+ //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available.
+ }
+
+ /**
+ * The expiration timestamp of this key TBD
+ * @returns {Date|null} TBD
+ */
+ get expires(){
+ return checkKey(this._fingerprint, 'expires');
+ // TODO convert to Date; check for 0
+ }
+
+ /**
+ * getter name TBD
+ * @returns {String|Array} The user ids associated with this key
+ */
+ get userIds(){
+ return checkKey(this._fingerprint, 'uids');
+ }
+
+ /**
+ * @returns {String} The public key algorithm supported by this subkey
+ */
+ get pubkey_algo(){
+ return checkKey(this._fingerprint, 'pubkey_algo');
+ }
+};
+
+/**
+ * generic function to query gnupg information on a key.
+ * @param {*} fingerprint The identifier of the Keyring
+ * @param {*} property The gpgme-json property to check
+ *
+ */
+function checkKey(fingerprint, property){
+ return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+
+ return new Promise(function(resolve, reject){
+ if (!isFingerprint(fingerprint)){
+ reject('not a fingerprint'); //TBD
+ }
+ let conn = new Connection();
+ conn.post('getkey',{ // TODO not yet implemented in gpgme
+ 'fingerprint': this.fingerprint})
+ .then(function(result){
+ if (property !== undefined){
+ if (result.hasOwnProperty(key)){
+ resolve(result[property]);
+ }
+ else if (property == 'secret'){
+ // property undefined means "not true" in case of secret
+ resolve(false);
+ } else {
+ reject('ERR_INVALID_PROPERTY') //TBD
+ }
+ }
+
+
+ resolve(result);
+ }, function(error){
+ reject(error);
+ });
+ });
+};
\ No newline at end of file
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
new file mode 100644
index 00000000..52fa7f71
--- /dev/null
+++ b/lang/js/src/Keyring.js
@@ -0,0 +1,151 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see .
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+import {GPGME_Message} from './Message'
+import {Connection} from './Connection'
+import {GPGME_Key} from './Key'
+import { isFingerprint, isLongId } from './Helpers';
+
+export class GPGME_Keyring {
+ constructor(){
+ this.reconnect();
+ }
+
+ /**
+ * (Re)-establishes the connection
+ * TODO TEMP: should we better use the connection of our parent,
+ * which we do not control?
+ */
+ reconnect(){
+ if (!this._connection || ! this._connection instanceof Connection){
+ this._connection = new Connection;
+ } else {
+ this._connection.disconnect();
+ this._connection.connect();
+ }
+ }
+
+ /**
+ * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
+ * @param {Boolean} (optional) Include listing of secret keys
+ * @returns {Promise.>}
+ *
+ */
+ getKeys(pattern, include_secret){
+ let msg = new GPGME_Message;
+ msg.operation = 'listkeys';
+ if (pattern && typeof(pattern) === 'string'){
+ msg.setParameter('pattern', pattern);
+ }
+ if (include_secret){
+ msg.setParameter('with-secret', true);
+ }
+
+ this._connection.post(msg).then(function(result){
+ let fpr_list = [];
+ let resultset = [];
+ if (!Array.isArray(result.keys)){
+ //TODO check assumption keys = Array
+ fpr_list = [result.keys];
+ } else {
+ fpr_list = result.keys;
+ }
+ for (let i=0; i < fpr_list.length; i++){
+ let newKey = new GPGME_Key(fpr_list[i]);
+ if (newKey instanceof GPGME_Key){
+ resultset.push(newKey);
+ }
+ }
+ return Promise.resolve(resultset);
+ });
+ }
+
+ /**
+ * @param {Object} flags subset filter expecting at least one of the
+ * filters described below. True will filter on the condition, False will
+ * reverse the filter, if not present or undefined, the filter will not be
+ * considered. Please note that some combination may not make sense
+ * @param {Boolean} flags.defaultKey Only Keys marked as Default Keys
+ * @param {Boolean} flags.secret Only Keys containing a secret part.
+ * @param {Boolean} flags.valid Valid Keys only
+ * @param {Boolean} flags.revoked revoked Keys only
+ * @param {Boolean} flags.expired Expired Keys only
+ * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
+ * @returns {Promise Array}
+ *
+ */
+ getSubset(flags, pattern){
+ if (flags === undefined) {
+ throw('ERR_WRONG_PARAM');
+ };
+ let secretflag = false;
+ if (flags.hasOwnProperty(secret) && flags.secret){
+ secretflag = true;
+ }
+ this.getKeys(pattern, secretflag).then(function(queryset){
+ let resultset = [];
+ for (let i=0; i < queryset.length; i++ ){
+ let conditions = [];
+ let anticonditions = [];
+ if (secretflag === true){
+ conditions.push('hasSecret');
+ } else if (secretflag === false){
+ anticonditions.push('hasSecret');
+ }
+ if (flags.defaultKey === true){
+ conditions.push('isDefault');
+ } else if (flags.defaultKey === false){
+ anticonditions.push('isDefault');
+ }
+ if (flags.valid === true){
+ anticonditions.push('isInvalid');
+ } else if (flags.valid === false){
+ conditions.push('isInvalid');
+ }
+ if (flags.revoked === true){
+ conditions.push('isRevoked');
+ } else if (flags.revoked === false){
+ anticonditions.push('isRevoked');
+ }
+ if (flags.expired === true){
+ conditions.push('isExpired');
+ } else if (flags.expired === false){
+ anticonditions.push('isExpired');
+ }
+ let decision = undefined;
+ for (let con = 0; con < conditions.length; con ++){
+ if (queryset[i][conditions[con]] !== true){
+ decision = false;
+ }
+ }
+ for (let acon = 0; acon < anticonditions.length; acon ++){
+ if (queryset[i][anticonditions[acon]] === true){
+ decision = false;
+ }
+ }
+ if (decision !== false){
+ resultset.push(queryset[i]);
+ }
+ }
+ return Promise.resolve(resultset);
+ });
+ }
+
+};
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 90b554a1..6a93b6f4 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -18,21 +18,24 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
import { permittedOperations } from './permittedOperations'
-
+import { GPGMEJS_Error } from './Errors'
export class GPGME_Message {
//TODO getter
- constructor(){
+ constructor(operation){
+ if (operation){
+ this.operation(operation);
+ }
}
/**
* Defines the operation this message will have
- * @param {String} operation Mus be defined in permittedOperations
+ * @param {String} operation Must be defined in permittedOperations
* TODO: move to constructor?
*/
set operation (operation){
if (!operation || typeof(operation) !== 'string'){
- throw('ERR_WRONG_PARAM');
+ return new GPGMEJS_Error('WRONGPARAM');
}
if (operation in permittedOperations){
if (!this._msg){
@@ -40,10 +43,14 @@ export class GPGME_Message {
}
this._msg.op = operation;
} else {
- throw('ERR_NOT_IMPLEMENTED');
+ return new GPGMEJS_Error('WRONG_OP');
}
}
+ get operation(){
+ return this._msg.op;
+ }
+
/**
* Sets a parameter for the message. Note that the operation has to be set
* first, to be able to check if the parameter is permittted
@@ -53,25 +60,20 @@ export class GPGME_Message {
*/
setParameter(param,value){
if (!param || typeof(param) !== 'string'){
- throw('ERR_WRONG_PARAM');
+ return new GPGMEJS_Error('WRONGPARAM', 'type check failed');
}
if (!this._msg || !this._msg.op){
- console.log('There is no operation specified yet. '+
- 'The parameter cannot be set');
- return false;
+ return new GPGMEJS_Error('MSG_OP_PENDING');
}
let po = permittedOperations[this._msg.op];
if (!po){
- throw('LAZY_PROGRAMMER');
- //TODO
- return false;
+ return new GPGMEJS_Error('WRONG_OP', param);
}
if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
this._msg[param] = value;
return true;
}
- console.log('' + param + ' is invalid and could not be set');
- return false;
+ return new GPGMEJS_Error('WRONGPARAM', param);
}
/**
@@ -85,7 +87,9 @@ export class GPGME_Message {
}
let reqParams = permittedOperations[this._msg.op].required;
for (let i=0; i < reqParams.length; i++){
- if (!reqParams[i] in this._msg){
+
+ if (!this._msg.hasOwnProperty(reqParams[i])){
+ console.log(reqParams[i] + 'missing');
return false;
}
}
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index 8323ac3b..c23a356b 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -21,26 +21,54 @@
import {Connection} from "./Connection"
import {GPGME_Message} from './Message'
import {toKeyIdArray} from "./Helpers"
+import {GPGMEJS_Error as Error, GPGMEJS_Error} from "./Errors"
export class GpgME {
/**
- * initial check if connection si successfull. Will throw ERR_NO_CONNECT or
- * ERR_NO_CONNECT_RLE (if chrome.runtime.lastError is available) if the
- * connection fails.
- * TODO The connection to the nativeMessaging host will, for now, be closed
- * after each interaction. Session management with gpg_agent is TBD.
+ * initializes GpgME by opening a nativeMessaging port
* TODO: add configuration
*/
- constructor(){
- let conn = new Connection();
- // this.keyring = new Keyring(); TBD
- // TODO config, e.g.
- this.configuration = {
- null_expire_is_never: true
- };
- conn.disconnect();
+ constructor(configuration = {
+ null_expire_is_never: false
+ }){
+ this._connection = new Connection;
+ }
+
+ /**
+ * refreshes the nativeApp connection
+ */
+ reconnect(){
+ if (!this._connection || ! this._connection instanceof Connection){
+ this._connection = new Connection;
+ } else {
+ this._connection.disconnect();
+ this._connection.connect();
+ }
+ }
+
+ /**
+ * inmediately tries to destroy the nativeMessaging connection.
+ * TODO: may not be included in final API, as it is redundant.
+ * For now, it just serves paranoia
+ */
+ disconnect(){
+ if (this._connection){
+ this._connection.disconnect();
+ this._connection = null;
+ }
+ }
+
+ /**
+ * tests the nativeApp connection
+ */
+ get connected(){
+ if (!this._connection || ! this._connection instanceof Connection){
+ return false;
+ }
+ return this._connection.connected;
}
+
/**
* @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array
* @param {GPGME_Key|String|Array|Array} publicKeys Keys used to encrypt the message
@@ -62,14 +90,7 @@ export class GpgME {
if (wildcard === true){msg.setParameter('throw-keyids', true);
};
- if (msg.isComplete === true) {
- let conn = new Connection();
- return (conn.post(msg.message));
- }
- else {
- return Promise.reject('NO_CONNECT');
- //TODO
- }
+ return (this._connection.post(msg));
}
/**
@@ -85,22 +106,47 @@ export class GpgME {
decrypt(data){
if (data === undefined){
- throw('ERR_EMPTY_MSG');
+ return Promise.reject(new GPGMEJS_Error ('EMPTY_MSG'));
}
let msg = new GPGME_Message;
msg.operation = 'decrypt';
putData(msg, data);
- // TODO: needs proper EOL to be decrypted.
+ return this._connection.post(msg);
+
+ }
- if (msg.isComplete === true){
- let conn = new Connection();
- return conn.post(msg.message);
+ deleteKey(key, delete_secret = false, no_confirm = false){
+ return Promise.reject(new GPGMEJS_Error ('NOT_YET_IMPLEMENTED'));
+ let msg = new GPGME_Message;
+ msg.operation = 'deletekey';
+ let key_arr = toKeyIdArray(key);
+ if (key_arr.length !== 1){
+ throw('TODO');
+ //should always be ONE key
+ }
+ msg.setParameter('key', key_arr[0]);
+ if (delete_secret === true){
+ msg.setParameter('allow_secret', true); //TBD
}
- else {
- return Promise.reject('NO_CONNECT');
- //TODO
+ if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
+ msg.setParameter('delete_force', true); //TBD
}
+ this._connection.post(msg).then(function(success){
+ //TODO: it seems that there is always errors coming back:
+ }, function(error){
+ switch (error.msg){
+ case 'ERR_NO_ERROR':
+ return Promise.resolve('okay'); //TBD
+ default:
+ return Promise.reject(new GPGMEJS_Error);
+ // INV_VALUE,
+ // GPG_ERR_NO_PUBKEY,
+ // GPG_ERR_AMBIGUOUS_NAME,
+ // GPG_ERR_CONFLICT
+ }
+ });
}
+
}
/**
@@ -112,7 +158,7 @@ export class GpgME {
*/
function putData(message, data){
if (!message || !message instanceof GPGME_Message ) {
- throw('NO_MESSAGE_OBJECT');
+ return new GPGMEJS_Error('WRONGPARAM');
}
if (!data){
//TODO Debug only! No data is legitimate
@@ -126,6 +172,6 @@ function putData(message, data){
message.setParameter('base64', false);
message.setParameter('data', data);
} else {
- throw('ERR_WRONG_TYPE');
+ return new GPGMEJS_Error('WRONGPARAM');
}
}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index 1eec4da4..54b9dd45 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -25,13 +25,18 @@
*/
import { GpgME } from "./gpgmejs";
-// import {Keyring} from "./Keyring" TODO
-
+ import {GPGME_Keyring} from "./Keyring"
+ import { GPGME_Key } from "./Key";
+ import { isFingerprint } from "./Helpers"
+ import { GPGMEJS_Error } from './Errors'
export class GpgME_openPGPCompatibility {
constructor(){
- this._gpgme = new GpgME;
+ this._gpgme = new GpgME({
+ null_expire_is_never: false
+ });
+ this.Keyring = this.initKeyring();
}
/**
@@ -67,15 +72,14 @@ export class GpgME_openPGPCompatibility {
|| signature !== null
|| returnSessionKey !== null
|| date !== null){
- throw('NOT_IMPLEMENTED');
+ return Promise.reject(new GPMGEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| filename
|| compression
|| armor === false
|| detached == true){
- console.log('may be implemented later');
- throw('NOT_YET_IMPLEMENTED');
+ return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
}
return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
}
@@ -103,16 +107,14 @@ export class GpgME_openPGPCompatibility {
if (passwords !== undefined
|| sessionKeys
|| date){
-
- throw('NOT_IMPLEMENTED');
+ return Promise.reject(new GPGMEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| publicKeys
|| format !== 'utf8'
|| signature
){
- console.log('may be implemented later');
- throw('NOT_YET_IMPLEMENTED');
+ return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
}
return this.GpgME.decrypt(message);
// TODO: translate between:
@@ -126,31 +128,74 @@ export class GpgME_openPGPCompatibility {
// mime: A Boolean indicating whether the data is a MIME object.
// info: An optional object with extra information.
}
+ initKeyring(){
+ return new GPGME_Keyring_openPGPCompatibility;
+ }
}
/**
- *
- * @param {Object | String} Key Either a (presumably openpgp Key) Object with a
- * primaryKeyproperty and a method getFingerprint, or a string.
- * @returns {String} Unchecked string value claiming to be a fingerprint
- * TODO: gpgmejs checks again, so it's okay here.
+ * Translation layer offering basic Keyring API to be used in Mailvelope.
+ * It may still be changed/expanded/merged with GPGME_Keyring
*/
-function translateKeyInput(Key){
- if (!Key){
- return [];
+class GPGME_Keyring_openPGPCompatibility {
+ constructor(){
+ this._gpgme_keyring = new GPGME_Keyring;
}
- if (!Array.isArray(Key)){
- Key = [Key];
+
+ /**
+ * Returns a GPGME_Key Object for each Key in the gnupg Keyring. This
+ * includes keys openpgpjs considers 'private' (usable for signing), with
+ * the difference that Key.armored will NOT contain any secret information.
+ * Please also note that a GPGME_Key does not offer full openpgpjs- Key
+ * compatibility.
+ * @returns {Array} with the objects offering at least:
+ * @property {String} armored The armored key block (does not include secret blocks)
+ * @property {Boolean} hasSecret Indicator if a private/secret key exists
+ * @property {Boolean} isDefault Indicator if private key exists and is the default key in this keyring
+ * @property {String} fingerprint The fingerprint identifying this key
+ * //TODO: Check if IsDefault is also always hasSecret
+ */
+ getPublicKeys(){
+ return this._gpgme_keyring.getKeys(null, true);
}
- let resultslist = [];
- for (let i=0; i < Key.length; i++){
- if (typeof(Key[i]) === 'string'){
- resultslist.push(Key);
- } else if (
- Key[i].hasOwnProperty(primaryKey) &&
- Key[i].primaryKey.hasOwnProperty(getFingerprint)){
- resultslist.push(Key[i].primaryKey.getFingerprint());
+
+ /**
+ * Returns the Default Key used for crypto operation in gnupg.
+ * Please note that the armored property does not contained secret key blocks,
+ * despite secret blocks being part of the key itself.
+ * @returns {Promise }
+ */
+ getDefaultKey(){
+ this._gpgme_keyring.getSubset({defaultKey: true}).then(function(result){
+ if (result.length === 1){
+ return Promise.resolve(result[0]);
+ }
+ else {
+ // TODO: Can there be "no default key"?
+ // TODO: Can there be several default keys?
+ return new GPGMEJS_Error; //TODO
+ }
+ });
+ }
+
+ /**
+ * Deletes a Key
+ * @param {Object} Object identifying key
+ * @param {String} key.fingerprint - fingerprint of the to be deleted key
+ * @param {Boolean} key.secret - indicator if private key should be deleted as well
+
+ * @returns {Promise., Error>} TBD: Not sure what is wanted
+ TODO @throws {Error} error.code = ‘KEY_NOT_EXIST’ - there is no key for the given fingerprint
+ TODO @throws {Error} error.code = ‘NO_SECRET_KEY’ - secret indicator set, but no secret key exists
+ */
+ deleteKey(key){
+ if (typeof(key) !== "object"){
+ return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ }
+ if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
+ return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
}
+ let key_to_delete = new GPGME_Key(key.fingerprint);
+ return key_to_delete.deleteKey(key.secret);
}
- return resultslist;
-}
\ No newline at end of file
+}
--
cgit v1.2.3
From 727340b295f25e04cb595022ba143cda48364697 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Mon, 23 Apr 2018 19:15:40 +0200
Subject: js: don't allow message operation changes
--
Once an operation is changed, their set of allowed/required parameters
will change. So we shouldn't set/change the operation later.
---
lang/js/src/Keyring.js | 3 +--
lang/js/src/Message.js | 42 ++++++++++++++++++++----------------------
lang/js/src/gpgmejs.js | 9 +++------
3 files changed, 24 insertions(+), 30 deletions(-)
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index 52fa7f71..d8cb84b2 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -49,8 +49,7 @@ export class GPGME_Keyring {
*
*/
getKeys(pattern, include_secret){
- let msg = new GPGME_Message;
- msg.operation = 'listkeys';
+ let msg = new GPGME_Message('listkeys');
if (pattern && typeof(pattern) === 'string'){
msg.setParameter('pattern', pattern);
}
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 6a93b6f4..f5e21e00 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -23,28 +23,7 @@ export class GPGME_Message {
//TODO getter
constructor(operation){
- if (operation){
- this.operation(operation);
- }
- }
-
- /**
- * Defines the operation this message will have
- * @param {String} operation Must be defined in permittedOperations
- * TODO: move to constructor?
- */
- set operation (operation){
- if (!operation || typeof(operation) !== 'string'){
- return new GPGMEJS_Error('WRONGPARAM');
- }
- if (operation in permittedOperations){
- if (!this._msg){
- this._msg = {};
- }
- this._msg.op = operation;
- } else {
- return new GPGMEJS_Error('WRONG_OP');
- }
+ setOperation(this, operation);
}
get operation(){
@@ -110,4 +89,23 @@ export class GPGME_Message {
}
}
+}
+
+/**
+ * Defines the operation this message will have
+ * @param {String} operation Must be defined in permittedOperations
+ * TODO: move to constructor?
+ */
+function setOperation (scope, operation){
+ if (!operation || typeof(operation) !== 'string'){
+ return new GPGMEJS_Error('WRONGTYPE');
+ }
+ if (permittedOperations.hasOwnProperty(operation)){
+ if (!scope._msg){
+ scope._msg = {};
+ }
+ scope._msg.op = operation;
+ } else {
+ return new GPGMEJS_Error('WRONG_OP');
+ }
}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index c23a356b..b15477f0 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -76,8 +76,7 @@ export class GpgME {
*/
encrypt (data, publicKeys, wildcard=false){
- let msg = new GPGME_Message;
- msg.operation = 'encrypt';
+ let msg = new GPGME_Message('encrypt');
// TODO temporary
msg.setParameter('armor', true);
@@ -108,8 +107,7 @@ export class GpgME {
if (data === undefined){
return Promise.reject(new GPGMEJS_Error ('EMPTY_MSG'));
}
- let msg = new GPGME_Message;
- msg.operation = 'decrypt';
+ let msg = new GPGME_Message('decrypt');
putData(msg, data);
return this._connection.post(msg);
@@ -117,8 +115,7 @@ export class GpgME {
deleteKey(key, delete_secret = false, no_confirm = false){
return Promise.reject(new GPGMEJS_Error ('NOT_YET_IMPLEMENTED'));
- let msg = new GPGME_Message;
- msg.operation = 'deletekey';
+ let msg = new GPGME_Message('deletekey');
let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){
throw('TODO');
--
cgit v1.2.3
From 461dd0c8b41683a91073b362d100ee5217ec53f6 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Tue, 24 Apr 2018 18:44:30 +0200
Subject: js: change in initialization ancd connection handling
--
* The Connection will now be started before an object is created, to
better account for failures.
* index.js: now exposes an init(), which returns a Promise of
configurable with an
established connection.
* TODO: There is currently no way to recover from a "connection lost"
* Connection.js offers Connection.isConnected, which toggles on port
closing.
---
lang/js/src/Connection.js | 41 ++++++++++++++-----------
lang/js/src/Keyring.js | 30 +++++++++---------
lang/js/src/gpgmejs.js | 61 ++++++++++++++++---------------------
lang/js/src/gpgmejs_openpgpjs.js | 33 ++++++++++++++------
lang/js/src/index.js | 40 ++++++++++++++++++++++--
lang/js/testapplication.js | 66 +++++++++++++++++++---------------------
6 files changed, 157 insertions(+), 114 deletions(-)
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 87ec8cf7..e6ff67be 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -35,6 +35,18 @@ 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;
}
/**
@@ -48,28 +60,20 @@ export class Connection{
/**
* Opens a nativeMessaging port.
- * returns nothing, but triggers errors if not successfull:
- * NO_CONNECT: connection not successfull, chrome.runtime.lastError may be
- * available
- * ALREADY_CONNECTED: There is already a connection present.
+ * TODO: Error handling ALREADY_CONNECTED
*/
connect(){
- if (this._connection){
+ if (this._isConnected === true){
return new GPGMEJS_Error('ALREADY_CONNECTED');
}
+ this._isConnected = true;
this._connection = chrome.runtime.connectNative('gpgmejson');
- if (!this._connection){
- return new GPGMEJS_Error('NO_CONNECT');
- }
- }
-
- /**
- * checks if the connection is established
- * TODO: some kind of ping to see if the other side responds as expected?
- * @returns {Boolean}
- */
- get connected(){
- return this._connection ? true: false;
+ let me = this;
+ this._connection.onDisconnect.addListener(
+ function(){
+ me._isConnected = false;
+ }
+ );
}
/**
@@ -79,6 +83,9 @@ export class Connection{
* information.
*/
post(message){
+ if (!this.isConnected){
+ return Promise.reject(new GPGMEJS_Error('NO_CONNECT'));
+ }
if (!message || !message instanceof GPGME_Message){
return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
}
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index d8cb84b2..ef8028ff 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -19,27 +19,27 @@
*/
import {GPGME_Message} from './Message'
-import {Connection} from './Connection'
import {GPGME_Key} from './Key'
import { isFingerprint, isLongId } from './Helpers';
export class GPGME_Keyring {
- constructor(){
- this.reconnect();
+ constructor(connection){
+ this.connection = connection;
}
- /**
- * (Re)-establishes the connection
- * TODO TEMP: should we better use the connection of our parent,
- * which we do not control?
- */
- reconnect(){
- if (!this._connection || ! this._connection instanceof Connection){
- this._connection = new Connection;
- } else {
- this._connection.disconnect();
- this._connection.connect();
+ set connection(connection){
+ if (!this._connection && connection instanceof Connection){
+ this._connection = connection;
+ }
+ }
+ get connection(){
+ if (this._connection instanceof Connection){
+ if (this._connection.isConnected){
+ return this._connection;
+ }
+ return undefined; //TODO: connection was lost!
}
+ return undefined; //TODO: no connection there
}
/**
@@ -57,7 +57,7 @@ export class GPGME_Keyring {
msg.setParameter('with-secret', true);
}
- this._connection.post(msg).then(function(result){
+ this.connection.post(msg).then(function(result){
let fpr_list = [];
let resultset = [];
if (!Array.isArray(result.keys)){
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index b15477f0..4b2a03a4 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -22,59 +22,51 @@ import {Connection} from "./Connection"
import {GPGME_Message} from './Message'
import {toKeyIdArray} from "./Helpers"
import {GPGMEJS_Error as Error, GPGMEJS_Error} from "./Errors"
+import { GPGME_Keyring } from "./Keyring";
export class GpgME {
/**
* initializes GpgME by opening a nativeMessaging port
* TODO: add configuration
*/
- constructor(configuration = {
- null_expire_is_never: false
- }){
- this._connection = new Connection;
+ constructor(connection){
+ this.connection = connection;
}
- /**
- * refreshes the nativeApp connection
- */
- reconnect(){
- if (!this._connection || ! this._connection instanceof Connection){
- this._connection = new Connection;
- } else {
- this._connection.disconnect();
- this._connection.connect();
+ set connection(connection){
+ if (this._connection instanceof Connection){
+ //TODO Warning: Connection already established
+ }
+ if (connection instanceof Connection){
+ this._connection = connection;
}
}
- /**
- * inmediately tries to destroy the nativeMessaging connection.
- * TODO: may not be included in final API, as it is redundant.
- * For now, it just serves paranoia
- */
- disconnect(){
- if (this._connection){
- this._connection.disconnect();
- this._connection = null;
+ get connection(){
+ if (this._connection instanceof Connection){
+ if (this._connection.isConnected){
+ return this._connection;
+ }
+ return undefined; //TODO: connection was lost!
}
+ return undefined; //TODO: no connection there
}
- /**
- * tests the nativeApp connection
- */
- get connected(){
- if (!this._connection || ! this._connection instanceof Connection){
- return false;
+ set Keyring(keyring){
+ if (ring && ring instanceof GPGME_Keyring){
+ this.Keyring = ring;
}
- return this._connection.connected;
}
+ get 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){
+ encrypt(data, publicKeys, wildcard=false){
let msg = new GPGME_Message('encrypt');
@@ -89,7 +81,7 @@ export class GpgME {
if (wildcard === true){msg.setParameter('throw-keyids', true);
};
- return (this._connection.post(msg));
+ return (this.connection.post(msg));
}
/**
@@ -109,7 +101,7 @@ export class GpgME {
}
let msg = new GPGME_Message('decrypt');
putData(msg, data);
- return this._connection.post(msg);
+ return this.connection.post(msg);
}
@@ -128,7 +120,7 @@ export class GpgME {
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){
+ this.connection.post(msg).then(function(success){
//TODO: it seems that there is always errors coming back:
}, function(error){
switch (error.msg){
@@ -143,7 +135,6 @@ export class GpgME {
}
});
}
-
}
/**
@@ -171,4 +162,4 @@ function putData(message, data){
} else {
return new GPGMEJS_Error('WRONGPARAM');
}
-}
\ No newline at end of file
+}
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index 54b9dd45..f1ddb5d6 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -30,13 +30,29 @@
import { isFingerprint } from "./Helpers"
import { GPGMEJS_Error } from './Errors'
+
export class GpgME_openPGPCompatibility {
- constructor(){
- this._gpgme = new GpgME({
- null_expire_is_never: false
- });
- this.Keyring = this.initKeyring();
+ constructor(connection){
+ this.initGpgME(connection);
+ }
+
+ get Keyring(){
+ if (this._keyring){
+ return this._keyring;
+ }
+ return undefined;
+ }
+
+ initGpgME(connection){
+ this._GpgME = new GpgME(connection);
+ this._Keyring = new GPGME_Keyring_openPGPCompatibility(connection);
+ }
+
+ get GpgME(){
+ if (this._GpGME){
+ return this._GpGME;
+ }
}
/**
@@ -128,9 +144,6 @@ export class GpgME_openPGPCompatibility {
// mime: A Boolean indicating whether the data is a MIME object.
// info: An optional object with extra information.
}
- initKeyring(){
- return new GPGME_Keyring_openPGPCompatibility;
- }
}
/**
@@ -138,8 +151,8 @@ export class GpgME_openPGPCompatibility {
* It may still be changed/expanded/merged with GPGME_Keyring
*/
class GPGME_Keyring_openPGPCompatibility {
- constructor(){
- this._gpgme_keyring = new GPGME_Keyring;
+ constructor(connection){
+ this._gpgme_keyring = new GPGME_Keyring(connection);
}
/**
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index f70bd2d8..0e2beda4 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -18,6 +18,40 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
-import { GpgME as gpgmejs } from "./gpgmejs";
-// import { GpgME_openPGPCompatibility as gpgmejs } from "./gpgmejs_openpgpjs";
-export default gpgmejs;
+import { GpgME } from "./gpgmejs";
+import { GpgME_openPGPCompatibility } from "./gpgmejs_openpgpjs";
+import { Connection } from "./Connection";
+
+/**
+ * Initializes a nativeMessaging Connection and returns a GPGMEjs object
+ * @param {*} conf Configuration. TBD
+ */
+function init( config = {
+ api_style: 'gpgme', // | gpgme_openpgpjs
+ null_expire_is_never: true // Boolean
+ }){
+ return new Promise(function(resolve, reject){
+ let connection = new Connection;
+ // TODO: Delayed reaction is ugly. We need to listen to the port's
+ // event listener in isConnected, but this takes some time (<5ms) to
+ // disconnect if there is no successfull connection.
+ let delayedreaction = function(){
+ if (connection.isConnected === true){
+ let gpgme = null;
+ if (config.api_style && config.api_style === 'gpgme_openpgpjs'){
+ resolve(
+ new GpgME_openPGPCompatibility(connection));
+ } else {
+ resolve(new GpgME(connection));
+ }
+ } else {
+ reject('NO_CONNECT');
+ }
+ };
+ setTimeout(delayedreaction, 5);
+ });
+};
+
+export default {
+ init: init
+}
\ No newline at end of file
diff --git a/lang/js/testapplication.js b/lang/js/testapplication.js
index 97b35527..f47299e8 100644
--- a/lang/js/testapplication.js
+++ b/lang/js/testapplication.js
@@ -19,39 +19,37 @@
*
*/
-function encryptbuttonclicked(event){
- let data = document.getElementById('cleartext').value;
- let keyId = document.getElementById('pubkey').value;
- let communication = new Gpgmejs;
- let enc = communication.encrypt(data, keyId).then(
- function(answer){
- console.log(answer);
- if (answer.data){
- console.log(answer.data);
- document.getElementById('answer').value = answer.data;
- }
- }, function(errormsg){
- alert('Error: '+ errormsg);
- });
-};
-
-function decryptbuttonclicked(event){
- let data = document.getElementById("ciphertext").value;
- let communication = new Gpgmejs;
- let enc = communication.decrypt(data).then(
- function(answer){
- console.log(answer);
- if (answer.data){
- document.getElementById('answer').value = answer.data;
- }
- }, function(errormsg){
- alert('Error: '+ errormsg);
- });
-};
-
document.addEventListener('DOMContentLoaded', function() {
- document.getElementById("buttonencrypt").addEventListener("click",
- encryptbuttonclicked);
- document.getElementById("buttondecrypt").addEventListener("click",
- decryptbuttonclicked);
+ Gpgmejs.init().then(function(gpgmejs){
+ document.getElementById("buttonencrypt").addEventListener("click",
+ function(){
+ let data = document.getElementById('cleartext').value;
+ let keyId = document.getElementById('pubkey').value;
+ gpgmejs.encrypt(data, keyId).then(
+ function(answer){
+ console.log(answer);
+ if (answer.data){
+ console.log(answer.data);
+ document.getElementById('answer').value = answer.data;
+ }
+ }, function(errormsg){
+ alert('Error: '+ errormsg);
+ });
+ });
+
+ document.getElementById("buttondecrypt").addEventListener("click",
+ function(){
+ let data = document.getElementById("ciphertext").value;
+ gpgmejs.decrypt(data).then(
+ function(answer){
+ console.log(answer);
+ if (answer.data){
+ document.getElementById('answer').value = answer.data;
+ }
+ }, function(errormsg){
+ alert('Error: '+ errormsg);
+ });
+ });
+ },
+ function(error){console.log(error)});
});
--
cgit v1.2.3
From e2aa8066a9b3ce694169ad9fcc26cae486a804af Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Tue, 24 Apr 2018 19:29:32 +0200
Subject: js: Key object adjustments after discussion
--
* src/aKey.js changed fingerprint to setter (to avoid overwrites)
* src/gpgmejs_openpgpjs.js
- Added a class GPGME_Key_openpgpmode, which allows for renaming and
deviation from GPGME.
- renamed classes *_openPGPCompatibility to *_openpgpmode. They are
not fully compatible, but only offer a subset of properties. Also,
the name seems less clunky
---
lang/js/src/Key.js | 8 +++--
lang/js/src/gpgmejs_openpgpjs.js | 67 ++++++++++++++++++++++++++++++++++------
lang/js/src/index.js | 4 +--
3 files changed, 64 insertions(+), 15 deletions(-)
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index d8f16c55..f59b9901 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -32,10 +32,12 @@ import {GPGMEJS_Error} from './Errors'
export class GPGME_Key {
constructor(fingerprint){
- if (isFingerprint(fingerprint) === true){
+ this.fingerprint = fingerprint;
+ }
+
+ set fingerprint(fpr){
+ if (isFingerprint(fpr) === true && !this._fingerprint){
this._fingerprint = fingerprint;
- } else {
- return new GPGMEJS_Error('WRONGPARAM', 'Key.js: invalid fingerprint');
}
}
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index f1ddb5d6..23076569 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -31,7 +31,7 @@
import { GPGMEJS_Error } from './Errors'
-export class GpgME_openPGPCompatibility {
+ export class GpgME_openpgpmode {
constructor(connection){
this.initGpgME(connection);
@@ -46,7 +46,7 @@ export class GpgME_openPGPCompatibility {
initGpgME(connection){
this._GpgME = new GpgME(connection);
- this._Keyring = new GPGME_Keyring_openPGPCompatibility(connection);
+ this._Keyring = new GPGME_Keyring_openpgpmode(connection);
}
get GpgME(){
@@ -150,7 +150,7 @@ export class GpgME_openPGPCompatibility {
* Translation layer offering basic Keyring API to be used in Mailvelope.
* It may still be changed/expanded/merged with GPGME_Keyring
*/
-class GPGME_Keyring_openPGPCompatibility {
+class GPGME_Keyring_openpgpmode {
constructor(connection){
this._gpgme_keyring = new GPGME_Keyring(connection);
}
@@ -161,15 +161,13 @@ class GPGME_Keyring_openPGPCompatibility {
* 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} with the objects offering at least:
- * @property {String} armored The armored key block (does not include secret blocks)
- * @property {Boolean} hasSecret Indicator if a private/secret key exists
- * @property {Boolean} isDefault Indicator if private key exists and is the default key in this keyring
- * @property {String} fingerprint The fingerprint identifying this key
+ * @returns {Array}
* //TODO: Check if IsDefault is also always hasSecret
+ * TODO Check if async is required
*/
getPublicKeys(){
- return this._gpgme_keyring.getKeys(null, true);
+ return translateKeys(
+ this._gpgme_keyring.getKeys(null, true));
}
/**
@@ -181,7 +179,8 @@ class GPGME_Keyring_openPGPCompatibility {
getDefaultKey(){
this._gpgme_keyring.getSubset({defaultKey: true}).then(function(result){
if (result.length === 1){
- return Promise.resolve(result[0]);
+ return Promise.resolve(
+ translateKeys(result)[0]);
}
else {
// TODO: Can there be "no default key"?
@@ -212,3 +211,51 @@ class GPGME_Keyring_openPGPCompatibility {
return key_to_delete.deleteKey(key.secret);
}
}
+
+/**
+ * TODO error handling.
+ * Offers the Key information as the openpgpmode wants
+ */
+class GPGME_Key_openpgpmode {
+ constructor(value){
+ this.init = value;
+ }
+
+ set init (value){
+ if (!this._GPGME_Key && value instanceof GPGME_Key){
+ this._GPGME_Key = value;
+ } else if (!this._GPGME_Key && isFingerprint(fpr)){
+ this._GPGME_Key = new GPGME_Key;
+ }
+ }
+
+ get fingerprint(){
+ return this._GPGME_Key.fingerprint;
+ }
+
+ get armor(){
+ return this._GPGME_Key.armored;
+ }
+
+ get secret(){
+ return this._GPGME_Key.hasSecret;
+ }
+
+ get default(){
+ return this._GPGME_Key.isDefault;
+ }
+}
+
+/**
+ * creates GPGME_Key_openpgpmode from GPGME_Keys
+ */
+function translateKeys(input){
+ if (!Array.isArray(input)){
+ input = [input];
+ }
+ let resultset;
+ for (let i=0; i< input.length; i++){
+ resultset.push(new GPGME_Key_openpgpmode(input[i]));
+ }
+ return resultset;
+}
\ No newline at end of file
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 0e2beda4..0cb2301c 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -19,7 +19,7 @@
*/
import { GpgME } from "./gpgmejs";
-import { GpgME_openPGPCompatibility } from "./gpgmejs_openpgpjs";
+import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs";
import { Connection } from "./Connection";
/**
@@ -40,7 +40,7 @@ function init( config = {
let gpgme = null;
if (config.api_style && config.api_style === 'gpgme_openpgpjs'){
resolve(
- new GpgME_openPGPCompatibility(connection));
+ new GpgME_openpgpmode(connection));
} else {
resolve(new GpgME(connection));
}
--
cgit v1.2.3
From 30c47d80a27054aa340cbd6dc39d1b8a5dc5cf22 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Tue, 24 Apr 2018 19:47:48 +0200
Subject: js: allow openpgp-like Message objects as Data
--
* src/gpgmejs.js: If a message offers a getText, consider it as the
message's content
---
lang/js/src/gpgmejs.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index 4b2a03a4..03ed5cb6 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -159,6 +159,13 @@ function putData(message, 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 new GPGMEJS_Error('WRONGPARAM');
}
--
cgit v1.2.3
From c72adc00965fe4fcedd9d18609211021a091b28b Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Wed, 25 Apr 2018 10:54:24 +0200
Subject: js: change in Error behaviour
--
* Error objects will now return the error code if defined as error type
in src/Errors.js, or do a console.log if it is a warning. Errors from
the native gpgme-json will be marked as GNUPG_ERROR.
---
lang/js/src/Connection.js | 58 +++++++------
lang/js/src/Errors.js | 170 ++++++++++++++++-----------------------
lang/js/src/Helpers.js | 16 ++--
lang/js/src/Key.js | 2 +-
lang/js/src/Message.js | 12 +--
lang/js/src/gpgmejs.js | 13 ++-
lang/js/src/gpgmejs_openpgpjs.js | 14 ++--
lang/js/src/index.js | 3 +-
8 files changed, 132 insertions(+), 156 deletions(-)
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index e6ff67be..8bc3d42a 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -1,5 +1,3 @@
-import { GPGME_Message } from "./Message";
-
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
@@ -26,7 +24,8 @@ import { GPGME_Message } from "./Message";
* expected.
*/
import { permittedOperations } from './permittedOperations'
-import { GPGMEJS_Error} from "./Errors"
+import { GPGMEJS_Error } from "./Errors"
+import { GPGME_Message } from "./Message";
/**
* A Connection handles the nativeMessaging interaction.
@@ -60,20 +59,20 @@ export class Connection{
/**
* Opens a nativeMessaging port.
- * TODO: Error handling ALREADY_CONNECTED
*/
connect(){
if (this._isConnected === true){
- return new GPGMEJS_Error('ALREADY_CONNECTED');
+ GPGMEJS_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;
+ }
+ );
}
- this._isConnected = true;
- this._connection = chrome.runtime.connectNative('gpgmejson');
- let me = this;
- this._connection.onDisconnect.addListener(
- function(){
- me._isConnected = false;
- }
- );
}
/**
@@ -84,28 +83,31 @@ export class Connection{
*/
post(message){
if (!this.isConnected){
- return Promise.reject(new GPGMEJS_Error('NO_CONNECT'));
+ return Promise.reject(GPGMEJS_Error('CONN_NO_CONNECT'));
}
if (!message || !message instanceof GPGME_Message){
- return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ return Promise.reject(GPGMEJS_Error('PARAM_WRONG'), message);
}
if (message.isComplete !== true){
- return Promise.reject(new GPGMEJS_Error('MSG_INCOMPLETE'));
+ return Promise.reject(GPGMEJS_Error('MSG_INCOMPLETE'));
}
- // let timeout = 5000; //TODO config
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(new GPGMEJS_Error('EMPTY_GPG_ANSWER'));
+ reject(GPGMEJS_Error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener)
- //TODO: GPGMEJS_Error?
- reject(msg.msg);
+ reject(
+ {code: 'GNUPG_ERROR',
+ msg: msg.msg} );
} else {
- answer.add(msg);
+ let answer_result = answer.add(msg);
+ if (answer_result !== true){
+ reject(answer_result);
+ }
if (msg.more === true){
me._connection.postMessage({'op': 'getmore'});
} else {
@@ -117,11 +119,12 @@ export class Connection{
me._connection.onMessage.addListener(listener);
me._connection.postMessage(message.message);
+
//TBD: needs to be aware if there is a pinentry pending
// setTimeout(
// function(){
// me.disconnect();
- // reject(new GPGMEJS_Error('TIMEOUT', 5000));
+ // reject(GPGMEJS_Error('CONN_TIMEOUT'));
// }, timeout);
});
}
@@ -141,6 +144,7 @@ class Answer{
/**
* Add the information to the answer
* @param {Object} msg The message as received with nativeMessaging
+ * returns true if successfull, GPGMEJS_Error otherwise
*/
add(msg){
if (this._response === undefined){
@@ -148,12 +152,15 @@ class Answer{
}
let messageKeys = Object.keys(msg);
let poa = permittedOperations[this.operation].answer;
+ if (messageKeys.length === 0){
+ return GPGMEJS_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 new GPGMEJS_Error('UNEXPECTED_ANSWER');
+ return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER');
}
break;
case 'more':
@@ -172,7 +179,7 @@ class Answer{
this._response[key] = msg[key];
}
else if (this._response[key] !== msg[key]){
- return new GPGMEJS_Error('UNEXPECTED_ANSWER',msg[key]);
+ return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER',msg[key]);
}
}
//infos may be json objects etc. Not yet defined.
@@ -184,11 +191,12 @@ class Answer{
this._response.push(msg[key]);
}
else {
- return new GPGMEJS_Error('UNEXPECTED_ANSWER', key);
+ return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER', key);
}
break;
}
}
+ return true;
}
/**
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index c2356f7c..c49bfe21 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -18,131 +18,99 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
-// This is a preliminary collection of erors and warnings to be thrown and implemented.
-
-// general idea: if throw , throw the NAME
-// return false || 'return' property
-
-//TODO: Connection.NOCONNECT promise
-//connection.timeout: Be aware of pinentry
-
-export class GPGMEJS_Error {
-
- constructor(code = 'GENERIC_ERROR', details){
- let config = { //TODO TEMP
- debug: 'console', // |'alert'
- throw: 'default' // | 'always' | 'never'
- };
+/**
+ * Checks the given error code and returns some information about it's meaning
+ * @param {String} code The error code
+ * @returns {Object} An object containing string properties code and msg
+ * TODO: error-like objects with the code 'GNUPG_ERROR' are errors sent
+ * directly by gnupg as answer in Connection.post()
+ */
+export function GPGMEJS_Error(code = 'GENERIC_ERROR'){
+ if (!typeof(code) === 'string'){
+ code = 'GENERIC_ERROR';
+ }
let errors = { //TODO: someplace else
- //Connection errors
- 'ALREADY_CONNECTED':{
- msg: 'The connection was already established. The action would overwrite the context',
- throw: true
+ // Connection
+ 'CONN_NO_CONNECT': {
+ msg:'Connection with the nativeMessaging host could not be'
+ + ' established.',
+ type: 'error'
},
- 'NO_CONNECT': {
- msg:'Connection with the nativeMessaging host could not be established.',
- throw: true
+ 'CONN_EMPTY_GPG_ANSWER':{
+ msg: 'The nativeMessaging answer was empty.',
+ type: 'error'
},
- 'EMPTY_GPG_ANSWER':{
- msg: 'The nativeMesaging answer was empty',
- throw: true
+ 'CONN_TIMEOUT': {
+ msg: 'A connection timeout was exceeded.',
+ type: 'error'
},
- 'TIMEOUT': {
- msg: 'A timeout was exceeded.',
- throw: false
+ 'CONN_UNEXPECTED_ANSWER': {
+ msg: 'The answer from gnupg was not as expected.',
+ type: 'error'
},
-
- 'UNEXPECTED_ANSWER': {
- msg: 'The answer from gnupg was not as expected',
- throw: true
- },
-
- // Message/Data Errors
-
- 'NO_KEYS' : {
- msg: 'There were no valid keys provided.',
- throw: true
- },
- 'NOT_A_FPR': {
- msg: 'The String is not an accepted fingerprint',
- throw: false
+ 'CONN_ALREADY_CONNECTED':{
+ msg: 'A connection was already established.',
+ type: 'warn'
},
+ // Message/Data
'MSG_INCOMPLETE': {
- msg: 'The Message did not match the minimum requirements for the interaction',
- throw: true
- },
- 'EMPTY_MSG' : {
- msg: 'The Message has no data.',
- throw: true
+ msg: 'The Message did not match the minimum requirements for'
+ + ' the interaction.',
+ type: 'error'
},
- 'MSG_NODATA':{
- msg: 'The data sent is empty. This may be unintentional.',
- throw: false
+ 'MSG_EMPTY' : {
+ msg: 'The Message is empty.',
+ type: 'error'
},
'MSG_OP_PENDING': {
- msg: 'There is no operation specified yet. The parameter cannot be set',
- throw: false
+ msg: 'There is no operation specified yet. The parameter cannot'
+ + ' be set',
+ type: 'warning'
},
- 'WRONG_OP': {
- msg: "The operation requested could not be found",
- throw: true
+ 'MSG_WRONG_OP': {
+ msg: 'The operation requested could not be found',
+ type: 'warning'
+ },
+ 'MSG_NO_KEYS' : {
+ msg: 'There were no valid keys provided.',
+ type: 'warn'
+ },
+ 'MSG_NOT_A_FPR': {
+ msg: 'The String is not an accepted fingerprint',
+ type: 'warn'
},
- //generic errors
-
- 'WRONGPARAM':{
+ // generic
+ 'PARAM_WRONG':{
msg: 'invalid parameter was found',
- throw: true
- },
- 'WRONGTYPE':{
- msg: 'invalid parameter type was found',
- throw: true
+ type: 'error'
},
'NOT_IMPLEMENTED': {
msg: 'A openpgpjs parameter was submitted that is not implemented',
- throw: true
+ type: 'error'
+ },
+ 'NOT_YET_IMPLEMENTED': {
+ msg: 'Support of this is probable, but it is not implemented yet',
+ type: 'error'
},
'GENERIC_ERROR': {
msg: 'Unspecified error',
- throw: true
+ type: 'error'
},
-
- // hopefully temporary errors
-
- 'NOT_YET_IMPLEMENTED': {
- msg: 'Support of this is probable, but it is not implemented yet',
- throw: false
- }
- }
- if (!errors.hasOwnProperty(code)){
- throw('GENERIC_ERROR');
}
- let msg = code;
- if (errors[code].msg !== undefined){
- msg = msg + ': ' + errors[code].msg;
+ if (code === 'TODO'){
+ alert('TODO_Error!');
}
- if (details){
- msg = msg + ' ' + details;
+ if (errors.hasOwnProperty(code)){
+ code = 'GENERIC_ERROR';
}
- if (config.debug === 'console'){
- console.log(msg);
- } else if (config.debug === 'alert'){
- alert(msg);
+ if (error.type === 'error'){
+ return {code: 'code',
+ msg: errors[code].msg
+ };
}
- switch (config.throw) {
- case 'default':
- if (errors[code].throw === true){
- throw(code);
- }
- break;
- case 'always':
- throw(code);
- break;
-
- case 'never':
- break;
- default:
- throw('GENERIC_ERROR');
+ if (error.type === 'warning'){
+ console.log(code + ': ' + error[code].msg);
}
- }
+ return undefined;
}
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index 922ca06c..d9750ba7 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -1,5 +1,3 @@
-import { GPGMEJS_Error } from "./Errors";
-
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
@@ -19,18 +17,19 @@ import { GPGMEJS_Error } from "./Errors";
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
+import { GPGMEJS_Error } from "./Errors";
/**
* Tries to return an array of fingerprints, either from input fingerprints or
* from Key objects
* @param {Key |Array| GPGME_Key | Array|String|Array} input
- * @param {Boolean} nocheck if set, an empty result is acceptable
* @returns {Array} Array of fingerprints.
*/
export function toKeyIdArray(input, nocheck){
if (!input){
- return (nocheck ===true)? [] : new GPGMEJS_Error('NO_KEYS');
+ GPGMEJS_Error('MSG_NO_KEYS');
+ return [];
}
if (!Array.isArray(input)){
input = [input];
@@ -41,7 +40,7 @@ export function toKeyIdArray(input, nocheck){
if (isFingerprint(input[i]) === true){
result.push(input[i]);
} else {
- GPGMEJS_Error
+ GPGMEJS_Error('MSG_NOT_A_FPR');
}
} else if (typeof(input[i]) === 'object'){
let fpr = '';
@@ -53,13 +52,16 @@ export function toKeyIdArray(input, nocheck){
}
if (isFingerprint(fpr) === true){
result.push(fpr);
+ } else {
+ GPGMEJS_Error('MSG_NOT_A_FPR');
}
} else {
- return new GPGMEJS_Error('WRONGTYPE');
+ return GPGMEJS_Error('PARAM_WRONG');
}
}
if (result.length === 0){
- return (nocheck===true)? [] : new GPGMEJS_Error('NO_KEYS');
+ GPGMEJS_Error('MSG_NO_KEYS');
+ return [];
} else {
return result;
}
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index f59b9901..5ae80438 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -172,7 +172,7 @@ export class GPGME_Key {
*
*/
function checkKey(fingerprint, property){
- return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+ return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
return new Promise(function(resolve, reject){
if (!isFingerprint(fingerprint)){
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index f5e21e00..1b36f11d 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -39,20 +39,20 @@ export class GPGME_Message {
*/
setParameter(param,value){
if (!param || typeof(param) !== 'string'){
- return new GPGMEJS_Error('WRONGPARAM', 'type check failed');
+ return GPGMEJS_Error('PARAM_WRONG');
}
if (!this._msg || !this._msg.op){
- return new GPGMEJS_Error('MSG_OP_PENDING');
+ return GPGMEJS_Error('MSG_OP_PENDING');
}
let po = permittedOperations[this._msg.op];
if (!po){
- return new GPGMEJS_Error('WRONG_OP', param);
+ return GPGMEJS_Error('MSG_WRONG_OP');
}
if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
this._msg[param] = value;
return true;
}
- return new GPGMEJS_Error('WRONGPARAM', param);
+ return GPGMEJS_Error('PARAM_WRONG');
}
/**
@@ -98,7 +98,7 @@ export class GPGME_Message {
*/
function setOperation (scope, operation){
if (!operation || typeof(operation) !== 'string'){
- return new GPGMEJS_Error('WRONGTYPE');
+ return GPGMEJS_Error('PARAM_WRONG');
}
if (permittedOperations.hasOwnProperty(operation)){
if (!scope._msg){
@@ -106,6 +106,6 @@ function setOperation (scope, operation){
}
scope._msg.op = operation;
} else {
- return new GPGMEJS_Error('WRONG_OP');
+ return GPGMEJS_Error('MSG_WRONG_OP');
}
}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index 03ed5cb6..b20ff0f2 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -95,9 +95,8 @@ export class GpgME {
*/
decrypt(data){
-
if (data === undefined){
- return Promise.reject(new GPGMEJS_Error ('EMPTY_MSG'));
+ return Promise.reject(GPGMEJS_Error('MSG_EMPTY'));
}
let msg = new GPGME_Message('decrypt');
putData(msg, data);
@@ -106,7 +105,7 @@ export class GpgME {
}
deleteKey(key, delete_secret = false, no_confirm = false){
- return Promise.reject(new GPGMEJS_Error ('NOT_YET_IMPLEMENTED'));
+ return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
let msg = new GPGME_Message('deletekey');
let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){
@@ -127,7 +126,7 @@ export class GpgME {
case 'ERR_NO_ERROR':
return Promise.resolve('okay'); //TBD
default:
- return Promise.reject(new GPGMEJS_Error);
+ return Promise.reject(GPGMEJS_Error('TODO') ); //
// INV_VALUE,
// GPG_ERR_NO_PUBKEY,
// GPG_ERR_AMBIGUOUS_NAME,
@@ -146,11 +145,9 @@ export class GpgME {
*/
function putData(message, data){
if (!message || !message instanceof GPGME_Message ) {
- return new GPGMEJS_Error('WRONGPARAM');
+ return GPGMEJS_Error('PARAM_WRONG');
}
if (!data){
- //TODO Debug only! No data is legitimate
- console.log('Warning. no data in message');
message.setParameter('data', '');
} else if (data instanceof Uint8Array){
let decoder = new TextDecoder('utf8');
@@ -167,6 +164,6 @@ function putData(message, data){
message.setParameter ('data', decoder.decode(txt));
}
} else {
- return new GPGMEJS_Error('WRONGPARAM');
+ return GPGMEJS_Error('PARAM_WRONG');
}
}
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index 23076569..e32f43a3 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -88,14 +88,14 @@
|| signature !== null
|| returnSessionKey !== null
|| date !== null){
- return Promise.reject(new GPMGEJS_Error('NOT_IMPLEMENTED'));
+ return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| filename
|| compression
|| armor === false
|| detached == true){
- return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+ return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
}
return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
}
@@ -123,14 +123,14 @@
if (passwords !== undefined
|| sessionKeys
|| date){
- return Promise.reject(new GPGMEJS_Error('NOT_IMPLEMENTED'));
+ return Promise.reject(GPGMEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
|| publicKeys
|| format !== 'utf8'
|| signature
){
- return Promise.reject(new GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+ return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
}
return this.GpgME.decrypt(message);
// TODO: translate between:
@@ -185,7 +185,7 @@ class GPGME_Keyring_openpgpmode {
else {
// TODO: Can there be "no default key"?
// TODO: Can there be several default keys?
- return new GPGMEJS_Error; //TODO
+ return GPGMEJS_Error('TODO');
}
});
}
@@ -202,10 +202,10 @@ class GPGME_Keyring_openpgpmode {
*/
deleteKey(key){
if (typeof(key) !== "object"){
- return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ return Promise.reject(GPGMEJS_Error('PARAM_WRONG'));
}
if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
- return Promise.reject(new GPGMEJS_Error('WRONGPARAM'));
+ return Promise.reject(GPGMEJS_Error('PARAM_WRONG'));
}
let key_to_delete = new GPGME_Key(key.fingerprint);
return key_to_delete.deleteKey(key.secret);
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 0cb2301c..a54277c2 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -19,6 +19,7 @@
*/
import { GpgME } from "./gpgmejs";
+import { GPGMEJS_Error } from "./Errors";
import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs";
import { Connection } from "./Connection";
@@ -45,7 +46,7 @@ function init( config = {
resolve(new GpgME(connection));
}
} else {
- reject('NO_CONNECT');
+ reject(GPGMEJS_Error('CONN_NO_CONNECT'));
}
};
setTimeout(delayedreaction, 5);
--
cgit v1.2.3
From 5befa1c9751fe54b5ae87906d7f09772ce9de6ea Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Wed, 25 Apr 2018 11:32:21 +0200
Subject: js: reactivate timeout on connection
--
* A timeout of 5 seconds is activated for functions that do not require
a pinentry. This definition is written to src/permittedOperations.js
* testapplication.js now alerts the proper error codes and messages.
* src/Errors.js fixed two typos in error handling
---
lang/js/src/Connection.js | 20 ++++++++++++--------
lang/js/src/Errors.js | 4 ++--
lang/js/src/permittedOperations.js | 3 +++
lang/js/testapplication.js | 4 ++--
4 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 8bc3d42a..4270be58 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -118,14 +118,18 @@ export class Connection{
};
me._connection.onMessage.addListener(listener);
- me._connection.postMessage(message.message);
-
- //TBD: needs to be aware if there is a pinentry pending
- // setTimeout(
- // function(){
- // me.disconnect();
- // reject(GPGMEJS_Error('CONN_TIMEOUT'));
- // }, timeout);
+ let timeout = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ reject(GPGMEJS_Error('CONN_TIMEOUT'));
+ }, 5000);
+ });
+ if (permittedOperations[message.operation].pinentry){
+ return me._connection.postMessage(message.message);
+ } else {
+ return Promise.race([timeout,
+ me._connection.postMessage(message.message)
+ ]);
+ }
});
}
};
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index c49bfe21..04b13e10 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -104,12 +104,12 @@ export function GPGMEJS_Error(code = 'GENERIC_ERROR'){
if (errors.hasOwnProperty(code)){
code = 'GENERIC_ERROR';
}
- if (error.type === 'error'){
+ if (errors.type === 'error'){
return {code: 'code',
msg: errors[code].msg
};
}
- if (error.type === 'warning'){
+ if (errors.type === 'warning'){
console.log(code + ': ' + error[code].msg);
}
return undefined;
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index 3c11b8e0..892f4f2e 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -23,6 +23,8 @@
* operation:
required: Array
optional: Array
+ pinentry: Boolean If a pinentry dialog is expected, and a timeout of
+ 5000 ms would be too short
answer:
type: The payload property of the answer. May be
@@ -59,6 +61,7 @@ export const permittedOperations = {
},
decrypt: {
+ pinentry: true,
required: ['data'],
optional: [
'protocol',
diff --git a/lang/js/testapplication.js b/lang/js/testapplication.js
index f47299e8..b2cb4c23 100644
--- a/lang/js/testapplication.js
+++ b/lang/js/testapplication.js
@@ -33,7 +33,7 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('answer').value = answer.data;
}
}, function(errormsg){
- alert('Error: '+ errormsg);
+ alert( errormsg.code + ' ' + errormsg.msg);
});
});
@@ -47,7 +47,7 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('answer').value = answer.data;
}
}, function(errormsg){
- alert('Error: '+ errormsg);
+ alert( errormsg.code + ' ' + errormsg.msg);
});
});
},
--
cgit v1.2.3
From 1fb310cabe578625f96fce5d84ff6f0092c08d24 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Wed, 25 Apr 2018 15:59:36 +0200
Subject: js: Configuration and Error handling
--
* gpgmejs_openpgpjs
- unsuported values with no negative consequences can now reject,
warn or be ignored, according to config.unconsidered_params
- cleanup of unsupported/supported parameters and TODOS
* A src/index.js init() now accepts a configuration object
* Errors will now be derived from Error, offering more info and a
stacktrace.
* Fixed Connection.post() timeout triggering on wrong cases
* Added comments in permittedOperations.js, which gpgme interactions
are still unimplemented and should be added next
---
lang/js/src/Connection.js | 39 +++----
lang/js/src/Errors.js | 208 +++++++++++++++++++++----------------
lang/js/src/Helpers.js | 12 +--
lang/js/src/Key.js | 44 ++++----
lang/js/src/Keyring.js | 13 ++-
lang/js/src/Message.js | 14 +--
lang/js/src/gpgmejs.js | 19 ++--
lang/js/src/gpgmejs_openpgpjs.js | 106 +++++++++++--------
lang/js/src/index.js | 9 +-
lang/js/src/permittedOperations.js | 52 +++++++++-
10 files changed, 309 insertions(+), 207 deletions(-)
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 4270be58..5b092ab0 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -24,7 +24,7 @@
* expected.
*/
import { permittedOperations } from './permittedOperations'
-import { GPGMEJS_Error } from "./Errors"
+import { gpgme_error } from "./Errors"
import { GPGME_Message } from "./Message";
/**
@@ -62,7 +62,7 @@ export class Connection{
*/
connect(){
if (this._isConnected === true){
- GPGMEJS_Error('CONN_ALREADY_CONNECTED');
+ gpgme_error('CONN_ALREADY_CONNECTED');
} else {
this._isConnected = true;
this._connection = chrome.runtime.connectNative('gpgmejson');
@@ -83,13 +83,13 @@ export class Connection{
*/
post(message){
if (!this.isConnected){
- return Promise.reject(GPGMEJS_Error('CONN_NO_CONNECT'));
+ return Promise.reject(gpgme_error('CONN_NO_CONNECT'));
}
if (!message || !message instanceof GPGME_Message){
- return Promise.reject(GPGMEJS_Error('PARAM_WRONG'), message);
+ return Promise.reject(gpgme_error('PARAM_WRONG'), message);
}
if (message.isComplete !== true){
- return Promise.reject(GPGMEJS_Error('MSG_INCOMPLETE'));
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
}
let me = this;
return new Promise(function(resolve, reject){
@@ -97,7 +97,7 @@ export class Connection{
let listener = function(msg) {
if (!msg){
me._connection.onMessage.removeListener(listener)
- reject(GPGMEJS_Error('CONN_EMPTY_GPG_ANSWER'));
+ reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener)
reject(
@@ -118,17 +118,18 @@ export class Connection{
};
me._connection.onMessage.addListener(listener);
- let timeout = new Promise(function(resolve, reject){
- setTimeout(function(){
- reject(GPGMEJS_Error('CONN_TIMEOUT'));
- }, 5000);
- });
if (permittedOperations[message.operation].pinentry){
return me._connection.postMessage(message.message);
} else {
- return Promise.race([timeout,
- me._connection.postMessage(message.message)
- ]);
+ return Promise.race([
+ me._connection.postMessage(message.message),
+ function(resolve, reject){
+ setTimeout(function(){
+ reject(gpgme_error('CONN_TIMEOUT'));
+ }, 5000);
+ }]).then(function(result){
+ return result;
+ });
}
});
}
@@ -148,7 +149,7 @@ class Answer{
/**
* Add the information to the answer
* @param {Object} msg The message as received with nativeMessaging
- * returns true if successfull, GPGMEJS_Error otherwise
+ * returns true if successfull, gpgme_error otherwise
*/
add(msg){
if (this._response === undefined){
@@ -157,14 +158,14 @@ class Answer{
let messageKeys = Object.keys(msg);
let poa = permittedOperations[this.operation].answer;
if (messageKeys.length === 0){
- return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER');
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
for (let i= 0; i < messageKeys.length; i++){
let key = messageKeys[i];
switch (key) {
case 'type':
if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
- return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER');
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
break;
case 'more':
@@ -183,7 +184,7 @@ class Answer{
this._response[key] = msg[key];
}
else if (this._response[key] !== msg[key]){
- return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER',msg[key]);
+ return gpgme_error('CONN_UNEXPECTED_ANSWER',msg[key]);
}
}
//infos may be json objects etc. Not yet defined.
@@ -195,7 +196,7 @@ class Answer{
this._response.push(msg[key]);
}
else {
- return GPGMEJS_Error('CONN_UNEXPECTED_ANSWER', key);
+ return gpgme_error('CONN_UNEXPECTED_ANSWER', key);
}
break;
}
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index 04b13e10..2f53aa89 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -18,99 +18,125 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
+const err_list = {
+ // Connection
+ 'CONN_NO_CONNECT': {
+ msg:'Connection with the nativeMessaging host could not be'
+ + ' established.',
+ type: 'error'
+ },
+ 'CONN_DISCONNECTED': {
+ msg:'Connection with the nativeMessaging host was lost.',
+ type: 'error'
+ },
+ 'CONN_EMPTY_GPG_ANSWER':{
+ msg: 'The nativeMessaging answer was empty.',
+ type: 'error'
+ },
+ 'CONN_TIMEOUT': {
+ msg: 'A connection timeout was exceeded.',
+ type: 'error'
+ },
+ 'CONN_UNEXPECTED_ANSWER': {
+ msg: 'The answer from gnupg was not as expected.',
+ type: 'error'
+ },
+ 'CONN_ALREADY_CONNECTED':{
+ msg: 'A connection was already established.',
+ type: 'warning'
+ },
+ // Message/Data
+ 'MSG_INCOMPLETE': {
+ msg: 'The Message did not match the minimum requirements for'
+ + ' the interaction.',
+ type: 'error'
+ },
+ 'MSG_EMPTY' : {
+ msg: 'The Message is empty.',
+ type: 'error'
+ },
+ 'MSG_OP_PENDING': {
+ msg: 'There is no operation specified yet. The parameter cannot'
+ + ' be set',
+ type: 'warning'
+ },
+ 'MSG_WRONG_OP': {
+ msg: 'The operation requested could not be found',
+ type: 'warning'
+ },
+ 'MSG_NO_KEYS' : {
+ msg: 'There were no valid keys provided.',
+ type: 'warning'
+ },
+ 'MSG_NOT_A_FPR': {
+ msg: 'The String is not an accepted fingerprint',
+ type: 'warning'
+ },
+ 'KEY_INVALID': {
+ msg:'Key object is invalid',
+ type: 'error'
+ },
+ // generic
+ 'PARAM_WRONG':{
+ msg: 'invalid parameter was found',
+ type: 'error'
+ },
+ 'PARAM_IGNORED': {
+ msg: 'An parameter was set that has no effect in gpgmejs',
+ type: 'warning'
+ },
+ 'NOT_IMPLEMENTED': {
+ msg: 'A openpgpjs parameter was submitted that is not implemented',
+ type: 'error'
+ },
+ 'NOT_YET_IMPLEMENTED': {
+ msg: 'Support of this is probable, but it is not implemented yet',
+ type: 'error'
+ },
+ 'GENERIC_ERROR': {
+ msg: 'Unspecified error',
+ type: 'error'
+ }
+};
+
/**
- * Checks the given error code and returns some information about it's meaning
- * @param {String} code The error code
- * @returns {Object} An object containing string properties code and msg
- * TODO: error-like objects with the code 'GNUPG_ERROR' are errors sent
- * directly by gnupg as answer in Connection.post()
+ * Checks the given error code and returns an error object with some
+ * information about meaning and origin
+ * @param {*} code Error code. Should be in err_list or 'GNUPG_ERROR'
+ * @param {*} info Error message passed through if code is 'GNUPG_ERROR'
*/
-export function GPGMEJS_Error(code = 'GENERIC_ERROR'){
- if (!typeof(code) === 'string'){
- code = 'GENERIC_ERROR';
- }
- let errors = { //TODO: someplace else
- // Connection
- 'CONN_NO_CONNECT': {
- msg:'Connection with the nativeMessaging host could not be'
- + ' established.',
- type: 'error'
- },
- 'CONN_EMPTY_GPG_ANSWER':{
- msg: 'The nativeMessaging answer was empty.',
- type: 'error'
- },
- 'CONN_TIMEOUT': {
- msg: 'A connection timeout was exceeded.',
- type: 'error'
- },
- 'CONN_UNEXPECTED_ANSWER': {
- msg: 'The answer from gnupg was not as expected.',
- type: 'error'
- },
- 'CONN_ALREADY_CONNECTED':{
- msg: 'A connection was already established.',
- type: 'warn'
- },
- // Message/Data
- 'MSG_INCOMPLETE': {
- msg: 'The Message did not match the minimum requirements for'
- + ' the interaction.',
- type: 'error'
- },
- 'MSG_EMPTY' : {
- msg: 'The Message is empty.',
- type: 'error'
- },
- 'MSG_OP_PENDING': {
- msg: 'There is no operation specified yet. The parameter cannot'
- + ' be set',
- type: 'warning'
- },
- 'MSG_WRONG_OP': {
- msg: 'The operation requested could not be found',
- type: 'warning'
- },
- 'MSG_NO_KEYS' : {
- msg: 'There were no valid keys provided.',
- type: 'warn'
- },
- 'MSG_NOT_A_FPR': {
- msg: 'The String is not an accepted fingerprint',
- type: 'warn'
- },
-
- // generic
- 'PARAM_WRONG':{
- msg: 'invalid parameter was found',
- type: 'error'
- },
- 'NOT_IMPLEMENTED': {
- msg: 'A openpgpjs parameter was submitted that is not implemented',
- type: 'error'
- },
- 'NOT_YET_IMPLEMENTED': {
- msg: 'Support of this is probable, but it is not implemented yet',
- type: 'error'
- },
- 'GENERIC_ERROR': {
- msg: 'Unspecified error',
- type: 'error'
- },
- }
- if (code === 'TODO'){
- alert('TODO_Error!');
+export function gpgme_error(code = 'GENERIC_ERROR', info){
+ if (err_list.hasOwnProperty(code)){
+ if (err_list[code].type === 'error'){
+ return new GPGME_Error(code);
}
- if (errors.hasOwnProperty(code)){
- code = 'GENERIC_ERROR';
+ if (err_list[code].type === 'warning'){
+ console.log(new GPGME_Error(code));
}
- if (errors.type === 'error'){
- return {code: 'code',
- msg: errors[code].msg
- };
- }
- if (errors.type === 'warning'){
- console.log(code + ': ' + error[code].msg);
- }
- return undefined;
+ return null;
+ } else if (code === 'GNUPG_ERROR'){
+ return new GPGME_Error(code, info.msg);
+ }
+ else {
+ return new GPGME_Error('GENERIC_ERROR');
+ }
}
+
+class GPGME_Error extends Error{
+ constructor(code, msg=''){
+ if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){
+ super(msg);
+ } else if (err_list.hasOwnProperty(code)){
+ super(err_list[code].msg);
+ } else {
+ super(err_list['GENERIC_ERROR'].msg);
+ }
+ this.code = code || 'GENERIC_ERROR';
+ }
+ set code(value){
+ this._code = value;
+ }
+ get code(){
+ return this._code;
+ }
+}
\ No newline at end of file
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index d9750ba7..841c0eda 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -17,7 +17,7 @@
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
-import { GPGMEJS_Error } from "./Errors";
+import { gpgme_error } from "./Errors";
/**
* Tries to return an array of fingerprints, either from input fingerprints or
@@ -28,7 +28,7 @@ import { GPGMEJS_Error } from "./Errors";
export function toKeyIdArray(input, nocheck){
if (!input){
- GPGMEJS_Error('MSG_NO_KEYS');
+ gpgme_error('MSG_NO_KEYS');
return [];
}
if (!Array.isArray(input)){
@@ -40,7 +40,7 @@ export function toKeyIdArray(input, nocheck){
if (isFingerprint(input[i]) === true){
result.push(input[i]);
} else {
- GPGMEJS_Error('MSG_NOT_A_FPR');
+ gpgme_error('MSG_NOT_A_FPR');
}
} else if (typeof(input[i]) === 'object'){
let fpr = '';
@@ -53,14 +53,14 @@ export function toKeyIdArray(input, nocheck){
if (isFingerprint(fpr) === true){
result.push(fpr);
} else {
- GPGMEJS_Error('MSG_NOT_A_FPR');
+ gpgme_error('MSG_NOT_A_FPR');
}
} else {
- return GPGMEJS_Error('PARAM_WRONG');
+ return gpgme_error('PARAM_WRONG');
}
}
if (result.length === 0){
- GPGMEJS_Error('MSG_NO_KEYS');
+ gpgme_error('MSG_NO_KEYS');
return [];
} else {
return result;
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 5ae80438..f6fa7ae3 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -27,7 +27,9 @@
*/
import {isFingerprint} from './Helpers'
-import {GPGMEJS_Error} from './Errors'
+import {gpgme_error} from './Errors'
+import { GPGME_Message } from './Message';
+import { permittedOperations } from './permittedOperations';
export class GPGME_Key {
@@ -172,32 +174,30 @@ export class GPGME_Key {
*
*/
function checkKey(fingerprint, property){
- return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
-
+ return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
+ if (!property ||
+ permittedOperations[keyinfo].indexOf(property) < 0){
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
return new Promise(function(resolve, reject){
if (!isFingerprint(fingerprint)){
- reject('not a fingerprint'); //TBD
+ reject('KEY_INVALID');
}
- let conn = new Connection();
- conn.post('getkey',{ // TODO not yet implemented in gpgme
- 'fingerprint': this.fingerprint})
- .then(function(result){
- if (property !== undefined){
- if (result.hasOwnProperty(key)){
- resolve(result[property]);
- }
- else if (property == 'secret'){
- // property undefined means "not true" in case of secret
- resolve(false);
- } else {
- reject('ERR_INVALID_PROPERTY') //TBD
- }
+ let msg = new GPGME_Message('keyinfo');
+ msg.setParameter('fingerprint', this.fingerprint);
+ return (this.connection.post(msg)).then(function(result){
+ if (result.hasOwnProperty(property)){
+ resolve(result[property]);
+ }
+ else if (property == 'secret'){
+ // TBD property undefined means "not true" in case of secret?
+ resolve(false);
+ } else {
+ reject(gpgme_error('CONN_UNEXPECTED_ANSWER'));
}
-
-
- resolve(result);
}, function(error){
- reject(error);
+ reject({code: 'GNUPG_ERROR',
+ msg: error.msg});
});
});
};
\ No newline at end of file
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index ef8028ff..e1f0a50f 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -21,6 +21,7 @@
import {GPGME_Message} from './Message'
import {GPGME_Key} from './Key'
import { isFingerprint, isLongId } from './Helpers';
+import { gpgme_error } from './Errors';
export class GPGME_Keyring {
constructor(connection){
@@ -37,9 +38,9 @@ export class GPGME_Keyring {
if (this._connection.isConnected){
return this._connection;
}
- return undefined; //TODO: connection was lost!
+ return gpgme_error('CONN_DISCONNECTED');
}
- return undefined; //TODO: no connection there
+ return gpgme_error('CONN_NO_CONNECT');
}
/**
@@ -81,9 +82,7 @@ export class GPGME_Keyring {
* filters described below. True will filter on the condition, False will
* reverse the filter, if not present or undefined, the filter will not be
* considered. Please note that some combination may not make sense
- * @param {Boolean} flags.defaultKey Only Keys marked as Default Keys
* @param {Boolean} flags.secret Only Keys containing a secret part.
- * @param {Boolean} flags.valid Valid Keys only
* @param {Boolean} flags.revoked revoked Keys only
* @param {Boolean} flags.expired Expired Keys only
* @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
@@ -108,16 +107,20 @@ export class GPGME_Keyring {
} else if (secretflag === false){
anticonditions.push('hasSecret');
}
+ /**
if (flags.defaultKey === true){
conditions.push('isDefault');
} else if (flags.defaultKey === false){
anticonditions.push('isDefault');
}
- if (flags.valid === true){
+ */
+ /**
+ * if (flags.valid === true){
anticonditions.push('isInvalid');
} else if (flags.valid === false){
conditions.push('isInvalid');
}
+ */
if (flags.revoked === true){
conditions.push('isRevoked');
} else if (flags.revoked === false){
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 1b36f11d..06ac8db2 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -18,7 +18,7 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
import { permittedOperations } from './permittedOperations'
-import { GPGMEJS_Error } from './Errors'
+import { gpgme_error } from './Errors'
export class GPGME_Message {
//TODO getter
@@ -39,20 +39,20 @@ export class GPGME_Message {
*/
setParameter(param,value){
if (!param || typeof(param) !== 'string'){
- return GPGMEJS_Error('PARAM_WRONG');
+ return gpgme_error('PARAM_WRONG');
}
if (!this._msg || !this._msg.op){
- return GPGMEJS_Error('MSG_OP_PENDING');
+ return gpgme_error('MSG_OP_PENDING');
}
let po = permittedOperations[this._msg.op];
if (!po){
- return GPGMEJS_Error('MSG_WRONG_OP');
+ return gpgme_error('MSG_WRONG_OP');
}
if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){
this._msg[param] = value;
return true;
}
- return GPGMEJS_Error('PARAM_WRONG');
+ return gpgme_error('PARAM_WRONG');
}
/**
@@ -98,7 +98,7 @@ export class GPGME_Message {
*/
function setOperation (scope, operation){
if (!operation || typeof(operation) !== 'string'){
- return GPGMEJS_Error('PARAM_WRONG');
+ return gpgme_error('PARAM_WRONG');
}
if (permittedOperations.hasOwnProperty(operation)){
if (!scope._msg){
@@ -106,6 +106,6 @@ function setOperation (scope, operation){
}
scope._msg.op = operation;
} else {
- return GPGMEJS_Error('MSG_WRONG_OP');
+ return gpgme_error('MSG_WRONG_OP');
}
}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index b20ff0f2..b504a457 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -21,7 +21,7 @@
import {Connection} from "./Connection"
import {GPGME_Message} from './Message'
import {toKeyIdArray} from "./Helpers"
-import {GPGMEJS_Error as Error, GPGMEJS_Error} from "./Errors"
+import { gpgme_error } from "./Errors"
import { GPGME_Keyring } from "./Keyring";
export class GpgME {
@@ -35,10 +35,12 @@ export class GpgME {
set connection(connection){
if (this._connection instanceof Connection){
- //TODO Warning: Connection already established
+ gpgme_error('CONN_ALREADY_CONNECTED');
}
if (connection instanceof Connection){
this._connection = connection;
+ } else {
+ gpgme_error('PARAM_WRONG');
}
}
@@ -54,11 +56,12 @@ export class GpgME {
set Keyring(keyring){
if (ring && ring instanceof GPGME_Keyring){
- this.Keyring = ring;
+ this._Keyring = ring;
}
}
get Keyring(){
+ return this._Keyring;
}
/**
@@ -96,7 +99,7 @@ export class GpgME {
decrypt(data){
if (data === undefined){
- return Promise.reject(GPGMEJS_Error('MSG_EMPTY'));
+ return Promise.reject(gpgme_error('MSG_EMPTY'));
}
let msg = new GPGME_Message('decrypt');
putData(msg, data);
@@ -105,7 +108,7 @@ export class GpgME {
}
deleteKey(key, delete_secret = false, no_confirm = false){
- return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+ return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
let msg = new GPGME_Message('deletekey');
let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){
@@ -126,7 +129,7 @@ export class GpgME {
case 'ERR_NO_ERROR':
return Promise.resolve('okay'); //TBD
default:
- return Promise.reject(GPGMEJS_Error('TODO') ); //
+ return Promise.reject(gpgme_error('TODO') ); //
// INV_VALUE,
// GPG_ERR_NO_PUBKEY,
// GPG_ERR_AMBIGUOUS_NAME,
@@ -145,7 +148,7 @@ export class GpgME {
*/
function putData(message, data){
if (!message || !message instanceof GPGME_Message ) {
- return GPGMEJS_Error('PARAM_WRONG');
+ return gpgme_error('PARAM_WRONG');
}
if (!data){
message.setParameter('data', '');
@@ -164,6 +167,6 @@ function putData(message, data){
message.setParameter ('data', decoder.decode(txt));
}
} else {
- return GPGMEJS_Error('PARAM_WRONG');
+ return gpgme_error('PARAM_WRONG');
}
}
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index e32f43a3..4e5e1ea0 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -28,13 +28,13 @@
import {GPGME_Keyring} from "./Keyring"
import { GPGME_Key } from "./Key";
import { isFingerprint } from "./Helpers"
- import { GPGMEJS_Error } from './Errors'
+ import { gpgme_error } from "./Errors"
export class GpgME_openpgpmode {
- constructor(connection){
- this.initGpgME(connection);
+ constructor(connection, config = {}){
+ this.initGpgME(connection, config);
}
get Keyring(){
@@ -44,9 +44,16 @@
return undefined;
}
- initGpgME(connection){
- this._GpgME = new GpgME(connection);
- this._Keyring = new GPGME_Keyring_openpgpmode(connection);
+ initGpgME(connection, config = {}){
+ if (connection && typeof(config) ==='object'){
+ this._config = config;
+ if (!this._GPGME){
+ this._GpgME = new GpgME(connection, config);
+ }
+ if (!this._Keyring){
+ this._Keyring = new GPGME_Keyring_openpgpmode(connection);
+ }
+ }
}
get GpgME(){
@@ -59,19 +66,23 @@
* Encrypt Message
* Supported:
* @param {String|Uint8Array} data
+ * //an openpgp Message also accepted here. TODO: is this wanted?
* @param {Key|Array} publicKeys
+ * //Strings of Fingerprints
* @param {Boolean} wildcard
* TODO:
- * @param {Key|Array} privateKeys
- * @param {String} filename
- * @param {module:enums.compression} compression
- * @param {Boolean} armor
- * @param {Boolean} detached
+ * @param {Key|Array} privateKeys // -> encryptsign
+ * @param {module:enums.compression} compression //TODO accepts integer, if 0 (no compression) it won't compress
+ * @param {Boolean} armor // TODO base64 switch
+ * @param {Boolean} detached // --> encryptsign
* unsupported:
* @param {String|Array} passwords
* @param {Object} sessionKey
* @param {Signature} signature
* @param {Boolean} returnSessionKey
+ * @param {String} filename
+ *
+ * Can be set, but will be ignored:
*
* @returns {Promise}
* {data: ASCII armored message,
@@ -80,57 +91,66 @@
* @async
* @static
*/
- encrypt({data = '', publicKeys = '', privateKeys, passwords, sessionKey,
- filename, compression, armor=true, detached=false, signature=null,
- returnSessionKey=null, wildcard=false, date=null}) {
- if (passwords !== undefined
- || sessionKey !== undefined
+ encrypt({data = '', publicKeys = '', privateKeys, passwords=null,
+ sessionKey = null, filename, compression, armor=true, detached=false,
+ signature=null, returnSessionKey=null, wildcard=false, date=null}) {
+ if (passwords !== null
+ || sessionKey !== null
|| signature !== null
|| returnSessionKey !== null
- || date !== null){
+ || date !== null
+ ){
return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
if ( privateKeys
- || filename
|| compression
|| armor === false
|| detached == true){
- return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+ return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
+ }
+ if (filename){
+ if (this._config.unconsidered_params === 'warn'){
+ GPMGEJS_Error('PARAM_IGNORED');
+ } else if (this._config.unconsidered_params === 'error'){
+ return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
+ }
}
return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
}
/** Decrypt Message
- * supported
- * TODO: @param {Message} message TODO: for now it accepts an armored string only
+ * supported openpgpjs parameters:
+ * @param {Message|Uint8Array|String} message Message object from openpgpjs
* Unsupported:
* @param {String|Array} passwords
+ * @param {Key|Array} privateKeys
* @param {Object|Array} sessionKeys
- * @param {Date} date
-
- * TODO
- * @param {Key|Array} privateKey
- * @param {Key|Array} publicKeys
+ * Not yet supported, but planned
* @param {String} format (optional) return data format either as 'utf8' or 'binary'
* @param {Signature} signature (optional) detached signature for verification
-
+ * Ignored values: can be safely set, but have no effect
+ * @param {Date} date
+ * @param {Key|Array} publicKeys
+ *
* @returns {Promise} decrypted and verified message in the form:
* { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] }
* @async
* @static
*/
- decrypt({ message, privateKeys, passwords, sessionKeys, publicKeys, format='utf8', signature=null, date}) {
- if (passwords !== undefined
- || sessionKeys
- || date){
- return Promise.reject(GPGMEJS_Error('NOT_IMPLEMENTED'));
+ decrypt({ message, privateKeys, passwords=null, sessionKeys,
+ publicKeys, format='utf8', signature=null, date= null}) {
+ if (passwords !== null || sessionKeys || privateKeys){
+ return Promise.reject(gpgme_error('NOT_IMPLEMENTED'));
}
- if ( privateKeys
- || publicKeys
- || format !== 'utf8'
- || signature
- ){
- return Promise.reject(GPGMEJS_Error('NOT_YET_IMPLEMENTED'));
+ if ( format !== 'utf8' || signature){
+ return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
+ }
+ if (date !== null || publicKeys){
+ if (this._config.unconsidered_params === 'warn'){
+ GPMGEJS_Error('PARAM_IGNORED');
+ } else if (this._config.unconsidered_params === 'reject'){
+ return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
+ }
}
return this.GpgME.decrypt(message);
// TODO: translate between:
@@ -185,7 +205,7 @@ class GPGME_Keyring_openpgpmode {
else {
// TODO: Can there be "no default key"?
// TODO: Can there be several default keys?
- return GPGMEJS_Error('TODO');
+ return gpgme_error('TODO');
}
});
}
@@ -202,10 +222,10 @@ class GPGME_Keyring_openpgpmode {
*/
deleteKey(key){
if (typeof(key) !== "object"){
- return Promise.reject(GPGMEJS_Error('PARAM_WRONG'));
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
}
if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
- return Promise.reject(GPGMEJS_Error('PARAM_WRONG'));
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
}
let key_to_delete = new GPGME_Key(key.fingerprint);
return key_to_delete.deleteKey(key.secret);
@@ -224,8 +244,8 @@ class GPGME_Key_openpgpmode {
set init (value){
if (!this._GPGME_Key && value instanceof GPGME_Key){
this._GPGME_Key = value;
- } else if (!this._GPGME_Key && isFingerprint(fpr)){
- this._GPGME_Key = new GPGME_Key;
+ } else if (!this._GPGME_Key && isFingerprint(value)){
+ this._GPGME_Key = new GPGME_Key(value);
}
}
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index a54277c2..48904316 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -19,7 +19,7 @@
*/
import { GpgME } from "./gpgmejs";
-import { GPGMEJS_Error } from "./Errors";
+import { gpgme_error } from "./Errors";
import { GpgME_openpgpmode } from "./gpgmejs_openpgpjs";
import { Connection } from "./Connection";
@@ -29,7 +29,8 @@ import { Connection } from "./Connection";
*/
function init( config = {
api_style: 'gpgme', // | gpgme_openpgpjs
- null_expire_is_never: true // Boolean
+ null_expire_is_never: true, // Boolean
+ unconsidered_params: 'warn'//'warn' || 'reject'
}){
return new Promise(function(resolve, reject){
let connection = new Connection;
@@ -41,12 +42,12 @@ function init( config = {
let gpgme = null;
if (config.api_style && config.api_style === 'gpgme_openpgpjs'){
resolve(
- new GpgME_openpgpmode(connection));
+ new GpgME_openpgpmode(connection, config));
} else {
resolve(new GpgME(connection));
}
} else {
- reject(GPGMEJS_Error('CONN_NO_CONNECT'));
+ reject(gpgme_error('CONN_NO_CONNECT'));
}
};
setTimeout(delayedreaction, 5);
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index 892f4f2e..79e74223 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -31,7 +31,7 @@
partial and in need of concatenation
params: Array Information that do not change throughout
the message
- infos: Array arbitrary information that may change
+ infos: Array<*> arbitrary information that may result in a list
}
}
*/
@@ -72,7 +72,55 @@ export const permittedOperations = {
type: ['plaintext'],
data: ['data'],
params: ['base64', 'mime'],
- infos: ['info']
+ infos: [] // pending. Info about signatures and validity
+ //signature: [{Key Fingerprint, valid Boolean}]
+ }
+ },
+ /**
+ keyinfo: { // querying the Key's information.
+ required: ['fingerprint'],
+ anser: {
+ type: ['TBD'],
+ data: [],
+ params: ['hasSecret', 'isRevoked', 'isExpired', 'armored',
+ 'timestamp', 'expires', 'pubkey_algo'],
+ infos: ['subkeys', 'userIds']
+ }*/
+
+ /**
+ listkeys:{
+ optional: ['with-secret', 'pattern'],
+ answer: {
+ type: ['TBD'], //Array of fingerprints?
+ infos: ['TBD'] //the property with infos
+ },
+ */
+
+ /**
+ importkey: {
+ required: ['keyarmored'],
+ answer: {
+ type: ['TBD'],
+ infos: [''], // for each key if import was a success, if it was an update
+ }
+ },
+ */
+
+ /**
+ deletekey: {
+ required: ['fingerprint'],
+ answer: {
+ type ['TBD'],
+ infos: [''] //success:true? in gpgme, an error NO_ERROR is returned
}
}
+ */
+
+ /**
+ *get armored secret different treatment from keyinfo!
+ */
+
+ /**
+ * TBD key modification requests?
+ */
}
--
cgit v1.2.3
From 3685913bf510a14b8cb324d980217d90489e6453 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Wed, 25 Apr 2018 19:45:39 +0200
Subject: js: First testing and improvements
--
* Introduced Mocha/chai as testsuite. After development build
'npm test' should run the unit tests. Functionality exclusive to
Browsers/WebExtensions cannot be run this way, so some other testing
is still needed.
- package.json: Added required development packages
- .babelrc indirect configuration for mocha. ES6 transpiling
needs some babel configuration, but mocha has no setting for it.
- test/mocha.opts Vonfiguration for mocha runs
* Fixed errors:
- Helpers.js toKeyIdArray; isLongId is now exported
- Key.js Key constructor failed
- Message.js will not throw an Error during construction, a new
message is now created with createMessage, which can return an
Error or a GPGME_Message object
* Tests:
- test/Helpers: exports from Helpers.js, GPGME_Error handling
- test/Message: first init test with bad parameters
---
lang/js/.babelrc | 1 +
lang/js/package.json | 6 ++-
lang/js/src/Connection.js | 2 +-
lang/js/src/Errors.js | 11 ++---
lang/js/src/Helpers.js | 7 +--
lang/js/src/Key.js | 15 ++++---
lang/js/src/Keyring.js | 7 ++-
lang/js/src/Message.js | 47 ++++++++++----------
lang/js/src/gpgmejs.js | 31 ++++++++-----
lang/js/test/Helpers.js | 110 ++++++++++++++++++++++++++++++++++++++++++++++
lang/js/test/Message.js | 42 ++++++++++++++++++
lang/js/test/mocha.opts | 4 ++
12 files changed, 227 insertions(+), 56 deletions(-)
create mode 100644 lang/js/.babelrc
create mode 100644 lang/js/test/Helpers.js
create mode 100644 lang/js/test/Message.js
create mode 100644 lang/js/test/mocha.opts
diff --git a/lang/js/.babelrc b/lang/js/.babelrc
new file mode 100644
index 00000000..9d8d5165
--- /dev/null
+++ b/lang/js/.babelrc
@@ -0,0 +1 @@
+{ "presets": ["es2015"] }
diff --git a/lang/js/package.json b/lang/js/package.json
index 2b7dd7ee..a794188a 100644
--- a/lang/js/package.json
+++ b/lang/js/package.json
@@ -5,13 +5,15 @@
"main": "src/index.js",
"private": true,
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "mocha"
},
"keywords": [],
"author": "",
"license": "",
"devDependencies": {
"webpack": "^4.5.0",
- "webpack-cli": "^2.0.14"
+ "webpack-cli": "^2.0.14",
+ "chai": "^4.1.2",
+ "mocha": "^5.1.1"
}
}
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 5b092ab0..a10f9d9a 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -83,7 +83,7 @@ export class Connection{
*/
post(message){
if (!this.isConnected){
- return Promise.reject(gpgme_error('CONN_NO_CONNECT'));
+ return Promise.reject(gpgme_error('CONN_DISCONNECTED'));
}
if (!message || !message instanceof GPGME_Message){
return Promise.reject(gpgme_error('PARAM_WRONG'), message);
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index 2f53aa89..d26aab18 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -55,14 +55,9 @@ const err_list = {
msg: 'The Message is empty.',
type: 'error'
},
- 'MSG_OP_PENDING': {
- msg: 'There is no operation specified yet. The parameter cannot'
- + ' be set',
- type: 'warning'
- },
'MSG_WRONG_OP': {
msg: 'The operation requested could not be found',
- type: 'warning'
+ type: 'error'
},
'MSG_NO_KEYS' : {
msg: 'There were no valid keys provided.',
@@ -78,7 +73,7 @@ const err_list = {
},
// generic
'PARAM_WRONG':{
- msg: 'invalid parameter was found',
+ msg: 'Invalid parameter was found',
type: 'error'
},
'PARAM_IGNORED': {
@@ -111,7 +106,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
return new GPGME_Error(code);
}
if (err_list[code].type === 'warning'){
- console.log(new GPGME_Error(code));
+ console.warn(code + ': ' + err_list[code].msg);
}
return null;
} else if (code === 'GNUPG_ERROR'){
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index 841c0eda..9a69f851 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -18,6 +18,7 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
import { gpgme_error } from "./Errors";
+import { GPGME_Key } from "./Key";
/**
* Tries to return an array of fingerprints, either from input fingerprints or
@@ -26,7 +27,7 @@ import { gpgme_error } from "./Errors";
* @returns {Array} Array of fingerprints.
*/
-export function toKeyIdArray(input, nocheck){
+export function toKeyIdArray(input){
if (!input){
gpgme_error('MSG_NO_KEYS');
return [];
@@ -46,7 +47,7 @@ export function toKeyIdArray(input, nocheck){
let fpr = '';
if (input[i] instanceof GPGME_Key){
fpr = input[i].fingerprint;
- } else if (input[i].hasOwnProperty(primaryKey) &&
+ } else if (input[i].hasOwnProperty('primaryKey') &&
input[i].primaryKey.hasOwnProperty(getFingerprint)){
fpr = input[i].primaryKey.getFingerprint();
}
@@ -92,7 +93,7 @@ export function isFingerprint(string){
/**
* check if the input is a valid Hex string with a length of 16
*/
-function isLongId(string){
+export function isLongId(string){
return hextest(string, 16);
};
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index f6fa7ae3..0b44b245 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -26,9 +26,9 @@
*
*/
-import {isFingerprint} from './Helpers'
-import {gpgme_error} from './Errors'
-import { GPGME_Message } from './Message';
+import { isFingerprint } from './Helpers'
+import { gpgme_error } from './Errors'
+import { createMessage } from './Message';
import { permittedOperations } from './permittedOperations';
export class GPGME_Key {
@@ -39,7 +39,7 @@ export class GPGME_Key {
set fingerprint(fpr){
if (isFingerprint(fpr) === true && !this._fingerprint){
- this._fingerprint = fingerprint;
+ this._fingerprint = fpr;
}
}
@@ -181,9 +181,12 @@ function checkKey(fingerprint, property){
}
return new Promise(function(resolve, reject){
if (!isFingerprint(fingerprint)){
- reject('KEY_INVALID');
+ reject(gpgme_error('KEY_INVALID'));
+ }
+ let msg = createMessage ('keyinfo');
+ if (msg instanceof Error){
+ reject(gpgme_error('PARAM_WRONG'));
}
- let msg = new GPGME_Message('keyinfo');
msg.setParameter('fingerprint', this.fingerprint);
return (this.connection.post(msg)).then(function(result){
if (result.hasOwnProperty(property)){
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index e1f0a50f..470eeeec 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -18,7 +18,7 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
-import {GPGME_Message} from './Message'
+import {createMessage} from './Message'
import {GPGME_Key} from './Key'
import { isFingerprint, isLongId } from './Helpers';
import { gpgme_error } from './Errors';
@@ -50,7 +50,10 @@ export class GPGME_Keyring {
*
*/
getKeys(pattern, include_secret){
- let msg = new GPGME_Message('listkeys');
+ let msg = createMessage('listkeys');
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
if (pattern && typeof(pattern) === 'string'){
msg.setParameter('pattern', pattern);
}
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 06ac8db2..4d242277 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -19,13 +19,34 @@
*/
import { permittedOperations } from './permittedOperations'
import { gpgme_error } from './Errors'
-export class GPGME_Message {
+
+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
+ */
+class GPGME_Message {
//TODO getter
constructor(operation){
- setOperation(this, operation);
+ this.operation = operation;
}
+ set operation (op){
+
+
+ }
get operation(){
return this._msg.op;
}
@@ -41,9 +62,6 @@ export class GPGME_Message {
if (!param || typeof(param) !== 'string'){
return gpgme_error('PARAM_WRONG');
}
- if (!this._msg || !this._msg.op){
- return gpgme_error('MSG_OP_PENDING');
- }
let po = permittedOperations[this._msg.op];
if (!po){
return gpgme_error('MSG_WRONG_OP');
@@ -90,22 +108,3 @@ export class GPGME_Message {
}
}
-
-/**
- * Defines the operation this message will have
- * @param {String} operation Must be defined in permittedOperations
- * TODO: move to constructor?
- */
-function setOperation (scope, operation){
- if (!operation || typeof(operation) !== 'string'){
- return gpgme_error('PARAM_WRONG');
- }
- if (permittedOperations.hasOwnProperty(operation)){
- if (!scope._msg){
- scope._msg = {};
- }
- scope._msg.op = operation;
- } else {
- return gpgme_error('MSG_WRONG_OP');
- }
-}
\ No newline at end of file
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index b504a457..2ddf2964 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -19,7 +19,7 @@
*/
import {Connection} from "./Connection"
-import {GPGME_Message} from './Message'
+import {GPGME_Message, createMessage} from './Message'
import {toKeyIdArray} from "./Helpers"
import { gpgme_error } from "./Errors"
import { GPGME_Keyring } from "./Keyring";
@@ -71,8 +71,10 @@ export class GpgME {
*/
encrypt(data, publicKeys, wildcard=false){
- let msg = new GPGME_Message('encrypt');
-
+ let msg = createMessage('encrypt');
+ if (msg instanceof Error){
+ return Promise.reject(msg)
+ }
// TODO temporary
msg.setParameter('armor', true);
msg.setParameter('always-trust', true);
@@ -101,7 +103,10 @@ export class GpgME {
if (data === undefined){
return Promise.reject(gpgme_error('MSG_EMPTY'));
}
- let msg = new GPGME_Message('decrypt');
+ let msg = createMessage('decrypt');
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
putData(msg, data);
return this.connection.post(msg);
@@ -109,21 +114,27 @@ export class GpgME {
deleteKey(key, delete_secret = false, no_confirm = false){
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
- let msg = new GPGME_Message('deletekey');
+ let msg = createMessage('deletekey');
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
let key_arr = toKeyIdArray(key);
if (key_arr.length !== 1){
- throw('TODO');
- //should always be ONE key
+ 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
+ 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
+ msg.setParameter('delete_force', true);
+ // TBD
}
this.connection.post(msg).then(function(success){
- //TODO: it seems that there is always errors coming back:
+ // TODO: it seems that there is always errors coming back:
}, function(error){
switch (error.msg){
case 'ERR_NO_ERROR':
diff --git a/lang/js/test/Helpers.js b/lang/js/test/Helpers.js
new file mode 100644
index 00000000..590f9f65
--- /dev/null
+++ b/lang/js/test/Helpers.js
@@ -0,0 +1,110 @@
+/* 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"
+
+const helper_params = {
+ validLongId: '0A0A0A0A0A0A0A0A',
+ validGPGME_Key: new GPGME_Key('ADDBC303B6D31026F5EB4591A27EABDF283121BB'),
+ validKeys: [new GPGME_Key('A1E3BC45BDC8E87B74F4392D53B151A1368E50F3'),
+ 'ADDBC303B6D31026F5EB4591A27EABDF283121BB',
+ new GPGME_Key('EE17AEE730F88F1DE7713C54BBE0A4FF7851650A')],
+ validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
+ invalidFingerprint: [{hello:'World'}],
+ invalidKeyArray: {curiosity:'uncat'},
+ invalidKeyArray_OneBad: [
+ new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'),
+ 'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
+ '3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
+ invalidErrorCode: 'Please type in all your passwords.'
+}
+
+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);
+ });
+ });
+});
diff --git a/lang/js/test/Message.js b/lang/js/test/Message.js
new file mode 100644
index 00000000..454b8ca3
--- /dev/null
+++ b/lang/js/test/Message.js
@@ -0,0 +1,42 @@
+/* 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";
+
+const message_params = {
+ invalid_op_action : 'dance',
+ invalid_op_type : [234, 34, '<>'],
+}
+
+describe('Message Object', function(){
+ describe('incorrect initialization', function(){
+ it('non-allowed operation', function(){
+ let test0 = createMessage(message_params.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);
+ expect(test0).to.be.an.instanceof(Error);
+ expect(test0.code).to.equal('PARAM_WRONG');
+ });
+ });
+});
diff --git a/lang/js/test/mocha.opts b/lang/js/test/mocha.opts
new file mode 100644
index 00000000..65adc1c3
--- /dev/null
+++ b/lang/js/test/mocha.opts
@@ -0,0 +1,4 @@
+--require babel-register
+--reporter spec
+--ui bdd
+--colors
--
cgit v1.2.3
From 1f7b19512cfa7e1b153b99d6a2b40bad82a5496e Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Thu, 26 Apr 2018 17:13:34 +0200
Subject: js: created TestExtension and smaller fixes
--
* Extensions:
- Moved testapplication to Demoextension
- Created BrowserTestExtension.
Includes mocha and chai. For running tests that cannot be run
outside a WebExtension
Both Extensions can be found zipped in build/extensions after
running build_extensions.sh
* Code changes:
- src/Config: Place for the configuration
- small fixes raised during testing in Keyring.js, Message.js,
- src/gpgmejs_openpgpjs.js don't offer direct GpgME object to the
outside, as it only causes confusion
- index.js init() now checks the config for validity
* Tests:
- Reordered tests in test/.
- Input values are now in a separate file which may be of use for
bulk testing
* moved the build directory from dist to build
---
lang/js/BrowserTestExtension/browsertest.html | 23 ++++
lang/js/BrowserTestExtension/manifest.json | 13 ++
lang/js/BrowserTestExtension/popup.html | 9 ++
lang/js/BrowserTestExtension/popup.js | 44 +++++++
lang/js/BrowserTestExtension/runbrowsertest.js | 21 ++++
lang/js/BrowserTestExtension/setup_testing.js | 22 ++++
lang/js/BrowserTestExtension/testicon.png | Bin 0 -> 16192 bytes
lang/js/BrowserTestExtension/tests/inputvalues.js | 28 +++++
lang/js/BrowserTestExtension/tests/startup.js | 51 ++++++++
lang/js/CHECKLIST | 8 +-
lang/js/CHECKLIST_build | 6 -
lang/js/DemoExtension/entry.js | 25 ++++
lang/js/DemoExtension/maindemo.js | 55 +++++++++
lang/js/DemoExtension/mainui.html | 33 +++++
lang/js/DemoExtension/manifest.json | 14 +++
lang/js/DemoExtension/popup.html | 9 ++
lang/js/DemoExtension/testicon.png | Bin 0 -> 16192 bytes
lang/js/DemoExtension/ui.css | 10 ++
lang/js/build_extensions.sh | 14 +++
lang/js/manifest.json | 14 ---
lang/js/src/Config.js | 31 +++++
lang/js/src/Keyring.js | 1 +
lang/js/src/Message.js | 2 +-
lang/js/src/gpgmejs_openpgpjs.js | 14 +--
lang/js/src/index.js | 70 +++++++----
lang/js/test/Helpers.js | 139 ++++++++++------------
lang/js/test/Message.js | 33 ++---
lang/js/test/inputvalues.js | 29 +++++
lang/js/test_index.js | 25 ----
lang/js/testapplication.js | 55 ---------
lang/js/testapplication_index.html | 9 --
lang/js/testicon.png | Bin 16192 -> 0 bytes
lang/js/ui.css | 10 --
lang/js/ui2.html | 33 -----
lang/js/webpack.conf.js | 2 +-
35 files changed, 569 insertions(+), 283 deletions(-)
create mode 100644 lang/js/BrowserTestExtension/browsertest.html
create mode 100644 lang/js/BrowserTestExtension/manifest.json
create mode 100644 lang/js/BrowserTestExtension/popup.html
create mode 100644 lang/js/BrowserTestExtension/popup.js
create mode 100644 lang/js/BrowserTestExtension/runbrowsertest.js
create mode 100644 lang/js/BrowserTestExtension/setup_testing.js
create mode 100644 lang/js/BrowserTestExtension/testicon.png
create mode 100644 lang/js/BrowserTestExtension/tests/inputvalues.js
create mode 100644 lang/js/BrowserTestExtension/tests/startup.js
create mode 100644 lang/js/DemoExtension/entry.js
create mode 100644 lang/js/DemoExtension/maindemo.js
create mode 100644 lang/js/DemoExtension/mainui.html
create mode 100644 lang/js/DemoExtension/manifest.json
create mode 100644 lang/js/DemoExtension/popup.html
create mode 100644 lang/js/DemoExtension/testicon.png
create mode 100644 lang/js/DemoExtension/ui.css
create mode 100755 lang/js/build_extensions.sh
delete mode 100644 lang/js/manifest.json
create mode 100644 lang/js/src/Config.js
create mode 100644 lang/js/test/inputvalues.js
delete mode 100644 lang/js/test_index.js
delete mode 100644 lang/js/testapplication.js
delete mode 100644 lang/js/testapplication_index.html
delete mode 100644 lang/js/testicon.png
delete mode 100644 lang/js/ui.css
delete mode 100644 lang/js/ui2.html
diff --git a/lang/js/BrowserTestExtension/browsertest.html b/lang/js/BrowserTestExtension/browsertest.html
new file mode 100644
index 00000000..d2c6396f
--- /dev/null
+++ b/lang/js/BrowserTestExtension/browsertest.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ Browsertest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lang/js/BrowserTestExtension/manifest.json b/lang/js/BrowserTestExtension/manifest.json
new file mode 100644
index 00000000..a9e605bc
--- /dev/null
+++ b/lang/js/BrowserTestExtension/manifest.json
@@ -0,0 +1,13 @@
+{
+ "manifest_version": 2,
+
+ "name": "Browsertests for gpgmejs",
+ "description": "Run the browsertests.",
+ "version": "0.1",
+ "content_security_policy": "default-src 'self' filesystem:",
+ "browser_action": {
+ "default_icon": "testicon.png",
+ "default_popup": "popup.html"
+ },
+ "permissions": ["nativeMessaging", "activeTab"]
+ }
diff --git a/lang/js/BrowserTestExtension/popup.html b/lang/js/BrowserTestExtension/popup.html
new file mode 100644
index 00000000..f17f262a
--- /dev/null
+++ b/lang/js/BrowserTestExtension/popup.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/popup.js b/lang/js/BrowserTestExtension/popup.js
new file mode 100644
index 00000000..4764df55
--- /dev/null
+++ b/lang/js/BrowserTestExtension/popup.js
@@ -0,0 +1,44 @@
+/* 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+
+ */
+/* 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+
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+ chrome.tabs.create({
+ url: './browsertest.html'
+ });
+});
diff --git a/lang/js/BrowserTestExtension/runbrowsertest.js b/lang/js/BrowserTestExtension/runbrowsertest.js
new file mode 100644
index 00000000..39bc3fb9
--- /dev/null
+++ b/lang/js/BrowserTestExtension/runbrowsertest.js
@@ -0,0 +1,21 @@
+/* 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+
+ */
+
+mocha.run();
diff --git a/lang/js/BrowserTestExtension/setup_testing.js b/lang/js/BrowserTestExtension/setup_testing.js
new file mode 100644
index 00000000..7f70d347
--- /dev/null
+++ b/lang/js/BrowserTestExtension/setup_testing.js
@@ -0,0 +1,22 @@
+/* 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+
+ */
+mocha.setup('bdd');
+var expect = chai.expect;
+chai.config.includeStack = true;
\ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/testicon.png b/lang/js/BrowserTestExtension/testicon.png
new file mode 100644
index 00000000..12c3f5df
Binary files /dev/null and b/lang/js/BrowserTestExtension/testicon.png differ
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
new file mode 100644
index 00000000..47600c84
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -0,0 +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+
+ */
+
+var inputvalues = {
+ encrypt: {
+ good:{
+ data : 'Hello World.',
+ keyid : 'CDC3A2B2860625CCBFC5A5A9FC6D1B604967FC40'
+ }
+ }
+};
diff --git a/lang/js/BrowserTestExtension/tests/startup.js b/lang/js/BrowserTestExtension/tests/startup.js
new file mode 100644
index 00000000..14d12c0a
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/startup.js
@@ -0,0 +1,51 @@
+/* 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(
+ 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);
+ });
+ });
+ 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);
+ });
+ });
+ });
diff --git a/lang/js/CHECKLIST b/lang/js/CHECKLIST
index 49b17265..75664ae5 100644
--- a/lang/js/CHECKLIST
+++ b/lang/js/CHECKLIST
@@ -8,8 +8,8 @@ receiving an answer
replicating Openpgpjs API:
- [*] Message handling (encrypt, verify, sign)
- [x] encrypt
+ [*] Message handling (encrypt, decrypt verify, sign)
+ [x] encrypt, decrypt
[ ] verify
[ ] sign
[*] Key handling (import/export, modifying, status queries)
@@ -23,6 +23,6 @@ Communication with other implementations
Management:
[*] Define the gpgme interface
- [ ] check Permissions (e.g. csp) for the different envs
+ [x] check Permissions (e.g. csp) for the different envs
[X] agree on license
- [ ] tests
+ [*] tests
diff --git a/lang/js/CHECKLIST_build b/lang/js/CHECKLIST_build
index 19eb2146..a7c8d08d 100644
--- a/lang/js/CHECKLIST_build
+++ b/lang/js/CHECKLIST_build
@@ -1,9 +1,3 @@
- Checklist for build/install:
browsers' manifests (see README) need allowedextension added, and the path set
-
-manifest.json/ csp needs adaption
-
-/dist should be built which is used by the example app.
-
-csp in manifest.json MUST NOT contain "unsafe-eval" in production!
diff --git a/lang/js/DemoExtension/entry.js b/lang/js/DemoExtension/entry.js
new file mode 100644
index 00000000..7e5e1ffe
--- /dev/null
+++ b/lang/js/DemoExtension/entry.js
@@ -0,0 +1,25 @@
+/* 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+
+ *
+ */
+document.addEventListener('DOMContentLoaded', function() {
+ chrome.tabs.create({
+ url: './uimainui.html'
+ });
+});
diff --git a/lang/js/DemoExtension/maindemo.js b/lang/js/DemoExtension/maindemo.js
new file mode 100644
index 00000000..b2cb4c23
--- /dev/null
+++ b/lang/js/DemoExtension/maindemo.js
@@ -0,0 +1,55 @@
+/* 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+
+ *
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+ Gpgmejs.init().then(function(gpgmejs){
+ document.getElementById("buttonencrypt").addEventListener("click",
+ function(){
+ let data = document.getElementById('cleartext').value;
+ let keyId = document.getElementById('pubkey').value;
+ gpgmejs.encrypt(data, keyId).then(
+ function(answer){
+ console.log(answer);
+ if (answer.data){
+ console.log(answer.data);
+ document.getElementById('answer').value = answer.data;
+ }
+ }, function(errormsg){
+ alert( errormsg.code + ' ' + errormsg.msg);
+ });
+ });
+
+ document.getElementById("buttondecrypt").addEventListener("click",
+ function(){
+ let data = document.getElementById("ciphertext").value;
+ gpgmejs.decrypt(data).then(
+ function(answer){
+ console.log(answer);
+ if (answer.data){
+ document.getElementById('answer').value = answer.data;
+ }
+ }, function(errormsg){
+ alert( errormsg.code + ' ' + errormsg.msg);
+ });
+ });
+ },
+ function(error){console.log(error)});
+});
diff --git a/lang/js/DemoExtension/mainui.html b/lang/js/DemoExtension/mainui.html
new file mode 100644
index 00000000..d85e7a46
--- /dev/null
+++ b/lang/js/DemoExtension/mainui.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+ Encrypt
+
+
+
+ Encrypted armored Text:
+
+
+
+ Decrypt
+
+ Result data:
+
+
+
diff --git a/lang/js/DemoExtension/manifest.json b/lang/js/DemoExtension/manifest.json
new file mode 100644
index 00000000..9e057b35
--- /dev/null
+++ b/lang/js/DemoExtension/manifest.json
@@ -0,0 +1,14 @@
+{
+ "manifest_version": 2,
+
+ "name": "gpgme-json with native Messaging",
+ "description": "A simple demo application",
+ "version": "0.1",
+ "content_security_policy": "default-src 'self' filesystem:",
+ "browser_action": {
+ "default_icon": "testicon.png",
+ "default_title": "gpgme.js",
+ "default_popup": "popup.html"
+ },
+ "permissions": ["nativeMessaging", "activeTab"]
+}
diff --git a/lang/js/DemoExtension/popup.html b/lang/js/DemoExtension/popup.html
new file mode 100644
index 00000000..866b1135
--- /dev/null
+++ b/lang/js/DemoExtension/popup.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lang/js/DemoExtension/testicon.png b/lang/js/DemoExtension/testicon.png
new file mode 100644
index 00000000..12c3f5df
Binary files /dev/null and b/lang/js/DemoExtension/testicon.png differ
diff --git a/lang/js/DemoExtension/ui.css b/lang/js/DemoExtension/ui.css
new file mode 100644
index 00000000..9c88698b
--- /dev/null
+++ b/lang/js/DemoExtension/ui.css
@@ -0,0 +1,10 @@
+ul {
+ list-style-type: none;
+ padding-left: 0px;
+}
+
+ul li span {
+ float: left;
+ width: 120px;
+ margin-top: 6px;
+}
diff --git a/lang/js/build_extensions.sh b/lang/js/build_extensions.sh
new file mode 100755
index 00000000..be7b0584
--- /dev/null
+++ b/lang/js/build_extensions.sh
@@ -0,0 +1,14 @@
+#/!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
+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/manifest.json b/lang/js/manifest.json
deleted file mode 100644
index e5e17aa5..00000000
--- a/lang/js/manifest.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "manifest_version": 2,
-
- "name": "gpgme-json with native Messaging",
- "description": "This should be able to encrypt a text using gpgme-json",
- "version": "0.1",
- "content_security_policy": "default-src 'self' 'unsafe-eval' filesystem:",
- "browser_action": {
- "default_icon": "testicon.png",
- "default_title": "gpgme.js",
- "default_popup": "testapplication_index.html"
- },
- "permissions": ["nativeMessaging", "activeTab"]
-}
diff --git a/lang/js/src/Config.js b/lang/js/src/Config.js
new file mode 100644
index 00000000..e18728de
--- /dev/null
+++ b/lang/js/src/Config.js
@@ -0,0 +1,31 @@
+/* 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+
+ */
+
+export const availableConf = {
+ api_style: ['gpgme', 'gpgme_openpgpjs'],
+ null_expire_is_never: [true, false],
+ unconsidered_params: ['warn','reject', 'ignore'],
+};
+
+export const defaultConf = {
+ api_style: 'gpgme',
+ null_expire_is_never: false,
+ unconsidered_params: 'reject',
+};
\ No newline at end of file
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index 470eeeec..364bfb46 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -22,6 +22,7 @@ import {createMessage} from './Message'
import {GPGME_Key} from './Key'
import { isFingerprint, isLongId } from './Helpers';
import { gpgme_error } from './Errors';
+import { Connection } from './Connection';
export class GPGME_Keyring {
constructor(connection){
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 4d242277..9e7a8835 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -36,7 +36,7 @@ export function createMessage(operation){
* ./permittedOperations.
* @param {String} operation
*/
-class GPGME_Message {
+export class GPGME_Message {
//TODO getter
constructor(operation){
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index 4e5e1ea0..cc2afde1 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -50,18 +50,12 @@
if (!this._GPGME){
this._GpgME = new GpgME(connection, config);
}
- if (!this._Keyring){
- this._Keyring = new GPGME_Keyring_openpgpmode(connection);
+ if (!this._keyring){
+ this._keyring = new GPGME_Keyring_openpgpmode(connection);
}
}
}
- get GpgME(){
- if (this._GpGME){
- return this._GpGME;
- }
- }
-
/**
* Encrypt Message
* Supported:
@@ -115,7 +109,7 @@
return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
}
- return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
+ return this._GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard);
}
/** Decrypt Message
@@ -152,7 +146,7 @@
return Promise.reject(GPMGEJS_Error('NOT_IMPLEMENTED'));
}
}
- return this.GpgME.decrypt(message);
+ return this._GpgME.decrypt(message);
// TODO: translate between:
// openpgp:
// { data:Uint8Array|String,
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 48904316..4de98457 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -22,36 +22,60 @@ 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 {*} conf Configuration. TBD
+ * @param {Object} config Configuration. See Config.js for available parameters. Still TODO
*/
-function init( config = {
- api_style: 'gpgme', // | gpgme_openpgpjs
- null_expire_is_never: true, // Boolean
- unconsidered_params: 'warn'//'warn' || 'reject'
- }){
- return new Promise(function(resolve, reject){
- let connection = new Connection;
- // TODO: Delayed reaction is ugly. We need to listen to the port's
- // event listener in isConnected, but this takes some time (<5ms) to
- // disconnect if there is no successfull connection.
- let delayedreaction = function(){
- if (connection.isConnected === true){
- let gpgme = null;
- if (config.api_style && config.api_style === 'gpgme_openpgpjs'){
- resolve(
- new GpgME_openpgpmode(connection, config));
- } else {
- resolve(new GpgME(connection));
- }
+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 {
- reject(gpgme_error('CONN_NO_CONNECT'));
+ resolve(new GpgME(connection));
}
- };
- setTimeout(delayedreaction, 5);
+ } else {
+ reject(gpgme_error('CONN_NO_CONNECT'));
+ }
+ };
+ setTimeout(delayedreaction, 5);
});
+}
+
+function parseconfiguration(config){
+ if (!config){
+ return defaultConf;
+ }
+ if ( typeof(config) !== 'object'){
+ return gpgme_error('PARAM_WRONG');
+ };
+ let result_config = defaultConf;
+ let conf_keys = Object.keys(config);
+ for (let i=0; i < conf_keys; i++){
+ if (availableConf.hasOwnProperty(conf_keys[i])){
+ let value = config[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');
+ }
+ }
+ return result_config;
};
export default {
diff --git a/lang/js/test/Helpers.js b/lang/js/test/Helpers.js
index 590f9f65..5d8909f9 100644
--- a/lang/js/test/Helpers.js
+++ b/lang/js/test/Helpers.js
@@ -22,89 +22,76 @@ 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";
-const helper_params = {
- validLongId: '0A0A0A0A0A0A0A0A',
- validGPGME_Key: new GPGME_Key('ADDBC303B6D31026F5EB4591A27EABDF283121BB'),
- validKeys: [new GPGME_Key('A1E3BC45BDC8E87B74F4392D53B151A1368E50F3'),
- 'ADDBC303B6D31026F5EB4591A27EABDF283121BB',
- new GPGME_Key('EE17AEE730F88F1DE7713C54BBE0A4FF7851650A')],
- validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
- invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
- invalidFingerprint: [{hello:'World'}],
- invalidKeyArray: {curiosity:'uncat'},
- invalidKeyArray_OneBad: [
- new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'),
- 'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
- '3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
- invalidErrorCode: 'Please type in all your passwords.'
-}
-
-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);
+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('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('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('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);
+
+ it('Warnings like PARAM_IGNORED should not return errors', function(){
+ let test0 = gpgme_error('PARAM_IGNORED');
+ expect(test0).to.be.null;
});
});
- describe('Incorrect inputs', function(){
- it('valid Long ID', function(){
- let test0 = toKeyIdArray(helper_params.validLongId);
- expect(test0).to.be.empty;
+
+ describe('Fingerprint checking', function(){
+ it('isFingerprint(): valid Fingerprint', function(){
+ let test0 = isFingerprint(helper_params.validFingerprint);
+ expect(test0).to.be.true;
});
- it('invalidFingerprint', function(){
- let test0 = toKeyIdArray(helper_params.invalidFingerprint);
- expect(test0).to.be.empty;
+ it('isFingerprint(): invalid Fingerprint', function(){
+ let test0 = isFingerprint(helper_params.invalidFingerprint);
+ expect(test0).to.be.false;
});
- it('invalidKeyArray', function(){
- let test0 = toKeyIdArray(helper_params.invalidKeyArray);
- expect(test0).to.be.empty;
+ });
+ 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);
+ });
});
- 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);
+ 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 454b8ca3..44206fba 100644
--- a/lang/js/test/Message.js
+++ b/lang/js/test/Message.js
@@ -21,22 +21,23 @@
import { expect } from "../node_modules/chai/chai";
import { GPGME_Message, createMessage } from "../src/Message";
-const message_params = {
- invalid_op_action : 'dance',
- invalid_op_type : [234, 34, '<>'],
-}
+import { message_params } from "./inputvalues";
-describe('Message Object', function(){
- describe('incorrect initialization', function(){
- it('non-allowed operation', function(){
- let test0 = createMessage(message_params.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);
- expect(test0).to.be.an.instanceof(Error);
- expect(test0.code).to.equal('PARAM_WRONG');
+function Messagetest(){
+
+ describe('Message Object', function(){
+ describe('incorrect initialization', function(){
+ it('non-allowed operation', function(){
+ let test0 = createMessage(message_params.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);
+ expect(test0).to.be.an.instanceof(Error);
+ expect(test0.code).to.equal('PARAM_WRONG');
+ });
});
});
-});
+};
+export default Messagetest;
\ No newline at end of file
diff --git a/lang/js/test/inputvalues.js b/lang/js/test/inputvalues.js
new file mode 100644
index 00000000..a50c8162
--- /dev/null
+++ b/lang/js/test/inputvalues.js
@@ -0,0 +1,29 @@
+
+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',
+ 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, '<>'],
+}
+
+export default {
+ helper_params: helper_params,
+ message_params: message_params
+}
\ No newline at end of file
diff --git a/lang/js/test_index.js b/lang/js/test_index.js
deleted file mode 100644
index 9119d271..00000000
--- a/lang/js/test_index.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* 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+
- *
- */
-document.addEventListener('DOMContentLoaded', function() {
- chrome.tabs.create({
- url: './ui2.html'
- });
-});
diff --git a/lang/js/testapplication.js b/lang/js/testapplication.js
deleted file mode 100644
index b2cb4c23..00000000
--- a/lang/js/testapplication.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/* 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+
- *
- */
-
-document.addEventListener('DOMContentLoaded', function() {
- Gpgmejs.init().then(function(gpgmejs){
- document.getElementById("buttonencrypt").addEventListener("click",
- function(){
- let data = document.getElementById('cleartext').value;
- let keyId = document.getElementById('pubkey').value;
- gpgmejs.encrypt(data, keyId).then(
- function(answer){
- console.log(answer);
- if (answer.data){
- console.log(answer.data);
- document.getElementById('answer').value = answer.data;
- }
- }, function(errormsg){
- alert( errormsg.code + ' ' + errormsg.msg);
- });
- });
-
- document.getElementById("buttondecrypt").addEventListener("click",
- function(){
- let data = document.getElementById("ciphertext").value;
- gpgmejs.decrypt(data).then(
- function(answer){
- console.log(answer);
- if (answer.data){
- document.getElementById('answer').value = answer.data;
- }
- }, function(errormsg){
- alert( errormsg.code + ' ' + errormsg.msg);
- });
- });
- },
- function(error){console.log(error)});
-});
diff --git a/lang/js/testapplication_index.html b/lang/js/testapplication_index.html
deleted file mode 100644
index 866b1135..00000000
--- a/lang/js/testapplication_index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/lang/js/testicon.png b/lang/js/testicon.png
deleted file mode 100644
index 12c3f5df..00000000
Binary files a/lang/js/testicon.png and /dev/null differ
diff --git a/lang/js/ui.css b/lang/js/ui.css
deleted file mode 100644
index 9c88698b..00000000
--- a/lang/js/ui.css
+++ /dev/null
@@ -1,10 +0,0 @@
-ul {
- list-style-type: none;
- padding-left: 0px;
-}
-
-ul li span {
- float: left;
- width: 120px;
- margin-top: 6px;
-}
diff --git a/lang/js/ui2.html b/lang/js/ui2.html
deleted file mode 100644
index 8d0abd97..00000000
--- a/lang/js/ui2.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Encrypt
-
-
-
- Encrypted armored Text:
-
-
-
- Decrypt
-
- Result data:
-
-
-
diff --git a/lang/js/webpack.conf.js b/lang/js/webpack.conf.js
index 7a5392ee..b2ad9098 100644
--- a/lang/js/webpack.conf.js
+++ b/lang/js/webpack.conf.js
@@ -26,7 +26,7 @@ module.exports = {
// mode: 'development',
mode: 'production',
output: {
- path: path.resolve(__dirname, 'dist'),
+ path: path.resolve(__dirname, 'build'),
filename: 'gpgmejs.bundle.js',
libraryTarget: 'var',
libraryExport: 'default',
--
cgit v1.2.3
From f45b926816340d3cca37f013a9eb1b1d9cdb0cfe Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Thu, 26 Apr 2018 17:59:40 +0200
Subject: js: fixed wrong paths in DemoExtension
--
* Some forgotten internal links after the move to a subdir and cleaning
---
lang/js/DemoExtension/entry.js | 2 +-
lang/js/DemoExtension/mainui.html | 2 +-
lang/js/DemoExtension/popup.html | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lang/js/DemoExtension/entry.js b/lang/js/DemoExtension/entry.js
index 7e5e1ffe..62583421 100644
--- a/lang/js/DemoExtension/entry.js
+++ b/lang/js/DemoExtension/entry.js
@@ -20,6 +20,6 @@
*/
document.addEventListener('DOMContentLoaded', function() {
chrome.tabs.create({
- url: './uimainui.html'
+ url: './mainui.html'
});
});
diff --git a/lang/js/DemoExtension/mainui.html b/lang/js/DemoExtension/mainui.html
index d85e7a46..76b8a221 100644
--- a/lang/js/DemoExtension/mainui.html
+++ b/lang/js/DemoExtension/mainui.html
@@ -3,7 +3,7 @@
-
+
diff --git a/lang/js/DemoExtension/popup.html b/lang/js/DemoExtension/popup.html
index 866b1135..50070311 100644
--- a/lang/js/DemoExtension/popup.html
+++ b/lang/js/DemoExtension/popup.html
@@ -2,7 +2,7 @@
-
+
--
cgit v1.2.3
From eb7129f3196ae4f0807ceba0c1fc9e818ea6cd22 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Fri, 27 Apr 2018 10:21:13 +0200
Subject: js: fixed empty operation setter in Message
--
* src/Message.js Messages failed because they were not assigned
operations
---
lang/js/src/Message.js | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 9e7a8835..95d043ba 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -44,9 +44,16 @@ export class GPGME_Message {
}
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;
}
--
cgit v1.2.3
From fda7b13f1b673962ce34b6f429158a7eb9cef47b Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Fri, 27 Apr 2018 20:03:09 +0200
Subject: js: more testing
--
* Tests: initialization of the two modes, encryption
* gpgme.js: reintroduced message check before calling
Connection.post()
* gpgmejs_openpgp.js: Fixed openpgp mode not passing keys
* index.js: fixed some confusion in parseconfig()
* Inserted some TODO stubs for missing error handling
---
lang/js/BrowserTestExtension/tests/encryptTest.js | 71 ++++++++++++++++
lang/js/BrowserTestExtension/tests/inputvalues.js | 6 +-
lang/js/BrowserTestExtension/tests/startup.js | 67 ++++++++++------
lang/js/build_extensions.sh | 1 +
lang/js/src/Connection.js | 6 +-
lang/js/src/Key.js | 23 +++---
lang/js/src/Keyring.js | 4 +
lang/js/src/Message.js | 66 +++++++++++++--
lang/js/src/gpgmejs.js | 56 +++++++------
lang/js/src/gpgmejs_openpgpjs.js | 7 +-
lang/js/src/index.js | 23 +++---
lang/js/src/permittedOperations.js | 98 +++++++++++++++++------
lang/js/test/Helpers.js | 2 +-
lang/js/test/Message.js | 62 ++++++++++++--
lang/js/test/index.js | 28 +++++++
lang/js/test/inputvalues.js | 10 +++
16 files changed, 416 insertions(+), 114 deletions(-)
create mode 100644 lang/js/BrowserTestExtension/tests/encryptTest.js
create mode 100644 lang/js/test/index.js
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
@@ -22,7 +22,11 @@ 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
@@ -20,32 +20,51 @@
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
@@ -6,6 +6,7 @@ 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
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
@@ -100,9 +100,7 @@ export class Connection{
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){
@@ -129,6 +127,8 @@ export class Connection{
}, 5000);
}]).then(function(result){
return result;
+ }, function(error){
+ return error;
});
}
});
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
@@ -51,10 +51,7 @@ export class GPGME_Key {
* 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(){
@@ -130,6 +127,8 @@ export class GPGME_Key {
}
}
return Promise.resolve(resultset);
+ }, function(error){
+ //TODO checkKey fails
});
}
@@ -175,8 +174,7 @@ export class GPGME_Key {
*/
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){
@@ -188,19 +186,20 @@ function checkKey(fingerprint, property){
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
@@ -78,6 +78,8 @@ export class GPGME_Keyring {
}
}
return Promise.resolve(resultset);
+ }, function(error){
+ //TODO error handling
});
}
@@ -151,6 +153,8 @@ export class GPGME_Keyring {
}
}
return Promise.resolve(resultset);
+ }, function(error){
+ //TODO error handling
});
}
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
@@ -73,11 +73,60 @@ export class GPGME_Message {
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;
}
/**
@@ -89,11 +138,12 @@ export class GPGME_Message {
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;
}
}
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
@@ -33,25 +33,24 @@ export class GpgME {
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){
@@ -85,8 +84,11 @@ export class GpgME {
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'));
+ }
}
/**
@@ -133,20 +135,24 @@ export class GpgME {
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'));
+ }
}
}
@@ -162,7 +168,7 @@ function putData(message, data){
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);
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
@@ -109,7 +109,7 @@
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
@@ -201,6 +201,8 @@ class GPGME_Keyring_openpgpmode {
// TODO: Can there be several default keys?
return gpgme_error('TODO');
}
+ }, function(error){
+ //TODO
});
}
@@ -264,6 +266,9 @@ class GPGME_Key_openpgpmode {
* creates GPGME_Key_openpgpmode from GPGME_Keys
*/
function translateKeys(input){
+ if (!input){
+ return null;
+ }
if (!Array.isArray(input)){
input = [input];
}
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
@@ -53,18 +53,17 @@ function init(config){
});
}
-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 {
@@ -75,6 +74,12 @@ function parseconfiguration(config){
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;
};
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
@@ -21,9 +21,16 @@
/**
* 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: .
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+import { Helpertest } from "./Helpers";
+import { Messagetest } from "./Message";
+
+/**
+ * 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
@@ -8,6 +8,8 @@ export const helper_params = {
'ADDBC303B6D31026F5EB4591A27EABDF283121BB',
new GPGME_Key('EE17AEE730F88F1DE7713C54BBE0A4FF7851650A')],
validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ '9AAE7A338A9A9A7A7A8A9A9A7A7A8A9A9A7A7DDA'],
invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
invalidFingerprint: [{hello:'World'}],
invalidKeyArray: {curiosity:'uncat'},
@@ -21,8 +23,16 @@ export const helper_params = {
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
--
cgit v1.2.3
From 6f67814eb45725bc7f3736a2638bad0a7470f17a Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Thu, 3 May 2018 14:12:10 +0200
Subject: js: changed Key class stub
--
* src/Key.js:
A Key object cannot offer more than basic functionality outside a
connection, so it now requires a connection to be present.
---
lang/js/src/Key.js | 124 ++++++++++++++++++++++++---------------
lang/js/src/Keyring.js | 3 +-
lang/js/src/gpgmejs_openpgpjs.js | 26 +++++---
lang/js/src/index.js | 7 ++-
lang/js/test/inputvalues.js | 18 +++---
5 files changed, 110 insertions(+), 68 deletions(-)
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 30449d63..1e0d3195 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -30,11 +30,42 @@ import { isFingerprint } from './Helpers'
import { gpgme_error } from './Errors'
import { createMessage } from './Message';
import { permittedOperations } from './permittedOperations';
+import { Connection } from './Connection';
+
+
+export function createKey(fingerprint, parent){
+ if (!isFingerprint(fingerprint)){
+ return gpgme_error('KEY_INVALID');
+ }
+ 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);
+ }
+}
export class GPGME_Key {
- constructor(fingerprint){
+ constructor(fingerprint, connection){
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._connection instanceof Connection){
+ return gpgme_error('CONN_NO_CONNECT');
+ } else {
+ return this._connection;
+ }
}
set fingerprint(fpr){
@@ -51,55 +82,56 @@ export class GPGME_Key {
* hasSecret returns true if a secret subkey is included in this Key
*/
get hasSecret(){
- return checkKey(this._fingerprint, 'secret');
+ return this.checkKey('secret');
}
get isRevoked(){
- return checkKey(this._fingerprint, 'revoked');
+ return this.checkKey('revoked');
}
get isExpired(){
- return checkKey(this._fingerprint, 'expired');
+ return this.checkKey('expired');
}
get isDisabled(){
- return checkKey(this._fingerprint, 'disabled');
+ return this.checkKey('disabled');
}
get isInvalid(){
- return checkKey(this._fingerprint, 'invalid');
+ return this.checkKey('invalid');
}
get canEncrypt(){
- return checkKey(this._fingerprint, 'can_encrypt');
+ return this.checkKey('can_encrypt');
}
get canSign(){
- return checkKey(this._fingerprint, 'can_sign');
+ return this.checkKey('can_sign');
}
get canCertify(){
- return checkKey(this._fingerprint, 'can_certify');
+ return this.checkKey('can_certify');
}
get canAuthenticate(){
- return checkKey(this._fingerprint, 'can_authenticate');
+ return this.checkKey('can_authenticate');
}
get isQualified(){
- return checkKey(this._fingerprint, 'is_qualified');
+ return this.checkKey('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});
+ let msg = createMessage ('export_key');
+ msg.setParameter('armor', true);
+ if (msg instanceof Error){
+ return gpgme_error('INVALID_KEY');
+ }
+ this.connection.post(msg).then(function(result){
+ return result.data;
});
// TODO return value not yet checked. Should result in an armored block
// in correct encoding
- // TODO openpgpjs also returns secKey if private = true?
}
/**
@@ -114,21 +146,21 @@ export class GPGME_Key {
* @returns {Array}
*/
get subkeys(){
- return checkKey(this._fingerprint, 'subkeys').then(function(result){
+ return this.checkKey('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]);
+ let subkey = new GPGME_Key(result[i], this.connection);
if (subkey instanceof GPGME_Key){
resultset.push(subkey);
}
}
return Promise.resolve(resultset);
}, function(error){
- //TODO checkKey fails
+ //TODO this.checkKey fails
});
}
@@ -137,7 +169,7 @@ export class GPGME_Key {
* @returns {Date|null} TBD
*/
get timestamp(){
- return checkKey(this._fingerprint, 'timestamp');
+ return this.checkKey('timestamp');
//TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available.
}
@@ -146,7 +178,7 @@ export class GPGME_Key {
* @returns {Date|null} TBD
*/
get expires(){
- return checkKey(this._fingerprint, 'expires');
+ return this.checkKey('expires');
// TODO convert to Date; check for 0
}
@@ -155,51 +187,47 @@ export class GPGME_Key {
* @returns {String|Array} The user ids associated with this key
*/
get userIds(){
- return checkKey(this._fingerprint, 'uids');
+ return this.checkKey('uids');
}
/**
* @returns {String} The public key algorithm supported by this subkey
*/
get pubkey_algo(){
- return checkKey(this._fingerprint, 'pubkey_algo');
+ return this.checkKey('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].hasOwnProperty(property)){
- return Promise.reject(gpgme_error('PARAM_WRONG'));
- }
- return new Promise(function(resolve, reject){
- if (!isFingerprint(fingerprint)){
- reject(gpgme_error('KEY_INVALID'));
+ /**
+ * generic function to query gnupg information on a key.
+ * @param {*} property The gpgme-json property to check.
+ * TODO: check if Promise.then(return)
+ */
+ checkKey(property){
+ return gpgme_error('NOT_YET_IMPLEMENTED');
+ // TODO: async is not what is to be ecpected from Key information :(
+ if (!property || typeof(property) !== 'string' ||
+ !permittedOperations['keyinfo'].hasOwnProperty(property)){
+ return gpgme_error('PARAM_WRONG');
}
let msg = createMessage ('keyinfo');
if (msg instanceof Error){
- reject(gpgme_error('PARAM_WRONG'));
+ return gpgme_error('PARAM_WRONG');
}
msg.setParameter('fingerprint', this.fingerprint);
- return (this.connection.post(msg)).then(function(result, error){
+ this.connection.post(msg).then(function(result, error){
if (error){
- reject(gpgme_error('GNUPG_ERROR',error.msg));
+ return gpgme_error('GNUPG_ERROR',error.msg);
} else if (result.hasOwnProperty(property)){
- resolve(result[property]);
+ return 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?
+ return false;
} else {
- reject(gpgme_error('CONN_UNEXPECTED_ANSWER'));
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
}, function(error){
- //TODO error handling
+ return gpgme_error('GENERIC_ERROR');
});
- });
+ }
};
\ No newline at end of file
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index d1f4122e..2cf87c24 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -61,6 +61,7 @@ export class GPGME_Keyring {
if (include_secret){
msg.setParameter('with-secret', true);
}
+ let me = this;
this.connection.post(msg).then(function(result){
let fpr_list = [];
@@ -72,7 +73,7 @@ export class GPGME_Keyring {
fpr_list = result.keys;
}
for (let i=0; i < fpr_list.length; i++){
- let newKey = new GPGME_Key(fpr_list[i]);
+ let newKey = new GPGME_Key(fpr_list[i], me._connection);
if (newKey instanceof GPGME_Key){
resultset.push(newKey);
}
diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js
index c80d5a86..b233f0fa 100644
--- a/lang/js/src/gpgmejs_openpgpjs.js
+++ b/lang/js/src/gpgmejs_openpgpjs.js
@@ -26,9 +26,10 @@
import { GpgME } from "./gpgmejs";
import {GPGME_Keyring} from "./Keyring"
- import { GPGME_Key } from "./Key";
+ import { GPGME_Key, createKey } from "./Key";
import { isFingerprint } from "./Helpers"
import { gpgme_error } from "./Errors"
+import { Connection } from "./Connection";
export class GpgME_openpgpmode {
@@ -47,7 +48,7 @@
initGpgME(connection, config = {}){
if (connection && typeof(config) ==='object'){
this._config = config;
- if (!this._GPGME){
+ if (!this._GpgME){
this._GpgME = new GpgME(connection, config);
}
if (!this._keyring){
@@ -223,7 +224,7 @@ class GPGME_Keyring_openpgpmode {
if ( !key.fingerprint || ! isFingerprint(key.fingerprint)){
return Promise.reject(gpgme_error('PARAM_WRONG'));
}
- let key_to_delete = new GPGME_Key(key.fingerprint);
+ let key_to_delete = createKey(key.fingerprint, this._gpgme_keyring_GpgME);
return key_to_delete.deleteKey(key.secret);
}
}
@@ -233,15 +234,22 @@ class GPGME_Keyring_openpgpmode {
* Offers the Key information as the openpgpmode wants
*/
class GPGME_Key_openpgpmode {
- constructor(value){
- this.init = value;
+ constructor(value, connection){
+ this.init(value, connection);
}
- set init (value){
+ /**
+ * Can be either constructed using an existing GPGME_Key, or a fingerprint
+ * and a connection
+ * @param {String|GPGME_Key} value
+ * @param {Connection} connection
+ */
+ init (value, connection){
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);
+ } else if (!this._GPGME_Key && isFingerprint(value) &&
+ connection instanceof Connection){
+ this._GPGME_Key = createKey(value, connection);
}
}
@@ -264,6 +272,8 @@ class GPGME_Key_openpgpmode {
/**
* creates GPGME_Key_openpgpmode from GPGME_Keys
+ * @param {GPGME_Key|Array} input keys
+ * @returns {Array}
*/
function translateKeys(input){
if (!input){
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
index 90fe99e3..fc406c66 100644
--- a/lang/js/src/index.js
+++ b/lang/js/src/index.js
@@ -36,9 +36,12 @@ function init(config){
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.
+ // event listener in isConnected, but in some cases this takes some
+ // time (<5ms) to disconnect if there is no successfull connection.
let delayedreaction = function(){
+ if (connection === undefined) {
+ reject(gpgme_error('CONN_NO_CONNECT'));
+ }
if (connection.isConnected === true){
if (_conf.api_style && _conf.api_style === 'gpgme_openpgpjs'){
resolve(new GpgME_openpgpmode(connection, _conf));
diff --git a/lang/js/test/inputvalues.js b/lang/js/test/inputvalues.js
index f6cd75aa..7752b82f 100644
--- a/lang/js/test/inputvalues.js
+++ b/lang/js/test/inputvalues.js
@@ -1,12 +1,10 @@
-
-import {GPGME_Key} from "../src/Key"
+import { gpgme_error } from "../src/Errors";
export const helper_params = {
validLongId: '0A0A0A0A0A0A0A0A',
- validGPGME_Key: new GPGME_Key('ADDBC303B6D31026F5EB4591A27EABDF283121BB'),
- validKeys: [new GPGME_Key('A1E3BC45BDC8E87B74F4392D53B151A1368E50F3'),
+ validKeys: ['A1E3BC45BDC8E87B74F4392D53B151A1368E50F3',
'ADDBC303B6D31026F5EB4591A27EABDF283121BB',
- new GPGME_Key('EE17AEE730F88F1DE7713C54BBE0A4FF7851650A')],
+ 'EE17AEE730F88F1DE7713C54BBE0A4FF7851650A'],
validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
'9AAE7A338A9A9A7A7A8A9A9A7A7A8A9A9A7A7DDA'],
@@ -14,10 +12,10 @@ export const helper_params = {
invalidFingerprint: [{hello:'World'}],
invalidKeyArray: {curiosity:'uncat'},
invalidKeyArray_OneBad: [
- new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'),
+ '12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08',
'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
'3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
- invalidErrorCode: 'Please type in all your passwords.'
+ invalidErrorCode: 'Please type in all your passwords.',
}
export const message_params = {
@@ -28,10 +26,12 @@ export const message_params = {
valid_op: 'encrypt',
invalid_param_names: [22,'dance', {}],
validparam_name_0: 'mime',
- invalid_values_0: [2134, 'All your passwords', new GPGME_Key('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'), null]
+ invalid_values_0: [2134, 'All your passwords', gpgme_error('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08'), null]
}
}
-
+export const whatever_params = {
+ four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}']
+}
export default {
helper_params: helper_params,
--
cgit v1.2.3
From c755287ba845c4cbbf1d50e5aafecb2e687c7ac9 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Thu, 3 May 2018 18:03:22 +0200
Subject: js: Added browser testing for unit tests
--
* Added unittests to be run inside a Browser. To be able to access
the non-exposed functions and classes, a testing bundle will be
created, containing the tests (unittests.js) and the items to be
tested.
* src/Helpelpers, src/Key, src/Keyring: fixed some errors found
during testing.
---
lang/js/BrowserTestExtension/browsertest.html | 2 +
lang/js/BrowserTestExtension/runbrowsertest.js | 1 +
lang/js/BrowserTestExtension/tests/encryptTest.js | 14 +-
lang/js/README_testing | 14 +
lang/js/build_extensions.sh | 4 +-
lang/js/src/Helpers.js | 4 +-
lang/js/src/Key.js | 11 +-
lang/js/src/Keyring.js | 2 +-
lang/js/unittest_inputvalues.js | 45 +++
lang/js/unittests.js | 321 ++++++++++++++++++++++
lang/js/webpack.conf_unittests.js | 34 +++
11 files changed, 436 insertions(+), 16 deletions(-)
create mode 100644 lang/js/README_testing
create mode 100644 lang/js/unittest_inputvalues.js
create mode 100644 lang/js/unittests.js
create mode 100644 lang/js/webpack.conf_unittests.js
diff --git a/lang/js/BrowserTestExtension/browsertest.html b/lang/js/BrowserTestExtension/browsertest.html
index d2c6396f..ce037a11 100644
--- a/lang/js/BrowserTestExtension/browsertest.html
+++ b/lang/js/BrowserTestExtension/browsertest.html
@@ -14,9 +14,11 @@
+
+
diff --git a/lang/js/BrowserTestExtension/runbrowsertest.js b/lang/js/BrowserTestExtension/runbrowsertest.js
index 39bc3fb9..308c716d 100644
--- a/lang/js/BrowserTestExtension/runbrowsertest.js
+++ b/lang/js/BrowserTestExtension/runbrowsertest.js
@@ -19,3 +19,4 @@
*/
mocha.run();
+Gpgmejs_test.unittests();
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
index e6000003..2178efac 100644
--- a/lang/js/BrowserTestExtension/tests/encryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -18,8 +18,7 @@
* SPDX-License-Identifier: LGPL-2.1+
*/
describe('Encryption', function(){
-
- it('Successfull encrypt', function(done){
+ it('Successfull encrypt', function(){
let prm = Gpgmejs.init();
prm.then(function(context){
context.encrypt(
@@ -29,10 +28,6 @@ describe('Encryption', function(){
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();
});
});
});
@@ -44,11 +39,10 @@ describe('Encryption', function(){
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()
+ //TODO: MSG_INCOMPLETE desired, GNUPG_ERROR coming
});
});
});
@@ -61,11 +55,9 @@ describe('Encryption', function(){
expect(answer).to.be.undefined;
}, function(error){
expect(error).to.be.an.instanceof(Error);
- expect(error.code).to.equal('MSG_INCOMPLETE');
- done();
+ expect(error.code).to.equal('PARAM_WRONG');
});
});
});
-
// TODO check different valid parameter
});
diff --git a/lang/js/README_testing b/lang/js/README_testing
new file mode 100644
index 00000000..b61ca1a6
--- /dev/null
+++ b/lang/js/README_testing
@@ -0,0 +1,14 @@
+Test extension:
+
+The test extension contains tests with mocha and chai. It will be packed as an
+extra extension (see build_extension.sh).
+
+Tests from BrowserTestExtension/tests will be run against the gpgmejs.bundle.js
+itself. They aim to test the outward facing functionality and API.
+
+Unittests as defined in ./unittests.js will be bundled in
+gpgmejs_unittests.bundle.js, and test the separate components of gpgmejs,
+which mostly are not exported.
+
+The BrowserExtension can be installed the same way as the DemoExtension
+(see README).
\ No newline at end of file
diff --git a/lang/js/build_extensions.sh b/lang/js/build_extensions.sh
index b99a362c..91d5479b 100755
--- a/lang/js/build_extensions.sh
+++ b/lang/js/build_extensions.sh
@@ -1,11 +1,13 @@
#/!bin/bash
npx webpack --config webpack.conf.js
+npx webpack --config webpack.conf_unittests.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
+ build/gpgmejs.bundle.js \
+ build/gpgmejs_unittests.bundle.js BrowserTestExtension/libs
rm -rf build/extensions
mkdir -p build/extensions
zip -r build/extensions/browsertest.zip BrowserTestExtension
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index 9a69f851..ea056fff 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -91,9 +91,9 @@ export function isFingerprint(string){
return hextest(string, 40);
};
/**
- * check if the input is a valid Hex string with a length of 16
+ * TODO no usage; check if the input is a valid Hex string with a length of 16
*/
-export function isLongId(string){
+function isLongId(string){
return hextest(string, 16);
};
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 1e0d3195..6d3cf17d 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -61,6 +61,9 @@ export class GPGME_Key {
}
get connection(){
+ if (!this._fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
if (!this._connection instanceof Connection){
return gpgme_error('CONN_NO_CONNECT');
} else {
@@ -75,6 +78,9 @@ export class GPGME_Key {
}
get fingerprint(){
+ if (!this._fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
return this._fingerprint;
}
@@ -125,7 +131,7 @@ export class GPGME_Key {
let msg = createMessage ('export_key');
msg.setParameter('armor', true);
if (msg instanceof Error){
- return gpgme_error('INVALID_KEY');
+ return gpgme_error('KEY_INVALID');
}
this.connection.post(msg).then(function(result){
return result.data;
@@ -203,6 +209,9 @@ export class GPGME_Key {
* TODO: check if Promise.then(return)
*/
checkKey(property){
+ if (!this._fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
return gpgme_error('NOT_YET_IMPLEMENTED');
// TODO: async is not what is to be ecpected from Key information :(
if (!property || typeof(property) !== 'string' ||
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index 2cf87c24..4596035a 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -20,7 +20,7 @@
import {createMessage} from './Message'
import {GPGME_Key} from './Key'
-import { isFingerprint, isLongId } from './Helpers';
+import { isFingerprint } from './Helpers';
import { gpgme_error } from './Errors';
import { Connection } from './Connection';
diff --git a/lang/js/unittest_inputvalues.js b/lang/js/unittest_inputvalues.js
new file mode 100644
index 00000000..3450afd2
--- /dev/null
+++ b/lang/js/unittest_inputvalues.js
@@ -0,0 +1,45 @@
+import {Connection} from "./src/Connection";
+import {createKey} from "./src/Key";
+
+let conn = new Connection;
+
+export const helper_params = {
+ validLongId: '0A0A0A0A0A0A0A0A',
+ validKeys: ['A1E3BC45BDC8E87B74F4392D53B151A1368E50F3',
+ createKey('ADDBC303B6D31026F5EB4591A27EABDF283121BB', conn),
+ 'EE17AEE730F88F1DE7713C54BBE0A4FF7851650A'],
+ validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ '9AAE7A338A9A9A7A7A8A9A9A7A7A8A9A9A7A7DDA'],
+ invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
+ invalidFingerprints: [{hello:'World'}, ['kekekeke'], new Uint32Array(40)],
+ invalidKeyArray: {curiosity:'uncat'},
+ invalidKeyArray_OneBad: [
+ createKey('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08', conn),
+ 'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
+ '3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
+ invalidErrorCode: 'Please type in all your passwords.',
+ validGPGME_Key: createKey('ADDBC303B6D31026F5EB4591A27EABDF283121BB', conn),
+ valid_openpgplike: { primaryKey: {
+ getFingerprint: function(){
+ return '85DE2A8BA5A5AB3A8A7BE2000B8AED24D7534BC2';}
+ }
+ }
+}
+
+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',
+ createKey('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08', conn), null]
+ }
+}
+
+export const whatever_params = {
+ four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}']
+}
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
new file mode 100644
index 00000000..0a1b4b48
--- /dev/null
+++ b/lang/js/unittests.js
@@ -0,0 +1,321 @@
+/* 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 "./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 { 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;
+ let delayed = function(){
+ expect(conn0.isConnected).to.be.true;
+ expect(conn0.connect).to.be.a('function');
+ expect(conn0.disconnect).to.be.a('function');
+ expect(conn0.post).to.be.a('function');
+ done();
+ };
+ setTimeout(delayed, 5);
+
+ });
+
+ it('Disconnecting', function(done) {
+ let conn0 = new Connection;
+ let delayed = function(){
+ conn0.disconnect(); // TODO fails!
+ expect(conn0.isConnected).to.be.false;
+ done();
+ };
+ setTimeout(delayed, 5);
+ });
+
+ // broken
+ // it('Connect info still only available after a delay', function(done){
+ // // if false, all delayed connections can be refactored
+ // let conn0 = new Connection;
+ // expect(conn0.isConnected).to.be.undefined;
+ // //
+ // })
+ });
+
+ 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(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);
+ console.log(test0);
+ 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(hp.validFingerprint, conn);
+
+ expect(key).to.be.an.instanceof(GPGME_Key);
+ expect(key.connection).to.be.an.instanceof(Connection);
+ // TODO not implemented yet: Further Key functionality
+ });
+
+ it('Key can use the connection', function(){
+ let conn = new Connection;
+ let key = createKey(hp.validFingerprint, conn);
+
+ expect(key.connection.isConnected).to.be.true;
+
+ key.connection.disconnect();
+ expect(key.connection.isConnected).to.be.false;
+ });
+
+ 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);
+
+ 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');
+ }
+ });
+ it('bad GPGME_Key returns Error if used', function(){
+ let conn = new Connection;
+ for (let i=0; i < 4; i++){
+ let key = new GPGME_Key(wp.four_invalid_params[i], conn);
+
+ expect(key.connection).to.be.an.instanceof(Error);
+ expect(key.connection.code).to.equal('KEY_INVALID');
+ }
+ });
+ });
+
+ describe('GPGME_Keyring', function(){
+
+ it('correct initialization', function(){
+ let conn = new Connection;
+ let keyring = new GPGME_Keyring(conn);
+
+ 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(){
+ 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');
+ expect(keyring.getKeys).to.be.an.instanceof(Error);
+ expect(keyring.getkeys.code).to.equal('CONN_NO_CONNECT');
+ });
+ //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 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);
+ });
+
+ 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');
+ }
+ });
+ });
+
+ mocha.run();
+}
+
+export default {unittests};
\ No newline at end of file
diff --git a/lang/js/webpack.conf_unittests.js b/lang/js/webpack.conf_unittests.js
new file mode 100644
index 00000000..4b903be6
--- /dev/null
+++ b/lang/js/webpack.conf_unittests.js
@@ -0,0 +1,34 @@
+/* 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 the configuration file for building the gpgmejs-Library with webpack
+ */
+const path = require('path');
+
+module.exports = {
+ entry: './unittests.js',
+ mode: 'production',
+ output: {
+ path: path.resolve(__dirname, 'build'),
+ filename: 'gpgmejs_unittests.bundle.js',
+ libraryTarget: 'var',
+ libraryExport: 'default',
+ library: 'Gpgmejs_test'
+ }
+};
--
cgit v1.2.3
From cf075846fb48c8d71937100d2c45069d37d54a38 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Fri, 4 May 2018 12:56:59 +0200
Subject: js: fixing errors found by testing
--
* Key.js: Error code for wrong parameter in createKey should be
"PARAM_WRONG"
* Helpers.js: The property openpgpjs-like Objects were checked for in
toKeyIdArray was not defined.
* src/permittedOperations.js: updated more expectations and assumptions
for the native API
---
lang/js/src/Helpers.js | 2 +-
lang/js/src/Key.js | 4 +-
lang/js/src/permittedOperations.js | 81 ++++++++++++++++++++++++++++----------
lang/js/unittests.js | 2 +-
4 files changed, 66 insertions(+), 23 deletions(-)
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index ea056fff..fd0e7200 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -48,7 +48,7 @@ export function toKeyIdArray(input){
if (input[i] instanceof GPGME_Key){
fpr = input[i].fingerprint;
} else if (input[i].hasOwnProperty('primaryKey') &&
- input[i].primaryKey.hasOwnProperty(getFingerprint)){
+ input[i].primaryKey.hasOwnProperty('getFingerprint')){
fpr = input[i].primaryKey.getFingerprint();
}
if (isFingerprint(fpr) === true){
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 6d3cf17d..075a190e 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -35,13 +35,15 @@ import { Connection } from './Connection';
export function createKey(fingerprint, parent){
if (!isFingerprint(fingerprint)){
- return gpgme_error('KEY_INVALID');
+ 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');
}
}
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index 274e037e..59597aaf 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -122,55 +122,96 @@ export const permittedOperations = {
type: ['plaintext'],
data: ['data'],
params: ['base64', 'mime'],
- infos: [] // pending. Info about signatures and validity
- //signature: [{Key Fingerprint, valid boolean}]
+ infos: [] // TODO pending. Info about signatures and validity
+ //{
+ //signatures: [{
+ //Key : Fingerprint,
+ //valid:
+ // }]
}
},
- /**
- keyinfo: { // querying the Key's information.
- required: ['fingerprint'],
- anser: {
+ /** TBD: querying the Key's information (keyinfo)
+ TBD name: {
+ required: {
+ 'fingerprint': {
+ allowed: ['string']
+ },
+ },
+ answer: {
type: ['TBD'],
data: [],
- params: ['hasSecret', 'isRevoked', 'isExpired', 'armored',
- 'timestamp', 'expires', 'pubkey_algo'],
+ params: ['hasSecret','isRevoked','isExpired','armored',
+ 'timestamp','expires','pubkey_algo'],
infos: ['subkeys', 'userIds']
+ // {'hasSecret': ,
+ // 'isRevoked': ,
+ // 'isExpired': ,
+ // 'armored': , // armored public Key block
+ // 'timestamp': , //
+ // 'expires': ,
+ // 'pubkey_algo': TBD // TBD (optional?),
+ // 'userIds': Array,
+ // 'subkeys': Array Fingerprints of Subkeys
+ // }
}*/
/**
listkeys:{
- optional: ['with-secret', 'pattern'],
+ required: {};
+ optional: {
+ 'with-secret':{
+ allowed: ['boolean']
+ },{
+ 'pattern': {
+ allowed: ['string']
+ }
+ },
answer: {
- type: ['TBD'], //Array of fingerprints?
- infos: ['TBD'] //the property with infos
+ type: ['TBD'],
+ infos: ['TBD']
+ // keys: Array Fingerprints representing the results
},
*/
/**
importkey: {
- required: ['keyarmored'],
+ required: {
+ 'keyarmored': {
+ allowed: ['string']
+ }
+ },
answer: {
type: ['TBD'],
- infos: [''], // for each key if import was a success, if it was an update
+ infos: ['TBD'],
+ // for each key if import was a success,
+ // and if it was an update of preexisting key
}
},
*/
/**
deletekey: {
- required: ['fingerprint'],
+ pinentry: true,
+ required: {
+ 'fingerprint': {
+ allowed: ['string'],
+ // array_allowed: TBD Allow several Keys to be deleted at once?
+ },
+ optional: {
+ 'TBD' //Flag to delete secret Key ?
+ }
answer: {
type ['TBD'],
- infos: [''] //success:true? in gpgme, an error NO_ERROR is returned
+ infos: ['']
+ // TBD (optional) Some kind of 'ok' if delete was successful.
}
}
*/
/**
- *get armored secret different treatment from keyinfo!
- */
-
- /**
- * TBD key modification requests?
+ *TBD get armored secret different treatment from keyinfo!
+ * TBD key modification?
+ * encryptsign: TBD
+ * verify: TBD
*/
}
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
index 0a1b4b48..6c0d1890 100644
--- a/lang/js/unittests.js
+++ b/lang/js/unittests.js
@@ -109,7 +109,7 @@ function unittests (){
});
});
- describe('toKeyIdArray() (converting input to fingerprint', function(){
+ describe('toKeyIdArray() (converting input to fingerprint)', function(){
it('Correct fingerprint string', function(){
let test0 = toKeyIdArray(hp.validFingerprint);
--
cgit v1.2.3
From 8f3d83e5f0903323ec92f588f60dcecb0ae96de4 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Mon, 7 May 2018 18:27:25 +0200
Subject: js: fixing errors found by testing: encrypt/decrypt
--
* Key.js: Error code for wrong parameter in createKey should be
"PARAM_WRONG"
* Helpers.js: The property openpgpjs-like Objects were checked for in
toKeyIdArray was not defined.
* src/permittedOperations.js: updated more expectations and assumptions
for the native API
* new Problems:
- There seems to be a message size limit of about 21 MB for
nativeMessaging, much lower than the documented 4GB.
- Some bytes are lost with random data in an encrypt-decrypt
roundtrip. The culprit is unclear.
---
lang/js/BrowserTestExtension/browsertest.html | 1 +
.../tests/encryptDecryptTest.js | 72 +++++++++++++
lang/js/BrowserTestExtension/tests/encryptTest.js | 114 ++++++++++++++++++---
lang/js/BrowserTestExtension/tests/inputvalues.js | 12 +++
lang/js/src/Connection.js | 13 ++-
lang/js/src/Message.js | 19 +++-
lang/js/unittests.js | 11 +-
7 files changed, 219 insertions(+), 23 deletions(-)
create mode 100644 lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
diff --git a/lang/js/BrowserTestExtension/browsertest.html b/lang/js/BrowserTestExtension/browsertest.html
index ce037a11..d12e03cf 100644
--- a/lang/js/BrowserTestExtension/browsertest.html
+++ b/lang/js/BrowserTestExtension/browsertest.html
@@ -18,6 +18,7 @@
+
diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
new file mode 100644
index 00000000..7abf2d94
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -0,0 +1,72 @@
+/* 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 and Decryption', function () {
+ it('Successful encrypt and decrypt', 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);
+ done();
+ });
+ });
+ });
+ });
+
+/**
+ * Fails with random data! Some bytes (up to 100) of the original are missing in
+ * the result
+ */
+/**
+ for (let i=0; i< 20; i++) {
+ it('Successful encrypt 1 MB '+ i+ '/20', function (done) {
+ let prm = Gpgmejs.init();
+ let data = bigString(0.1);
+ 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);
+ done();
+ });
+ });
+ });
+ }).timeout(10000);
+ };*/
+});
\ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
index 2178efac..37b319ed 100644
--- a/lang/js/BrowserTestExtension/tests/encryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -17,47 +17,133 @@
* License along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1+
*/
-describe('Encryption', function(){
- it('Successfull encrypt', function(){
+describe('Encryption', function () {
+ it('Successful encrypt', function (done) {
let prm = Gpgmejs.init();
- prm.then(function(context){
+ prm.then(function (context) {
context.encrypt(
- inputvalues.encrypt.good.data,
- inputvalues.encrypt.good.fingerprint).then(function(answer){
+ 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();
});
});
});
- it('Sending encryption without keys fails', function(){
+ it('Successful encrypt 5 MB', function (done) {
let prm = Gpgmejs.init();
- prm.then(function(context){
+ let data = bigString(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');
+ done();
+ });
+ });
+ }).timeout(5000);
+
+ it('Successful encrypt 20 MB', function (done) {
+ let prm = Gpgmejs.init();
+ let data = bigString(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');
+ done();
+ });
+ });
+ }).timeout(20000);
+
+/**
+ it('Successful encrypt 30 MB', function (done) {
+ // TODO: There seems to be a limit imposed at least by chrome at about 21 MB
+ let prm = Gpgmejs.init();
+ let data = bigString(30);
+ 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');
+ 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){
+ null).then(function (answer) {
expect(answer).to.be.undefined;
}, function(error){
expect(error).to.be.an('Error');
expect(error.code).to.equal('MSG_INCOMPLETE');
- //TODO: MSG_INCOMPLETE desired, GNUPG_ERROR coming
+ done();
});
});
});
- it('Sending encryption without data fails', function(){
+ it('Sending encryption without data fails', function (done) {
let prm = Gpgmejs.init();
- prm.then(function(context){
+ prm.then(function (context) {
context.encrypt(
- null,inputvalues.encrypt.good.keyid).then(function(answer){
+ null, inputvalues.encrypt.good.keyid).then(function (answer) {
expect(answer).to.be.undefined;
- }, function(error){
+ }, function (error) {
expect(error).to.be.an.instanceof(Error);
- expect(error.code).to.equal('PARAM_WRONG');
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ 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');
+ done();
});
});
});
+
+ it('Overly large message ( >= 48MB) is rejected', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ bigString(48),
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function(error){
+ expect(error).to.be.an.instanceof(Error);
+ // TODO who is throwing the error here?
+ // It is not a GPGME_Error!
+ done();
+ });
+ });
+ }).timeout(8000);
// TODO check different valid parameter
});
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
index 1761c82f..3cd1e92a 100644
--- a/lang/js/BrowserTestExtension/tests/inputvalues.js
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -23,6 +23,9 @@ var inputvalues = {
good:{
data : 'Hello World.',
fingerprint : 'CDC3A2B2860625CCBFC5A5A9FC6D1B604967FC40'
+ },
+ bad: {
+ fingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A'
}
},
init: {
@@ -30,3 +33,12 @@ var inputvalues = {
}
};
+
+function bigString(megabytes){
+ let maxlength = 1024 * 1024 * megabytes;
+ let uint = new Uint8Array(maxlength);
+ for (let i= 0; i < maxlength; i++){
+ uint[i] = Math.random() * Math.floor(256);
+ }
+ return new TextDecoder('utf-8').decode(uint);
+}
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index a198bdc6..2c8792d6 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -99,11 +99,12 @@ export class Connection{
me._connection.onMessage.removeListener(listener)
reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
- me._connection.onMessage.removeListener(listener)
+ me._connection.onMessage.removeListener(listener);
reject(gpgme_error('GNUPG_ERROR', msg.msg));
} else {
let answer_result = answer.add(msg);
if (answer_result !== true){
+ me._connection.onMessage.removeListener(listener);
reject(answer_result);
}
if (msg.more === true){
@@ -127,8 +128,12 @@ export class Connection{
}, 5000);
}]).then(function(result){
return result;
- }, function(error){
- return error;
+ }, function(reject){
+ if(!reject instanceof Error) {
+ return gpgme_error('GNUPG_ERROR', reject);
+ } else {
+ return reject;
+ }
});
}
});
@@ -196,7 +201,7 @@ class Answer{
this._response.push(msg[key]);
}
else {
- return gpgme_error('CONN_UNEXPECTED_ANSWER', key);
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
break;
}
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index c42480f2..6a59c3e0 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -84,9 +84,22 @@ export class GPGME_Message {
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(typeof(val)) >= 0){
+ if (poparam.allowed.indexOf('boolean') >= 0){
return true;
}
return gpgme_error('PARAM_WRONG');
@@ -102,7 +115,9 @@ export class GPGME_Message {
return res;
}
}
- return true;
+ if (val.length > 0) {
+ return true;
+ }
} else if (val instanceof Uint8Array){
if (poparam.allowed.indexOf('Uint8Array') >= 0){
return true;
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
index 6c0d1890..c437d599 100644
--- a/lang/js/unittests.js
+++ b/lang/js/unittests.js
@@ -130,7 +130,6 @@ function unittests (){
let test0 = toKeyIdArray(hp.valid_openpgplike);
expect(test0).to.be.an('array').with.lengthOf(1);
- console.log(test0);
expect(test0).to.include(
hp.valid_openpgplike.primaryKey.getFingerprint());
});
@@ -255,7 +254,7 @@ function unittests (){
expect(test0.isComplete).to.be.false;
});
- it('Message is complete after setting mandatoy data', function(){
+ 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);
@@ -263,6 +262,13 @@ function unittests (){
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);
@@ -315,7 +321,6 @@ function unittests (){
});
});
- mocha.run();
}
export default {unittests};
\ No newline at end of file
--
cgit v1.2.3
From cca40627b0afa2efc85ef7f5f1a1060a221ff2a2 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Tue, 8 May 2018 18:33:41 +0200
Subject: js: more testing
--
* Tests: Under certain circumstances, some data change during
encrypt-decrypt. Committing the current state so the problem can be
discussed.
* Fixes:
- disconnecting the test ports after tests are complete
- fixed passing of the error message from gpgme-json
---
lang/js/BrowserTestExtension/testkey.pub | 30 ++++
lang/js/BrowserTestExtension/testkey.sec | 57 +++++++
.../tests/encryptDecryptTest.js | 186 +++++++++++++++++++--
lang/js/BrowserTestExtension/tests/encryptTest.js | 13 +-
lang/js/BrowserTestExtension/tests/inputvalues.js | 62 ++++++-
lang/js/src/Errors.js | 2 +-
lang/js/src/gpgmejs.js | 11 +-
7 files changed, 342 insertions(+), 19 deletions(-)
create mode 100644 lang/js/BrowserTestExtension/testkey.pub
create mode 100644 lang/js/BrowserTestExtension/testkey.sec
diff --git a/lang/js/BrowserTestExtension/testkey.pub b/lang/js/BrowserTestExtension/testkey.pub
new file mode 100644
index 00000000..cfc329f3
--- /dev/null
+++ b/lang/js/BrowserTestExtension/testkey.pub
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFrsKEkBCADKw4Wt8J6M/88qD8PO6lSMCxH1cpwH8iK0uPaFFYsJkkXo7kWf
+PTAtrV+REqF/o80dvYcdLvRsV21pvncZz/HXLu1yQ18mC3XObrKokbdgrTTKA5XE
+BZkNsqyaMMJauT18H4hYkSg62/tTdO1cu/zWv/LFf7Xyn6+uA74ovXCJlO1s0N2c
+PShtr98QRzPMf2owgVk37JnDNp4gGVDGHxSZOuUwxgYAZYnA8SFc+c+3ZrQfY870
++O4j3Mz4p7yD13AwP4buQLBsb/icxekeQCqpRJhLH9f7MdEcGXa1x36RcEkHdu+M
+yJ392eMgD+dKNfRCtyTPhjZTxvbNELIBYICfABEBAAG0EHRlc3RAZXhhbXBsZS5v
+cmeJAVQEEwEIAD4WIQTUFzW5Ejb9uIIEjFojAWNe7/DLBQUCWuwoSQIbAwUJA8Jn
+AAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRAjAWNe7/DLBf9kB/wOQ/S60HGw
+Fq07W9N01HWULyhHKoMmcHL6rfZ64oDqLxolPSasz7WAMW1jN4qtWJ0mFzwO83V6
+kaBe+wF6Kqir6udFSBW9rPcFg6/VZXPltT0a6uacIHq6DyQ5iMW4YQWbVy9OR2rN
+GkYo1JCBR0XdRJYCSX3yB4TWv/eXnZ37/WjmiTOIZh35rjs+NuU/S5JPDfAp2/k7
+0DevQeBsv+UjVXjWpNTZmPbvDnd995uSmC6UY4hzyP84ORYMYn9n1QAR0goxDN6U
+unOf9Rlp1oMzdxMool/d1MlCxg2h3jheuhv7lgUF4KpvHOuEPXQ7UO417E0TYcDZ
+1J8Nsv87SZeEuQENBFrsKEkBCADjoEBhG/QPqZHg8VyoD1xYRAWGxyDJkX/GrSs6
+yE+x2hk5FoQCajxKa/d4AVxOnJpdwhAfeXeSNaql5Ejgzax+Tdj9BV6vtGVJVv0p
+O7bgAiZxkA6RHxtNqhpPnPQoXvUzkzpRgpuL+Nj4yIg7z1ITH6KQH4u5SI9vd+j/
+8i9Taz67pdZwuJjac8qBuJHjzAo1bjYctFYUSG5pbmMQyNLySzgiNkFa4DajODlt
+3RuqVGP316Fk+Sy2+60tC/HlX8jgMyMONfOGBQx6jk8tvAphS/LAqrrNepnagIyL
+UGKU+L8cB2g1PGGp2biBFWqZbudZoyRBet/0yH/zirBdQJw1ABEBAAGJATwEGAEI
+ACYWIQTUFzW5Ejb9uIIEjFojAWNe7/DLBQUCWuwoSQIbDAUJA8JnAAAKCRAjAWNe
+7/DLBf0pCACPp5hBuUWngu2Hqvg+tNiujfsiYzId3MffFxEk3CbXeHcJ5F32NDJ9
+PYCnra4L8wSv+NZt9gIa8lFwoFSFQCjzH7KE86XcV3MhfdJTNb/+9CR7Jq3e/4Iy
+0N5ip7PNYMCyakcAsxvsNCJKrSaDuYe/OAoTXRBtgRWE2uyT315em02Lkr+2Cc/Q
+k6H+vlNOHGRgnpI/OZZjnUuUfBUvMGHr1phW+y7aeymC9PnUGdViRdJe23nntMSD
+A+0/I7ESO9JsWvJbyBmuiZpu9JjScOjYH9xpQLqRNyw4WHpZriN69F0t9Mmd7bM1
++UyPgbPEr0iWMeyctYsuOLeUyQKMscDT
+=QyY6
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/lang/js/BrowserTestExtension/testkey.sec b/lang/js/BrowserTestExtension/testkey.sec
new file mode 100644
index 00000000..ced8f3ec
--- /dev/null
+++ b/lang/js/BrowserTestExtension/testkey.sec
@@ -0,0 +1,57 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQOYBFrsKEkBCADKw4Wt8J6M/88qD8PO6lSMCxH1cpwH8iK0uPaFFYsJkkXo7kWf
+PTAtrV+REqF/o80dvYcdLvRsV21pvncZz/HXLu1yQ18mC3XObrKokbdgrTTKA5XE
+BZkNsqyaMMJauT18H4hYkSg62/tTdO1cu/zWv/LFf7Xyn6+uA74ovXCJlO1s0N2c
+PShtr98QRzPMf2owgVk37JnDNp4gGVDGHxSZOuUwxgYAZYnA8SFc+c+3ZrQfY870
++O4j3Mz4p7yD13AwP4buQLBsb/icxekeQCqpRJhLH9f7MdEcGXa1x36RcEkHdu+M
+yJ392eMgD+dKNfRCtyTPhjZTxvbNELIBYICfABEBAAEAB/wLJ0gyMjs2fFfT83wM
+5Lzz2yQIwV4t3bblBAujdHTqeN5Zmsm/oakFyjSokULK96Kv0R4ej9eoIgMFvxFk
+HRkrggxTrbsNJ7I6QcKYHTPeIIj318ykNL6fj0WJUcdPIENukXl5jbqNyk3/4D2y
+TTDySyq6jHTgvMH4K4KJUSpglvSJPntTk9RhuFGHAF+sNR9atygDYctAaERMRtSg
+LCoSt/AoX5GRMlQjXT9oqQjwSQoZyF4s8HMC8wdTFIE/E0L4IVdHVp8sz2UszNtT
+W/evmCA+KVruKjRH/Fhrq4hHkEamW28+j4L6uAyagONP7BONs+S5Oo2zTT9+tV2R
+ILTZBADdgLuAgF6C5Lu9jCF6DfFgaT/uafMyQNkEGNlxOHMWHTgLHe475V2eG9gA
+amd4yXKyEFKU1PWnvlGuicQSGdzVcwmq61msvXgYD0FK3LP3yWzKnE4X1tzrC9Vp
+/uHJxKjewCuyt1f5in919v+T8TbUxBYKC0zX/qWtX+10cTx77QQA6leqhToJ95Yc
+u4UBrKMEO+y2v8Svb3LG7yI5oY8tkw0EkJ/kpZ8xTAfZYCe6fXdvVE3PHg2lrxyc
+Wv/EU3QY/qA3G82mbXYeJ2jNZaTNYo4MylMrt4Mx25x4ke7JlsE8SVrQ+4CrHkqp
+OjSIa7fppLrQ78uW980AtN8NNQGrlTsD/A9aoA60Igxy1Q3K2uSyDCyjLknv57ym
+ZSBD3/t7m0l6Q6gbdfhNGosT+Hd4y3actqEqzXZHW2VG4dKZ/wRNkxtSm9adU9vs
+EHyzxjb6mKIH32zAG5TaFT20hC+NK6lsyHr9UE2ZrS6ma2sLxGW2O40hqNsdD+5m
+NrqeBc2I/js1PMK0EHRlc3RAZXhhbXBsZS5vcmeJAVQEEwEIAD4WIQTUFzW5Ejb9
+uIIEjFojAWNe7/DLBQUCWuwoSQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIe
+AQIXgAAKCRAjAWNe7/DLBf9kB/wOQ/S60HGwFq07W9N01HWULyhHKoMmcHL6rfZ6
+4oDqLxolPSasz7WAMW1jN4qtWJ0mFzwO83V6kaBe+wF6Kqir6udFSBW9rPcFg6/V
+ZXPltT0a6uacIHq6DyQ5iMW4YQWbVy9OR2rNGkYo1JCBR0XdRJYCSX3yB4TWv/eX
+nZ37/WjmiTOIZh35rjs+NuU/S5JPDfAp2/k70DevQeBsv+UjVXjWpNTZmPbvDnd9
+95uSmC6UY4hzyP84ORYMYn9n1QAR0goxDN6UunOf9Rlp1oMzdxMool/d1MlCxg2h
+3jheuhv7lgUF4KpvHOuEPXQ7UO417E0TYcDZ1J8Nsv87SZeEnQOYBFrsKEkBCADj
+oEBhG/QPqZHg8VyoD1xYRAWGxyDJkX/GrSs6yE+x2hk5FoQCajxKa/d4AVxOnJpd
+whAfeXeSNaql5Ejgzax+Tdj9BV6vtGVJVv0pO7bgAiZxkA6RHxtNqhpPnPQoXvUz
+kzpRgpuL+Nj4yIg7z1ITH6KQH4u5SI9vd+j/8i9Taz67pdZwuJjac8qBuJHjzAo1
+bjYctFYUSG5pbmMQyNLySzgiNkFa4DajODlt3RuqVGP316Fk+Sy2+60tC/HlX8jg
+MyMONfOGBQx6jk8tvAphS/LAqrrNepnagIyLUGKU+L8cB2g1PGGp2biBFWqZbudZ
+oyRBet/0yH/zirBdQJw1ABEBAAEAB/4lN3gXOI4OuoOcsvHak4pebx61Mt0YP9cT
+qZASIBqxok5x8E28pFh/tYfkYdqRCtdNYZOnxcEoUWh5j6nfwZkEnJ9P/T8GPNk7
+pMKnKXmExi05b5uGHD8nU1rSbf/YkvAF0vpbxd4/RDxbbtQhbUwGzusSI+pBLM0w
+5TreEB+vRGBc2gOvXXOtKLNEa7M9rH2EwbAkP3jOGGwgk6adxbQdBcRxq4merqhL
+YrVz73bCj8TDc0fsNJyIaZZJ++ejfBFYavsF1pvx9z7FNFi8rSXoiB3SBtaWGfhr
+bwNaMZrDc7TRIq/fgGaL6g//bzcWrr1YaHXZ10Bgx6UymDOlYkCpBADm0Hv46sPw
+07SO8+IACcaQliOto1pndOPwTimCeo58/7rf8I2a5uuJloGrnPwAX65bKDnUALp6
+X3lnXRNMhnB3Uewx4i00LQmjsxhJfQiGLpMv0j58tn64s7GqQzGVV1JKcQm992RV
+jFOydyjZ+K4LGWEOITG/bZrMEVNGCM+OnQQA/Haz8xN0NFSlq7tyfFc0pkx/TiCX
+xGfBqbO0wU2b5GMnZbY/06HENpidIzpa231VQaw5/nPTvfhlLKW1iGAkc148cX1q
+lL9w2ksXuaHR3LXud2VcfVTIdxU/7h7u1dD/85+c0+7jlGObD9cXKxlM6OjpIJz1
+l5/1h3C5S0TuxHkEAL/3BGihkhNfv1Xx0rWu0/732usX/nE/A9C26hGu41FUf3fp
+0ilonKpKZUEwWt5hWSEFCSrznNVekiO0rxvuu3RVegvzThPNU4Pf4JZtJpRVhvUQ
+d9ulxJw7V9rs75uNBatTNC0kXuGoXhehw4Bn93xa67gYGd3LfrH+oT0GCDpTSHCJ
+ATwEGAEIACYWIQTUFzW5Ejb9uIIEjFojAWNe7/DLBQUCWuwoSQIbDAUJA8JnAAAK
+CRAjAWNe7/DLBf0pCACPp5hBuUWngu2Hqvg+tNiujfsiYzId3MffFxEk3CbXeHcJ
+5F32NDJ9PYCnra4L8wSv+NZt9gIa8lFwoFSFQCjzH7KE86XcV3MhfdJTNb/+9CR7
+Jq3e/4Iy0N5ip7PNYMCyakcAsxvsNCJKrSaDuYe/OAoTXRBtgRWE2uyT315em02L
+kr+2Cc/Qk6H+vlNOHGRgnpI/OZZjnUuUfBUvMGHr1phW+y7aeymC9PnUGdViRdJe
+23nntMSDA+0/I7ESO9JsWvJbyBmuiZpu9JjScOjYH9xpQLqRNyw4WHpZriN69F0t
+9Mmd7bM1+UyPgbPEr0iWMeyctYsuOLeUyQKMscDT
+=hkUm
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
index 7abf2d94..e28dd66b 100644
--- a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -24,30 +24,144 @@ describe('Encryption and Decryption', function () {
prm.then(function (context) {
context.encrypt(
inputvalues.encrypt.good.data,
- inputvalues.encrypt.good.fingerprint).then( function(answer){
+ 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){
+ 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();
});
});
});
});
+ /**
+ * Fails with random data! Some bytes (up to 100) of the original are missing in
+ * the result
+*/
/**
- * Fails with random data! Some bytes (up to 100) of the original are missing in
- * the result
- */
+ for (let j = 0; j < 10; j++){
+ it('Successful encrypt and decrypt specific sets: ',
+ function (done) {
+ let prm = Gpgmejs.init();
+ let data = bigBoringString(5); //see ./inputvalues.js
+ expect(Object.prototype.toString.call(data)).to.equal("[object String]");
+ 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) {
+ if (data.length !== result.data.length) {
+
+ for (let k = 0; k < data.length; k++) {
+ if (data[k] !== result.data[k]) {
+ console.log(k);
+ console.log(data[k - 2] + data[k - 1] + data[k] + data[k + 1]);
+ console.log(result.data[k - 2] + result.data[k - 1] + result.data[k] + result.data[k + 1]);
+ break;
+ }
+ }
+ }
+ 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);
+ }
+
+
+ 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(3000);
+
+ it('Test with simple non-ascii input',
+ function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ let data = '';
+ for (let i=0; i < 1024 * 1024 * 0.1; i++){
+ data += inputvalues.encrypt.good.data_nonascii;
+ }
+ 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');
+ console.log(answer);
+ context.decrypt(answer.data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ if (data.length !== result.data.length) {
+
+ for (let k = 0; k < data.length; k++) {
+ if (data[k] !== result.data[k]) {
+ console.log(k);
+ console.log(data[k - 2] + data[k - 1] + data[k] + data[k + 1]);
+ console.log(result.data[k - 2] + result.data[k - 1] + result.data[k] + result.data[k + 1]);
+ break;
+ }
+ }
+ }
+ console.log(data.length - result.data.length);
+ expect(result.data).to.equal(data);
+ context.connection.disconnect();
+ done();
+
+ });
+ });
+ });
+ }).timeout(3000);
+*/
/**
- for (let i=0; i< 20; i++) {
- it('Successful encrypt 1 MB '+ i+ '/20', function (done) {
+ for (let i=0; i< 100; i++) {
+ it('Successful encrypt random data '+ (i+1) + '/100', function (done) {
let prm = Gpgmejs.init();
- let data = bigString(0.1);
+ let data = bigString(0.2); // << set source data here
prm.then(function (context) {
context.encrypt(data,
inputvalues.encrypt.good.fingerprint).then(
@@ -63,10 +177,62 @@ describe('Encryption and Decryption', function () {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(data);
+ context.connection.disconnect();
done();
});
});
});
- }).timeout(10000);
- };*/
+ }).timeout(5000);
+ };
+*/
+
+/** still fails
+ it('Successful encrypt 0.8 MB Uint8Array', function (done) {
+ let prm = Gpgmejs.init();
+ let data = bigUint8(0.8);
+ 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);
+ done();
+ });
+ });
+ });
+ }).timeout(5000);
+*/
+
+ it('Decrypt simple non-ascii',
+ function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ 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(inputvalues.encrypt.good.data_nonascii);
+ context.encrypt(inputvalues.encrypt.good.data_nonascii, inputvalues.encrypt.good.fingerprint).then(
+ function(result){
+ context.decrypt(result.data).then(function(answer){
+ expect(answer.data).to.equal(inputvalues.encrypt.good.data_nonascii);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+
+ });
+ }).timeout(8000);
+
});
\ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
index 37b319ed..2e95151b 100644
--- a/lang/js/BrowserTestExtension/tests/encryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -28,6 +28,7 @@ describe('Encryption', function () {
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();
});
});
@@ -44,11 +45,13 @@ describe('Encryption', function () {
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(5000);
+/**
it('Successful encrypt 20 MB', function (done) {
let prm = Gpgmejs.init();
let data = bigString(20);
@@ -60,11 +63,12 @@ describe('Encryption', function () {
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 30 MB', function (done) {
// TODO: There seems to be a limit imposed at least by chrome at about 21 MB
@@ -78,6 +82,7 @@ describe('Encryption', function () {
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();
});
});
@@ -94,6 +99,7 @@ describe('Encryption', function () {
}, function(error){
expect(error).to.be.an('Error');
expect(error.code).to.equal('MSG_INCOMPLETE');
+ context.connection.disconnect();
done();
});
});
@@ -108,6 +114,7 @@ describe('Encryption', function () {
}, function (error) {
expect(error).to.be.an.instanceof(Error);
expect(error.code).to.equal('MSG_INCOMPLETE');
+ context.connection.disconnect();
done();
});
});
@@ -125,10 +132,11 @@ describe('Encryption', function () {
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 ( >= 48MB) is rejected', function (done) {
let prm = Gpgmejs.init();
@@ -141,6 +149,7 @@ describe('Encryption', function () {
expect(error).to.be.an.instanceof(Error);
// TODO who is throwing the error here?
// It is not a GPGME_Error!
+ context.connection.disconnect();
done();
});
});
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
index 3cd1e92a..bc8c97bb 100644
--- a/lang/js/BrowserTestExtension/tests/inputvalues.js
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -22,7 +22,8 @@ var inputvalues = {
encrypt: {
good:{
data : 'Hello World.',
- fingerprint : 'CDC3A2B2860625CCBFC5A5A9FC6D1B604967FC40'
+ fingerprint : 'D41735B91236FDB882048C5A2301635EEFF0CB05',
+ data_nonascii: '¡Äußerste µ€ før ñoquis@hóme! Добрый день\n'
},
bad: {
fingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A'
@@ -42,3 +43,62 @@ function bigString(megabytes){
}
return new TextDecoder('utf-8').decode(uint);
}
+
+function bigUint8(megabytes){
+ let maxlength = 1024 * 1024 * megabytes;
+ let uint = new Uint8Array(maxlength);
+ for (let i= 0; i < maxlength; i++){
+ uint[i] = Math.random() * Math.floor(256);
+ }
+ return uint;
+}
+
+function bigBoringString(megabytes){
+ let maxlength = 1024 * 1024 * megabytes;
+ let string = '';
+ let chars = ' ä0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ for (let i= 0; i < maxlength; i++){
+ string = string + chars[Math.floor(Math.random() * chars.length)];
+ }
+ return string;
+}
+
+function slightlyLessBoringString(megabytes, set){
+ let maxlength = 1024 * 1024 * megabytes;
+ let string = '';
+ let chars = '';
+ if (!set){
+
+ } else if (set ===1 ) {
+ chars = '\n\"\r \'';
+ } else if (set === 2 ) {
+ chars = '()=?`#+-{}[]';
+ } else if (set === 3){
+ chars = '^°/';
+ //'*<>\\^°/';
+ } else if (set ===4) {
+ chars = 'äüßµüþÖ~ɁÑ||@';
+ } else {
+ chars = '*<>\n\"\r§$%&/()=?`#+-{}[] \''; //fails!
+
+ }
+ for (let i= 0; i < maxlength; i++){
+ string = string + chars[Math.floor(Math.random() * chars.length)];
+ }
+ return string;
+}
+
+var encryptedData =
+ '-----BEGIN PGP MESSAGE-----\n' +
+ '\n' +
+ 'hQEMA6B8jfIUScGEAQgAlANd3uyhmhYLzVcfz4LEqA8tgUC3n719YH0iuKEzG/dv\n' +
+ 'B8fsIK2HoeQh2T3/Cc2LBMjgn4K33ksG3k2MqrbIvxWGUQlOAuggc259hquWtX9B\n' +
+ 'EcEoOAeh5DuZT/b8CM5seJKNEpPzNxbEDiGikp9DV9gfIQTTUnrDjAu5YtgCN9vA\n' +
+ '3PJxihioH8ODoQw2jlYSkqgXpBVP2Fbx7qgTuxGNu5w36E0/P93//4hDXcKou7ez\n' +
+ 'o0+NEGSkbaY+OPk1k7k9n+vBSC3F440dxsTNs5WmRvx9XZEotJkUBweE+8XaoLCn\n' +
+ '3RrtyD/lj63qi3dbyI5XFLuPU1baFskJ4UAmI4wNhdJ+ASailpnFBnNgiFBh3ZfB\n' +
+ 'G5Rmd3ocSL7l6lq1bVK9advXb7vcne502W1ldAfHgTdQgc2CueIDFUYAaXP2OvhP\n' +
+ 'isGL7jOlDCBKwep67ted0cTRPLWkk3NSuLIlvD5xs6L4z3rPu92gXYgbZoMMdP0N\n' +
+ 'kSAQYOHplfA7YJWkrlRm\n' +
+ '=zap6\n' +
+ '-----END PGP MESSAGE-----\n';
\ No newline at end of file
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index d26aab18..b71004a5 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -110,7 +110,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
}
return null;
} else if (code === 'GNUPG_ERROR'){
- return new GPGME_Error(code, info.msg);
+ return new GPGME_Error(code, info);
}
else {
return new GPGME_Error('GENERIC_ERROR');
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index 9475b2b0..c1a01377 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -170,18 +170,19 @@ function putData(message, data){
if (!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));
+ message.setParameter ('data', btoa(data));
} else if (typeof(data) === 'string') {
message.setParameter('base64', false);
message.setParameter('data', data);
- } else if ( typeof(data) === 'object' && data.hasOwnProperty(getText)){
+ } 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));
+ message.setParameter ('data', btoa(txt));
+ }
+ else {
+ return gpgme_error('PARAM_WRONG');
}
} else {
return gpgme_error('PARAM_WRONG');
--
cgit v1.2.3
From c92326cc257cf7c8b6c0ddc43ec81573c409bc64 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Wed, 9 May 2018 19:40:57 +0200
Subject: js: more testing of nativeMessaging connection
--
* There were some inconsistencies between utf-8, transfer and browsers'
utf16, which broke characters that were split between individual
messages. src/Connection now contains a workaround that reassembles
javascripts' format from passed base64 strings. This needs someone
more experienced looking.
* Added several new tests which were failing during initial debugging
of this issue
* reorganized BrowsertestExtension to avoid cluttering.
---
lang/js/BrowserTestExtension/browsertest.html | 3 -
lang/js/BrowserTestExtension/index.html | 40 +++++
lang/js/BrowserTestExtension/longTests.html | 22 +++
lang/js/BrowserTestExtension/popup.js | 2 +-
lang/js/BrowserTestExtension/runbrowsertest.js | 1 -
lang/js/BrowserTestExtension/rununittests.js | 21 +++
.../tests/encryptDecryptTest.js | 191 +++++----------------
lang/js/BrowserTestExtension/tests/encryptTest.js | 2 +-
lang/js/BrowserTestExtension/tests/inputvalues.js | 119 ++++++++++++-
.../BrowserTestExtension/tests/longRunningTests.js | 53 ++++++
lang/js/BrowserTestExtension/unittests.html | 17 ++
lang/js/src/Connection.js | 19 +-
12 files changed, 328 insertions(+), 162 deletions(-)
create mode 100644 lang/js/BrowserTestExtension/index.html
create mode 100644 lang/js/BrowserTestExtension/longTests.html
create mode 100644 lang/js/BrowserTestExtension/rununittests.js
create mode 100644 lang/js/BrowserTestExtension/tests/longRunningTests.js
create mode 100644 lang/js/BrowserTestExtension/unittests.html
diff --git a/lang/js/BrowserTestExtension/browsertest.html b/lang/js/BrowserTestExtension/browsertest.html
index d12e03cf..c379ef53 100644
--- a/lang/js/BrowserTestExtension/browsertest.html
+++ b/lang/js/BrowserTestExtension/browsertest.html
@@ -12,14 +12,11 @@
-
-
-
diff --git a/lang/js/BrowserTestExtension/index.html b/lang/js/BrowserTestExtension/index.html
new file mode 100644
index 00000000..05d413ba
--- /dev/null
+++ b/lang/js/BrowserTestExtension/index.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+ gpgmejs - Tests
+
+ The unittests rely on a separately packaged version of gpgmejs,
+ with the different classes and functions exposed. These tests and their
+ input values can be found in gpgme/lang/js/test. They do not test the
+ overall functionality, but the individual behaviour of the components.
+
+
+
+ The functionality tests, to be found in
+ gpgme/lang/js/BrowserTestExtension, check the overall functionality of
+ the standard packaged version of gpgmejs.
+
+
+
+
diff --git a/lang/js/BrowserTestExtension/longTests.html b/lang/js/BrowserTestExtension/longTests.html
new file mode 100644
index 00000000..8ff969b7
--- /dev/null
+++ b/lang/js/BrowserTestExtension/longTests.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+ Browsertest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lang/js/BrowserTestExtension/popup.js b/lang/js/BrowserTestExtension/popup.js
index 4764df55..12beb1eb 100644
--- a/lang/js/BrowserTestExtension/popup.js
+++ b/lang/js/BrowserTestExtension/popup.js
@@ -39,6 +39,6 @@
document.addEventListener('DOMContentLoaded', function() {
chrome.tabs.create({
- url: './browsertest.html'
+ url: './index.html'
});
});
diff --git a/lang/js/BrowserTestExtension/runbrowsertest.js b/lang/js/BrowserTestExtension/runbrowsertest.js
index 308c716d..39bc3fb9 100644
--- a/lang/js/BrowserTestExtension/runbrowsertest.js
+++ b/lang/js/BrowserTestExtension/runbrowsertest.js
@@ -19,4 +19,3 @@
*/
mocha.run();
-Gpgmejs_test.unittests();
diff --git a/lang/js/BrowserTestExtension/rununittests.js b/lang/js/BrowserTestExtension/rununittests.js
new file mode 100644
index 00000000..f85ed8b5
--- /dev/null
+++ b/lang/js/BrowserTestExtension/rununittests.js
@@ -0,0 +1,21 @@
+/* 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+
+ */
+Gpgmejs_test.unittests();
+mocha.run();
diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
index e28dd66b..a66e1534 100644
--- a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -19,7 +19,7 @@
*/
describe('Encryption and Decryption', function () {
- it('Successful encrypt and decrypt', function (done) {
+ it('Successful encrypt and decrypt simple string', function (done) {
let prm = Gpgmejs.init();
prm.then(function (context) {
context.encrypt(
@@ -39,54 +39,6 @@ describe('Encryption and Decryption', function () {
});
});
});
-
- /**
- * Fails with random data! Some bytes (up to 100) of the original are missing in
- * the result
-*/
-/**
- for (let j = 0; j < 10; j++){
- it('Successful encrypt and decrypt specific sets: ',
- function (done) {
- let prm = Gpgmejs.init();
- let data = bigBoringString(5); //see ./inputvalues.js
- expect(Object.prototype.toString.call(data)).to.equal("[object String]");
- 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) {
- if (data.length !== result.data.length) {
-
- for (let k = 0; k < data.length; k++) {
- if (data[k] !== result.data[k]) {
- console.log(k);
- console.log(data[k - 2] + data[k - 1] + data[k] + data[k + 1]);
- console.log(result.data[k - 2] + result.data[k - 1] + result.data[k] + result.data[k + 1]);
- break;
- }
- }
- }
- 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);
- }
-
-
it('Roundtrip does not destroy trailing whitespace',
function (done) {
let prm = Gpgmejs.init();
@@ -112,88 +64,22 @@ describe('Encryption and Decryption', function () {
});
});
});
- }).timeout(3000);
-
- it('Test with simple non-ascii input',
- function (done) {
- let prm = Gpgmejs.init();
- prm.then(function (context) {
- let data = '';
- for (let i=0; i < 1024 * 1024 * 0.1; i++){
- data += inputvalues.encrypt.good.data_nonascii;
- }
- 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');
- console.log(answer);
- context.decrypt(answer.data).then(
- function (result) {
- expect(result).to.not.be.empty;
- expect(result.data).to.be.a('string');
- if (data.length !== result.data.length) {
-
- for (let k = 0; k < data.length; k++) {
- if (data[k] !== result.data[k]) {
- console.log(k);
- console.log(data[k - 2] + data[k - 1] + data[k] + data[k + 1]);
- console.log(result.data[k - 2] + result.data[k - 1] + result.data[k] + result.data[k + 1]);
- break;
- }
- }
- }
- console.log(data.length - result.data.length);
- expect(result.data).to.equal(data);
- context.connection.disconnect();
- done();
+ }).timeout(5000);
- });
- });
- });
- }).timeout(3000);
-*/
-/**
- for (let i=0; i< 100; i++) {
- it('Successful encrypt random data '+ (i+1) + '/100', function (done) {
- let prm = Gpgmejs.init();
- let data = bigString(0.2); // << set source data here
+ 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(5000);
- };
-*/
-
-/** still fails
- it('Successful encrypt 0.8 MB Uint8Array', function (done) {
- let prm = Gpgmejs.init();
- let data = bigUint8(0.8);
- prm.then(function (context) {
- context.encrypt(data,
- inputvalues.encrypt.good.fingerprint).then(
- function (answer){
+ function (answer) {
expect(answer).to.not.be.empty;
expect(answer.data).to.be.a("string");
expect(answer.data).to.include(
@@ -201,38 +87,39 @@ describe('Encryption and Decryption', function () {
expect(answer.data).to.include(
'END PGP MESSAGE');
context.decrypt(answer.data).then(
- function(result){
+ function (result) {
expect(result).to.not.be.empty;
expect(result.data).to.be.a('string');
expect(result.data).to.equal(data);
- done();
- });
- });
- });
- }).timeout(5000);
-*/
-
- it('Decrypt simple non-ascii',
- function (done) {
- let prm = Gpgmejs.init();
- prm.then(function (context) {
- 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(inputvalues.encrypt.good.data_nonascii);
- context.encrypt(inputvalues.encrypt.good.data_nonascii, inputvalues.encrypt.good.fingerprint).then(
- function(result){
- context.decrypt(result.data).then(function(answer){
- expect(answer.data).to.equal(inputvalues.encrypt.good.data_nonascii);
context.connection.disconnect();
done();
});
+ });
+ });
+ }).timeout(5000);
+ };
+
+ it('Encrypt-decrypt simple non-ascii', function (done) {
+ //FAILS TODO: Check newline at the end
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ 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(inputvalues.encrypt.good.data_nonascii);
+ context.encrypt(inputvalues.encrypt.good.data_nonascii, inputvalues.encrypt.good.fingerprint).then(
+ function(result){
+ context.decrypt(result.data).then(function(answer){
+ expect(answer.data).to.equal('¡Äußerste µ€ før ñoquis@hóme! Добрый день');
+ context.connection.disconnect();
+ done();
});
});
+ });
- });
- }).timeout(8000);
+ });
+ }).timeout(6000);
-});
\ No newline at end of file
+});
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
index 2e95151b..5ef68a32 100644
--- a/lang/js/BrowserTestExtension/tests/encryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -49,7 +49,7 @@ describe('Encryption', function () {
done();
});
});
- }).timeout(5000);
+ }).timeout(10000);
/**
it('Successful encrypt 20 MB', function (done) {
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
index bc8c97bb..e23b7786 100644
--- a/lang/js/BrowserTestExtension/tests/inputvalues.js
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -23,7 +23,23 @@ var inputvalues = {
good:{
data : 'Hello World.',
fingerprint : 'D41735B91236FDB882048C5A2301635EEFF0CB05',
- data_nonascii: '¡Äußerste µ€ før ñoquis@hóme! Добрый день\n'
+ data_nonascii: '¡Äußerste µ€ før ñoquis@hóme! Добрый день',
+ data_nonascii_32: [
+ 'K€K€K€K€K€K€K€K€K€K€K€K€K€K€K€K€',
+ 'µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€', //fails result has 3 chars more
+ '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€', //fails 3 chars
+ '²³²³²³²³²³²³²³²³²³²³²³²³²³²³²³²³',
+ 'µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€A€µ€µ€µ€µ€', //fails 2 chars
+ 'µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µAµ€µ€µ€µ€', //is okay if 2 chunksizes.
+ 'üüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü',
+ 'µAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€',
+ 'µAAAAµAAAAAAAAAAAAAAAAAAAAAAAAA€',
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAµ€',
+ 'µAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA°',
+ '€AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€',
+ 'µ||||||||||||||||||||||||||||||€',
+ 'æſæſ³¼„¬“³³¬“¬½”æſæſ³¼„¬“³³¬“¬½”'
+ ]
},
bad: {
fingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A'
@@ -56,7 +72,7 @@ function bigUint8(megabytes){
function bigBoringString(megabytes){
let maxlength = 1024 * 1024 * megabytes;
let string = '';
- let chars = ' ä0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ let chars = ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (let i= 0; i < maxlength; i++){
string = string + chars[Math.floor(Math.random() * chars.length)];
}
@@ -101,4 +117,101 @@ var encryptedData =
'isGL7jOlDCBKwep67ted0cTRPLWkk3NSuLIlvD5xs6L4z3rPu92gXYgbZoMMdP0N\n' +
'kSAQYOHplfA7YJWkrlRm\n' +
'=zap6\n' +
- '-----END PGP MESSAGE-----\n';
\ No newline at end of file
+ '-----END PGP MESSAGE-----\n';
+var encryptedBroken = '-----BEGIN PGP MESSAGE-----\n' +
+'\n' +
+'hQEMA6B8jfIUScGEAQf/bUYF70KRCHWITfNH7zaYaLa8P+QoCo+NpFzc3U9J4mty\n' +
+'FxjIpoNwxEvQ9UUEMi6LgHhvURYCbWrCV5XYjo/sE66CRXsEuNirfYkAzXVNcUf7\n' +
+'BaAzio/QzyyvBfzwHHqMLSxAcNggs+f5lob+TcBnBghwpn1lh5BgNUuhDKVq21/F\n' +
+'wWK4rqjmmjrpoR3tKcl916+/Z0VI5SAkPG4IrWUfumxG0xbePB9IFT8uGMmXy2qr\n' +
+'ICmEfPakLUIo7NLrdMNInnVQaAeNS/5u5TbpZpRxZWtRP7m4EyUoEA+TgSkp+hG8\n' +
+'Um7hmbFsB99H0yiyCSLicN5AxzmgCrL3D77Fqh7LaNLsAYjcyVZm+R7te4vwpv9P\n' +
+'F/MCAEUFKGfNYHqyVjBhBlm4/PMC+YtOE9jF920hwtDckT/V3L2POk1Kr78+nVjw\n' +
+'1HXTfK/Tk6QMGrzCd2ril5aB2RCi+Fr41B2ftS8SLwcrnrFkP2enH6VYBserx5l8\n' +
+'qZlgRR53QNnLvqnn7h/NO1ZNN5cnD2pf0PWBkSHmr5ph82JQ+XyB0h4eV1kwX80K\n' +
+'8IkBAq6hFpfm7TU4gy5x1VNTeVoCRdlzESkzVwbvjNZ+OU6+vcpfCaHMbuVBUmYz\n' +
+'xjTKYlenevSzwfF1RY7noDTrPUQrBrVor2cPjN3ROLCbFpARrQf44BfzGaq5XdWc\n' +
+'NZWFgiRKVGVJQeBQjRyqHAv4e8rkcr5qwnY8kyZpLYAKIVBgtqnh7GExaW5efWRG\n' +
+'tyJMgUuP+dF/+HymhlEmMKZabLf5W8J3p8+uBOkU359OX/HOS8mPr6a7bnI4895W\n' +
+'7Dt5vkpHRR81V1Le0+Mtcj7G46hsvFMA0dgw29mBbaOA8fhOrumqTBOh01lZliwI\n' +
+'6/OF6iqAeBAH3hJQlodCACf1yTxHynF6Ro/SnIa/3BN4CN4PPRHdLMHBJevRm3Ih\n' +
+'CbqXVmSdtrihHsViPKjc8+u+7g2n/lt9LHrMyOmptyVX8vT9B/AQYHxf0FDmv4Vg\n' +
+'62Mo+eDRWZF+XmKPQYedM6nF5hcyxc/1aCM4yXtu8qQir/GDvyghPbfnKkium5kk\n' +
+'+XOb+aIUsxbNzhdLowp2mZcy1MYMPHIJNjIXmVjPnc/GwB8S2SX/gHn1quz52ENq\n' +
+'l12ome7rfAp9JkrVbHOK11iDPbd3UdHSTfFNO8wQrxtqnZhUwqLhZwteOi4EGSSh\n' +
+'OrWihjdonqL0qcfiS6N9QemJz2w40fR8ZwDuGvPgl6LeNtKjihyqsWvh+zJzwmwM\n' +
+'R2Y50wNyvQnXGH4RJJUQVAKO/vMp63K2j3DnHsyz/XLbmp25QGn9f1QIjfplY64D\n' +
+'q3lp2W6GvhpYWLRzBfIo6ebwLtqHTsTgON9TA4CD+1QbOXMIxQKAb9hhzEtp/5zN\n' +
+'+gJhF4pOvEu5Cg1j9CtXh93iE0J9rwrjyMujzBSiaoqxHabXtRarv8d2v/w75AKh\n' +
+'6Avt+WFYRdSLKCstdHeuREXEibIaM55nUUIEO0v9kcb0Y7LyH/vFVGAo0QFh3u+t\n' +
+'zMupQwywjeuuUwM18KeWjKrhGuRf1WWCDRnnH1yEztDPLx5kyxadsC31/XyqLjYl\n' +
+'zt+vUSm+JrXujhba9VaYO3DSB9hL0qdrA3gaK2DAl2nvFGRn0fjtw0xfa9VJlafN\n' +
+'JLosw7MDDEFx962vHbx5XfjJRGaEdDnsco5E5VUkQ+RjhWWrzMHpIPYWYacXiUKr\n' +
+'TcNTAg1jR5M2FRz/QOk7qsTl98RyNCYXTUmuPh/pLJI0kJ5rtTPrlzFNgVjwiYEJ\n' +
+'+iNITXhqx5KJ5ifY89BXeNVavIb1Tp0xc1+637U/ztH9D0Jp6m0w/VIHW+881Ik3\n' +
+'fMKw8A/RuEdTil/PU0bjVRNYLS/KCQCqrlYdItYh57IAkt+sQNxvw0xg46QN+OkO\n' +
+'QHKnIazexhGAqyBe6c2KYuRLW46h9grGbCJnqvmoThBRrqL7twmp00O846tvRms8\n' +
+'3QEXL3oXqBTH1d6bRd/E6m++X/n9I6VaKMgYe6GNQEqwvtSySFi65VK5cH1jnEGw\n' +
+'wr2ZkXUrVbNTfXci6SdNqh+W8DRnFvlRyKzG1jnibsOW5FwGSMT3kVRUvnnJbzlc\n' +
+'wj1cJC/NMvkoQtGHppHkMjE23byjBhJlZXBTbGc3kSOfXKAMAT7I9Dm/GgEpbbpD\n' +
+'4fgzqNEeWucrCWgbXviXt1pWOyNtudb9rHWgvIQlE9JeykPgvmg+pl4Av42lQTYp\n' +
+'kyNFjq46niWT9VsYlsW52x4jCQifT7HkxTuSaD9JyVqjQWS11rci9UM/NuoXfqrv\n' +
+'vJYMBJGhzTxFzzFCzSRSERbjN0iXJ2E8vFKkpd5nCZxRMz6XBMk1NVyrE956BMum\n' +
+'yNaSy5mwR+ekS3xM7oUdbqyyDwFEDxpPhtIRqRfFugpIn8tRy7jwDZB9mctFGfKo\n' +
+'th5dCzcaU0qPfUJWPVQVh2LCPneLGhLENgFUhoNZ+rzaf5SltLeB4vuVjZMLe+PW\n' +
+'KqtT9l6QFQajbe7pj99BScteaI8lpiQiNTvQq/LZRFWr9eb5z0Xk5Wc3aYZgymkp\n' +
+'EYxyVqwomyz4wPf2BrgsSdKk0OZKIkAxfA3i73tHvCsCQOHeriRMSfLzFN3J54nf\n' +
+'+MOuUm1hKLsLbPLQxOfzPiymVGp6DjYCkrRmafvZUJHkvGubvVVR5Yq0txznM1Vg\n' +
+'yZq4HoF3RGgKzJtk8N4me5YsVaM2/q+2B2ziVa/HeEFt/cZfcH/byY3ooW3OnAum\n' +
+'KTe/+T2BEjXfipmbIMA6iK3IKIoguuVwvSJz+5QfjMH1o8HIUdDOhnrbBBHmkvNK\n' +
+'MG+dV+oDijC2rL3n0qRURu4VWdk/bqKcaaLoZC5iDGLThZ20q+9jlFKahmlKe1WH\n' +
+'2Rch+JJfqSHtNYVKxZU0CC0I9Wg/Ws6TQJREKCiJf0/aTvxWRSHZtecFiZK7q+zn\n' +
+'NyRdWnqAv+HKRjN/tVZcf8I0CERswxmixF9uWMTjH+hq0u/h4It3I3tOObNyAQO3\n' +
+'iY9uSZEZbrKBSM3DqFF75toLjooWXU8yaC9so3mQVf5MnSZpG3PA5klwusLmi0QU\n' +
+'HD1eZ2aXUnTx7TbHuovWLjI40SIUKnaMAf0TCUHfBvJ5rLUPYez35QwrYRx0Qixn\n' +
+'Pcj7KCCXrT5cqwH64vGTiW6JCZJlLzneiE+dmnAT+wnNRNxbVooi6ejWce5HYbYd\n' +
+'c2SyBHJstGn0zuNN/248qhV+r5AMBgZ+vDilV8Bmdh3N/xlXBIgLIocegL6Kc+S0\n' +
+'Pr60DHKLcnZIunQwZOwyRb8wG9jV6I718CmbSw94gKNCi99B8BSDZ7z2ai+0yv44\n' +
+'ErR4Qp/gnCp9/6NXNmafluYn5Pgl9vZCozcJ8EN8mzD4szZBL19btecoT6Wcnve2\n' +
+'fYDRuYPWpT79QyRDSMSSzrQoFpezIOtPS2nrN+II81TxyTgOMY+jzR4TRJyMt185\n' +
+'7OG4t8Q+WOgzNS4clmPHnmgBBhsueWob72SvIgRtq5pQYB0fStx9qUDMZPnePdhS\n' +
+'rI+K82k1/eY5vTQ/eDXMN7UUfdLriuK0UXnJFu5CQSwrMD1u5nFVbQYC9PEwgdUc\n' +
+'XEASt9/jh2wDgSXAGegc6mLRI+Zu5H5ygpCIAMs8pNwFJ5DhCsve5RbalGEbYbuL\n' +
+'NwB1rRExCCUBjnAkpwNU0TL991y1Gn+gpN2lNvITq/BroE3HLjXbnEACTN+hwNPB\n' +
+'KJi38zKSb6/k27/zpTMuEKRXkSz4QuuviQbGJTmCbub+l2aVBQhVNwooGI92Gt8n\n' +
+'EQjGOzqeS4J0KQGZmhYRGVc7DdwjBYLV5pi1WkCIt1a1PDK9VZ4vzz978gLaxSZM\n' +
+'yozdL97g9wo0IJcAj+36b1Wewj+hL81t0SgIShEO0aIGSNDlFZM4mKQNmCUhvWuO\n' +
+'M1CpniR8cBN4MHUaQdBIlW2ua9Ba8JM7LNwcD8JddGvmUBwzFr5w4Hu4ylweacXP\n' +
+'5zUfZpJyFZKoxJe1cPY47NmXemOLuBVJRlThnUazvhM/KRxfyu2q4WOz6VSm6LEq\n' +
+'PFfr/NYH1AxIda/Z4tLLAs0nLbV+HrqRFMJOBGdY6dMxuvaiUutY3MZCMCKupz8f\n' +
+'yHh2p2lFy2jQvZs4HAKN6hTx8X7at1ue0RYw3hdjoPHa/NBKDzrkKjGInfraTVr6\n' +
+'qrxqW09/yNuiatISi+KxuBM4o9L/w85Zf01RNEZTS5zCKX0ml33JHgNxQgPosp+7\n' +
+'R0TUK2lANdKVTXJe8V/IT4tGUD4mg0EjMVRmFV2CL3LgBbW3ScOC15D4mzD14Yyb\n' +
+'KTUHwfX189GHKjJhHnSuZ3QgVKynoSII+0x4fiDHsdhdXdMj/qvVdZIMlABWKRD0\n' +
+'JVmrkFpzFtt4yXupl62+9ZYZehSKNKurlO4A8OBeg6xKDUKuvrI7Ug/2s5Q0pCxp\n' +
+'EgtxwOhhYrAhd8mN2ilKeB++JCAmZ2KwnwCGFF8kZ/5TOwWZHm/RNKEchTRC5kws\n' +
+'KsDUxq/19ORifzCA19f6Tc5s9HcPwxvnrscvb6LLTGGiROp3BlcitHjmPsH5bRUX\n' +
+'OAqV069l1JKeiCkGgQmlRviBGG0yO2zIcAeoDIPhaO4O0K6/VHo4p6kAlZAzWJuT\n' +
+'QmHI0ETyO+2m0jySoxW0EUU1FB3eQ4KBocneYqJUgCbOCeXf14TO8HekDtkfoKOK\n' +
+'bded3iCtnSAH6I9ERtPebqiWdR2tVCO4Yyqkf2f3vzCWrtyXHUWtZtC1I08HNLin\n' +
+'zGhEdQZ/VFCLP8CWmbtLU8BPeu88VTpw7i8G76QuHq5+0DY9eBgHWxcBYiwRisT/\n' +
+'DHXH0TvjuPedJ4F/sNmlktTXLLMqVu+J8i/qJ48E1r9wXkHTICnFy8jvm5MpQ4gu\n' +
+'rwzpyjSFLJZpzDMAxcPSXYGi1kchW+CDg/N/cdeYlVLCoBrUn6dEq6CC05Y6JmDW\n' +
+'t46R6lFHbQoq1WsMWZSKomB4WlxWP+hYDsssQOUR9Y7wwI4KXPtf6Ar9W2T9cSfO\n' +
+'mtDpgfeOVq/vE01TQGlZc4zwF5dcXBV3OLYBSXlv4JFIreOlKDi/IbPc6TYw0mbV\n' +
+'wFuzPi8VpHip3YoGdM7XUDvO1sE07FX8/xrEQVkJfzgl/v+mQ66TCb+/g13QPgZI\n' +
+'UftRS6hLeKNTd0pZc8+CTbNzgrCDGqbYn5ZpyPFYF+fVGZnqqLUid5NTjkwI1IoD\n' +
+'PgOSHQEo+pIlNfTtR2DCYgqOiMaBSZ4bc4b6SohAKGJkPhNmlMJ61MwGN2J8pFpl\n' +
+'1uG2MO3TUo6MxQAkCcKe4twwy1bQh4kO3kReUqTDW/VTnp6HfZhqtYc1tBGLcahu\n' +
+'C0ZX7B/8Wbu1PWN4Y34F7ouuSu2l6ASnoAc/Ek1S9R1uyiwLtaPuK58oUbVisDh3\n' +
+'cYmnjP0DelYq8FpJPWPrSGwqlERotf3KU3L1k84SHYUB1pHFYPF46KAKYH5qTrsO\n' +
+'T3id3CO3mt1gtgWAEGRkEQ+qVmvWtINBOwyFYVAD9ZqXflzF83ZGvdmvdJ6kzRZ7\n' +
+'fY5ACZGMghb3f4mfLlbF81WluDbk2k+t186qmRFrJFtJPvAl3VxXczo8pw5bSAdK\n' +
+'R6c7cagA6ql4QaYqtbIHpFbgz7iQ9ESe23Q2+o82lkTbUFdG+GDhnZFOL+ldWf/g\n' +
+'ufSCqY7IlNxj3hYxgTpaXb2lWvVVdo7C4VhPHyIDbQUCdUE80t2cDgJqPFABe3la\n' +
+'Y+UsW9W787mGGuuNSF/iI0tANw5twlQjdRQtqxnF1yETh/hFA4bgD9bmBOBFd+GT\n' +
+'+ECxkqI4/UYMgYfVMFja/e6+dQTWLblzuNaZh6wHASeNqpFmeQSBawBVV7qK3nC7\n' +
+'CDY9r6Aq9JYMiJTE/TzyfBmBhnxtL1aKTu6EHy3siDlID7EjQx1Xyr/EtbJCmsVl\n' +
+'E14StpggdK8=\n' +
+'=enm3\n' +
+'-----END PGP MESSAGE-----\n';
\ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/longRunningTests.js b/lang/js/BrowserTestExtension/tests/longRunningTests.js
new file mode 100644
index 00000000..0f32ca92
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/longRunningTests.js
@@ -0,0 +1,53 @@
+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);
+ 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(5000);
+ };
+
+ it('Successful encrypt 1 MB Uint8Array', function (done) {
+ let prm = Gpgmejs.init();
+ let data = bigUint8(1);
+ 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);
+ done();
+ });
+ });
+ });
+ }).timeout(5000);
+
+});
diff --git a/lang/js/BrowserTestExtension/unittests.html b/lang/js/BrowserTestExtension/unittests.html
new file mode 100644
index 00000000..6f7da3f1
--- /dev/null
+++ b/lang/js/BrowserTestExtension/unittests.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+ Unit tests
+
+
+
+
+
+
+
+
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 2c8792d6..64621f60 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -181,7 +181,8 @@ class Answer{
if (!this._response.hasOwnProperty(key)){
this._response[key] = '';
}
- this._response[key] = this._response[key].concat(msg[key]);
+ // console.log(msg[key]);
+ this._response[key] += msg[key];
}
//params should not change through the message
else if (poa.params.indexOf(key) >= 0){
@@ -214,6 +215,22 @@ class Answer{
* TODO: does not care yet if completed.
*/
get message(){
+ let keys = Object.keys(this._response);
+ let poa = permittedOperations[this.operation].answer;
+ for (let i=0; i < keys.length; i++) {
+ if (poa.data.indexOf(keys[i]) >= 0){
+ if (this._response.base64 == true){
+ let respatob = atob(this._response[keys[i]]);
+
+ let result = decodeURIComponent(
+ respatob.split('').map(function(c) {
+ return '%' +
+ ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+ }).join(''));
+ this._response[keys[i]] = result;
+ }
+ }
+ }
return this._response;
}
}
--
cgit v1.2.3
From 987b31746809dfe04966e37edd759a448a28d975 Mon Sep 17 00:00:00 2001
From: Maximilian Krambach
Date: Mon, 14 May 2018 16:23:24 +0200
Subject: js: Tests and improvements for openpgp mode
--
* Added openpgp - Mode tests to the browsertest Extension. These tests
require openpgp, which should not be a hard dependency for the main
project. Packing openpgpjs into the extension is still TODO
* Fixes:
- openpgp mode API now correctly handles parameters as an object,
similar to openpgpjs
- proper check and parsing of openpgpjs Message Objects
---
lang/js/BrowserTestExtension/index.html | 7 +
lang/js/BrowserTestExtension/openpgpModeTest.html | 23 +++
.../tests/encryptDecryptTest.js | 21 +--
.../tests/inputValues_openpgpjs.js | 32 ++++
lang/js/BrowserTestExtension/tests/inputvalues.js | 131 +++-----------
.../BrowserTestExtension/tests/longRunningTests.js | 1 +
.../BrowserTestExtension/tests/openpgpModeTest.js | 196 +++++++++++++++++++++
lang/js/src/Connection.js | 1 -
lang/js/src/gpgmejs.js | 18 +-
lang/js/src/gpgmejs_openpgpjs.js | 77 ++++----
10 files changed, 346 insertions(+), 161 deletions(-)
create mode 100644 lang/js/BrowserTestExtension/openpgpModeTest.html
create mode 100644 lang/js/BrowserTestExtension/tests/inputValues_openpgpjs.js
create mode 100644 lang/js/BrowserTestExtension/tests/openpgpModeTest.js
diff --git a/lang/js/BrowserTestExtension/index.html b/lang/js/BrowserTestExtension/index.html
index 05d413ba..c49aedae 100644
--- a/lang/js/BrowserTestExtension/index.html
+++ b/lang/js/BrowserTestExtension/index.html
@@ -34,6 +34,13 @@
Functionality tests with larger/longer running data sets.
+
+
+ Testing openPGP mode.
+ - Please notice that, due to comparing
+ the inputs and outputs with openpgpjs objects, this test
+ requires a copy of openpgpjs in libs.
+
+ Openpgp mode test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lang/js/BrowserTestExtension/openpgpModeTest.html b/lang/js/BrowserTestExtension/openpgpModeTest.html
new file mode 100644
index 00000000..e7a12be9
--- /dev/null
+++ b/lang/js/BrowserTestExtension/openpgpModeTest.html
@@ -0,0 +1,23 @@
+
+
+