summaryrefslogtreecommitdiff
path: root/lang/js
diff options
context:
space:
mode:
Diffstat (limited to 'lang/js')
-rw-r--r--lang/js/.eslintrc.json49
-rw-r--r--lang/js/BrowserTestExtension/Makefile.am45
-rw-r--r--lang/js/BrowserTestExtension/Makefile.in555
-rw-r--r--lang/js/BrowserTestExtension/browsertest.html28
-rw-r--r--lang/js/BrowserTestExtension/index.html113
-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.js30
-rw-r--r--lang/js/BrowserTestExtension/runbrowsertest.js26
-rw-r--r--lang/js/BrowserTestExtension/rununittests.js27
-rw-r--r--lang/js/BrowserTestExtension/setup_testing.js28
-rw-r--r--lang/js/BrowserTestExtension/testicon.pngbin0 -> 2697 bytes
-rw-r--r--lang/js/BrowserTestExtension/testkey.pub30
-rw-r--r--lang/js/BrowserTestExtension/testkey.sec57
-rw-r--r--lang/js/BrowserTestExtension/testkey2.pub30
-rw-r--r--lang/js/BrowserTestExtension/tests/KeyImportExport.js152
-rw-r--r--lang/js/BrowserTestExtension/tests/KeyInfos.js59
-rw-r--r--lang/js/BrowserTestExtension/tests/decryptTest.js114
-rw-r--r--lang/js/BrowserTestExtension/tests/encryptDecryptTest.js200
-rw-r--r--lang/js/BrowserTestExtension/tests/encryptTest.js159
-rw-r--r--lang/js/BrowserTestExtension/tests/inputvalues.js453
-rw-r--r--lang/js/BrowserTestExtension/tests/longRunningTests.js58
-rw-r--r--lang/js/BrowserTestExtension/tests/signTest.js64
-rw-r--r--lang/js/BrowserTestExtension/tests/startup.js47
-rw-r--r--lang/js/BrowserTestExtension/tests/verifyTest.js90
-rw-r--r--lang/js/BrowserTestExtension/unittests.html17
-rw-r--r--lang/js/DemoExtension/Makefile.am27
-rw-r--r--lang/js/DemoExtension/Makefile.in537
-rw-r--r--lang/js/DemoExtension/entry.js30
-rw-r--r--lang/js/DemoExtension/maindemo.js123
-rw-r--r--lang/js/DemoExtension/mainui.html47
-rw-r--r--lang/js/DemoExtension/manifest.json14
-rw-r--r--lang/js/DemoExtension/popup.html9
-rw-r--r--lang/js/DemoExtension/testicon.pngbin0 -> 2670 bytes
-rw-r--r--lang/js/DemoExtension/ui.css33
-rw-r--r--lang/js/Makefile.am32
-rw-r--r--lang/js/Makefile.in721
-rw-r--r--lang/js/README116
-rwxr-xr-xlang/js/build_extensions.sh17
-rw-r--r--lang/js/jsdoc.conf24
-rw-r--r--lang/js/jsdoc_index.md50
-rw-r--r--lang/js/package.json17
-rw-r--r--lang/js/src/Connection.js320
-rw-r--r--lang/js/src/Errors.js177
-rw-r--r--lang/js/src/Helpers.js219
-rw-r--r--lang/js/src/Key.js711
-rw-r--r--lang/js/src/Keyring.js439
-rw-r--r--lang/js/src/Makefile.am30
-rw-r--r--lang/js/src/Makefile.in540
-rw-r--r--lang/js/src/Message.js243
-rw-r--r--lang/js/src/Signature.js206
-rw-r--r--lang/js/src/gpgmejs.js465
-rw-r--r--lang/js/src/index.js58
-rw-r--r--lang/js/src/permittedOperations.js413
-rw-r--r--lang/js/unittest_inputvalues.js123
-rw-r--r--lang/js/unittests.js386
-rw-r--r--lang/js/webpack.conf.js36
-rw-r--r--lang/js/webpack.conf_unittests.js36
59 files changed, 8674 insertions, 0 deletions
diff --git a/lang/js/.eslintrc.json b/lang/js/.eslintrc.json
new file mode 100644
index 0000000..dc3be2e
--- /dev/null
+++ b/lang/js/.eslintrc.json
@@ -0,0 +1,49 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "sourceType": "module"
+ },
+ "rules": {
+ "indent": [
+ "warn",
+ 4
+ ],
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "quotes": [
+ "error",
+ "single"
+ ],
+ "semi": [
+ "error",
+ "always"
+ ],
+ "no-var": [
+ "warn"
+ ],
+ "max-len": 1,
+ "default-case": 2,
+ "no-invalid-this": 2,
+ "no-lone-blocks": 1,
+ "no-self-compare": 2,
+ "radix": 2,
+ "no-use-before-define": ["error", {
+ "functions": false,
+ "classes": false,
+ "variables": true
+ }],
+ "no-useless-constructor": 1,
+ "space-before-function-paren": ["error", "always"],
+ "keyword-spacing": 2,
+ "spaced-comment": 1,
+ "space-unary-ops": 2,
+ "object-curly-spacing": ["error", "always"],
+ "array-bracket-spacing": ["error", "never"]
+ }
+} \ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/Makefile.am b/lang/js/BrowserTestExtension/Makefile.am
new file mode 100644
index 0000000..8f0a4f9
--- /dev/null
+++ b/lang/js/BrowserTestExtension/Makefile.am
@@ -0,0 +1,45 @@
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Intevation GmbH
+#
+# This file is part of GPGME.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+
+EXTRA_DIST = browsertest.html \
+ index.html \
+ longTests.html \
+ Makefile.am \
+ manifest.json \
+ popup.html \
+ popup.js \
+ runbrowsertest.js \
+ rununittests.js \
+ setup_testing.js \
+ testicon.png \
+ testkey2.pub \
+ testkey.pub \
+ testkey.sec \
+ tests/decryptTest.js \
+ tests/encryptDecryptTest.js \
+ tests/encryptTest.js \
+ tests/inputvalues.js \
+ tests/KeyImportExport.js \
+ tests/KeyInfos.js \
+ tests/longRunningTests.js \
+ tests/signTest.js \
+ tests/startup.js \
+ tests/verifyTest.js \
+ unittests.html
diff --git a/lang/js/BrowserTestExtension/Makefile.in b/lang/js/BrowserTestExtension/Makefile.in
new file mode 100644
index 0000000..842ca1a
--- /dev/null
+++ b/lang/js/BrowserTestExtension/Makefile.in
@@ -0,0 +1,555 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Intevation GmbH
+#
+# This file is part of GPGME.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = lang/js/BrowserTestExtension
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/mkinstalldirs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_pkg_swig.m4 \
+ $(top_srcdir)/m4/ax_python_devel.m4 \
+ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \
+ $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLED_LANGUAGES = @ENABLED_LANGUAGES@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@
+GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@
+GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@
+GPGME_QT_LIBS = @GPGME_QT_LIBS@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GRAPHVIZ = @GRAPHVIZ@
+GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGMEPP_LT_AGE = @LIBGPGMEPP_LT_AGE@
+LIBGPGMEPP_LT_CURRENT = @LIBGPGMEPP_LT_CURRENT@
+LIBGPGMEPP_LT_REVISION = @LIBGPGMEPP_LT_REVISION@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBQGPGME_LT_AGE = @LIBQGPGME_LT_AGE@
+LIBQGPGME_LT_CURRENT = @LIBQGPGME_LT_CURRENT@
+LIBQGPGME_LT_REVISION = @LIBQGPGME_LT_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MOC = @MOC@
+MOC2 = @MOC2@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PYTHON = @PYTHON@
+PYTHONS = @PYTHONS@
+PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LDFLAGS = @PYTHON_LDFLAGS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SITE_PKG = @PYTHON_SITE_PKG@
+PYTHON_VERSION = @PYTHON_VERSION@
+QTCHOOSER = @QTCHOOSER@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SWIG = @SWIG@
+SWIG_LIB = @SWIG_LIB@
+SYSROOT = @SYSROOT@
+VERSION = @VERSION@
+VERSION_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+VERSION_NUMBER = @VERSION_NUMBER@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = browsertest.html \
+ index.html \
+ longTests.html \
+ Makefile.am \
+ manifest.json \
+ popup.html \
+ popup.js \
+ runbrowsertest.js \
+ rununittests.js \
+ setup_testing.js \
+ testicon.png \
+ testkey2.pub \
+ testkey.pub \
+ testkey.sec \
+ tests/decryptTest.js \
+ tests/encryptDecryptTest.js \
+ tests/encryptTest.js \
+ tests/inputvalues.js \
+ tests/KeyImportExport.js \
+ tests/KeyInfos.js \
+ tests/longRunningTests.js \
+ tests/signTest.js \
+ tests/startup.js \
+ tests/verifyTest.js \
+ unittests.html
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lang/js/BrowserTestExtension/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/js/BrowserTestExtension/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lang/js/BrowserTestExtension/browsertest.html b/lang/js/BrowserTestExtension/browsertest.html
new file mode 100644
index 0000000..0d3e293
--- /dev/null
+++ b/lang/js/BrowserTestExtension/browsertest.html
@@ -0,0 +1,28 @@
+<!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/KeyInfos.js"></script>
+ <script src="tests/encryptTest.js"></script>
+ <script src="tests/encryptDecryptTest.js"></script>
+ <script src="tests/signTest.js"></script>
+ <script src="tests/verifyTest.js"></script>
+ <script src="tests/decryptTest.js"></script>
+ <script src="tests/KeyImportExport.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 0000000..7f8d97d
--- /dev/null
+++ b/lang/js/BrowserTestExtension/index.html
@@ -0,0 +1,113 @@
+<!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.
+ </p>
+ <p>
+ Most tests rely on a test gpg key to be available in gpg, which can be
+ found at the bottom of this page, or as "testkey.sec" in the
+ BrowserTestExtension's directory. Please import this key to your tested
+ gpg installation, or adapt the input defined in tests/inputvalues.js
+ if you want to use different values.
+ </p>
+ <p>
+ <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>
+ <hr />
+ <p>
+
+ <textarea rows="5" cols="65" wrap="hard" readonly>
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+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
+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-----
+ </textarea>
+
+ </p>
+ </body>
+</html>
diff --git a/lang/js/BrowserTestExtension/longTests.html b/lang/js/BrowserTestExtension/longTests.html
new file mode 100644
index 0000000..8ff969b
--- /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 0000000..a9e605b
--- /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 0000000..f17f262
--- /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 0000000..794620b
--- /dev/null
+++ b/lang/js/BrowserTestExtension/popup.js
@@ -0,0 +1,30 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global chrome */
+
+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 0000000..c46eb12
--- /dev/null
+++ b/lang/js/BrowserTestExtension/runbrowsertest.js
@@ -0,0 +1,26 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global mocha */
+
+mocha.run();
diff --git a/lang/js/BrowserTestExtension/rununittests.js b/lang/js/BrowserTestExtension/rununittests.js
new file mode 100644
index 0000000..df31589
--- /dev/null
+++ b/lang/js/BrowserTestExtension/rununittests.js
@@ -0,0 +1,27 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global Gpgmejs_test, mocha*/
+
+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 0000000..52aeac5
--- /dev/null
+++ b/lang/js/BrowserTestExtension/setup_testing.js
@@ -0,0 +1,28 @@
+/* gpgme.js - Javascript integration for gpgme
+ * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global mocha, chai */
+
+mocha.setup('bdd');
+const expect = chai.expect; //eslint-disable-line no-unused-vars
+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 0000000..a98463d
--- /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 0000000..cfc329f
--- /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 0000000..ced8f3e
--- /dev/null
+++ b/lang/js/BrowserTestExtension/testkey.sec
@@ -0,0 +1,57 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+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
+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/testkey2.pub b/lang/js/BrowserTestExtension/testkey2.pub
new file mode 100644
index 0000000..557bd5b
--- /dev/null
+++ b/lang/js/BrowserTestExtension/testkey2.pub
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFsMHecBCACqdJgqa+CeNYwPCK+MpOwAV6uFVjDyO2LmOs6+XfDWRBU/Zjtz
+8zdYNKSbLjkWN4ujV5aiyA7MtEofszzYLEoKUt1wiDScHMpW8qmEFDvl9g26MeAV
+rTno9D5KodHvEIs8wnrqBs8ix0WLbh6J1Dtt8HQgIbN+v3gaRQrgBFe6z2ZYpHHx
+ZfOu3iFKlm2WE/NekRkvvFIo3ApGvRhGIYw6JMmugBlo7s5xosJK0I9dkPGlEEtt
+aF1RkcMj8sWG9vHAXcjlGgFfXSN9YLppydXpkuZGm4+gjLB2a3rbQCZVFnxCyG4O
+ybjkP8Jw6Udm89bK2ucYFfjdrmYn/nJqRxeNABEBAAG0I1Rlc3QgTm9Qcml2S2V5
+IDxub2JvZHlAZXhhbXBsZS5vcmc+iQFOBBMBCAA4FiEE4Fmh4IZtMa4TEXCITZou
+EzBBU9EFAlsMHecCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQTZouEzBB
+U9F+qwf/SHj4uRnTWgyJ71FBxQDYCBq3jbi6e7hMkRPbJyJdnPIMAb2p0PJjBgjW
+0pp4+kDPZans3UDHbma1u/SFI4/y6isJiK94Bk5xp5YliLGnUceTjgDFe6lBhfQ1
+zVWZC/NF3tPgbziIxXQTNt34nS+9dbV/QFDLW0POcN7C0jR/hgkBjMEH2PezWhSj
+mL/yLfLfUYAoxVpXjfC5aPJKqw0tR7m5ibznjCphE+FUMRg8EOmJcg6soeJ5QspU
+k2dPN3+Y0zCTNRgAHEI+yIQbM6pio6v2c+UCtT1QhW4xSI38/kcEG8QiM55r1TUy
+FcWAY5n5t1nNZtMxxse3LqEon3rKiLkBDQRbDB3nAQgAqfAjSjcngERtM+ZYOwN0
+QF2v2FuEuMe8mhju7Met7SN2zGv1LnjhTNshEa9IABEfjZirE2Tqx4xCWDwDedK4
+u1ToFvcnuAMnq2O47Sh+eTypsf6WPFtPBWf6ctKY31hFXjgoyDBULBvl43XU/D9C
+Mt7nsKDPYHVrrnge/qWPYVcb+cO0sSwNImMcwQSdTQ3VBq7MeNS9ZeBcXi+XCjhN
+kjNum2AQqpkHHDQV7871yQ8RIILvZSSfkLb0/SNDU+bGaw2G3lcyKdIfZi2EWWZT
+oCbH38I/+LV7nAEe4zFpHwW8X0Dkx2aLgxe6UszDH9L3eGhTLpJhOSiaanG+zZKm
++QARAQABiQE2BBgBCAAgFiEE4Fmh4IZtMa4TEXCITZouEzBBU9EFAlsMHecCGwwA
+CgkQTZouEzBBU9H5TQgAolWvIsez/WW8N2tmZEnX0LOFNB+1S4L4X983njwNdoVI
+w19pbj+8RIHF/H9kcPGi7jK96gvlykQn3uez/95D2AiRFW5KYdOouFisKgHpv8Ay
+BrhclHv11yK+X/0iTD0scYaG7np5162xLkaxSO9hsz2fGv20RKaXCWkI69fWw0BR
+XlI5pZh2YFei2ZhH/tIMIW65h3w0gtgaZBBdpZTOOW4zvghyN+0MSObqkI1BvUJu
+caDFI4d6ZTmp5SY+pZyktZ4bg/vMH5VFxdIKgbLx9uVeTvOupvbAW0TNulYGUBQE
+nm+S0zr3W18t64e4sS3oHse8zCqo1iiImpba6F1Oaw==
+=y6DD
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/lang/js/BrowserTestExtension/tests/KeyImportExport.js b/lang/js/BrowserTestExtension/tests/KeyImportExport.js
new file mode 100644
index 0000000..b3d95bb
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/KeyImportExport.js
@@ -0,0 +1,152 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ * Raimund Renkert <rrenkert@intevation.de>
+ */
+
+/* global describe, it, expect, before, afterEach, Gpgmejs*/
+/* global ImportablePublicKey, inputvalues */
+
+describe('Key importing', function () {
+ const fpr = ImportablePublicKey.fingerprint;
+ const pubKey = ImportablePublicKey.key;
+ const changedKey = ImportablePublicKey.keyChangedUserId;
+
+ let context = null;
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ context.Keyring.getKeys({ pattern: fpr }).then(
+ function (result){
+ if (result.length === 1) {
+ result[0].delete().then(function (){
+ done();
+ },function (){
+ done();
+ });
+ } else {
+ done();
+ }
+ });
+ });
+ });
+
+ afterEach(function (done){
+ // delete the test key if still present
+ context.Keyring.getKeys({ pattern: fpr }).then(
+ function (result){
+ if (result.length === 1) {
+ result[0].delete().then(function (){
+ done();
+ },function (){
+ done();
+ });
+ } else {
+ done();
+ }
+ });
+ });
+
+ it('Importing Key', function (done) {
+ context.Keyring.getKeys({ pattern: fpr }).then(function (result){
+ expect(result).to.be.an('array');
+ expect(result.length).to.equal(0);
+ context.Keyring.importKey(pubKey).then(function (result){
+ expect(result.Keys).to.be.an('array');
+ expect(result.Keys[0]).to.not.be.undefined;
+ expect(result.Keys[0].key).to.be.an('object');
+ expect(result.Keys[0].key.fingerprint).to.equal(fpr);
+ expect(result.Keys[0].status).to.equal('newkey');
+ expect(result.summary.considered).to.equal(1);
+ expect(result.summary.imported).to.equal(1);
+ done();
+ });
+ });
+ });
+
+ it('Updating Key', function (done){
+ context.Keyring.importKey(pubKey)
+ .then(function (result){
+ expect(result.Keys[0].key).to.not.be.undefined;
+ expect(result.Keys[0].status).to.equal('newkey');
+ context.Keyring.importKey(changedKey).then(function (res){
+ expect(res.Keys[0].key).to.be.an('object');
+ expect(res.Keys[0].key.fingerprint).to.equal(fpr);
+ expect(res.Keys[0].status).to.equal('change');
+ expect(res.Keys[0].changes.userId).to.be.true;
+ expect(res.Keys[0].changes.subkey).to.be.false;
+ expect(res.Keys[0].changes.signature).to.be.true;
+ expect(res.summary.considered).to.equal(1);
+ done();
+ });
+ });
+ });
+
+ it('Deleting Key', function (done) {
+ context.Keyring.importKey(pubKey).then(function (result){
+ expect(result.Keys[0].key).to.be.an('object');
+ expect(result.Keys[0].key.fingerprint).to.equal(fpr);
+ result.Keys[0].key.delete().then(function (result){
+ expect(result).to.be.true;
+ done();
+ });
+ });
+ });
+
+ it('Import result feedback', function (done){
+ context.Keyring.importKey(pubKey, true).then(function (result){
+ expect(result).to.be.an('object');
+ expect(result.Keys[0]).to.be.an('object');
+ expect(result.Keys[0].key.fingerprint).to.equal(fpr);
+ expect(result.Keys[0].status).to.equal('newkey');
+ result.Keys[0].key.getArmor().then(function (armor){
+ expect(armor).to.be.a('string');
+ done();
+ });
+ });
+ });
+
+ it('exporting armored Key with getKeysArmored', function (done) {
+ context.Keyring.importKey(pubKey).then(function (){
+ context.Keyring.getKeysArmored({ pattern: fpr })
+ .then(function (result){
+ expect(result).to.be.an('object');
+ expect(result.armored).to.be.a('string');
+ expect(result.secret_fprs).to.be.undefined;
+ done();
+ });
+ });
+ });
+
+ it('Exporting Key (including secret fingerprints)', function (done) {
+ const key_secret = inputvalues.encrypt.good.fingerprint;
+ context.Keyring.getKeysArmored({
+ pattern: key_secret, with_secret_fpr: true })
+ .then(function (result){
+ expect(result).to.be.an('object');
+ expect(result.armored).to.be.a('string');
+ expect(result.secret_fprs).to.be.an('array');
+ expect(result.secret_fprs[0]).to.equal(key_secret);
+ done();
+ });
+ });
+}); \ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/KeyInfos.js b/lang/js/BrowserTestExtension/tests/KeyInfos.js
new file mode 100644
index 0000000..d122958
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/KeyInfos.js
@@ -0,0 +1,59 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, expect, before, Gpgmejs */
+/* global inputvalues*/
+
+describe('Key information', function () {
+ let context = null;
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+
+ it('A fingerprint is consistently returned upper case hex', function (done){
+ const mixedCase = inputvalues.encrypt.good.fingerprint_mixedcase;
+ context.Keyring.getKeys({ pattern: mixedCase }).then(function (result){
+ expect(result).to.be.an('array');
+ expect(result.length).to.equal(1);
+ expect(result[0].fingerprint).to.equal(mixedCase.toUpperCase());
+ done();
+ });
+ });
+
+ it('A userId keeps their encoding', function (done){
+ context.Keyring.importKey(inputvalues.publicKeyNonAscii.key, true)
+ .then(function (result){
+ expect(result.Keys[0]).to.be.an('object');
+ const user = result.Keys[0].key.get('userids')[0];
+ expect(user.get('name')).to.equal(
+ inputvalues.publicKeyNonAscii.userid);
+ result.Keys[0].key.delete().then(function (){
+ done();
+ });
+ });
+ });
+});
diff --git a/lang/js/BrowserTestExtension/tests/decryptTest.js b/lang/js/BrowserTestExtension/tests/decryptTest.js
new file mode 100644
index 0000000..5817eb4
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/decryptTest.js
@@ -0,0 +1,114 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, before, expect, Gpgmejs */
+/* global bigString, inputvalues, sabotageMsg, binaryData, filename_files */
+
+describe('Decryption', function () {
+ let context = null;
+ const good_fpr = inputvalues.encrypt.good.fingerprint;
+
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout:2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+
+ it('Decryption of random string fails', function (done) {
+ let data = bigString(20 * 1024);
+ context.decrypt({ data: data }).then(
+ function (){},
+ function (error){
+ expect(error).to.be.an('error');
+ expect(error.code).to.equal('GNUPG_ERROR');
+ done();
+ });
+ });
+
+ it('Decryption of slightly corrupted message fails', function (done) {
+ const data = bigString(10000);
+ context.encrypt({ data: data, publicKeys:good_fpr }).then(
+ function (enc){
+ context.decrypt({ data: sabotageMsg(enc.data) }).then(
+ function (){},
+ function (error){
+ expect(error).to.be.an('error');
+ expect(error.code).to.equal('GNUPG_ERROR');
+ done();
+ });
+ });
+ }).timeout(5000);
+
+
+ it('decrypt/verify operations return proper information', function (done){
+ const data = inputvalues.encryptSignedMessage;
+ context.decrypt({ data: data }).then(function (result){
+ expect(result).to.be.an('object');
+ expect(result.signatures).to.be.an('object');
+ expect(result.signatures.all_valid).to.be.true;
+ expect(result.signatures.count).to.equal(1);
+ expect(result.signatures.signatures.good).to.be.an('array');
+ expect(
+ result.signatures.signatures.good[0].fingerprint).to.equal(
+ good_fpr);
+ done();
+ });
+ });
+
+ it('decrypt of a png, result as base64', function (done){
+ const data = binaryData.encryptedArmored;
+ context.decrypt({ data: data, expect: 'base64' })
+ .then(function (result){
+ expect(result.data).to.be.a('String');
+ expect(result.data).to.equal(binaryData.base64);
+ expect(result.format).to.equal('base64');
+ done();
+ });
+ });
+
+ it('decrypt of a png, result as Uint8Array', function (done){
+ const data = binaryData.encryptedArmored;
+ context.decrypt({ data: data, expect: 'uint8' })
+ .then(function (result){
+ expect(result.data).to.be.an('Uint8Array');
+ expect(result.format).to.equal('uint8');
+ done();
+ });
+ });
+
+ for (let i=0; i < filename_files.length; i++) {
+ it (
+ 'decrypted file_names keep correct encoding (' + i + ')',
+ function (done){
+ context.decrypt({ data:filename_files[i].data })
+ .then(function (answer){
+ expect(answer.file_name).to.equal(
+ filename_files[i].name);
+ done();
+ });
+ });
+ }
+
+}); \ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
new file mode 100644
index 0000000..b4e4f3f
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -0,0 +1,200 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, expect, before, Gpgmejs */
+/* global inputvalues, encryptedData, bigString, bigBoringString */
+
+describe('Encryption and Decryption', function (){
+ let context = null;
+ let good_fpr = inputvalues.encrypt.good.fingerprint;
+
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+
+ it('Successful encrypt and decrypt simple string', function (done) {
+ let data = inputvalues.encrypt.good.data;
+ context.encrypt({ data: data, publicKeys: good_fpr }).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({ data: answer.data }).then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(
+ inputvalues.encrypt.good.data);
+ done();
+ });
+ });
+ });
+
+ it('Decrypt simple non-ascii', function (done) {
+ let data = encryptedData;
+ context.decrypt({ data: 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('Trailing whitespace and different line endings', function (done) {
+ const data = 'Keks. \rKeks \n Keks \r\n';
+ context.encrypt({ data: data, publicKeys: good_fpr }).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({ data: answer.data }).then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ done();
+ });
+ });
+ }).timeout(5000);
+
+ it('Random data, as string', function (done) {
+ let data = bigString(1000);
+ context.encrypt({ data:data, publicKeys: good_fpr }).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({ data: answer.data }).then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ done();
+ });
+ });
+ }).timeout(3000);
+
+ it('Data, input as base64', function (done) {
+ let data = inputvalues.encrypt.good.data;
+ let b64data = btoa(data);
+ context.encrypt({ data: b64data, publicKeys: good_fpr, base64: 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({ data: answer.data }).then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ done();
+ });
+ });
+ }).timeout(3000);
+
+ it('Random data, input as base64', function (done) {
+ let data = bigBoringString(0.001);
+ let b64data = btoa(data);
+ context.encrypt(
+ { data: b64data, publicKeys: good_fpr, base64: 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({ data:answer.data }).then(
+ function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ done();
+ });
+ });
+ }).timeout(3000);
+
+ it('Random data, original data is and should stay base64 encoded',
+ function (done) {
+ let data = bigBoringString(0.001);
+ let b64data = btoa(data);
+ context.encrypt(
+ { data: b64data, publicKeys: good_fpr })
+ .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({
+ data:answer.data, expect: 'base64' })
+ .then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(b64data);
+ done();
+ });
+ });
+ }).timeout(3000);
+
+ 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 data = '';
+ for (let i=0; i < 34 * 1024; i++){
+ data += input;
+ }
+ context.encrypt({ data: data, publicKeys: good_fpr })
+ .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({ data: answer.data })
+ .then(function (result) {
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ done();
+ });
+ });
+ }).timeout(5000);
+ }
+});
diff --git a/lang/js/BrowserTestExtension/tests/encryptTest.js b/lang/js/BrowserTestExtension/tests/encryptTest.js
new file mode 100644
index 0000000..1017d71
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/encryptTest.js
@@ -0,0 +1,159 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, expect, before, Gpgmejs */
+/* global inputvalues, fixedLengthString, bigString */
+
+describe('Encryption', function () {
+ let context = null;
+ const good_fpr = inputvalues.encrypt.good.fingerprint;
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+
+ it('Successful encrypt', function (done) {
+ const data = inputvalues.encrypt.good.data;
+ context.encrypt({ data: data, publicKeys: good_fpr })
+ .then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a('string');
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ done();
+ });
+ });
+
+
+ it( 'encrypt with \'armor\': true should returned an armored block',
+ function (done){
+ const data = bigString(1000);
+ context.encrypt({ data: data, publicKeys: good_fpr, armor: 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');
+ expect(answer.format).to.equal('ascii');
+ done();
+ });
+ });
+
+ it( 'encrypt with \'armor\': false and \'expected\': \'uint8\' returns ' +
+ 'an Uint8Array', function (done) {
+ const data = bigString(1000);
+ context.encrypt({
+ data: data, publicKeys: good_fpr, armor: false, expect: 'uint8'
+ }).then(function (answer){
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a('Uint8Array');
+ expect(answer.format).to.equal('uint8');
+ done();
+ });
+ });
+
+ it( 'encrypt with \'armor\': false and \'expected\': \'base64\' returns ' +
+ 'a base64 string', function (done) {
+ const data = bigString(1000);
+ context.encrypt({
+ data: data, publicKeys: good_fpr, armor: false, expect: 'base64'
+ }).then(function (answer){
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a('string');
+ expect(answer.format).to.equal('base64');
+ done();
+ });
+ });
+
+ const sizes = [5,20,50];
+ for (let i=0; i < sizes.length; i++) {
+ it('Successful encrypt a ' + sizes[i] + 'MB message', function (done) {
+ const data = fixedLengthString(sizes[i]);
+ context.encrypt({ data: data, publicKeys: good_fpr })
+ .then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a('string');
+ expect(answer.data).to.include('BEGIN PGP MESSAGE');
+ expect(answer.data).to.include('END PGP MESSAGE');
+ done();
+ });
+ }).timeout(20000);
+ }
+
+ it('Sending encryption without keys fails', function (done) {
+ const data = inputvalues.encrypt.good.data;
+ context.encrypt({ data: data }).then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function (error){
+ expect(error).to.be.an('Error');
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ done();
+ });
+ });
+
+ it('Sending encryption without data fails', function (done) {
+ context.encrypt({ data: null, publicKeys: good_fpr })
+ .then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function (error) {
+ expect(error).to.be.an.instanceof(Error);
+ expect(error.code).to.equal('MSG_INCOMPLETE');
+ done();
+ });
+ });
+
+ it('Sending encryption with non existing keys fails', function (done) {
+ const data = inputvalues.encrypt.good.data;
+ const bad_fpr = inputvalues.encrypt.bad.fingerprint;
+ context.encrypt({ data:data, publicKeys: bad_fpr })
+ .then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function (error){
+ expect(error).to.be.an('Error');
+ expect(error.code).to.not.be.undefined;
+ expect(error.code).to.equal('GNUPG_ERROR');
+ done();
+ });
+ }).timeout(5000);
+
+ it('Overly large message ( > 64MB) is rejected', function (done) {
+ const data = fixedLengthString(65);
+ context.encrypt({ data: data, publicKeys: good_fpr })
+ .then(function (answer) {
+ expect(answer).to.be.undefined;
+ }, function (error){
+ expect(error).to.be.an.instanceof(Error);
+ // TODO: there is a 64 MB hard limit at least in chrome at:
+ // chromium//extensions/renderer/messaging_util.cc:
+ // kMaxMessageLength
+ // The error will be a browser error, not from gnupg or from
+ // this library
+ done();
+ });
+ }).timeout(8000);
+
+
+});
diff --git a/lang/js/BrowserTestExtension/tests/inputvalues.js b/lang/js/BrowserTestExtension/tests/inputvalues.js
new file mode 100644
index 0000000..730e48a
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/inputvalues.js
@@ -0,0 +1,453 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+const inputvalues = {// eslint-disable-line no-unused-vars
+ encrypt: {
+ good:{
+ data : 'Hello World.',
+ // Fingerprint of a key that has been imported to gnupg
+ // (i.e. see testkey.pub; testkey.sec)
+ fingerprint : 'D41735B91236FDB882048C5A2301635EEFF0CB05',
+ fingerprint_mixedcase: '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'
+ }
+ },
+
+ signedMessage: {
+ good: '-----BEGIN PGP SIGNED MESSAGE-----\n' +
+ 'Hash: SHA256\n' +
+ '\n' +
+ 'Matschige Münsteraner Marshmallows\n' +
+ '-----BEGIN PGP SIGNATURE-----\n' +
+ '\n' +
+ 'iQEzBAEBCAAdFiEE1Bc1uRI2/biCBIxaIwFjXu/wywUFAltRoiMACgkQIwFjXu/w\n' +
+ 'ywUvagf6ApQbZbTPOROqfTfxAPdtzJsSDKHla6D0G5wom2gJbAVb0B2YS1c3Gjpq\n' +
+ 'I4kTKT1W1RRkne0mK9cexf4sjb5DQcV8PLhfmmAJEpljDFei6i/E309BvW4CZ4rG\n' +
+ 'jiurf8CkaNkrwn2fXJDaT4taVCX3V5FQAlgLxgOrm1zjiGA4mz98gi5zL4hvZXF9\n' +
+ 'dHY0jLwtQMVUO99q+5XC1TJfPsnteWL9m4e/YYPfYJMZZso+/0ib/yX5vHCk7RXH\n' +
+ 'CfhY40nMXSYdfl8mDOhvnKcCvy8qxetFv9uCX06OqepAamu/bvxslrzocRyJ/eq0\n' +
+ 'T2JfzEN+E7Y3PB8UwLgp/ZRmG8zRrQ==\n' +
+ '=ioB6\n' +
+ '-----END PGP SIGNATURE-----\n',
+ bad: '-----BEGIN PGP SIGNED MESSAGE-----\n' +
+ 'Hash: SHA256\n' +
+ '\n' +
+ 'Matschige Münchener Marshmallows\n' +
+ '-----BEGIN PGP SIGNATURE-----\n' +
+ '\n' +
+ 'iQEzBAEBCAAdFiEE1Bc1uRI2/biCBIxaIwFjXu/wywUFAltRoiMACgkQIwFjXu/w\n' +
+ 'ywUvagf6ApQbZbTPOROqfTfxAPdtzJsSDKHla6D0G5wom2gJbAVb0B2YS1c3Gjpq\n' +
+ 'I4kTKT1W1RRkne0mK9cexf4sjb5DQcV8PLhfmmAJEpljDFei6i/E309BvW4CZ4rG\n' +
+ 'jiurf8CkaNkrwn2fXJDaT4taVCX3V5FQAlgLxgOrm1zjiGA4mz98gi5zL4hvZXF9\n' +
+ 'dHY0jLwtQMVUO99q+5XC1TJfPsnteWL9m4e/YYPfYJMZZso+/0ib/yX5vHCk7RXH\n' +
+ 'CfhY40nMXSYdfl8mDOhvnKcCvy8qxetFv9uCX06OqepAamu/bvxslrzocRyJ/eq0\n' +
+ 'T2JfzEN+E7Y3PB8UwLgp/ZRmG8zRrQ==\n' +
+ '=ioB6\n' +
+ '-----END PGP SIGNATURE-----\n'
+ },
+ encryptSignedMessage: '-----BEGIN PGP MESSAGE-----\n'+
+ '\n'+
+ 'hQEMA6B8jfIUScGEAQf/bmQ+xNMGTjPvQCktkxR4Svt2dVNVdSzKsCmvSv24QOQF\n'+
+ 'yBMK5w51S/6DTdiZI12IYD7hjvkr9NqxXXupjrVKwqEVpg4Pkwckac0OcElJIBsL\n'+
+ '3htr4iYsr8dhSgSS4BO0azcu4wZQTXy5v2P7yYPECMEagNEXnW+tE7sHLCq8Ysqz\n'+
+ 'LVxG0R0IUijKeEd3xQC2Tt20e1Z1j5tnqaPhE/9Smqf5OjSUDqpXxvRnSNRk/zEs\n'+
+ 'cGVgCF+cv68nUJM9lwEAbBQChplwL6ebnhunC6DsRCxnjLHVyKm127hmhSiMGC0e\n'+
+ 'Ns31mGeP1dxpDv6Gi2/oKmq67vG3i4fKeckj7bt30tLA1wH0Qn5Mn6Tzxzve0W0q\n'+
+ 'Ghqn9PY9qNK8EkrkzqaFk9dzu5tfSbaJBLS/uIhX2Wj70EMEBbFSkN0qlgOfLgGw\n'+
+ '5mwRvCgj4nvV1ByFhnx7uwgQixvOwLH4JLKvwCQpJm+O2R0eC7M6CzR/b9iL/oaO\n'+
+ 'JTkoD9hcLhxF7j+3ZYg7rbNwofuHST097vFjzItsucb0jHOzjlkCqbhdczICILTa\n'+
+ 'H76Q6YGdMLyG9a3s4yZUMruaeQyWGeXlryzLDvdEoSgoD5YrolsFOM+Z2apbzVs2\n'+
+ 'k5CltwtanjjWGnpAqSyr49C6CSU8G1QHpNygx5frtAS8bojR2ovB9OJp2wUklDvC\n'+
+ 'LtU7dLpTY/BIvfB1vzwcW/aNgmPadNHX8mAzlqTQJjeLoo69Wp804t+u36sgfd/J\n'+
+ 'ser7vdJJUm+86Q9csefItvFmHhqjMg5XXHoa8WZWJOHIQMxZkaIwKAzcEt/oEOdJ\n'+
+ 'rbVNVabhTdbmS5I1ok16wg5jMF07ZDM7nXWMcQNjwT646XKP+pp2N6YQROVidNXj\n'+
+ 'COyRyiXE/csr\n'+
+ '=Ik7G\n'+
+ '-----END PGP MESSAGE-----\n',
+ someInputParameter: 'bad string',
+
+ publicKeyNonAscii: {
+ userid: 'Müller €uro',
+ key: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' + '\n' +
+ 'mQENBFt2/VIBCADIWBIMxExZlHda4XIVnM9nsIfUYLebJSC/krEriyWgzytU8/fQ\n' +
+ 'S05cfnYx7RXvOOq4k8aa7mu80ovg3q77idXauLreAUwng4Njw0nMxWq/vtoMiZ60\n' +
+ '9f8EmfthZophhkQF2HIPHyqXMDZzMLWv4oTr2UJ9BKudL1XtbK51y9TbiyfQygBl\n' +
+ '8bl+zrOo70/dN6aunvuo6Hlu5cEzkj2QrzZlqTdfG5qv6KVEMut1eAbxZAmvSnna\n' +
+ 'R4wqiRCT3/eRXGJbDL/8GaCEYkwi9FBrimjOTV0MpcLNwAU4aGfDxMUsxML9xJ+/\n' +
+ '/6GFxzYf7Lmk5UhvoewR58uQkHkTVPjZ9hXZABEBAAG0KE3DvGxsZXIg4oKsdXJv\n' +
+ 'IDxtdWVsbGVyZXVyb0BleGFtcGxlLm9yZz6JAVQEEwEIAD4WIQQVNixp3XT/DuGT\n' +
+ 'F4MFmkL4L5UZdAUCW3b9UgIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIX\n' +
+ 'gAAKCRAFmkL4L5UZdAhiCACowW1aC8DYGtJyAaBO2MqWhyw1wVCbQN9uFsQZPydY\n' +
+ 'v3BEbCDrRc0HyfV1PVoRQsgkiNMes1S2tz2IMJoEOTMaz3WjPM8yK0dDbo5sfx/o\n' +
+ '/XaXeKhyYNqRkz2dPzorg1sHyHe0ki/HoQiANEJ8mByMtlwnPWlhnINAX+27eL17\n' +
+ 'JC8juhBYUchqoIBAl+ajYKSThdLzrUkcL7QfJjZb3pPytJSTTdFc0rD6ERDbfXXc\n' +
+ '/vnE2SDYme+XXn7H5tNe67tPM8M96vbp+uM+n2t/z96C+Pqb6GJFMBa35PM+/qQO\n' +
+ 'yr0I2oaQnTecx2AfBXGZvd81wMYikAJ9rAOWyMQZHJWouQENBFt2/VIBCADXCvKD\n' +
+ '3wRWCOzRWtLTs7hpAjCDxp6niPkwxKuUf9r/sUPmn0pWdZHYlbPDev9psN9bnJ+C\n' +
+ '+wzzPZ1zgSYKIAN0IMoh0L7BRAoau7VWQ3Q7hP6HIbdzOTEGyklSoh9pIh6IlwZZ\n' +
+ 'XfPlFlnn7FeH1UeA711E174SUpDRKYSfT+mFObQUuQewGi9QC3gBsz5MPLQQLzML\n' +
+ 'yimIOT+8i64fHHSKChw5ZDckBffej31/YHPQ7+JsWFV+G/6xDfbwnaFZFAUwo+1L\n' +
+ '4w9UiMyCNkIWCkulzJ2Hbz66xzFMi/8zMYxr08Af+PpsXaWTQHAa5V4GNJSInDEB\n' +
+ '7gy/CGLcY90EozoDABEBAAGJATwEGAEIACYWIQQVNixp3XT/DuGTF4MFmkL4L5UZ\n' +
+ 'dAUCW3b9UgIbDAUJA8JnAAAKCRAFmkL4L5UZdPqoB/9kpqxqa82k7JMcq7UiwQY7\n' +
+ 'CdqCUPKF88ciOWKBpZmpl8V7zgM7kEXwmM6ocHcznXi8xM7eOfDIJcBeqFVIE4OT\n' +
+ '63OCMuvZICM9Kiu48wLNAw5W/YGAOBH7ySQzZM2XrtvwfFtJ3lR00t5f4FVtriA5\n' +
+ '47BjYYG5tTdJc8HwEHs045S99xKCWqwuDgO9qskIi6iPePUkuhpaVBLuEj2Goku6\n' +
+ 'i8aql/vKYQS67L7UHJiEbjLe+wP9k3FvWUFTx39lAubsDzb4Abhe+qRqs2TKD7Go\n' +
+ 'k35ZriRIYllmx4c9KyWL7Mvzcp+84Sq9LeMfsN4JstBDJ7jn6g19SjO5dmtxSuP0\n' +
+ '=zZSJ\n' +
+ '-----END PGP PUBLIC KEY BLOCK-----\n'
+ }
+};
+
+// (Pseudo-)Random String covering all of utf8.
+function bigString (length){// eslint-disable-line no-unused-vars
+ 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){// eslint-disable-line no-unused-vars
+ 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){// eslint-disable-line no-unused-vars
+ let maxlength = 1024 * 1024 * megabytes;
+ let uint = new Uint8Array(maxlength);
+ for (let i= 0; i < maxlength; i++){
+ uint[i] = Math.floor(Math.random() * 256);
+ }
+ return uint;
+}
+
+// (Pseudo-)Random string with very limited charset
+// (ascii only, no control chars)
+function bigBoringString (megabytes){// eslint-disable-line no-unused-vars
+ 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
+// eslint-disable-next-line no-unused-vars
+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
+const encryptedData =// eslint-disable-line no-unused-vars
+ '-----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';
+
+const ImportablePublicKey = {// eslint-disable-line no-unused-vars
+ fingerprint: '78034948BA7F5D0E9BDB67E4F63790C11E60278A',
+ key:'-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
+ '\n' +
+ 'mQENBFsPvK0BCACaIgoIN+3g05mrTITULK/YDTrfg4W7RdzIZBxch5CM0zdu/dby\n' +
+ 'esFwaJbVQIqu54CRz5xKAiWmRrQCaRvhvjY0na5r5UUIpbeQiOVrl65JtNbRmlik\n' +
+ 'd9Prn1kZDUOZiCPIKn+/M2ecJ92YedM7I4/BbpiaFB11cVrPFg4thepn0LB3+Whp\n' +
+ '9HDm4orH9rjy6IUr6yjWNIr+LYRY6/Ip2vWcMVjleEpTFznXrm83hrJ0n0INtyox\n' +
+ 'Nass4eDWkgo6ItxDFFLOORSmpfrToxZymSosWqgux/qG6sxHvLqlqy6Xe3ZYRFbG\n' +
+ '+JcA1oGdwOg/c0ndr6BYYiXTh8+uUJfEoZvzABEBAAG0HEJsYSBCbGEgPGJsYWJs\n' +
+ 'YUBleGFtcGxlLm9yZz6JAVQEEwEIAD4WIQR4A0lIun9dDpvbZ+T2N5DBHmAnigUC\n' +
+ 'Ww+8rQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD2N5DBHmAn\n' +
+ 'igwIB/9K3E3Yev9taZP4KnXPhk1oMQRW1MWAsFGUr+70N85VwedpUawymW4vXi1+\n' +
+ 'hMeTc39QjmZ0+VqHkJttkqEN6bLcEvgmU/mOlOgKdzy6eUcasYAzgoAKUqSX1SPs\n' +
+ '0Imo7Tj04wnfnVwvKxaeadi0VmdqIYaW75UlrzIaltsBctyeYH8sBrvaTLscb4ON\n' +
+ '46OM3Yw2G9+dBF0P+4UYFHP3EYZMlzNxfwF+i2HsYcNDHlcLfjENr9GwKn5FJqpY\n' +
+ 'Iq3qmI37w1hVasHDxXdz1X06dpsa6Im4ACk6LXa7xIQlXxTgPAQV0sz2yB5eY+Md\n' +
+ 'uzEXPGW+sq0WRp3hynn7kVP6QQYvuQENBFsPvK0BCACwvBcmbnGJk8XhEBRu2QN3\n' +
+ 'jKgVs3CG5nE2Xh20JipZwAuGHugDLv6/jlizzz5jtj3SAHVtJB8lJW8I0cNSEIX8\n' +
+ 'bRYH4C7lP2DTb9CgMcGErQIyK480+HIsbsZhJSNHdjUUl6IPEEVfSQzWaufmuswe\n' +
+ 'e+giqHiTsaiW20ytXilwVGpjlHBaxn/bpskZ0YRasgnPqKgJD3d5kunNqWoyCpMc\n' +
+ 'FYgDERvPbhhceFbvFE9G/u3gbcuV15mx53dDX0ImvPcvJnDOyJS9yr7ApdOV312p\n' +
+ 'A1MLbxfPnbnVu+dGXn7D/VCDd5aBYVPm+5ANrk6z9lYKH9aO5wgXpLAdJvutCOL5\n' +
+ 'ABEBAAGJATwEGAEIACYWIQR4A0lIun9dDpvbZ+T2N5DBHmAnigUCWw+8rQIbDAUJ\n' +
+ 'A8JnAAAKCRD2N5DBHmAnigMVB/484G2+3R0cAaj3V/z4gW3MRSMhcYqEMyJ/ACdo\n' +
+ '7y8eoreYW843JWWVDRY6/YcYYGuBBP47WO4JuP2wIlVn17XOCSgnNjmmjsIYiAzk\n' +
+ 'op772TB27o0VeiFX5iWcawy0EI7JCb23xpI+QP31ksL2yyRYFXCtXSUfcOrLpCY8\n' +
+ 'aEQMQbAGtkag1wHTo/Tf/Vip8q0ZEQ4xOKTR2/ll6+inP8kzGyzadElUnH1Q1OUX\n' +
+ 'd2Lj/7BpBHE2++hAjBQRgnyaONF7mpUNEuw64iBNs0Ce6Ki4RV2+EBLnFubnFNRx\n' +
+ 'fFJcYXcijhuf3YCdWzqYmPpU/CtF4TgDlfSsdxHxVOmnZkY3\n' +
+ '=qP6s\n' +
+ '-----END PGP PUBLIC KEY BLOCK-----\n',
+
+ keyChangedUserId: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
+ '\n' +
+ 'mQENBFsPvK0BCACaIgoIN+3g05mrTITULK/YDTrfg4W7RdzIZBxch5CM0zdu/dby\n' +
+ 'esFwaJbVQIqu54CRz5xKAiWmRrQCaRvhvjY0na5r5UUIpbeQiOVrl65JtNbRmlik\n' +
+ 'd9Prn1kZDUOZiCPIKn+/M2ecJ92YedM7I4/BbpiaFB11cVrPFg4thepn0LB3+Whp\n' +
+ '9HDm4orH9rjy6IUr6yjWNIr+LYRY6/Ip2vWcMVjleEpTFznXrm83hrJ0n0INtyox\n' +
+ 'Nass4eDWkgo6ItxDFFLOORSmpfrToxZymSosWqgux/qG6sxHvLqlqy6Xe3ZYRFbG\n' +
+ '+JcA1oGdwOg/c0ndr6BYYiXTh8+uUJfEoZvzABEBAAG0HEJsYSBCbGEgPGJsYWJs\n' +
+ 'YUBleGFtcGxlLm9yZz6JAVQEEwEIAD4WIQR4A0lIun9dDpvbZ+T2N5DBHmAnigUC\n' +
+ 'Ww+8rQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD2N5DBHmAn\n' +
+ 'igwIB/9K3E3Yev9taZP4KnXPhk1oMQRW1MWAsFGUr+70N85VwedpUawymW4vXi1+\n' +
+ 'hMeTc39QjmZ0+VqHkJttkqEN6bLcEvgmU/mOlOgKdzy6eUcasYAzgoAKUqSX1SPs\n' +
+ '0Imo7Tj04wnfnVwvKxaeadi0VmdqIYaW75UlrzIaltsBctyeYH8sBrvaTLscb4ON\n' +
+ '46OM3Yw2G9+dBF0P+4UYFHP3EYZMlzNxfwF+i2HsYcNDHlcLfjENr9GwKn5FJqpY\n' +
+ 'Iq3qmI37w1hVasHDxXdz1X06dpsa6Im4ACk6LXa7xIQlXxTgPAQV0sz2yB5eY+Md\n' +
+ 'uzEXPGW+sq0WRp3hynn7kVP6QQYvtCZTb21lb25lIEVsc2UgPHNvbWVvbmVlbHNl\n' +
+ 'QGV4YW1wbGUub3JnPokBVAQTAQgAPhYhBHgDSUi6f10Om9tn5PY3kMEeYCeKBQJb\n' +
+ 'D705AhsDBQkDwmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEPY3kMEeYCeK\n' +
+ 'aIUH/2o+Ra+GzxgZrVexXLL+FCSmcu0cxeWfMhL8jd96c6uXIT21qQMRU2jgvnUp\n' +
+ 'Wdi/BeLKp5lYwywm04PFhmRVxWXLuLArCsDu+CFys+aPeybnjikPBZov6P8/cZV3\n' +
+ 'cd6zxFvqB9J15HjDMcl/r5v6d4CgSLKlFebrO5WKxHa6zGK9TRMQrqTu1heKHRf6\n' +
+ '4+Wj+MZmYnPzEQePjiBw/VkJ1Nm37Dd24gKdcN/qJFwEOqvbI5RIjB7xqoDslZk9\n' +
+ 'sAivBXwF0E9HKqvh4WZZeA7uaWNdGo/cQkD5rab5SdHGNPHLbzoRWScsM8WYtsME\n' +
+ 'dEMp5iPuG9M63+TD7losAkJ/TlS5AQ0EWw+8rQEIALC8FyZucYmTxeEQFG7ZA3eM\n' +
+ 'qBWzcIbmcTZeHbQmKlnAC4Ye6AMu/r+OWLPPPmO2PdIAdW0kHyUlbwjRw1IQhfxt\n' +
+ 'FgfgLuU/YNNv0KAxwYStAjIrjzT4cixuxmElI0d2NRSXog8QRV9JDNZq5+a6zB57\n' +
+ '6CKoeJOxqJbbTK1eKXBUamOUcFrGf9umyRnRhFqyCc+oqAkPd3mS6c2pajIKkxwV\n' +
+ 'iAMRG89uGFx4Vu8UT0b+7eBty5XXmbHnd0NfQia89y8mcM7IlL3KvsCl05XfXakD\n' +
+ 'UwtvF8+dudW750ZefsP9UIN3loFhU+b7kA2uTrP2Vgof1o7nCBeksB0m+60I4vkA\n' +
+ 'EQEAAYkBPAQYAQgAJhYhBHgDSUi6f10Om9tn5PY3kMEeYCeKBQJbD7ytAhsMBQkD\n' +
+ 'wmcAAAoJEPY3kMEeYCeKAxUH/jzgbb7dHRwBqPdX/PiBbcxFIyFxioQzIn8AJ2jv\n' +
+ 'Lx6it5hbzjclZZUNFjr9hxhga4EE/jtY7gm4/bAiVWfXtc4JKCc2OaaOwhiIDOSi\n' +
+ 'nvvZMHbujRV6IVfmJZxrDLQQjskJvbfGkj5A/fWSwvbLJFgVcK1dJR9w6sukJjxo\n' +
+ 'RAxBsAa2RqDXAdOj9N/9WKnyrRkRDjE4pNHb+WXr6Kc/yTMbLNp0SVScfVDU5Rd3\n' +
+ 'YuP/sGkEcTb76ECMFBGCfJo40XualQ0S7DriIE2zQJ7oqLhFXb4QEucW5ucU1HF8\n' +
+ 'UlxhdyKOG5/dgJ1bOpiY+lT8K0XhOAOV9Kx3EfFU6admRjc=\n' +
+ '=9WZ7\n' +
+ '-----END PGP PUBLIC KEY BLOCK-----\n'
+};
+
+/**
+ * Changes base64 encoded gpg messages
+ * @param {String} msg input message
+ * @param {Number} rate of changes as percentage of message length.
+ * @param {[Number, Number]} p begin and end of the message left untouched (to
+ * preserve) header/footer
+ */
+// eslint-disable-next-line no-unused-vars
+function sabotageMsg (msg, rate = 0.01, p= [35,35]){
+ const iterations = Math.floor(Math.random() * msg.length * rate) + 1;
+ const base64_set =
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/';
+ for (let i=0; i < iterations; i++){
+ let str0, str1, str2;
+ const chosePosition = function (){
+ let position =
+ Math.floor( Math.random() * (msg.length - p[0] + p[1]))
+ + p[0];
+ str1 = msg.substring(position,position+1);
+ if (str1 === '\n'){
+ chosePosition();
+ } else {
+ str0 = msg.substring(0,position);
+ str2 = msg.substring(position +1);
+ }
+ };
+ chosePosition();
+ let new1 = function (){
+ let n = base64_set[Math.floor(Math.random() * 64)];
+ return (n === str1) ? new1() : n;
+ };
+ msg = str0.concat(new1()).concat(str2);
+ }
+ return msg;
+}
+
+// a simple png
+// eslint-disable-next-line no-unused-vars
+const binaryData = {
+ base64:
+ 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAEwElEQVQ4y62R228b1xn' +
+ 'E51z27I271K64okTqRkuOLRuNHcNBCqTwS//qvvYCtAmQNrETO7akyJJsipRIUeSSez' +
+ 'vnfH1IkBboW5F5mpf5ATPDiAi/hTh+I8n/I/NrCcbY/4DI/GKY+E/CGLIWROCccQ7Or' +
+ 'bVVXWmttTHKUY6UjuP8jJMggilhVjAluILwIUMwRlpTUVBVQWsoxT1PS3E7v7u6Ho0m' +
+ '48boOIz63V6WdsIgZIxJmBXyMyx/QjWEbCPYReuQRNsWhbm5tqMRFQVPU6x3JlZ/e/r' +
+ 'm76+/OR6elbrKovT5/ae/f/L5YGcQeL5EOcHsa8z/hOY1eIbVF6b5o/YeNrnWFxf2+5' +
+ 'fs5kY9PDKHdlgu/vrPv/357F95UxLo7G44nk+EEO2orbKuRHOL1feo/wJ8hFEmn5dNv' +
+ 'JB8MRP1xYV79j68HrEss1ub15Ph8dXpoil+3lqTPZ+PXp7+8On9x51kXcLUsAtgARBR' +
+ 'ZfRpnn93voouxsJM5ptJsp1lfn+HwtDOpAX77/sM2bvVfFUW2hgJJ4bTQ9UH5YA1psz' +
+ 'zyYery69ezaihzx883BgM2GDgxFGqq92N3YvltDA1CGDwJU8j3/UYZ1rCTRE+QfMBpQ' +
+ 'BdgRzwAFwyKSAV1jvU38HGptvy+4J/8ej5sih+uHq31HUg+ePexvNHg27mcGch4aZY+' +
+ '4wsB23Z4j1XCNeOtr0dG6Eyst3tFV47ZwpQcZQcDR6YUnfD9UWxSBL/06Peo0+67WRC' +
+ 'cinBBKmMnE8s98gcQjlh1Nn2sqArxiusyP1Yu8WdyUzVYhVpkiQ2/PX7W4PdQXdvP1x' +
+ 'Lx0x9Z8BkXVVU1rQgGjZ2VPNOy9nrxPGmjdRkUs8XuqhEAcOIrCzG1zdv3/x4fn7+2b' +
+ 'OngTMIPZfL0mJIsHJyfc3LUlyN5Jt37MfX4uixs96TmcO5MpxqggWKhlaNiUBE1pC2M' +
+ 'BaWOOPM40gY7hEq+fLVKx/olOVa07hEgjEwzhnzlYg9GXs2r21L8dgXLddGaWtwtBv1' +
+ '/LgbWE9rzn2khK7FWPT7/Xy5bIVBO03U5qbc2+fdDZkkJBUDlGCh4h2fd0LpKlOw6VQ' +
+ 'MF+FQxwvlyziMAyXBJxbH8vjkhKw92N8peplyOcWRE7mMk6t4J1KRJ1arQsIwW1qqKs' +
+ 'ymwekV+wcTTKLeNt0OdRwowJEvXrxoJ+1wM8kTNncWQpVtIVLWapEbKIebmvQyv72pl' +
+ '3da2VJMSzOu1RWAnG7KpjC1VOhK/lR++YcvucPIa6biw6V+zSy7J5/6uh3IlrEMpilm' +
+ 'N9fvXs0uT4KtnuoHvfBBg4Y46ztHbRvLleSrFpQrDw4OGqpv9Xi0qqf1R1hs6oPaVNp' +
+ 'oznlT1+VycXd5cvv2a6JnycaTA/93mRqAsYytp0UoRzO7mLPQlVEUNaY2dZ3RVoVnAN' +
+ 'ZExhpRmrJmDYyRXtDePgDQ3r63nuwEaUaKM8CtyZlN6fhEv33DuxtScAHmhIh62I+cx' +
+ 'BjDaye/XU3zO85E6HvK9bODo7X+nhdGYdrx/VgoF9ZCL20zqW9v6f17ayEBCCYDGTpc' +
+ 'Rc5aXdez5d3Hy/PT41MAh4eH+7s7ydY2Z8xRSjiOVC5jDLDkuBRFYm8fAEvSfwPxgHl' +
+ 'kzr8e4QAAAABJRU5ErkJggg==',
+ encryptedArmored: '-----BEGIN PGP MESSAGE-----\n' +
+ '\n' +
+ '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\n' +
+ '=AkaP\n' +
+ '-----END PGP MESSAGE-----\n'
+};
+
+// eslint-disable-next-line no-unused-vars
+const filename_files = [{
+ name: 'Example-1234.txt',
+ data: '-----BEGIN PGP MESSAGE-----\n' +
+ '\n' +
+ 'hQEMA6B8jfIUScGEAQf/Ylt9GDcv/PGjX8v8CBWIeetzD7DpB8c5dZu57rPOhF7a\n' +
+ 'gZ5wUCNwuZ5jSnPh/MAH1amr9AEHhW28JlHq+Lpoohl50iNFQy01M+Kxh1LmSKup\n' +
+ 'hFQl3Lu+NewdShq/RwNc9+qdTAnCdwjGJ+SxODfo73cflLl9SSPJ7k29bdUUL1mp\n' +
+ 'aGlYdecTB6lcz4pCNOyyGryDBJQcS5ObulpN4zvhSfFzT27GQFmQPElm7CTdGOf0\n' +
+ '5VUxFe0TqRmdJ9LzVuOVZB7x8E0BpuQYpPd88emS+KOozx4KWu0IakdQ4QBY0av5\n' +
+ 'ZID2rgM640Z4T8kXgGZq2qFN1Ap5X3iwfjkEHaJIP9JXAb86F8IP7nLrxzN2V0eM\n' +
+ '3v0+1o0HJd/E4LPeXHXCaNDaJOr8rviOCLwoFvCJ9E10ZASLyqOXzhlW9Tkvxrjl\n' +
+ 'ldeXQI8Fp6oWPfvW8qGQ917mzxuoQYGn\n' +
+ '=993W\n' +
+ '-----END PGP MESSAGE-----\n'
+}, {
+ name: 'Example-@€µ2äüß.txt',
+ data: '-----BEGIN PGP MESSAGE-----\n'+
+ '\n'+
+ 'hQEMA6B8jfIUScGEAQgAiX5vBNJGPYvljleo/7nkee4mGsFL1ROXLOs7sUlBImFm\n'+
+ 'axQ0PAtVsX9NvDY70Tj5EIaGmgQWr/WAnH5fuV+ctsZtPm/UsL2BhYgKz3cDcS2P\n'+
+ '1tni3WhHXVr8ldC3PePuEn0Wfy/wOS+y2FbkJOD9EqXeui06phB8ScGdF6se3AcA\n'+
+ 'lNo6bFeURgK6NhIYgibKbybAr1+D/zUvksn5xnLztBarVeJFOwAj8I+lthLpoyj2\n'+
+ 'vUFu2qOlSOW/98Z0ZYDvRqnB5Mqmqsgf0cWl4Lwt0+GrdfzuB+479+ouIJCFUaIA\n'+
+ 'JDoU8Ct0UwgAoYZmDkxBtjZALmf3dGqH1gjSe0UbDdJhAZ9h5rlC525JNOse0v21\n'+
+ 'LdrDtwtiETFZ9ras8RelYeyYyE7PfhBxtmP5EBZUk7Be6JbD2vn5s2pgsbmBTzGJ\n'+
+ 'AcxxSN6MbTvInIvC3GhSTs0mLiC4sToVoPp/F8tfQIGZWg==\n'+
+ '=V6wP\n'+
+ '-----END PGP MESSAGE-----\n'
+}, {
+ name: 'Example- äüüß.txt',
+ data: '-----BEGIN PGP MESSAGE-----\n' +
+ '\n' +
+ 'hQEMA6B8jfIUScGEAQf9H7CbkI952WbUqkuYIlgKri+Tr+G+9m1GN/mKh82GnwfZ\n' +
+ '8JekOOzdZ6BdCfyJohOSan959r1pOHJzj2sh+LitBbD02MDPg8BL14lUXfbUju7s\n' +
+ 'eT5HuVDfnFWV2ThfEyVUNmAEaE57FwTzdO7vN1VYkkBNFC8pjCONQ6/iRWnDgUyB\n' +
+ 'fJJSLkdFMDBgHSrEeSCyDP4P5rJyd/1JhqXXECLIMzIKWCUbvWNvKLfA71fhPbi3\n' +
+ 'XzXLWhNKQWoMZsl2oEHJuPY7ez/KePJ07Km0gxcbBJhUGTRRNrHSjOxiaV7/TLp2\n' +
+ 'O3U/GuPQ/eY4Xl3rE/cDaCjy2sdR4VyuxlbLeUVIvtJbAUzNkaibs9ydZshBj9UD\n' +
+ 'x2JWCwkBa7Q1Mah9nciT8S2Co71dsVMdIc3VtsXUtlhomL1bHd8ipRhFSiqiyZM3\n' +
+ 'Pih6tFUOcXuSaf0lv6FENXP+IThHiaujtjAbkA==\n' +
+ '=UxvV\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 0000000..534a95a
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/longRunningTests.js
@@ -0,0 +1,58 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+/* global describe, it, before, expect, Gpgmejs */
+/* global bigString, inputvalues */
+
+describe('Long running Encryption/Decryption', function () {
+ let context = null;
+ const good_fpr = inputvalues.encrypt.good.fingerprint;
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+
+ for (let i=1; i < 101; i++) {
+ it('Successful encrypt/decrypt completely random data '
+ + (i) + '/100', function (done) {
+ const data = bigString(2*1024*1024);
+ context.encrypt({ data: data, publicKeys: good_fpr })
+ .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({ data: answer.data })
+ .then(function (result){
+ expect(result).to.not.be.empty;
+ expect(result.data).to.be.a('string');
+ expect(result.data).to.equal(data);
+ done();
+ });
+ });
+ }).timeout(15000);
+ }
+
+});
diff --git a/lang/js/BrowserTestExtension/tests/signTest.js b/lang/js/BrowserTestExtension/tests/signTest.js
new file mode 100644
index 0000000..1e269e6
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/signTest.js
@@ -0,0 +1,64 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, expect, before, Gpgmejs */
+/* global bigString, inputvalues */
+
+describe('Signing', function () {
+ let context = null;
+ const good_fpr = inputvalues.encrypt.good.fingerprint;
+
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+
+ it('Sign a message', function (done) {
+ const data = bigString(100);
+ context.sign({ data: data, keys: good_fpr }).then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a('string');
+ expect(answer.data).to.include('BEGIN PGP SIGNATURE');
+ expect(answer.data).to.include('END PGP SIGNATURE');
+ expect(answer.data).to.include(data);
+ done();
+ });
+ });
+
+ it('Detached sign a message', function (done) {
+ const data = bigString(100);
+ context.sign({ data: data, keys: good_fpr, mode: 'detached' })
+ .then(function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.data).to.be.a('string');
+ expect(answer.data).to.include(data);
+ expect(answer.signature).to.be.a('string');
+ expect(answer.signature).to.be.a('string');
+ done();
+ });
+ });
+
+});
diff --git a/lang/js/BrowserTestExtension/tests/startup.js b/lang/js/BrowserTestExtension/tests/startup.js
new file mode 100644
index 0000000..e7c7478
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/startup.js
@@ -0,0 +1,47 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, expect, Gpgmejs, inputvalues */
+
+describe('GPGME context', function (){
+ it('Starting a GpgME instance', function (done){
+ let prm = Gpgmejs.init({ timeout: 2000 });
+ const input = inputvalues.someInputParameter;
+ prm.then(
+ function (context){
+ expect(context).to.be.an('object');
+ expect(context.encrypt).to.be.a('function');
+ expect(context.decrypt).to.be.a('function');
+ expect(context.sign).to.be.a('function');
+ expect(context.verify).to.be.a('function');
+ context.Keyring = input;
+ expect(context.Keyring).to.be.an('object');
+ expect(context.Keyring).to.not.equal(input);
+ expect(context.Keyring.getKeys).to.be.a('function');
+ expect(context.Keyring.getDefaultKey).to.be.a('function');
+ expect(context.Keyring.importKey).to.be.a('function');
+ expect(context.Keyring.generateKey).to.be.a('function');
+ done();
+ });
+ });
+}); \ No newline at end of file
diff --git a/lang/js/BrowserTestExtension/tests/verifyTest.js b/lang/js/BrowserTestExtension/tests/verifyTest.js
new file mode 100644
index 0000000..c63f684
--- /dev/null
+++ b/lang/js/BrowserTestExtension/tests/verifyTest.js
@@ -0,0 +1,90 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global describe, it, expect, before, bigString, inputvalues, Gpgmejs */
+
+
+
+describe('Verifying data', function () {
+ let context = null;
+ before(function (done){
+ const prm = Gpgmejs.init({ timeout: 2000 });
+ prm.then(function (gpgmejs){
+ context = gpgmejs;
+ done();
+ });
+ });
+ it('Successful verify message', function (done) {
+ const message = inputvalues.signedMessage.good;
+ context.verify({ data: message }).then(function (result){
+ expect(result.data).to.be.a('string');
+ expect(result.signatures.all_valid).to.be.true;
+ expect(result.signatures.count).to.equal(1);
+ expect(result.signatures.signatures.good).to.be.an('array');
+ expect(result.signatures.signatures.good.length).to.equal(1);
+ expect(result.signatures.signatures.good[0].fingerprint).to.be.a('string');
+ expect(result.signatures.signatures.good[0].valid).to.be.true;
+ done();
+ });
+ });
+
+ it('Successfully recognize changed cleartext', function (done) {
+ const message = inputvalues.signedMessage.bad;
+ context.verify({ data: message }).then(function (result){
+ expect(result.data).to.be.a('string');
+ expect(result.signatures.all_valid).to.be.false;
+ expect(result.signatures.count).to.equal(1);
+ expect(result.signatures.signatures.bad).to.be.an('array');
+ expect(result.signatures.signatures.bad.length).to.equal(1);
+ expect(result.signatures.signatures.bad[0].fingerprint)
+ .to.be.a('string');
+ expect(result.signatures.signatures.bad[0].valid)
+ .to.be.false;
+ done();
+ });
+ });
+
+ it('Encrypt-Sign-Verify random message', function (done) {
+ const message = bigString(2000);
+ let fpr = inputvalues.encrypt.good.fingerprint;
+ context.encrypt({ data: message, publicKeys: fpr })
+ .then(function (message_enc){
+ context.sign({ data: message_enc.data, keys: fpr })
+ .then(function (message_encsign){
+ context.verify({ data: message_encsign.data })
+ .then(function (result){
+ expect(result.data).to.equal(message_enc.data);
+ expect(result.data).to.be.a('string');
+ expect(result.signatures.all_valid).to.be.true;
+ expect(result.signatures.count).to.equal(1);
+ const arr = result.signatures.signatures.good;
+ expect(arr).to.be.an('array');
+ expect(arr.length).to.equal(1);
+ expect(arr[0].fingerprint).to.equal(fpr);
+ expect(arr[0].valid).to.be.true;
+ 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 0000000..6f7da3f
--- /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/DemoExtension/Makefile.am b/lang/js/DemoExtension/Makefile.am
new file mode 100644
index 0000000..d6e87fd
--- /dev/null
+++ b/lang/js/DemoExtension/Makefile.am
@@ -0,0 +1,27 @@
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Intevation GmbH
+#
+# This file is part of gpgme.js.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+
+EXTRA_DIST = manifest.json \
+ popup.html \
+ entry.js \
+ maindemo.js \
+ mainui.html \
+ testicon.png \
+ ui.css
diff --git a/lang/js/DemoExtension/Makefile.in b/lang/js/DemoExtension/Makefile.in
new file mode 100644
index 0000000..1a379fd
--- /dev/null
+++ b/lang/js/DemoExtension/Makefile.in
@@ -0,0 +1,537 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Intevation GmbH
+#
+# This file is part of gpgme.js.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = lang/js/DemoExtension
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/mkinstalldirs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_pkg_swig.m4 \
+ $(top_srcdir)/m4/ax_python_devel.m4 \
+ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \
+ $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLED_LANGUAGES = @ENABLED_LANGUAGES@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@
+GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@
+GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@
+GPGME_QT_LIBS = @GPGME_QT_LIBS@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GRAPHVIZ = @GRAPHVIZ@
+GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGMEPP_LT_AGE = @LIBGPGMEPP_LT_AGE@
+LIBGPGMEPP_LT_CURRENT = @LIBGPGMEPP_LT_CURRENT@
+LIBGPGMEPP_LT_REVISION = @LIBGPGMEPP_LT_REVISION@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBQGPGME_LT_AGE = @LIBQGPGME_LT_AGE@
+LIBQGPGME_LT_CURRENT = @LIBQGPGME_LT_CURRENT@
+LIBQGPGME_LT_REVISION = @LIBQGPGME_LT_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MOC = @MOC@
+MOC2 = @MOC2@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PYTHON = @PYTHON@
+PYTHONS = @PYTHONS@
+PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LDFLAGS = @PYTHON_LDFLAGS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SITE_PKG = @PYTHON_SITE_PKG@
+PYTHON_VERSION = @PYTHON_VERSION@
+QTCHOOSER = @QTCHOOSER@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SWIG = @SWIG@
+SWIG_LIB = @SWIG_LIB@
+SYSROOT = @SYSROOT@
+VERSION = @VERSION@
+VERSION_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+VERSION_NUMBER = @VERSION_NUMBER@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = manifest.json \
+ popup.html \
+ entry.js \
+ maindemo.js \
+ mainui.html \
+ testicon.png \
+ ui.css
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lang/js/DemoExtension/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/js/DemoExtension/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lang/js/DemoExtension/entry.js b/lang/js/DemoExtension/entry.js
new file mode 100644
index 0000000..fd261a0
--- /dev/null
+++ b/lang/js/DemoExtension/entry.js
@@ -0,0 +1,30 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global chrome */
+
+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 0000000..c992e7e
--- /dev/null
+++ b/lang/js/DemoExtension/maindemo.js
@@ -0,0 +1,123 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global document, Gpgmejs */
+
+document.addEventListener('DOMContentLoaded', function () {
+ Gpgmejs.init().then(function (gpgmejs){
+ document.getElementById('buttonencrypt').addEventListener('click',
+ function (){
+ let data = document.getElementById('inputtext').value;
+ let keyId = document.getElementById('pubkey').value;
+ gpgmejs.encrypt({ data: data, publicKeys: keyId, armor: true })
+ .then(function (answer){
+ if (answer.data){
+ document.getElementById(
+ 'answer').value = answer.data;
+ }
+ }, function (errormsg){
+ alert( errormsg.message);
+ });
+ });
+
+ document.getElementById('buttondecrypt').addEventListener('click',
+ function (){
+ let data = document.getElementById('inputtext').value;
+ gpgmejs.decrypt({ data: data }).then(
+ function (answer){
+ if (answer.data){
+ document.getElementById(
+ 'answer').value = answer.data;
+ }
+ }, function (errormsg){
+ alert(errormsg.message);
+ });
+ });
+
+ document.getElementById('getdefaultkey').addEventListener('click',
+ function (){
+ gpgmejs.Keyring.getDefaultKey().then(function (answer){
+ document.getElementById('pubkey').value =
+ answer.fingerprint;
+ }, function (errormsg){
+ alert(errormsg.message);
+ });
+ });
+
+ document.getElementById('signtext').addEventListener('click',
+ function (){
+ let data = document.getElementById('inputtext').value;
+ let keyId = document.getElementById('pubkey').value;
+ gpgmejs.sign({ data: data, keys: keyId }).then(
+ function (answer){
+ if (answer.data){
+ document.getElementById(
+ 'answer').value = answer.data;
+ }
+ }, function (errormsg){
+ alert( errormsg.message);
+ });
+ });
+
+ document.getElementById('verifytext').addEventListener('click',
+ function (){
+ let data = document.getElementById('inputtext').value;
+ gpgmejs.verify({ data: data }).then(
+ function (answer){
+ let vals = '';
+ if (answer.all_valid === true){
+ vals = 'Success! ';
+ } else {
+ vals = 'Failure! ';
+ }
+ vals = vals + (answer.count - answer.failures) + 'of '
+ + answer.count + ' signature(s) were successfully '
+ + 'verified.\n\n' + answer.data;
+ document.getElementById('answer').value = vals;
+ }, function (errormsg){
+ alert( errormsg.message);
+ });
+ });
+ document.getElementById('searchkey').addEventListener('click',
+ function (){
+ let data = document.getElementById('inputtext').value;
+ gpgmejs.Keyring.getKeys({
+ pattern: data,
+ prepare_sync: true,
+ search: true }
+ ).then(function (keys){
+ if (keys.length === 1){
+ document.getElementById(
+ 'pubkey').value = keys[0].fingerprint;
+ } else if (keys.length > 1) {
+ alert('The pattern was not unambigious enough for a Key. '
+ + keys.length + ' Keys were found');
+ } else {
+ alert('No keys found');
+ }
+ }, function (errormsg){
+ alert( errormsg.message);
+ });
+ });
+ });
+});
diff --git a/lang/js/DemoExtension/mainui.html b/lang/js/DemoExtension/mainui.html
new file mode 100644
index 0000000..c773c9b
--- /dev/null
+++ b/lang/js/DemoExtension/mainui.html
@@ -0,0 +1,47 @@
+<!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>
+ <div>
+
+ <div class="left">
+ <ul>
+ <li>
+ <span class="label">Input</span>
+ <textarea rows="5" cols="65" id="inputtext" wrap="hard"></textarea>
+ </li>
+ <li>
+ <span class="label">Fingerprint of Key to use: </span>
+ <input type="text" id="pubkey" value="" />
+ <button id="getdefaultkey">
+ Set to default signing key
+ </button>&nbsp;
+ <button id="searchkey">
+ Look up Key
+ </button>
+ </li>
+ </ul>
+ </div>
+ <div class="right">
+ <ul>
+ <li>
+ <span class="label">Result</span>
+ <textarea id="answer" rows="5" cols="65" wrap="hard"></textarea>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <div class="center">
+ <button id="buttonencrypt">Encrypt input text</button><br>
+ <button id="buttondecrypt">Decrypt input text</button><br>
+ <button id="signtext">Sign input text</button> <br>
+ <button id="verifytext">Verify input text</button><br>
+
+ </div>
+</body>
+</html>
diff --git a/lang/js/DemoExtension/manifest.json b/lang/js/DemoExtension/manifest.json
new file mode 100644
index 0000000..9e057b3
--- /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 0000000..5007031
--- /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 0000000..84284e0
--- /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 0000000..16dfb5a
--- /dev/null
+++ b/lang/js/DemoExtension/ui.css
@@ -0,0 +1,33 @@
+ul {
+ list-style-type: none;
+ padding-left: 0px;
+}
+
+ul li span {
+ float: left;
+ width: 120px;
+ margin-top: 6px;
+}
+
+div .left {
+ float: left;
+ align-items: stretch;
+ width: 40%;
+}
+div .center {
+ width: 50%;
+ align-content: space-between;
+}
+
+div .center button {
+ align-self: stretch;
+}
+div .right {
+ float: right;
+ align-items: stretch;
+ width: 40%;
+}
+
+div .bottom {
+ clear:both;
+} \ No newline at end of file
diff --git a/lang/js/Makefile.am b/lang/js/Makefile.am
new file mode 100644
index 0000000..63cc41b
--- /dev/null
+++ b/lang/js/Makefile.am
@@ -0,0 +1,32 @@
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+#
+# This file is part of gpgme.js.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+
+SUBDIRS = src BrowserTestExtension DemoExtension
+
+EXTRA_DIST = build_extensions.sh \
+ jsdoc.conf \
+ jsdoc_index.md \
+ .eslintrc.json \
+ package.json \
+ README \
+ unittest_inputvalues.js \
+ unittests.js \
+ webpack.conf.js \
+ webpack.conf_unittests.js
diff --git a/lang/js/Makefile.in b/lang/js/Makefile.in
new file mode 100644
index 0000000..0dbc0bc
--- /dev/null
+++ b/lang/js/Makefile.in
@@ -0,0 +1,721 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
+#
+# This file is part of gpgme.js.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = lang/js
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/mkinstalldirs README
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_pkg_swig.m4 \
+ $(top_srcdir)/m4/ax_python_devel.m4 \
+ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \
+ $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLED_LANGUAGES = @ENABLED_LANGUAGES@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@
+GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@
+GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@
+GPGME_QT_LIBS = @GPGME_QT_LIBS@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GRAPHVIZ = @GRAPHVIZ@
+GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGMEPP_LT_AGE = @LIBGPGMEPP_LT_AGE@
+LIBGPGMEPP_LT_CURRENT = @LIBGPGMEPP_LT_CURRENT@
+LIBGPGMEPP_LT_REVISION = @LIBGPGMEPP_LT_REVISION@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBQGPGME_LT_AGE = @LIBQGPGME_LT_AGE@
+LIBQGPGME_LT_CURRENT = @LIBQGPGME_LT_CURRENT@
+LIBQGPGME_LT_REVISION = @LIBQGPGME_LT_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MOC = @MOC@
+MOC2 = @MOC2@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PYTHON = @PYTHON@
+PYTHONS = @PYTHONS@
+PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LDFLAGS = @PYTHON_LDFLAGS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SITE_PKG = @PYTHON_SITE_PKG@
+PYTHON_VERSION = @PYTHON_VERSION@
+QTCHOOSER = @QTCHOOSER@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SWIG = @SWIG@
+SWIG_LIB = @SWIG_LIB@
+SYSROOT = @SYSROOT@
+VERSION = @VERSION@
+VERSION_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+VERSION_NUMBER = @VERSION_NUMBER@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = src BrowserTestExtension DemoExtension
+EXTRA_DIST = build_extensions.sh \
+ jsdoc.conf \
+ jsdoc_index.md \
+ .eslintrc.json \
+ package.json \
+ README \
+ unittest_inputvalues.js \
+ unittests.js \
+ webpack.conf.js \
+ webpack.conf_unittests.js
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lang/js/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/js/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lang/js/README b/lang/js/README
new file mode 100644
index 0000000..fd95cc4
--- /dev/null
+++ b/lang/js/README
@@ -0,0 +1,116 @@
+gpgme.js - JavaScript for GPGME
+-------------------------------
+Initially developed for integration with the Mailvelope Web Extension.
+
+Overview
+--------
+
+gpgme.js is a javascript library for direct use of GnuPG in browsers.
+It interacts with GPGME through nativeMessaging and gpgme-json.
+
+It is meant to be distributed directly by its downstream users in
+their extension package. As such it is not integrated in the
+autotools build system. See build instructions below.
+
+
+gpgme-json
+----------
+
+gpgme-json (see core src/gpgme-json.c) the json to GPGME bridge is
+required as native messaging backend for gpgme.js to work.
+It needs to be installed and registered as native messaging
+backend with the browser.
+
+See gpgme-mozilla.json and gpgme-chrome.json examples in
+the top level doc/examples as example manifests.
+
+Any web extension using gpgme.js will need to be whitelisted in the manifest
+file by its id.
+
+Distributors are encouraged to create manifest packages for their
+distributions.
+
+
+Building gpgme.js
+-----------------
+
+gpgme.js uses webpack, and thus depends on Node.js for building.
+All dependencies will be installed (in a local subdirectory) with the command
+`npm install`.
+
+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 and Test WebExtension:
+---------------------------
+
+The Demo Extension shows simple examples of the usage of gpgme.js.
+
+The BrowsertestExtension runs more intensive tests (using the mocha and chai
+frameworks). 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 gpgme.js,
+which mostly are not exported.
+
+The file `build_extension.sh` may serve as a pointer on how to build and
+assemble these two Extensions and their dependencies. It can directly
+be used in most linux systems.
+
+The resulting folders 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!
+
+For the Extensions to successfully communicate with gpgme-json, a manifest file
+is needed.
+
+- `~/.config/chromium/NativeMessagingHosts/gpgmejson.json`
+
+In the browsers' nativeMessaging configuration folder a file 'gpgmejs.json'
+is needed, with the following content:
+
+- For Chrome/Chromium:
+ ```
+ {
+ "name": "gpgmejson",
+ "description": "This is a test application for gpgme.js",
+ "path": "/usr/bin/gpgme-json",
+ "type": "stdio",
+ "allowed_origins": ["chrome-extension://ExtensionIdentifier/"]
+ }
+ ```
+ The usual path for Linux is similar to:
+ `~/.config/chromium/NativeMessagingHosts/gpgmejson.json` for
+ For Windows, the path to the manifest needs to be placed in
+ `HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\gpgmejson`
+
+ - For firefox:
+ ```
+ {
+ "name": "gpgmejson",
+ "description": "This is a test application for gpgme.js",
+ "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.
+
+ The manifest for linux is usually placed at:
+ `~/.mozilla/native-messaging-hosts/gpgmejson.json`
+
+
+Documentation
+-------------
+
+The documentation can be built by jsdoc. It currently uses the command
+`./node_modules/.bin/jsdoc -c jsdoc.conf`.
diff --git a/lang/js/build_extensions.sh b/lang/js/build_extensions.sh
new file mode 100755
index 0000000..91d5479
--- /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/jsdoc.conf b/lang/js/jsdoc.conf
new file mode 100644
index 0000000..976f4e4
--- /dev/null
+++ b/lang/js/jsdoc.conf
@@ -0,0 +1,24 @@
+{
+ "tags": {
+ "allowUnknownTags": false,
+ "dictionaries": ["jsdoc"]
+ },
+ "source": {
+ "include": ["jsdoc_index.md", "./src"],
+ "includePattern": ".+\\.js(doc|x)?$",
+ "excludePattern": "(^|\\/|\\\\)_"
+ },
+ "opts":{
+ "destination": "./doc/",
+ "recurse": true
+ },
+ "sourceType": "module",
+ "plugins": [],
+ "templates": {
+ "cleverLinks": false,
+ "monospaceLinks": false,
+ "default": {
+ "outputSourceFiles": true
+ }
+ }
+} \ No newline at end of file
diff --git a/lang/js/jsdoc_index.md b/lang/js/jsdoc_index.md
new file mode 100644
index 0000000..b7371ad
--- /dev/null
+++ b/lang/js/jsdoc_index.md
@@ -0,0 +1,50 @@
+Using gpgme.js
+---------------
+At first, make sure that the environment you want to use gpgme.js in has access
+and permissions for nativeMessaging, and gpgme-json installed. For details,
+see the README.
+
+The library itself is started via the {@link init} method. This will test the
+nativeMessaging connection, and then resolve into an Object offering
+the top level API:
+
+* [encrypt]{@link GpgME#encrypt}
+* [decrypt]{@link GpgME#decrypt}
+* [sign]{@link GpgME#sign}
+* [verify]{@link GpgME#verify}
+* [Keyring]{@link GPGME_Keyring}
+
+```
+gpgmejs.init()
+ .then(function(GPGME) {
+ // using GPGME
+ }, function(error){
+ // error handling;
+ })
+```
+
+All methods that require communication with nativeMessaging are asynchronous,
+using Promises. Rejections will be instances of {@link GPGME_Error}.
+
+An exaeption are Keys, which can be initialized in a 'sync' mode, allowing them
+to be cached and used synchronously until manually refreshed.
+
+Keyring and Keys
+----------------
+The gnupg keys can be accessed via the [Keyring]{@link GPGME_Keyring}.
+
+The Keyring offers the methods for accessing information on all Keys known to
+gnupg.
+
+**Due to security constraints, the javascript-binding currently only offers
+limited support for secret-Key interaction.**
+
+The existance of secret Keys is not secret, and those secret Keys can be used
+for signing, but Operations that may expose, modify or delete secret Keys are
+not supported.
+
+* [getKeysArmored]{@link GPGME_Keyring#getKeysArmored}
+* [getKeys]{@link GPGME_Keyring#getKeys}
+* [getDefaultKey]{@link GPGME_Keyring#getDefaultKey}
+* [generateKey]{@link GPGME_Keyring#generateKey}
+* [deleteKey]{@link GPGME_Keyring#deleteKey}
diff --git a/lang/js/package.json b/lang/js/package.json
new file mode 100644
index 0000000..54af298
--- /dev/null
+++ b/lang/js/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "gpgmejs",
+ "version": "0.0.1-dev",
+ "description": "Javascript part of the GPGME nativeMessaging integration",
+ "main": "src/index.js",
+ "private": true,
+ "keywords": [],
+ "author": "",
+ "license": "LGPL-2.1+",
+ "devDependencies": {
+ "webpack": "^4.5.0",
+ "webpack-cli": "^3.0.8",
+ "chai": "^4.1.2",
+ "mocha": "^5.1.1",
+ "jsdoc": "^3.5.5"
+ }
+}
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
new file mode 100644
index 0000000..d43d55f
--- /dev/null
+++ b/lang/js/src/Connection.js
@@ -0,0 +1,320 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/* global chrome */
+
+import { permittedOperations } from './permittedOperations';
+import { gpgme_error } from './Errors';
+import { GPGME_Message, createMessage } from './Message';
+import { decode, atobArray, Utf8ArrayToStr } from './Helpers';
+
+/**
+ * A Connection handles the nativeMessaging interaction via a port. As the
+ * protocol only allows up to 1MB of message sent from the nativeApp to the
+ * browser, the connection will stay open until all parts of a communication
+ * are finished. For a new request, a new port will open, to avoid mixing
+ * contexts.
+ * @class
+ * @private
+ */
+export class Connection{
+
+ constructor (){
+ this._connection = chrome.runtime.connectNative('gpgmejson');
+ }
+
+ /**
+ * Immediately closes an open port.
+ */
+ disconnect () {
+ if (this._connection){
+ this._connection.disconnect();
+ this._connection = null;
+ }
+ }
+
+
+ /**
+ * @typedef {Object} backEndDetails
+ * @property {String} gpgme Version number of gpgme
+ * @property {Array<Object>} info Further information about the backend
+ * and the used applications (Example:
+ * <pre>
+ * {
+ * "protocol": "OpenPGP",
+ * "fname": "/usr/bin/gpg",
+ * "version": "2.2.6",
+ * "req_version": "1.4.0",
+ * "homedir": "default"
+ * }
+ * </pre>
+ */
+
+ /**
+ * Retrieves the information about the backend.
+ * @param {Boolean} details (optional) If set to false, the promise will
+ * just return if a connection was successful.
+ * @param {Number} timeout (optional)
+ * @returns {Promise<backEndDetails>|Promise<Boolean>} Details from the
+ * backend
+ * @async
+ */
+ checkConnection (details = true, timeout = 1000){
+ if (typeof timeout !== 'number' && timeout <= 0) {
+ timeout = 1000;
+ }
+ const msg = createMessage('version');
+ if (details === true) {
+ return this.post(msg);
+ } else {
+ let me = this;
+ return new Promise(function (resolve) {
+ Promise.race([
+ me.post(msg),
+ new Promise(function (resolve, reject){
+ setTimeout(function (){
+ reject(gpgme_error('CONN_TIMEOUT'));
+ }, timeout);
+ })
+ ]).then(function (){ // success
+ resolve(true);
+ }, function (){ // failure
+ resolve(false);
+ });
+ });
+ }
+ }
+
+ /**
+ * Sends a {@link GPGME_Message} via the nativeMessaging port. It
+ * resolves with the completed answer after all parts have been
+ * received and reassembled, or rejects with an {@link GPGME_Error}.
+ *
+ * @param {GPGME_Message} message
+ * @returns {Promise<*>} The collected answer, depending on the messages'
+ * operation
+ * @private
+ * @async
+ */
+ post (message){
+ if (!message || !(message instanceof GPGME_Message)){
+ this.disconnect();
+ return Promise.reject(gpgme_error(
+ 'PARAM_WRONG', 'Connection.post'));
+ }
+ if (message.isComplete() !== true){
+ this.disconnect();
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ let chunksize = message.chunksize;
+ const me = this;
+ return new Promise(function (resolve, reject){
+ let answer = new Answer(message);
+ let listener = function (msg) {
+ if (!msg){
+ me._connection.onMessage.removeListener(listener);
+ me._connection.disconnect();
+ reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
+ } else {
+ let answer_result = answer.collect(msg);
+ if (answer_result !== true){
+ me._connection.onMessage.removeListener(listener);
+ me._connection.disconnect();
+ reject(answer_result);
+ } else {
+ if (msg.more === true){
+ me._connection.postMessage({
+ 'op': 'getmore',
+ 'chunksize': chunksize
+ });
+ } else {
+ me._connection.onMessage.removeListener(listener);
+ me._connection.disconnect();
+ const message = answer.getMessage();
+ if (message instanceof Error){
+ reject(message);
+ } else {
+ resolve(message);
+ }
+ }
+ }
+ }
+ };
+ me._connection.onMessage.addListener(listener);
+ if (permittedOperations[message.operation].pinentry){
+ return me._connection.postMessage(message.message);
+ } else {
+ return Promise.race([
+ me._connection.postMessage(message.message),
+ function (resolve, reject){
+ setTimeout(function (){
+ me._connection.disconnect();
+ reject(gpgme_error('CONN_TIMEOUT'));
+ }, 5000);
+ }
+ ]).then(function (result){
+ return result;
+ }, function (reject){
+ if (!(reject instanceof Error)) {
+ me._connection.disconnect();
+ return gpgme_error('GNUPG_ERROR', reject);
+ } else {
+ return reject;
+ }
+ });
+ }
+ });
+ }
+}
+
+
+/**
+ * A class for answer objects, checking and processing the return messages of
+ * the nativeMessaging communication.
+ * @private
+ */
+class Answer{
+
+ /**
+ * @param {GPGME_Message} message
+ */
+ constructor (message){
+ this._operation = message.operation;
+ this._expected = message.expected;
+ this._response_b64 = null;
+ }
+
+ get operation (){
+ return this._operation;
+ }
+
+ get expected (){
+ return this._expected;
+ }
+
+ /**
+ * Adds incoming base64 encoded data to the existing response
+ * @param {*} msg base64 encoded data.
+ * @returns {Boolean}
+ *
+ * @private
+ */
+ collect (msg){
+ if (typeof (msg) !== 'object' || !msg.hasOwnProperty('response')) {
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ if (!this._response_b64){
+ this._response_b64 = msg.response;
+ return true;
+ } else {
+ this._response_b64 += msg.response;
+ return true;
+ }
+ }
+ /**
+ * Decodes and verifies the base64 encoded answer data. Verified against
+ * {@link permittedOperations}.
+ * @returns {Object} The readable gpnupg answer
+ */
+ getMessage (){
+ if (this._response_b64 === null){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ let _decodedResponse = JSON.parse(atob(this._response_b64));
+ let _response = {
+ format: 'ascii'
+ };
+ let messageKeys = Object.keys(_decodedResponse);
+ 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 (_decodedResponse.type === 'error'){
+ return (gpgme_error('GNUPG_ERROR',
+ decode(_decodedResponse.msg)));
+ } else if (poa.type.indexOf(_decodedResponse.type) < 0){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ break;
+ }
+ case 'base64': {
+ break;
+ }
+ case 'msg': {
+ if (_decodedResponse.type === 'error'){
+ return (gpgme_error('GNUPG_ERROR', _decodedResponse.msg));
+ }
+ break;
+ }
+ default: {
+ let answerType = null;
+ if (poa.payload && poa.payload.hasOwnProperty(key)){
+ answerType = 'p';
+ } else if (poa.info && poa.info.hasOwnProperty(key)){
+ answerType = 'i';
+ }
+ if (answerType !== 'p' && answerType !== 'i'){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+
+ if (answerType === 'i') {
+ if ( typeof (_decodedResponse[key]) !== poa.info[key] ){
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ _response[key] = decode(_decodedResponse[key]);
+
+ } else if (answerType === 'p') {
+ if (_decodedResponse.base64 === true
+ && poa.payload[key] === 'string'
+ ) {
+ if (this.expected === 'uint8'){
+ _response[key] = atobArray(_decodedResponse[key]);
+ _response.format = 'uint8';
+
+ } else if (this.expected === 'base64'){
+ _response[key] = _decodedResponse[key];
+ _response.format = 'base64';
+
+ } else { // no 'expected'
+ _response[key] = Utf8ArrayToStr(
+ atobArray(_decodedResponse[key]));
+ _response.format = 'string';
+ }
+ } else if (poa.payload[key] === 'string') {
+ _response[key] = _decodedResponse[key];
+ } else {
+ // fallthrough, should not be reached
+ // (payload is always string)
+ return gpgme_error('CONN_UNEXPECTED_ANSWER');
+ }
+ }
+ break;
+ } }
+ }
+ return _response;
+ }
+}
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
new file mode 100644
index 0000000..2f66c83
--- /dev/null
+++ b/lang/js/src/Errors.js
@@ -0,0 +1,177 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/**
+ * Listing of all possible error codes and messages of a {@link GPGME_Error}.
+ */
+export const err_list = {
+ // Connection
+ 'CONN_NO_CONNECT': {
+ msg:'Connection with the nativeMessaging host could not be'
+ + ' established.',
+ type: 'error'
+ },
+ 'CONN_EMPTY_GPG_ANSWER':{
+ msg: 'The nativeMessaging answer was empty.',
+ type: 'error'
+ },
+ 'CONN_TIMEOUT': {
+ msg: 'A connection timeout was exceeded.',
+ type: 'error'
+ },
+ 'CONN_UNEXPECTED_ANSWER': {
+ msg: 'The answer from gnupg was not as expected.',
+ type: 'error'
+ },
+ 'CONN_ALREADY_CONNECTED':{
+ msg: 'A connection was already established.',
+ type: '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'
+ },
+ 'KEY_NOKEY': {
+ msg:'This key does not exist in GPG',
+ type: 'error'
+ },
+ 'KEY_NO_INIT': {
+ msg:'This property has not been retrieved yet from GPG',
+ type: 'error'
+ },
+ 'KEY_ASYNC_ONLY': {
+ msg: 'This property cannot be used in synchronous calls',
+ type: 'error'
+ },
+ 'KEY_NO_DEFAULT': {
+ msg:'A default key could not be established. Please check yout gpg ' +
+ 'configuration',
+ type: 'error'
+ },
+ 'SIG_WRONG': {
+ msg:'A malformed signature was created',
+ type: 'error'
+ },
+ 'SIG_NO_SIGS': {
+ msg:'There were no signatures found',
+ type: 'error'
+ },
+ // generic
+ 'PARAM_WRONG':{
+ msg: 'Invalid parameter was found',
+ type: 'error'
+ },
+ 'DECODE_FAIL': {
+ msg: 'Decoding failed due to unexpected data',
+ 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 {@link GPGME_Error} error object
+ * with some information about meaning and origin
+ * @param {String} code Error code as defined in {@link err_list}.
+ * @param {String} info Possible additional error message to pass through.
+ * Currently used for errors sent as answer by gnupg via a native Message port
+ * @returns {GPGME_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'){
+ // eslint-disable-next-line no-console
+ // 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');
+ }
+}
+
+/**
+ * An error class with additional info about the origin of the error, as string
+ * It is created by {@link gpgme_error}, and its' codes are defined in
+ * {@link err_list}.
+ *
+ * @property {String} code Short description of origin and type of the error
+ * @property {String} msg Additional info
+ * @protected
+ * @class
+ * @extends Error
+ */
+class GPGME_Error extends Error{
+ constructor (code = 'GENERIC_ERROR', msg=''){
+
+ if (code === 'GNUPG_ERROR' && typeof (msg) === 'string'){
+ super(msg);
+ } else if (err_list.hasOwnProperty(code)){
+ if (msg){
+ super(err_list[code].msg + '--' + msg);
+ } else {
+ super(err_list[code].msg);
+ }
+ } else {
+ super(err_list['GENERIC_ERROR'].msg);
+ }
+ this._code = code;
+ }
+
+ 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 0000000..0b41852
--- /dev/null
+++ b/lang/js/src/Helpers.js
@@ -0,0 +1,219 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+import { gpgme_error } from './Errors';
+
+/**
+ * Helper function that tries to return an array of fingerprints, either from
+ * input fingerprints or from Key objects (openpgp Keys or GPGME_Keys are both
+ * accepted).
+ *
+ * @param {Object | Object[] | String | String[] } input
+ * @returns {String[]} Array of fingerprints, or an empty array
+ */
+export function toKeyIdArray (input){
+ if (!input){
+ 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 {
+ // MSG_NOT_A_FPR is just a console warning if warning enabled
+ // in src/Errors.js
+ gpgme_error('MSG_NOT_A_FPR');
+ }
+ } else if (typeof (input[i]) === 'object'){
+ let fpr = '';
+ if (input[i].fingerprint !== undefined){
+ 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){
+ return [];
+ } else {
+ return result;
+ }
+}
+
+/**
+ * Check if values are valid hexadecimal values of a specified length
+ * @param {String} key input value.
+ * @param {int} len the expected length of the value
+ * @returns {Boolean} true if value passes test
+ * @private
+ */
+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);
+}
+
+/**
+ * Checks if the input is a valid Fingerprint
+ * (Hex string with a length of 40 characters)
+ * @param {String} value to check
+ * @returns {Boolean} true if value passes test
+ */
+export function isFingerprint (value){
+ return hextest(value, 40);
+}
+
+/**
+ * check if the input is a valid gnupg long ID (Hex string with a length of 16
+ * characters)
+ * @param {String} value to check
+ * @returns {Boolean} true if value passes test
+ */
+export function isLongId (value){
+ return hextest(value, 16);
+}
+
+/**
+ * Recursively decodes input (utf8) to output (utf-16; javascript) strings.
+ * @param {Object | Array | String} property
+ * @private
+ */
+export function decode (property){
+ if (typeof property === 'string'){
+ try {
+ return decodeURIComponent(escape(unescape(property)));
+ }
+ catch (error){
+ if (error instanceof URIError) {
+ return property;
+ }
+ }
+ } else if (Array.isArray(property)){
+ let res = [];
+ for (let arr=0; arr < property.length; arr++){
+ res.push(decode(property[arr]));
+ }
+ return res;
+ } else if (typeof property === 'object'){
+ const keys = Object.keys(property);
+ if (keys.length){
+ let res = {};
+ for (let k=0; k < keys.length; k++ ){
+ res[keys[k]] = decode(property[keys[k]]);
+ }
+ return res;
+ }
+ return property;
+ }
+ return property;
+}
+
+/**
+ * Turns a base64 encoded string into an uint8 array
+ * adapted from https://gist.github.com/borismus/1032746
+ * @param {String} base64 encoded String
+ * @returns {Uint8Array}
+ * @private
+ */
+export function atobArray (base64) {
+ if (typeof (base64) !== 'string'){
+ throw gpgme_error('DECODE_FAIL');
+ }
+ const raw = window.atob(base64);
+ const rawLength = raw.length;
+ let array = new Uint8Array(new ArrayBuffer(rawLength));
+ for (let i = 0; i < rawLength; i++) {
+ array[i] = raw.charCodeAt(i);
+ }
+ return array;
+}
+
+/**
+ * Turns a Uint8Array into an utf8-String
+ * <pre>
+ * Taken and slightly adapted from
+ * http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
+ * (original header:
+ * utf.js - UTF-8 <=> UTF-16 convertion
+ *
+ * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
+ * Version: 1.0
+ * LastModified: Dec 25 1999
+ * This library is free. You can redistribute it and/or modify it.
+ * )
+ * </pre>
+ * @param {*} array Uint8Array
+ * @returns {String}
+ * @private
+ */
+export function Utf8ArrayToStr (array) {
+ let out, i, len, c, char2, char3;
+ out = '';
+ len = array.length;
+ i = 0;
+ if (array instanceof Uint8Array === false){
+ throw gpgme_error('DECODE_FAIL');
+ }
+ while (i < len) {
+ c = array[i++];
+ switch (c >> 4) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ // 0xxxxxxx
+ out += String.fromCharCode(c);
+ break;
+ case 12: case 13:
+ // 110x xxxx 10xx xxxx
+ char2 = array[i++];
+ out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
+ break;
+ case 14:
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ char2 = array[i++];
+ char3 = array[i++];
+ out += String.fromCharCode(((c & 0x0F) << 12) |
+ ((char2 & 0x3F) << 6) |
+ ((char3 & 0x3F) << 0));
+ break;
+ default:
+ break;
+ }
+ }
+ return out;
+}
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
new file mode 100644
index 0000000..7f0554c
--- /dev/null
+++ b/lang/js/src/Key.js
@@ -0,0 +1,711 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+
+import { isFingerprint, isLongId } from './Helpers';
+import { gpgme_error } from './Errors';
+import { createMessage } from './Message';
+
+/**
+ * Validates the given fingerprint and creates a new {@link GPGME_Key}
+ * @param {String} fingerprint
+ * @param {Boolean} async If True, Key properties (except fingerprint) will be
+ * queried from gnupg on each call, making the operation up-to-date, the
+ * answers will be Promises, and the performance will likely suffer
+ * @param {Object} data additional initial properties this Key will have. Needs
+ * a full object as delivered by gpgme-json
+ * @returns {Object} The verified and updated data
+ */
+export function createKey (fingerprint, async = false, data){
+ if (!isFingerprint(fingerprint) || typeof (async) !== 'boolean'){
+ throw gpgme_error('PARAM_WRONG');
+ }
+ if (data !== undefined){
+ data = validateKeyData(fingerprint, data);
+ }
+ if (data instanceof Error){
+ throw gpgme_error('KEY_INVALID');
+ } else {
+ return new GPGME_Key(fingerprint, async, data);
+ }
+}
+
+/**
+ * Represents the Keys as stored in the gnupg backend. A key is defined by a
+ * fingerprint.
+ * A key cannot be directly created via the new operator, please use
+ * {@link createKey} instead.
+ * A GPGME_Key object allows to query almost all information defined in gpgme
+ * Keys. It offers two modes, async: true/false. In async mode, Key properties
+ * with the exception of the fingerprint will be queried from gnupg on each
+ * call, making the operation up-to-date, the answers will be Promises, and
+ * the performance will likely suffer. In Sync modes, all information except
+ * for the armored Key export will be cached and can be refreshed by
+ * [refreshKey]{@link GPGME_Key#refreshKey}.
+ *
+ * <pre>
+ * see also:
+ * {@link GPGME_UserId} user Id objects
+ * {@link GPGME_Subkey} subKey objects
+ * </pre>
+ * For other Key properteis, refer to {@link validKeyProperties},
+ * and to the [gpgme documentation]{@link https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html}
+ * for meanings and further details.
+ *
+ * @class
+ */
+class GPGME_Key {
+
+ constructor (fingerprint, async, data){
+
+ /**
+ * @property {Boolean} _async If true, the Key was initialized without
+ * cached data
+ */
+ this._async = async;
+
+ this._data = { fingerprint: fingerprint.toUpperCase() };
+ if (data !== undefined
+ && data.fingerprint.toUpperCase() === this._data.fingerprint
+ ) {
+ this._data = data;
+ }
+ }
+
+ /**
+ * Query any property of the Key listed in {@link validKeyProperties}
+ * @param {String} property property to be retreived
+ * @returns {Boolean| String | Date | Array | Object}
+ * @returns {Promise<Boolean| String | Date | Array | Object>} (if in async
+ * mode)
+ * <pre>
+ * Returns the value of the property requested. If the Key is set to async,
+ * the value will be fetched from gnupg and resolved as a Promise. If Key
+ * is not async, the armored property is not available (it can still be
+ * retrieved asynchronously by [getArmor]{@link GPGME_Key#getArmor})
+ */
+ get (property) {
+ if (this._async === true) {
+ switch (property){
+ case 'armored':
+ return this.getArmor();
+ case 'hasSecret':
+ return this.getGnupgSecretState();
+ default:
+ return getGnupgState(this.fingerprint, property);
+ }
+ } else {
+ if (property === 'armored') {
+ throw gpgme_error('KEY_ASYNC_ONLY');
+ }
+ // eslint-disable-next-line no-use-before-define
+ if (!validKeyProperties.hasOwnProperty(property)){
+ throw gpgme_error('PARAM_WRONG');
+ } else {
+ return (this._data[property]);
+ }
+ }
+ }
+
+ /**
+ * Reloads the Key information from gnupg. This is only useful if the Key
+ * use the GPGME_Keys cached. Note that this is a performance hungry
+ * operation. If you desire more than a few refreshs, it may be
+ * advisable to run [Keyring.getKeys]{@link Keyring#getKeys} instead.
+ * @returns {Promise<GPGME_Key>}
+ * @async
+ */
+ refreshKey () {
+ let me = this;
+ return new Promise(function (resolve, reject) {
+ if (!me._data.fingerprint){
+ reject(gpgme_error('KEY_INVALID'));
+ }
+ let msg = createMessage('keylist');
+ msg.setParameter('sigs', true);
+ msg.setParameter('keys', me._data.fingerprint);
+ msg.post().then(function (result){
+ if (result.keys.length === 1){
+ const newdata = validateKeyData(
+ me._data.fingerprint, result.keys[0]);
+ if (newdata instanceof Error){
+ reject(gpgme_error('KEY_INVALID'));
+ } else {
+ me._data = newdata;
+ me.getGnupgSecretState().then(function (){
+ me.getArmor().then(function (){
+ resolve(me);
+ }, function (error){
+ reject(error);
+ });
+ }, function (error){
+ reject(error);
+ });
+ }
+ } else {
+ reject(gpgme_error('KEY_NOKEY'));
+ }
+ }, function (error) {
+ reject(gpgme_error('GNUPG_ERROR'), error);
+ });
+ });
+ }
+
+ /**
+ * Query the armored block of the Key directly from gnupg. Please note
+ * that this will not get you any export of the secret/private parts of
+ * a Key
+ * @returns {Promise<String>}
+ * @async
+ */
+ getArmor () {
+ const me = this;
+ return new Promise(function (resolve, reject) {
+ if (!me._data.fingerprint){
+ reject(gpgme_error('KEY_INVALID'));
+ }
+ let msg = createMessage('export');
+ msg.setParameter('armor', true);
+ msg.setParameter('keys', me._data.fingerprint);
+ msg.post().then(function (result){
+ resolve(result.data);
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * Find out if the Key is part of a Key pair including public and
+ * private key(s). If you want this information about more than a few
+ * Keys in synchronous mode, it may be advisable to run
+ * [Keyring.getKeys]{@link Keyring#getKeys} instead, as it performs faster
+ * in bulk querying.
+ * @returns {Promise<Boolean>} True if a private Key is available in the
+ * gnupg Keyring.
+ * @async
+ */
+ getGnupgSecretState (){
+ const me = this;
+ return new Promise(function (resolve, reject) {
+ if (!me._data.fingerprint){
+ reject(gpgme_error('KEY_INVALID'));
+ } else {
+ let msg = createMessage('keylist');
+ msg.setParameter('keys', me._data.fingerprint);
+ msg.setParameter('secret', true);
+ msg.post().then(function (result){
+ me._data.hasSecret = null;
+ if (
+ result.keys &&
+ result.keys.length === 1 &&
+ result.keys[0].secret === true
+ ) {
+ me._data.hasSecret = true;
+ resolve(true);
+ } else {
+ me._data.hasSecret = false;
+ resolve(false);
+ }
+ }, function (error){
+ reject(error);
+ });
+ }
+ });
+ }
+
+ /**
+ * Deletes the (public) Key from the GPG Keyring. Note that a deletion
+ * of a secret key is not supported by the native backend, and gnupg will
+ * refuse to delete a Key if there is still a secret/private Key present
+ * to that public Key
+ * @returns {Promise<Boolean>} Success if key was deleted.
+ */
+ delete (){
+ const me = this;
+ return new Promise(function (resolve, reject){
+ if (!me._data.fingerprint){
+ reject(gpgme_error('KEY_INVALID'));
+ }
+ let msg = createMessage('delete');
+ msg.setParameter('key', me._data.fingerprint);
+ msg.post().then(function (result){
+ resolve(result.success);
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * @returns {String} The fingerprint defining this Key. Convenience getter
+ */
+ get fingerprint (){
+ return this._data.fingerprint;
+ }
+}
+
+/**
+ * Representing a subkey of a Key. See {@link validSubKeyProperties} for
+ * possible properties.
+ * @class
+ * @protected
+ */
+class GPGME_Subkey {
+
+ /**
+ * Initializes with the json data sent by gpgme-json
+ * @param {Object} data
+ * @private
+ */
+ constructor (data){
+ this._data = {};
+ let keys = Object.keys(data);
+ const me = this;
+
+ /**
+ * Validates a subkey property against {@link validSubKeyProperties} and
+ * sets it if validation is successful
+ * @param {String} property
+ * @param {*} value
+ * @param private
+ */
+ const setProperty = function (property, value){
+ // eslint-disable-next-line no-use-before-define
+ if (validSubKeyProperties.hasOwnProperty(property)){
+ // eslint-disable-next-line no-use-before-define
+ if (validSubKeyProperties[property](value) === true) {
+ if (property === 'timestamp' || property === 'expires'){
+ me._data[property] = new Date(value * 1000);
+ } else {
+ me._data[property] = value;
+ }
+ }
+ }
+ };
+ for (let i=0; i< keys.length; i++) {
+ setProperty(keys[i], data[keys[i]]);
+ }
+ }
+
+ /**
+ * Fetches any information about this subkey
+ * @param {String} property Information to request
+ * @returns {String | Number | Date}
+ */
+ get (property) {
+ if (this._data.hasOwnProperty(property)){
+ return (this._data[property]);
+ }
+ }
+
+}
+
+/**
+ * Representing user attributes associated with a Key or subkey. See
+ * {@link validUserIdProperties} for possible properties.
+ * @class
+ * @protected
+ */
+class GPGME_UserId {
+
+ /**
+ * Initializes with the json data sent by gpgme-json
+ * @param {Object} data
+ * @private
+ */
+ constructor (data){
+ this._data = {};
+ const me = this;
+ let keys = Object.keys(data);
+ const setProperty = function (property, value){
+ // eslint-disable-next-line no-use-before-define
+ if (validUserIdProperties.hasOwnProperty(property)){
+ // eslint-disable-next-line no-use-before-define
+ if (validUserIdProperties[property](value) === true) {
+ if (property === 'last_update'){
+ me._data[property] = new Date(value*1000);
+ } else {
+ me._data[property] = value;
+ }
+ }
+ }
+ };
+ for (let i=0; i< keys.length; i++) {
+ setProperty(keys[i], data[keys[i]]);
+ }
+ }
+
+ /**
+ * Fetches information about the user
+ * @param {String} property Information to request
+ * @returns {String | Number}
+ */
+ get (property) {
+ if (this._data.hasOwnProperty(property)){
+ return (this._data[property]);
+ }
+ }
+
+}
+
+/**
+ * Validation definition for userIds. Each valid userId property is represented
+ * as a key- Value pair, with their value being a validation function to check
+ * against
+ * @protected
+ * @const
+ */
+const validUserIdProperties = {
+ 'revoked': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'invalid': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'uid': function (value){
+ if (typeof (value) === 'string' || value === ''){
+ return true;
+ }
+ return false;
+ },
+ 'validity': function (value){
+ if (typeof (value) === 'string'){
+ return true;
+ }
+ return false;
+ },
+ 'name': function (value){
+ if (typeof (value) === 'string' || value === ''){
+ return true;
+ }
+ return false;
+ },
+ 'email': function (value){
+ if (typeof (value) === 'string' || value === ''){
+ return true;
+ }
+ return false;
+ },
+ 'address': function (value){
+ if (typeof (value) === 'string' || value === ''){
+ return true;
+ }
+ return false;
+ },
+ 'comment': function (value){
+ if (typeof (value) === 'string' || value === ''){
+ return true;
+ }
+ return false;
+ },
+ 'origin': function (value){
+ return Number.isInteger(value);
+ },
+ 'last_update': function (value){
+ return Number.isInteger(value);
+ }
+};
+
+/**
+ * Validation definition for subKeys. Each valid userId property is represented
+ * as a key-value pair, with the value being a validation function
+ * @protected
+ * @const
+ */
+const validSubKeyProperties = {
+ 'invalid': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_encrypt': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_sign': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_certify': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_authenticate': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'secret': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'is_qualified': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'is_cardkey': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'is_de_vs': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'pubkey_algo_name': function (value){
+ return typeof (value) === 'string';
+ // TODO: check against list of known?['']
+ },
+ 'pubkey_algo_string': function (value){
+ return typeof (value) === 'string';
+ // TODO: check against list of known?['']
+ },
+ 'keyid': function (value){
+ return isLongId(value);
+ },
+ 'pubkey_algo': function (value) {
+ return (Number.isInteger(value) && value >= 0);
+ },
+ 'length': function (value){
+ return (Number.isInteger(value) && value > 0);
+ },
+ 'timestamp': function (value){
+ return (Number.isInteger(value) && value > 0);
+ },
+ 'expires': function (value){
+ return (Number.isInteger(value) && value > 0);
+ }
+};
+
+/**
+ * Validation definition for Keys. Each valid Key property is represented
+ * as a key-value pair, with their value being a validation function. For
+ * details on the meanings, please refer to the gpgme documentation
+ * https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html#Key-objects
+ * @param {String} fingerprint
+ * @param {Boolean} revoked
+ * @param {Boolean} expired
+ * @param {Boolean} disabled
+ * @param {Boolean} invalid
+ * @param {Boolean} can_encrypt
+ * @param {Boolean} can_sign
+ * @param {Boolean} can_certify
+ * @param {Boolean} can_authenticate
+ * @param {Boolean} secret
+ * @param {Boolean}is_qualified
+ * @param {String} protocol
+ * @param {String} issuer_serial
+ * @param {String} issuer_name
+ * @param {Boolean} chain_id
+ * @param {String} owner_trust
+ * @param {Date} last_update
+ * @param {String} origin
+ * @param {Array<GPGME_Subkey>} subkeys
+ * @param {Array<GPGME_UserId>} userids
+ * @param {Array<String>} tofu
+ * @param {Boolean} hasSecret
+ * @protected
+ * @const
+ */
+const validKeyProperties = {
+ 'fingerprint': function (value){
+ return isFingerprint(value);
+ },
+ 'revoked': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'expired': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'disabled': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'invalid': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_encrypt': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_sign': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_certify': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'can_authenticate': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'secret': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'is_qualified': function (value){
+ return typeof (value) === 'boolean';
+ },
+ 'protocol': function (value){
+ return typeof (value) === 'string';
+ // TODO check for implemented ones
+ },
+ 'issuer_serial': function (value){
+ return typeof (value) === 'string';
+ },
+ 'issuer_name': function (value){
+ return typeof (value) === 'string';
+ },
+ 'chain_id': function (value){
+ return typeof (value) === 'string';
+ },
+ 'owner_trust': function (value){
+ return typeof (value) === 'string';
+ },
+ 'last_update': function (value){
+ return (Number.isInteger(value));
+ // TODO undefined/null possible?
+ },
+ 'origin': function (value){
+ return (Number.isInteger(value));
+ },
+ 'subkeys': function (value){
+ return (Array.isArray(value));
+ },
+ 'userids': function (value){
+ return (Array.isArray(value));
+ },
+ 'tofu': function (value){
+ return (Array.isArray(value));
+ },
+ 'hasSecret': function (value){
+ return typeof (value) === 'boolean';
+ }
+
+};
+
+/**
+* sets the Key data in bulk. It can only be used from inside a Key, either
+* during construction or on a refresh callback.
+* @param {Object} key the original internal key data.
+* @param {Object} data Bulk set the data for this key, with an Object structure
+* as sent by gpgme-json.
+* @returns {Object|GPGME_Error} the changed data after values have been set,
+* an error if something went wrong.
+* @private
+*/
+function validateKeyData (fingerprint, data){
+ const key = {};
+ if (!fingerprint || typeof (data) !== 'object' || !data.fingerprint
+ || fingerprint !== data.fingerprint.toUpperCase()
+ ){
+ return gpgme_error('KEY_INVALID');
+ }
+ let props = Object.keys(data);
+ for (let i=0; i< props.length; i++){
+ if (!validKeyProperties.hasOwnProperty(props[i])){
+ return gpgme_error('KEY_INVALID');
+ }
+ // running the defined validation function
+ if (validKeyProperties[props[i]](data[props[i]]) !== true ){
+ return gpgme_error('KEY_INVALID');
+ }
+ switch (props[i]){
+ case 'subkeys':
+ key.subkeys = [];
+ for (let i=0; i< data.subkeys.length; i++) {
+ key.subkeys.push(
+ new GPGME_Subkey(data.subkeys[i]));
+ }
+ break;
+ case 'userids':
+ key.userids = [];
+ for (let i=0; i< data.userids.length; i++) {
+ key.userids.push(
+ new GPGME_UserId(data.userids[i]));
+ }
+ break;
+ case 'last_update':
+ key[props[i]] = new Date( data[props[i]] * 1000 );
+ break;
+ default:
+ key[props[i]] = data[props[i]];
+ }
+ }
+ return key;
+}
+
+/**
+ * Fetches and sets properties from gnupg
+ * @param {String} fingerprint
+ * @param {String} property to search for.
+ * @private
+ * @async
+ */
+function getGnupgState (fingerprint, property){
+ return new Promise(function (resolve, reject) {
+ if (!isFingerprint(fingerprint)) {
+ reject(gpgme_error('KEY_INVALID'));
+ } else {
+ let msg = createMessage('keylist');
+ msg.setParameter('keys', fingerprint);
+ msg.post().then(function (res){
+ if (!res.keys || res.keys.length !== 1){
+ reject(gpgme_error('KEY_INVALID'));
+ } else {
+ const key = res.keys[0];
+ let result;
+ switch (property){
+ case 'subkeys':
+ result = [];
+ if (key.subkeys.length){
+ for (let i=0; i < key.subkeys.length; i++) {
+ result.push(
+ new GPGME_Subkey(key.subkeys[i]));
+ }
+ }
+ resolve(result);
+ break;
+ case 'userids':
+ result = [];
+ if (key.userids.length){
+ for (let i=0; i< key.userids.length; i++) {
+ result.push(
+ new GPGME_UserId(key.userids[i]));
+ }
+ }
+ resolve(result);
+ break;
+ case 'last_update':
+ if (key.last_update === undefined){
+ reject(gpgme_error('CONN_UNEXPECTED_ANSWER'));
+ } else if (key.last_update !== null){
+ resolve(new Date( key.last_update * 1000));
+ } else {
+ resolve(null);
+ }
+ break;
+ default:
+ if (!validKeyProperties.hasOwnProperty(property)){
+ reject(gpgme_error('PARAM_WRONG'));
+ } else {
+ if (key.hasOwnProperty(property)){
+ resolve(key[property]);
+ } else {
+ reject(gpgme_error(
+ 'CONN_UNEXPECTED_ANSWER'));
+ }
+ }
+ break;
+ }
+ }
+ }, function (error){
+ reject(gpgme_error(error));
+ });
+ }
+ });
+} \ No newline at end of file
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
new file mode 100644
index 0000000..2071c6d
--- /dev/null
+++ b/lang/js/src/Keyring.js
@@ -0,0 +1,439 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+
+import { createMessage } from './Message';
+import { createKey } from './Key';
+import { isFingerprint } from './Helpers';
+import { gpgme_error } from './Errors';
+
+/**
+ * This class offers access to the gnupg keyring
+ */
+export class GPGME_Keyring {
+
+ /**
+ * Queries Keys (all Keys or a subset) from gnupg.
+ *
+ * @param {Object} options:
+ * @param {String | Array<String>} options.pattern (optional) A pattern to
+ * search for in userIds or KeyIds.
+ * @param {Boolean} options.prepare_sync (optional) if set to true, most
+ * data (with the exception of armored Key blocks) will be cached for the
+ * Keys. This enables direct, synchronous use of these properties for
+ * all keys. It does not check for changes on the backend. The cached
+ * information can be updated with the {@link Key.refresh} method.
+ * @param {Boolean} options.search (optional) retrieve Keys from external
+ * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup)
+ * @returns {Promise<GPGME_Key[]>}
+ * @static
+ * @async
+ */
+ getKeys ({ pattern, prepare_sync = false, search = false } = {}){
+ if (typeof arguments[0] !== 'object') {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ if (arguments.length && typeof arguments[0] !== 'object') {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ return new Promise(function (resolve, reject) {
+ let msg = createMessage('keylist');
+ if (pattern) {
+ msg.setParameter('keys', pattern);
+ }
+ msg.setParameter('sigs', true);
+ if (search === true){
+ msg.setParameter('locate', true);
+ }
+ msg.post().then(function (result){
+ let resultset = [];
+ if (result.keys.length === 0){
+ resolve([]);
+ } else {
+ let secondrequest;
+ if (prepare_sync === true) {
+ secondrequest = function () {
+ let msg2 = createMessage('keylist');
+ if (pattern){
+ msg2.setParameter('keys', pattern);
+ }
+ msg2.setParameter('secret', true);
+ return msg2.post();
+ };
+ } else {
+ secondrequest = function () {
+ return Promise.resolve(true);
+ };
+ }
+ secondrequest().then(function (answer) {
+ for (let i=0; i < result.keys.length; i++){
+ if (prepare_sync === true){
+ if (answer && answer.keys) {
+ for (let j=0;
+ j < answer.keys.length; j++ ){
+ const a = answer.keys[j];
+ const b = result.keys[i];
+ if (
+ a.fingerprint === b.fingerprint
+ ) {
+ if (a.secret === true){
+ b.hasSecret = true;
+ } else {
+ b.hasSecret = false;
+ }
+ break;
+ }
+ }
+ }
+ }
+ let k = createKey(result.keys[i].fingerprint,
+ !prepare_sync, result.keys[i]);
+ resultset.push(k);
+ }
+ resolve(resultset);
+ }, function (error){
+ reject(error);
+ });
+ }
+ });
+ });
+ }
+
+ /**
+ * @typedef {Object} exportResult The result of a getKeysArmored
+ * operation.
+ * @property {String} armored The public Key(s) as armored block. Note
+ * that the result is one armored block, and not a block per key.
+ * @property {Array<String>} secret_fprs (optional) list of
+ * fingerprints for those Keys that also have a secret Key available in
+ * gnupg. The secret key will not be exported, but the fingerprint can
+ * be used in operations needing a secret key.
+ */
+
+ /**
+ * Fetches the armored public Key blocks for all Keys matching the
+ * pattern (if no pattern is given, fetches all keys known to gnupg).
+ * @param {Object} options (optional)
+ * @param {String|Array<String>} options.pattern The Pattern to
+ * search for
+ * @param {Boolean} options.with_secret_fpr also return a list of
+ * fingerprints for the keys that have a secret key available
+ * @returns {Promise<exportResult>} Object containing the
+ * armored Key(s) and additional information.
+ * @static
+ * @async
+ */
+ getKeysArmored ({ pattern, with_secret_fpr }) {
+ return new Promise(function (resolve, reject) {
+ let msg = createMessage('export');
+ msg.setParameter('armor', true);
+ if (with_secret_fpr === true) {
+ msg.setParameter('with-sec-fprs', true);
+ }
+ if (pattern){
+ msg.setParameter('keys', pattern);
+ }
+ msg.post().then(function (answer){
+ const result = { armored: answer.data };
+ if (with_secret_fpr === true){
+ if (answer.hasOwnProperty('sec-fprs')){
+ result.secret_fprs = answer['sec-fprs'];
+ } else {
+ result.secret_fprs = [];
+ }
+ }
+ resolve(result);
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * Returns the Key used by default in gnupg.
+ * (a.k.a. 'primary Key or 'main key').
+ * It looks up the gpg configuration if set, or the first key that
+ * contains a secret key.
+ *
+ * @returns {Promise<GPGME_Key>}
+ * @async
+ * @static
+ */
+ getDefaultKey (prepare_sync = false) {
+ let me = this;
+ return new Promise(function (resolve, reject){
+ let msg = createMessage('config_opt');
+ msg.setParameter('component', 'gpg');
+ msg.setParameter('option', 'default-key');
+ msg.post().then(function (resp){
+ if (resp.option !== undefined
+ && resp.option.hasOwnProperty('value')
+ && resp.option.value.length === 1
+ && resp.option.value[0].hasOwnProperty('string')
+ && typeof (resp.option.value[0].string) === 'string'){
+ me.getKeys({ pattern: resp.option.value[0].string,
+ prepare_sync: true }).then(
+ function (keys){
+ if (keys.length === 1){
+ resolve(keys[0]);
+ } else {
+ reject(gpgme_error('KEY_NO_DEFAULT'));
+ }
+ }, function (error){
+ reject(error);
+ });
+ } else {
+ let msg = createMessage('keylist');
+ msg.setParameter('secret', true);
+ msg.post().then(function (result){
+ if (result.keys.length === 0){
+ reject(gpgme_error('KEY_NO_DEFAULT'));
+ } else {
+ for (let i=0; i< result.keys.length; i++ ) {
+ if (
+ result.keys[i].invalid === false &&
+ result.keys[i].expired === false &&
+ result.keys[i].revoked === false &&
+ result.keys[i].can_sign === true
+ ) {
+ let k = createKey(
+ result.keys[i].fingerprint,
+ !prepare_sync,
+ result.keys[i]);
+ resolve(k);
+ break;
+ } else if (i === result.keys.length - 1){
+ reject(gpgme_error('KEY_NO_DEFAULT'));
+ }
+ }
+ }
+ }, function (error){
+ reject(error);
+ });
+ }
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * @typedef {Object} importResult The result of a Key update
+ * @property {Object} summary Numerical summary of the result. See the
+ * feedbackValues variable for available Keys values and the gnupg
+ * documentation.
+ * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html
+ * for details on their meaning.
+ * @property {Array<importedKeyResult>} Keys Array of Object containing
+ * GPGME_Keys with additional import information
+ *
+ */
+
+ /**
+ * @typedef {Object} importedKeyResult
+ * @property {GPGME_Key} key The resulting key
+ * @property {String} status:
+ * 'nochange' if the Key was not changed,
+ * 'newkey' if the Key was imported in gpg, and did not exist
+ * previously,
+ * 'change' if the key existed, but details were updated. For details,
+ * Key.changes is available.
+ * @property {Boolean} changes.userId Changes in userIds
+ * @property {Boolean} changes.signature Changes in signatures
+ * @property {Boolean} changes.subkey Changes in subkeys
+ */
+
+ /**
+ * Import an armored Key block into gnupg. Note that this currently
+ * will not succeed on private Key blocks.
+ * @param {String} armored Armored Key block of the Key(s) to be
+ * imported into gnupg
+ * @param {Boolean} prepare_sync prepare the keys for synched use
+ * (see {@link getKeys}).
+ * @returns {Promise<importResult>} A summary and Keys considered.
+ * @async
+ * @static
+ */
+ importKey (armored, prepare_sync) {
+ let feedbackValues = ['considered', 'no_user_id', 'imported',
+ 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys',
+ 'new_signatures', 'new_revocations', 'secret_read',
+ 'secret_imported', 'secret_unchanged', 'skipped_new_keys',
+ 'not_imported', 'skipped_v3_keys'];
+ if (!armored || typeof (armored) !== 'string'){
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ let me = this;
+ return new Promise(function (resolve, reject){
+ let msg = createMessage('import');
+ msg.setParameter('data', armored);
+ msg.post().then(function (response){
+ let infos = {};
+ let fprs = [];
+ let summary = {};
+ for (let i=0; i < feedbackValues.length; i++ ){
+ summary[feedbackValues[i]] =
+ response.result[feedbackValues[i]];
+ }
+ if (!response.result.hasOwnProperty('imports') ||
+ response.result.imports.length === 0
+ ){
+ resolve({ Keys:[],summary: summary });
+ return;
+ }
+ for (let res=0; res<response.result.imports.length; res++){
+ let result = response.result.imports[res];
+ let status = '';
+ if (result.status === 0){
+ status = 'nochange';
+ } else if ((result.status & 1) === 1){
+ status = 'newkey';
+ } else {
+ status = 'change';
+ }
+ let changes = {};
+ changes.userId = (result.status & 2) === 2;
+ changes.signature = (result.status & 4) === 4;
+ changes.subkey = (result.status & 8) === 8;
+ // 16 new secret key: not implemented
+ fprs.push(result.fingerprint);
+ infos[result.fingerprint] = {
+ changes: changes,
+ status: status
+ };
+ }
+ let resultset = [];
+ if (prepare_sync === true){
+ me.getKeys({ pattern: fprs, prepare_sync: true })
+ .then(function (result){
+ for (let i=0; i < result.length; i++) {
+ resultset.push({
+ key: result[i],
+ changes:
+ infos[result[i].fingerprint].changes,
+ status: infos[result[i].fingerprint].status
+ });
+ }
+ resolve({ Keys:resultset,summary: summary });
+ }, function (error){
+ reject(error);
+ });
+ } else {
+ for (let i=0; i < fprs.length; i++) {
+ resultset.push({
+ key: createKey(fprs[i]),
+ changes: infos[fprs[i]].changes,
+ status: infos[fprs[i]].status
+ });
+ }
+ resolve({ Keys:resultset,summary:summary });
+ }
+
+ }, function (error){
+ reject(error);
+ });
+
+
+ });
+
+
+ }
+
+ /**
+ * Convenience function for deleting a Key. See {@link Key#delete} for
+ * further information about the return values.
+ * @param {String} fingerprint
+ * @returns {Promise<Boolean>}
+ * @async
+ * @static
+ */
+ deleteKey (fingerprint){
+ if (isFingerprint(fingerprint) === true) {
+ let key = createKey(fingerprint);
+ return key.delete();
+ } else {
+ return Promise.reject(gpgme_error('KEY_INVALID'));
+ }
+ }
+
+ /**
+ * Generates a new Key pair directly in gpg, and returns a GPGME_Key
+ * representing that Key. Please note that due to security concerns,
+ * secret Keys can not be deleted or exported from inside gpgme.js.
+ * @param {Object} options
+ * @param {String} option.userId The user Id, e.g. 'Foo Bar <foo@bar.baz>'
+ * @param {String} option.algo (optional) algorithm (and optionally key
+ * size) to be used. See {@link supportedKeyAlgos} below for supported
+ * values. If ommitted, 'default' is used.
+ * @param {Number} option.expires (optional) Expiration time in seconds
+ * from now. If not set or set to 0, expiration will be 'never'
+ *
+ * @return {Promise<Key|GPGME_Error>}
+ * @async
+ */
+ generateKey ({ userId, algo = 'default', expires= 0 } = {}){
+ if (typeof userId !== 'string'
+ // eslint-disable-next-line no-use-before-define
+ || (algo && supportedKeyAlgos.indexOf(algo) < 0 )
+ || (!Number.isInteger(expires) || expires < 0 )
+ ){
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ // eslint-disable-next-line no-use-before-define
+ let me = this;
+ return new Promise(function (resolve, reject){
+ let msg = createMessage('createkey');
+ msg.setParameter('userid', userId);
+ msg.setParameter('algo', algo);
+ msg.setParameter('expires', expires);
+ msg.post().then(function (response){
+ me.getKeys({
+ pattern: response.fingerprint,
+ prepare_sync: true
+ }).then(function (result){
+ resolve(result);
+ }, function (error){
+ reject(error);
+ });
+ }, function (error) {
+ reject(error);
+ });
+ });
+ }
+}
+
+
+/**
+ * List of algorithms supported for key generation. Please refer to the gnupg
+ * documentation for details
+ */
+const supportedKeyAlgos = [
+ 'default', 'future-default',
+ 'rsa', 'rsa2048', 'rsa3072', 'rsa4096',
+ 'dsa', 'dsa2048', 'dsa3072', 'dsa4096',
+ 'elg', 'elg2048', 'elg3072', 'elg4096',
+ 'ed25519',
+ 'cv25519',
+ 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1',
+ 'NIST P-256', 'NIST P-384', 'NIST P-521'
+];
diff --git a/lang/js/src/Makefile.am b/lang/js/src/Makefile.am
new file mode 100644
index 0000000..dc58fd3
--- /dev/null
+++ b/lang/js/src/Makefile.am
@@ -0,0 +1,30 @@
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Intevation GmbH
+#
+# This file is part of GPGME.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+
+EXTRA_DIST = Connection.js \
+ Errors.js \
+ gpgmejs.js \
+ Helpers.js \
+ index.js \
+ Key.js \
+ Keyring.js \
+ Message.js \
+ permittedOperations.js \
+ Signature.js
diff --git a/lang/js/src/Makefile.in b/lang/js/src/Makefile.in
new file mode 100644
index 0000000..fe5e274
--- /dev/null
+++ b/lang/js/src/Makefile.in
@@ -0,0 +1,540 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am for gpgme.js.
+# Copyright (C) 2018 Intevation GmbH
+#
+# This file is part of GPGME.
+#
+# gpgme.js is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# gpgme.js 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 General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = lang/js/src
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/mkinstalldirs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_pkg_swig.m4 \
+ $(top_srcdir)/m4/ax_python_devel.m4 \
+ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \
+ $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/python.m4 $(top_srcdir)/m4/qt.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLED_LANGUAGES = @ENABLED_LANGUAGES@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@
+GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@
+GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@
+GPGME_QT_LIBS = @GPGME_QT_LIBS@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GRAPHVIZ = @GRAPHVIZ@
+GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGMEPP_LT_AGE = @LIBGPGMEPP_LT_AGE@
+LIBGPGMEPP_LT_CURRENT = @LIBGPGMEPP_LT_CURRENT@
+LIBGPGMEPP_LT_REVISION = @LIBGPGMEPP_LT_REVISION@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBQGPGME_LT_AGE = @LIBQGPGME_LT_AGE@
+LIBQGPGME_LT_CURRENT = @LIBQGPGME_LT_CURRENT@
+LIBQGPGME_LT_REVISION = @LIBQGPGME_LT_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MOC = @MOC@
+MOC2 = @MOC2@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PYTHON = @PYTHON@
+PYTHONS = @PYTHONS@
+PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LDFLAGS = @PYTHON_LDFLAGS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SITE_PKG = @PYTHON_SITE_PKG@
+PYTHON_VERSION = @PYTHON_VERSION@
+QTCHOOSER = @QTCHOOSER@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SWIG = @SWIG@
+SWIG_LIB = @SWIG_LIB@
+SYSROOT = @SYSROOT@
+VERSION = @VERSION@
+VERSION_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+VERSION_NUMBER = @VERSION_NUMBER@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = Connection.js \
+ Errors.js \
+ gpgmejs.js \
+ Helpers.js \
+ index.js \
+ Key.js \
+ Keyring.js \
+ Message.js \
+ permittedOperations.js \
+ Signature.js
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lang/js/src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/js/src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
new file mode 100644
index 0000000..9f6abb7
--- /dev/null
+++ b/lang/js/src/Message.js
@@ -0,0 +1,243 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+import { permittedOperations } from './permittedOperations';
+import { gpgme_error } from './Errors';
+import { Connection } from './Connection';
+
+/**
+ * Initializes a message for gnupg, validating the message's purpose with
+ * {@link permittedOperations} first
+ * @param {String} operation
+ * @returns {GPGME_Message} The Message object
+ */
+export function createMessage (operation){
+ if (typeof (operation) !== 'string'){
+ throw gpgme_error('PARAM_WRONG');
+ }
+ if (permittedOperations.hasOwnProperty(operation)){
+ return new GPGME_Message(operation);
+ } else {
+ throw gpgme_error('MSG_WRONG_OP');
+ }
+}
+
+/**
+ * A Message collects, validates and handles all information required to
+ * successfully establish a meaningful communication with gpgme-json via
+ * [Connection.post]{@link Connection#post}. The definition on which
+ * communication is available can be found in {@link permittedOperations}.
+ * @class
+ * @protected
+ */
+export class GPGME_Message {
+
+ constructor (operation){
+ this._msg = {
+ op: operation,
+ chunksize: 1023* 1024
+ };
+ this._expected = null;
+ }
+
+ get operation (){
+ return this._msg.op;
+ }
+
+ set expected (value){
+ if (value === 'uint8' || value === 'base64'){
+ this._expected = value;
+ }
+ }
+
+ get expected () {
+ return this._expected;
+ }
+ /**
+ * The maximum size of responses from gpgme in bytes. As of September 2018,
+ * most browsers will only accept answers up to 1 MB of size.
+ * Everything above that threshold will not pass through
+ * nativeMessaging; answers that are larger need to be sent in parts.
+ * The lower limit is set to 10 KB. Messages smaller than the threshold
+ * will not encounter problems, larger messages will be received in
+ * chunks. If the value is not explicitly specified, 1023 KB is used.
+ */
+ set chunksize (value){
+ if (
+ Number.isInteger(value) &&
+ value > 10 * 1024 &&
+ value <= 1024 * 1024
+ ){
+ this._msg.chunksize = value;
+ }
+ }
+
+ get chunksize (){
+ return this._msg.chunksize;
+ }
+
+ /**
+ * Returns the prepared message after their parameters and the completion
+ * of required parameters have been 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;
+ }
+ }
+
+ /**
+ * Sets a parameter for the message. It validates with
+ * {@link permittedOperations}
+ * @param {String} param Parameter to set
+ * @param {any} value Value to set
+ * @returns {Boolean} True if the parameter was set successfully.
+ * Throws errors if the parameters don't match the message operation
+ */
+ setParameter ( param,value ){
+ if (!param || typeof (param) !== 'string'){
+ throw gpgme_error('PARAM_WRONG');
+ }
+ let po = permittedOperations[this._msg.op];
+ if (!po){
+ throw 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 {
+ throw gpgme_error('PARAM_WRONG');
+ }
+ // check incoming value for correctness
+ let checktype = function (val){
+ switch (typeof (val)){
+ case 'string':
+ if (poparam.allowed.indexOf(typeof (val)) >= 0
+ && val.length > 0) {
+ return true;
+ }
+ throw gpgme_error('PARAM_WRONG');
+ case 'number':
+ if (
+ poparam.allowed.indexOf('number') >= 0
+ && isNaN(value) === false){
+ return true;
+ }
+ throw gpgme_error('PARAM_WRONG');
+
+ case 'boolean':
+ if (poparam.allowed.indexOf('boolean') >= 0){
+ return true;
+ }
+ throw gpgme_error('PARAM_WRONG');
+ case 'object':
+ if (Array.isArray(val)){
+ if (poparam.array_allowed !== true){
+ throw 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;
+ }
+ throw gpgme_error('PARAM_WRONG');
+ } else {
+ throw gpgme_error('PARAM_WRONG');
+ }
+ break;
+ default:
+ throw 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, that is
+ * all 'required' parameters according to {@link permittedOperations}.
+ * @returns {Boolean} true if message is complete.
+ */
+ 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){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Sends the Message via nativeMessaging and resolves with the answer.
+ * @returns {Promise<Object>} GPGME response
+ * @async
+ */
+ post (){
+ let me = this;
+ return new Promise(function (resolve, reject) {
+ if (me.isComplete() === true) {
+
+ let conn = new Connection;
+ conn.post(me).then(function (response) {
+ resolve(response);
+ }, function (reason) {
+ reject(reason);
+ });
+ }
+ else {
+ reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ });
+ }
+
+}
diff --git a/lang/js/src/Signature.js b/lang/js/src/Signature.js
new file mode 100644
index 0000000..f848e32
--- /dev/null
+++ b/lang/js/src/Signature.js
@@ -0,0 +1,206 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+import { gpgme_error } from './Errors';
+
+/**
+ * Validates an object containing a signature, as sent by the nativeMessaging
+ * interface
+ * @param {Object} sigObject Object as returned by gpgme-json. The definition
+ * of the expected values are to be found in {@link expKeys}, {@link expSum},
+ * {@link expNote}.
+ * @returns {GPGME_Signature|GPGME_Error} Signature Object
+ * @private
+ */
+export function createSignature (sigObject){
+ if (
+ typeof (sigObject) !=='object' ||
+ !sigObject.hasOwnProperty('summary') ||
+ !sigObject.hasOwnProperty('fingerprint') ||
+ !sigObject.hasOwnProperty('timestamp')
+ // TODO check if timestamp is mandatory in specification
+ ){
+ return gpgme_error('SIG_WRONG');
+ }
+ let keys = Object.keys(sigObject);
+ for (let i=0; i< keys.length; i++){
+ // eslint-disable-next-line no-use-before-define
+ if ( typeof (sigObject[keys[i]]) !== expKeys[keys[i]] ){
+ return gpgme_error('SIG_WRONG');
+ }
+ }
+ let sumkeys = Object.keys(sigObject.summary);
+ for (let i=0; i< sumkeys.length; i++){
+ // eslint-disable-next-line no-use-before-define
+ if ( typeof (sigObject.summary[sumkeys[i]]) !== expSum[sumkeys[i]] ){
+ return gpgme_error('SIG_WRONG');
+ }
+ }
+ if (sigObject.hasOwnProperty('notations')){
+ if (!Array.isArray(sigObject.notations)){
+ return gpgme_error('SIG_WRONG');
+ }
+ for (let i=0; i < sigObject.notations.length; i++){
+ let notation = sigObject.notations[i];
+ let notekeys = Object.keys(notation);
+ for (let j=0; j < notekeys.length; j++){
+ // eslint-disable-next-line no-use-before-define
+ if ( typeof (notation[notekeys[j]]) !== expNote[notekeys[j]] ){
+ return gpgme_error('SIG_WRONG');
+ }
+ }
+ }
+ }
+ return new GPGME_Signature(sigObject);
+}
+
+
+/**
+ * Representing the details of a signature. The full details as given by
+ * gpgme-json can be read from the _rawSigObject.
+ *
+ * Note to reviewers: This class should be read only except via
+ * {@link createSignature}
+ * @protected
+ * @class
+ */
+class GPGME_Signature {
+
+ constructor (sigObject){
+ this._rawSigObject = sigObject;
+ }
+ /**
+ * @returns {String} the fingerprint of this signature
+ */
+ get fingerprint (){
+ if (!this._rawSigObject.fingerprint){
+ throw gpgme_error('SIG_WRONG');
+ } else {
+ return this._rawSigObject.fingerprint;
+ }
+ }
+
+ /**
+ * The expiration of this Signature as Javascript date, or null if
+ * signature does not expire
+ * @returns {Date | null}
+ */
+ get expiration (){
+ if (!this._rawSigObject.exp_timestamp){
+ return null;
+ }
+ return new Date(this._rawSigObject.exp_timestamp* 1000);
+ }
+
+ /**
+ * The creation date of this Signature in Javascript Date
+ * @returns {Date}
+ */
+ get timestamp (){
+ return new Date(this._rawSigObject.timestamp * 1000);
+ }
+
+ /**
+ * The overall validity of the key. If false, errorDetails may contain
+ * additional information.
+ */
+ get valid () {
+ if (this._rawSigObject.summary.valid === true){
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Object with boolean properties giving more information on non-valid
+ * signatures. Refer to the [gpgme docs]{@link https://www.gnupg.org/documentation/manuals/gpgme/Verify.html}
+ * for details on the values.
+ */
+ get errorDetails (){
+ let properties = ['revoked', 'key-expired', 'sig-expired',
+ 'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy',
+ 'sys-error'];
+ let result = {};
+ for (let i=0; i< properties.length; i++){
+ if ( this._rawSigObject.summary.hasOwnProperty(properties[i]) ){
+ result[properties[i]] = this._rawSigObject.summary[properties[i]];
+ }
+ }
+ return result;
+ }
+}
+
+/**
+ * Expected keys and their value's type for the signature Object
+ * @private
+ */
+const expKeys = {
+ 'wrong_key_usage': 'boolean',
+ 'chain_model': 'boolean',
+ 'summary': 'object',
+ 'is_de_vs': 'boolean',
+ 'status_string':'string',
+ 'fingerprint':'string',
+ 'validity_string': 'string',
+ 'pubkey_algo_name':'string',
+ 'hash_algo_name':'string',
+ 'pka_address':'string',
+ 'status_code':'number',
+ 'timestamp':'number',
+ 'exp_timestamp':'number',
+ 'pka_trust':'number',
+ 'validity':'number',
+ 'validity_reason':'number',
+ 'notations': 'object'
+};
+
+/**
+ * Keys and their value's type for the summary
+ * @private
+ */
+const expSum = {
+ 'valid': 'boolean',
+ 'green': 'boolean',
+ 'red': 'boolean',
+ 'revoked': 'boolean',
+ 'key-expired': 'boolean',
+ 'sig-expired': 'boolean',
+ 'key-missing': 'boolean',
+ 'crl-missing': 'boolean',
+ 'crl-too-old': 'boolean',
+ 'bad-policy': 'boolean',
+ 'sys-error': 'boolean',
+ 'sigsum': 'object'
+};
+
+/**
+ * Keys and their value's type for notations objects
+ * @private
+ */
+const expNote = {
+ 'human_readable': 'boolean',
+ 'critical':'boolean',
+ 'name': 'string',
+ 'value': 'string',
+ 'flags': 'number'
+};
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
new file mode 100644
index 0000000..9105724
--- /dev/null
+++ b/lang/js/src/gpgmejs.js
@@ -0,0 +1,465 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+
+import { GPGME_Message, createMessage } from './Message';
+import { toKeyIdArray } from './Helpers';
+import { gpgme_error } from './Errors';
+import { GPGME_Keyring } from './Keyring';
+import { createSignature } from './Signature';
+
+/**
+ * @typedef {Object} decrypt_result
+ * @property {String|Uint8Array} data The decrypted data.
+ * @property {String} format Indicating how the data was converted after being
+ * received from gpgme:
+ * <pre>
+ * 'ascii': Data was ascii-encoded and no further processed
+ * 'string': Data was decoded into an utf-8 string,
+ * 'base64': Data was not processed and is a base64 string
+ * 'uint8': data was turned into a Uint8Array
+ * </pre>
+ * @property {Boolean} is_mime (optional) the data claims to be a MIME object.
+ * @property {String} file_name (optional) the original file name
+ * @property {signatureDetails} signatures Verification details for
+ * signatures
+ */
+
+/**
+ * @typedef {Object} signatureDetails
+ * @property {Boolean} all_valid Quick summary. True if all signatures are
+ * fully valid according to gnupg.
+ * @property {Number} count Number of signatures parsed.
+ * @property {Number} failures Number of signatures not passing as valid. This
+ * may imply bad signatures, or signatures with e.g. the public Key not being
+ * available.
+ * @property {GPGME_Signature[]} signatures.good Array of all signatures
+ * considered valid.
+ * @property {GPGME_Signature[]} signatures.bad All invalid signatures.
+ */
+
+/**
+ * @typedef {Object} encrypt_result The result of an encrypt operation,
+ * containing the encrypted data and some additional information.
+ * @property {String} data The encrypted message.
+ * @property {String} format Indicating how the data was converted after being
+ * received from gpgme.
+ * <pre>
+ * 'ascii': Data was ascii-encoded and no further processed
+ * 'string': Data was decoded into an utf-8 string,
+ * 'base64': Data was not processed and is a base64 string
+ * 'uint8': Data was turned into a Uint8Array
+ * </pre>
+ */
+
+/**
+ * @typedef { GPGME_Key | String | Object } inputKeys
+ * Accepts different identifiers of a gnupg Key that can be parsed by
+ * {@link toKeyIdArray}. Expected inputs are: One or an array of
+ * GPGME_Keys; one or an array of fingerprint strings; one or an array of
+ * openpgpjs Key objects.
+ */
+
+/**
+ * @typedef {Object} signResult The result of a signing operation
+ * @property {String} data The resulting data. Includes the signature in
+ * clearsign mode
+ * @property {String} signature The detached signature (only present in in
+ * detached mode)
+ */
+
+/** @typedef {Object} verifyResult The result of a verification
+ * @property {Boolean} data: The verified data
+ * @property {Boolean} is_mime (optional) the data claims to be a MIME
+ * object.
+ * @property {signatureDetails} signatures Verification details for
+ * signatures
+ */
+
+/**
+ * The main entry point for gpgme.js.
+ * @class
+ */
+export class GpgME {
+
+ constructor (){
+ this._Keyring = null;
+ }
+
+ set Keyring (keyring){
+ if (keyring && keyring instanceof GPGME_Keyring){
+ this._Keyring = keyring;
+ }
+ }
+
+ /**
+ * Accesses the {@link GPGME_Keyring}. From the Keyring, all Keys can be
+ * accessed.
+ */
+ get Keyring (){
+ if (!this._Keyring){
+ this._Keyring = new GPGME_Keyring;
+ }
+ return this._Keyring;
+ }
+
+ /**
+ * Encrypt data for the recipients specified in publicKeys. If privateKeys
+ * are submitted, the data will be signed by those Keys.
+ * @param {Object} options
+ * @param {String|Object} options.data text/data to be encrypted as String.
+ * Also accepts Objects with a getText method.
+ * @param {inputKeys} options.publicKeys
+ * Keys used to encrypt the message
+ * @param {inputKeys} options.secretKeys (optional) Keys used to sign the
+ * message. If Keys are present, the operation requested is assumed
+ * to be 'encrypt and sign'
+ * @param {Boolean} options.base64 (optional, default: false) The data will
+ * be interpreted as base64 encoded data.
+ * @param {Boolean} options.armor (optional, default: true) Request the
+ * output as armored block.
+ * @param {Boolean} options.wildcard (optional, default: false) If true,
+ * recipient information will not be added to the message.
+ * @param {Boolean} options.always_trust (optional, default true) This
+ * assumes that used keys are fully trusted. If set to false, encryption to
+ * a key not fully trusted in gnupg will fail.
+ * @param {String} options.expect (default: 'base64') In case of
+ * armored:false, request how to return the binary result.
+ * Accepts 'base64' or 'uint8'
+ * @param {Object} options.additional use additional valid gpg options as
+ * defined in {@link permittedOperations}
+ * @returns {Promise<encrypt_result>} Object containing the encrypted
+ * message and additional info.
+ * @async
+ */
+ encrypt ({ data, publicKeys, secretKeys, base64 = false, armor = true,
+ wildcard, always_trust = true, expect = 'base64',
+ additional = {} } = {}){
+ if (typeof arguments[0] !== 'object') {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ if (!data || !publicKeys){
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ let msg = createMessage('encrypt');
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
+ if (armor === false){
+ msg.setParameter('armor', false);
+ if (expect === 'uint8' || expect === 'base64') {
+ msg.expected = expect;
+ } else {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ } else if (armor === true) {
+ msg.setParameter('armor', true);
+ }
+ if (base64 === true) {
+ msg.setParameter('base64', true);
+ }
+ if (always_trust === true) {
+ msg.setParameter('always-trust', true);
+ }
+ let pubkeys = toKeyIdArray(publicKeys);
+ if (!pubkeys.length) {
+ return Promise.reject(gpgme_error('MSG_NO_KEYS'));
+ }
+ msg.setParameter('keys', pubkeys);
+ let sigkeys = toKeyIdArray(secretKeys);
+ if (sigkeys.length > 0) {
+ msg.setParameter('signing_keys', sigkeys);
+ }
+ putData(msg, data);
+ if (wildcard === true){
+ msg.setParameter('throw-keyids', true);
+ }
+ if (additional){
+ let additional_Keys = Object.keys(additional);
+ for (let k = 0; k < additional_Keys.length; k++) {
+ try {
+ msg.setParameter(additional_Keys[k],
+ additional[additional_Keys[k]]);
+ }
+ catch (error){
+ return Promise.reject(error);
+ }
+ }
+ }
+ if (msg.isComplete() === true){
+ return msg.post();
+ } else {
+ return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
+ }
+ }
+
+ /**
+ * Decrypts (and verifies, if applicable) a message.
+ * @param {Object} options
+ * @param {String|Object} options.data text/data to be decrypted. Accepts
+ * Strings and Objects with a getText method.
+ * @param {Boolean} options.base64 (optional, default: false). Indicate that
+ * the input given is base64-encoded binary instead of an armored block in
+ * gpg armored form.
+ * @param {String} options.expect (optional). By default, the output is
+ * expected to be a string compatible with javascript. In cases of binary
+ * data the decryption may fail due to encoding problems. For data expected
+ * to return as binary data, the decroding after decryption can be bypassed:
+ * <pre>
+ * 'uint8': Return as Uint8Array
+ * 'base64': Return as unprocessed (base64 encoded) string.
+ * </pre>
+ * @returns {Promise<decrypt_result>} Decrypted Message and information
+ * @async
+ */
+ decrypt ({ data, base64, expect } = {}){
+ if (typeof arguments[0] !== 'object') {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ if (!data){
+ return Promise.reject(gpgme_error('MSG_EMPTY'));
+ }
+ let msg = createMessage('decrypt');
+
+ if (msg instanceof Error){
+ return Promise.reject(msg);
+ }
+ if (base64 === true){
+ msg.setParameter('base64', true);
+ }
+ if (expect === 'base64' || expect === 'uint8'){
+ msg.expected = expect;
+ }
+ putData(msg, data);
+ return new Promise(function (resolve, reject){
+ msg.post().then(function (result){
+ let returnValue = { data: result.data };
+ returnValue.format = result.format ? result.format : null;
+ if (result.hasOwnProperty('dec_info')){
+ returnValue.is_mime = result.dec_info.is_mime ? true: false;
+ if (result.dec_info.file_name) {
+ returnValue.file_name = result.dec_info.file_name;
+ }
+ }
+ if (!returnValue.file_name) {
+ returnValue.file_name = null;
+ }
+ if (result.hasOwnProperty('info')
+ && result.info.hasOwnProperty('signatures')
+ && Array.isArray(result.info.signatures)
+ ) {
+ returnValue.signatures = collectSignatures(
+ result.info.signatures);
+ }
+ if (returnValue.signatures instanceof Error){
+ reject(returnValue.signatures);
+ } else {
+ resolve(returnValue);
+ }
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * Sign a Message.
+ * @param {Object} options Signing options
+ * @param {String|Object} options.data text/data to be signed. Accepts
+ * Strings and Objects with a getText method.
+ * @param {inputKeys} options.keys The key/keys to use for signing
+ * @param {String} options.mode The signing mode. Currently supported:
+ * <pre>
+ * 'clearsign':The Message is embedded into the signature;
+ * 'detached': The signature is stored separately
+ * </pre>
+ * @param {Boolean} options.base64 input is considered base64
+ * @returns {Promise<signResult>}
+ * @async
+ */
+ sign ({ data, keys, mode = 'clearsign', base64 } = {}){
+ if (typeof arguments[0] !== 'object') {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ if (!data){
+ return Promise.reject(gpgme_error('MSG_EMPTY'));
+ }
+ let key_arr = toKeyIdArray(keys);
+ if (key_arr.length === 0){
+ return Promise.reject(gpgme_error('MSG_NO_KEYS'));
+ }
+
+ let msg = createMessage('sign');
+ msg.setParameter('keys', key_arr);
+ if (base64 === true){
+ msg.setParameter('base64', true);
+ }
+ msg.setParameter('mode', mode);
+ putData(msg, data);
+
+ return new Promise(function (resolve,reject) {
+ msg.post().then( function (message) {
+ if (mode === 'clearsign'){
+ resolve({
+ data: message.data }
+ );
+ } else if (mode === 'detached') {
+ resolve({
+ data: data,
+ signature: message.data
+ });
+ }
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+
+ /**
+ * Verifies data.
+ * @param {Object} options
+ * @param {String|Object} options.data text/data to be verified. Accepts
+ * Strings and Objects with a getText method
+ * @param {String} options.signature A detached signature. If not present,
+ * opaque mode is assumed
+ * @param {Boolean} options.base64 Indicating that data and signature are
+ * base64 encoded
+ * @returns {Promise<verifyResult>}
+ *@async
+ */
+ verify ({ data, signature, base64 } = {}){
+ if (typeof arguments[0] !== 'object') {
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ if (!data){
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ }
+ let msg = createMessage('verify');
+ let dt = putData(msg, data);
+ if (dt instanceof Error){
+ return Promise.reject(dt);
+ }
+ if (signature){
+ if (typeof signature !== 'string'){
+ return Promise.reject(gpgme_error('PARAM_WRONG'));
+ } else {
+ msg.setParameter('signature', signature);
+ }
+ }
+ if (base64 === true){
+ msg.setParameter('base64', true);
+ }
+ return new Promise(function (resolve, reject){
+ msg.post().then(function (message){
+ if (!message.info || !message.info.signatures){
+ reject(gpgme_error('SIG_NO_SIGS'));
+ } else {
+ let returnValue = {
+ signatures: collectSignatures(message.info.signatures)
+ };
+ if (returnValue.signatures instanceof Error){
+ reject(returnValue.signatures);
+ } else {
+ returnValue.is_mime = message.info.is_mime? true: false;
+ if (message.info.filename){
+ returnValue.file_name = message.info.filename;
+ }
+ returnValue.data = message.data;
+ resolve(returnValue);
+ }
+ }
+ }, function (error){
+ reject(error);
+ });
+ });
+ }
+}
+
+/**
+ * Sets the data of the message, setting flags according on the data type
+ * @param {GPGME_Message} message The message where this data will be set
+ * @param { String| Object } data The data to enter. Expects either a string of
+ * data, or an object with a getText method
+ * @returns {undefined| GPGME_Error} Error if not successful, nothing otherwise
+ * @private
+ */
+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');
+ }
+}
+
+/**
+ * Parses, validates and converts incoming objects into signatures.
+ * @param {Array<Object>} sigs
+ * @returns {signatureDetails} Details about the signatures
+ * @private
+ */
+function collectSignatures (sigs){
+ if (!Array.isArray(sigs)){
+ return gpgme_error('SIG_NO_SIGS');
+ }
+ let summary = {
+ all_valid: false,
+ count: sigs.length,
+ failures: 0,
+ signatures: {
+ good: [],
+ bad: [],
+ }
+ };
+ for (let i=0; i< sigs.length; i++){
+ let sigObj = createSignature(sigs[i]);
+ if (sigObj instanceof Error) {
+ return gpgme_error('SIG_WRONG');
+ }
+ if (sigObj.valid !== true){
+ summary.failures += 1;
+ summary.signatures.bad.push(sigObj);
+ } else {
+ summary.signatures.good.push(sigObj);
+ }
+ }
+ if (summary.failures === 0){
+ summary.all_valid = true;
+ }
+ return summary;
+} \ No newline at end of file
diff --git a/lang/js/src/index.js b/lang/js/src/index.js
new file mode 100644
index 0000000..b8e4274
--- /dev/null
+++ b/lang/js/src/index.js
@@ -0,0 +1,58 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+
+import { GpgME } from './gpgmejs';
+import { gpgme_error } from './Errors';
+import { Connection } from './Connection';
+
+/**
+ * Main entry point for gpgme.js. It initializes by testing the nativeMessaging
+ * connection once, and then offers the available functions as method of the
+ * response object.
+ * An unsuccessful attempt will reject as a GPGME_Error.
+ * @param {Object} config (optional) configuration options
+ * @param {Number} config.timeout set the timeout for the initial connection
+ * check. On some machines and operating systems a default timeout of 500 ms is
+ * too low, so a higher number might be attempted.
+ * @returns {Promise<GpgME>}
+ * @async
+ */
+function init ({ timeout = 500 } = {}){
+ return new Promise(function (resolve, reject){
+ const connection = new Connection;
+ connection.checkConnection(false, timeout).then(
+ function (result){
+ if (result === true) {
+ resolve(new GpgME());
+ } else {
+ reject(gpgme_error('CONN_NO_CONNECT'));
+ }
+ }, function (){ // unspecific connection error. Should not happen
+ reject(gpgme_error('CONN_NO_CONNECT'));
+ });
+ });
+}
+
+const exportvalue = { init:init };
+export default exportvalue; \ 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 0000000..09a1783
--- /dev/null
+++ b/lang/js/src/permittedOperations.js
@@ -0,0 +1,413 @@
+/* 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+
+ *
+ * Author(s):
+ * Maximilian Krambach <mkrambach@intevation.de>
+ */
+
+/**
+ * @typedef {Object} messageProperty
+ * A message Property is defined by it's key.
+ * @property {Array<String>} allowed Array of allowed types.
+ * Currently accepted values are 'number', 'string', 'boolean'.
+ * @property {Boolean} array_allowed If the value can be an array of types
+ * defined in allowed
+ * @property {Array<*>} allowed_data (optional) restricts to the given values
+ */
+
+/**
+ * Definition of the possible interactions with gpgme-json.
+ * @param {Object} operation Each operation is named by a key and contains
+ * the following properties:
+ * @property {messageProperty} required An object with all required parameters
+ * @property {messageProperty} optional An object with all optional parameters
+ * @property {Boolean} pinentry (optional) If true, a password dialog is
+ * expected, thus a connection tuimeout is not advisable
+ * @property {Object} answer The definition on what to expect as answer, if the
+ * answer is not an error
+ * @property {Array<String>} answer.type the type(s) as reported by gpgme-json.
+ * @property {Object} answer.payload key-value combinations of expected
+ * properties of an answer and their type ('boolean', 'string', object), which
+ * may need further decoding from base64
+ * @property {Object} answer.info key-value combinations of expected
+ * properties of an answer and their type ('boolean', 'string', object), which
+ * are meant to be data directly sent by gpgme (i.e. user ids)
+ @const
+*/
+export const permittedOperations = {
+ encrypt: {
+ pinentry: true, // TODO only with signing_keys
+ required: {
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ 'data': {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'signing_keys': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ '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']
+ },
+ 'sender': {
+ allowed: ['string']
+ },
+ 'file_name': {
+ allowed: ['string']
+ }
+ },
+ answer: {
+ type: ['ciphertext'],
+ payload: {
+ 'data': 'string'
+ },
+ info: {
+ 'base64':'boolean'
+ }
+ }
+ },
+
+ decrypt: {
+ pinentry: true,
+ required: {
+ 'data': {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'base64': {
+ allowed: ['boolean']
+ }
+ },
+ answer: {
+ type: ['plaintext'],
+ payload: {
+ 'data': 'string',
+ },
+ info: {
+ 'base64': 'boolean',
+ 'mime': 'boolean',
+ 'info': 'object',
+ 'dec_info': 'object'
+ }
+ }
+ },
+
+ sign: {
+ pinentry: true,
+ required: {
+ 'data': {
+ allowed: ['string'] },
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'sender': {
+ allowed: ['string'],
+ },
+ 'mode': {
+ allowed: ['string'],
+ allowed_data: ['detached', 'clearsign']
+ // TODO 'opaque' is not used, but available on native app
+ },
+ 'base64': {
+ allowed: ['boolean']
+ },
+ 'armor': {
+ allowed: ['boolean']
+ },
+ },
+ answer: {
+ type: ['signature', 'ciphertext'],
+ payload: {
+ 'data': 'string',
+ },
+ info: {
+ 'base64':'boolean'
+ }
+ }
+ },
+
+ // note: For the meaning of the optional keylist flags, refer to
+ // https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html
+ keylist:{
+ required: {},
+
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'secret': {
+ allowed: ['boolean']
+ },
+ 'extern': {
+ allowed: ['boolean']
+ },
+ 'local':{
+ allowed: ['boolean']
+ },
+ 'locate': {
+ allowed: ['boolean']
+ },
+ 'sigs':{
+ allowed: ['boolean']
+ },
+ 'notations':{
+ allowed: ['boolean']
+ },
+ 'tofu': {
+ allowed: ['boolean']
+ },
+ 'ephemeral': {
+ allowed: ['boolean']
+ },
+ 'validate': {
+ allowed: ['boolean']
+ },
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
+ }
+ },
+ answer: {
+ type: ['keys'],
+ info: {
+ 'keys': 'object',
+ 'base64': 'boolean',
+ }
+ }
+ },
+
+ export: {
+ required: {},
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
+ },
+ 'armor': {
+ allowed: ['boolean']
+ },
+ 'extern': {
+ allowed: ['boolean']
+ },
+ 'minimal': {
+ allowed: ['boolean']
+ },
+ 'raw': {
+ allowed: ['boolean']
+ },
+ 'pkcs12': {
+ allowed: ['boolean']
+ },
+ 'with-sec-fprs': {
+ allowed: ['boolean']
+ }
+ // secret: not yet implemented
+ },
+ answer: {
+ type: ['keys'],
+ payload: {
+ 'data': 'string',
+ },
+ info: {
+ 'base64': 'boolean',
+ 'sec-fprs': 'object'
+ }
+ }
+ },
+
+ import: {
+ required: {
+ 'data': {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'base64': {
+ allowed: ['boolean']
+ },
+ },
+ answer: {
+ type: [],
+ info: {
+ 'result': 'object'
+ }
+ }
+ },
+
+ delete: {
+ pinentry: true,
+ required:{
+ 'key': {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ },
+ answer: {
+ info: {
+ 'success': 'boolean'
+ }
+ }
+ },
+
+ version: {
+ required: {},
+ optional: {},
+ answer: {
+ type: [''],
+ info: {
+ 'gpgme': 'string',
+ 'info': 'object'
+ }
+ }
+ },
+
+ createkey: {
+ pinentry: true,
+ required: {
+ userid: {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ algo: {
+ allowed: ['string']
+ },
+ expires: {
+ allowed: ['number'],
+ }
+ },
+ answer: {
+ type: [''],
+ info: { 'fingerprint': 'string' }
+ }
+ },
+
+ verify: {
+ required: {
+ data: {
+ allowed: ['string']
+ }
+ },
+ optional: {
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'signature': {
+ allowed: ['string']
+ },
+ 'base64':{
+ allowed: ['boolean']
+ }
+ },
+ answer: {
+ type: ['plaintext'],
+ payload:{
+ 'data': 'string'
+ },
+ info: {
+ 'base64':'boolean',
+ 'info': 'object'
+ // info.file_name: Optional string of the plaintext file name.
+ // info.is_mime: Boolean if the messages claims it is MIME.
+ // info.signatures: Array of signatures
+ }
+ }
+ },
+
+ config_opt: {
+ required: {
+ 'component':{
+ allowed: ['string'],
+ // allowed_data: ['gpg'] // TODO check all available
+ },
+ 'option': {
+ allowed: ['string'],
+ // allowed_data: ['default-key'] // TODO check all available
+ }
+ },
+ optional: {},
+ answer: {
+ type: [],
+ info: {
+ 'option': 'object'
+ }
+ }
+ }
+};
diff --git a/lang/js/unittest_inputvalues.js b/lang/js/unittest_inputvalues.js
new file mode 100644
index 0000000..659ef85
--- /dev/null
+++ b/lang/js/unittest_inputvalues.js
@@ -0,0 +1,123 @@
+import { createKey } from './src/Key';
+
+export const helper_params = {
+ validLongId: '0A0A0A0A0A0A0A0A',
+ validKeys: ['A1E3BC45BDC8E87B74F4392D53B151A1368E50F3',
+ createKey('D41735B91236FDB882048C5A2301635EEFF0CB05'),
+ 'EE17AEE730F88F1DE7713C54BBE0A4FF7851650A'],
+ validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
+ '9AAE7A338A9A9A7A7A8A9A9A7A7A8A9A9A7A7DDA'],
+ invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
+ invalidFingerprints: [{ hello:'World' }, ['kekekeke'], new Uint32Array(40)],
+ invalidKeyArray: { curiosity:'uncat' },
+ invalidKeyArray_OneBad: [
+ createKey('D41735B91236FDB882048C5A2301635EEFF0CB05'),
+ 'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
+ '3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
+ invalidErrorCode: 'Please type in all your passwords.',
+ validGPGME_Key: createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', true),
+ 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'), null]
+ }
+};
+
+export const whatever_params = {
+ four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}'],
+};
+export const key_params = {
+// A Key you own (= having a secret Key) in GPG. See testkey.pub/testkey.sec
+ validKeyFingerprint: 'D41735B91236FDB882048C5A2301635EEFF0CB05',
+ // A Key you do not own (= having no secret Key) in GPG. See testkey2.pub
+ validFingerprintNoSecret: 'E059A1E0866D31AE131170884D9A2E13304153D1',
+ // A Key not in your Keyring. This is just a random hex string.
+ invalidKeyFingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A',
+ validKeyProperties: ['expired', 'disabled','invalid','can_encrypt',
+ 'can_sign','can_certify','can_authenticate','secret','is_qualified']
+};
+export const armoredKey = {
+ fingerprint: '78034948BA7F5D0E9BDB67E4F63790C11E60278A',
+ key:'-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
+ '\n' +
+ 'mQENBFsPvK0BCACaIgoIN+3g05mrTITULK/YDTrfg4W7RdzIZBxch5CM0zdu/dby\n' +
+ 'esFwaJbVQIqu54CRz5xKAiWmRrQCaRvhvjY0na5r5UUIpbeQiOVrl65JtNbRmlik\n' +
+ 'd9Prn1kZDUOZiCPIKn+/M2ecJ92YedM7I4/BbpiaFB11cVrPFg4thepn0LB3+Whp\n' +
+ '9HDm4orH9rjy6IUr6yjWNIr+LYRY6/Ip2vWcMVjleEpTFznXrm83hrJ0n0INtyox\n' +
+ 'Nass4eDWkgo6ItxDFFLOORSmpfrToxZymSosWqgux/qG6sxHvLqlqy6Xe3ZYRFbG\n' +
+ '+JcA1oGdwOg/c0ndr6BYYiXTh8+uUJfEoZvzABEBAAG0HEJsYSBCbGEgPGJsYWJs\n' +
+ 'YUBleGFtcGxlLm9yZz6JAVQEEwEIAD4WIQR4A0lIun9dDpvbZ+T2N5DBHmAnigUC\n' +
+ 'Ww+8rQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD2N5DBHmAn\n' +
+ 'igwIB/9K3E3Yev9taZP4KnXPhk1oMQRW1MWAsFGUr+70N85VwedpUawymW4vXi1+\n' +
+ 'hMeTc39QjmZ0+VqHkJttkqEN6bLcEvgmU/mOlOgKdzy6eUcasYAzgoAKUqSX1SPs\n' +
+ '0Imo7Tj04wnfnVwvKxaeadi0VmdqIYaW75UlrzIaltsBctyeYH8sBrvaTLscb4ON\n' +
+ '46OM3Yw2G9+dBF0P+4UYFHP3EYZMlzNxfwF+i2HsYcNDHlcLfjENr9GwKn5FJqpY\n' +
+ 'Iq3qmI37w1hVasHDxXdz1X06dpsa6Im4ACk6LXa7xIQlXxTgPAQV0sz2yB5eY+Md\n' +
+ 'uzEXPGW+sq0WRp3hynn7kVP6QQYvuQENBFsPvK0BCACwvBcmbnGJk8XhEBRu2QN3\n' +
+ 'jKgVs3CG5nE2Xh20JipZwAuGHugDLv6/jlizzz5jtj3SAHVtJB8lJW8I0cNSEIX8\n' +
+ 'bRYH4C7lP2DTb9CgMcGErQIyK480+HIsbsZhJSNHdjUUl6IPEEVfSQzWaufmuswe\n' +
+ 'e+giqHiTsaiW20ytXilwVGpjlHBaxn/bpskZ0YRasgnPqKgJD3d5kunNqWoyCpMc\n' +
+ 'FYgDERvPbhhceFbvFE9G/u3gbcuV15mx53dDX0ImvPcvJnDOyJS9yr7ApdOV312p\n' +
+ 'A1MLbxfPnbnVu+dGXn7D/VCDd5aBYVPm+5ANrk6z9lYKH9aO5wgXpLAdJvutCOL5\n' +
+ 'ABEBAAGJATwEGAEIACYWIQR4A0lIun9dDpvbZ+T2N5DBHmAnigUCWw+8rQIbDAUJ\n' +
+ 'A8JnAAAKCRD2N5DBHmAnigMVB/484G2+3R0cAaj3V/z4gW3MRSMhcYqEMyJ/ACdo\n' +
+ '7y8eoreYW843JWWVDRY6/YcYYGuBBP47WO4JuP2wIlVn17XOCSgnNjmmjsIYiAzk\n' +
+ 'op772TB27o0VeiFX5iWcawy0EI7JCb23xpI+QP31ksL2yyRYFXCtXSUfcOrLpCY8\n' +
+ 'aEQMQbAGtkag1wHTo/Tf/Vip8q0ZEQ4xOKTR2/ll6+inP8kzGyzadElUnH1Q1OUX\n' +
+ 'd2Lj/7BpBHE2++hAjBQRgnyaONF7mpUNEuw64iBNs0Ce6Ki4RV2+EBLnFubnFNRx\n' +
+ 'fFJcYXcijhuf3YCdWzqYmPpU/CtF4TgDlfSsdxHxVOmnZkY3\n' +
+ '=qP6s\n' +
+ '-----END PGP PUBLIC KEY BLOCK-----\n',
+ keyChangedUserId: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
+ '\n' +
+ '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\n' +
+ '=9WZ7\n' +
+ '-----END PGP PUBLIC KEY BLOCK-----\n'
+}; \ No newline at end of file
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
new file mode 100644
index 0000000..f28be76
--- /dev/null
+++ b/lang/js/unittests.js
@@ -0,0 +1,386 @@
+/* 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'; /* global mocha, it, describe*/
+import './node_modules/chai/chai';/* global chai*/
+import { helper_params as hp } from './unittest_inputvalues';
+import { message_params as mp } from './unittest_inputvalues';
+import { whatever_params as wp } from './unittest_inputvalues';
+import { key_params as kp } from './unittest_inputvalues';
+import { Connection } from './src/Connection';
+import { gpgme_error, err_list } from './src/Errors';
+import { toKeyIdArray , isFingerprint } from './src/Helpers';
+import { createKey } from './src/Key';
+import { GPGME_Keyring } from './src/Keyring';
+import { GPGME_Message, createMessage } from './src/Message';
+
+mocha.setup('bdd');
+const expect = chai.expect;
+chai.config.includeStack = true;
+const connectionTimeout = 2000;
+
+function unittests (){
+ describe('Connection testing', function (){
+
+ it('Connecting', function (done) {
+ let conn0 = new Connection;
+ conn0.checkConnection(true, connectionTimeout).then(
+ function (answer) {
+ expect(answer).to.not.be.empty;
+ expect(answer.gpgme).to.not.be.undefined;
+ expect(answer.gpgme).to.be.a('string');
+ expect(answer.info).to.be.an('Array');
+ expect(conn0.disconnect).to.be.a('function');
+ expect(conn0.post).to.be.a('function');
+ done();
+ });
+
+ });
+
+ it('Disconnecting', function (done) {
+ let conn0 = new Connection;
+ conn0.checkConnection(false, connectionTimeout).then(
+ function (answer) {
+ expect(answer).to.be.true;
+ conn0.disconnect();
+ conn0.checkConnection(false, connectionTimeout).then(
+ function (result) {
+ expect(result).to.be.false;
+ done();
+ });
+ });
+ });
+ });
+
+ describe('Error Object handling', function (){
+ // TODO: new GPGME_Error codes
+ it('check the Timeout error', function (){
+ let test0 = gpgme_error('CONN_TIMEOUT');
+
+ expect(test0).to.be.an.instanceof(Error);
+ expect(test0.code).to.equal('CONN_TIMEOUT');
+ });
+
+ it('Error Object returns generic code if code is not listed',
+ function (){
+ let test0 = gpgme_error(hp.invalidErrorCode);
+
+ expect(test0).to.be.an.instanceof(Error);
+ expect(test0.code).to.equal('GENERIC_ERROR');
+ }
+ );
+
+ it('Warnings like PARAM_IGNORED should not return errors', function (){
+ let test0 = gpgme_error('PARAM_IGNORED');
+
+ expect(test0).to.be.null;
+ });
+ });
+
+ describe('Fingerprint checking', function (){
+
+ it('isFingerprint(): valid Fingerprint', function (){
+ let test0 = isFingerprint(hp.validFingerprint);
+
+ expect(test0).to.be.true;
+ });
+
+ it('isFingerprint(): invalid Fingerprints', function (){
+ for (let i=0; i < hp.invalidFingerprints.length; i++){
+ let test0 = isFingerprint(hp.invalidFingerprints[i]);
+
+ expect(test0).to.be.false;
+ }
+ });
+ });
+
+ describe('toKeyIdArray() (converting input to fingerprint)', function (){
+
+ it('Correct fingerprint string', function (){
+ let test0 = toKeyIdArray(hp.validFingerprint);
+
+ expect(test0).to.be.an('array');
+ expect(test0).to.include(hp.validFingerprint);
+ });
+
+ it('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('Key has data after a first refresh', function (done) {
+ let key = createKey(kp.validKeyFingerprint);
+ key.refreshKey().then(function (key2){
+ expect(key2.get).to.be.a('function');
+ for (let i=0; i < kp.validKeyProperties.length; i++) {
+ let prop = key2.get(kp.validKeyProperties[i]);
+ expect(prop).to.not.be.undefined;
+ expect(prop).to.be.a('boolean');
+ }
+ expect(isFingerprint(key2.get('fingerprint'))).to.be.true;
+ expect(
+ key2.get('fingerprint')).to.equal(kp.validKeyFingerprint);
+ expect(
+ key2.get('fingerprint')).to.equal(key.fingerprint);
+ done();
+ });
+ });
+
+ it('Non-cached key async data retrieval', function (done){
+ let key = createKey(kp.validKeyFingerprint, true);
+ key.get('can_authenticate').then(function (result){
+ expect(result).to.be.a('boolean');
+ done();
+ });
+ });
+
+ it('Non-cached key async armored Key', function (done){
+ let key = createKey(kp.validKeyFingerprint, true);
+ key.get('armored').then(function (result){
+ expect(result).to.be.a('string');
+ expect(result).to.include('KEY BLOCK-----');
+ done();
+ });
+ });
+
+ it('Non-cached key async hasSecret', function (done){
+ let key = createKey(kp.validKeyFingerprint, true);
+ key.get('hasSecret').then(function (result){
+ expect(result).to.be.a('boolean');
+ done();
+ });
+ });
+
+ it('Non-cached key async hasSecret (no secret in Key)', function (done){
+ let key = createKey(kp.validFingerprintNoSecret, true);
+ key.get('hasSecret').then(function (result){
+ expect(result).to.be.a('boolean');
+ expect(result).to.equal(false);
+ done();
+ });
+ });
+
+ it('Querying non-existing Key returns an error', function (done) {
+ let key = createKey(kp.invalidKeyFingerprint);
+ key.refreshKey().then(function (){},
+ function (error){
+ expect(error).to.be.an.instanceof(Error);
+ expect(error.code).to.equal('KEY_NOKEY');
+ done();
+ });
+ });
+
+ it('createKey returns error if parameters are wrong', function (){
+ for (let i=0; i< 4; i++){
+ expect(function (){
+ createKey(wp.four_invalid_params[i]);
+ }).to.throw(
+ err_list.PARAM_WRONG.msg
+ );
+
+ }
+ });
+
+ // it('Overwriting getFingerprint does not work', function(){
+ // const evilFunction = function(){
+ // return 'bad Data';
+ // };
+ // let key = createKey(kp.validKeyFingerprint, true);
+ // expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
+ // try {
+ // key.getFingerprint = evilFunction;
+ // }
+ // catch(e) {
+ // expect(e).to.be.an.instanceof(TypeError);
+ // }
+ // expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
+ // expect(key.getFingerprint).to.not.equal(evilFunction);
+ // });
+ });
+
+ describe('GPGME_Keyring', function (){
+
+ it('correct Keyring initialization', function (){
+ let keyring = new GPGME_Keyring;
+ expect(keyring).to.be.an.instanceof(GPGME_Keyring);
+ expect(keyring.getKeys).to.be.a('function');
+ });
+
+ it('Loading Keys from Keyring, to be used synchronously',
+ function (done){
+ let keyring = new GPGME_Keyring;
+ keyring.getKeys({ prepare_sync: true }).then(function (result){
+ expect(result).to.be.an('array');
+ expect(result[0].get('hasSecret')).to.be.a('boolean');
+ done();
+ });
+ }
+ );
+
+ it('Loading specific Key from Keyring, to be used synchronously',
+ function (done){
+ let keyring = new GPGME_Keyring;
+ keyring.getKeys({
+ pattern: kp.validKeyFingerprint,
+ prepare_sync: true }).then(
+ function (result){
+ expect(result).to.be.an('array');
+ expect(result[0].get('hasSecret')).to.be.a('boolean');
+ done();
+ }
+ );
+ }
+ );
+
+ it('Querying non-existing Key from Keyring', function (done){
+ let keyring = new GPGME_Keyring;
+ keyring.getKeys({
+ pattern: kp.invalidKeyFingerprint,
+ prepare_sync: true
+ }).then(function (result){
+ expect(result).to.be.an('array');
+ expect(result.length).to.equal(0);
+ done();
+ });
+ });
+
+ });
+
+ 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('keys', hp.validFingerprints);
+ expect(test0.isComplete()).to.be.false;
+ expect(function (){
+ test0.setParameter('data', '');
+ }).to.throw(
+ err_list.PARAM_WRONG.msg);
+ });
+
+ 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',
+ 'chunksize');
+ expect(test0.message.op).to.equal('encrypt');
+ expect(test0.message.data).to.equal(
+ mp.valid_encrypt_data);
+ });
+
+ it ('Not accepting non-allowed operation', function (){
+ expect(function () {
+ createMessage(mp.invalid_op_action);
+ }).to.throw(
+ err_list.MSG_WRONG_OP.msg);
+ });
+ it('Not accepting wrong parameter type', function (){
+ expect(function () {
+ createMessage(mp.invalid_op_type);
+ }).to.throw(
+ err_list.PARAM_WRONG.msg);
+ });
+
+ 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++){
+ expect(function (){
+ test0.setParameter(
+ mp.invalid_param_test.invalid_param_names[i],
+ 'Somevalue');}
+ ).to.throw(err_list.PARAM_WRONG.msg);
+ }
+ });
+
+ 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++){
+ expect(function (){
+ test0.setParameter(
+ mp.invalid_param_test.validparam_name_0,
+ mp.invalid_param_test.invalid_values_0[j]);
+ }).to.throw(err_list.PARAM_WRONG.msg);
+ }
+ });
+ });
+
+}
+
+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 0000000..19f3bbd
--- /dev/null
+++ b/lang/js/webpack.conf.js
@@ -0,0 +1,36 @@
+/* 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
+ */
+/* global require, module, __dirname */
+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 0000000..c3c87f3
--- /dev/null
+++ b/lang/js/webpack.conf_unittests.js
@@ -0,0 +1,36 @@
+/* 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
+ */
+/* global require, module, __dirname */
+
+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'
+ }
+};