aboutsummaryrefslogtreecommitdiffstats
path: root/lang/js
diff options
context:
space:
mode:
Diffstat (limited to 'lang/js')
-rw-r--r--lang/js/.babelrc1
-rw-r--r--lang/js/BrowserTestExtension/browsertest.html23
-rw-r--r--lang/js/BrowserTestExtension/index.html40
-rw-r--r--lang/js/BrowserTestExtension/longTests.html22
-rw-r--r--lang/js/BrowserTestExtension/manifest.json13
-rw-r--r--lang/js/BrowserTestExtension/popup.html9
-rw-r--r--lang/js/BrowserTestExtension/popup.js44
-rw-r--r--lang/js/BrowserTestExtension/runbrowsertest.js21
-rw-r--r--lang/js/BrowserTestExtension/rununittests.js21
-rw-r--r--lang/js/BrowserTestExtension/setup_testing.js22
-rw-r--r--lang/js/BrowserTestExtension/testicon.pngbin0 -> 16192 bytes
-rw-r--r--lang/js/BrowserTestExtension/testkey.pub30
-rw-r--r--lang/js/BrowserTestExtension/testkey.sec57
-rw-r--r--lang/js/BrowserTestExtension/tests/encryptDecryptTest.js225
-rw-r--r--lang/js/BrowserTestExtension/tests/encryptTest.js156
-rw-r--r--lang/js/BrowserTestExtension/tests/inputvalues.js143
-rw-r--r--lang/js/BrowserTestExtension/tests/longRunningTests.js40
-rw-r--r--lang/js/BrowserTestExtension/tests/startup.js54
-rw-r--r--lang/js/BrowserTestExtension/unittests.html17
-rw-r--r--lang/js/CHECKLIST25
-rw-r--r--lang/js/CHECKLIST_build3
-rw-r--r--lang/js/DemoExtension/entry.js25
-rw-r--r--lang/js/DemoExtension/maindemo.js55
-rw-r--r--lang/js/DemoExtension/mainui.html33
-rw-r--r--lang/js/DemoExtension/manifest.json14
-rw-r--r--lang/js/DemoExtension/popup.html9
-rw-r--r--lang/js/DemoExtension/testicon.pngbin0 -> 16192 bytes
-rw-r--r--lang/js/DemoExtension/ui.css10
-rw-r--r--lang/js/README52
-rw-r--r--lang/js/README_testing14
-rwxr-xr-xlang/js/build_extensions.sh17
-rw-r--r--lang/js/package.json16
-rw-r--r--lang/js/src/Config.js31
-rw-r--r--lang/js/src/Connection.js241
-rw-r--r--lang/js/src/Errors.js129
-rw-r--r--lang/js/src/Helpers.js103
-rw-r--r--lang/js/src/Key.js244
-rw-r--r--lang/js/src/Keyring.js162
-rw-r--r--lang/js/src/Message.js196
-rw-r--r--lang/js/src/gpgmejs.js192
-rw-r--r--lang/js/src/index.js86
-rw-r--r--lang/js/src/permittedOperations.js217
-rw-r--r--lang/js/unittest_inputvalues.js45
-rw-r--r--lang/js/unittests.js326
-rw-r--r--lang/js/webpack.conf.js35
-rw-r--r--lang/js/webpack.conf_unittests.js34
46 files changed, 3252 insertions, 0 deletions
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/BrowserTestExtension/browsertest.html b/lang/js/BrowserTestExtension/browsertest.html
new file mode 100644
index 00000000..c379ef53
--- /dev/null
+++ b/lang/js/BrowserTestExtension/browsertest.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link href="libs/mocha.css" rel="stylesheet" />
+ </head>
+<body>
+ <h3>Browsertest</h3>
+ <div id="mocha"></div>
+ <!-- load unit tests -->
+ <script src="libs/mocha.js"></script>
+ <script src="libs/chai.js"></script>
+ <script src="setup_testing.js"></script>
+ <script src="libs/gpgmejs.bundle.js"></script>
+ <script src="tests/inputvalues.js"></script>
+<!-- insert tests here-->
+ <script src="tests/startup.js"></script>
+ <script src="tests/encryptTest.js"></script>
+ <script src="tests/encryptDecryptTest.js"></script>
+<!-- run tests -->
+ <script src="runbrowsertest.js"></script>
+ </body>
+</html>
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 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link href="libs/mocha.css" rel="stylesheet" />
+ </head>
+<body>
+ <h3>gpgmejs - Tests</h3>
+ <p>
+ 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.
+ <ul>
+ <li>
+ <a href="unittests.html">
+ Unittests of the individual functions and classes.
+ </a>
+ </li>
+ </ul>
+ </p>
+ <p>
+ The functionality tests, to be found in
+ gpgme/lang/js/BrowserTestExtension, check the overall functionality of
+ the standard packaged version of gpgmejs.
+ <ul>
+ <li>
+ <a href="browsertest.html">
+ Functionality tests using the bundled library.
+ </a>
+ </li>
+ <li>
+ <a href="longTests.html">
+ Functionality tests with larger/longer running data sets.
+ </a>
+ </li>
+ </ul>
+ </p>
+ </body>
+</html>
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 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link href="libs/mocha.css" rel="stylesheet" />
+ </head>
+<body>
+ <h3>Browsertest</h3>
+ <div id="mocha"></div>
+ <!-- load unit tests -->
+ <script src="libs/mocha.js"></script>
+ <script src="libs/chai.js"></script>
+ <script src="setup_testing.js"></script>
+ <script src="libs/gpgmejs.bundle.js"></script>
+ <script src="tests/inputvalues.js"></script>
+<!-- insert tests here-->
+ <script src="tests/startup.js"></script>
+ <script src="tests/longRunningTests.js"></script>
+<!-- run tests -->
+ <script src="runbrowsertest.js"></script>
+ </body>
+</html>
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 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script src="popup.js"></script>
+ </head>
+ <body>
+ </body>
+</html> \ 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..12beb1eb
--- /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 <http://www.gnu.org/licenses/>.
+ * 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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+ chrome.tabs.create({
+ url: './index.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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+mocha.run();
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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+Gpgmejs_test.unittests();
+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 <http://www.gnu.org/licenses/>.
+ * 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
--- /dev/null
+++ b/lang/js/BrowserTestExtension/testicon.png
Binary files differ
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
new file mode 100644
index 00000000..2fe955e6
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -0,0 +1,225 @@
+
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+describe('Encryption and Decryption', function () {
+ it('Successful encrypt and decrypt simple string', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ inputvalues.encrypt.good.data,
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ context.decrypt(answer.data).then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(inputvalues.encrypt.good.data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ });
+
+ it('Decrypt simple non-ascii', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ let data = encryptedData;
+ context.decrypt(data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(
+ '¡Äußerste µ€ før ñoquis@hóme! Добрый день\n');
+ done();
+ });
+ });
+ }).timeout(3000);
+
+ it('Roundtrip does not destroy trailing whitespace',
+ function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ let data = 'Keks. \rKeks \n Keks \r\n';
+ context.encrypt(data,
+ inputvalues.encrypt.good.fingerprint).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ context.connection.disconnect();
+ done();
+
+ });
+ });
+ });
+ }).timeout(5000);
+
+ for (let j = 0; j < inputvalues.encrypt.good.data_nonascii_32.length; j++){
+ it('Roundtrip with >1MB non-ascii input meeting default chunksize (' + (j + 1) + '/' + inputvalues.encrypt.good.data_nonascii_32.length + ')',
+ function (done) {
+ let input = inputvalues.encrypt.good.data_nonascii_32[j];
+ expect(input).to.have.length(32);
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ let data = '';
+ for (let i=0; i < 34 * 1024; i++){
+ data += input;
+ }
+ context.encrypt(data,
+ inputvalues.encrypt.good.fingerprint).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ }).timeout(3000);
+ };
+
+ it('Random data, as string', function (done) {
+ let data = bigString(1000);
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(data,
+ inputvalues.encrypt.good.fingerprint).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ }).timeout(3000);
+
+ it('Data, input as base64', function (done) {
+ let data = inputvalues.encrypt.good.data;
+ let b64data = btoa(data);
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(b64data,
+ inputvalues.encrypt.good.fingerprint,).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(data).to.equal(data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ }).timeout(3000);
+
+ it('Random data, input as base64', function (done) {
+ //TODO fails. The result is
+ let data = bigBoringString(0.001);
+ let b64data = btoa(data);
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(b64data,
+ inputvalues.encrypt.good.fingerprint, true).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ }).timeout(3000);
+
+ it('Random data, input and output as base64', function (done) {
+ let data = bigBoringString(0.0001);
+ let b64data = btoa(data);
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(b64data,
+ inputvalues.encrypt.good.fingerprint).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data, true).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(b64data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ }).timeout(3000);
+
+
+});
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
new file mode 100644
index 00000000..521ed276
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+describe('Encryption', function () {
+ it('Successful encrypt', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ inputvalues.encrypt.good.data,
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+
+ it('Successful encrypt 5 MB', function (done) {
+ let prm = Gpgmejs.init();
+ let data = fixedLengthString(5);
+ prm.then(function (context) {
+ context.encrypt(
+ data,
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ }).timeout(10000);
+
+ it('Successful encrypt 20 MB', function (done) {
+ let prm = Gpgmejs.init();
+ let data = fixedLengthString(20);
+ prm.then(function (context) {
+ context.encrypt(
+ data,
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ }).timeout(20000);
+
+ it('Successful encrypt 50 MB', function (done) {
+ let prm = Gpgmejs.init();
+ let data = fixedLengthString(50);
+ prm.then(function (context) {
+ context.encrypt(
+ data,
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ }).timeout(20000);
+
+ it('Sending encryption without keys fails', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ inputvalues.encrypt.good.data,
+ null).then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function(error){
+ expect(error).to.be.an('Error');
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+
+ it('Sending encryption without data fails', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ null, inputvalues.encrypt.good.keyid).then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function (error) {
+ expect(error).to.be.an.instanceof(Error);
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+
+ it('Sending encryption with non existing keys fails', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ inputvalues.encrypt.good.data,
+ inputvalues.encrypt.bad.fingerprint).then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function(error){
+ expect(error).to.be.an('Error');
+ expect(error.code).to.not.be.undefined;
+ expect(error.code).to.equal('GNUPG_ERROR');
+ context.connection.disconnect();
+ done();
+ });
+ });
+ }).timeout(5000);;
+
+ it('Overly large message ( > 65MB) is rejected', function (done) {
+ let prm = Gpgmejs.init();
+ prm.then(function (context) {
+ context.encrypt(
+ fixedLengthString(65),
+ inputvalues.encrypt.good.fingerprint).then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function(error){
+ expect(error).to.be.an.instanceof(Error);
+ // expect(error.code).to.equal('GNUPG_ERROR');
+ // TODO: there is a 64 MB hard limit at least in chrome at:
+ // chromium//extensions/renderer/messaging_util.cc:
+ // kMaxMessageLength
+ context.connection.disconnect();
+ done();
+ });
+ });
+ }).timeout(8000);
+
+ // TODO check different valid parameter
+});
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
new file mode 100644
index 00000000..52e3a7b0
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -0,0 +1,143 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+var inputvalues = {
+ encrypt: {
+ good:{
+ data : 'Hello World.',
+ // Fingerprint of a key that has been imported to gnupg (i.e. see testkey.pub; testkey.sec)
+ fingerprint : 'D41735B91236FDB882048C5A2301635EEFF0CB05',
+ data_nonascii: '¡Äußerste µ€ før ñoquis@hóme! Добрый день',
+
+ // used for checking encoding consistency in > 2MB messages.
+ data_nonascii_32: [
+ 'K€K€K€K€K€K€K€K€K€K€K€K€K€K€K€K€',
+ 'µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€',
+ '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€',
+ '²³²³²³²³²³²³²³²³²³²³²³²³²³²³²³²³',
+ 'µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€A€µ€µ€µ€µ€',
+ 'µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µ€µAµ€µ€µ€µ€',
+ 'üüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü',
+ 'µAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€',
+ 'µAAAAµAAAAAAAAAAAAAAAAAAAAAAAAA€',
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAµ€',
+ 'µAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA°',
+ '€AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA€',
+ 'µ||||||||||||||||||||||||||||||€',
+ 'æſæſ³¼„¬“³³¬“¬½”æſæſ³¼„¬“³³¬“¬½”'
+ ]
+ },
+ bad: {
+ // valid Hex value, but not usable (not imported to gnupg, or bogus fingerprint)
+ fingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A'
+ }
+ },
+ init: {
+ // some parameters
+ invalid_startups: [
+ {all_passwords: true},
+ 'openpgpmode',
+ {api_style:"frankenstein"}
+ ]
+ }
+};
+
+// (Pseudo-)Random String covering all of utf8.
+function bigString(length){
+ var uint = '';
+ let arr = [];
+ for (let i= 0; i < length; i++){
+ arr.push(String.fromCharCode(
+ Math.floor(Math.random() * 10174) + 1)
+ );
+ }
+ return arr.join('');
+}
+
+function fixedLengthString(megabytes){
+ let maxlength = 1024 * 1024 * megabytes / 2;
+ let uint = new Uint8Array(maxlength);
+ for (let i = 0; i < maxlength; i++){
+ uint[i] = Math.floor(Math.random()* 256);
+ }
+ let td = new TextDecoder('ascii');
+ let result = td.decode(uint);
+ return result;
+}
+
+// (Pseudo-)Random Uint8Array, given size in Megabytes
+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;
+}
+
+// (Pseudo-)Random string with very limited charset (ascii only, no control chars)
+function bigBoringString(megabytes){
+ let maxlength = 1024 * 1024 * megabytes;
+ let string = [];
+ let chars = ' 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ for (let i= 0; i < maxlength; i++){
+ string.push(chars[Math.floor(Math.random() * chars.length)]);
+ }
+ return string.join('');
+}
+
+// Some String with simple chars, with different characteristics, but still
+// expected to occur in an averag message
+function slightlyLessBoringString(megabytes, set){
+ let maxlength = 1024 * 1024 * megabytes;
+ let string = [];
+ let chars = '';
+ 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§$%&/()=?`#+-{}[] \'';
+ }
+ for (let i= 0; i < maxlength; i++){
+ string.push(chars[Math.floor(Math.random() * chars.length)]);
+ }
+ return string.join('');
+}
+
+// Data encrypted with testKey
+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';
diff --git a/lang/js/BrowserTestExtension/tests/longRunningTests.js b/lang/js/BrowserTestExtension/tests/longRunningTests.js
new file mode 100644
index 00000000..4e55fd26
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/longRunningTests.js
@@ -0,0 +1,40 @@
+describe('Long running Encryption/Decryption', function () {
+ for (let i=0; i < 100; i++) {
+ it('Successful encrypt/decrypt completely random data ' + (i+1) + '/100', function (done) {
+ let prm = Gpgmejs.init();
+ let data = bigString(2*1024*1024);
+ prm.then(function (context) {
+ context.encrypt(data,
+ inputvalues.encrypt.good.fingerprint).then(
+ function (answer){
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a("string");
+ expect(answer.data).to.include(
+ 'BEGIN PGP MESSAGE');
+ expect(answer.data).to.include(
+ 'END PGP MESSAGE');
+ context.decrypt(answer.data).then(
+ function(result){
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ if (result.data.length !== data.length) {
+ console.log('diff: ' + (result.data.length - data.length));
+ for (let i=0; i < result.data.length; i++){
+ if (result.data[i] !== data[i]){
+ console.log('position: ' + i);
+ console.log('result : '+ result.data.charCodeAt(i) + result.data[i-2] + result.data[i-1] + result.data[i] + result.data[i+1] + result.data[i+2]);
+ console.log('original: ' + data.charCodeAt(i) + data[i-2] + data[i-1] + data[i] + data[i+1] + data[i+2]);
+ break;
+ }
+ }
+ }
+ expect(result.data).to.equal(data);
+ context.connection.disconnect();
+ done();
+ });
+ });
+ });
+ }).timeout(8000);
+ };
+
+});
diff --git a/lang/js/BrowserTestExtension/tests/startup.js b/lang/js/BrowserTestExtension/tests/startup.js
new file mode 100644
index 00000000..5de70a6b
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/startup.js
@@ -0,0 +1,54 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+ describe('GPGME context', function(){
+ it('Starting a GpgME instance', function(done){
+ let prm = Gpgmejs.init();
+ prm.then(
+ function(context){
+ expect(context.connection).to.not.be.undefined;
+ expect(context).to.be.an('object');
+ expect(context.connection).to.be.an('object');
+ expect(context.Keyring).to.be.undefined;
+ expect(context.encrypt).to.be.a('function');
+ expect(context.decrypt).to.be.a('function');
+ done();
+ }, function(errorr){
+ 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/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 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link href="libs/mocha.css" rel="stylesheet" />
+
+ </head>
+<body>
+ <h3>Unit tests</h3>
+ <div id="mocha"></div>
+ <script src="libs/mocha.js"></script>
+ <script src="libs/chai.js"></script>
+ <script src="setup_testing.js"></script>
+ <script src="libs/gpgmejs_unittests.bundle.js"></script>
+ <script src="rununittests.js"></script>
+ </body>
+</html>
diff --git a/lang/js/CHECKLIST b/lang/js/CHECKLIST
new file mode 100644
index 00000000..278f39dd
--- /dev/null
+++ b/lang/js/CHECKLIST
@@ -0,0 +1,25 @@
+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
+ [X] nativeConnection with delayed, multipart (> 1MB) answer
+
+ [*] Message handling (encrypt, decrypt verify, sign)
+ [x] encrypt, decrypt
+ [ ] verify
+ [ ] sign
+ [*] Key handling (import/export, modifying, status queries)
+ [*] Configuration handling
+ [ ] check for completeness
+
+Communication with other implementations
+
+ [ ] option to export SECRET Key into localstore used by e.g. mailvelope
+
+Management:
+ [*] Define the gpgme interface
+ [x] check Permissions (e.g. csp) for the different envs
+ [X] agree on license
+ [*] tests
diff --git a/lang/js/CHECKLIST_build b/lang/js/CHECKLIST_build
new file mode 100644
index 00000000..a7c8d08d
--- /dev/null
+++ b/lang/js/CHECKLIST_build
@@ -0,0 +1,3 @@
+- Checklist for build/install:
+
+browsers' manifests (see README) need allowedextension added, and the path set
diff --git a/lang/js/DemoExtension/entry.js b/lang/js/DemoExtension/entry.js
new file mode 100644
index 00000000..62583421
--- /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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ */
+document.addEventListener('DOMContentLoaded', function() {
+ chrome.tabs.create({
+ url: './mainui.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 <http://www.gnu.org/licenses/>.
+ * 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..76b8a221
--- /dev/null
+++ b/lang/js/DemoExtension/mainui.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" href="ui.css"/>
+ <script src="libs/gpgmejs.bundle.js"></script>
+ <script src="maindemo.js"></script>
+ </head>
+ <body>
+ <ul>
+ <li>
+ <span class="label">Text: </span>
+ <input type="text" id='cleartext' />
+ </li>
+ <li>
+ <span class="label">Public key ID: </span>
+ <input type="text" id="pubkey" value="" />
+ </li>
+ </ul>
+ <button id="buttonencrypt">Encrypt</button><br>
+ <hr>
+ <ul>
+ <li>
+ <span class="label">Encrypted armored Text: </span>
+ <textarea rows="5" cols="65" id="ciphertext" wrap="hard"></textarea>
+ </li>
+ </ul>
+ <button id="buttondecrypt">Decrypt</button><br>
+ <hr>
+ <h3>Result data:</h3>
+ <textarea id="answer" rows="5" cols="65" wrap="hard"></textarea>
+ </body>
+</html>
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..50070311
--- /dev/null
+++ b/lang/js/DemoExtension/popup.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script src="entry.js"></script>
+ </head>
+ <body>
+ </body>
+</html> \ 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
--- /dev/null
+++ b/lang/js/DemoExtension/testicon.png
Binary files 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/README b/lang/js/README
new file mode 100644
index 00000000..b597adb2
--- /dev/null
+++ b/lang/js/README
@@ -0,0 +1,52 @@
+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`.
+
+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.
+
+Demo WebExtension:
+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:
+
+(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.
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
new file mode 100755
index 00000000..91d5479b
--- /dev/null
+++ b/lang/js/build_extensions.sh
@@ -0,0 +1,17 @@
+#/!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 \
+ build/gpgmejs_unittests.bundle.js BrowserTestExtension/libs
+rm -rf build/extensions
+mkdir -p build/extensions
+zip -r build/extensions/browsertest.zip BrowserTestExtension
+
+mkdir -p DemoExtension/libs
+cp build/gpgmejs.bundle.js DemoExtension/libs
+zip -r build/extensions/demoextension.zip DemoExtension
diff --git a/lang/js/package.json b/lang/js/package.json
new file mode 100644
index 00000000..be52a554
--- /dev/null
+++ b/lang/js/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "gpgmejs",
+ "version": "0.0.1",
+ "description": "javascript part of a nativeMessaging gnupg integration",
+ "main": "src/index.js",
+ "private": true,
+ "keywords": [],
+ "author": "",
+ "license": "",
+ "devDependencies": {
+ "webpack": "^4.5.0",
+ "webpack-cli": "^2.0.14",
+ "chai": "^4.1.2",
+ "mocha": "^5.1.1"
+ }
+}
diff --git a/lang/js/src/Config.js b/lang/js/src/Config.js
new file mode 100644
index 00000000..e85bbb82
--- /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 <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+export const availableConf = {
+ null_expire_is_never: [true, false],
+ // cachedKeys: Some Key info will not be queried on each invocation,
+ // manual refresh by Key.refresh()
+ cachedKeys: [true, false]
+};
+
+export const defaultConf = {
+ null_expire_is_never: false,
+ cachedKeys: false
+}; \ No newline at end of file
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
new file mode 100644
index 00000000..9c2a6428
--- /dev/null
+++ b/lang/js/src/Connection.js
@@ -0,0 +1,241 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+/**
+ * A connection port will be opened for each communication between gpgmejs and
+ * gnupg. It should be alive as long as there are additional messages to be
+ * expected.
+ */
+import { permittedOperations } from './permittedOperations'
+import { gpgme_error } from "./Errors"
+import { GPGME_Message } from "./Message";
+
+/**
+ * A Connection handles the nativeMessaging interaction.
+ */
+export class Connection{
+
+ constructor(){
+ this.connect();
+ let me = this;
+ }
+
+ /**
+ * (Simple) Connection check.
+ * @returns {Boolean} true if the onDisconnect event has not been fired.
+ * Please note that the event listener of the port takes some time
+ * (5 ms seems enough) to react after the port is created. Then this will
+ * return undefined
+ */
+ get isConnected(){
+ return this._isConnected;
+ }
+
+ /**
+ * Immediately closes the open port.
+ */
+ disconnect() {
+ if (this._connection){
+ this._connection.disconnect();
+ }
+ }
+
+ /**
+ * Opens a nativeMessaging port.
+ */
+ connect(){
+ if (this._isConnected === true){
+ gpgme_error('CONN_ALREADY_CONNECTED');
+ } else {
+ this._isConnected = true;
+ this._connection = chrome.runtime.connectNative('gpgmejson');
+ let me = this;
+ this._connection.onDisconnect.addListener(
+ function(){
+ me._isConnected = false;
+ }
+ );
+ }
+ }
+
+ /**
+ * Sends a message and resolves with the answer.
+ * @param {GPGME_Message} message
+ * @returns {Promise<Object>} the gnupg answer, or rejection with error
+ * information.
+ */
+ post(message){
+ if (!this.isConnected){
+ return Promise.reject(gpgme_error('CONN_DISCONNECTED'));
+ }
+ if (!message || !message instanceof GPGME_Message){
+ return Promise.reject(gpgme_error('PARAM_WRONG'), message);
+ }
+ if (message.isComplete !== true){
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ let me = this;
+ return new Promise(function(resolve, reject){
+ let answer = new Answer(message);
+ let listener = function(msg) {
+ if (!msg){
+ me._connection.onMessage.removeListener(listener)
+ reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
+ } else if (msg.type === "error"){
+ me._connection.onMessage.removeListener(listener);
+ reject(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){
+ me._connection.postMessage({'op': 'getmore'});
+ } else {
+ me._connection.onMessage.removeListener(listener)
+ resolve(answer.message);
+ }
+ }
+ };
+
+ me._connection.onMessage.addListener(listener);
+ if (permittedOperations[message.operation].pinentry){
+ return me._connection.postMessage(message.message);
+ } else {
+ return Promise.race([
+ me._connection.postMessage(message.message),
+ function(resolve, reject){
+ setTimeout(function(){
+ reject(gpgme_error('CONN_TIMEOUT'));
+ }, 5000);
+ }]).then(function(result){
+ return result;
+ }, function(reject){
+ if(!reject instanceof Error) {
+ return gpgme_error('GNUPG_ERROR', reject);
+ } else {
+ return reject;
+ }
+ });
+ }
+ });
+ }
+};
+
+/**
+ * A class for answer objects, checking and processing the return messages of
+ * the nativeMessaging communication.
+ * @param {String} operation The operation, to look up validity of returning messages
+ */
+class Answer{
+
+ constructor(message){
+ this.operation = message.operation;
+ this.expected = message.expected;
+ }
+
+ /**
+ * Add the information to the answer
+ * @param {Object} msg The message as received with nativeMessaging
+ * returns true if successfull, gpgme_error otherwise
+ */
+ add(msg){
+ if (this._response === undefined){
+ this._response = {};
+ }
+ let messageKeys = Object.keys(msg);
+ let poa = permittedOperations[this.operation].answer;
+ if (messageKeys.length === 0){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ for (let i= 0; i < messageKeys.length; i++){
+ let key = messageKeys[i];
+ switch (key) {
+ case 'type':
+ if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ break;
+ case 'more':
+ break;
+ default:
+ //data should be concatenated
+ if (poa.data.indexOf(key) >= 0){
+ if (!this._response.hasOwnProperty(key)){
+ this._response[key] = '';
+ }
+ this._response[key] += msg[key];
+ }
+ //params should not change through the message
+ else if (poa.params.indexOf(key) >= 0){
+ if (!this._response.hasOwnProperty(key)){
+ this._response[key] = msg[key];
+ }
+ else if (this._response[key] !== msg[key]){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER',msg[key]);
+ }
+ }
+ //infos may be json objects etc. Not yet defined.
+ // Pushing them into arrays for now
+ else if (poa.infos.indexOf(key) >= 0){
+ if (!this._response.hasOwnProperty(key)){
+ this._response[key] = [];
+ }
+ this._response.push(msg[key]);
+ }
+ else {
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @returns {Object} the assembled message, original data assumed to be
+ * (javascript-) strings
+ */
+ get message(){
+ let keys = Object.keys(this._response);
+ let msg = {};
+ let poa = permittedOperations[this.operation].answer;
+ for (let i=0; i < keys.length; i++) {
+ if (poa.data.indexOf(keys[i]) >= 0
+ && this._response.base64 === true
+ ) {
+ msg[keys[i]] = atob(this._response[keys[i]]);
+ if (this.expected === 'base64'){
+ msg[keys[i]] = this._response[keys[i]];
+ } else {
+ msg[keys[i]] = decodeURIComponent(
+ atob(this._response[keys[i]]).split('').map(function(c) {
+ return '%' +
+ ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+ }).join(''));
+ }
+ } else {
+ msg[keys[i]] = this._response[keys[i]];
+ }
+ }
+ return msg;
+ }
+}
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
new file mode 100644
index 00000000..bfe3a2f4
--- /dev/null
+++ b/lang/js/src/Errors.js
@@ -0,0 +1,129 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+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_WRONG_OP': {
+ msg: 'The operation requested could not be found',
+ type: 'error'
+ },
+ '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'
+ },
+ 'GENERIC_ERROR': {
+ msg: 'Unspecified error',
+ type: 'error'
+ }
+};
+
+/**
+ * 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 gpgme_error(code = 'GENERIC_ERROR', info){
+ if (err_list.hasOwnProperty(code)){
+ if (err_list[code].type === 'error'){
+ return new GPGME_Error(code);
+ }
+ if (err_list[code].type === 'warning'){
+ console.warn(code + ': ' + err_list[code].msg);
+ }
+ return null;
+ } else if (code === 'GNUPG_ERROR'){
+ return new GPGME_Error(code, info);
+ }
+ 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
new file mode 100644
index 00000000..fd0e7200
--- /dev/null
+++ b/lang/js/src/Helpers.js
@@ -0,0 +1,103 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+import { gpgme_error } from "./Errors";
+import { GPGME_Key } from "./Key";
+
+/**
+ * Tries to return an array of fingerprints, either from input fingerprints or
+ * from Key objects
+ * @param {Key |Array<Key>| GPGME_Key | Array<GPGME_Key>|String|Array<String>} input
+ * @returns {Array<String>} Array of fingerprints.
+ */
+
+export function toKeyIdArray(input){
+ if (!input){
+ gpgme_error('MSG_NO_KEYS');
+ return [];
+ }
+ if (!Array.isArray(input)){
+ input = [input];
+ }
+ 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 {
+ gpgme_error('MSG_NOT_A_FPR');
+ }
+ } 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 {
+ gpgme_error('MSG_NOT_A_FPR');
+ }
+ } else {
+ return gpgme_error('PARAM_WRONG');
+ }
+ }
+ if (result.length === 0){
+ gpgme_error('MSG_NO_KEYS');
+ return [];
+ } else {
+ return result;
+ }
+};
+
+/**
+ * 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 no usage; check if the input is a valid Hex string with a length of 16
+ */
+function isLongId(string){
+ return hextest(string, 16);
+};
+
+// 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..075a190e
--- /dev/null
+++ b/lang/js/src/Key.js
@@ -0,0 +1,244 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+/**
+ * The key class allows to query the information defined in gpgme Key Objects
+ * (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)
+ *
+ * This is a stub, as the gpgme-json side is not yet implemented
+ *
+ */
+
+import { isFingerprint } from './Helpers'
+import { 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('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');
+ }
+}
+
+export class GPGME_Key {
+
+ 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._fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
+ if (!this._connection instanceof Connection){
+ return gpgme_error('CONN_NO_CONNECT');
+ } else {
+ return this._connection;
+ }
+ }
+
+ set fingerprint(fpr){
+ if (isFingerprint(fpr) === true && !this._fingerprint){
+ this._fingerprint = fpr;
+ }
+ }
+
+ get fingerprint(){
+ if (!this._fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
+ return this._fingerprint;
+ }
+
+ /**
+ * hasSecret returns true if a secret subkey is included in this Key
+ */
+ get hasSecret(){
+ return this.checkKey('secret');
+ }
+
+ get isRevoked(){
+ return this.checkKey('revoked');
+ }
+
+ get isExpired(){
+ return this.checkKey('expired');
+ }
+
+ get isDisabled(){
+ return this.checkKey('disabled');
+ }
+
+ get isInvalid(){
+ return this.checkKey('invalid');
+ }
+
+ get canEncrypt(){
+ return this.checkKey('can_encrypt');
+ }
+
+ get canSign(){
+ return this.checkKey('can_sign');
+ }
+
+ get canCertify(){
+ return this.checkKey('can_certify');
+ }
+
+ get canAuthenticate(){
+ return this.checkKey('can_authenticate');
+ }
+
+ get isQualified(){
+ return this.checkKey('is_qualified');
+ }
+
+ get armored(){
+ let msg = createMessage ('export_key');
+ msg.setParameter('armor', true);
+ if (msg instanceof Error){
+ return gpgme_error('KEY_INVALID');
+ }
+ 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 returns true if this is the default key used to sign
+ */
+ get isDefault(){
+ throw('NOT_YET_IMPLEMENTED');
+ }
+
+ /**
+ * get the Key's subkeys as GPGME_Key objects
+ * @returns {Array<GPGME_Key>}
+ */
+ get subkeys(){
+ return 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], this.connection);
+ if (subkey instanceof GPGME_Key){
+ resultset.push(subkey);
+ }
+ }
+ return Promise.resolve(resultset);
+ }, function(error){
+ //TODO this.checkKey fails
+ });
+ }
+
+ /**
+ * creation time stamp of the key
+ * @returns {Date|null} TBD
+ */
+ get timestamp(){
+ return this.checkKey('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 this.checkKey('expires');
+ // TODO convert to Date; check for 0
+ }
+
+ /**
+ * getter name TBD
+ * @returns {String|Array<String>} The user ids associated with this key
+ */
+ get userIds(){
+ return this.checkKey('uids');
+ }
+
+ /**
+ * @returns {String} The public key algorithm supported by this subkey
+ */
+ get pubkey_algo(){
+ return this.checkKey('pubkey_algo');
+ }
+
+ /**
+ * 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){
+ 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' ||
+ !permittedOperations['keyinfo'].hasOwnProperty(property)){
+ return gpgme_error('PARAM_WRONG');
+ }
+ let msg = createMessage ('keyinfo');
+ if (msg instanceof Error){
+ return gpgme_error('PARAM_WRONG');
+ }
+ msg.setParameter('fingerprint', this.fingerprint);
+ this.connection.post(msg).then(function(result, error){
+ if (error){
+ return gpgme_error('GNUPG_ERROR',error.msg);
+ } else if (result.hasOwnProperty(property)){
+ return result[property];
+ }
+ else if (property == 'secret'){
+ // TBD property undefined means "not true" in case of secret?
+ return false;
+ } else {
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ }, function(error){
+ 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
new file mode 100644
index 00000000..4596035a
--- /dev/null
+++ b/lang/js/src/Keyring.js
@@ -0,0 +1,162 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+import {createMessage} from './Message'
+import {GPGME_Key} from './Key'
+import { isFingerprint } from './Helpers';
+import { gpgme_error } from './Errors';
+import { Connection } from './Connection';
+
+export class GPGME_Keyring {
+ constructor(connection){
+ this.connection = connection;
+ }
+
+ set connection(connection){
+ if (!this._connection && connection instanceof Connection){
+ this._connection = connection;
+ }
+ }
+ get connection(){
+ if (this._connection instanceof Connection){
+ if (this._connection.isConnected){
+ return this._connection;
+ }
+ return gpgme_error('CONN_DISCONNECTED');
+ }
+ return gpgme_error('CONN_NO_CONNECT');
+ }
+
+ /**
+ * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
+ * @param {Boolean} (optional) Include listing of secret keys
+ * @returns {Promise.<Array<GPGME_Key>>}
+ *
+ */
+ getKeys(pattern, include_secret){
+ let msg = createMessage('listkeys');
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
+ if (pattern && typeof(pattern) === 'string'){
+ msg.setParameter('pattern', pattern);
+ }
+ if (include_secret){
+ msg.setParameter('with-secret', true);
+ }
+ let me = this;
+
+ this.connection.post(msg).then(function(result){
+ let fpr_list = [];
+ let resultset = [];
+ if (!Array.isArray(result.keys)){
+ //TODO check assumption keys = Array<String fingerprints>
+ fpr_list = [result.keys];
+ } else {
+ fpr_list = result.keys;
+ }
+ for (let i=0; i < fpr_list.length; i++){
+ let newKey = new GPGME_Key(fpr_list[i], me._connection);
+ if (newKey instanceof GPGME_Key){
+ resultset.push(newKey);
+ }
+ }
+ return Promise.resolve(resultset);
+ }, function(error){
+ //TODO error handling
+ });
+ }
+
+ /**
+ * @param {Object} flags subset filter expecting at least one of the
+ * filters described below. True will filter on the condition, False will
+ * reverse the filter, if not present or undefined, the filter will not be
+ * considered. Please note that some combination may not make sense
+ * @param {Boolean} flags.secret Only Keys containing a secret part.
+ * @param {Boolean} flags.revoked revoked Keys only
+ * @param {Boolean} flags.expired Expired Keys only
+ * @param {String} (optional) pattern A pattern to search for, in userIds or KeyIds
+ * @returns {Promise Array<GPGME_Key>}
+ *
+ */
+ getSubset(flags, pattern){
+ if (flags === undefined) {
+ throw('ERR_WRONG_PARAM');
+ };
+ let secretflag = false;
+ if (flags.hasOwnProperty(secret) && flags.secret){
+ secretflag = true;
+ }
+ this.getKeys(pattern, secretflag).then(function(queryset){
+ let resultset = [];
+ for (let i=0; i < queryset.length; i++ ){
+ let conditions = [];
+ let anticonditions = [];
+ if (secretflag === true){
+ conditions.push('hasSecret');
+ } else if (secretflag === false){
+ anticonditions.push('hasSecret');
+ }
+ /**
+ if (flags.defaultKey === true){
+ conditions.push('isDefault');
+ } else if (flags.defaultKey === false){
+ anticonditions.push('isDefault');
+ }
+ */
+ /**
+ * if (flags.valid === true){
+ anticonditions.push('isInvalid');
+ } else if (flags.valid === false){
+ conditions.push('isInvalid');
+ }
+ */
+ if (flags.revoked === true){
+ conditions.push('isRevoked');
+ } else if (flags.revoked === false){
+ anticonditions.push('isRevoked');
+ }
+ if (flags.expired === true){
+ conditions.push('isExpired');
+ } else if (flags.expired === false){
+ anticonditions.push('isExpired');
+ }
+ let decision = undefined;
+ for (let con = 0; con < conditions.length; con ++){
+ if (queryset[i][conditions[con]] !== true){
+ decision = false;
+ }
+ }
+ for (let acon = 0; acon < anticonditions.length; acon ++){
+ if (queryset[i][anticonditions[acon]] === true){
+ decision = false;
+ }
+ }
+ if (decision !== false){
+ resultset.push(queryset[i]);
+ }
+ }
+ return Promise.resolve(resultset);
+ }, function(error){
+ //TODO error handling
+ });
+ }
+
+};
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
new file mode 100644
index 00000000..932212a6
--- /dev/null
+++ b/lang/js/src/Message.js
@@ -0,0 +1,196 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+import { permittedOperations } from './permittedOperations'
+import { gpgme_error } from './Errors'
+
+export function createMessage(operation){
+ if (typeof(operation) !== 'string'){
+ return gpgme_error('PARAM_WRONG');
+ }
+ if (permittedOperations.hasOwnProperty(operation)){
+ return new GPGME_Message(operation);
+ } else {
+ return gpgme_error('MSG_WRONG_OP');
+ }
+}
+
+/**
+ * Prepares a communication request. It checks operations and parameters in
+ * ./permittedOperations.
+ * @param {String} operation
+ */
+export class GPGME_Message {
+ //TODO getter
+
+ constructor(operation){
+ this.operation = operation;
+ this._expected = 'string';
+ }
+
+ set operation (op){
+ if (typeof(op) === "string"){
+ if (!this._msg){
+ this._msg = {};
+ }
+ if (!this._msg.op & permittedOperations.hasOwnProperty(op)){
+ this._msg.op = op;
+ }
+ }
+ }
+
+ get operation(){
+ return this._msg.op;
+ }
+
+ set expected(string){
+ if (string === 'base64'){
+ this._expected = 'base64';
+ }
+ }
+
+ get expected() {
+ if (this._expected === "base64"){
+ return this._expected;
+ }
+ return "string";
+ }
+
+ /**
+ * Sets a parameter for the message. Note that the operation has to be set
+ * first, to be able to check if the parameter is permittted
+ * @param {String} param Parameter to set
+ * @param {any} value Value to set //TODO: Some type checking
+ * @returns {Boolean} If the parameter was set successfully
+ */
+ setParameter(param,value){
+ if (!param || typeof(param) !== 'string'){
+ return gpgme_error('PARAM_WRONG');
+ }
+ let po = permittedOperations[this._msg.op];
+ if (!po){
+ return gpgme_error('MSG_WRONG_OP');
+ }
+ let poparam = null;
+ if (po.required.hasOwnProperty(param)){
+ poparam = po.required[param];
+ } else if (po.optional.hasOwnProperty(param)){
+ poparam = po.optional[param];
+ } else {
+ return gpgme_error('PARAM_WRONG');
+ }
+ let checktype = function(val){
+ switch(typeof(val)){
+ case 'string':
+ if (poparam.allowed.indexOf(typeof(val)) >= 0
+ && val.length > 0) {
+ return true;
+ }
+ return gpgme_error('PARAM_WRONG');
+ break;
+ case 'number':
+ if (
+ poparam.allowed.indexOf('number') >= 0
+ && isNaN(value) === false){
+ return true;
+ }
+ return gpgme_error('PARAM_WRONG');
+ break;
+ case 'boolean':
+ if (poparam.allowed.indexOf('boolean') >= 0){
+ return true;
+ }
+ return gpgme_error('PARAM_WRONG');
+ break;
+ case 'object':
+ if (Array.isArray(val)){
+ if (poparam.array_allowed !== true){
+ return gpgme_error('PARAM_WRONG');
+ }
+ for (let i=0; i < val.length; i++){
+ let res = checktype(val[i]);
+ if (res !== true){
+ return res;
+ }
+ }
+ if (val.length > 0) {
+ return true;
+ }
+ } else if (val instanceof Uint8Array){
+ if (poparam.allowed.indexOf('Uint8Array') >= 0){
+ return true;
+ }
+ return gpgme_error('PARAM_WRONG');
+ } else {
+ return gpgme_error('PARAM_WRONG');
+ }
+ break;
+ default:
+ return gpgme_error('PARAM_WRONG');
+ }
+ };
+ let typechecked = checktype(value);
+ if (typechecked !== true){
+ return typechecked;
+ }
+ if (poparam.hasOwnProperty('allowed_data')){
+ if (poparam.allowed_data.indexOf(value) < 0){
+ return gpgme_error('PARAM_WRONG');
+ }
+ }
+ this._msg[param] = value;
+ return true;
+ }
+
+ /**
+ * Check if the message has the minimum requirements to be sent, according
+ * to the definitions in permittedOperations
+ * @returns {Boolean}
+ */
+ get isComplete(){
+ if (!this._msg.op){
+ return false;
+ }
+ let reqParams = Object.keys(
+ permittedOperations[this._msg.op].required);
+ let msg_params = Object.keys(this._msg);
+ for (let i=0; i < reqParams.length; i++){
+ if (msg_params.indexOf(reqParams[i]) < 0){
+ console.log(reqParams[i] + ' missing');
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the prepared message with parameters and completeness checked
+ * @returns {Object|null} Object to be posted to gnupg, or null if
+ * incomplete
+ */
+ get message(){
+ if (this.isComplete === true){
+ return this._msg;
+ }
+ else {
+ return null;
+ }
+
+ }
+}
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
new file mode 100644
index 00000000..3aa5957a
--- /dev/null
+++ b/lang/js/src/gpgmejs.js
@@ -0,0 +1,192 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+import {Connection} from "./Connection"
+import {GPGME_Message, createMessage} from './Message'
+import {toKeyIdArray} from "./Helpers"
+import { gpgme_error } from "./Errors"
+import { GPGME_Keyring } from "./Keyring";
+
+export class GpgME {
+ /**
+ * initializes GpgME by opening a nativeMessaging port
+ * TODO: add configuration
+ */
+ constructor(connection){
+ this.connection = connection;
+ }
+
+ set connection(conn){
+ if (this._connection instanceof Connection){
+ gpgme_error('CONN_ALREADY_CONNECTED');
+ } else if (conn instanceof Connection){
+ this._connection = conn;
+ } else {
+ gpgme_error('PARAM_WRONG');
+ }
+ }
+
+ get connection(){
+ if (this._connection){
+ if (this._connection.isConnected === true){
+ return this._connection;
+ }
+ return undefined;
+ }
+ return undefined;
+ }
+
+ set Keyring(keyring){
+ if (keyring && keyring instanceof GPGME_Keyring){
+ this._Keyring = keyring;
+ }
+ }
+
+ get Keyring(){
+ return this._Keyring;
+ }
+
+ /**
+ * @param {String} data text/data to be encrypted as String
+ * @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message
+ * @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message
+ */
+ encrypt(data, publicKeys, base64=false, wildcard=false){
+
+ let msg = createMessage('encrypt');
+ if (msg instanceof Error){
+ return Promise.reject(msg)
+ }
+ // TODO temporary
+ msg.setParameter('armor', true);
+ msg.setParameter('always-trust', true);
+ if (base64 === true) {
+ msg.setParameter('base64', true);
+ }
+ let pubkeys = toKeyIdArray(publicKeys);
+ msg.setParameter('keys', pubkeys);
+ putData(msg, data);
+ if (wildcard === true){
+ msg.setParameter('throw-keyids', true);
+ };
+ if (msg.isComplete === true){
+ return this.connection.post(msg);
+ } else {
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ }
+
+ /**
+ * @param {String} data TODO base64? Message with the encrypted data
+ * @param {Boolean} base64 (optional) Response should stay base64
+ * @returns {Promise<Object>} decrypted message:
+ data: The decrypted data. This may be base64 encoded.
+ base64: Boolean indicating whether data is base64 encoded.
+ mime: A Boolean indicating whether the data is a MIME object.
+ info: An optional object with extra information.
+ * @async
+ */
+
+ decrypt(data, base64=false){
+ if (data === undefined){
+ return Promise.reject(gpgme_error('MSG_EMPTY'));
+ }
+ let msg = createMessage('decrypt');
+ if (base64 === true){
+ msg.expected = 'base64';
+ }
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
+ putData(msg, data);
+ return this.connection.post(msg);
+
+ }
+
+ deleteKey(key, delete_secret = false, no_confirm = false){
+ return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
+ let msg = createMessage('deletekey');
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
+ let key_arr = toKeyIdArray(key);
+ if (key_arr.length !== 1){
+ return Promise.reject(
+ gpgme_error('GENERIC_ERROR'));
+ // TBD should always be ONE key?
+ }
+ msg.setParameter('key', key_arr[0]);
+ if (delete_secret === true){
+ msg.setParameter('allow_secret', true);
+ // TBD
+ }
+ if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
+ msg.setParameter('delete_force', true);
+ // TBD
+ }
+ if (msg.isComplete === true){
+ this.connection.post(msg).then(function(success){
+ // TODO: it seems that there is always errors coming back:
+ }, function(error){
+ switch (error.msg){
+ case 'ERR_NO_ERROR':
+ return Promise.resolve('okay'); //TBD
+ default:
+ return Promise.reject(gpgme_error('TODO') ); //
+ // INV_VALUE,
+ // GPG_ERR_NO_PUBKEY,
+ // GPG_ERR_AMBIGUOUS_NAME,
+ // GPG_ERR_CONFLICT
+ }
+ });
+ } else {
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ }
+}
+
+/**
+ * Sets the data of the message
+ * @param {GPGME_Message} message The message where this data will be set
+ * @param {*} data The data to enter
+ */
+function putData(message, data){
+ if (!message || !message instanceof GPGME_Message ) {
+ return gpgme_error('PARAM_WRONG');
+ }
+ if (!data){
+ return gpgme_error('PARAM_WRONG');
+ } else if (typeof(data) === 'string') {
+ message.setParameter('data', data);
+ } else if (
+ typeof(data) === 'object' &&
+ typeof(data.getText) === 'function'
+ ){
+ let txt = data.getText();
+ if (typeof(txt) === 'string'){
+ message.setParameter('data', txt);
+ } else {
+ return gpgme_error('PARAM_WRONG');
+ }
+
+ } else {
+ return gpgme_error('PARAM_WRONG');
+ }
+}
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
new file mode 100644
index 00000000..8527b3f3
--- /dev/null
+++ b/lang/js/src/index.js
@@ -0,0 +1,86 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+import { GpgME } from "./gpgmejs";
+import { gpgme_error } from "./Errors";
+import { Connection } from "./Connection";
+import { defaultConf, availableConf } from "./Config";
+
+/**
+ * Initializes a nativeMessaging Connection and returns a GPGMEjs object
+ * @param {Object} config Configuration. See Config.js for available parameters. Still TODO
+ */
+function init(config){
+ let _conf = parseconfiguration(config);
+ if (_conf instanceof Error){
+ return Promise.reject(_conf);
+ }
+ return new Promise(function(resolve, reject){
+ let connection = new Connection;
+ // TODO: Delayed reaction is ugly. We need to listen to the port's
+ // 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){
+ resolve(new GpgME(connection, _conf));
+ } else {
+ reject(gpgme_error('CONN_NO_CONNECT'));
+ }
+ };
+ setTimeout(delayedreaction, 5);
+ });
+}
+
+function parseconfiguration(rawconfig = {}){
+ if ( typeof(rawconfig) !== 'object'){
+ return gpgme_error('PARAM_WRONG');
+ };
+ let result_config = {};
+ let conf_keys = Object.keys(rawconfig);
+
+ for (let i=0; i < conf_keys.length; i++){
+
+ if (availableConf.hasOwnProperty(conf_keys[i])){
+ let value = rawconfig[conf_keys[i]];
+ if (availableConf[conf_keys[i]].indexOf(value) < 0){
+ return gpgme_error('PARAM_WRONG');
+ } else {
+ result_config[conf_keys[i]] = value;
+ }
+ }
+ else {
+ return gpgme_error('PARAM_WRONG');
+ }
+ }
+ let default_keys = Object.keys(defaultConf);
+ for (let j=0; j < default_keys.length; j++){
+ if (!result_config.hasOwnProperty(default_keys[j])){
+ result_config[default_keys[j]] = defaultConf[default_keys[j]];
+ }
+ }
+ return result_config;
+};
+
+export default {
+ init: init
+} \ No newline at end of file
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
new file mode 100644
index 00000000..da46a1fd
--- /dev/null
+++ b/lang/js/src/permittedOperations.js
@@ -0,0 +1,217 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+ /**
+ * Definition of the possible interactions with gpgme-json.
+ * operation: <Object>
+ required: Array<Object>
+ <String> 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: <Array> If present, restricts to the given value
+ optional: Array<Object>
+ 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: <Object>
+ type: <String< The content type of answer expected
+ data: Array<String> The payload property of the answer. May be
+ partial and in need of concatenation
+ params: Array<String> Information that do not change throughout
+ the message
+ infos: Array<*> arbitrary information that may result in a list
+ }
+ }
+ */
+
+export const permittedOperations = {
+ encrypt: {
+ required: {
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ 'data': {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'chunksize': {
+ allowed: ['number']
+ },
+ 'base64': {
+ allowed: ['boolean']
+ },
+ 'mime': {
+ allowed: ['boolean']
+ },
+ 'armor': {
+ allowed: ['boolean']
+ },
+ 'always-trust': {
+ allowed: ['boolean']
+ },
+ 'no-encrypt-to': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ 'no-compress': {
+ allowed: ['boolean']
+ },
+ 'throw-keyids': {
+ allowed: ['boolean']
+ },
+ 'want-address': {
+ allowed: ['boolean']
+ },
+ 'wrap': {
+ allowed: ['boolean']
+ },
+ },
+ answer: {
+ type: ['ciphertext'],
+ data: ['data'],
+ params: ['base64'],
+ infos: []
+ }
+ },
+
+ decrypt: {
+ pinentry: true,
+ required: {
+ 'data': {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'chunksize': {
+ allowed: ['number'],
+ },
+ 'base64': {
+ allowed: ['boolean']
+ }
+ },
+ answer: {
+ type: ['plaintext'],
+ data: ['data'],
+ params: ['base64', 'mime'],
+ infos: [] // TODO pending. Info about signatures and validity
+ //{
+ //signatures: [{
+ //Key : <String>Fingerprint,
+ //valid: <Boolean>
+ // }]
+ }
+ },
+ /** 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'],
+ infos: ['subkeys', 'userIds']
+ // {'hasSecret': <Boolean>,
+ // 'isRevoked': <Boolean>,
+ // 'isExpired': <Boolean>,
+ // 'armored': <String>, // armored public Key block
+ // 'timestamp': <Number>, //
+ // 'expires': <Number>,
+ // 'pubkey_algo': TBD // TBD (optional?),
+ // 'userIds': Array<String>,
+ // 'subkeys': Array<String> Fingerprints of Subkeys
+ // }
+ }*/
+
+ /**
+ listkeys:{
+ required: {};
+ optional: {
+ 'with-secret':{
+ allowed: ['boolean']
+ },{
+ 'pattern': {
+ allowed: ['string']
+ }
+ },
+ answer: {
+ type: ['TBD'],
+ infos: ['TBD']
+ // keys: Array<String> Fingerprints representing the results
+ },
+ */
+
+ /**
+ importkey: {
+ required: {
+ 'keyarmored': {
+ allowed: ['string']
+ }
+ },
+ answer: {
+ type: ['TBD'],
+ infos: ['TBD'],
+ // for each key if import was a success,
+ // and if it was an update of preexisting key
+ }
+ },
+ */
+
+ /**
+ deletekey: {
+ 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: ['']
+ // TBD (optional) Some kind of 'ok' if delete was successful.
+ }
+ }
+ */
+
+ /**
+ *TBD get armored secret different treatment from keyinfo!
+ * TBD key modification?
+ * encryptsign: TBD
+ * verify: TBD
+ */
+}
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..c437d599
--- /dev/null
+++ b/lang/js/unittests.js
@@ -0,0 +1,326 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+import "./node_modules/mocha/mocha";
+import "./node_modules/chai/chai";
+import { helper_params as hp } from "./unittest_inputvalues";
+import { message_params as mp } from "./unittest_inputvalues";
+import { whatever_params as wp } from "./unittest_inputvalues";
+import { 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);
+ 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 mandatory data', function(){
+ let test0 = createMessage('encrypt');
+ test0.setParameter('data', mp.valid_encrypt_data);
+ test0.setParameter('keys', hp.validFingerprints);
+
+ expect(test0.isComplete).to.be.true;
+ });
+
+ it('Message is not complete after mandatory data is empty', function(){
+ let test0 = createMessage('encrypt');
+ test0.setParameter('data', '');
+ test0.setParameter('keys', hp.validFingerprints);
+ expect(test0.isComplete).to.be.false;
+ });
+
+ it('Complete Message contains the data that was set', function(){
+ let test0 = createMessage('encrypt');
+ test0.setParameter('data', mp.valid_encrypt_data);
+ test0.setParameter('keys', hp.validFingerprints);
+
+ expect(test0.message).to.not.be.null;
+ expect(test0.message).to.have.keys('op', 'data', 'keys');
+ expect(test0.message.op).to.equal('encrypt');
+ expect(test0.message.data).to.equal(
+ mp.valid_encrypt_data);
+ });
+
+ it ('Not accepting non-allowed operation', function(){
+ let test0 = createMessage(mp.invalid_op_action);
+
+ expect(test0).to.be.an.instanceof(Error);
+ expect(test0.code).to.equal('MSG_WRONG_OP');
+ });
+ it('Not accepting wrong parameter type', function(){
+ let test0 = createMessage(mp.invalid_op_type);
+
+ expect(test0).to.be.an.instanceof(Error);
+ expect(test0.code).to.equal('PARAM_WRONG');
+ });
+
+ it('Not accepting wrong parameter name', function(){
+ let test0 = createMessage(mp.invalid_param_test.valid_op);
+ for (let i=0;
+ i < mp.invalid_param_test.invalid_param_names.length; i++){
+ let ret = test0.setParameter(
+ mp.invalid_param_test.invalid_param_names[i],
+ 'Somevalue');
+
+ expect(ret).to.be.an.instanceof(Error);
+ expect(ret.code).to.equal('PARAM_WRONG');
+ }
+ });
+
+ it('Not accepting wrong parameter value', function(){
+ let test0 = createMessage(mp.invalid_param_test.valid_op);
+ for (let j=0;
+ j < mp.invalid_param_test.invalid_values_0.length; j++){
+ let ret = test0.setParameter(
+ mp.invalid_param_test.validparam_name_0,
+ mp.invalid_param_test.invalid_values_0[j]);
+
+ expect(ret).to.be.an.instanceof(Error);
+ expect(ret.code).to.equal('PARAM_WRONG');
+ }
+ });
+ });
+
+}
+
+export default {unittests}; \ No newline at end of file
diff --git a/lang/js/webpack.conf.js b/lang/js/webpack.conf.js
new file mode 100644
index 00000000..b2ad9098
--- /dev/null
+++ b/lang/js/webpack.conf.js
@@ -0,0 +1,35 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * This is the configuration file for building the gpgmejs-Library with webpack
+ */
+const path = require('path');
+
+module.exports = {
+ entry: './src/index.js',
+ // mode: 'development',
+ mode: 'production',
+ output: {
+ path: path.resolve(__dirname, 'build'),
+ filename: 'gpgmejs.bundle.js',
+ libraryTarget: 'var',
+ libraryExport: 'default',
+ library: 'Gpgmejs'
+ }
+};
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 <http://www.gnu.org/licenses/>.
+ * 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'
+ }
+};