diff options
author | Justus Winter <justus@g10code.com> | 2016-01-07 17:01:45 +0100 |
---|---|---|
committer | Justus Winter <justus@g10code.com> | 2016-06-17 11:57:12 +0200 |
commit | 9609cb20e4caee739b9fc4fd36797029d2970041 (patch) | |
tree | 052eaa0ad4afda93f4bd7eec4c90886cf72f778e /tests/openpgp | |
parent | tests/gpgscm: Add a TinySCHEME-based test driver. (diff) | |
download | gnupg2-9609cb20e4caee739b9fc4fd36797029d2970041.tar.xz gnupg2-9609cb20e4caee739b9fc4fd36797029d2970041.zip |
tests/openpgp: Reimplement tests in Scheme.
* Makefile.am: Build the test infrastructure on Windows.
* tests/openpgp/Makefile.am (required_pgms): Add gpgscm.
(TESTS_ENVIRONMENT): Make sure gpgscm and the libraries are found.
(TESTS): Replace tests with the new Scheme implementations.
* tests/openpgp/4gb-packet.scm: New file.
* tests/openpgp/README: Likewise.
* tests/openpgp/armdetach.scm: Likewise.
* tests/openpgp/armdetachm.scm: Likewise.
* tests/openpgp/armencrypt.scm: Likewise.
* tests/openpgp/armencryptp.scm: Likewise.
* tests/openpgp/armor.scm: Likewise.
* tests/openpgp/armsignencrypt.scm: Likewise.
* tests/openpgp/armsigs.scm: Likewise.
* tests/openpgp/clearsig.scm: Likewise.
* tests/openpgp/conventional-mdc.scm: Likewise.
* tests/openpgp/conventional.scm: Likewise.
* tests/openpgp/decrypt-dsa.scm: Likewise.
* tests/openpgp/decrypt.scm: Likewise.
* tests/openpgp/default-key.scm: Likewise.
* tests/openpgp/defs.scm: Likewise.
* tests/openpgp/detach.scm: Likewise.
* tests/openpgp/detachm.scm: Likewise.
* tests/openpgp/ecc.scm: Likewise.
* tests/openpgp/encrypt-dsa.scm: Likewise.
* tests/openpgp/encrypt.scm: Likewise.
* tests/openpgp/encryptp.scm: Likewise.
* tests/openpgp/finish.scm: Likewise.
* tests/openpgp/genkey1024.scm: Likewise.
* tests/openpgp/gpgtar.scm: Likewise.
* tests/openpgp/import.scm: Likewise.
* tests/openpgp/mds.scm: Likewise.
* tests/openpgp/multisig.scm: Likewise.
* tests/openpgp/run-tests.scm: Likewise.
* tests/openpgp/seat.scm: Likewise.
* tests/openpgp/setup.scm: Likewise.
* tests/openpgp/signencrypt-dsa.scm: Likewise.
* tests/openpgp/signencrypt.scm: Likewise.
* tests/openpgp/sigs-dsa.scm: Likewise.
* tests/openpgp/sigs.scm: Likewise.
* tests/openpgp/use-exact-key.scm: Likewise.
* tests/openpgp/verify.scm: Likewise.
* tests/openpgp/version.scm: Likewise.
Signed-off-by: Justus Winter <justus@g10code.com>
Diffstat (limited to 'tests/openpgp')
39 files changed, 3473 insertions, 17 deletions
diff --git a/tests/openpgp/4gb-packet.scm b/tests/openpgp/4gb-packet.scm new file mode 100755 index 000000000..8b2fcd6ca --- /dev/null +++ b/tests/openpgp/4gb-packet.scm @@ -0,0 +1,27 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +;; GnuPG through 2.1.7 would incorrect mark packets whose size is +;; 2^32-1 as invalid and exit with status code 2. + +(load (with-path "defs.scm")) + +(if (= 0 (call `(,@GPG --list-packets ,(in-srcdir "4gb-packet.asc")))) + (info "Can parse 4GB packets.") + (error "Failed to parse 4GB packet.")) diff --git a/tests/openpgp/Makefile.am b/tests/openpgp/Makefile.am index bb1047d5e..f1fdad7ef 100644 --- a/tests/openpgp/Makefile.am +++ b/tests/openpgp/Makefile.am @@ -22,7 +22,8 @@ # Programs required before we can run these tests. required_pgms = ../../g10/gpg$(EXEEXT) ../../agent/gpg-agent$(EXEEXT) \ ../../tools/gpg-connect-agent$(EXEEXT) \ - ../../tools/mk-tdata$(EXEEXT) + ../../tools/mk-tdata$(EXEEXT) \ + ../gpgscm/gpgscm$(EXEEXT) AM_CPPFLAGS = -I$(top_srcdir)/common include $(top_srcdir)/am/cmacros.am @@ -33,7 +34,11 @@ noinst_PROGRAMS = fake-pinentry fake_pinentry_SOURCES = fake-pinentry.c -TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO= LC_ALL=C +TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO= LC_ALL=C \ + EXEEXT=$(EXEEXT) \ + PATH=../gpgscm:$(PATH) \ + objdir=$(shell readlink -f ../..) \ + GPGSCM_PATH=$(top_srcdir)/tests/gpgscm:$(top_srcdir)/tests/openpgp if SQLITE3 sqlite3_dependent_tests = tofu.test @@ -41,24 +46,45 @@ else sqlite3_dependent_tests = endif -# Note: version.test needs to be the first test to run and finish.test +# Note: setup.scm needs to be the first test to run and finish.scm # the last one -TESTS = version.test mds.test \ - decrypt.test decrypt-dsa.test \ - sigs.test sigs-dsa.test \ - encrypt.test encrypt-dsa.test \ - seat.test clearsig.test encryptp.test detach.test \ - armsigs.test armencrypt.test armencryptp.test \ - signencrypt.test signencrypt-dsa.test \ - armsignencrypt.test armdetach.test \ - armdetachm.test detachm.test genkey1024.test \ - conventional.test conventional-mdc.test \ - multisig.test verify.test armor.test \ - import.test ecc.test 4gb-packet.test \ +TESTS = setup.scm \ + version.scm \ + mds.scm \ + decrypt.scm \ + decrypt-dsa.scm \ + sigs.scm \ + sigs-dsa.scm \ + encrypt.scm \ + encrypt-dsa.scm \ + seat.scm \ + clearsig.scm \ + encryptp.scm \ + detach.scm \ + detachm.scm \ + armsigs.scm \ + armencrypt.scm \ + armencryptp.scm \ + signencrypt.scm \ + signencrypt-dsa.scm \ + armsignencrypt.scm \ + armdetach.scm \ + armdetachm.scm \ + genkey1024.scm \ + conventional.scm \ + conventional-mdc.scm \ + multisig.scm \ + verify.scm \ + armor.scm \ + import.scm \ + ecc.scm \ + 4gb-packet.scm \ $(sqlite3_dependent_tests) \ - gpgtar.test use-exact-key.test default-key.test \ + gpgtar.scm \ + use-exact-key.scm \ + default-key.scm \ export.test \ - finish.test + finish.scm TEST_FILES = pubring.asc secring.asc plain-1o.asc plain-2o.asc plain-3o.asc \ diff --git a/tests/openpgp/README b/tests/openpgp/README new file mode 100644 index 000000000..498d5f5d6 --- /dev/null +++ b/tests/openpgp/README @@ -0,0 +1,160 @@ +Emacs, this is an -*- org -*- file. + +* How to run the test suite +** using the legacy driver +On POSIX you can just use + + $ make -C tests/openpgp check + +or + + $ make -C tests/openpgp check TESTS="setup.scm your-test.scm finish.scm" + +as before. +** using the Scheme driver +This is a bit tricky because one needs to manually set some +environment variables. We should make that easier. See discussion +below. From your build directory, do: + + obj $ srcdir=<path to>/tests/openpgp \ + GPGSCM_PATH=<path to>/tests/gpgscm:<path to>/tests/openpgp \ + $(pwd)/tests/gpgscm/gpgscm [gpgscm args] \ + run-tests.scm [test suite runner args] + +*** Arguments supported by the test suite runner +The test suite runner supports four modes of operation, +{sequential,parallel}x{isolated,shared}. You can select the mode of +operation using a combination of the flags --parallel, --sequential, +--shared, and --isolated. + +By default the tests are run in sequential order, each one in a clean +environment. + +You can specify the tests to run as positional arguments relative to +srcdir (e.g. just 'version.scm'). By default all tests listed in +run-tests.scm are executed. Note that you do not have to specify +setup.scm and finish.scm, they are executed implicitly. + +The test suite runner can be executed in any location that the current +user can write to. It will create temporary files and directories, +but will in general clean up all of them. +*** Discussion of the various environment variables +**** srcdir +Must be set to the source of the openpgp test suite. Used to locate +data files. +**** GPGSCM_PATH +Used to locate the Scheme library as well as code used by the test +suite. +**** BIN_PREFIX +The test suite does not hardcode any paths to tools. If set it is +used to locate the tools to test, otherwise the test suite assumes to +be run from the build directory. +**** MKTDATA and GPG_PRESET_PASSPHRASE +These two tools are not installed by 'make install', hence we need to +explicitly override their position. In fact, the location of any tool +used by the test suite can be overridden this way. See defs.scm. +**** argv[0] +run-tests.scm depends on being able to re-exec gpgscm. It uses +argv[0] for that. Therefore you must use an absolute path to invoke +gpgscm. +* How to write tests +gpgscm provides a number of functions to aid you in writing tests, as +well as bindings to process management abstractions provided by GnuPG. +For the Scheme environment provided by TinySCHEME, see the TinySCHEME +manual that is included in tests/gpgscm/Manual.txt. + +For a quick start, please have a look at various tests that are +already implemented, e.g. 'encrypt.scm'. +** The test framework +The functions info, error, and skip display their first argument and +flush the output buffers. error and skip will also terminate the +process, signaling that the test failed or should be skipped. + +(for-each-p msg proc list) will display msg, and call proc with each +element of list while displaying the progress appropriately. +for-each-p' is similar, but accepts another callback before the 'list' +argument to format each item. for-each-p can be safely nested, and +the inner progress indicator will be abbreviated using '.'. +** Temporary files +(lettmp <bindings> <body>) will create and delete temporary files that +you can use in <body>. (with-temporary-working-directory <body>) will +create a temporary director, change to that, and clean it up after +executing <body>). + +make-temporary-file will create a temporary file. You can optionally +provide an argument to that function that will serve as tag so you can +distinguish the files for debugging. remove-temporary-file will +delete a file created using make-temporary-file. + +** Monadic transformer and pipe support +Tests often perform sequential transformations on files, or connect +processes using pipes. To aid you in this, the test framework +provides two monadic data structures. + +(Currently, the implementation mashes the 'bind' operation together +with the application of the monad. Also, there is no 'return' +operation. I guess all of that could be implemented on top of +call/cc, but it isn't at the moment.) +*** pipe +The pipe monad constructs pipe lines. It consists of a function +pipe:do that binds the functions together and manages the execution of +the child processes, a family of functions that act as sources, a +function to spawn processes, and a family of functions acting as +sinks. + +Sources are pipe:open, pipe:defer, pipe:echo. To spawn a process use +pipe:spawn, or the convenience function pipe:gpg. To sink the data +use pipe:splice, or pipe:write-to. + +Example: + + (pipe:do + (pipe:echo "3\n1\n2\n") + (pipe:spawn '("/usr/bin/sort")) + (pipe:write-to "sorted" (logior O_WRONLY O_CREAT) #o600)) + +Caveats: Due to the single-threaded nature of gpgscm you cannot use +both a source and sink that is implemented in Scheme. pipe:defer and +pipe:echo are executing in gpgscm, and so does pipe:splice. +*** tr +The transformer monad describes sequential file transformations. + +There is one source function, tr:open. To describe a transformation +using some process, use tr:spawn, tr:gpg, or tr:pipe-do. There are +several sinks, although sink is not quite the right term, because the +data is not consumed, and hence one can use them at any position. The +"sinks" are tr:write-to, tr:call-with-content, tr:assert-identity, and +tr:assert-weak-identity. + +A somewhat contrived example demonstrating many functions is: + + (tr:do + (tr:pipe-do + (pipe:echo "3\n1\n2\n") + (pipe:spawn '("/usr/bin/sort"))) + (tr:write-to "reference") + (tr:call-with-content + (lambda (c) + (echo "currently, c contains" (string-length c) "bytes"))) + (tr:spawn "" '("/usr/bin/gcc" -x c "-E" -o **out** **in**)) + (tr:pipe-do + (pipe:spawn '("/bin/grep" -v "#"))) + (tr:assert-identity "reference")) + +Caveats: As a convenience, gpgscm allows one to specify command line +arguments as Scheme symbols. Scheme symbols, however, are +case-insensitive, and get converted to lower case. Therefore, the -E +argument must be given as a string in the example above. Similarly, +you need to quote numerical values. +** Process management +If you just need to execute a single command, there is (call-with-fds +cmdline infd outfd errfd) which executes cmdline with the given file +descriptors bound to it, and waits for its completion returning the +status code. There is (call cmdline) which is similar, but calls the +command with a closed stdin, connecting stdout and stderr to stderr if +gpgscm is executed with --verbose. (call-check cmdline) raises an +exception if the command does not return 0. + +(call-popen cmdline input) calls a command, writes input to its stdin, +and returns any output from stdout, or raises an exception containing +stderr on failure. diff --git a/tests/openpgp/armdetach.scm b/tests/openpgp/armdetach.scm new file mode 100755 index 000000000..69e09d8ce --- /dev/null +++ b/tests/openpgp/armdetach.scm @@ -0,0 +1,31 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking armored detached signatures" + (lambda (source) + (lettmp (tmp) + (call-popen `(,@GPG --yes --passphrase-fd "0" -sab + --output ,tmp ,source ) usrpass1) + (pipe:do + (pipe:open source (logior O_RDONLY O_BINARY)) + (pipe:spawn `(,@GPG --yes ,tmp))))) + (append plain-files data-files)) diff --git a/tests/openpgp/armdetachm.scm b/tests/openpgp/armdetachm.scm new file mode 100755 index 000000000..618f7aab4 --- /dev/null +++ b/tests/openpgp/armdetachm.scm @@ -0,0 +1,35 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define files (append plain-files data-files)) + +(info "Checking armored detached signatures of multiple files") +(lettmp (tmp) + (call-popen `(,@GPG --yes --passphrase-fd "0" -sab + --output ,tmp ,@files) usrpass1) + (pipe:do + (pipe:defer (lambda (sink) + (for-each (lambda (file) + (pipe:do + (pipe:open file (logior O_RDONLY O_BINARY)) + (pipe:splice sink))) + files))) + (pipe:spawn `(,@GPG --yes ,tmp)))) diff --git a/tests/openpgp/armencrypt.scm b/tests/openpgp/armencrypt.scm new file mode 100755 index 000000000..b0cf0991a --- /dev/null +++ b/tests/openpgp/armencrypt.scm @@ -0,0 +1,30 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking armored encryption" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg usrpass1 `(--yes --passphrase-fd "0" -ea --recipient ,usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) diff --git a/tests/openpgp/armencryptp.scm b/tests/openpgp/armencryptp.scm new file mode 100755 index 000000000..7555ce9d9 --- /dev/null +++ b/tests/openpgp/armencryptp.scm @@ -0,0 +1,31 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking armored encryption and decryption using pipes" + (lambda (source) + (tr:do + (tr:open source) + (tr:pipe-do + (pipe:gpg `(--yes -ea --recipient ,usrname2)) + (pipe:gpg '(--yes))) + (tr:assert-identity source))) + (append plain-files data-files)) diff --git a/tests/openpgp/armor.scm b/tests/openpgp/armor.scm new file mode 100755 index 000000000..5b4ea1409 --- /dev/null +++ b/tests/openpgp/armor.scm @@ -0,0 +1,766 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define armored_key_8192 "-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.0.9 + +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 +=oXxa +-----END PGP PUBLIC KEY BLOCK----- +") + +;; Bug solved 2005-04-07: +;; Try importing the attached key file. As the key is exactly 8192 +;; bytes long, radix64_read is called twice - the first time to read +;; the 8192 bytes, and then once again, to handle the pad '=' on the +;; last four character radix64 block '0uI='. gpg bails out with +;; gpg: [don't know]: invalid packet (ctb=2d) +;; On a read for only the = sign, radix64_read returns -1 for EOF. +;; This causes the iobuf code to pop the armor filter and thus the next +;; byte read is the '-' from the END header line, causing an error. +(info "Checking armored_key_8192") +(pipe:do + (pipe:echo armored_key_8192) + (pipe:gpg '(--import))) + +(define nopad_armored_msg "-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.11-svn5139 (GNU/Linux) + +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 +jnOKMUrdbDkbYM+ojXZ18/4WPKtaxf6lTLrh3m5CJ1V8slRJp0Jes74aDQf/OXqo +QgqVrI2wnl1klGwn06bhVaymdk0hMkxAfeHBGZIQ4BMMZ3aLuVWjAcBH/HP1tVQ4 +IG7MHRnm81yVNgA6yAuZZIPQwxDWVko7rRm/DnsJcpKfL0nZHoF+bJ6q4Rd6Dey1 +S96L4PDmXseFkVZOH/7dNWyKuvSA/MmthkN14lWJiYJebaXb7oZG3XDC70o7bG6Y +mtGOrOwVthFereg1Ii98ZA4nKgqeu2paMju/t03hQXHY5iqyV9ax0A3B0rdivz4U +VbqCcO7pGNb/Ki3gGc3hfVw9YXnR7E+tra19eE7UM7o+YlunKF1Q5dJaLKY1z3L9 +3UAJmXG/sZcLqrDn9zHfb/YxqbyIkM7VIU2kKztWCCNiNKgLQsIoVzK4sbS2LVDI +grmvZg+Q7EkwxZ1FXeBGJmsiyjKYPU3PI8ZBU1MXjwTjtnQRuBSxh+Ok9glPeE+1 +rUDdlVoogUGgPAvLV3BM43E/Q5VE+X04KvPNKINvvS1nZpGKyyy1MayOfstc7Hsw +W/RgllTEd7VW5jgZKg7YyA8r8cUjMqE7zewrq6MtkoFk6ZVslWxfoADBW3ivwdwe +I4XwNboOgBx77qxf95aHRRdqWZa0JF8zvK5BH6EpBI29hlJU4leax696ACA7QIIM +Rpc+ulkvHsNxsUlrUut9AXoob0MqDJRmZvU4gSVivHjvKLEIlymJw4pLbMWhcrNa +v94TExP21Fy/zYcWiZT2nCX6qZVXTcUcQGIrBTWDbR8bxKll4FNo/2zmelyFmeXt +iH+Zp2EwFAUELsrxx9plNd5WceyU9VnZoeucNFd2QFl7lV9KL8AlQaaMdsYOt27m +8j7iNBID4HtCYM0xLwE/5uqVQPxX6qzPPZc66MSRIi8ZHZPFvVilFVzFyZSrq5Lo +ojQQmAycQGOzx7dwx7vi9SeTrBRY2PK3WVurcLiRxM+2u9vjlxigwimpnK3VU1dh +GDoPghB3O34bfiV5GndcnPDLJxsSGWz2z2WiyCFgCkVp3shHUN5c2PLWDIOf/Ejz +LzNJXFKjO6/6ZGpMmGf6bZCa9MBnvXMNic9/k0lmtbgj8SS+2//gko1EDa/Gpaqo +J2xgEuHLvp9KQABWt9VRI+tNUJQCS/Jq10ZiT1TzhczyJEqbtVDOo0l5A5sMV0PO +HszFNxw2BmuM8RYGgAe5pkrdzVdtB8TLmhys7P+xRXNXnsUb+878kt2EyiXjMl1g +oFeAlj2afj/GgelJG0G0FXm5WPDhbthHOO1hW9RP5WCTybjUTAS+GbikVwxa7bZE +LHrROUOInY0ntR9lRNjpVCMdajCsMiqT/G0C/ApzeW6ErLMFIDdVdGSdnz/WDi5C +xQTIS1FOAdefQ0CohE0yOOvjKTAGzht+g4gYiSa/mOnvXcCVM8t39thglZhq4+6G +oWpOrXwByd8OA5f1aQMqSgoySJOGg8a3X7NR9bEDbF7/6QNJE5FvxwXvA8zcabUo +OGxXen85Gkq1M/VnlJ3RGM8vA5UizsdPESYUCVH1eKg8ROlrQOr3ISj5kaNL42fv +OSROmeHvZrHtvGn6hLTNPtWcm1hGSJS/Q5CEZe0/4ActkorF9kuoHCSG3UegX/lg +5mD9aMTHUhD5VooS6OdyakqD+6LptNqQPL0IQALsS9Ls+8KUBxIi9cOe0xIusAQl +OmyJcUJNr+Oq+Ypr6sWvelbWiymgGDN4gHm6BvzyXv5ihnvnmkIQ16WkAsIChzZx +cZUl/bz9bDsSyJ4FyzSRoWuURTz9la3Bo2x5ufLChv2i9+X9WO6Y3nFc4KOBY4hD +WeFt0ZUiY30SyTiWqrPHP8Lnto9JTBOZcIIHOqPgy4L+685Ou6xwO0cUzicIza5M +TbMBOPfnVSgPSCFImGSjAaahWEvl360B8qjx8i1vgkUOxjqfFMnjZUF5b9lBDS72 +JK0esvGRUQqyC4uQHbTi8EOJYaOnCn+0lXPzLNpN171DHfEg/X6iiD0zTjMX/7Sk +PPm2z3zH7yJmeDnh5e/gvgWaPuTVaN+LdUYv/ijqfS3bx6yF1VpNr+esTI23S+o2 +1HqlCfhZUnVmn6r0J1L8tuVeZaMni1qOFs4KacGA9UwAZeGVdOmK0rFIqUXKDehq +7BAmDZV3hnQD2TQzgfDfXpegRECX/wZZVrcg896NltY1r6AVm9jcKLVCNalyoCwe +Rp6anjx8qsUxnXXYN2rhl/l2Y9D23QZ2OM0cpvb9QPJGGgGeSaQu6p8N4hRUroje +UlL8vBLle6tcvVoiRCvPCja857vnthqUppv9bzM9SAyMz4RXcYgQvFErExOI0eyT +II67WbrY2j9ul7h4fZjNKCND7o2aENbylK8CU1wlwEBYC8BPgkTqi+dErP+VrWte +QAhjomMkOVKKzG8JaoPJoVYBmMkMTCPsFFuTjtgvg2+a+DOiYbI8yTDT4+Mi/Woi +gZUcE0HUosPkkJ2ZU3zDXdwPIr4DI7TlrnZPF99Es68+NXD6lQgc6U2fjnBfKMFw +MfaTfrg3ykA8mBwqZ6hQdIoqha14uje9Ses2axrEF1FY+pU7JEKDozmo3HzgTfYA +UFoHCu4Zbeu1IiHHJuVSRrwVy0BsY0nrq0Tjq2uYSbXyR9KylHCxztpzku5Gncy2 +Cl0sBUqv1uNWIt1v6yHdupzRM/fIZ3F95nPIomgM+uHmdpHaXyizM4D3doRGzNPH +fOm6jWgKCllI8JHYJ1DUBUokYv8TYJLf0gOSaZLZmpEh88iXfBP52ZBlUIokeUN0 +aZlxSqawVMteeqcjCs7TNySBDzfTCZREHr77tnBz4+jINXi06mh+/Hz+LRA3YNRD +Do5hyzvvFBg49rY0JzZPTC9J4bi+w1MPmmdz0OgQGG1Wiu+4LrSuBUOgiA0V+FyY +/Md51ShFwRg/5zgcWS4hK1Eg4kKTKLF6Wjdu88PCKx2+gu9dYjXgdRuzZ6LUqqS2 +It/j3VptrXm8NwQFGM27HnqGwK5u+Ym3qLJ0FE4vWNbbxGlZtX3NS8SqZSqVfbqq +TwIz4lXU3ipZJ5b42IanZ2nfWqKpdYn99C7yK6AbwA+qZf5zmy436dH+Rvo6PC6W ++9MQcrrNgvq0tiAJvA39dzb77bhRjAKzL5cDiA40hPlcZs5+Q5g9XpYtKsSfzu7s +FCFRnhXmhiBXoUqf5jsYNrm5dvtl27wPTaKvVQQ6SsVtXDZSWEbdAj8Xfeq7kev+ +jtvaOUmFRMaFevzu5t2uuLYzH7zufMB6p13chVUH9yRnWBzdha/Sqf78k57UQnZg +EJwvXcDJ+uFbnd2sibgoASzDNljSfERK5RfD7Re3n5dK6W0PXzic+7ljGoSLedtd +6DD11IzD6WjBlE/9Aiof6IUDdzuo2VZ0XtufBxmYHXUx9LF3/dgGOr8hvxz/wpMx +nPnr9QDgF9svoCvYq1toUbtWgKd1LjXeVoprAhHXwbn4Z8hj7+/LpPYwR1X3u1ik +wL916n0bOkLgWgqGjqsrgskk5Lk6ZzyrESZ0xd6/+dSrf2YxLivF8O4eCLfNxB3d +=3akT +-----END PGP MESSAGE----- + +") + +(define alpha_seckey "-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.8 (GNU/Linux) + +lQHhBDbjjp4RBAC2ZbFDX0wmJI8yLDYQdIiZeAuHLmfyHsqXaLGUMZtWiAvn/hNp +ctwahmzKm5oXinHUvUkLOQ0s8rOlu15nhw4azc30rTP1LsIkn5zORNnFdgYC6RKy +hOeim/63+/yGtdnTm49lVfaCqwsEmBCEkXaeWDGq+ie1b89J89T6n/JquwCgoQkj +VeVGG+B/SzJ6+yifdHWQVkcD/RXDyLXX4+WHGP2aet51XlKojWGwsZmc9LPPYhwU +/RcUO7ce1QQb0XFlUVFBhY0JQpM/ty/kNi+aGWFzigbQ+HAWZkUvA8+VIAVneN+p ++SHhGIyLTXKpAYTq46AwvllZ5Cpvf02Cp/+W1aVyA0qnBWMyeIxXmR9HOi6lxxn5 +cjajA/9VZufOXWqCXkBvz4Oy3Q5FbjQQ0/+ty8rDn8OTaiPi41FyUnEi6LO+qyBS +09FjnZj++PkcRcXW99SNxmEJRY7MuNHt5wIvEH2jNEOJ9lszzZFBDbuwsjXHK35+ +lPbGEy69xCP26iEafysKKbRXJhE1C+tk8SnK+Gm62sivmK/5av4CAwKcF1Qep+Pf +ssOqtJhr+klruUBf55onBJi4vkk0gK3m32p/05YB2bbMURGz8R4JxUZfUxjdDk73 +LaNYRbQpQWxwaGEgVGVzdCAoZGVtbyBrZXkpIDxhbHBoYUBleGFtcGxlLm5ldD6I +VQQTEQIAFQUCNuOOngMLCgMDFQMCAxYCAQIXgAAKCRAtcnzHaGl3NDl4AJ4rouHB ++LpCkNi5C59jHEa1kbANzACgmddtrNSj1yPyTCwUwRghPUomECS0EEFsaWNlIChk +ZW1vIGtleSmIVQQTEQIAFQUCNuO2qwMLCgMDFQMCAxYCAQIXgAAKCRAtcnzHaGl3 +NCeMAJ9MeUVrago5Jc6PdwdeN5OMwby37QCghW65cZTQlD1bBlIq/QM8bz9AN4G0 +J0FsZmEgVGVzdCAoZGVtbyBrZXkpIDxhbGZhQGV4YW1wbGUubmV0PohVBBMRAgAV +BQI247hYAwsKAwMVAwIDFgIBAheAAAoJEC1yfMdoaXc0t8IAoJPwa6j+Vm5Vi3Nv +uo8JZri4PJ/DAJ9dqbmaJdB8FdJnHfGh1rXK3y/Jcp0BuAQ2448PEAQAnI3XH1f0 +uyN9fZnw72zsHMw706g7EW29nD4UDQG4OzRZViSrUa5n39eI7QrfTO+1meVvs0y8 +F/PvFst5jH68rPLnGSrXz4sTl1T4cop1FBkquvCAKwPLy0lE7jjtCyItOSwIOo8x +oTfY4JEEXmcqsbm+KHv9yYSF/YK4Cf7bIzcAAwcD/Rnl5jKxoucDA96pD2829TKs +LFQSau+Xiy8bvOSSDdlyABsOkNBSaeKO3eAQEKgDM7dzjVNTnAlpQ0EQ8Y9Z8pxO +WYEQYlaMrnRBC4DZ2IadzEhLlIOz5BVp/jfhrr8oVVBwKZXsrz9PZLz+e4Yn+siU +Uvlei9boD9L2ZgSOHakP/gIDApwXVB6n49+yw6e5k2VJBGTFDkQbxpgi4oslePpT +7Tc2qjAke4zO8JHkgKSokEgnMpMz412q9otFX/3qC5MpPG5P8f4r00Kfy9Am/thk +ri01WTIUqF8L/VZXJxLKVoRAabSXudG0eavfah14fN5/+Bw5i8vSHhc/xmQEKTya +2X8Nt1F5zMrE1LAGVVCL9i/DUygnJYOZzAd1Ct0RJ4kFj7lOBICF2IWWiEYEGBEC +AAYFAjbjjw8ACgkQLXJ8x2hpdzQgqQCgn81AaW8W/lyVwMh/UBeMuVMUb24An2uz +wg7Md81a5RI3F2FG8747t9gX +=VM1e +-----END PGP PRIVATE KEY BLOCK----- +") + +;; Bug 1179 solved 2010-05-12: +;; It occured for messages of a multiple of the iobuf block size where +;; the last line had no pad character. Due to premature poppng of thea +;; rmor filter gpg swalled the CRC line and passed the '-----END...' +;; line on to the decryption layer. + +(info "Importing alpha_seckey") +(pipe:do + (pipe:echo alpha_seckey) + (pipe:gpg '(--import))) + +(info "Checking for bug #1179") +(tr:do + (tr:pipe-do + (pipe:echo nopad_armored_msg) + (pipe:gpg '()))) diff --git a/tests/openpgp/armsignencrypt.scm b/tests/openpgp/armsignencrypt.scm new file mode 100755 index 000000000..b84bfe4e0 --- /dev/null +++ b/tests/openpgp/armsignencrypt.scm @@ -0,0 +1,30 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking armored signing and encryption" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg usrpass1 `(--yes --passphrase-fd "0" -sea --recipient ,usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) diff --git a/tests/openpgp/armsigs.scm b/tests/openpgp/armsigs.scm new file mode 100755 index 000000000..d897581cb --- /dev/null +++ b/tests/openpgp/armsigs.scm @@ -0,0 +1,30 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking armored signatures" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg usrpass1 `(--yes --passphrase-fd "0" -sa --recipient ,usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) diff --git a/tests/openpgp/clearsig.scm b/tests/openpgp/clearsig.scm new file mode 100755 index 000000000..96b1b4c31 --- /dev/null +++ b/tests/openpgp/clearsig.scm @@ -0,0 +1,107 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define (check-signing args input) + (lambda (source sink) + (lettmp (signed) + (call-popen `(,@GPG --output ,signed --yes + ,@args ,source) input) + (call-popen `(,@GPG --output ,sink --yes ,signed) "")))) + +(for-each-p + "Checking signing and verifying plain text messages" + (lambda (source) + ((if (equal? "plain-3" source) + ;; plain-3 does not end in a newline, and gpg will add one. + ;; Therefore, we merely check that the verification is ok. + check-execution + ;; Otherwise, we do check that we recover the original file. + check-identity) + source + (check-signing '(--passphrase-fd "0" --clearsign) usrpass1))) + (append plain-files '("plain-large"))) + +;; The test vectors are lists of length three, containing +;; - a string to be signed, +;; - a flag indicating whether we verify that the exact message is +;; reconstructed (whitespace at the end is normalized for plain text +;; messages), +;; - and a list of arguments to add to gpg when encoding +;; the string. + +(define :string car) +(define :check-equality cadr) +(define :options caddr) + +(define + vectors + '(;; one with long lines + ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyx + +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +" #t ()) + + ;; one with only one long line + ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyx +" #t ()) + + ;; and one with an empty body + ("" #f ()) + + ;; and one with one empty line at the end + ("line 1 +line 2 +line 3 +there is a blank line after this + +" #t ()) + + ;; I think this file will be constructed wrong (gpg 0.9.3) but it + ;; should verify okay anyway. + ("this is a sig test + " #f ()) + + ;; check our special diff mode + ("--- mainproc.c Tue Jun 27 09:28:11 2000 ++++ mainproc.c~ Thu Jun 8 22:50:25 2000 +@@ -1190,16 +1190,13 @@ + md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo); + } + /* ask for file and hash it */ +- if( c->sigs_only ) { ++ if( c->sigs_only ) + rc = hash_datafiles( c->mfx.md, NULL, + c->signed_data, c->sigfilename, + n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 ); +" #t (--not-dash-escaped)))) + +(let ((counter (make-counter))) + (for-each-p' + "Checking signing and verifying test vectors" + (lambda (vec) + (lettmp (tmp) + (with-output-to-file tmp (lambda () (display (:string vec)))) + ((if (:check-equality vec) check-identity check-execution) + tmp + (check-signing `(--passphrase-fd "0" --clearsign ,@(:options vec)) + usrpass1)))) + (lambda (vec) (counter)) + vectors)) diff --git a/tests/openpgp/conventional-mdc.scm b/tests/openpgp/conventional-mdc.scm new file mode 100755 index 000000000..c52492175 --- /dev/null +++ b/tests/openpgp/conventional-mdc.scm @@ -0,0 +1,65 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define s2k '--s2k-count=65536) +(define passphrase "Hier spricht HAL") + +(define (file-copy-n from to n) + (catch '() (unlink to)) + (letfd ((source (open from (logior O_RDONLY O_BINARY))) + (sink (open to (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (splice source sink n))) + +(define test-files + (map (lambda (size) + (let ((tmp (make-temporary-file + (string-append "data-80000-" (number->string size))))) + (file-copy-n "data-80000" tmp size) + tmp)) + '(0 1 2 3 9 10 11 19 20 21 22 23 39 40 41 8192 32000))) + +(for-each-p + "Checking conventional encryption with MDC" + (lambda (algo) + (for-each-p + "" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k + --force-mdc -c + --cipher-algo ,algo)) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k)) + (tr:assert-identity source))) + test-files)) + all-cipher-algos) + +(for-each remove-temporary-file test-files) + +(for-each-p + "Checking sign+symencrypt" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k -cs)) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k)) + (tr:assert-identity source))) + (append plain-files data-files)) diff --git a/tests/openpgp/conventional.scm b/tests/openpgp/conventional.scm new file mode 100755 index 000000000..67e28e246 --- /dev/null +++ b/tests/openpgp/conventional.scm @@ -0,0 +1,48 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define s2k '--s2k-count=65536) +(define passphrase "Hier spricht HAL") + +(for-each-p + "Checking conventional encryption" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k -c)) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k)) + (tr:assert-identity source))) + '("plain-2" "data-32000")) + +(for-each-p + "Checking conventional encryption using a specific cipher" + (lambda (algo) + (for-each-p + "" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k -c + --cipher-algo ,algo)) + (tr:gpg passphrase `(--yes --passphrase-fd "0" ,s2k)) + (tr:assert-identity source))) + '("plain-1" "data-80000"))) + all-cipher-algos) diff --git a/tests/openpgp/decrypt-dsa.scm b/tests/openpgp/decrypt-dsa.scm new file mode 100755 index 000000000..b01a0f771 --- /dev/null +++ b/tests/openpgp/decrypt-dsa.scm @@ -0,0 +1,29 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking decryption of supplied DSA encrypted file" + (lambda (name) + (tr:do + (tr:open (in-srcdir (string-append name "-pgp.asc"))) + (tr:gpg "" '(--yes)) + (tr:assert-identity name))) + (list (car plain-files))) diff --git a/tests/openpgp/decrypt.scm b/tests/openpgp/decrypt.scm new file mode 100755 index 000000000..ec0f8e7ee --- /dev/null +++ b/tests/openpgp/decrypt.scm @@ -0,0 +1,29 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking decryption of supplied files" + (lambda (name) + (tr:do + (tr:open (in-srcdir (string-append name ".asc"))) + (tr:gpg "" '(--yes)) + (tr:assert-identity name))) + plain-files) diff --git a/tests/openpgp/default-key.scm b/tests/openpgp/default-key.scm new file mode 100755 index 000000000..443365883 --- /dev/null +++ b/tests/openpgp/default-key.scm @@ -0,0 +1,76 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +;; Import the sample key +;; +;; pub 1024R/8BC90111 2015-12-02 +;; Key fingerprint = E657 FB60 7BB4 F21C 90BB 6651 BC06 7AF2 8BC9 0111 +;; uid [ultimate] Barrett Brown <barrett@example.org> +;; sub 1024R/3E880CFF 2015-12-02 (encryption) +;; sub 1024R/F5F77B83 2015-12-02 (signing) +;; sub 1024R/45117079 2015-12-02 (encryption) +;; sub 1024R/1EA97479 2015-12-02 (signing) + +(info "Importing public key.") +(call-check + `(,(tool 'gpg) --import + ,(in-srcdir "samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc"))) + +;; By default, the most recent, valid signing subkey (1EA97479). +(for-each-p + "Checking that the most recent, valid signing subkey is used by default" + (lambda (keyid) + (tr:do + (tr:pipe-do + (pipe:defer (lambda (sink) (display "" (fdopen sink "w")))) + (pipe:gpg `(--default-key ,keyid -s)) + (pipe:gpg '(--verify --status-fd=1))) + (tr:call-with-content + (lambda (c) + (unless (string-contains? + c "VALIDSIG 5FBA84ACE02DCB17DA3DFF6BBCA43C441EA97479") + (exit 1)))))) + '("8BC90111" "3E880CFF" "F5F77B83" "45117079" "1EA97479")) + +;; But, if we request a particular signing key, we should get it. +(for-each-p + "Checking that the most recent, valid encryption subkey is used by default" + (lambda (keyid) + (tr:do + (tr:pipe-do + (pipe:defer (lambda (sink) (display "" (fdopen sink "w")))) + ;; We need another recipient, because --encrypt-to-default-key is + ;; not considered a recipient and gpg doesn't encrypt without any + ;; recipients. + ;; + ;; Note: it doesn't matter whether we specify the primary key or + ;; a subkey: the newest encryption subkey will be used. + (pipe:gpg `(--default-key ,keyid --encrypt-to-default-key + -r "439F02CA" -e)) + (pipe:gpg '(--list-packets))) + (tr:call-with-content + (lambda (c) + (unless (any (lambda (line) + (and (string-prefix? line ":pubkey enc packet:") + (string-suffix? line "45117079"))) + (string-split c #\newline)) + (exit 1)))))) + '("8BC90111" "3E880CFF" "F5F77B83" "45117079" "1EA97479")) diff --git a/tests/openpgp/defs.scm b/tests/openpgp/defs.scm new file mode 100644 index 000000000..6fdb95580 --- /dev/null +++ b/tests/openpgp/defs.scm @@ -0,0 +1,130 @@ +;; Common definitions for the OpenPGP test scripts. +;; +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +;; +;; Constants. +;; + +(define usrname1 "one@example.com") +(define usrpass1 "def") +(define usrname2 "two@example.com") +(define usrpass2 "") +(define usrname3 "three@example.com") +(define usrpass3 "") + +(define dsa-usrname1 "pgp5") +;; we use the sub key because we do not yet have the logic to to derive +;; the first encryption key from a keyblock (I guess) (Well of course +;; we have this by now and the notation below will lookup the primary +;; first and then search for the encryption subkey.) +(define dsa-usrname2 "0xCB879DE9") + +(define plain-files '("plain-1" "plain-2" "plain-3")) +(define data-files '("data-500" "data-9000" "data-32000" "data-80000")) +(define exp-files '()) + +(define (qualify executable) + (string-append executable (getenv "EXEEXT"))) + +(define (getenv' key default) + (let ((value (getenv key))) + (if (string=? "" value) + default + value))) + +(define tools + '((gpg "GPG" "g10/gpg") + (gpg-agent "GPG_AGENT" "agent/gpg-agent") + (gpg-connect-agent "GPG_CONNECT_AGENT" "tools/gpg-connect-agent") + (gpgconf "GPGCONF" "tools/gpgconf") + (gpg-preset-passphrase "GPG_PRESET_PASSPHRASE" + "agent/gpg-preset-passphrase") + (mktdata "MKTDATA" "tools/mk-tdata") + (gpgtar "GPGTAR" "tools/gpgtar") + (gpg-zip "GPGZIP" "tools/gpg-zip"))) + +(define (tool which) + (let ((t (assoc which tools)) + (prefix (getenv "BIN_PREFIX"))) + (getenv' (cadr t) + (qualify (if (string=? prefix "") + (string-append (getenv "objdir") "/" (caddr t)) + (string-append prefix "/" (basename (caddr t)))))))) + + +(define have-opt-always-trust + (string-contains? (call-popen `(,(tool 'gpg) --dump-options) "") + "--always-trust")) + +(define GPG `(,(tool 'gpg) --no-permission-warning + ,@(if have-opt-always-trust '(--always-trust) '()))) +(define PINENTRY (string-append (getcwd) "/" (qualify "fake-pinentry"))) + +(define (tr:gpg input args) + (tr:spawn input `(,@GPG --output **out** ,@args **in**))) + +(define (pipe:gpg args) + (pipe:spawn `(,@GPG --output - ,@args -))) + +(define (get-config what) + (let* ((config-string + (call-popen `(,@GPG --with-colons --list-config ,what) "")) + (config (string-splitn + (string-rtrim char-whitespace? config-string) #\: 2))) + (string-split (caddr config) #\;))) + +(define all-pubkey-algos (get-config "pubkeyname")) +(define all-hash-algos (get-config "digestname")) +(define all-cipher-algos (get-config "ciphername")) + +(define (have-pubkey-algo? x) + (not (not (member x all-pubkey-algos)))) +(define (have-hash-algo? x) + (not (not (member x all-hash-algos)))) +(define (have-cipher-algo? x) + (not (not (member x all-cipher-algos)))) + +(define (gpg-pipe args0 args1 errfd) + (lambda (source sink) + (let* ((p (pipe)) + (task0 (spawn-process-fd `(,@GPG ,@args0) + source (:write-end p) errfd)) + (_ (close (:write-end p))) + (task1 (spawn-process-fd `(,@GPG ,@args1) + (:read-end p) sink errfd))) + (close (:read-end p)) + (wait-processes (list GPG GPG) (list task0 task1) #t)))) + +(setenv "GPG_AGENT_INFO" "" #t) +(setenv "GNUPGHOME" (getcwd) #t) + +;; +;; GnuPG helper. +;; + +;; Call GPG to obtain the hash sums. Either specify an input file in +;; ARGS, or an string in INPUT. Returns a list of (<algo> +;; "<hashsum>") lists. +(define (gpg-hash-string args input) + (map + (lambda (line) + (let ((p (string-split line #\:))) + (list (string->number (cadr p)) (caddr p)))) + (string-split + (call-popen `(,@GPG --with-colons ,@args) input) #\newline))) diff --git a/tests/openpgp/detach.scm b/tests/openpgp/detach.scm new file mode 100755 index 000000000..375e92272 --- /dev/null +++ b/tests/openpgp/detach.scm @@ -0,0 +1,31 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking detached signatures" + (lambda (source) + (lettmp (tmp) + (call-popen `(,@GPG --yes --passphrase-fd "0" -sb + --output ,tmp ,source ) usrpass1) + (pipe:do + (pipe:open source (logior O_RDONLY O_BINARY)) + (pipe:spawn `(,@GPG --yes ,tmp))))) + (append plain-files data-files)) diff --git a/tests/openpgp/detachm.scm b/tests/openpgp/detachm.scm new file mode 100755 index 000000000..a4ebce03e --- /dev/null +++ b/tests/openpgp/detachm.scm @@ -0,0 +1,35 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define files (append plain-files data-files)) + +(info "Checking detached signatures of multiple files") +(lettmp (tmp) + (call-popen `(,@GPG --yes --passphrase-fd "0" -sb + --output ,tmp ,@files) usrpass1) + (pipe:do + (pipe:defer (lambda (sink) + (for-each (lambda (file) + (pipe:do + (pipe:open file (logior O_RDONLY O_BINARY)) + (pipe:splice sink))) + files))) + (pipe:spawn `(,@GPG --yes ,tmp)))) diff --git a/tests/openpgp/ecc.scm b/tests/openpgp/ecc.scm new file mode 100755 index 000000000..f2f3b7c3a --- /dev/null +++ b/tests/openpgp/ecc.scm @@ -0,0 +1,249 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define keygrips '("8E06A180EFFE4C65B812150CAF19BF30C0689A4C" + "E4403F3FD7A443FAC29FEF288FA0D20AC212851E" + "0B7554421FFB14A06CB9F63FB49A85A58E97ABAC" + "303ACC892C2D786C8A789677C0BE54DA8538F903" + "9FE5C36985351524B6AFA19FDCBC1A3A750B6F5F" + "145A52CC7ED3FD41C5B0A26BE220FEED36AF24DE")) +(define mainkeyids '("BAA59D9C" "0F54719F" "45AF2FFE")) + +(unless (have-pubkey-algo? "ECDH") + (skip "No ECC support due to an old Libgcrypt")) + +(info "Preparing for ECC test") +(for-each + (lambda (grip) + (catch '() (unlink (string-append "private-keys-v1.d/" grip ".key"))) + (call-check `(,(tool 'gpg-preset-passphrase) + --preset --passphrase ecc ,grip))) + keygrips) + +(info "Importing ECC public keys") +(for-each + (lambda (keyid) + (call `(,(tool 'gpg) --delete-key --batch --yes ,keyid))) + mainkeyids) + +(for-each + (lambda (n) + (call-check `(,(tool 'gpg) --import + ,(in-srcdir (string-append + "samplekeys/ecc-sample-" + (number->string n) + "-pub.asc"))))) + '(1 2 3)) + +;; The following is an opaque ECDSA signature on a message "This is one +;; line\n" (17 byte long) by the primary 256 bit key: +(define msg_opaque_signed_256 "-----BEGIN PGP MESSAGE----- +Version: GnuPG v2.1.0-ecc (GNU/Linux) + +owGbwMvMwCHMvVT3w66lc+cwrlFK4k5N1k3KT6nUK6ko8Zl8MSEkI7NYAYjy81IV +cjLzUrk64lgYhDkY2FiZQNIMXJwCMO31rxgZ+tW/zesUPxWzdKWrtLGW/LkP5rXL +V/Yvnr/EKjBbQuvZSYa/klsum6XFmTze+maVgclT6Rc6hzqqxNy6o6qdTTmLJuvp +AQA= +=GDv4 +-----END PGP MESSAGE----") + +;; The following is an opaque ECDSA signature on a message "This is one +;; line\n" (17 byte long) by the primary 384 bit key: +(define msg_opaque_signed_384 "-----BEGIN PGP MESSAGE----- +Version: PGP Command Line v10.0.0 (Linux) + +qANQR1DIqwE7wsvMwCnM2WDcwR9SOJ/xtFISd25qcXFieqpeSUUJAxCEZGQWKwBR +fl6qQk5mXirXoXJmVgbfYC5xmC5hzsDPjHXqbDLzpXpTBXSZV3L6bAgP3Kq7Ykmo +7Ds1v4UfBS+3CSSon7Pzq79WLjzXXEH54MkjPxnrw+8cfMVnY7Bi18J702Nnsa7a +9lMv/PM0/ao9CZ3KX7Q+Tv1rllTZ5Hj4V1frw431QnHfAA== +=elKT +-----END PGP MESSAGE-----") + +;; The following is an opaque ECDSA signature on a message "This is one +;; line\n" (17 byte long) by the primary 521 bit key: +(define msg_opaque_signed_521 "-----BEGIN PGP MESSAGE----- +Version: PGP Command Line v10.0.0 (Linux) + +qANQR1DIwA8BO8LLzMAlnO3Y8tB1vf4/xtNKSdy5qcXFiempeiUVJQxAEJKRWawA +RPl5qQo5mXmpXIdmMLMy+AaLnoLpEubatpeJY2Lystd7Qt32q2UcvRS5kNPWtDB7 +ryufvcrWtFM7Jx8qXKDxZuqr7b9PGv1Ssk+I8TzB2O9dZC+n/jv+PAdbuu7mLe33 +Gf9pLd3weV3Qno6FOqxGa5ZszQx+uer2xH3/El9x/2pVeO4l15ScsL7qWMTmffmG +Ic1RdzgeCfosMF+l/zVRchcLKzenEQA= +=ATtX +-----END PGP MESSAGE-----") + +(lettmp (z) + (letfd ((fd (open z (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (display "This is one line\n" (fdopen fd "wb"))) + + (for-each-p + "Checking opaque ECDSA signatures" + (lambda (test) + (lettmp (x y) + (call-with-output-file + x (lambda (p) (display (eval test (current-environment)) p))) + (call-check `(,(tool 'gpg) --verify ,x)) + (call-check `(,(tool 'gpg) --output ,y ,x)) + (unless (file=? y z) (error "mismatch")))) + '(msg_opaque_signed_256 msg_opaque_signed_384 msg_opaque_signed_521))) + +;; +;; Import the secret keys so that we now can sign and decrypt. +;; +;; Note that the PGP generated secret keys are not self-signed, thus we +;; need to pass an appropriate option. +;; +(info "Importing ECC secret keys") +(setenv "PINENTRY_USER_DATA" "ecc" #t) +(for-each + (lambda (n) + (call-check `(,(tool 'gpg) --import + ,@(if (> n 1) '(--allow-non-selfsigned-uid) '()) + ,(in-srcdir (string-append + "samplekeys/ecc-sample-" + (number->string n) + "-sec.asc"))))) + '(1 2 3)) + +;; +;; Check a few sample encrtpted messages. +;; +(info "Checking ECC encryption") + +;; The following block encrypts the text "This is one line\n", 17 bytes, +;; with the subkey 4089AB73. +(define msg_encrypted_256 "-----BEGIN PGP MESSAGE----- +Version: GnuPG v2.1.0-ecc (GNU/Linux) + +hH4Dd863o0CJq3MSAgMEHdIYZQx+rV1cjy7qitIOEICFFzp4cjsRX4r+rDdMcQUs +h7VZmbP1c9C0s9sgCKwubWfkcYUl2ZOju4gy+s4MYTBb4/j8JjnJ9Bqn6LWutTXJ +zwsdP13VIJLnhiNqISdR3/6xWQ0ICRYzwb95nUZ1c1DSVgFpjPgUvi4pgYbTpcDB +jzILKWBfBDT/jck169XE8vgtbcqVQYZ7lZpaY9CzEbC+4dXZmV1gm5MafpTyFWgH +VnyrZB4gad9Lp9e0RKHHcOOE7s/NeLuu +=odUZ +-----END PGP MESSAGE-----") + +;; The following block encrypts the text "This is one line\n", 17 bytes, +;; with the subkey 9A201946: +(define msg_encrypted_384 "-----BEGIN PGP MESSAGE----- +Version: PGP Command Line v10.0.0 (Linux) + +qANQR1DBngOqi5OPmiAZRhIDAwQqIr/00cJyf+QP+VA4QKVkk77KMHdz9OVaR2XK +0VYu0F/HPm89vL2orfm2hrAZxY9G2R0PG4Wk5Lg04UjKca/O72uWtjdPYulFidmo +uB0QpzXFz22ZZinxeVPLPEr19Pow0EwCc95cg4HAgrD0nV9vRcTJ/+juVfvsJhAO +isMKqrFNMvwnK5A1ECeyVXe7oLZl0lUBRhLr59QTtvf85QJjg/m5kaGy8XCJvLv3 +61pZa6KUmw89PjtPak7ebcjnINL01vwmyeg1PAyW/xjeGGvcO+R4P1b4ewyFnJyR +svzIJcP7d4DqYOw7 +=oiTJ +-----END PGP MESSAGE-----") + +;; The following block encrypts the text "This is one line\n", 17 bytes, +;; with the subkey A81C4838: +(define msg_encrypted_521 "-----BEGIN PGP MESSAGE----- +Version: PGP Command Line v10.0.0 (Linux) + +qANQR1DBwAIDB+qqSKgcSDgSBCMEAKpzTUxB4c56C7g09ekD9I+ttC5ER/xzDmXU +OJmFqU5w3FllhFj4TgGxxdH+8fv4W2Ag0IKoJvIY9V1V7oUCClfqAR01QbN7jGH/ +I9GFFnH19AYEgMKgFmh14ZwN1BS6/VHh+H4apaYqapbx8/09EL+DV9zWLX4GRLXQ +VqCR1N2rXE29MJFzGmDOCueQNkUjcbuenoCSKcNT+6xhO27U9IYVCg4BhRUDGfD6 +dhfRzBLxL+bKR9JVAe46+K8NLjRVu/bd4Iounx4UF5dBk8ERy+/8k9XantDoQgo6 +RPqCad4Dg/QqkpbK3y574ds3VFNJmc4dVpsXm7lGV5w0FBxhVNPoWNhhECMlTroX +Rg== +=5GqW +-----END PGP MESSAGE-----") + +(lettmp (z) + (letfd ((fd (open z (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (display "This is one line\n" (fdopen fd "wb"))) + + (for-each-p + "Checking ECDSA decryption" + (lambda (test) + (lettmp (x y) + (call-with-output-file + x (lambda (p) (display (eval test (current-environment)) p))) + (call-check `(,@GPG --yes --output ,y ,x)) + (unless (file=? y z) (error "mismatch")))) + '(msg_encrypted_256 msg_encrypted_384 msg_encrypted_521))) + +;; +;; Now check that we can encrypt and decrypt our own messages. +;; +;; Note that we don't need to provide a passppharse because we already +;; preset the passphrase into the gpg-agent. +;; +(for-each-p + "Checking ECC encryption and decryption" + (lambda (source) + (for-each-p + "" + (lambda (keyid) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --encrypt --recipient ,keyid)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + mainkeyids)) + (append plain-files data-files)) + +;; +;; Now check that we can sign and verify our own messages. +;; +(for-each-p + "Checking ECC signing and verifiction" + (lambda (source) + (for-each-p + "" + (lambda (keyid) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --sign --local-user ,keyid)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + mainkeyids)) + (append plain-files data-files)) + +;; +;; Let us also try to import the keys only from a secret keyblock. +;; +;; Because PGP does not sign the UID, it is not very useful to work +;; with this key unless we go into the trouble of adding the +;; self-signature. +;; +(info "Importing ECC secret keys directly") +(for-each + (lambda (keyid) + (catch '() (unlink (string-append "private-keys-v1.d/" keyid ".key")))) + keygrips) +(for-each + (lambda (keyid) + (call `(,(tool 'gpg) --delete-key --batch --yes ,keyid))) + mainkeyids) + +(for-each + (lambda (n) + (call-check `(,(tool 'gpg) --import + ,@(if (> n 1) '(--allow-non-selfsigned-uid) '()) + ,(in-srcdir (string-append + "samplekeys/ecc-sample-" + (number->string n) + "-sec.asc"))))) + '(1 2 3)) diff --git a/tests/openpgp/encrypt-dsa.scm b/tests/openpgp/encrypt-dsa.scm new file mode 100755 index 000000000..5228e43a7 --- /dev/null +++ b/tests/openpgp/encrypt-dsa.scm @@ -0,0 +1,45 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking encryption using DSA" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --encrypt --recipient ,dsa-usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) + +(for-each-p + "Checking encryption using DSA and a specific cipher algorithm" + (lambda (cipher) + (for-each-p + "" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --encrypt --recipient ,dsa-usrname2 + --cipher-algo ,cipher)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files))) + all-cipher-algos) diff --git a/tests/openpgp/encrypt.scm b/tests/openpgp/encrypt.scm new file mode 100755 index 000000000..5a3e1788c --- /dev/null +++ b/tests/openpgp/encrypt.scm @@ -0,0 +1,45 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking encryption" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --encrypt --recipient ,usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) + +(for-each-p + "Checking encryption using a specific cipher algorithm" + (lambda (cipher) + (for-each-p + "" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --encrypt --recipient ,usrname2 + --cipher-algo ,cipher)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files))) + all-cipher-algos) diff --git a/tests/openpgp/encryptp.scm b/tests/openpgp/encryptp.scm new file mode 100755 index 000000000..2b010acd1 --- /dev/null +++ b/tests/openpgp/encryptp.scm @@ -0,0 +1,31 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking encryption and decryption using pipes" + (lambda (source) + (tr:do + (tr:open source) + (tr:pipe-do + (pipe:gpg `(--yes --encrypt --recipient ,usrname2)) + (pipe:gpg '(--yes))) + (tr:assert-identity source))) + (append plain-files data-files)) diff --git a/tests/openpgp/finish.scm b/tests/openpgp/finish.scm new file mode 100755 index 000000000..48801c861 --- /dev/null +++ b/tests/openpgp/finish.scm @@ -0,0 +1,23 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(echo "Killing gpg-agent...") +(call-check `(,(tool 'gpg-connect-agent) --verbose killagent /bye)) diff --git a/tests/openpgp/genkey1024.scm b/tests/openpgp/genkey1024.scm new file mode 100755 index 000000000..9870f4624 --- /dev/null +++ b/tests/openpgp/genkey1024.scm @@ -0,0 +1,52 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define (genkey config) + (pipe:do + (pipe:echo config) + (pipe:spawn `(,(tool 'gpg) --quiet --batch --gen-key)))) + +(info "Checking batch key generation") +(genkey "Key-Type: DSA +Key-Length: 1024 +Subkey-Type: ELG +Subkey-Length: 1024 +Name-Real: Harry H. +Name-Comment: test key +Name-Email: hh@@ddorf.de +Expire-Date: 1 +%no-protection +%transient-key +%commit +") + +(if (have-pubkey-algo? "RSA") + (genkey "Key-Type: RSA +Key-Length: 1024 +Key-Usage: sign,encrypt +Name-Real: Harry A. +Name-Comment: RSA test key +Name-Email: hh@@ddorf.de +Expire-Date: 2 +%no-protection +%transient-key +%commit +")) diff --git a/tests/openpgp/gpgtar.scm b/tests/openpgp/gpgtar.scm new file mode 100755 index 000000000..d406079f5 --- /dev/null +++ b/tests/openpgp/gpgtar.scm @@ -0,0 +1,88 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(unless (= 0 (call `(,(tool 'gpgtar) --help))) + (skip "gpgtar not installed")) + +(define testfiles (append plain-files data-files)) +(define gpgargs + (if have-opt-always-trust + "--no-permission-warning --always-trust" + "--no-permission-warning")) + +(define (do-test create-flags inspect-flags extract-flags) + (lettmp (archive) + (call-check `(,(tool 'gpgtar) --gpg ,(tool 'gpg) --gpg-args ,gpgargs + ,@create-flags + --output ,archive + ,@testfiles)) + (tr:do + (tr:pipe-do + (pipe:spawn `(,(tool 'gpgtar) --gpg ,(tool 'gpg) --gpg-args ,gpgargs + --list-archive ,@inspect-flags + ,archive))) + (tr:call-with-content + (lambda (c) + (unless (all (lambda (f) (string-contains? c f)) testfiles) + (error "some file(s) are missing from archive"))))) + + (with-temporary-working-directory + (call-check `(,(tool 'gpgtar) --gpg ,(tool 'gpg) --gpg-args ,gpgargs + --tar-args --directory=. + --decrypt + ,@extract-flags + ,archive)) + + (for-each + (lambda (f) (unless (call-with-input-file f (lambda (x) #t)) + (error (string-append "missing file: " f)))) + testfiles)))) + +(info "Checking gpgtar without encryption") +(do-test `(--skip-crypto --encrypt) '(--skip-crypto) '(--skip-crypto)) + +(info "Checking gpgtar with asymmetric encryption") +(do-test `(--encrypt --recipient ,usrname2) '() '()) + +(info "Checking gpgtar with asymmetric encryption and signature") +(do-test `(--encrypt --recipient ,usrname2 --sign --local-user ,usrname3) + '() '()) + +(info "Checking gpgtar with signature") +(do-test `(--sign --local-user ,usrname3) '() '()) + +(lettmp (passphrasefile) + (letfd ((fd (open passphrasefile (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (display "streng geheimes hupsipupsi" (fdopen fd "wb"))) + + (let ((ppflags `(--gpg-args ,(string-append "--passphrase-file=" + passphrasefile)))) + (info "Checking gpgtar with symmetric encryption") + (do-test `(,@ppflags --symmetric) ppflags ppflags) + + (info "Checking gpgtar with symmetric encryption and chosen cipher") + (do-test `(,@ppflags --symmetric --gpg-args + ,(string-append "--cipher=" (car all-cipher-algos))) + ppflags ppflags) + + (info "Checking gpgtar with both symmetric and asymmetric encryption") + (do-test `(,@ppflags --symmetric --encrypt --recipient ,usrname2 + --sign --local-user ,usrname3) ppflags ppflags))) diff --git a/tests/openpgp/import.scm b/tests/openpgp/import.scm new file mode 100755 index 000000000..580acea0d --- /dev/null +++ b/tests/openpgp/import.scm @@ -0,0 +1,60 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(info "Checking bug 894: segv importing certain keys.") +(call-check `(,(tool 'gpg) --import ,(in-srcdir "bug894-test.asc"))) + +(define keyid "0xC108E83A") +(info "Checking bug 1223: designated revoker sigs are not properly merged.") +(call `(,(tool 'gpg) --delete-key --batch --yes ,keyid)) +(call `(,(tool 'gpg) --import ,(in-srcdir "bug1223-bogus.asc"))) +(call `(,(tool 'gpg) --import ,(in-srcdir "bug1223-good.asc"))) +(tr:do + (tr:pipe-do + (pipe:gpg `(--list-keys --with-colons ,keyid))) + (tr:call-with-content + (lambda (c) + ;; XXX we do not have a regexp library + (unless (any (lambda (line) + (and (string-prefix? line "rvk:") + (string-contains? line ":0EE5BE979282D80B9F7540F1CCD2ED94D21739E9:"))) + (string-split c #\newline)) + (exit 1))))) + +(define fpr1 "9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF") +(define fpr2 "A55120427374F3F7AA5F1166DDA252EBB8EBE1AF") +(info "Checking import of two keys with colliding long key ids.") +(call `(,(tool 'gpg) --delete-key --batch --yes ,fpr1 ,fpr2)) +(call `(,(tool 'gpg) --import ,(in-srcdir "samplekeys/dda252ebb8ebe1af-1.asc"))) +(call `(,(tool 'gpg) --import ,(in-srcdir "samplekeys/dda252ebb8ebe1af-2.asc"))) +(tr:do + (tr:pipe-do + (pipe:gpg `(--list-keys --with-colons ,fpr1 ,fpr2))) + (tr:call-with-content + (lambda (c) + ;; XXX we do not have a regexp library + (let ((keys (filter + (lambda (line) + (and (string-prefix? line "pub:") + (string-contains? line ":4096:1:DDA252EBB8EBE1AF:"))) + (string-split c #\newline)))) + (unless (= 2 (length keys)) + (error "Importing keys with long id collision failed")))))) diff --git a/tests/openpgp/mds.scm b/tests/openpgp/mds.scm new file mode 100755 index 000000000..8ca6c7b31 --- /dev/null +++ b/tests/openpgp/mds.scm @@ -0,0 +1,68 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(define empty-string-hashes + `((1 "D41D8CD98F00B204E9800998ECF8427E" "MD5") + (2 "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" "SHA1") + (3 "9C1185A5C5E9FC54612808977EE8F548B2258D31" "RIPEMD160") + (11 "D14A028C2A3A2BC9476102BB288234C415A2B01F828EA62AC5B3E42F" "SHA224") + (8 "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" "SHA256") + (9 "38B060A751AC96384CD9327EB1B1E36A21FDB71114BE07434C0CC7BF63F6E1DA274EDEBFE76F65FBD51AD2F14898B95B" "SHA384") + (10 + "CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E" + "SHA512"))) + +(define abc-hashes + `((1 "C3FCD3D76192E4007DFB496CCA67E13B" "MD5") + (2 "32D10C7B8CF96570CA04CE37F2A19D84240D3A89" "SHA1") + (3 "F71C27109C692C1B56BBDCEB5B9D2865B3708DBC" "RIPEMD160") + (11 "45A5F72C39C5CFF2522EB3429799E49E5F44B356EF926BCF390DCCC2" "SHA224") + (8 "71C480DF93D6AE2F1EFAD1447C66C9525E316218CF51FC8D9ED832F2DAF18B73" "SHA256") + (9 "FEB67349DF3DB6F5924815D6C3DC133F091809213731FE5C7B5F4999E463479FF2877F5F2936FA63BB43784B12F3EBB4" "SHA384") + (10 "4DBFF86CC2CA1BAE1E16468A05CB9881C97F1753BCE3619034898FAA1AABE429955A1BF8EC483D7421FE3C1646613A59ED5441FB0F321389F77F48A879C7B1F1" "SHA512"))) + +;; Symbolic names for the triples above. +(define :algo car) +(define :value cadr) +(define :name caddr) + +;; Test whether HASH matches REF. +(define (test-hash hash ref) + (unless (eq? #f ref) + (if (not (string=? (:value hash) (:value ref))) + (error "failed")))) + +;; Test whether the hashes computed over S match the REFERENCE set. +(define (test-hashes msg s reference) + (for-each-p' + msg + (lambda (hash) (test-hash hash (assv (:algo hash) reference))) + (lambda (hash) + (let ((ref (assv (:algo hash) reference))) + (if (eq? #f ref) + (string-append "no-ref-for:" (number->string (:algo hash))) + (:name ref)))) + (gpg-hash-string '(--print-mds) s))) + +(test-hashes "Hashing the empty string" + "" empty-string-hashes) +(test-hashes "Hashing the string \"abcdefghijklmnopqrstuvwxyz\"" + "abcdefghijklmnopqrstuvwxyz" abc-hashes) diff --git a/tests/openpgp/multisig.scm b/tests/openpgp/multisig.scm new file mode 100755 index 000000000..3788f672f --- /dev/null +++ b/tests/openpgp/multisig.scm @@ -0,0 +1,172 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +;; Check that gpg verifies only signatures where there is no ambiguity +;; in the order of packets. Needs the Demo Keys Lima and Mike. +;; +;; Note: We do not support multiple signatures anymore thus this test is +;; not really needed because verify could do the same. We keep it anyway. + +(load (with-path "defs.scm")) + +(define sig-1ls1ls-valid " +-----BEGIN PGP ARMORED FILE----- + +kA0DAAIRN8q1H7eRA/gBrCdiBXRleHQxOogq9EkgYW0gc29ycnksIEkgY2FuJ3Qg +ZG8gdGhhdAqIPwMFADqIKvQ3yrUft5ED+BEC2joAoJaSaXOZEtSZqQ780HIXG77e +8PB7AJ4wCprmaFTO0fBaTcXDuEOBdAWnOZANAwACETfKtR+3kQP4AawnYgV0ZXh0 +MTqIKvRJIGFtIHNvcnJ5LCBJIGNhbid0IGRvIHRoYXQKiD8DBQA6iCr0N8q1H7eR +A/gRAto6AKCWkmlzmRLUmakO/NByFxu+3vDwewCeMAqa5mhUztHwWk3Fw7hDgXQF +pzk= +=8jSC +-----END PGP ARMORED FILE----- +") +(define sig-ls-valid " +-----BEGIN PGP ARMORED FILE----- + +rCdiBXRleHQxOogrS0kgYW0gc29ycnksIEkgY2FuJ3QgZG8gdGhhdAqIPwMFADqI +K0s3yrUft5ED+BECLQMAn2jZUNOpB4OuurSQkc2TRfg6ek02AJ9+oJS0frQ+yUsT +QDUFTH2PvZRxjw== +=J+lb +-----END PGP ARMORED FILE----- +") +(define sig-sl-valid " +-----BEGIN PGP ARMORED FILE----- + +iD8DBQA6iCtLN8q1H7eRA/gRAi0DAJ9o2VDTqQeDrrq0kJHNk0X4OnpNNgCffqCU +tH60PslLE0A1BUx9j72UcY+sJ2IFdGV4dDE6iCtLSSBhbSBzb3JyeSwgSSBjYW4n +dCBkbyB0aGF0Cg== +=N9MP +-----END PGP ARMORED FILE----- +") +(define sig-11lss-valid-but-is-not " +-----BEGIN PGP ARMORED FILE----- + +kA0DAAIRN8q1H7eRA/gAkA0DAAIRN8q1H7eRA/gBrCdiBXRleHQxOogyXUkgYW0g +c29ycnksIEkgY2FuJ3QgZG8gdGhhdAqIPwMFADqIMl03yrUft5ED+BECwQAAnRXT +mXjVd385oD38W80XuheWKTGcAJ9pZ6/flaKDfw+SLido7xaUHuhp5Yg/AwUAOogy +XTfKtR+3kQP4EQLBAACgnN0IP+NztE0aAc/DZ17yHWR9diwAniN0P01WmbgZJoZB +Q341WRXKS/at +=Ekrs +-----END PGP ARMORED FILE----- +") +(define sig-11lss11lss-valid-but-is-not " +-----BEGIN PGP ARMORED FILE----- + +kA0DAAIRN8q1H7eRA/gAkA0DAAIRN8q1H7eRA/gBrCdiBXRleHQxOogyXUkgYW0g +c29ycnksIEkgY2FuJ3QgZG8gdGhhdAqIPwMFADqIMl03yrUft5ED+BECwQAAnRXT +mXjVd385oD38W80XuheWKTGcAJ9pZ6/flaKDfw+SLido7xaUHuhp5Yg/AwUAOogy +XTfKtR+3kQP4EQLBAACgnN0IP+NztE0aAc/DZ17yHWR9diwAniN0P01WmbgZJoZB +Q341WRXKS/atkA0DAAIRN8q1H7eRA/gAkA0DAAIRN8q1H7eRA/gBrCdiBXRleHQx +OogyXUkgYW0gc29ycnksIEkgY2FuJ3QgZG8gdGhhdAqIPwMFADqIMl03yrUft5ED ++BECwQAAnRXTmXjVd385oD38W80XuheWKTGcAJ9pZ6/flaKDfw+SLido7xaUHuhp +5Yg/AwUAOogyXTfKtR+3kQP4EQLBAACgnN0IP+NztE0aAc/DZ17yHWR9diwAniN0 +P01WmbgZJoZBQ341WRXKS/at +=P1Mu +-----END PGP ARMORED FILE----- +") +(define sig-ssl-valid-but-is-not " +-----BEGIN PGP ARMORED FILE----- + +iD8DBQA6iCtLN8q1H7eRA/gRAi0DAJ9o2VDTqQeDrrq0kJHNk0X4OnpNNgCffqCU +tH60PslLE0A1BUx9j72UcY+IPwMFADqIK0s3yrUft5ED+BECLQMAn2jZUNOpB4Ou +urSQkc2TRfg6ek02AJ9+oJS0frQ+yUsTQDUFTH2PvZRxj6wnYgV0ZXh0MTqIK0tJ +IGFtIHNvcnJ5LCBJIGNhbid0IGRvIHRoYXQK +=Zven +-----END PGP ARMORED FILE----- +") +(define sig-1lsls-invalid " +-----BEGIN PGP ARMORED FILE----- + +kA0DAAIRN8q1H7eRA/gBrCdiBXRleHQxOogq9EkgYW0gc29ycnksIEkgY2FuJ3Qg +ZG8gdGhhdAqIPwMFADqIKvQ3yrUft5ED+BEC2joAoJaSaXOZEtSZqQ780HIXG77e +8PB7AJ4wCprmaFTO0fBaTcXDuEOBdAWnOawnYgV0ZXh0MTqIK0tJIGFtIHNvcnJ5 +LCBJIGNhbid0IGRvIHRoYXQKiD8DBQA6iCtLN8q1H7eRA/gRAi0DAJ9o2VDTqQeD +rrq0kJHNk0X4OnpNNgCffqCUtH60PslLE0A1BUx9j72UcY8= +=nkeu +-----END PGP ARMORED FILE----- +") +(define sig-lsls-invalid " +-----BEGIN PGP ARMORED FILE----- + +rCdiBXRleHQxOogrS0kgYW0gc29ycnksIEkgY2FuJ3QgZG8gdGhhdAqIPwMFADqI +K0s3yrUft5ED+BECLQMAn2jZUNOpB4OuurSQkc2TRfg6ek02AJ9+oJS0frQ+yUsT +QDUFTH2PvZRxj6wnYgV0ZXh0MTqIK0tJIGFtIHNvcnJ5LCBJIGNhbid0IGRvIHRo +YXQKiD8DBQA6iCtLN8q1H7eRA/gRAi0DAJ9o2VDTqQeDrrq0kJHNk0X4OnpNNgCf +fqCUtH60PslLE0A1BUx9j72UcY8= +=BlZH +-----END PGP ARMORED FILE----- +") +(define sig-lss-invalid " +-----BEGIN PGP ARMORED FILE----- + +rCdiBXRleHQxOogrS0kgYW0gc29ycnksIEkgY2FuJ3QgZG8gdGhhdAqIPwMFADqI +K0s3yrUft5ED+BECLQMAn2jZUNOpB4OuurSQkc2TRfg6ek02AJ9+oJS0frQ+yUsT +QDUFTH2PvZRxj4g/AwUAOogrSzfKtR+3kQP4EQItAwCfaNlQ06kHg666tJCRzZNF ++Dp6TTYAn36glLR+tD7JSxNANQVMfY+9lHGP +=jmt6 +-----END PGP ARMORED FILE----- +") +(define sig-slsl-invalid " +-----BEGIN PGP ARMORED FILE----- + +iD8DBQA6iCtLN8q1H7eRA/gRAi0DAJ9o2VDTqQeDrrq0kJHNk0X4OnpNNgCffqCU +tH60PslLE0A1BUx9j72UcY+sJ2IFdGV4dDE6iCtLSSBhbSBzb3JyeSwgSSBjYW4n +dCBkbyB0aGF0Cog/AwUAOogrSzfKtR+3kQP4EQItAwCfaNlQ06kHg666tJCRzZNF ++Dp6TTYAn36glLR+tD7JSxNANQVMfY+9lHGPrCdiBXRleHQxOogrS0kgYW0gc29y +cnksIEkgY2FuJ3QgZG8gdGhhdAo= +=phBF +-----END PGP ARMORED FILE----- +") + +(for-each-p + "Checking that a valid signature is verified as such" + (lambda (armored-file) + (tr:do + (tr:pipe-do + (pipe:echo (eval armored-file (current-environment))) + (pipe:spawn `(,@GPG --dearmor))) + (tr:spawn "" `(,@GPG --verify **in**)))) + '(sig-sl-valid)) + +;; ??? +;; +;; #for i in "$sig-11lss-valid-but-is-not" "$sig-11lss11lss-valid-but-is-not" \ +;; # "$sig-ssl-valid-but-is-not"; do +;; # echo "$i" | $GPG --dearmor >x +;; # $GPG --verify <x 2>/dev/null || error "valid is invalid" +;; #done + +(for-each-p + "Checking that an invalid signature is verified as such" + (lambda (armored-file) + (tr:do + (tr:pipe-do + (pipe:echo (eval armored-file (current-environment))) + (pipe:spawn `(,@GPG --dearmor))) + ;; XXX: this is ugly + (lambda args + (if (catch #f ;; verifikation failed, this is what we want + (apply (tr:spawn "" `(,@GPG --verify **in**)) args) + ;; verification succeded, this is an error. + #t) + (error "invalid signature is valid") + args)))) + '(sig-1ls1ls-valid sig-ls-valid sig-1lsls-invalid + sig-lsls-invalid sig-lss-invalid sig-slsl-invalid)) diff --git a/tests/openpgp/run-tests.scm b/tests/openpgp/run-tests.scm new file mode 100644 index 000000000..a921fdbe9 --- /dev/null +++ b/tests/openpgp/run-tests.scm @@ -0,0 +1,209 @@ +;; Test-suite runner. +;; +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(if (string=? "" (getenv "srcdir")) + (begin + (echo "Environment variable 'srcdir' not set. Please point it to" + "tests/openpgp.") + (exit 2))) + +;; Set objdir so that the tests can locate built programs. +(setenv "objdir" (getcwd) #f) + +(define test-pool + (package + (define (new procs) + (package + (define (add test) + (new (cons test procs))) + (define (wait) + (let ((unfinished (filter (lambda (t) (not t::retcode)) procs))) + (if (null? unfinished) + (package) + (let* ((commands (map (lambda (t) t::command) unfinished)) + (pids (map (lambda (t) t::pid) unfinished)) + (results + (map (lambda (pid retcode) (list pid retcode)) + pids + (wait-processes (map stringify commands) pids #t)))) + (new + (map (lambda (t) + (if t::retcode + t + (t::set-retcode (cadr (assoc t::pid results))))) + procs)))))) + (define (passed) + (filter (lambda (p) (= 0 p::retcode)) procs)) + (define (skipped) + (filter (lambda (p) (= 77 p::retcode)) procs)) + (define (hard-errored) + (filter (lambda (p) (= 99 p::retcode)) procs)) + (define (failed) + (filter (lambda (p) + (not (or (= 0 p::retcode) (= 77 p::retcode) + (= 99 p::retcode)))) + procs)) + (define (report) + (echo (length procs) "tests run," + (length (passed)) "succeeded," + (length (failed)) "failed," + (length (skipped)) "skipped.") + (length (failed))))))) + +(define (verbosity n) + (if (= 0 n) '() (cons '--verbose (verbosity (- n 1))))) + +(define test + (package + (define (scm name . args) + (new name #f `(,*argv0* ,@(verbosity *verbose*) ,@args + ,(in-srcdir name)) #f #f)) + (define (new name directory command pid retcode) + (package + (define (set-directory x) + (new name x command pid retcode)) + (define (set-retcode x) + (new name directory command pid x)) + (define (set-pid x) + (new name directory command x retcode)) + (define (run-sync) + (with-working-directory directory + (let* ((p (inbound-pipe)) + (pid (spawn-process-fd command CLOSED_FD + (:write-end p) (:write-end p)))) + (close (:write-end p)) + (splice (:read-end p) STDERR_FILENO) + (close (:read-end p)) + (let ((t' (set-retcode (wait-process name pid #t)))) + (t'::report) + t')))) + (define (run-sync-quiet) + (with-working-directory directory + (set-retcode + (wait-process + name (spawn-process-fd command CLOSED_FD CLOSED_FD CLOSED_FD) #t)))) + (define (run-async) + (with-working-directory directory + (set-pid (spawn-process-fd command CLOSED_FD CLOSED_FD CLOSED_FD)))) + (define (status) + (let ((t (assoc retcode '((0 "PASS") (77 "SKIP") (99 "ERROR"))))) + (if (not t) "FAIL" (cadr t)))) + (define (report) + (echo (string-append (status retcode) ":") name)))))) + +(define (run-tests-parallel-shared setup teardown . tests) + (setup::run-sync) + (let loop ((pool (test-pool::new '())) (tests' tests)) + (if (null? tests') + (let ((results (pool::wait))) + (for-each (lambda (t) (t::report)) results::procs) + (teardown::run-sync) + (exit (results::report))) + (let ((test (car tests'))) + (loop (pool::add (test::run-async)) (cdr tests')))))) + +(define (run-tests-parallel-isolated setup teardown . tests) + (let loop ((pool (test-pool::new '())) (tests' tests)) + (if (null? tests') + (let ((results (pool::wait))) + (for-each (lambda (t) + (let ((teardown' (teardown::set-directory t::directory))) + (teardown'::run-sync-quiet)) + (unlink-recursively t::directory) + (t::report)) results::procs) + (exit (results::report))) + (let* ((wd (mkdtemp "gpgscm-XXXXXX")) + (test (car tests')) + (test' (test::set-directory wd)) + (setup' (setup::set-directory wd))) + (setup'::run-sync-quiet) + (loop (pool::add (test'::run-async)) (cdr tests')))))) + +(define (run-tests-sequential-shared setup teardown . tests) + (let loop ((pool (test-pool::new '())) + (tests' `(,setup ,@tests ,teardown))) + (if (null? tests') + (let ((results (pool::wait))) + (exit (results::report))) + (let ((test (car tests'))) + (loop (pool::add (test::run-sync)) (cdr tests')))))) + +(define (run-tests-sequential-isolated setup teardown . tests) + (let loop ((pool (test-pool::new '())) (tests' tests)) + (if (null? tests') + (let ((results (pool::wait))) + (for-each (lambda (t) + (let ((teardown' (teardown::set-directory t::directory))) + (teardown'::run-sync-quiet)) + (unlink-recursively t::directory)) + results::procs) + (exit (results::report))) + (let* ((wd (mkdtemp "gpgscm-XXXXXX")) + (test (car tests')) + (test' (test::set-directory wd)) + (setup' (setup::set-directory wd))) + (setup'::run-sync-quiet) + (loop (pool::add (test'::run-sync)) (cdr tests')))))) + +(define all-tests + '("version.scm" + "mds.scm" + "decrypt.scm" + "decrypt-dsa.scm" + "sigs.scm" + "sigs-dsa.scm" + "encrypt.scm" + "encrypt-dsa.scm" + "seat.scm" + "clearsig.scm" + "encryptp.scm" + "detach.scm" + "detachm.scm" + "armsigs.scm" + "armencrypt.scm" + "armencryptp.scm" + "signencrypt.scm" + "signencrypt-dsa.scm" + "armsignencrypt.scm" + "armdetach.scm" + "armdetachm.scm" + "genkey1024.scm" + "conventional.scm" + "conventional-mdc.scm" + "multisig.scm" + "verify.scm" + "armor.scm" + "import.scm" + "ecc.scm" + "4gb-packet.scm" + "gpgtar.scm" + "use-exact-key.scm" + "default-key.scm")) + +(let* ((runner (if (member "--parallel" *args*) + (if (member "--shared" *args*) + run-tests-parallel-shared + run-tests-parallel-isolated) + (if (member "--shared" *args*) + run-tests-sequential-shared + run-tests-sequential-isolated))) + (tests' (filter (lambda (arg) (not (string-prefix? arg "--"))) *args*)) + (tests (if (null? tests') all-tests tests'))) + (apply runner (append (list (test::scm "setup.scm") (test::scm "finish.scm")) + (map test::scm tests)))) diff --git a/tests/openpgp/seat.scm b/tests/openpgp/seat.scm new file mode 100755 index 000000000..aceeccac1 --- /dev/null +++ b/tests/openpgp/seat.scm @@ -0,0 +1,30 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking encryption, signing, and producing armored output" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg usrpass1 '(--yes -seat -r two@example.com --passphrase-fd "0")) + (tr:gpg "" '(--yes)) + (tr:assert-weak-identity source))) + plain-files) diff --git a/tests/openpgp/setup.scm b/tests/openpgp/setup.scm new file mode 100755 index 000000000..6518dae41 --- /dev/null +++ b/tests/openpgp/setup.scm @@ -0,0 +1,121 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(echo "Creating test environment...") + +(letfd ((fd (open "random_seed" (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (call-with-fds (list (tool 'mktdata) "600") CLOSED_FD fd STDERR_FILENO)) + +(for-each-p + "Creating configuration files" + (lambda (name) + (file-copy (in-srcdir (string-append name ".tmpl")) name) + (let ((p (open-input-output-file name))) + (cond + ((string=? "gpg.conf" name) + (if have-opt-always-trust + (display "no-auto-check-trustdb\n" p)) + (display (string-append "agent-program " + (tool 'gpg-agent) + "|--debug-quick-random\n") p) + (display "allow-weak-digest-algos\n" p)) + ((string=? "gpg-agent.conf" name) + (display (string-append "pinentry-program " PINENTRY "\n") p))))) + '("gpg.conf" "gpg-agent.conf")) + +(echo "Starting gpg-agent...") +(call-check `(,(tool 'gpg-connect-agent) --verbose + ,(string-append "--agent-program=" (tool 'gpg-agent) + "|--debug-quick-random") + /bye)) + +(for-each-p "Creating sample data files" + (lambda (size) + (letfd ((fd (open (string-append "data-" (number->string size)) + (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (call-with-fds (list (tool 'mktdata) (number->string size)) + CLOSED_FD fd STDERR_FILENO))) + '(500 9000 32000 80000)) + +(define (dearmor source-name sink-name) + (letfd ((source (open source-name (logior O_RDONLY O_BINARY))) + (sink (open sink-name (logior O_WRONLY O_CREAT O_BINARY) #o600))) + (call-with-fds `(,@GPG --dearmor) source sink STDERR_FILENO))) + +(for-each-p "Unpacking samples" + (lambda (name) + (dearmor (in-srcdir (string-append name "o.asc")) name)) + '("plain-1" "plain-2" "plain-3" "plain-large")) + +;; XXX implement cleanup +(catch '() + (mkdir "private-keys-v1.d" "-rwx")) + +(define counter (make-counter)) +(for-each-p' "Storing private keys" + (lambda (name) + (dearmor (in-srcdir (string-append "/privkeys/" name ".asc")) + (string-append "private-keys-v1.d/" name ".key"))) + (lambda (name) (counter)) + '("50B2D4FA4122C212611048BC5FC31BD44393626E" + "7E201E28B6FEB2927B321F443205F4724EBE637E" + "13FDB8809B17C5547779F9D205C45F47CE0217CE" + "343D8AF79796EE107D645A2787A9D9252F924E6F" + "8B5ABF3EF9EB8D96B91A0B8C2C4401C91C834C34" + "0D6F6AD4C4C803B25470F9104E9F4E6A4CA64255" + "FD692BD59D6640A84C8422573D469F84F3B98E53" + "76F7E2B35832976B50A27A282D9B87E44577EB66" + "A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD" + "00FE67F28A52A8AA08FFAED20AF832DA916D1985" + "1DF48228FEFF3EC2481B106E0ACA8C465C662CC5" + "A2832820DC9F40751BDCD375BB0945BA33EC6B4C" + "ADE710D74409777B7729A7653373D820F67892E0" + "CEFC51AF91F68A2904FBFF62C4F075A4785B803F")) + +(info "Importing public demo and test keys") +(call-check `(,@GPG --yes --import + ,(in-srcdir "pubdemo.asc") + ,(in-srcdir "pubring.asc"))) +;; (letfd ((source (open (in-srcdir "pubring.pkr.asc") O_RDONLY))) +;; ((gpg-pipe '(--dearmor) '(--yes --import) STDERR_FILENO) +;; source CLOSED_FD)) +(pipe:do + (pipe:open (in-srcdir "pubring.pkr.asc") (logior O_RDONLY O_BINARY)) + (pipe:spawn `(,@GPG --dearmor)) + (pipe:spawn `(,@GPG --yes --import))) + +(info "Preset passphrases") +;; one@example.com +(call-check `(,(tool 'gpg-preset-passphrase) + --preset --passphrase def + "50B2D4FA4122C212611048BC5FC31BD44393626E")) +(call-check `(,(tool 'gpg-preset-passphrase) + --preset --passphrase def + "7E201E28B6FEB2927B321F443205F4724EBE637E")) +;; alpha@example.net +(call-check `(,(tool 'gpg-preset-passphrase) + --preset --passphrase abc + "76F7E2B35832976B50A27A282D9B87E44577EB66")) +(call-check `(,(tool 'gpg-preset-passphrase) + --preset --passphrase abc + "A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD")) + +(echo "All set up.") diff --git a/tests/openpgp/signencrypt-dsa.scm b/tests/openpgp/signencrypt-dsa.scm new file mode 100755 index 000000000..baf1def53 --- /dev/null +++ b/tests/openpgp/signencrypt-dsa.scm @@ -0,0 +1,48 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking signing and encryption using DSA" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg usrpass1 `(--yes --passphrase-fd "0" -se + -u ,dsa-usrname1 + --recipient ,dsa-usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) + +(define algos (if (have-hash-algo? "RIPEMD160") + '("SHA1" "RIPEMD160") + '("SHA1"))) +(for-each-p + "Checking signing and encryption using DSA with a specific hash algorithm" + (lambda (hash) + (tr:do + (tr:open (car plain-files)) + (tr:gpg usrpass1 `(--yes --passphrase-fd "0" -se + -u ,dsa-usrname1 + --recipient ,dsa-usrname2 + --digest-algo ,hash)) + (tr:gpg "" '(--yes)) + (tr:assert-identity (car plain-files)))) + algos) diff --git a/tests/openpgp/signencrypt.scm b/tests/openpgp/signencrypt.scm new file mode 100755 index 000000000..b138dce50 --- /dev/null +++ b/tests/openpgp/signencrypt.scm @@ -0,0 +1,39 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking signing and encryption" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg usrpass1 `(--yes --passphrase-fd "0" -se --recipient ,usrname2)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) + +(info "Checking bug 537: MDC problem with old style compressed packets.") +(lettmp (tmp) + (call-popen `(,@GPG --yes --passphrase-fd "0" + --output ,tmp ,(in-srcdir "bug537-test.data.asc")) + usrpass1) + (if (not (string=? "4336AE2A528FAE091E73E59E325B588FEE795F9B" + (cadar (gpg-hash-string `(--print-md SHA1 ,tmp) "")))) + (error "bug537-test.data.asc: mismatch (bug 537)"))) diff --git a/tests/openpgp/sigs-dsa.scm b/tests/openpgp/sigs-dsa.scm new file mode 100755 index 000000000..bf5e41501 --- /dev/null +++ b/tests/openpgp/sigs-dsa.scm @@ -0,0 +1,43 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking signing using DSA with the default hash algorithm" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes --sign --user ,dsa-usrname1)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) + +(define algos (if (have-hash-algo? "RIPEMD160") + '("SHA1" "RIPEMD160") + '("SHA1"))) +(for-each-p + "Checking signing using DSA with a specific hash algorithm" + (lambda (hash) + (tr:do + (tr:open (car plain-files)) + (tr:gpg "" `(--yes --sign --user ,dsa-usrname1 --digest-algo ,hash)) + (tr:gpg "" '(--yes)) + (tr:assert-identity (car plain-files)))) + algos) diff --git a/tests/openpgp/sigs.scm b/tests/openpgp/sigs.scm new file mode 100755 index 000000000..c47823108 --- /dev/null +++ b/tests/openpgp/sigs.scm @@ -0,0 +1,50 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(for-each-p + "Checking signing with the default hash algorithm" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" '(--yes --sign)) + (tr:gpg "" '(--yes)) + (tr:assert-identity source))) + (append plain-files data-files)) + +(for-each-p + "Checking signing with a specific hash algorithm" + (lambda (hash) + (if (have-pubkey-algo? "RSA") + ;; RSA key, so any hash is okay. + (tr:do + (tr:open (car plain-files)) + (tr:gpg "" `(--yes --sign --user ,usrname3 --digest-algo ,hash)) + (tr:gpg "" '(--yes)) + (tr:assert-identity (car plain-files)))) + (if (not (equal? "MD5" hash)) + ;; Using the DSA sig key - only 160 bit or larger hashes + (tr:do + (tr:open (car plain-files)) + (tr:gpg usrpass1 + `(--yes --sign --passphrase-fd "0" --digest-algo ,hash)) + (tr:gpg "" '(--yes)) + (tr:assert-identity (car plain-files))))) + all-hash-algos) diff --git a/tests/openpgp/use-exact-key.scm b/tests/openpgp/use-exact-key.scm new file mode 100755 index 000000000..bec537bb9 --- /dev/null +++ b/tests/openpgp/use-exact-key.scm @@ -0,0 +1,68 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +;; Import the sample key +;; +;; pub 1024R/8BC90111 2015-12-02 +;; Key fingerprint = E657 FB60 7BB4 F21C 90BB 6651 BC06 7AF2 8BC9 0111 +;; uid [ultimate] Barrett Brown <barrett@example.org> +;; sub 1024R/3E880CFF 2015-12-02 (encryption) +;; sub 1024R/F5F77B83 2015-12-02 (signing) +;; sub 1024R/45117079 2015-12-02 (encryption) +;; sub 1024R/1EA97479 2015-12-02 (signing) + +(info "Importing public key.") +(call-check + `(,(tool 'gpg) --import + ,(in-srcdir "samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc"))) + +;; By default, the most recent, valid signing subkey (1EA97479). +(for-each-p + "Checking that the most recent, valid signing subkey is used by default" + (lambda (keyid) + (tr:do + (tr:pipe-do + (pipe:defer (lambda (sink) (display "" (fdopen sink "w")))) + (pipe:gpg `(-s -u ,keyid)) + (pipe:gpg '(--verify --status-fd=1))) + (tr:call-with-content + (lambda (c) + (unless (string-contains? + c "VALIDSIG 5FBA84ACE02DCB17DA3DFF6BBCA43C441EA97479") + (exit 1)))))) + '("8BC90111" "3E880CFF" "F5F77B83" "45117079" "1EA97479")) + +;; But, if we request a particular signing key, we should get it. +(for-each-p + "Checking that we can select a specific signing key" + (lambda (keyid) + (tr:do + (tr:pipe-do + (pipe:defer (lambda (sink) (display "" (fdopen sink "w")))) + (pipe:gpg `(-s -u ,(string-append keyid "!"))) + (pipe:gpg '(--verify --status-fd=1))) + (tr:call-with-content + (lambda (c) + ;; XXX we do not have a regexp library + (unless (and (string-contains? c "VALIDSIG") + (string-contains? c keyid)) + (exit 1)))))) + '("8BC90111" "F5F77B83" "1EA97479")) diff --git a/tests/openpgp/verify.scm b/tests/openpgp/verify.scm new file mode 100755 index 000000000..de03db531 --- /dev/null +++ b/tests/openpgp/verify.scm @@ -0,0 +1,274 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +;; +;; Two simple tests to check that verify fails for bad input data +;; +(for-each-p + "Checking bogus signature" + (lambda (char) + (lettmp (x) + (pipe:do + (pipe:spawn `(,(tool 'mktdata) --char ,char "64")) + (pipe:write-to x (logior O_WRONLY O_CREAT O_BINARY) #o600)) + (if (= 0 (call `(,@GPG --verify ,x data-500))) + (error "no error code from verify")))) + '("0x2d" "0xca")) + +;; A plain signed message created using +;; echo abc | gpg --homedir . --passphrase-fd 0 -u Alpha -z0 -sa msg +(define msg_ols_asc " +-----BEGIN PGP MESSAGE----- + +kA0DAAIRLXJ8x2hpdzQBrQEHYgNtc2dEDFJaSSB0aGluayB0aGF0IGFsbCByaWdo +dC10aGlua2luZyBwZW9wbGUgaW4gdGhpcyBjb3VudHJ5IGFyZSBzaWNrIGFuZAp0 +aXJlZCBvZiBiZWluZyB0b2xkIHRoYXQgb3JkaW5hcnkgZGVjZW50IHBlb3BsZSBh +cmUgZmVkIHVwIGluIHRoaXMKY291bnRyeSB3aXRoIGJlaW5nIHNpY2sgYW5kIHRp +cmVkLiAgSSdtIGNlcnRhaW5seSBub3QuICBCdXQgSSdtCnNpY2sgYW5kIHRpcmVk +IG9mIGJlaW5nIHRvbGQgdGhhdCBJIGFtLgotIE1vbnR5IFB5dGhvbgqIPwMFAEQM +UlotcnzHaGl3NBECR4IAoJlEGTY+bHjD2HYuCixLQCmk01pbAKCIjkzLOAmkZNm0 +D8luT78c/1x45Q== +=a29i +-----END PGP MESSAGE----- +") + +;; A plain signed message created using +;; echo abc | gpg --homedir . --passphrase-fd 0 -u Alpha -sa msg +(define msg_cols_asc " +-----BEGIN PGP MESSAGE----- + +owGbwMvMwCSoW1RzPCOz3IRxLSN7EnNucboLT6Cgp0JJRmZeNpBMLFFIzMlRKMpM +zyjRBQtm5qUrFKTmF+SkKmTmgdQVKyTnl+aVFFUqJBalKhRnJmcrJOalcJVkFqWm +KOSnKSSlgrSU5OekQMzLL0rJzEsEKk9JTU7NK4EZBtKcBtRRWgAzlwtmbnlmSQbU +GJjxCmDj9RQUPNVzFZJTi0oSM/NyKhXy8kuAYk6lJSBxLlTF2NziqZCYq8elq+Cb +n1dSqRBQWZKRn8fVYc/MygAKBljYCDIFiTDMT+9seu836Q+bevyHTJ0dzPNuvCjn +ZpgrwX38z58rJsfYDhwOSS4SkN/d6vUAAA== +=s6sY +-----END PGP MESSAGE----- +") + +;; A PGP 2 style message. +(define msg_sl_asc " +-----BEGIN PGP MESSAGE----- + +iD8DBQBEDFJaLXJ8x2hpdzQRAkeCAKCZRBk2Pmx4w9h2LgosS0AppNNaWwCgiI5M +yzgJpGTZtA/Jbk+/HP9ceOWtAQdiA21zZ0QMUlpJIHRoaW5rIHRoYXQgYWxsIHJp +Z2h0LXRoaW5raW5nIHBlb3BsZSBpbiB0aGlzIGNvdW50cnkgYXJlIHNpY2sgYW5k +CnRpcmVkIG9mIGJlaW5nIHRvbGQgdGhhdCBvcmRpbmFyeSBkZWNlbnQgcGVvcGxl +IGFyZSBmZWQgdXAgaW4gdGhpcwpjb3VudHJ5IHdpdGggYmVpbmcgc2ljayBhbmQg +dGlyZWQuICBJJ20gY2VydGFpbmx5IG5vdC4gIEJ1dCBJJ20Kc2ljayBhbmQgdGly +ZWQgb2YgYmVpbmcgdG9sZCB0aGF0IEkgYW0uCi0gTW9udHkgUHl0aG9uCg== +=0ukK +-----END PGP MESSAGE----- +") + +;; An OpenPGP message lacking the onepass packet. We used to accept +;; such messages but now consider them invalid. +(define bad_ls_asc " +-----BEGIN PGP MESSAGE----- + +rQEHYgNtc2dEDFJaSSB0aGluayB0aGF0IGFsbCByaWdodC10aGlua2luZyBwZW9w +bGUgaW4gdGhpcyBjb3VudHJ5IGFyZSBzaWNrIGFuZAp0aXJlZCBvZiBiZWluZyB0 +b2xkIHRoYXQgb3JkaW5hcnkgZGVjZW50IHBlb3BsZSBhcmUgZmVkIHVwIGluIHRo +aXMKY291bnRyeSB3aXRoIGJlaW5nIHNpY2sgYW5kIHRpcmVkLiAgSSdtIGNlcnRh +aW5seSBub3QuICBCdXQgSSdtCnNpY2sgYW5kIHRpcmVkIG9mIGJlaW5nIHRvbGQg +dGhhdCBJIGFtLgotIE1vbnR5IFB5dGhvbgqIPwMFAEQMUlotcnzHaGl3NBECR4IA +oJlEGTY+bHjD2HYuCixLQCmk01pbAKCIjkzLOAmkZNm0D8luT78c/1x45Q== +=Mpiu +-----END PGP MESSAGE----- +") + + +;; A signed message prefixed with an unsigned literal packet. +;; (fols = faked-literal-data, one-pass, literal-data, signature) +;; This should throw an error because running gpg to extract the +;; signed data will return both literal data packets +(define bad_fols_asc " +-----BEGIN PGP MESSAGE----- + +rF1iDG1zZy51bnNpZ25lZEQMY0x0aW1lc2hhcmluZywgbjoKCUFuIGFjY2VzcyBt +ZXRob2Qgd2hlcmVieSBvbmUgY29tcHV0ZXIgYWJ1c2VzIG1hbnkgcGVvcGxlLgqQ +DQMAAhEtcnzHaGl3NAGtAQdiA21zZ0QMUlpJIHRoaW5rIHRoYXQgYWxsIHJpZ2h0 +LXRoaW5raW5nIHBlb3BsZSBpbiB0aGlzIGNvdW50cnkgYXJlIHNpY2sgYW5kCnRp +cmVkIG9mIGJlaW5nIHRvbGQgdGhhdCBvcmRpbmFyeSBkZWNlbnQgcGVvcGxlIGFy +ZSBmZWQgdXAgaW4gdGhpcwpjb3VudHJ5IHdpdGggYmVpbmcgc2ljayBhbmQgdGly +ZWQuICBJJ20gY2VydGFpbmx5IG5vdC4gIEJ1dCBJJ20Kc2ljayBhbmQgdGlyZWQg +b2YgYmVpbmcgdG9sZCB0aGF0IEkgYW0uCi0gTW9udHkgUHl0aG9uCog/AwUARAxS +Wi1yfMdoaXc0EQJHggCgmUQZNj5seMPYdi4KLEtAKaTTWlsAoIiOTMs4CaRk2bQP +yW5Pvxz/XHjl +=UNM4 +-----END PGP MESSAGE----- +") + +;; A signed message suffixed with an unsigned literal packet. +;; (fols = faked-literal-data, one-pass, literal-data, signature) +;; This should throw an error because running gpg to extract the +;; signed data will return both literal data packets +(define bad_olsf_asc " +-----BEGIN PGP MESSAGE----- + +kA0DAAIRLXJ8x2hpdzQBrQEHYgNtc2dEDFJaSSB0aGluayB0aGF0IGFsbCByaWdo +dC10aGlua2luZyBwZW9wbGUgaW4gdGhpcyBjb3VudHJ5IGFyZSBzaWNrIGFuZAp0 +aXJlZCBvZiBiZWluZyB0b2xkIHRoYXQgb3JkaW5hcnkgZGVjZW50IHBlb3BsZSBh +cmUgZmVkIHVwIGluIHRoaXMKY291bnRyeSB3aXRoIGJlaW5nIHNpY2sgYW5kIHRp +cmVkLiAgSSdtIGNlcnRhaW5seSBub3QuICBCdXQgSSdtCnNpY2sgYW5kIHRpcmVk +IG9mIGJlaW5nIHRvbGQgdGhhdCBJIGFtLgotIE1vbnR5IFB5dGhvbgqIPwMFAEQM +UlotcnzHaGl3NBECR4IAoJlEGTY+bHjD2HYuCixLQCmk01pbAKCIjkzLOAmkZNm0 +D8luT78c/1x45axdYgxtc2cudW5zaWduZWREDGNMdGltZXNoYXJpbmcsIG46CglB +biBhY2Nlc3MgbWV0aG9kIHdoZXJlYnkgb25lIGNvbXB1dGVyIGFidXNlcyBtYW55 +IHBlb3BsZS4K +=3gnG +-----END PGP MESSAGE----- +") + + +;; Two standard signed messages in a row +(define msg_olsols_asc_multiple " +-----BEGIN PGP MESSAGE----- + +kA0DAAIRLXJ8x2hpdzQBrQEHYgNtc2dEDFJaSSB0aGluayB0aGF0IGFsbCByaWdo +dC10aGlua2luZyBwZW9wbGUgaW4gdGhpcyBjb3VudHJ5IGFyZSBzaWNrIGFuZAp0 +aXJlZCBvZiBiZWluZyB0b2xkIHRoYXQgb3JkaW5hcnkgZGVjZW50IHBlb3BsZSBh +cmUgZmVkIHVwIGluIHRoaXMKY291bnRyeSB3aXRoIGJlaW5nIHNpY2sgYW5kIHRp +cmVkLiAgSSdtIGNlcnRhaW5seSBub3QuICBCdXQgSSdtCnNpY2sgYW5kIHRpcmVk +IG9mIGJlaW5nIHRvbGQgdGhhdCBJIGFtLgotIE1vbnR5IFB5dGhvbgqIPwMFAEQM +UlotcnzHaGl3NBECR4IAoJlEGTY+bHjD2HYuCixLQCmk01pbAKCIjkzLOAmkZNm0 +D8luT78c/1x45ZANAwACES1yfMdoaXc0Aa0BB2IDbXNnRAxSWkkgdGhpbmsgdGhh +dCBhbGwgcmlnaHQtdGhpbmtpbmcgcGVvcGxlIGluIHRoaXMgY291bnRyeSBhcmUg +c2ljayBhbmQKdGlyZWQgb2YgYmVpbmcgdG9sZCB0aGF0IG9yZGluYXJ5IGRlY2Vu +dCBwZW9wbGUgYXJlIGZlZCB1cCBpbiB0aGlzCmNvdW50cnkgd2l0aCBiZWluZyBz +aWNrIGFuZCB0aXJlZC4gIEknbSBjZXJ0YWlubHkgbm90LiAgQnV0IEknbQpzaWNr +IGFuZCB0aXJlZCBvZiBiZWluZyB0b2xkIHRoYXQgSSBhbS4KLSBNb250eSBQeXRo +b24KiD8DBQBEDFJaLXJ8x2hpdzQRAkeCAKCZRBk2Pmx4w9h2LgosS0AppNNaWwCg +iI5MyzgJpGTZtA/Jbk+/HP9ceOU= +=8nLN +-----END PGP MESSAGE----- +") + +;; A standard message with two signatures (actually the same signature +;; duplicated). +(define msg_oolss_asc " +-----BEGIN PGP MESSAGE----- + +kA0DAAIRLXJ8x2hpdzQBkA0DAAIRLXJ8x2hpdzQBrQEHYgNtc2dEDFJaSSB0aGlu +ayB0aGF0IGFsbCByaWdodC10aGlua2luZyBwZW9wbGUgaW4gdGhpcyBjb3VudHJ5 +IGFyZSBzaWNrIGFuZAp0aXJlZCBvZiBiZWluZyB0b2xkIHRoYXQgb3JkaW5hcnkg +ZGVjZW50IHBlb3BsZSBhcmUgZmVkIHVwIGluIHRoaXMKY291bnRyeSB3aXRoIGJl +aW5nIHNpY2sgYW5kIHRpcmVkLiAgSSdtIGNlcnRhaW5seSBub3QuICBCdXQgSSdt +CnNpY2sgYW5kIHRpcmVkIG9mIGJlaW5nIHRvbGQgdGhhdCBJIGFtLgotIE1vbnR5 +IFB5dGhvbgqIPwMFAEQMUlotcnzHaGl3NBECR4IAoJlEGTY+bHjD2HYuCixLQCmk +01pbAKCIjkzLOAmkZNm0D8luT78c/1x45Yg/AwUARAxSWi1yfMdoaXc0EQJHggCg +mUQZNj5seMPYdi4KLEtAKaTTWlsAoIiOTMs4CaRk2bQPyW5Pvxz/XHjl +=KVw5 +-----END PGP MESSAGE----- +") + +;; A standard message with two one-pass packet but only one signature +;; packet +(define bad_ools_asc " +-----BEGIN PGP MESSAGE----- + +kA0DAAIRLXJ8x2hpdzQBkA0DAAIRLXJ8x2hpdzQBrQEHYgNtc2dEDFJaSSB0aGlu +ayB0aGF0IGFsbCByaWdodC10aGlua2luZyBwZW9wbGUgaW4gdGhpcyBjb3VudHJ5 +IGFyZSBzaWNrIGFuZAp0aXJlZCBvZiBiZWluZyB0b2xkIHRoYXQgb3JkaW5hcnkg +ZGVjZW50IHBlb3BsZSBhcmUgZmVkIHVwIGluIHRoaXMKY291bnRyeSB3aXRoIGJl +aW5nIHNpY2sgYW5kIHRpcmVkLiAgSSdtIGNlcnRhaW5seSBub3QuICBCdXQgSSdt +CnNpY2sgYW5kIHRpcmVkIG9mIGJlaW5nIHRvbGQgdGhhdCBJIGFtLgotIE1vbnR5 +IFB5dGhvbgqIPwMFAEQMUlotcnzHaGl3NBECR4IAoJlEGTY+bHjD2HYuCixLQCmk +01pbAKCIjkzLOAmkZNm0D8luT78c/1x45Q== +=1/ix +-----END PGP MESSAGE----- +") + +;; Standard cleartext signature +(define msg_cls_asc " +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +I think that all right-thinking people in this country are sick and +tired of being told that ordinary decent people are fed up in this +country with being sick and tired. I'm certainly not. But I'm +sick and tired of being told that I am. +- - Monty Python +-----BEGIN PGP SIGNATURE----- + +iD8DBQFEDVp1LXJ8x2hpdzQRAplUAKCMfpG3GPw/TLN52tosgXP5lNECkwCfQhAa +emmev7IuQjWYrGF9Lxj+zj8= +=qJsY +-----END PGP SIGNATURE----- +") + +;; Cleartext signature with two signatures +(define msg_clss_asc " +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +What is the difference between a Turing machine and the modern computer? +It's the same as that between Hillary's ascent of Everest and the +establishment of a Hilton on its peak. +-----BEGIN PGP SIGNATURE----- + +iD8DBQFEDVz6LXJ8x2hpdzQRAtkGAKCeMhNbHnh339fpjNj9owsYcC4zBwCfYO5l +2u+KEfXX0FKyk8SMzLjZ536IPwMFAUQNXPr+GAsdqeOwshEC2QYAoPOWAiQm0EF/ +FWIAQUplk7JWbyRKAJ92ZJyJpWfzb0yc1s7MY65r2qEHrg== +=1Xvv +-----END PGP SIGNATURE----- +") + +;; Two clear text signatures in a row +(define msg_clsclss_asc_multiple (string-append msg_cls_asc msg_clss_asc)) + +;; Fixme: We need more tests with manipulated cleartext signatures. + +;; +;; Now run the tests. +;; +(for-each-p + "Checking that a valid signature is verified as such" + (lambda (armored-file) + (pipe:do + (pipe:echo (eval armored-file (current-environment))) + (pipe:spawn `(,@GPG --verify)))) + '(msg_ols_asc msg_cols_asc msg_sl_asc msg_oolss_asc msg_cls_asc msg_clss_asc)) + +(for-each-p + "Checking that a valid signature over multiple messages is verified as such" + (lambda (armored-file) + (pipe:do + (pipe:echo (eval armored-file (current-environment))) + (pipe:spawn `(,@GPG --verify --allow-multiple-messages))) + (catch '() + (pipe:do + (pipe:defer (lambda (sink) + (display armored-file (fdopen sink "w")))) + (pipe:spawn `(,@GPG --verify))) + (error "verification succeded but should not"))) + '(msg_olsols_asc_multiple msg_clsclss_asc_multiple)) + +(for-each-p + "Checking that an invalid signature is verified as such" + (lambda (armored-file) + (catch '() + (pipe:do + (pipe:echo (eval armored-file (current-environment))) + (pipe:spawn `(,@GPG --verify))) + (error "verification succeded but should not"))) + '(bad_ls_asc bad_fols_asc bad_olsf_asc bad_ools_asc)) diff --git a/tests/openpgp/version.scm b/tests/openpgp/version.scm new file mode 100755 index 000000000..dbcb484e8 --- /dev/null +++ b/tests/openpgp/version.scm @@ -0,0 +1,25 @@ +#!/usr/bin/env gpgscm + +;; Copyright (C) 2016 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG 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 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG 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 General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(load (with-path "defs.scm")) + +(info "Printing the GPG version") +(assert (= 0 (call `(,@GPG --version)))) + +;; fixme: check that the output is as expected |