Merge branch 'javascript-binding'
This adds a new language binding "gpgme.js" to GPGME. It serves as a bridge between the native-messaging service "gpgme-json" and JavaScript Applications. The first user of this binding will be Mailvelope which will see GnuPG integration in the near future. GnuPG-Bug-Id: T4107
This commit is contained in:
commit
59ed27bae1
@ -908,6 +908,9 @@ AC_CONFIG_FILES(lang/qt/tests/Makefile)
|
|||||||
AC_CONFIG_FILES(lang/qt/src/qgpgme_version.h)
|
AC_CONFIG_FILES(lang/qt/src/qgpgme_version.h)
|
||||||
AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpgme.asd])
|
AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpgme.asd])
|
||||||
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([lang/qt/doc/Doxyfile])])
|
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([lang/qt/doc/Doxyfile])])
|
||||||
|
AC_CONFIG_FILES([lang/js/Makefile lang/js/src/Makefile
|
||||||
|
lang/js/BrowserTestExtension/Makefile
|
||||||
|
lang/js/DemoExtension/Makefile])
|
||||||
AC_CONFIG_FILES(lang/qt/doc/Makefile)
|
AC_CONFIG_FILES(lang/qt/doc/Makefile)
|
||||||
AC_CONFIG_FILES([lang/python/Makefile
|
AC_CONFIG_FILES([lang/python/Makefile
|
||||||
lang/python/version.py
|
lang/python/version.py
|
||||||
|
@ -23,7 +23,8 @@ DISTCLEANFILES = gpgme.tmp
|
|||||||
CLEANFILES = mkdefsinc defs.inc
|
CLEANFILES = mkdefsinc defs.inc
|
||||||
|
|
||||||
EXTRA_DIST = module-overview.sk HACKING DCO ChangeLog-2011 \
|
EXTRA_DIST = module-overview.sk HACKING DCO ChangeLog-2011 \
|
||||||
mkdefsinc.c defsincdate
|
mkdefsinc.c defsincdate \
|
||||||
|
examples/gpgme-mozilla.json examples/gpgme-chrome.json
|
||||||
|
|
||||||
BUILT_SOURCES = defsincdate defs.inc
|
BUILT_SOURCES = defsincdate defs.inc
|
||||||
|
|
||||||
|
9
doc/examples/gpgme-chrome.json
Normal file
9
doc/examples/gpgme-chrome.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "gpgmejson",
|
||||||
|
"description": "Integration with GnuPG",
|
||||||
|
"path": "/usr/bin/gpgme-json",
|
||||||
|
"type": "stdio",
|
||||||
|
"allowed_origins": [
|
||||||
|
"chrome-extension://kajibbejlbohfaggdiogboambcijhkke/"
|
||||||
|
]
|
||||||
|
}
|
9
doc/examples/gpgme-mozilla.json
Normal file
9
doc/examples/gpgme-mozilla.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "gpgmejson",
|
||||||
|
"description": "Integration with GnuPG",
|
||||||
|
"path": "/usr/bin/gpgme-json",
|
||||||
|
"type": "stdio",
|
||||||
|
"allowed_extensions": [
|
||||||
|
"jid1-AQqSMBYb0a8ADg@jetpack"
|
||||||
|
]
|
||||||
|
}
|
@ -18,6 +18,6 @@
|
|||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
|
|
||||||
SUBDIRS = $(ENABLED_LANGUAGES)
|
SUBDIRS = $(ENABLED_LANGUAGES)
|
||||||
DIST_SUBDIRS = cl cpp qt python
|
DIST_SUBDIRS = cl cpp qt python js
|
||||||
|
|
||||||
EXTRA_DIST = README
|
EXTRA_DIST = README
|
||||||
|
@ -13,4 +13,4 @@ cl Common Lisp
|
|||||||
cpp C++
|
cpp C++
|
||||||
qt Qt-Framework API
|
qt Qt-Framework API
|
||||||
python Python 2 and 3 (module name: gpg)
|
python Python 2 and 3 (module name: gpg)
|
||||||
javascript Native messaging client for the gpgme-json server.
|
js Native messaging client for the gpgme-json server.
|
||||||
|
1
lang/js/.babelrc
Normal file
1
lang/js/.babelrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "presets": ["es2015"] }
|
49
lang/js/.eslintrc.json
Normal file
49
lang/js/.eslintrc.json
Normal file
@ -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"]
|
||||||
|
}
|
||||||
|
}
|
45
lang/js/BrowserTestExtension/Makefile.am
Normal file
45
lang/js/BrowserTestExtension/Makefile.am
Normal file
@ -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
|
28
lang/js/BrowserTestExtension/browsertest.html
Normal file
28
lang/js/BrowserTestExtension/browsertest.html
Normal file
@ -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>
|
113
lang/js/BrowserTestExtension/index.html
Normal file
113
lang/js/BrowserTestExtension/index.html
Normal file
@ -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-----
|
||||||
|
|
||||||
|
lQOYBFrsKEkBCADKw4Wt8J6M/88qD8PO6lSMCxH1cpwH8iK0uPaFFYsJkkXo7kWf
|
||||||
|
PTAtrV+REqF/o80dvYcdLvRsV21pvncZz/HXLu1yQ18mC3XObrKokbdgrTTKA5XE
|
||||||
|
BZkNsqyaMMJauT18H4hYkSg62/tTdO1cu/zWv/LFf7Xyn6+uA74ovXCJlO1s0N2c
|
||||||
|
PShtr98QRzPMf2owgVk37JnDNp4gGVDGHxSZOuUwxgYAZYnA8SFc+c+3ZrQfY870
|
||||||
|
+O4j3Mz4p7yD13AwP4buQLBsb/icxekeQCqpRJhLH9f7MdEcGXa1x36RcEkHdu+M
|
||||||
|
yJ392eMgD+dKNfRCtyTPhjZTxvbNELIBYICfABEBAAEAB/wLJ0gyMjs2fFfT83wM
|
||||||
|
5Lzz2yQIwV4t3bblBAujdHTqeN5Zmsm/oakFyjSokULK96Kv0R4ej9eoIgMFvxFk
|
||||||
|
HRkrggxTrbsNJ7I6QcKYHTPeIIj318ykNL6fj0WJUcdPIENukXl5jbqNyk3/4D2y
|
||||||
|
TTDySyq6jHTgvMH4K4KJUSpglvSJPntTk9RhuFGHAF+sNR9atygDYctAaERMRtSg
|
||||||
|
LCoSt/AoX5GRMlQjXT9oqQjwSQoZyF4s8HMC8wdTFIE/E0L4IVdHVp8sz2UszNtT
|
||||||
|
W/evmCA+KVruKjRH/Fhrq4hHkEamW28+j4L6uAyagONP7BONs+S5Oo2zTT9+tV2R
|
||||||
|
ILTZBADdgLuAgF6C5Lu9jCF6DfFgaT/uafMyQNkEGNlxOHMWHTgLHe475V2eG9gA
|
||||||
|
amd4yXKyEFKU1PWnvlGuicQSGdzVcwmq61msvXgYD0FK3LP3yWzKnE4X1tzrC9Vp
|
||||||
|
/uHJxKjewCuyt1f5in919v+T8TbUxBYKC0zX/qWtX+10cTx77QQA6leqhToJ95Yc
|
||||||
|
u4UBrKMEO+y2v8Svb3LG7yI5oY8tkw0EkJ/kpZ8xTAfZYCe6fXdvVE3PHg2lrxyc
|
||||||
|
Wv/EU3QY/qA3G82mbXYeJ2jNZaTNYo4MylMrt4Mx25x4ke7JlsE8SVrQ+4CrHkqp
|
||||||
|
OjSIa7fppLrQ78uW980AtN8NNQGrlTsD/A9aoA60Igxy1Q3K2uSyDCyjLknv57ym
|
||||||
|
ZSBD3/t7m0l6Q6gbdfhNGosT+Hd4y3actqEqzXZHW2VG4dKZ/wRNkxtSm9adU9vs
|
||||||
|
EHyzxjb6mKIH32zAG5TaFT20hC+NK6lsyHr9UE2ZrS6ma2sLxGW2O40hqNsdD+5m
|
||||||
|
NrqeBc2I/js1PMK0EHRlc3RAZXhhbXBsZS5vcmeJAVQEEwEIAD4WIQTUFzW5Ejb9
|
||||||
|
uIIEjFojAWNe7/DLBQUCWuwoSQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIe
|
||||||
|
AQIXgAAKCRAjAWNe7/DLBf9kB/wOQ/S60HGwFq07W9N01HWULyhHKoMmcHL6rfZ6
|
||||||
|
4oDqLxolPSasz7WAMW1jN4qtWJ0mFzwO83V6kaBe+wF6Kqir6udFSBW9rPcFg6/V
|
||||||
|
ZXPltT0a6uacIHq6DyQ5iMW4YQWbVy9OR2rNGkYo1JCBR0XdRJYCSX3yB4TWv/eX
|
||||||
|
nZ37/WjmiTOIZh35rjs+NuU/S5JPDfAp2/k70DevQeBsv+UjVXjWpNTZmPbvDnd9
|
||||||
|
95uSmC6UY4hzyP84ORYMYn9n1QAR0goxDN6UunOf9Rlp1oMzdxMool/d1MlCxg2h
|
||||||
|
3jheuhv7lgUF4KpvHOuEPXQ7UO417E0TYcDZ1J8Nsv87SZeEnQOYBFrsKEkBCADj
|
||||||
|
oEBhG/QPqZHg8VyoD1xYRAWGxyDJkX/GrSs6yE+x2hk5FoQCajxKa/d4AVxOnJpd
|
||||||
|
whAfeXeSNaql5Ejgzax+Tdj9BV6vtGVJVv0pO7bgAiZxkA6RHxtNqhpPnPQoXvUz
|
||||||
|
kzpRgpuL+Nj4yIg7z1ITH6KQH4u5SI9vd+j/8i9Taz67pdZwuJjac8qBuJHjzAo1
|
||||||
|
bjYctFYUSG5pbmMQyNLySzgiNkFa4DajODlt3RuqVGP316Fk+Sy2+60tC/HlX8jg
|
||||||
|
MyMONfOGBQx6jk8tvAphS/LAqrrNepnagIyLUGKU+L8cB2g1PGGp2biBFWqZbudZ
|
||||||
|
oyRBet/0yH/zirBdQJw1ABEBAAEAB/4lN3gXOI4OuoOcsvHak4pebx61Mt0YP9cT
|
||||||
|
qZASIBqxok5x8E28pFh/tYfkYdqRCtdNYZOnxcEoUWh5j6nfwZkEnJ9P/T8GPNk7
|
||||||
|
pMKnKXmExi05b5uGHD8nU1rSbf/YkvAF0vpbxd4/RDxbbtQhbUwGzusSI+pBLM0w
|
||||||
|
5TreEB+vRGBc2gOvXXOtKLNEa7M9rH2EwbAkP3jOGGwgk6adxbQdBcRxq4merqhL
|
||||||
|
YrVz73bCj8TDc0fsNJyIaZZJ++ejfBFYavsF1pvx9z7FNFi8rSXoiB3SBtaWGfhr
|
||||||
|
bwNaMZrDc7TRIq/fgGaL6g//bzcWrr1YaHXZ10Bgx6UymDOlYkCpBADm0Hv46sPw
|
||||||
|
07SO8+IACcaQliOto1pndOPwTimCeo58/7rf8I2a5uuJloGrnPwAX65bKDnUALp6
|
||||||
|
X3lnXRNMhnB3Uewx4i00LQmjsxhJfQiGLpMv0j58tn64s7GqQzGVV1JKcQm992RV
|
||||||
|
jFOydyjZ+K4LGWEOITG/bZrMEVNGCM+OnQQA/Haz8xN0NFSlq7tyfFc0pkx/TiCX
|
||||||
|
xGfBqbO0wU2b5GMnZbY/06HENpidIzpa231VQaw5/nPTvfhlLKW1iGAkc148cX1q
|
||||||
|
lL9w2ksXuaHR3LXud2VcfVTIdxU/7h7u1dD/85+c0+7jlGObD9cXKxlM6OjpIJz1
|
||||||
|
l5/1h3C5S0TuxHkEAL/3BGihkhNfv1Xx0rWu0/732usX/nE/A9C26hGu41FUf3fp
|
||||||
|
0ilonKpKZUEwWt5hWSEFCSrznNVekiO0rxvuu3RVegvzThPNU4Pf4JZtJpRVhvUQ
|
||||||
|
d9ulxJw7V9rs75uNBatTNC0kXuGoXhehw4Bn93xa67gYGd3LfrH+oT0GCDpTSHCJ
|
||||||
|
ATwEGAEIACYWIQTUFzW5Ejb9uIIEjFojAWNe7/DLBQUCWuwoSQIbDAUJA8JnAAAK
|
||||||
|
CRAjAWNe7/DLBf0pCACPp5hBuUWngu2Hqvg+tNiujfsiYzId3MffFxEk3CbXeHcJ
|
||||||
|
5F32NDJ9PYCnra4L8wSv+NZt9gIa8lFwoFSFQCjzH7KE86XcV3MhfdJTNb/+9CR7
|
||||||
|
Jq3e/4Iy0N5ip7PNYMCyakcAsxvsNCJKrSaDuYe/OAoTXRBtgRWE2uyT315em02L
|
||||||
|
kr+2Cc/Qk6H+vlNOHGRgnpI/OZZjnUuUfBUvMGHr1phW+y7aeymC9PnUGdViRdJe
|
||||||
|
23nntMSDA+0/I7ESO9JsWvJbyBmuiZpu9JjScOjYH9xpQLqRNyw4WHpZriN69F0t
|
||||||
|
9Mmd7bM1+UyPgbPEr0iWMeyctYsuOLeUyQKMscDT
|
||||||
|
=hkUm
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
22
lang/js/BrowserTestExtension/longTests.html
Normal file
22
lang/js/BrowserTestExtension/longTests.html
Normal file
@ -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>
|
13
lang/js/BrowserTestExtension/manifest.json
Normal file
13
lang/js/BrowserTestExtension/manifest.json
Normal file
@ -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"]
|
||||||
|
}
|
9
lang/js/BrowserTestExtension/popup.html
Normal file
9
lang/js/BrowserTestExtension/popup.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="popup.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
30
lang/js/BrowserTestExtension/popup.js
Normal file
30
lang/js/BrowserTestExtension/popup.js
Normal file
@ -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'
|
||||||
|
});
|
||||||
|
});
|
26
lang/js/BrowserTestExtension/runbrowsertest.js
Normal file
26
lang/js/BrowserTestExtension/runbrowsertest.js
Normal file
@ -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();
|
27
lang/js/BrowserTestExtension/rununittests.js
Normal file
27
lang/js/BrowserTestExtension/rununittests.js
Normal file
@ -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();
|
28
lang/js/BrowserTestExtension/setup_testing.js
Normal file
28
lang/js/BrowserTestExtension/setup_testing.js
Normal file
@ -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;
|
BIN
lang/js/BrowserTestExtension/testicon.png
Normal file
BIN
lang/js/BrowserTestExtension/testicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
30
lang/js/BrowserTestExtension/testkey.pub
Normal file
30
lang/js/BrowserTestExtension/testkey.pub
Normal file
@ -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-----
|
57
lang/js/BrowserTestExtension/testkey.sec
Normal file
57
lang/js/BrowserTestExtension/testkey.sec
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
lQOYBFrsKEkBCADKw4Wt8J6M/88qD8PO6lSMCxH1cpwH8iK0uPaFFYsJkkXo7kWf
|
||||||
|
PTAtrV+REqF/o80dvYcdLvRsV21pvncZz/HXLu1yQ18mC3XObrKokbdgrTTKA5XE
|
||||||
|
BZkNsqyaMMJauT18H4hYkSg62/tTdO1cu/zWv/LFf7Xyn6+uA74ovXCJlO1s0N2c
|
||||||
|
PShtr98QRzPMf2owgVk37JnDNp4gGVDGHxSZOuUwxgYAZYnA8SFc+c+3ZrQfY870
|
||||||
|
+O4j3Mz4p7yD13AwP4buQLBsb/icxekeQCqpRJhLH9f7MdEcGXa1x36RcEkHdu+M
|
||||||
|
yJ392eMgD+dKNfRCtyTPhjZTxvbNELIBYICfABEBAAEAB/wLJ0gyMjs2fFfT83wM
|
||||||
|
5Lzz2yQIwV4t3bblBAujdHTqeN5Zmsm/oakFyjSokULK96Kv0R4ej9eoIgMFvxFk
|
||||||
|
HRkrggxTrbsNJ7I6QcKYHTPeIIj318ykNL6fj0WJUcdPIENukXl5jbqNyk3/4D2y
|
||||||
|
TTDySyq6jHTgvMH4K4KJUSpglvSJPntTk9RhuFGHAF+sNR9atygDYctAaERMRtSg
|
||||||
|
LCoSt/AoX5GRMlQjXT9oqQjwSQoZyF4s8HMC8wdTFIE/E0L4IVdHVp8sz2UszNtT
|
||||||
|
W/evmCA+KVruKjRH/Fhrq4hHkEamW28+j4L6uAyagONP7BONs+S5Oo2zTT9+tV2R
|
||||||
|
ILTZBADdgLuAgF6C5Lu9jCF6DfFgaT/uafMyQNkEGNlxOHMWHTgLHe475V2eG9gA
|
||||||
|
amd4yXKyEFKU1PWnvlGuicQSGdzVcwmq61msvXgYD0FK3LP3yWzKnE4X1tzrC9Vp
|
||||||
|
/uHJxKjewCuyt1f5in919v+T8TbUxBYKC0zX/qWtX+10cTx77QQA6leqhToJ95Yc
|
||||||
|
u4UBrKMEO+y2v8Svb3LG7yI5oY8tkw0EkJ/kpZ8xTAfZYCe6fXdvVE3PHg2lrxyc
|
||||||
|
Wv/EU3QY/qA3G82mbXYeJ2jNZaTNYo4MylMrt4Mx25x4ke7JlsE8SVrQ+4CrHkqp
|
||||||
|
OjSIa7fppLrQ78uW980AtN8NNQGrlTsD/A9aoA60Igxy1Q3K2uSyDCyjLknv57ym
|
||||||
|
ZSBD3/t7m0l6Q6gbdfhNGosT+Hd4y3actqEqzXZHW2VG4dKZ/wRNkxtSm9adU9vs
|
||||||
|
EHyzxjb6mKIH32zAG5TaFT20hC+NK6lsyHr9UE2ZrS6ma2sLxGW2O40hqNsdD+5m
|
||||||
|
NrqeBc2I/js1PMK0EHRlc3RAZXhhbXBsZS5vcmeJAVQEEwEIAD4WIQTUFzW5Ejb9
|
||||||
|
uIIEjFojAWNe7/DLBQUCWuwoSQIbAwUJA8JnAAULCQgHAgYVCgkICwIEFgIDAQIe
|
||||||
|
AQIXgAAKCRAjAWNe7/DLBf9kB/wOQ/S60HGwFq07W9N01HWULyhHKoMmcHL6rfZ6
|
||||||
|
4oDqLxolPSasz7WAMW1jN4qtWJ0mFzwO83V6kaBe+wF6Kqir6udFSBW9rPcFg6/V
|
||||||
|
ZXPltT0a6uacIHq6DyQ5iMW4YQWbVy9OR2rNGkYo1JCBR0XdRJYCSX3yB4TWv/eX
|
||||||
|
nZ37/WjmiTOIZh35rjs+NuU/S5JPDfAp2/k70DevQeBsv+UjVXjWpNTZmPbvDnd9
|
||||||
|
95uSmC6UY4hzyP84ORYMYn9n1QAR0goxDN6UunOf9Rlp1oMzdxMool/d1MlCxg2h
|
||||||
|
3jheuhv7lgUF4KpvHOuEPXQ7UO417E0TYcDZ1J8Nsv87SZeEnQOYBFrsKEkBCADj
|
||||||
|
oEBhG/QPqZHg8VyoD1xYRAWGxyDJkX/GrSs6yE+x2hk5FoQCajxKa/d4AVxOnJpd
|
||||||
|
whAfeXeSNaql5Ejgzax+Tdj9BV6vtGVJVv0pO7bgAiZxkA6RHxtNqhpPnPQoXvUz
|
||||||
|
kzpRgpuL+Nj4yIg7z1ITH6KQH4u5SI9vd+j/8i9Taz67pdZwuJjac8qBuJHjzAo1
|
||||||
|
bjYctFYUSG5pbmMQyNLySzgiNkFa4DajODlt3RuqVGP316Fk+Sy2+60tC/HlX8jg
|
||||||
|
MyMONfOGBQx6jk8tvAphS/LAqrrNepnagIyLUGKU+L8cB2g1PGGp2biBFWqZbudZ
|
||||||
|
oyRBet/0yH/zirBdQJw1ABEBAAEAB/4lN3gXOI4OuoOcsvHak4pebx61Mt0YP9cT
|
||||||
|
qZASIBqxok5x8E28pFh/tYfkYdqRCtdNYZOnxcEoUWh5j6nfwZkEnJ9P/T8GPNk7
|
||||||
|
pMKnKXmExi05b5uGHD8nU1rSbf/YkvAF0vpbxd4/RDxbbtQhbUwGzusSI+pBLM0w
|
||||||
|
5TreEB+vRGBc2gOvXXOtKLNEa7M9rH2EwbAkP3jOGGwgk6adxbQdBcRxq4merqhL
|
||||||
|
YrVz73bCj8TDc0fsNJyIaZZJ++ejfBFYavsF1pvx9z7FNFi8rSXoiB3SBtaWGfhr
|
||||||
|
bwNaMZrDc7TRIq/fgGaL6g//bzcWrr1YaHXZ10Bgx6UymDOlYkCpBADm0Hv46sPw
|
||||||
|
07SO8+IACcaQliOto1pndOPwTimCeo58/7rf8I2a5uuJloGrnPwAX65bKDnUALp6
|
||||||
|
X3lnXRNMhnB3Uewx4i00LQmjsxhJfQiGLpMv0j58tn64s7GqQzGVV1JKcQm992RV
|
||||||
|
jFOydyjZ+K4LGWEOITG/bZrMEVNGCM+OnQQA/Haz8xN0NFSlq7tyfFc0pkx/TiCX
|
||||||
|
xGfBqbO0wU2b5GMnZbY/06HENpidIzpa231VQaw5/nPTvfhlLKW1iGAkc148cX1q
|
||||||
|
lL9w2ksXuaHR3LXud2VcfVTIdxU/7h7u1dD/85+c0+7jlGObD9cXKxlM6OjpIJz1
|
||||||
|
l5/1h3C5S0TuxHkEAL/3BGihkhNfv1Xx0rWu0/732usX/nE/A9C26hGu41FUf3fp
|
||||||
|
0ilonKpKZUEwWt5hWSEFCSrznNVekiO0rxvuu3RVegvzThPNU4Pf4JZtJpRVhvUQ
|
||||||
|
d9ulxJw7V9rs75uNBatTNC0kXuGoXhehw4Bn93xa67gYGd3LfrH+oT0GCDpTSHCJ
|
||||||
|
ATwEGAEIACYWIQTUFzW5Ejb9uIIEjFojAWNe7/DLBQUCWuwoSQIbDAUJA8JnAAAK
|
||||||
|
CRAjAWNe7/DLBf0pCACPp5hBuUWngu2Hqvg+tNiujfsiYzId3MffFxEk3CbXeHcJ
|
||||||
|
5F32NDJ9PYCnra4L8wSv+NZt9gIa8lFwoFSFQCjzH7KE86XcV3MhfdJTNb/+9CR7
|
||||||
|
Jq3e/4Iy0N5ip7PNYMCyakcAsxvsNCJKrSaDuYe/OAoTXRBtgRWE2uyT315em02L
|
||||||
|
kr+2Cc/Qk6H+vlNOHGRgnpI/OZZjnUuUfBUvMGHr1phW+y7aeymC9PnUGdViRdJe
|
||||||
|
23nntMSDA+0/I7ESO9JsWvJbyBmuiZpu9JjScOjYH9xpQLqRNyw4WHpZriN69F0t
|
||||||
|
9Mmd7bM1+UyPgbPEr0iWMeyctYsuOLeUyQKMscDT
|
||||||
|
=hkUm
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
30
lang/js/BrowserTestExtension/testkey2.pub
Normal file
30
lang/js/BrowserTestExtension/testkey2.pub
Normal file
@ -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-----
|
149
lang/js/BrowserTestExtension/tests/KeyImportExport.js
Normal file
149
lang/js/BrowserTestExtension/tests/KeyImportExport.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/* 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();
|
||||||
|
prm.then(function (gpgmejs){
|
||||||
|
context = gpgmejs;
|
||||||
|
context.Keyring.getKeys(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(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(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(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(key_secret, 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
57
lang/js/BrowserTestExtension/tests/KeyInfos.js
Normal file
57
lang/js/BrowserTestExtension/tests/KeyInfos.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* 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();
|
||||||
|
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(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);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
78
lang/js/BrowserTestExtension/tests/decryptTest.js
Normal file
78
lang/js/BrowserTestExtension/tests/decryptTest.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/* 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*/
|
||||||
|
|
||||||
|
describe('Decryption', function () {
|
||||||
|
let context = null;
|
||||||
|
const good_fpr = inputvalues.encrypt.good.fingerprint;
|
||||||
|
|
||||||
|
before(function (done){
|
||||||
|
const prm = Gpgmejs.init();
|
||||||
|
prm.then(function (gpgmejs){
|
||||||
|
context = gpgmejs;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Decryption of random string fails', function (done) {
|
||||||
|
let data = bigString(20 * 1024);
|
||||||
|
context.decrypt(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, good_fpr).then(function (enc){
|
||||||
|
context.decrypt(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).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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
170
lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
Normal file
170
lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/* 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();
|
||||||
|
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, 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(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).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, 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(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, 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(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(b64data, good_fpr, true).then(function (answer) {
|
||||||
|
expect(answer).to.not.be.empty;
|
||||||
|
expect(answer.data).to.be.a('string');
|
||||||
|
expect(answer.data).to.include(
|
||||||
|
'BEGIN PGP MESSAGE');
|
||||||
|
expect(answer.data).to.include(
|
||||||
|
'END PGP MESSAGE');
|
||||||
|
context.decrypt(answer.data).then(
|
||||||
|
function (result) {
|
||||||
|
expect(result).to.not.be.empty;
|
||||||
|
expect(result.data).to.be.a('string');
|
||||||
|
expect(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(b64data, good_fpr, true).then(function (answer) {
|
||||||
|
expect(answer).to.not.be.empty;
|
||||||
|
expect(answer.data).to.be.a('string');
|
||||||
|
expect(answer.data).to.include(
|
||||||
|
'BEGIN PGP MESSAGE');
|
||||||
|
expect(answer.data).to.include(
|
||||||
|
'END PGP MESSAGE');
|
||||||
|
context.decrypt(answer.data).then(
|
||||||
|
function (result) {
|
||||||
|
expect(result).to.not.be.empty;
|
||||||
|
expect(result.data).to.be.a('string');
|
||||||
|
expect(result.data).to.equal(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,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(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);
|
||||||
|
}
|
||||||
|
});
|
113
lang/js/BrowserTestExtension/tests/encryptTest.js
Normal file
113
lang/js/BrowserTestExtension/tests/encryptTest.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/* 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 */
|
||||||
|
|
||||||
|
describe('Encryption', function () {
|
||||||
|
let context = null;
|
||||||
|
const good_fpr = inputvalues.encrypt.good.fingerprint;
|
||||||
|
before(function (done){
|
||||||
|
const prm = Gpgmejs.init();
|
||||||
|
prm.then(function (gpgmejs){
|
||||||
|
context = gpgmejs;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Successful encrypt', function (done) {
|
||||||
|
const data = inputvalues.encrypt.good.data;
|
||||||
|
context.encrypt(data, 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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, 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,null).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(null, 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, 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, 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);
|
||||||
|
|
||||||
|
// TODO check different valid parameter
|
||||||
|
});
|
338
lang/js/BrowserTestExtension/tests/inputvalues.js
Normal file
338
lang/js/BrowserTestExtension/tests/inputvalues.js
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
56
lang/js/BrowserTestExtension/tests/longRunningTests.js
Normal file
56
lang/js/BrowserTestExtension/tests/longRunningTests.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* gpgme.js - Javascript integration for gpgme
|
||||||
|
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
|
||||||
|
*
|
||||||
|
* This file is part of GPGME.
|
||||||
|
*
|
||||||
|
* GPGME is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2.1 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GPGME is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
prm.then(function (gpgmejs){
|
||||||
|
context = gpgmejs;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i=0; i < 101; i++) {
|
||||||
|
it('Successful encrypt/decrypt completely random data '
|
||||||
|
+ (i+1) + '/100', function (done) {
|
||||||
|
const data = bigString(2*1024*1024);
|
||||||
|
context.encrypt(data,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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
63
lang/js/BrowserTestExtension/tests/signTest.js
Normal file
63
lang/js/BrowserTestExtension/tests/signTest.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/* 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();
|
||||||
|
prm.then(function (gpgmejs){
|
||||||
|
context = gpgmejs;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Sign a message', function (done) {
|
||||||
|
const data = bigString(100);
|
||||||
|
context.sign(data, 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,good_fpr, '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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
47
lang/js/BrowserTestExtension/tests/startup.js
Normal file
47
lang/js/BrowserTestExtension/tests/startup.js
Normal file
@ -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();
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
90
lang/js/BrowserTestExtension/tests/verifyTest.js
Normal file
90
lang/js/BrowserTestExtension/tests/verifyTest.js
Normal file
@ -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();
|
||||||
|
prm.then(function (gpgmejs){
|
||||||
|
context = gpgmejs;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('Successful verify message', function (done) {
|
||||||
|
const message = inputvalues.signedMessage.good;
|
||||||
|
context.verify(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(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(message, fpr).then(function (message_enc){
|
||||||
|
context.sign(message_enc.data, fpr).then(function (message_encsign){
|
||||||
|
context.verify(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);
|
||||||
|
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.equal(fpr);
|
||||||
|
expect(result.signatures.signatures.good[0].valid)
|
||||||
|
.to.be.true;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
17
lang/js/BrowserTestExtension/unittests.html
Normal file
17
lang/js/BrowserTestExtension/unittests.html
Normal file
@ -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>
|
27
lang/js/DemoExtension/Makefile.am
Normal file
27
lang/js/DemoExtension/Makefile.am
Normal file
@ -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
|
30
lang/js/DemoExtension/entry.js
Normal file
30
lang/js/DemoExtension/entry.js
Normal file
@ -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'
|
||||||
|
});
|
||||||
|
});
|
119
lang/js/DemoExtension/maindemo.js
Normal file
119
lang/js/DemoExtension/maindemo.js
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/* 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, keyId).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).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, 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).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(data, true, 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
47
lang/js/DemoExtension/mainui.html
Normal file
47
lang/js/DemoExtension/mainui.html
Normal file
@ -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>
|
||||||
|
<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>
|
14
lang/js/DemoExtension/manifest.json
Normal file
14
lang/js/DemoExtension/manifest.json
Normal file
@ -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"]
|
||||||
|
}
|
9
lang/js/DemoExtension/popup.html
Normal file
9
lang/js/DemoExtension/popup.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="entry.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
lang/js/DemoExtension/testicon.png
Normal file
BIN
lang/js/DemoExtension/testicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
33
lang/js/DemoExtension/ui.css
Normal file
33
lang/js/DemoExtension/ui.css
Normal file
@ -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;
|
||||||
|
}
|
31
lang/js/Makefile.am
Normal file
31
lang/js/Makefile.am
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# 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 \
|
||||||
|
.eslintrc.json \
|
||||||
|
package.json \
|
||||||
|
README \
|
||||||
|
unittest_inputvalues.js \
|
||||||
|
unittests.js \
|
||||||
|
webpack.conf.js \
|
||||||
|
webpack.conf_unittests.js
|
116
lang/js/README
Normal file
116
lang/js/README
Normal file
@ -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`.
|
17
lang/js/build_extensions.sh
Executable file
17
lang/js/build_extensions.sh
Executable file
@ -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
|
24
lang/js/jsdoc.conf
Normal file
24
lang/js/jsdoc.conf
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"tags": {
|
||||||
|
"allowUnknownTags": false,
|
||||||
|
"dictionaries": ["jsdoc"]
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"include": ["./src"],
|
||||||
|
"includePattern": ".+\\.js(doc|x)?$",
|
||||||
|
"excludePattern": "(^|\\/|\\\\)_"
|
||||||
|
},
|
||||||
|
"opts":{
|
||||||
|
"destination": "./doc/",
|
||||||
|
"recurse": true
|
||||||
|
},
|
||||||
|
"sourceType": "module",
|
||||||
|
"plugins": [],
|
||||||
|
"templates": {
|
||||||
|
"cleverLinks": false,
|
||||||
|
"monospaceLinks": false,
|
||||||
|
"default": {
|
||||||
|
"outputSourceFiles": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
lang/js/package.json
Normal file
17
lang/js/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
283
lang/js/src/Connection.js
Normal file
283
lang/js/src/Connection.js
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
/* 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 } 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
|
||||||
|
*/
|
||||||
|
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:
|
||||||
|
* {
|
||||||
|
* "protocol": "OpenPGP",
|
||||||
|
* "fname": "/usr/bin/gpg",
|
||||||
|
* "version": "2.2.6",
|
||||||
|
* "req_version": "1.4.0",
|
||||||
|
* "homedir": "default"
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the information about the backend.
|
||||||
|
* @param {Boolean} details (optional) If set to false, the promise will
|
||||||
|
* just return if a connection was successful.
|
||||||
|
* @returns {Promise<backEndDetails>|Promise<Boolean>} Details from the
|
||||||
|
* backend
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
checkConnection (details = true){
|
||||||
|
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'));
|
||||||
|
}, 500);
|
||||||
|
})
|
||||||
|
]).then(function (){ // success
|
||||||
|
resolve(true);
|
||||||
|
}, function (){ // failure
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a {@link GPGME_Message} via tghe 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<Object>} The collected answer
|
||||||
|
* @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.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the base64 encoded answer data with the content verified
|
||||||
|
* against {@link permittedOperations}.
|
||||||
|
*/
|
||||||
|
getMessage (){
|
||||||
|
if (this._response_b64 === null){
|
||||||
|
return gpgme_error('CONN_UNEXPECTED_ANSWER');
|
||||||
|
}
|
||||||
|
let _decodedResponse = JSON.parse(atob(this._response_b64));
|
||||||
|
let _response = {};
|
||||||
|
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:
|
||||||
|
if (!poa.data.hasOwnProperty(key)){
|
||||||
|
return gpgme_error('CONN_UNEXPECTED_ANSWER');
|
||||||
|
}
|
||||||
|
if ( typeof (_decodedResponse[key]) !== poa.data[key] ){
|
||||||
|
return gpgme_error('CONN_UNEXPECTED_ANSWER');
|
||||||
|
}
|
||||||
|
if (_decodedResponse.base64 === true
|
||||||
|
&& poa.data[key] === 'string'
|
||||||
|
&& this.expected !== 'base64'
|
||||||
|
){
|
||||||
|
_response[key] = decodeURIComponent(
|
||||||
|
atob(_decodedResponse[key]).split('').map(
|
||||||
|
function (c) {
|
||||||
|
return '%' +
|
||||||
|
('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
}).join(''));
|
||||||
|
} else {
|
||||||
|
_response[key] = decode(_decodedResponse[key]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _response;
|
||||||
|
}
|
||||||
|
}
|
169
lang/js/src/Errors.js
Normal file
169
lang/js/src/Errors.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/* 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'
|
||||||
|
},
|
||||||
|
'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 {*} code Error code. Should be in err_list or 'GNUPG_ERROR'
|
||||||
|
* @param {*} info Error message passed through if code is 'GNUPG_ERROR'
|
||||||
|
* @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
|
||||||
|
* @property {String} code Short description of origin and type of the error
|
||||||
|
* @property {String} msg Additional info
|
||||||
|
* @class
|
||||||
|
* @protected
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
137
lang/js/src/Helpers.js
Normal file
137
lang/js/src/Helpers.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/* 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 | Array<Object> | String | Array<String>} input
|
||||||
|
* @returns {Array<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].hasOwnProperty('fingerprint')){
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check 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
|
||||||
|
*/
|
||||||
|
export function decode (property){
|
||||||
|
if (typeof property === 'string'){
|
||||||
|
return decodeURIComponent(escape(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;
|
||||||
|
}
|
688
lang/js/src/Key.js
Normal file
688
lang/js/src/Key.js
Normal file
@ -0,0 +1,688 @@
|
|||||||
|
/* 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
|
||||||
|
* It allows to query almost all information defined in gpgme Key Objects
|
||||||
|
* Refer to {@link validKeyProperties} for available information, and the gpgme
|
||||||
|
* documentation on their meaning
|
||||||
|
* (https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class GPGME_Key {
|
||||||
|
|
||||||
|
constructor (fingerprint, async, data){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property {Boolean} If true, most answers will be asynchronous
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
* the value of the property. 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 {@link 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 you
|
||||||
|
* 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 {@link Keyring.getKeys} instead.
|
||||||
|
* @returns {Promise<GPGME_Key|GPGME_Error>}
|
||||||
|
* @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|GPGME_Error>}
|
||||||
|
* @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
|
||||||
|
* {@link Keyring.getKeys} instead, as it performs faster in bulk
|
||||||
|
* querying this state.
|
||||||
|
* @returns {Promise<Boolean|GPGME_Error>} 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.
|
||||||
|
* @returns {Promise<Boolean|GPGME_Error>} Success if key was deleted,
|
||||||
|
* rejects with a GPG error otherwise.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
* @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
|
||||||
|
* @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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
435
lang/js/src/Keyring.js
Normal file
435
lang/js/src/Keyring.js
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
/* 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 {String | Array<String>} pattern (optional) A pattern to
|
||||||
|
* search for in userIds or KeyIds.
|
||||||
|
* @param {Boolean} 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} search (optional) retrieve Keys from external
|
||||||
|
* servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup)
|
||||||
|
* @returns {Promise<Array<GPGME_Key>>}
|
||||||
|
* @static
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
getKeys (pattern, prepare_sync=false, search=false){
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let msg = createMessage('keylist');
|
||||||
|
if (pattern !== undefined && pattern !== null){
|
||||||
|
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 {String|Array<String>} pattern (optional) The Pattern to
|
||||||
|
* search for
|
||||||
|
* @param {Boolean} with_secret_fpr (optional) also return a list of
|
||||||
|
* fingerprints for the keys that have a secret key available
|
||||||
|
* @returns {Promise<exportResult|GPGME_Error>} 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 !== undefined && pattern !== null){
|
||||||
|
msg.setParameter('keys', pattern);
|
||||||
|
}
|
||||||
|
msg.post().then(function (answer){
|
||||||
|
const result = { armored: answer.data };
|
||||||
|
if (with_secret_fpr === true
|
||||||
|
&& answer.hasOwnProperty('sec-fprs')
|
||||||
|
) {
|
||||||
|
result.secret_fprs = answer['sec-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|GPGME_Error>}
|
||||||
|
* @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(resp.option.value[0].string, 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) {
|
||||||
|
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(fprs, 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|GPGME_Error>}
|
||||||
|
* @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 {String} userId The user Id, e.g. 'Foo Bar <foo@bar.baz>'
|
||||||
|
* @param {String} algo (optional) algorithm (and optionally key size)
|
||||||
|
* to be used. See {@link supportedKeyAlgos} below for supported
|
||||||
|
* values. If ommitted, 'default' is used.
|
||||||
|
* @param {Number} expires (optional) Expiration time in seconds from now.
|
||||||
|
* If not set or set to 0, expiration will be 'never'
|
||||||
|
* @param {String} subkey_algo (optional) algorithm of the encryption
|
||||||
|
* subkey. If ommited the same as algo is used.
|
||||||
|
*
|
||||||
|
* @return {Promise<Key|GPGME_Error>}
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
generateKey (userId, algo = 'default', expires, subkey_algo){
|
||||||
|
if (
|
||||||
|
typeof (userId) !== 'string' ||
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
supportedKeyAlgos.indexOf(algo) < 0 ||
|
||||||
|
(expires && !( Number.isInteger(expires) || expires < 0 ))
|
||||||
|
){
|
||||||
|
return Promise.reject(gpgme_error('PARAM_WRONG'));
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
if (subkey_algo && supportedKeyAlgos.indexOf(subkey_algo) < 0 ){
|
||||||
|
return Promise.reject(gpgme_error('PARAM_WRONG'));
|
||||||
|
}
|
||||||
|
let me = this;
|
||||||
|
return new Promise(function (resolve, reject){
|
||||||
|
let msg = createMessage('createkey');
|
||||||
|
msg.setParameter('userid', userId);
|
||||||
|
msg.setParameter('algo', algo );
|
||||||
|
if (subkey_algo) {
|
||||||
|
msg.setParameter('subkey-algo', subkey_algo );
|
||||||
|
}
|
||||||
|
if (expires){
|
||||||
|
msg.setParameter('expires', expires);
|
||||||
|
} else {
|
||||||
|
msg.setParameter('expires', 0);
|
||||||
|
}
|
||||||
|
msg.post().then(function (response){
|
||||||
|
me.getKeys(response.fingerprint, true).then(
|
||||||
|
// TODO prepare_sync?
|
||||||
|
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',
|
||||||
|
'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'
|
||||||
|
];
|
30
lang/js/src/Makefile.am
Normal file
30
lang/js/src/Makefile.am
Normal file
@ -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
|
239
lang/js/src/Message.js
Normal file
239
lang/js/src/Message.js
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
/* 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
|
||||||
|
* {@link Connection.post}. The definition on which communication is available
|
||||||
|
* can be found in {@link permittedOperations}.
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
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 === 'base64'){
|
||||||
|
this._expected = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get expected () {
|
||||||
|
return this._expected;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The maximum size of responses from gpgme in bytes. As of July 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 with parameters and completeness checked
|
||||||
|
* @returns {Object|null} Object to be posted to gnupg, or null if
|
||||||
|
* incomplete
|
||||||
|
*/
|
||||||
|
get message () {
|
||||||
|
if (this.isComplete() === true){
|
||||||
|
return this._msg;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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} If the parameter was set successfully
|
||||||
|
*/
|
||||||
|
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_Error>}
|
||||||
|
* @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'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
200
lang/js/src/Signature.js
Normal file
200
lang/js/src/Signature.js
Normal file
@ -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>
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
get fingerprint (){
|
||||||
|
if (!this._rawSigObject.fingerprint){
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gives more information on non-valid signatures. Refer to the gpgme
|
||||||
|
* docs https://www.gnupg.org/documentation/manuals/gpgme/Verify.html
|
||||||
|
* for details on the values.
|
||||||
|
* @returns {Object} Object with boolean properties
|
||||||
|
*/
|
||||||
|
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.hasOwnProperty(properties[i]) ){
|
||||||
|
result[properties[i]] = this._rawSigObject[properties[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys and their value's type for the signature Object
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
const expNote = {
|
||||||
|
'human_readable': 'boolean',
|
||||||
|
'critical':'boolean',
|
||||||
|
'name': 'string',
|
||||||
|
'value': 'string',
|
||||||
|
'flags': 'number'
|
||||||
|
};
|
391
lang/js/src/gpgmejs.js
Normal file
391
lang/js/src/gpgmejs.js
Normal file
@ -0,0 +1,391 @@
|
|||||||
|
/* 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} data The decrypted data
|
||||||
|
* @property {Boolean} base64 indicating whether data is base64 encoded.
|
||||||
|
* @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 Summary if all signatures are fully valid
|
||||||
|
* @property {Number} count Number of signatures found
|
||||||
|
* @property {Number} failures Number of invalid signatures
|
||||||
|
* @property {Array<GPGME_Signature>} signatures.good All valid signatures
|
||||||
|
* @property {Array<GPGME_Signature>} signatures.bad All invalid signatures
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} encrypt_result The result of an encrypt operation
|
||||||
|
* @property {String} data The encrypted message
|
||||||
|
* @property {Boolean} base64 Indicating whether data is base64 encoded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 (if 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 {String} file_name (optional) the original file name
|
||||||
|
* @property {signatureDetails} signatures Verification details for
|
||||||
|
* signatures
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for gpgme.js.
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
export class GpgME {
|
||||||
|
|
||||||
|
constructor (){
|
||||||
|
this._Keyring = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setter for {@link setKeyring}.
|
||||||
|
* @param {GPGME_Keyring} keyring A Keyring to use
|
||||||
|
*/
|
||||||
|
set Keyring (keyring){
|
||||||
|
if (keyring && keyring instanceof GPGME_Keyring){
|
||||||
|
this._Keyring = keyring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Accesses the {@link GPGME_Keyring}.
|
||||||
|
*/
|
||||||
|
get Keyring (){
|
||||||
|
if (!this._Keyring){
|
||||||
|
this._Keyring = new GPGME_Keyring;
|
||||||
|
}
|
||||||
|
return this._Keyring;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt (and optionally sign) data
|
||||||
|
* @param {String|Object} data text/data to be encrypted as String. Also
|
||||||
|
* accepts Objects with a getText method
|
||||||
|
* @param {inputKeys} publicKeys
|
||||||
|
* Keys used to encrypt the message
|
||||||
|
* @param {inputKeys} secretKeys (optional) Keys used to sign the
|
||||||
|
* message. If Keys are present, the operation requested is assumed
|
||||||
|
* to be 'encrypt and sign'
|
||||||
|
* @param {Boolean} base64 (optional) The data will be interpreted as
|
||||||
|
* base64 encoded data.
|
||||||
|
* @param {Boolean} armor (optional) Request the output as armored
|
||||||
|
* block.
|
||||||
|
* @param {Boolean} wildcard (optional) If true, recipient information
|
||||||
|
* will not be added to the message.
|
||||||
|
* @param {Object} 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=false, additional = {}){
|
||||||
|
let msg = createMessage('encrypt');
|
||||||
|
if (msg instanceof Error){
|
||||||
|
return Promise.reject(msg);
|
||||||
|
}
|
||||||
|
msg.setParameter('armor', armor);
|
||||||
|
msg.setParameter('always-trust', true);
|
||||||
|
if (base64 === true) {
|
||||||
|
msg.setParameter('base64', true);
|
||||||
|
}
|
||||||
|
let pubkeys = toKeyIdArray(publicKeys);
|
||||||
|
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 a Message
|
||||||
|
* @param {String|Object} data text/data to be decrypted. Accepts
|
||||||
|
* Strings and Objects with a getText method
|
||||||
|
* @param {Boolean} base64 (optional) false if the data is an armored
|
||||||
|
* block, true if it is base64 encoded binary data
|
||||||
|
* @returns {Promise<decrypt_result>} Decrypted Message and information
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
decrypt (data, base64=false){
|
||||||
|
if (data === undefined){
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
putData(msg, data);
|
||||||
|
return new Promise(function (resolve, reject){
|
||||||
|
msg.post().then(function (result){
|
||||||
|
let _result = { data: result.data };
|
||||||
|
_result.base64 = result.base64 ? true: false;
|
||||||
|
if (result.hasOwnProperty('dec_info')){
|
||||||
|
_result.is_mime = result.dec_info.is_mime ? true: false;
|
||||||
|
if (result.dec_info.file_name) {
|
||||||
|
_result.file_name = result.dec_info.file_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!result.file_name) {
|
||||||
|
_result.file_name = null;
|
||||||
|
}
|
||||||
|
if (result.hasOwnProperty('info')
|
||||||
|
&& result.info.hasOwnProperty('signatures')
|
||||||
|
&& Array.isArray(result.info.signatures)
|
||||||
|
) {
|
||||||
|
_result.signatures = collectSignatures(
|
||||||
|
result.info.signatures);
|
||||||
|
}
|
||||||
|
if (_result.signatures instanceof Error){
|
||||||
|
reject(_result.signatures);
|
||||||
|
} else {
|
||||||
|
resolve(_result);
|
||||||
|
}
|
||||||
|
}, function (error){
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a Message
|
||||||
|
* @param {String|Object} data text/data to be signed. Accepts Strings
|
||||||
|
* and Objects with a getText method.
|
||||||
|
* @param {inputKeys} keys The key/keys to use for signing
|
||||||
|
* @param {String} mode The signing mode. Currently supported:
|
||||||
|
* 'clearsign':The Message is embedded into the signature;
|
||||||
|
* 'detached': The signature is stored separately
|
||||||
|
* @param {Boolean} base64 input is considered base64
|
||||||
|
* @returns {Promise<signResult>}
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
sign (data, keys, mode='clearsign', base64=false) {
|
||||||
|
if (data === undefined){
|
||||||
|
return Promise.reject(gpgme_error('MSG_EMPTY'));
|
||||||
|
}
|
||||||
|
let key_arr = toKeyIdArray(keys);
|
||||||
|
if (key_arr.length === 0){
|
||||||
|
return Promise.reject(gpgme_error('MSG_NO_KEYS'));
|
||||||
|
}
|
||||||
|
let msg = createMessage('sign');
|
||||||
|
|
||||||
|
msg.setParameter('keys', key_arr);
|
||||||
|
if (base64 === true){
|
||||||
|
msg.setParameter('base64', true);
|
||||||
|
}
|
||||||
|
msg.setParameter('mode', mode);
|
||||||
|
putData(msg, data);
|
||||||
|
return new Promise(function (resolve,reject) {
|
||||||
|
if (mode ==='detached'){
|
||||||
|
msg.expected ='base64';
|
||||||
|
}
|
||||||
|
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 {String|Object} data text/data to be verified. Accepts Strings
|
||||||
|
* and Objects with a getText method
|
||||||
|
* @param {String} (optional) A detached signature. If not present,
|
||||||
|
* opaque mode is assumed
|
||||||
|
* @param {Boolean} (optional) Data and signature are base64 encoded
|
||||||
|
* @returns {Promise<verifyResult>}
|
||||||
|
*@async
|
||||||
|
*/
|
||||||
|
verify (data, signature, base64 = false){
|
||||||
|
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 _result = {
|
||||||
|
signatures: collectSignatures(message.info.signatures)
|
||||||
|
};
|
||||||
|
if (_result.signatures instanceof Error){
|
||||||
|
reject(_result.signatures);
|
||||||
|
} else {
|
||||||
|
_result.is_mime = message.info.is_mime? true: false;
|
||||||
|
if (message.info.filename){
|
||||||
|
_result.file_name = message.info.filename;
|
||||||
|
}
|
||||||
|
_result.data = message.data;
|
||||||
|
resolve(_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
52
lang/js/src/index.js
Normal file
52
lang/js/src/index.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* 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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes gpgme.js by testing the nativeMessaging connection once.
|
||||||
|
* @returns {Promise<GpgME> | GPGME_Error}
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
function init (){
|
||||||
|
return new Promise(function (resolve, reject){
|
||||||
|
const connection = new Connection;
|
||||||
|
connection.checkConnection(false).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;
|
403
lang/js/src/permittedOperations.js
Normal file
403
lang/js/src/permittedOperations.js
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
/* 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.data key-value combinations of expected properties
|
||||||
|
* of an answer and their type ('boolean', 'string', object)
|
||||||
|
@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']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer: {
|
||||||
|
type: ['ciphertext'],
|
||||||
|
data: {
|
||||||
|
'data': 'string',
|
||||||
|
'base64':'boolean'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
decrypt: {
|
||||||
|
pinentry: true,
|
||||||
|
required: {
|
||||||
|
'data': {
|
||||||
|
allowed: ['string']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optional: {
|
||||||
|
'protocol': {
|
||||||
|
allowed: ['string'],
|
||||||
|
allowed_data: ['cms', 'openpgp']
|
||||||
|
},
|
||||||
|
'base64': {
|
||||||
|
allowed: ['boolean']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer: {
|
||||||
|
type: ['plaintext'],
|
||||||
|
data: {
|
||||||
|
'data': 'string',
|
||||||
|
'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'],
|
||||||
|
data: {
|
||||||
|
'data': 'string',
|
||||||
|
'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'],
|
||||||
|
data: {
|
||||||
|
'base64': 'boolean',
|
||||||
|
'keys': 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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'],
|
||||||
|
data: {
|
||||||
|
'data': 'string',
|
||||||
|
'base64': 'boolean',
|
||||||
|
'sec-fprs': 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
import: {
|
||||||
|
required: {
|
||||||
|
'data': {
|
||||||
|
allowed: ['string']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optional: {
|
||||||
|
'protocol': {
|
||||||
|
allowed: ['string'],
|
||||||
|
allowed_data: ['cms', 'openpgp']
|
||||||
|
},
|
||||||
|
'base64': {
|
||||||
|
allowed: ['boolean']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
answer: {
|
||||||
|
type: [],
|
||||||
|
data: {
|
||||||
|
'result': 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: {
|
||||||
|
pinentry: true,
|
||||||
|
required:{
|
||||||
|
'key': {
|
||||||
|
allowed: ['string']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optional: {
|
||||||
|
'protocol': {
|
||||||
|
allowed: ['string'],
|
||||||
|
allowed_data: ['cms', 'openpgp']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
answer: {
|
||||||
|
data: {
|
||||||
|
'success': 'boolean'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
version: {
|
||||||
|
required: {},
|
||||||
|
optional: {},
|
||||||
|
answer: {
|
||||||
|
type: [''],
|
||||||
|
data: {
|
||||||
|
'gpgme': 'string',
|
||||||
|
'info': 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createkey: {
|
||||||
|
pinentry: true,
|
||||||
|
required: {
|
||||||
|
userid: {
|
||||||
|
allowed: ['string']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optional: {
|
||||||
|
algo: {
|
||||||
|
allowed: ['string']
|
||||||
|
},
|
||||||
|
'subkey-algo': {
|
||||||
|
allowed: ['string']
|
||||||
|
},
|
||||||
|
expires: {
|
||||||
|
allowed: ['number'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer: {
|
||||||
|
type: [''],
|
||||||
|
data: { 'fingerprint': 'string' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
verify: {
|
||||||
|
required: {
|
||||||
|
data: {
|
||||||
|
allowed: ['string']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optional: {
|
||||||
|
'protocol': {
|
||||||
|
allowed: ['string'],
|
||||||
|
allowed_data: ['cms', 'openpgp']
|
||||||
|
},
|
||||||
|
'signature': {
|
||||||
|
allowed: ['string']
|
||||||
|
},
|
||||||
|
'base64':{
|
||||||
|
allowed: ['boolean']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer: {
|
||||||
|
type: ['plaintext'],
|
||||||
|
data:{
|
||||||
|
data: 'string',
|
||||||
|
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: [],
|
||||||
|
data: {
|
||||||
|
option: 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TBD handling of secrets
|
||||||
|
* TBD key modification?
|
||||||
|
*/
|
||||||
|
|
||||||
|
};
|
123
lang/js/unittest_inputvalues.js
Normal file
123
lang/js/unittest_inputvalues.js
Normal file
@ -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' +
|
||||||
|
'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'
|
||||||
|
};
|
379
lang/js/unittests.js
Normal file
379
lang/js/unittests.js
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
/* 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;
|
||||||
|
|
||||||
|
function unittests (){
|
||||||
|
describe('Connection testing', function (){
|
||||||
|
|
||||||
|
it('Connecting', function (done) {
|
||||||
|
let conn0 = new Connection;
|
||||||
|
conn0.checkConnection().then(function (answer) {
|
||||||
|
expect(answer).to.not.be.empty;
|
||||||
|
expect(answer.gpgme).to.not.be.undefined;
|
||||||
|
expect(answer.gpgme).to.be.a('string');
|
||||||
|
expect(answer.info).to.be.an('Array');
|
||||||
|
expect(conn0.disconnect).to.be.a('function');
|
||||||
|
expect(conn0.post).to.be.a('function');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Disconnecting', function (done) {
|
||||||
|
let conn0 = new Connection;
|
||||||
|
conn0.checkConnection(false).then(function (answer) {
|
||||||
|
expect(answer).to.be.true;
|
||||||
|
conn0.disconnect();
|
||||||
|
conn0.checkConnection(false).then(function (result) {
|
||||||
|
expect(result).to.be.false;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Error Object handling', function (){
|
||||||
|
// TODO: new GPGME_Error codes
|
||||||
|
it('check the Timeout error', function (){
|
||||||
|
let test0 = gpgme_error('CONN_TIMEOUT');
|
||||||
|
|
||||||
|
expect(test0).to.be.an.instanceof(Error);
|
||||||
|
expect(test0.code).to.equal('CONN_TIMEOUT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Error Object returns generic code if code is not listed',
|
||||||
|
function (){
|
||||||
|
let test0 = gpgme_error(hp.invalidErrorCode);
|
||||||
|
|
||||||
|
expect(test0).to.be.an.instanceof(Error);
|
||||||
|
expect(test0.code).to.equal('GENERIC_ERROR');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Warnings like PARAM_IGNORED should not return errors', function (){
|
||||||
|
let test0 = gpgme_error('PARAM_IGNORED');
|
||||||
|
|
||||||
|
expect(test0).to.be.null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Fingerprint checking', function (){
|
||||||
|
|
||||||
|
it('isFingerprint(): valid Fingerprint', function (){
|
||||||
|
let test0 = isFingerprint(hp.validFingerprint);
|
||||||
|
|
||||||
|
expect(test0).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isFingerprint(): invalid Fingerprints', function (){
|
||||||
|
for (let i=0; i < hp.invalidFingerprints.length; i++){
|
||||||
|
let test0 = isFingerprint(hp.invalidFingerprints[i]);
|
||||||
|
|
||||||
|
expect(test0).to.be.false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toKeyIdArray() (converting input to fingerprint)', function (){
|
||||||
|
|
||||||
|
it('Correct fingerprint string', function (){
|
||||||
|
let test0 = toKeyIdArray(hp.validFingerprint);
|
||||||
|
|
||||||
|
expect(test0).to.be.an('array');
|
||||||
|
expect(test0).to.include(hp.validFingerprint);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('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(null, 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(kp.validKeyFingerprint, 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(kp.invalidKeyFingerprint, 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 };
|
36
lang/js/webpack.conf.js
Normal file
36
lang/js/webpack.conf.js
Normal file
@ -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'
|
||||||
|
}
|
||||||
|
};
|
36
lang/js/webpack.conf_unittests.js
Normal file
36
lang/js/webpack.conf_unittests.js
Normal file
@ -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'
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user