Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/NEWS b/NEWS
index e12e295e9..c13006e9c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5145 +1,5147 @@
Noteworthy changes in version 2.2.42 (unreleased)
-------------------------------------------------
* gpg: Set default expiration date to 3 years. [T2701]
* gpg: Support OCB encryption. [T6263]
* gpg: New command --quick-update-pref. [rGf16c946be7]
* gpg: New list-options show-pref and show-pref-verbose.
[rGb6ba7054a0]
* gpg: Add modes 16 and 30 to --gen-random.
* gpg: Emit status line and proper diagnostics for write errors.
[T6528]
* gpg: Make progress work for large files on Windows. [T6534]
* gpgsm: Support ECC certificates. [T6253]
+ * gpg: Make progress work for large files on Windows. [T6534]
+
* gpgsm: Also announce AES256-CBC in signatures. [rGaa397fdcdb21]
Release-info: https://dev.gnupg.org/T6307
Noteworthy changes in version 2.2.41 (2022-12-09)
-------------------------------------------------
* gpg: Add a notation to encryption subkeys in de-vs mode. [T6279]
* gpg: Fix trusted introducer for mbox only user-ids. [T6238]
* gpg: Report an error via status-fd for receiving a key from the
agent. [T5151]
* gpg: Make --require-compliance work without the --status-fd
option. [r11f3232716]
* gpg: Improve signature verification speed by a factor of more than
four. Double detached signing speed. [T5826]
* gpg: New --export-filter export-revocs. [rGedbe30c152]
* gpg: Import stray revocation certificates to improve WKD
usability. [rGbd825ead36af]
* wkd: New option --add-revocs for gpg-wks-client. [rG2f4492f3be]
* wkd: Ignore expired user-ids in gpg-wks-client. [T6292]
* scd: Support the Telesec Signature Card v2.0. [T6252]
* Fix build regression depending on libgpg-error version. [T6244]
Release-info: https://dev.gnupg.org/T6280
Noteworthy changes in version 2.2.40 (2022-10-10)
-------------------------------------------------
* gpg: Do not consider unknown public keys as non-compliant while
decrypting. [T6205]
* gpg: Avoid to emit a compliance mode line if Libgcrypt is
non-compliant. [T6221]
* gpg: In de-vs mode use AES-128 instead of 3-DES as implicit
preference. [T6063]
* gpgsm: Fix reporting of bad passphrase error during PKCS#11
import. [T5713,T6037]
* dirmngr: Fix CRL Distribution Point fallback to other schemes.
[rG289fbc550d]
* dirmngr: New LDAP server flag "areconly" (A-record-only).
[rG6300035ba1]
* dirmngr: Fix upload of multiple keys for an LDAP server specified
using the colon format. [rG32ce7ac0c6]
* dirmngr: Use LDAP schema v2 when a Base DN is specified. [T6047]
* wkd: New command --mirror for gpg-wks-client. [T6224]
Release-info: https://dev.gnupg.org/T6181
Noteworthy changes in version 2.2.39 (2022-09-02)
-------------------------------------------------
* agent: Fix regression in 2.2.37 related to non-extended format
private keys. [T6176]
Release-info: https://dev.gnupg.org/T6175
Noteworthy changes in version 2.2.38 (2022-09-01)
-------------------------------------------------
* gpg: Make --require-compliance work for sign+encrypt. [T6174]
* gpg: Fix an encoding problem under Windows in the printed
timezone. [T5073]
* gpg: Emit a FAILURE status for --require-compliance errors.
[rGe05fb5ca37]
* dirmngr: Avoid caching expired certificates. [T6142]
Release-info: https://dev.gnupg.org/T6159
Noteworthy changes in version 2.2.37 (2022-08-24)
-------------------------------------------------
* gpg: In de-vs mode use SHA-256 instead of SHA-1 as implicit
preference. [T6043, T6063]
* gpg: Actually show symmetric+pubkey encrypted data as de-vs
compliant. Add extra compliance checks for symkey_enc packets.
[T6119]
* gpg: Request keygrip of key to be added via command-fd interface.
[T5771]
* gpg: Look up user ID to revoke by UID hash. [T5936]
* gpg: Fix wrong error message for "keytocard". [T6122]
* gpg: --card-status shows the application type for non-openpgp
cards again. [rG8e393e2592]
* gpg: The options --auto-key-import and --include-key-block are
again listed by gpgconf. [T6138]
* gpgsm: New option --compatibility-flags. [rG77b6896f7a]
* agent: New options --no-user-trustlist and --sys-trustlist-name.
[T5990]
* agent: Track and update the Display-S/N of cards so that the
"please insert card" prompt may now show more information. Use
"gpg --card-status" to update stored card meta data. [T6135]
* scd:openpgp: Fix problem with ECC algorithm attributes on
Yubikeys. [rG225c66f13b87]
* scd:openpgp: Fix problem with Yubikey 5.4 firmware. [T6070]
* dirmngr: Ask keyservers to provide the key fingerprints. [T5741]
* ssh: Allow authentication as used by OpenSSH's PQ crypto support.
[T5935]
* wkd: Fix path traversal attack in gpg-wks-server. Add the mail
address to the pending request data. [rGc1489ca0e1, T6098]
* gpgconf: Improve registry dumping. [rG6bc9592318]
* Silence warnings from AllowSetForegroundWindow. [rG6583abedf3]
Release-info: https://dev.gnupg.org/T6105
Noteworthy changes in version 2.2.36 (2022-07-06)
-------------------------------------------------
* g10: Fix possibly garbled status messages in NOTATION_DATA. This
bug could trick GPGME and other parsers to accept faked status
lines. [T6027, CVE-2022-34903]
* gpg: Handle leading zeroes in Ed25519 private keys and reverse
change regarding Ed25519 SOS encoding as introduced with 2.2.34.
[T5120]
* gpg: Allow Unicode file names for iobuf_cancel under Windows.
* gpgsm: Improve pkcs#12 import. [T6037,T5793,T4921,T4757]
* scd,p15: Fix reading certificates w/o length info.
* scd,p15: Improve the displayed S/N for Technology Nexus cards.
* scd,openpgp: Add workaround for ECC attribute on Yubikey. [T5963]
* scd: Fix use of SCardListReaders for PC/SC. [T5979]
* gpgconf: New short options -X and -V.
* Make sure to always set CONFIDENTIAL flag in Assuan. [T5977]
Release-info: https://dev.gnupg.org/T5949
Noteworthy changes in version 2.2.35 (2022-04-25)
-------------------------------------------------
* gpg,gpgsm: New option --require-compliance. [17890d4318]
* gpgtar: New option --with-log. [rGce69d55f70]
* gpg: Threefold decryption speedup for large files.
[T5820,rG9116fd1e9a]
* gpgtar: Support file names longer than MAX_PATH. [rG5492079def]
* scdaemon: Add support for GeNUA cards. [rG44ec383cde]
* gpg: Allow decryption of symmetric encrypted data even for
non-compliant cipher. [rGe081a601f7]
* gpg: Avoid possible race condition in --edit-card/factory-reset.
[T5831]
* gpg: Emit an ERROR status as hint for a bad passphrase. [T5943]
* gpg: Avoid NULL-ptr access due to corrupted packets. [T5940]
* gpgsm: Fix parsing of certain PKCS#12 files. [T5793]
* gpgtar: Use a pipe for decryption and thus avoid memory
exhaustion. [rGd431feb307]
* scdaemon: Use extended mode for pkcs#15 already for rsa2048.
[rGa2db490de5]
* dirmngr: Make WKD lookups work for resolvers not handling SRV
records. [T4729]
* dirmngr: Escape more characters in WKD requests. [T5902]
* gpgconf: Silence warnings from parsing the option files. [T5874]
* Improve removing of stale lockfiles under Unix. [T5884]
Release-info: https://dev.gnupg.org/T5928
Noteworthy changes in version 2.2.34 (2022-02-07)
-------------------------------------------------
* gpgconf: Backport the improved option reading and writing code
from 2.3. [rG7a3a1ef370,T4788]
* gpgconf: Do not list ignored options and mark forced options as
read-only. [T5732]
* gpgconf: Correctly show registry entries with --show-configs.
[T5724]
* gpgconf: Add command aliases -L, -K, and -R. [rGf16c535eee]
* gpgconf: Tweak the use of the ldapserver option. [T5801]
* gpgconf: Make "--launch gpg-agent" work again. [rG5a7ed6dd8f]
* gpg: Accept Ed25519 private keys in modernized encoding. [T5120]
* gpg: Fix adding the list of ultimate trusted keys. [T5742]
* gpgsm: New option --ignore-cert-with-oid. [rGbcf446b70c]
* dirmngr: Avoid initial delay on the first keyserver access in
presence of --no-use-tor. [rGdde88897e2]
* scdaemon: Also prefer Yubikeys if no reader port is given.
[rG38c666ec3f]
* agent: Make missing strings translatable and update German and
Japanese translations. [T4777]
* ssh: Fix adding an ed25519 key with a zero length comment. [T5794]
* gpgtar: Create and handle extended headers to support long file
names. [T5754]
* Fix the creation of socket directories under Windows for non-ascii
account names. [rG7d1215cb9c]
* Improve the registry HKCU->HKLM fallback. [rG96db487a4d]
* Prettify the --help output of most commands.
Release-info: https://dev.gnupg.org/T5703
See-also: gnupg-announce/2022q1/000470.html
Noteworthy changes in version 2.2.33 (2021-11-23)
-------------------------------------------------
* gpg: New option --min-rsa-length. [rG6ee01c1d26]
* gpg: New option --forbid-gen-key. [rG985fb25c46]
* gpg: New option --override-compliance-check. [T5655]
* gpgconf: New command --show-configs. [rG8fe3f57643]
* agent,dirmngr: New option --steal-socket. [rG6507c6ab10]
* scd: Improve the selection of the default PC/SC reader. [T5644]
* gpg: Fix printing of binary notations. [T5667]
* gpg: Remove stale ultimately trusted keys from the trustdb. [T5685]
* gpgsm: Detect circular chains in --list-chain. [rGc9343bec83]
* gpgconf: Create the local option file even if the global file
exists. [T5650]
* dirmngr: Make reading resolv.conf more robust. [T5657]
* gpg-wks-server: Fix created file permissions. [rGf54feb4470]
* scd: Support longer data for ssh-agent authentication with openpgp
cards. [T5682]
* Support gpgconf.ctl for NetBSD and Solaris. [T5656,T5671]
* Silence "Garbled console data" warning under Windows in most
cases.
* Silence warning about the rootdir under Unices w/o a mounted /proc
file system.
* Fix possible build problems about missing include files. [T5592]
* i18n: Replace the term "PIN-Cache" by "Passswort-Cache" in the
German translation. [rgf453d52e53]
* i18n: Update the Russian translation.
Release-info: https://dev.gnupg.org/T5641
See-also: gnupg-announce/2021q4/000467.html
Noteworthy changes in version 2.2.32 (2021-10-06)
-------------------------------------------------
* dirmngr: Fix Let's Encrypt certificate chain validation. [T5639]
* dirmngr: New option --ignore-cert. [323a20399d]
* gpg: Fix --list-packets for AEAD packets with unknown key. [T5584]
Release-info: https://dev.gnupg.org/T5601
See-also: gnupg-announce/2021q4/000465.html
Noteworthy changes in version 2.2.31 (2021-09-15)
-------------------------------------------------
* agent: Fix a regression in GET_PASSPHRASE. [#5577]
* scd: Fix an assertion failure in close_pcsc_reader. [67e1834ad4]
* scd: Add support for PC/SC in "GETINFO reader_list".
Release-info: https://dev.gnupg.org/T5571
See-also: gnupg-announce/2021q3/000464.html
Noteworthy changes in version 2.2.30 (2021-08-26)
-------------------------------------------------
* gpg: Extended gpg-check-pattern to support accept rules,
conjunctions, and case-sensitive matching. [5ca15e58b2]
* agent: New option --pinentry-formatted-passphrase. [#5553]
* agent: New option --check-sym-passphrase-pattern. [#5517]
* agent: Use the sysconfdir for the pattern files. [5ed8e598fa]
* agent: Add "checkpin" inquiry for use by pinentry. [#5532]
* wkd: Fix client issue with leading or trailing spaces in
user-ids. [576e429d41]
* Pass XDG_SESSION_TYPE and QT_QPA_PLATFORM envvars to Pinentry.
[#3659]
* Under Windows use LOCAL_APPDATA for the socket directory. [#5537]
Release-info: https://dev.gnupg.org/T5519
See-also: gnupg-announce/2021q3/000463.html
Noteworthy changes in version 2.2.29 (2021-07-04)
-------------------------------------------------
* Fix regression in 2.2.28 for Yubikey NEO. [#5487]
* Change the default keyserver to keyserver.ubuntu.com. This is a
temporary change due to the shutdown of the SKS keyserver pools.
[47c4e3e00a]
* gpg: Let --fetch-key return an exit code on failure. [#5376]
* dirmngr: Fix regression in KS_GET for mail address pattern.
[#5497]
* Add fallback in case the Windows console can't cope with Unicode.
[#5491]
* Improve initialization of SPR532 in the CCID driver and make the
driver more robust. [#5297,b90c55fa66db]
* Make test suite work in presence of a broken Libgcrypt
installation. [#5502]
* Make configure option --disable-ldap work again.
Release-info: https://dev.gnupg.org/T5498
See-also: gnupg-announce/2021q3/000461.html
Noteworthy changes in version 2.2.28 (2021-06-10)
-------------------------------------------------
* gpg: Auto import keys specified with --trusted-keys.
[e7251be84c79]
* gpg: Allow decryption w/o public key but with correct card
inserted. [e53f6037283e]
* gpg: Allow fingerprint based lookup with --locate-external-key.
[2af217ecd7e4]
* gpg: Lookup a missing public key of the current card via LDAP.
[b59af0e2a05a]
* gpg: New option --force-sign-key. [#4584]
* gpg: Use a more descriptive password prompt for symmetric
decryption. [03f83bcda5d1]
* gpg: Do not use the self-sigs-only option for LDAP keyserver
imports. [#5387]
* gpg: Keep temp files when opening images via xdg-open.
[0441ed6e1c]
* gpg: Fix mailbox based search via AKL keyserver method.
[22fe23f46d31]
* gpg: Fix sending an OpenPGP key with umlaut to an LDAP keyserver.
[7bf8530e75d0]
* gpg: Allow ECDH with a smartcard returning only the x-coordinate.
[b203325ce1]
* gpgsm: New option --ldapserver as an alias for --keyserver. Note
that configuring servers in gpgsm and gpg is deprecated; please
use the dirmngr configuration options.
* gpgsm: Support AES-GCM decryption. [b722fd755c77]
* gpgsm: Support decryption of password protected files.
[6f31acac767f]
* gpgsm: Lock keyboxes also during a search to fix lockups on
Windows. [#4505]
* agent: Skip unknown unknown ssh curves seen on
cards. [bbf4bd3bfcb5]
* scdaemon: New option --pcsc-shared. [5eec40f3d827]
* scdaemon: Backport PKCS#15 card support from GnuPG 2.3
[7637d39fe20e]
* scdaemon: Fix CCID driver for SCM SPR332/SPR532. [#5297]
* scdaemon: Fix possible PC/SC removed card problem. [9d83bfb63968]
* scdaemon: Fix unblock PIN by a Reset Code with KDF. [#5413]
* scdaemon: Support compressed points. [96577e2e46e4]
* scdaemon: Prettify S/N for Yubikeys and fix reading for early
Yubikey 5 tokens. [f8588369bcb0,#5442]
* dirmngr: New option --ldapserver to avoid the need for the
separate dirmngr_ldapservers.conf file.
* dirmngr: The dirmngr_ldap wrapper has been rewritten to properly
support ldap-over-tls and starttls for X.509 certificates and
CRLs. [39815c023f03]
* dirmngr: OpenPGP LDAP keyservers may now also be configured using
the same syntax as used for X.509 and CRL LDAP servers. This
avoids the former cumbersome quoting rules and adds a flexible set
of flags to control the connection. [2b4cddf9086f]
* dirmngr: The "ldaps" scheme of an OpenPGP keyserver URL is now
interpreted as ldap-with-starttls on port 389. To use the
non-standardized ldap-over-tls the new LDAP configuration method
of the new attribute "gpgNtds" needs to be used. [55f46b33df08]
* dirmngr: Return the fingerprint as search result also for LDAP
OpenPGP keyservers. This requires the modernized LDAP schema.
[#5441]
* dirmngr: An OpenPGP LDAP search by a mailbox now ignores revoked
keys. [b6f8cd7eef4b]
* gpgconf: Make runtime changes with non-default homedir work.
[c8f0b02936c7]
* gpgconf: Do not translate an empty string to the PO file's meta
data. [#5363]
* gpgconf: Fix argv overflow if --homedir is used. [#5366]
* gpgconf: Return a new pseudo option "compliance_de_vs".
[9feffc03f364]
* gpgtar: Fix file size computation under Windows. [198b240b1955]
* Full Unicode support for the Windows command line. [#4398]
* Fix problem with Windows Job objects and auto start of our
daemons. [#4333]
* i18n: In German always use "Passwort" instead of "Passphrase" in
prompts.
Release-info: https://dev.gnupg.org/T5482
See-also: gnupg-announce/2021q2/000460.html
Noteworthy changes in version 2.2.27 (2021-01-11)
-------------------------------------------------
* gpg: Fix regression in 2.2.24 for gnupg_remove function under
Windows. [#5230]
* gpgconf: Fix case with neither local nor global gpg.conf. [9f37d3e6f3]
* gpgconf: Fix description of two new options. [#5221]
* Build Windows installer without timestamps. Note that the
Authenticode signatures still carry a timestamp.
Release-info: https://dev.gnupg.org/T5234
See-also: gnupg-announce/2021q1/000452.html
Noteworthy changes in version 2.2.26 (2020-12-21)
-------------------------------------------------
* gpg: New AKL method "ntds". [559efd23e9]
* gpg: Fix --trusted-key with fingerprint arg. [8a2e5025eb]
* scd: Fix writing of ECC keys to an OpenPGP card. [#5163]
* scd: Make an USB error fix specific to SPR532 readers. [#5167]
* dirmngr: With new LDAP keyservers store the new attributes. Never
store the useless pgpSignerID. Fix a long standing bug storing
some keys on an ldap server. [0e88c73bc9,e47de85382]
* dirmngr: Support the new Active Direcory LDAP schema for
keyservers. [ac8ece9266]
* dirmngr: Allow LDAP OpenPGP searches via fingerprint.
[c75fd75532]
* dirmngr: Do not block other threads during keyserver LDAP calls.
[15bfd189c0]
* Support global configuration files. [#4788,a028f24136]
* Fix the iconv fallback handling to UTF-8. [#5038]
Release-info: https://dev.gnupg.org/T5153
See-also: gnupg-announce/2020q4/000451.html
Noteworthy changes in version 2.2.25 (2020-11-23)
-------------------------------------------------
* scd: Fix regression in 2.2.24 requiring gpg --card-status before
signing or decrypting. [#5065]
* gpgsm: Using Libksba 1.5.0 signatures with a rarely used
combination of attributes can now be verified. [#5146]
Release-info: https://dev.gnupg.org/T5140
See-also: gnupg-announce/2020q4/000450.html
Noteworthy changes in version 2.2.24 (2020-11-17)
-------------------------------------------------
* Allow Unicode file names on Windows almost everywhere. Note that
it is still not possible to use Unicode strings on the command
line. This change also fixes a regression in 2.2.22 related to
non-ascii file names. [#5098]
* Fix localized time printing on Windows. [#5073]
* gpg: New command --quick-revoke-sig. [#5093]
* gpg: Do not use weak digest algos if selected by recipient
preference during sign+encrypt. [4c181d51a6]
* gpg: Switch to AES256 for symmetric encryption in de-vs mode.
[166e779634]
* gpg: Silence weak digest warnings with --quiet. [#4893]
* gpg: Print new status line CANCELED_BY_USER for a cancel during
symmetric encryption. [f05d1772c4]
* gpg: Fix the encrypt+sign hash algo preference selection for
ECDSA. This is in particular needed for keys created from
existing smartcard based keys. [aeed0b93ff]
* agent: Fix secret key import of GnuPG 2.3 generated Ed25519 keys.
[#5114]
* agent: Keep some permissions of private-keys-v1.d. [#2312]
* dirmngr: Align sks-keyservers.netCA.pem use between ntbtls and
gnutls builds. [e4f3b74c91]
* dirmngr: Fix the pool keyserver case for a single host in the
pool. [72e04b03b1a7]
* scd: Fix the use case of verify_chv2 by CHECKPIN. [61aea64b3c]
* scd: Various improvements to the ccid-driver. [#4616,#5065]
* scd: Minor fixes for Yubikey [25bec16d0b]
* gpgconf: New option --show-versions.
* w32: Install gpg-check-pattern and example profiles. Install
Windows subsystem variant of gpgconf (gpgconf-w32).
* i18n: Complete overhaul and completion of the Italian translation.
Thanks to Denis Renzi.
* Require Libgcrypt 1.8 because 1.7 has long reached end-of-life.
Release-info: https://dev.gnupg.org/T5052
See-also: gnupg-announce/2020q4/000449.html
Noteworthy changes in version 2.2.23 (2020-09-03)
-------------------------------------------------
* gpg: Fix AEAD preference list overflow. [#5050]
* gpg: Fix a possible segv in the key cleaning code.
* gpgsm: Fix a minor RFC2253 parser bug. [#5037]
* scdaemon: Fix a PIN verify failure on certain OpenPGP card
implementations. Regression in 2.2.22. [#5039]
* po: Fix bug in the Hungarian translation. Updates for the Czech,
Polish, and Ukrainian translations.
Release-info: https://dev.gnupg.org/T5045
See-also: gnupg-announce/2020q3/000448.html
Noteworthy changes in version 2.2.22 (2020-08-27)
-------------------------------------------------
* gpg: Change the default key algorithm to rsa3072.
* gpg: Add regular expression support for Trust Signatures on all
platforms. [#4843]
* gpg: Fix regression in 2.2.21 with non-default --passphrase-repeat
option. [#4991]
* gpg: Ignore --personal-digest-prefs for ECDSA keys. [#5021]
* gpgsm: Make rsaPSS a de-vs compliant scheme.
* gpgsm: Show also the SHA256 fingerprint in key listings.
* gpgsm: Do not require a default keyring for --gpgconf-list. [#4867]
* gpg-agent: Default to extended key format and record the creation
time of keys. Add new option --disable-extended-key-format.
* gpg-agent: Support the WAYLAND_DISPLAY envvar. [#5016]
* gpg-agent: Allow using --gpgconf-list even if HOME does not
exist. [#4866]
* gpg-agent: Make the Pinentry work even if the envvar TERM is set
to the empty string. [#4137]
* scdaemon: Add a workaround for Gnuk tokens <= 2.15 which wrongly
incremented the error counter when using the "verify" command of
"gpg --edit-key" with only the signature key being present.
* dirmngr: Better handle systems with disabled IPv6. [#4977]
* gpgpslit: Install tool. It was not installed in the past to avoid
conflicts with the version installed by GnuPG 1.4. [#5023]
* gpgtar: Handle Unicode file names on Windows correctly (requires
libgpg-error 1.39). [#4083]
* gpgtar: Make --files-from and --null work as documented. [#5027]
* Build the Windows installer with the new Ntbtls 0.2.0 so that TLS
connections succeed for servers demanding GCM.
Release-info: https://dev.gnupg.org/T5030
See-also: gnupg-announce/2020q3/000447.html
Noteworthy changes in version 2.2.21 (2020-07-09)
-------------------------------------------------
* gpg: Improve symmetric decryption speed by about 25%.
See commit 144b95cc9d.
* gpg: Support decryption of AEAD encrypted data packets.
* gpg: Add option --no-include-key-block. [#4856]
* gpg: Allow for extra padding in ECDH. [#4908]
* gpg: Only a single pinentry is shown for symmetric encryption if
the pinentry supports this. [#4971]
* gpg: Print a note if no keys are given to --delete-key. [#4959]
* gpg,gpgsm: The ridiculous passphrase quality bar is not anymore
shown. [#2103]
* gpgsm: Certificates without a CRL distribution point are now
considered valid without looking up a CRL. The new option
--enable-issuer-based-crl-check can be used to revert to the
former behaviour.
* gpgsm: Support rsaPSS signature verification. [#4538]
* gpgsm: Unless CRL checking is disabled lookup a missing issuer
certificate using the certificate's authorityInfoAccess. [#4898]
* gpgsm: Print the certificate's serial number also in decimal
notation.
* gpgsm: Fix possible NULL-deref in messages of --gen-key. [#4895]
* scd: Support the CardOS 5 based D-Trust Card 3.1.
* dirmngr: Allow http URLs with "LOOKUP --url".
* wkd: Take name of sendmail from configure. Fixes an OpenBSD
specific bug. [#4886]
Release-info: https://dev.gnupg.org/T4897
See-also: gnupg-announce/2020q3/000446.html
Noteworthy changes in version 2.2.20 (2020-03-20)
-------------------------------------------------
* Protect the error counter against overflow to guarantee that the
tools can't be tricked into returning success after an error.
* gpg: Make really sure that --verify-files always returns an error.
* gpg: Fix key listing --with-secret if a pattern is given. [#4061]
* gpg: Fix detection of certain keys used as default-key. [#4810]
* gpg: Fix default-key selection when a card is available. [#4850]
* gpg: Fix key expiration and key usage for keys created with a
creation date of zero. [4670]
* gpgsm: Fix import of some CR,LF terminated certificates. [#4847]
* gpg: New options --include-key-block and --auto-key-import to
allow encrypted replies after an initial signed message. [#4856]
* gpg: Allow the use of a fingerprint with --trusted-key. [#4855]
* gpg: New property "fpr" for use by --export-filter.
* scdaemon: Disable the pinpad if a KDF DO is used. [#4832]
* dirmngr: Improve finding OCSP certificates. [#4536]
* Avoid build problems with LTO or gcc-10. [#4831]
Release-info: https://dev.gnupg.org/T4860
See-also: gnupg-announce/2020q1/000444.html
Noteworthy changes in version 2.2.19 (2019-12-07)
-------------------------------------------------
* gpg: Fix double free when decrypting for hidden recipients.
Regression in 2.2.18. [#4762].
* gpg: Use auto-key-locate for encryption even for mail addressed
given with angle brackets. [#4726]
* gpgsm: Add special case for certain expired intermediate
certificates. [#4696]
Release-info: https://dev.gnupg.org/T4768
See-also: gnupg-announce/2019q4/000443.html
Noteworthy changes in version 2.2.18 (2019-11-25)
-------------------------------------------------
* gpg: Changed the way keys are detected on a smartcards; this
allows the use of non-OpenPGP cards. In the case of a not very
likely regression the new option --use-only-openpgp-card is
available. [#4681]
* gpg: The commands --full-gen-key and --quick-gen-key now allow
direct key generation from supported cards. [#4681]
* gpg: Prepare against chosen-prefix SHA-1 collisions in key
signatures. This change removes all SHA-1 based key signature
newer than 2019-01-19 from the web-of-trust. Note that this
includes all key signature created with dsa1024 keys. The new
option --allow-weak-key-signatues can be used to override the new
and safer behaviour. [#4755,CVE-2019-14855]
* gpg: Improve performance for import of large keyblocks. [#4592]
* gpg: Implement a keybox compression run. [#4644]
* gpg: Show warnings from dirmngr about redirect and certificate
problems (details require --verbose as usual).
* gpg: Allow to pass the empty string for the passphrase if the
'--passphase=' syntax is used. [#4633]
* gpg: Fix printing of the KDF object attributes.
* gpg: Avoid surprises with --locate-external-key and certain
--auto-key-locate settings. [#4662]
* gpg: Improve selection of best matching key. [#4713]
* gpg: Delete key binding signature when deleting a subkey.
[#4665,#4457]
* gpg: Fix a potential loss of key signatures during import with
self-sigs-only active. [#4628]
* gpg: Silence "marked as ultimately trusted" diagnostics if
option --quiet is used. [#4634]
* gpg: Silence some diagnostics during in key listsing even with
option --verbose. [#4627]
* gpg, gpgsm: Change parsing of agent's pkdecrypt results. [#4652]
* gpgsm: Support AES-256 keys.
* gpgsm: Fix a bug in triggering a keybox compression run if
--faked-system-time is used.
* dirmngr: System CA certificates are no longer used for the SKS
pool if GNUTLS instead of NTBTLS is used as TLS library. [#4594]
* dirmngr: On Windows detect usability of IPv4 and IPv6 interfaces
to avoid long timeouts. [#4165]
* scd: Fix BWI value for APDU level transfers to make Gemalto Ezio
Shield and Trustica Cryptoucan work. [#4654,#4566]
* wkd: gpg-wks-client --install-key now installs the required policy
file.
Release-info: https://dev.gnupg.org/T4684
See-also: gnupg-announce/2019q4/000442.html
Noteworthy changes in version 2.2.17 (2019-07-09)
-------------------------------------------------
* gpg: Ignore all key-signatures received from keyservers. This
change is required to mitigate a DoS due to keys flooded with
faked key-signatures. The old behaviour can be achieved by adding
keyserver-options no-self-sigs-only,no-import-clean
to your gpg.conf. [#4607]
* gpg: If an imported keyblocks is too large to be stored in the
keybox (pubring.kbx) do not error out but fallback to an import
using the options "self-sigs-only,import-clean". [#4591]
* gpg: New command --locate-external-key which can be used to
refresh keys from the Web Key Directory or via other methods
configured with --auto-key-locate.
* gpg: New import option "self-sigs-only".
* gpg: In --auto-key-retrieve prefer WKD over keyservers. [#4595]
* dirmngr: Support the "openpgpkey" subdomain feature from
draft-koch-openpgp-webkey-service-07. [#4590].
* dirmngr: Add an exception for the "openpgpkey" subdomain to the
CSRF protection. [#4603]
* dirmngr: Fix endless loop due to http errors 503 and 504. [#4600]
* dirmngr: Fix TLS bug during redirection of HKP requests. [#4566]
* gpgconf: Fix a race condition when killing components. [#4577]
Release-info: https://dev.gnupg.org/T4606
See-also: gnupg-announce/2019q3/000439.html
Noteworthy changes in version 2.2.16 (2019-05-28)
-------------------------------------------------
* gpg,gpgsm: Fix deadlock on Windows due to a keybox sharing
violation. [#4505]
* gpg: Allow deletion of subkeys with --delete-key. This finally
makes the bang-suffix work as expected for that command. [#4457]
* gpg: Replace SHA-1 by SHA-256 in self-signatures when updating
them with --quick-set-expire or --quick-set-primary-uid. [#4508]
* gpg: Improve the photo image viewer selection. [#4334]
* gpg: Fix decryption with --use-embedded-filename. [#4500]
* gpg: Remove hints on using the --keyserver option. [#4512]
* gpg: Fix export of certain secret keys with comments. [#4490]
* gpg: Reject too long user-ids in --quick-gen-key. [#4532]
* gpg: Fix a double free in the best key selection code. [#4462]
* gpg: Fix the key generation dialog for switching back from EdDSA
to ECDSA.
* gpg: Use AES-192 with SHA-384 to comply with RFC-6637.
* gpg: Use only the addrspec from the Signer's UID subpacket to
mitigate a problem with another implementation.
* gpg: Skip invalid packets during a keyring listing and sync
diagnostics with the output.
* gpgsm: Avoid confusing diagnostic when signing with the default
key. [#4535]
* agent: Do not delete any secret key in --dry-run mode.
* agent: Fix failures on 64 bit big-endian boxes related to URIs in
a keyfile. [#4501]
* agent: Stop scdaemon after a reload with disable-scdaemon newly
configured. [#4326]
* dirmngr: Improve caching algorithm for WKD domains.
* dirmngr: Support other hash algorithms than SHA-1 for OCSP. [#3966]
* gpgconf: Make --homedir work for --launch. [#4496]
* gpgconf: Before --launch check for a valid config file. [#4497]
* wkd: Do not import more than 5 keys from one WKD address.
* wkd: Accept keys which are stored in armored format in the
directory.
* The installer for Windows now comes with signed binaries.
Release-info: https://dev.gnupg.org/T4509
See-also: gnupg-announce/2019q2/000438.html
Noteworthy changes in version 2.2.15 (2019-03-26)
-------------------------------------------------
* sm: Fix --logger-fd and --status-fd on Windows for non-standard
file descriptors.
* sm: Allow decryption even if expired keys are configured. [#4431]
* agent: Change command KEYINFO to print ssh fingerprints with other
hash algos.
* dirmngr: Fix build problems on Solaris due to the use of reserved
symbol names. [#4420]
* wkd: New commands --print-wkd-hash and --print-wkd-url for
gpg-wks-client.
Release-info: https://dev.gnupg.org/T4434
See-also: gnupg-announce/2019q1/000436.html
Noteworthy changes in version 2.2.14 (2019-03-19)
-------------------------------------------------
* gpg: Allow import of PGP desktop exported secret keys. Also avoid
importing secret keys if the secret keyblock is not valid. [#4392]
* gpg: Do not error out on version 5 keys in the local keyring.
* gpg: Make invalid primary key algo obvious in key listings.
* sm: Do not mark a certificate in a key listing as de-vs compliant
if its use for a signature will not be possible.
* sm: Fix certificate creation with key on card.
* sm: Create rsa3072 bit certificates by default.
* sm: Print Yubikey attestation extensions with --dump-cert.
* agent: Fix cancellation handling for scdaemon.
* agent: Support --mode=ssh option for CLEAR_PASSPHRASE. [#4340]
* scd: Fix flushing of the CA-FPR DOs in app-openpgp.
* scd: Avoid a conflict error with the "undefined" app.
* dirmngr: Add CSRF protection exception for protonmail.
* dirmngr: Fix build problems with gcc 9 in libdns.
* gpgconf: New option --show-socket for use with --launch.
* gpgtar: Make option -C work for archive creation.
Release-info: https://dev.gnupg.org/T4412
See-also: gnupg-announce/2019q1/000435.html
Noteworthy changes in version 2.2.13 (2019-02-12)
-------------------------------------------------
* gpg: Implement key lookup via keygrip (using the & prefix).
* gpg: Allow generating Ed25519 key from existing key.
* gpg: Emit an ERROR status line if no key was found with -k.
* gpg: Stop early when trying to create a primary Elgamal key. [#4329]
* gpgsm: Print the card's key algorithms along with their keygrips
in interactive key generation.
* agent: Clear bogus pinentry cache in the error case. [#4348]
* scd: Support "acknowledge button" feature.
* scd: Fix for USB INTERRUPT transfer. [#4308]
* wks: Do no use compression for the the encrypted challenge and
response.
Release-info: https://dev.gnupg.org/T4290
See-also: gnupg-announce/2019q1/000434.html
Noteworthy changes in version 2.2.12 (2018-12-14)
-------------------------------------------------
* tools: New commands --install-key and --remove-key for
gpg-wks-client. This allows to prepare a Web Key Directory on a
local file system for later upload to a web server.
* gpg: New --list-option "show-only-fpr-mbox". This makes the use
of the new gpg-wks-client --install-key command easier on Windows.
* gpg: Improve processing speed when --skip-verify is used.
* gpg: Fix a bug where a LF was accidentally written to the console.
* gpg: --card-status now shows whether a card has the new KDF
feature enabled.
* agent: New runtime option --s2k-calibration=MSEC. New configure
option --with-agent-s2k-calibration=MSEC. [#3399]
* dirmngr: Try another keyserver from the pool on receiving a 502,
503, or 504 error. [#4175]
* dirmngr: Avoid possible CSRF attacks via http redirects. A HTTP
query will not anymore follow a 3xx redirect unless the Location
header gives the same host. If the host is different only the
host and port is taken from the Location header and the original
path and query parts are kept.
* dirmngr: New command FLUSHCRL to flush all CRLS from disk and
memory. [#3967]
* New simplified Chinese translation (zh_CN).
Release-info: https://dev.gnupg.org/T4289
See-also: gnupg-announce/2018q4/000433.html
Noteworthy changes in version 2.2.11 (2018-11-06)
-------------------------------------------------
* gpgsm: Fix CRL loading when intermediate certicates are not yet
trusted.
* gpgsm: Fix an error message about the digest algo. [#4219]
* gpg: Fix a wrong warning due to new sign usage check introduced
with 2.2.9. [#4014]
* gpg: Print the "data source" even for an unsuccessful keyserver
query.
* gpg: Do not store the TOFU trust model in the trustdb. This
allows to enable or disable a TOFO model without triggering a
trustdb rebuild. [#4134]
* scd: Fix cases of "Bad PIN" after using "forcesig". [#4177]
* agent: Fix possible hang in the ssh handler. [#4221]
* dirmngr: Tack the unmodified mail address to a WKD request. See
commit a2bd4a64e5b057f291a60a9499f881dd47745e2f for details.
* dirmngr: Tweak diagnostic about missing LDAP server file.
* dirmngr: In verbose mode print the OCSP responder id.
* dirmngr: Fix parsing of the LDAP port. [#4230]
* wks: Add option --directory/-C to the server. Always build the
server on Unix systems.
* wks: Add option --with-colons to the client. Support sites which
use the policy file instead of the submission-address file.
* Fix EBADF when gpg et al. are called by broken CGI scripts.
* Fix some minor memory leaks and bugs.
Release-info: https://dev.gnupg.org/T4233
See-also: gnupg-announce/2018q4/000432.html
Noteworthy changes in version 2.2.10 (2018-08-30)
-------------------------------------------------
* gpg: Refresh expired keys originating from the WKD. [#2917]
* gpg: Use a 256 KiB limit for a WKD imported key.
* gpg: New option --known-notation. [#4060]
* scd: Add support for the Trustica Cryptoucan reader.
* agent: Speed up starting during on-demand launching. [#3490]
* dirmngr: Validate SRV records in WKD queries.
Release-info: https://dev.gnupg.org/T4112
See-also: gnupg-announce/2018q3/000428.html
Noteworthy changes in version 2.2.9 (2018-07-12)
------------------------------------------------
* dirmngr: Fix recursive resolver mode and other bugs in the libdns
code. [#3374,#3803,#3610]
* dirmngr: When using libgpg-error 1.32 or later a GnuPG build with
NTBTLS support (e.g. the standard Windows installer) does not
anymore block for dozens of seconds before returning data.
* gpg: Fix bug in --show-keys which actually imported revocation
certificates. [#4017]
* gpg: Ignore too long user-ID and comment packets. [#4022]
* gpg: Fix crash due to bad German translation. Improved printf
format compile time check.
* gpg: Handle missing ISSUER sub packet gracefully in the presence of
the new ISSUER_FPR. [#4046]
* gpg: Allow decryption using several passphrases in most cases.
[#3795,#4050]
* gpg: Command --show-keys now enables the list options
show-unusable-uids, show-unusable-subkeys, show-notations and
show-policy-urls by default.
* gpg: Command --show-keys now prints revocation certificates. [#4018]
* gpg: Add revocation reason to the "rev" and "rvs" records of the
option --with-colons. [#1173]
* gpg: Export option export-clean does now remove certain expired
subkeys; export-minimal removes all expired subkeys. [#3622]
* gpg: New "usage" property for the drop-subkey filters. [#4019]
Release-info: https://dev.gnupg.org/T4036
See-also: gnupg-announce/2018q3/000427.html
Noteworthy changes in version 2.2.8 (2018-06-08)
------------------------------------------------
* gpg: Decryption of messages not using the MDC mode will now lead
to a hard failure even if a legacy cipher algorithm was used. The
option --ignore-mdc-error can be used to turn this failure into a
warning. Take care: Never use that option unconditionally or
without a prior warning.
* gpg: The MDC encryption mode is now always used regardless of the
cipher algorithm or any preferences. For testing --rfc2440 can be
used to create a message without an MDC.
* gpg: Sanitize the diagnostic output of the original file name in
verbose mode. [#4012,CVE-2018-12020]
* gpg: Detect suspicious multiple plaintext packets in a more
reliable way. [#4000]
* gpg: Fix the duplicate key signature detection code. [#3994]
* gpg: The options --no-mdc-warn, --force-mdc, --no-force-mdc,
--disable-mdc and --no-disable-mdc have no more effect.
* gpg: New command --show-keys.
* agent: Add DBUS_SESSION_BUS_ADDRESS and a few other envvars to the
list of startup environment variables. [#3947]
See-also: gnupg-announce/2018q2/000425.html
Noteworthy changes in version 2.2.7 (2018-05-02)
------------------------------------------------
* gpg: New option --no-symkey-cache to disable the passphrase cache
for symmetrical en- and decryption.
* gpg: The ERRSIG status now prints the fingerprint if that is part
of the signature.
* gpg: Relax emitting of FAILURE status lines
* gpg: Add a status flag to "sig" lines printed with --list-sigs.
* gpg: Fix "Too many open files" when using --multifile. [#3951]
* ssh: Return an error for unknown ssh-agent flags. [#3880]
* dirmngr: Fix a regression since 2.1.16 which caused corrupted CRL
caches under Windows. [#2448,#3923]
* dirmngr: Fix a CNAME problem with pools and TLS. Also use a fixed
mapping of keys.gnupg.net to sks-keyservers.net. [#3755]
* dirmngr: Try resurrecting dead hosts earlier (from 3 to 1.5 hours).
* dirmngr: Fallback to CRL if no default OCSP responder is configured.
* dirmngr: Implement CRL fetching via https. Here a redirection to
http is explictly allowed.
* dirmngr: Make LDAP searching and CRL fetching work under Windows.
This stopped working with 2.1. [#3937]
* agent,dirmngr: New sub-command "getenv" for "getinfo" to ease
debugging.
See-also: gnupg-announce/2018q2/000424.html
Noteworthy changes in version 2.2.6 (2018-04-09)
------------------------------------------------
* gpg,gpgsm: New option --request-origin to pretend requests coming
from a browser or a remote site.
* gpg: Fix race condition on trustdb.gpg updates due to too early
released lock. [#3839]
* gpg: Emit FAILURE status lines in almost all cases. [#3872]
* gpg: Implement --dry-run for --passwd to make checking a key's
passphrase straightforward.
* gpg: Make sure to only accept a certification capable key for key
signatures. [#3844]
* gpg: Better user interaction in --card-edit for the factory-reset
sub-command.
* gpg: Improve changing key attributes in --card-edit by adding an
explicit "key-attr" sub-command. [#3781]
* gpg: Print the keygrips in the --card-status.
* gpg: Improve the OpenPGP card's factory-reset. [7f765a98fd]
* scd: Support KDF DO setup. [#3823]
* scd: Fix some issues with PC/SC on Windows. [#3825]
* scd: Fix suspend/resume handling in the CCID driver.
* scd: Fix a race condition in the CCID driver leading to a segv for
some readers. [#5121]
* agent: Evict cached passphrases also via a timer. [#3829]
* agent: Use separate passphrase caches depending on the request
origin. [#3858]
* ssh: Support signature flags. [#3880]
* dirmngr: Handle failures related to missing IPv6 support
gracefully. [#3331]
* Fix corner cases related to specified home directory with
drive letter on Windows. [#3720]
* Allow the use of UNC directory names as homedir. [#3818]
See-also: gnupg-announce/2018q2/000421.html
Noteworthy changes in version 2.2.5 (2018-02-22)
------------------------------------------------
* gpg: Allow the use of the "cv25519" and "ed25519" short names in
addition to the canonical curve names in --batch --gen-key.
* gpg: Make sure to print all secret keys with option --list-only
and --decrypt. [#3718]
* gpg: Fix the use of future-default with --quick-add-key for
signing keys. [#3747]
* gpg: Select a secret key by checking availability under gpg-agent.
[#1967]
* gpg: Fix reversed prompt texts for --only-sign-text-ids. [#3787]
* gpg,gpgsm: Fix detection of bogus keybox blobs on 32 bit systems.
[#3770]
* gpgsm: Fix regression since 2.1 in --export-secret-key-raw which
got $d mod (q-1)$ wrong. Note that most tools automatically fixup
that parameter anyway.
* ssh: Fix a regression in getting the client'd PID on *BSD and
macOS.
* scd: Support the KDF Data Object of the OpenPGP card 3.3. [#3152]
* scd: Fix a regression in the internal CCID driver for certain card
readers. [#3508]
* scd: Fix a problem on NetBSD killing scdaemon on gpg-agent
shutdown. [#3778]
* dirmngr: Improve returned error description on failure of DNS
resolving. [#3756]
* wks: Implement command --install-key for gpg-wks-server.
* Add option STATIC=1 to the Speedo build system to allow a build
with statically linked versions of the core GnuPG libraries. Also
use --enable-wks-tools by default by Speedo builds for Unix.
See-also: gnupg-announce/2018q1/000420.html
Noteworthy changes in version 2.2.4 (2017-12-20)
------------------------------------------------
* gpg: Change default preferences to prefer SHA512.
* gpg: Print a warning when more than 150 MiB are encrypted using a
cipher with 64 bit block size.
* gpg: Print a warning if the MDC feature has not been used for a
message.
* gpg: Fix regular expression of domain addresses in trust
signatures. [#2923]
* agent: New option --auto-expand-secmem to help with high numbers
of concurrent connections. Requires libgcrypt 1.8.2 for having
an effect. [#3530]
* dirmngr: Cache responses of WKD queries.
* gpgconf: Add option --status-fd.
* wks: Add commands --check and --remove-key to gpg-wks-server.
* Increase the backlog parameter of the daemons to 64 and add
option --listen-backlog.
* New configure option --enable-run-gnupg-user-socket to first try a
socket directory which is not removed by systemd at session end.
See-also: gnupg-announce/2017q4/000419.html
Noteworthy changes in version 2.2.3 (2017-11-20)
------------------------------------------------
* gpgsm: Fix initial keybox creation on Windows. [#3507]
* dirmngr: Fix crash in case of a CRL loading error. [#3510]
* Fix the name of the Windows registry key. [Git#4f5afaf1fd]
* gpgtar: Fix wrong behaviour of --set-filename. [#3500]
* gpg: Silence AKL retrieval messages. [#3504]
* agent: Use clock or clock_gettime for calibration. [#3056]
* agent: Improve robustness of the shutdown pending
state. [Git#7ffedfab89]
See-also: gnupg-announce/2017q4/000417.html
Noteworthy changes in version 2.2.2 (2017-11-07)
------------------------------------------------
* gpg: Avoid duplicate key imports by concurrently running gpg
processes. [#3446]
* gpg: Fix creating on-disk subkey with on-card primary key. [#3280]
* gpg: Fix validity retrieval for multiple keyrings. [Debian#878812]
* gpg: Fix --dry-run and import option show-only for secret keys.
* gpg: Print "sec" or "sbb" for secret keys with import option
import-show. [#3431]
* gpg: Make import less verbose. [#3397]
* gpg: Add alias "Key-Grip" for parameter "Keygrip" and new
parameter "Subkey-Grip" to unattended key generation. [#3478]
* gpg: Improve "factory-reset" command for OpenPGP cards. [#3286]
* gpg: Ease switching Gnuk tokens into ECC mode by using the magic
keysize value 25519.
* gpgsm: Fix --with-colon listing in crt records for fields > 12.
* gpgsm: Do not expect X.509 keyids to be unique. [#1644]
* agent: Fix stucked Pinentry when using --max-passphrase-days. [#3190]
* agent: New option --s2k-count. [#3276 (workaround)]
* dirmngr: Do not follow https-to-http redirects. [#3436]
* dirmngr: Reduce default LDAP timeout from 100 to 15 seconds. [#3487]
* gpgconf: Ignore non-installed components for commands
--apply-profile and --apply-defaults. [#3313]
* Add configure option --enable-werror. [#2423]
See-also: gnupg-announce/2017q4/000416.html
Noteworthy changes in version 2.2.1 (2017-09-19)
------------------------------------------------
* gpg: Fix formatting of the user id in batch mode key generation
if only "name-email" is given.
* gpgv: Fix annoying "not suitable for" warnings.
* wks: Convey only the newest user id to the provider. This is the
case if different names are used with the same addr-spec.
* wks: Create a complying user id for provider policy mailbox-only.
* wks: Add workaround for posteo.de.
* scd: Fix the use of large ECC keys with an OpenPGP card.
* dirmngr: Use system provided root certificates if no specific HKP
certificates are configured. If build with GNUTLS, this was
already the case.
See-also: gnupg-announce/2017q3/000415.html
Noteworthy changes in version 2.2.0 (2017-08-28)
------------------------------------------------
This is the new long term stable branch. This branch will only see
bug fixes and no new features.
* gpg: Reverted change in 2.1.23 so that --no-auto-key-retrieve is
again the default.
* Fixed a few minor bugs.
See-also: gnupg-announce/2017q3/000413.html
Noteworthy changes in version 2.1.23 (2017-08-09)
-------------------------------------------------
* gpg: "gpg" is now installed as "gpg" and not anymore as "gpg2".
If needed, the new configure option --enable-gpg-is-gpg2 can be
used to revert this.
* gpg: Options --auto-key-retrieve and --auto-key-locate "local,wkd"
are now used by default. Note: this enables keyserver and Web Key
Directory operators to notice when a signature from a locally
non-available key is being verified for the first time or when
you intend to encrypt to a mail address without having the key
locally. This new behaviour will eventually make key discovery
much easier and mostly automatic. Disable this by adding
no-auto-key-retrieve
auto-key-locate local
to your gpg.conf.
* agent: Option --no-grab is now the default. The new option --grab
allows to revert this.
* gpg: New import option "show-only".
* gpg: New option --disable-dirmngr to entirely disable network
access for gpg.
* gpg,gpgsm: Tweaked DE-VS compliance behaviour.
* New configure flag --enable-all-tests to run more extensive tests
during "make check".
* gpgsm: The keygrip is now always printed in colon mode as
documented in the man page.
* Fixed connection timeout problem under Windows.
See-also: gnupg-announce/2017q3/000412.html
Noteworthy changes in version 2.1.22 (2017-07-28)
-------------------------------------------------
* gpg: Extend command --quick-set-expire to allow for setting the
expiration time of subkeys.
* gpg: By default try to repair keys during import. New sub-option
no-repair-keys for --import-options.
* gpg,gpgsm: Improved checking and reporting of DE-VS compliance.
* gpg: New options --key-origin and --with-key-origin. Store the
time of the last key update from keyservers, WKD, or DANE.
* agent: New option --ssh-fingerprint-digest.
* dimngr: Lower timeouts on keyserver connection attempts and made
it configurable.
* dirmngr: Tor will now automatically be detected and used. The
option --no-use-tor disables Tor detection.
* dirmngr: Now detects a changed /etc/resolv.conf.
* agent,dirmngr: Initiate shutdown on removal of the GnuPG home
directory.
* gpg: Avoid caching passphrase for failed symmetric encryption.
* agent: Support for unprotected ssh keys.
* dirmngr: Fixed name resolving on systems using only v6
nameservers.
* dirmngr: Allow the use of TLS over http proxies.
* w32: Change directory of the daemons after startup.
* wks: New man pages for client and server.
* Many other bug fixes.
See-also: gnupg-announce/2017q3/000411.html
Noteworthy changes in version 2.1.21 (2017-05-15)
-------------------------------------------------
* gpg,gpgsm: Fix corruption of old style keyring.gpg files. This
bug was introduced with version 2.1.20. Note that the default
pubring.kbx format was not affected.
* gpg,dirmngr: Removed the skeleton config file support. The
system's standard methods for providing default configuration
files should be used instead.
* w32: The Windows installer now allows installation of GnuPG
without Administrator permissions.
* gpg: Fixed import filter property match bug.
* scd: Removed Linux support for Cardman 4040 PCMCIA reader.
* scd: Fixed some corner case bugs in resume/suspend handling.
* Many minor bug fixes and code cleanup.
See-also: gnupg-announce/2017q2/000405.html
Noteworthy changes in version 2.1.20 (2017-04-03)
-------------------------------------------------
* gpg: New properties 'expired', 'revoked', and 'disabled' for the
import and export filters.
* gpg: New command --quick-set-primary-uid.
* gpg: New compliance field for the --with-colon key listing.
* gpg: Changed the key parser to generalize the processing of local
meta data packets.
* gpg: Fixed assertion failure in the TOFU trust model.
* gpg: Fixed exporting of zero length user ID packets.
* scd: Improved support for multiple readers.
* scd: Fixed timeout handling for key generation.
* agent: New option --enable-extended-key-format.
* dirmngr: Do not add a keyserver to a new dirmngr.conf. Dirmngr
uses a default keyserver.
* dimngr: Do not treat TLS warning alerts as severe error when
building with GNUTLS.
* dirmngr: Actually take /etc/hosts in account.
* wks: Fixed client problems on Windows. Published keys are now set
to world-readable.
* tests: Fixed creation of temporary directories.
* A socket directory for a non standard GNUGHOME is now created on
the fly under /run/user. Thus "gpgconf --create-socketdir" is now
optional. The use of "gpgconf --remove-socketdir" to clean up
obsolete socket directories is however recommended to avoid
cluttering /run/user with useless directories.
* Fixed build problems on some platforms.
See-also: gnupg-announce/2017q2/000404.html
Noteworthy changes in version 2.1.19 (2017-03-01)
-------------------------------------------------
* gpg: Print a warning if Tor mode is requested but the Tor daemon
is not running.
* gpg: New status code DECRYPTION_KEY to print the actual private
key used for decryption.
* gpgv: New options --log-file and --debug.
* gpg-agent: Revamp the prompts to ask for card PINs.
* scd: Support for multiple card readers.
* scd: Removed option --debug-disable-ticker. Ticker is used
only when it is required to watch removal of device/card.
* scd: Improved detection of card inserting and removal.
* dirmngr: New option --disable-ipv4.
* dirmngr: New option --no-use-tor to explicitly disable the use of
Tor.
* dirmngr: The option --allow-version-check is now required even if
the option --use-tor is also used.
* dirmngr: Handle a missing nsswitch.conf gracefully.
* dirmngr: Avoid PTR lookups for keyserver pools. The are only done
for the debug command "keyserver --hosttable".
* dirmngr: Rework the internal certificate cache to support classes
of certificates. Load system provided certificates on startup.
Add options --tls, --no-crl, and --systrust to the "VALIDATE"
command.
* dirmngr: Add support for the ntbtls library.
* wks: Create mails with a "WKS-Phase" header. Fix detection of
Draft-2 mode.
* The Windows installer is now build with limited TLS support.
* Many other bug fixes and new regression tests.
See-also: gnupg-announce/2017q1/000402.html
Noteworthy changes in version 2.1.18 (2017-01-23)
-------------------------------------------------
* gpg: Remove bogus subkey signature while cleaning a key (with
export-clean, import-clean, or --edit-key's sub-command clean)
* gpg: Allow freezing the clock with --faked-system-time.
* gpg: New --export-option flag "backup", new --import-option flag
"restore".
* gpg-agent: Fixed long delay due to a regression in the progress
callback code.
* scd: Lots of code cleanup and internal changes.
* scd: Improved the internal CCID driver.
* dirmngr: Fixed problem with the DNS glue code (removal of the
trailing dot in domain names).
* dirmngr: Make sure that Tor is actually enabled after changing the
conf file and sending SIGHUP or "gpgconf --reload dirmngr".
* dirmngr: Fixed Tor access to IPv6 addresses. Note that current
versions of Tor may require that the flag "IPv6Traffic" is used
with the option "SocksPort" in torrc to actually allow IPv6
traffic.
* dirmngr: Fixed HKP for literally given IPv6 addresses.
* dirmngr: Enabled reverse DNS lookups via Tor.
* dirmngr: Added experimental SRV record lookup for WKD.
See commit 88dc3af3d4ae1afe1d5e136bc4c38bc4e7d4cd10 for details.
* dirmngr: For HKP use "pgpkey-hkps" and "pgpkey-hkp" in SRV record
lookups. Avoid SRV record lookup when a port is explicitly
specified. This fixes a regression from the 1.4 and 2.0 behavior.
* dirmngr: Gracefully handle a missing /etc/nsswitch.conf. Ignore
negation terms (e.g. "[!UNAVAIL=return]" instead of bailing out.
* dirmngr: Better debug output for flags "dns" and "network".
* dirmngr: On reload mark all known HKP servers alive.
* gpgconf: Allow keyword "all" for --launch, --kill, and --reload.
* tools: gpg-wks-client now ignores a missing policy file on the
server.
* Avoid unnecessary ambiguity error message in the option parsing.
* Further improvements of the regression test suite.
* Fixed building with --disable-libdns configure option.
* Fixed a crash running the tests on 32 bit architectures.
* Fixed spurious failures on BSD system in the spawn functions.
This affected for example gpg-wks-client and gpgconf.
See-also: gnupg-announce/2017q1/000401.html
Noteworthy changes in version 2.1.17 (2016-12-20)
-------------------------------------------------
* gpg: By default new keys expire after 2 years.
* gpg: New command --quick-set-expire to conveniently change the
expiration date of keys.
* gpg: Option and command names have been changed for easier
comprehension. The old names are still available as aliases.
* gpg: Improved the TOFU trust model.
* gpg: New option --default-new-key-algo.
* scd: Support OpenPGP card V3 for RSA.
* dirmngr: Support for the ADNS library has been removed. Instead
William Ahern's Libdns is now source included and used on all
platforms. This enables Tor support on all platforms. The new
option --standard-resolver can be used to disable this code at
runtime. In case of build problems the new configure option
--disable-libdns can be used to build without Libdns.
* dirmngr: Lazily launch ldap reaper thread.
* tools: New options --check and --status-fd for gpg-wks-client.
* The UTF-8 byte order mark is now skipped when reading conf files.
* Fixed many bugs and regressions.
* Major improvements to the test suite. For example it is possible
to run the external test suite of GPGME.
See-also: gnupg-announce/2016q4/000400.html
Noteworthy changes in version 2.1.16 (2016-11-18)
-------------------------------------------------
* gpg: New algorithm for selecting the best ranked public key when
using a mail address with -r, -R, or --locate-key.
* gpg: New option --with-tofu-info to print a new "tfs" record in
colon formatted key listings.
* gpg: New option --compliance as an alternative way to specify
options like --rfc2440, --rfc4880, et al.
* gpg: Many changes to the TOFU implementation.
* gpg: Improve usability of --quick-gen-key.
* gpg: In --verbose mode print a diagnostic when a pinentry is
launched.
* gpg: Remove code which warns for old versions of gnome-keyring.
* gpg: New option --override-session-key-fd.
* gpg: Option --output does now work with --verify.
* gpgv: New option --output to allow saving the verified data.
* gpgv: New option --enable-special-filenames.
* agent, dirmngr: New --supervised mode for use by systemd and alike.
* agent: By default listen on all available sockets using standard
names.
* agent: Invoke scdaemon with --homedir.
* dirmngr: On Linux now detects the removal of its own socket and
terminates.
* scd: Support ECC key generation.
* scd: Support more card readers.
* dirmngr: New option --allow-version-check to download a software
version database in the background.
* dirmngr: Use system provided CAs if no --hkp-cacert is given.
* dirmngr: Use a default keyserver if none is explicitly set
* gpgconf: New command --query-swdb to check software versions
against an copy of an online database.
* gpgconf: Print the socket directory with --list-dirs.
* tools: The WKS tools now support draft version -02.
* tools: Always build gpg-wks-client and install under libexec.
* tools: New option --supported for gpg-wks-client.
* The log-file option now accepts a value "socket://" to log to the
socket named "S.log" in the standard socket directory.
* Provide fake pinentries for use by tests cases of downstream
developers.
* Fixed many bugs and regressions.
* Many changes and improvements for the test suite.
See-also: gnupg-announce/2016q4/000398.html
Noteworthy changes in version 2.1.15 (2016-08-18)
-------------------------------------------------
* gpg: Remove the --tofu-db-format option and support for the split
TOFU database.
* gpg: Add option --sender to prepare for coming features.
* gpg: Add option --input-size-hint to help progress indicators.
* gpg: Extend the PROGRESS status line with the counted unit.
* gpg: Avoid publishing the GnuPG version by default with --armor.
* gpg: Properly ignore legacy keys in the keyring cache.
* gpg: Always print fingerprint records in --with-colons mode.
* gpg: Make sure that keygrips are printed for each subkey in
--with-colons mode.
* gpg: New import filter "drop-sig".
* gpgsm: Fix a bug in the machine-readable key listing.
* gpg,gpgsm: Block signals during keyring updates to limits the
effects of a Ctrl-C at the wrong time.
* g13: Add command --umount and other fixes for dm-crypt.
* agent: Fix regression in SIGTERM handling.
* agent: Cleanup of the ssh-agent code.
* agent: Allow import of overly long keys.
* scd: Fix problems with card removal.
* dirmngr: Remove all code for running as a system service.
* tools: Make gpg-wks-client conforming to the specs.
* tests: Improve the output of the new regression test tool.
* tests: Distribute the standalone test runner.
* tests: Run each test in a clean environment.
* Spelling and grammar fixes.
See-also: gnupg-announce/2016q3/000396.html
Noteworthy changes in version 2.1.14 (2016-07-14)
-------------------------------------------------
* gpg: Removed options --print-dane-records and --print-pka-records.
The new export options "export-pka" and "export-dane" can instead
be used with the export command.
* gpg: New options --import-filter and --export-filter.
* gpg: New import options "import-show" and "import-export".
* gpg: New option --no-keyring.
* gpg: New command --quick-revuid.
* gpg: New options -f/--recipient-file and -F/--hidden-recipient-file
to directly specify encryption keys.
* gpg: New option --mimemode to indicate that the content is a MIME
part. Does only enable --textmode right now.
* gpg: New option --rfc4880bis to allow experiments with proposed
changes to the current OpenPGP specs.
* gpg: Fix regression in the "fetch" sub-command of --card-edit.
* gpg: Fix regression since 2.1 in option --try-all-secrets.
* gpgv: Change default options for extra security.
* gpgsm: No more root certificates are installed by default.
* agent: "updatestartuptty" does now affect more environment
variables.
* scd: The option --homedir does now work with scdaemon.
* scd: Support some more GEMPlus card readers.
* gpgtar: Fix handling of '-' as file name.
* gpgtar: New commands --create and --extract.
* gpgconf: Tweak for --list-dirs to better support shell scripts.
* tools: Add programs gpg-wks-client and gpg-wks-server to implement
a Web Key Service. The configure option --enable-wks-tools is
required to build them; they should be considered Beta software.
* tests: Complete rework of the openpgp part of the test suite. The
test scripts have been changed from Bourne shell scripts to Scheme
programs. A customized scheme interpreter (gpgscm) is included.
This change was triggered by the need to run the test suite on
non-Unix platforms.
* The rendering of the man pages has been improved.
See-also: gnupg-announce/2016q3/000393.html
Noteworthy changes in version 2.1.13 (2016-06-16)
-------------------------------------------------
* gpg: New command --quick-addkey. Extend the --quick-gen-key
command.
* gpg: New --keyid-format "none" which is now also the default.
* gpg: New option --with-subkey-fingerprint.
* gpg: Include Signer's UID subpacket in signatures if the secret key
has been specified using a mail address and the new option
--disable-signer-uid is not used.
* gpg: Allow unattended deletion of a secret key.
* gpg: Allow export of non-passphrase protected secret keys.
* gpg: New status lines KEY_CONSIDERED and NOTATION_FLAGS.
* gpg: Change status line TOFU_STATS_LONG to use '~' as
a non-breaking-space character.
* gpg: Speedup key listings in Tofu mode.
* gpg: Make sure that the current and total values of a PROGRESS
status line are small enough.
* gpgsm: Allow the use of AES192 and SERPENT ciphers.
* dirmngr: Adjust WKD lookup to current specs.
* dirmngr: Fallback to LDAP v3 if v2 is is not supported.
* gpgconf: New commands --create-socketdir and --remove-socketdir,
new option --homedir.
* If a /run/user/$UID directory exists, that directory is now used
for IPC sockets instead of the GNUPGHOME directory. This fixes
problems with NFS and too long socket names and thus avoids the
need for redirection files.
* The Speedo build systems now uses the new versions.gnupg.org server
to retrieve the default package versions.
* Fix detection of libusb on FreeBSD.
* Speedup fd closing after a fork.
See-also: gnupg-announce/2016q2/000390.html
Noteworthy changes in version 2.1.12 (2016-05-04)
-------------------------------------------------
* gpg: New --edit-key sub-command "change-usage" for testing
purposes.
* gpg: Out of order key-signatures are now systematically detected
and fixed by --edit-key.
* gpg: Improved detection of non-armored messages.
* gpg: Removed the extra prompt needed to create Curve25519 keys.
* gpg: Improved user ID selection for --quick-sign-key.
* gpg: Use the root CAs provided by the system with --fetch-key.
* gpg: Add support for the experimental Web Key Directory key
location service.
* gpg: Improve formatting of Tofu messages and emit new Tofu specific
status lines.
* gpgsm: Add option --pinentry-mode to support a loopback pinentry.
* gpgsm: A new pubring.kbx is now created with the header blob so
that gpg can detect that the keybox format needs to be used.
* agent: Add read support for the new private key protection format
openpgp-s2k-ocb-aes.
* agent: Add read support for the new extended private key format.
* agent: Default to --allow-loopback-pinentry and add option
--no-allow-loopback-pinentry.
* scd: Changed to use the new libusb 1.0 API for the internal CCID
driver.
* dirmngr: The dirmngr-client does now auto-detect the PEM format.
* g13: Add experimental support for dm-crypt.
* w32: Tofu support is now available with the Speedo build method.
* w32: Removed the need for libiconv.dll.
* The man pages for gpg and gpgv are now installed under the correct
name (gpg2 or gpg - depending on a configure option).
* Lots of internal cleanups and bug fixes.
See-also: gnupg-announce/2016q2/000387.html
Noteworthy changes in version 2.1.11 (2016-01-26)
-------------------------------------------------
* gpg: New command --export-ssh-key to replace the gpgkey2ssh tool.
* gpg: Allow to generate mail address only keys with --gen-key.
* gpg: "--list-options show-usage" is now the default.
* gpg: Make lookup of DNS CERT records holding an URL work.
* gpg: Emit PROGRESS status lines during key generation.
* gpg: Don't check for ambigious or non-matching key specification in
the config file or given to --encrypt-to. This feature will return
in 2.3.x.
* gpg: Lock keybox files while updating them.
* gpg: Solve rare error on Windows during keyring and Keybox updates.
* gpg: Fix possible keyring corruption. (bug#2193)
* gpg: Fix regression of "bkuptocard" sub-command in --edit-key and
remove "checkbkupkey" sub-command introduced with 2.1. (bug#2169)
* gpg: Fix internal error in gpgv when using default keyid-format.
* gpg: Fix --auto-key-retrieve to work with dirmngr.conf configured
keyservers. (bug#2147).
* agent: New option --pinentry-timeout.
* scd: Improve unplugging of USB readers under Windows.
* scd: Fix regression for generating RSA keys on card.
* dirmmgr: All configured keyservers are now searched.
* dirmngr: Install CA certificate for hkps.pool.sks-keyservers.net.
Use this certiticate even if --hkp-cacert is not used.
* gpgtar: Add actual encryption code. gpgtar does now fully replace
gpg-zip.
* gpgtar: Fix filename encoding problem on Windows.
* Print a warning if a GnuPG component is using an older version of
gpg-agent, dirmngr, or scdaemon.
See-also: gnupg-announce/2016q1/000383.html
Noteworthy changes in version 2.1.10 (2015-12-04)
-------------------------------------------------
* gpg: New trust models "tofu" and "tofu+pgp".
* gpg: New command --tofu-policy. New options --tofu-default-policy
and --tofu-db-format.
* gpg: New option --weak-digest to specify hash algorithms which
should be considered weak.
* gpg: Allow the use of multiple --default-key options; take the last
available key.
* gpg: New option --encrypt-to-default-key.
* gpg: New option --unwrap to only strip the encryption layer.
* gpg: New option --only-sign-text-ids to exclude photo IDs from key
signing.
* gpg: Check for ambigious or non-matching key specification in the
config file or given to --encrypt-to.
* gpg: Show the used card reader with --card-status.
* gpg: Print export statistics and an EXPORTED status line.
* gpg: Allow selecting subkeys by keyid in --edit-key.
* gpg: Allow updating the expiration time of multiple subkeys at
once.
* dirmngr: New option --use-tor. For full support this requires
libassuan version 2.4.2 and a patched version of libadns
(e.g. adns-1.4-g10-7 as used by the standard Windows installer).
* dirmngr: New option --nameserver to specify the nameserver used in
Tor mode.
* dirmngr: Keyservers may again be specified by IP address.
* dirmngr: Fixed problems in resolving keyserver pools.
* dirmngr: Fixed handling of premature termination of TLS streams so
that large numbers of keys can be refreshed via hkps.
* gpg: Fixed a regression in --locate-key [since 2.1.9].
* gpg: Fixed another bug for keyrings with legacy keys.
* gpgsm: Allow combinations of usage flags in --gen-key.
* Make tilde expansion work with most options.
* Many other cleanups and bug fixes.
See-also: gnupg-announce/2015q4/000381.html
Noteworthy changes in version 2.1.9 (2015-10-09)
------------------------------------------------
* gpg: Allow fetching keys via OpenPGP DANE (--auto-key-locate). New
option --print-dane-records. [Update: --print-dane-records replaced
in 2.1.4.]
* gpg: Fix for a problem with PGP-2 keys in a keyring.
* gpg: Fail with an error instead of a warning if a modern cipher
algorithm is used without a MDC.
* agent: New option --pinentry-invisible-char.
* agent: Always do a RSA signature verification after creation.
* agent: Fix a regression in ssh-add-ing Ed25519 keys.
* agent: Fix ssh fingerprint computation for nistp384 and EdDSA.
* agent: Fix crash during passphrase entry on some platforms.
* scd: Change timeout to fix problems with some 2.1 cards.
* dirmngr: Displayed name is now Key Acquirer.
* dirmngr: Add option --keyserver. Deprecate that option for gpg.
Install a dirmngr.conf file from a skeleton for new installations.
See-also: gnupg-announce/2015q4/000380.html
Noteworthy changes in version 2.1.8 (2015-09-10)
------------------------------------------------
* gpg: Sending very large keys to the keyservers works again.
* gpg: Validity strings in key listings are now again translatable.
* gpg: Emit FAILURE status lines to help GPGME.
* gpg: Does not anymore link to Libksba to reduce dependencies.
* gpgsm: Export of secret keys via Assuan is now possible.
* agent: Raise the maximum passphrase length from 100 to 255 bytes.
* agent: Fix regression using EdDSA keys with ssh.
* Does not anymore use a build timestamp by default.
* The fallback encoding for broken locale settings changed
from Latin-1 to UTF-8.
* Many code cleanups and improved internal documentation.
* Various minor bug fixes.
See-also: gnupg-announce/2015q3/000379.html
Noteworthy changes in version 2.1.7 (2015-08-11)
------------------------------------------------
* gpg: Support encryption with Curve25519 if Libgcrypt 1.7 is used.
* gpg: In the --edit-key menu: Removed the need for "toggle", changed
how secret keys are indicated, new commands "fpr *" and "grip".
* gpg: More fixes related to legacy keys in a keyring.
* gpgv: Does now also work with a "trustedkeys.kbx" file.
* scd: Support some feature from the OpenPGP card 3.0 specs.
* scd: Improved ECC support
* agent: New option --force for the DELETE_KEY command.
* w32: Look for the Pinentry at more places.
* Dropped deprecated gpgsm-gencert.sh
* Various other bug fixes.
See-also: gnupg-announce/2015q3/000371.html
Noteworthy changes in version 2.1.6 (2015-07-01)
------------------------------------------------
* agent: New option --verify for the PASSWD command.
* gpgsm: Add command option "offline" as an alternative to
--disable-dirmngr.
* gpg: Do not prompt multiple times for a password in pinentry
loopback mode.
* Allow the use of debug category names with --debug.
* Using gpg-agent and gpg/gpgsm with different locales will now show
the correct translations in Pinentry.
* gpg: Improve speed of --list-sigs and --check-sigs.
* gpg: Make --list-options show-sig-subpackets work again.
* gpg: Fix an export problem for old keyrings with PGP-2 keys.
* scd: Support PIN-pads on more readers.
* dirmngr: Properly cleanup zombie LDAP helper processes and avoid
hangs on dirmngr shutdown.
* Various other bug fixes.
See-also: gnupg-announce/2015q3/000370.html
Noteworthy changes in version 2.1.5 (2015-06-11)
------------------------------------------------
* Support for an external passphrase cache.
* Support for the forthcoming version 3 OpenPGP smartcard.
* Manuals now show the actual used file names.
* Prepared for improved integration with Emacs.
* Code cleanups and minor bug fixes.
See-also: gnupg-announce/2015q2/000369.html
Noteworthy changes in version 2.1.4 (2015-05-12)
------------------------------------------------
* gpg: Add command --quick-adduid to non-interactively add a new user
id to an existing key.
* gpg: Do no enable honor-keyserver-url by default. Make it work if
enabled.
* gpg: Display the serial number in the --card-status output again.
* agent: Support for external password managers.
Add option --no-allow-external-cache.
* scdaemon: Improved handling of extended APDUs.
* Make HTTP proxies work again.
* All network access including DNS as been moved to Dirmngr.
* Allow building without LDAP support.
* Fixed lots of smaller bugs.
See-also: gnupg-announce/2015q2/000366.html
Noteworthy changes in version 2.1.3 (2015-04-11)
------------------------------------------------
* gpg: LDAP keyservers are now supported by 2.1.
* gpg: New option --with-icao-spelling.
* gpg: New option --print-pka-records. Changed the PKA method to use
CERT records and hashed names. [Update: --print-pka-records
replaced in 2.1.14.]
* gpg: New command --list-gcrypt-config. New parameter "curve"
for --list-config.
* gpg: Print a NEWSIG status line like gpgsm always did.
* gpg: Print MPI values with --list-packets and --verbose.
* gpg: Write correct MPI lengths with ECC keys.
* gpg: Skip legacy PGP-2 keys while searching.
* gpg: Improved searching for mail addresses when using a keybox.
* gpgsm: Changed default algos to AES-128 and SHA-256.
* gpgtar: Fixed extracting files with sizes of a multiple of 512.
* dirmngr: Fixed SNI handling for hkps pools.
* dirmngr: extra-certs and trusted-certs are now always loaded from
the sysconfig dir instead of the homedir.
* Fixed possible problems due to compiler optimization, two minor
regressions, and other bugs.
See-also: gnupg-announce/2015q2/000365.html
Noteworthy changes in version 2.1.2 (2015-02-11)
------------------------------------------------
* gpg: The parameter 'Passphrase' for batch key generation works
again.
* gpg: Using a passphrase option in batch mode now has the expected
effect on --quick-gen-key.
* gpg: Improved reporting of unsupported PGP-2 keys.
* gpg: Added support for algo names when generating keys using
--command-fd.
* gpg: Fixed DoS based on bogus and overlong key packets.
* agent: When setting --default-cache-ttl the value
for --max-cache-ttl is adjusted to be not lower than the former.
* agent: Fixed problems with the new --extra-socket.
* agent: Made --allow-loopback-pinentry changeable with gpgconf.
* agent: Fixed importing of unprotected openpgp keys.
* agent: Now tries to use a fallback pinentry if the standard
pinentry is not installed.
* scd: Added support for ECDH.
* Fixed several bugs related to bogus keyrings and improved some
other code.
See-also: gnupg-announce/2015q1/000361.html
Noteworthy changes in version 2.1.1 (2014-12-16)
------------------------------------------------
* gpg: Detect faulty use of --verify on detached signatures.
* gpg: New import option "keep-ownertrust".
* gpg: New sub-command "factory-reset" for --card-edit.
* gpg: A stub key for smartcards is now created by --card-status.
* gpg: Fixed regression in --refresh-keys.
* gpg: Fixed regresion in %g and %p codes for --sig-notation.
* gpg: Fixed best matching hash algo detection for ECDSA and EdDSA.
* gpg: Improved perceived speed of secret key listisngs.
* gpg: Print number of skipped PGP-2 keys on import.
* gpg: Removed the option aliases --throw-keyid and --notation-data;
use --throw-keyids and --set-notation instead.
* gpg: New import option "keep-ownertrust".
* gpg: Skip too large keys during import.
* gpg,gpgsm: New option --no-autostart to avoid starting gpg-agent or
dirmngr.
* gpg-agent: New option --extra-socket to provide a restricted
command set for use with remote clients.
* gpgconf --kill does not anymore start a service only to kill it.
* gpg-pconnect-agent: Add convenience option --uiserver.
* Fixed keyserver access for Windows.
* Fixed build problems on Mac OS X
* The Windows installer does now install development files
* More translations (but most of them are not complete).
* To support remotely mounted home directories, the IPC sockets may
now be redirected. This feature requires Libassuan 2.2.0.
* Improved portability and the usual bunch of bug fixes.
See-also: gnupg-announce/2014q4/000360.html
Noteworthy changes in version 2.1.0 (2014-11-06)
------------------------------------------------
This release introduces a lot of changes. Most of them are internal
and thus not user visible. However, some long standing behavior has
slightly changed and it is strongly suggested that an existing
"~/.gnupg" directory is backed up before this version is used.
A verbose description of the major new features and changes can be
found in the file doc/whats-new-in-2.1.txt.
* gpg: All support for v3 (PGP 2) keys has been dropped. All
signatures are now created as v4 signatures. v3 keys will be
removed from the keyring.
* gpg: With pinentry-0.9.0 the passphrase "enter again" prompt shows
up in the same window as the "new passphrase" prompt.
* gpg: Allow importing keys with duplicated long key ids.
* dirmngr: May now be build without support for LDAP.
* For a complete list of changes see the lists of changes for the
2.1.0 beta versions below. Note that all relevant fixes from
versions 2.0.14 to 2.0.26 are also applied to this version.
[Noteworthy changes in version 2.1.0-beta864 (2014-10-03)]
* gpg: Removed the GPG_AGENT_INFO related code. GnuPG does now
always use a fixed socket name in its home directory.
* gpg: Renamed --gen-key to --full-gen-key and re-added a --gen-key
command with less choices.
* gpg: Use SHA-256 for all signature types also on RSA keys.
* gpg: Default keyring is now created with a .kbx suffix.
* gpg: Add a shortcut to the key capabilies menu (e.g. "=e" sets the
encryption capabilities).
* gpg: Fixed obsolete options parsing.
* Further improvements for the alternative speedo build system.
[Noteworthy changes in version 2.1.0-beta834 (2014-09-18)]
* gpg: Improved passphrase caching.
* gpg: Switched to algorithm number 22 for EdDSA.
* gpg: Removed CAST5 from the default preferences.
* gpg: Order SHA-1 last in the hash preferences.
* gpg: Changed default cipher for --symmetric to AES-128.
* gpg: Fixed export of ECC keys and import of EdDSA keys.
* dirmngr: Fixed the KS_FETCH command.
* The speedo build system now downloads related packages and works
for non-Windows platforms.
[Noteworthy changes in version 2.1.0-beta783 (2014-08-14)]
* gpg: Add command --quick-gen-key.
* gpg: Make --quick-sign-key promote local key signatures.
* gpg: Added "show-usage" sub-option to --list-options.
* gpg: Screen keyserver responses to avoid importing unwanted keys
from rogue servers.
* gpg: Removed the option --pgp2 and --rfc1991 and the ability to
create PGP-2 compatible messages.
* gpg: Removed options --compress-keys and --compress-sigs.
* gpg: Cap attribute packets at 16MB.
* gpg: Improved output of --list-packets.
* gpg: Make with-colons output of --search-keys work again.
* gpgsm: Auto-create the ".gnupg" directory like gpg does.
* agent: Fold new passphrase warning prompts into one.
* scdaemon: Add support for the Smartcard-HSM card.
* scdaemon: Remove the use of the pcsc-wrapper.
[Noteworthy changes in version 2.1.0-beta751 (2014-07-03)]
* gpg: Create revocation certificates during key generation.
* gpg: Create exported secret keys and revocation certifciates with
mode 0700
* gpg: The validity of user ids is now shown by default. To revert
this add "list-options no-show-uid-validity" to gpg.conf.
* gpg: Make export of secret keys work again.
* gpg: The output of --list-packets does now print the offset of the
packet and information about the packet header.
* gpg: Avoid DoS due to garbled compressed data packets. [CVE-2014-4617]
* gpg: Print more specific reason codes with the INV_RECP status.
* gpg: Cap RSA and Elgamal keysize at 4096 bit also for unattended
key generation.
* scdaemon: Support reader Gemalto IDBridge CT30 and pinpad of SCT
cyberJack go.
* The speedo build system has been improved. It is now also possible
to build a partly working installer for Windows.
[Noteworthy changes in version 2.1.0-beta442 (2014-06-05)]
* gpg: Changed the format of key listings. To revert to the old
format the option --legacy-list-mode is available.
* gpg: Add experimental signature support using curve Ed25519 and
with a patched Libgcrypt also encryption support with Curve25519.
[Update: this encryption support has been removed from 2.1.0 until
we have agreed on a suitable format.]
* gpg: Allow use of Brainpool curves.
* gpg: Accepts a space separated fingerprint as user ID. This
allows to copy and paste the fingerprint from the key listing.
* gpg: The hash algorithm is now printed for signature records in key
listings.
* gpg: Reject signatures made using the MD5 hash algorithm unless the
new option --allow-weak-digest-algos or --pgp2 are given.
* gpg: Print a warning if the Gnome-Keyring-Daemon intercepts the
communication with the gpg-agent.
* gpg: New option --pinentry-mode.
* gpg: Fixed decryption using an OpenPGP card.
* gpg: Fixed bug with deeply nested compressed packets.
* gpg: Only the major version number is by default included in the
armored output.
* gpg: Do not create a trustdb file if --trust-model=always is used.
* gpg: Protect against rogue keyservers sending secret keys.
* gpg: The format of the fallback key listing ("gpg KEYFILE") is now
more aligned to the regular key listing ("gpg -k").
* gpg: The option--show-session-key prints its output now before the
decryption of the bulk message starts.
* gpg: New %U expando for the photo viewer.
* gpg,gpgsm: New option --with-secret.
* gpgsm: By default the users are now asked via the Pinentry whether
they trust an X.509 root key. To prohibit interactive marking of
such keys, the new option --no-allow-mark-trusted may be used.
* gpgsm: New commands to export a secret RSA key in PKCS#1 or PKCS#8
format.
* gpgsm: Improved handling of re-issued CA certificates.
* agent: The included ssh agent does now support ECDSA keys.
* agent: New option --enable-putty-support to allow gpg-agent on
Windows to act as a Pageant replacement with full smartcard support.
* scdaemon: New option --enable-pinpad-varlen.
* scdaemon: Various fixes for pinpad equipped card readers.
* scdaemon: Rename option --disable-pinpad (was --disable-keypad).
* scdaemon: Better support fo CCID readers. Now, internal CCID
driver supports readers with no auto configuration feature.
* dirmngr: Removed support for the original HKP keyserver which is
not anymore used by any site.
* dirmngr: Improved support for keyserver pools.
* tools: New option --dirmngr for gpg-connect-agent.
* The GNU Pth library has been replaced by the new nPth library.
* Support installation as portable application under Windows.
* All kind of other improvements - see the git log.
[Noteworthy changes in version 2.1.0beta3 (2011-12-20)]
* gpg: Fixed regression in the secret key export function.
* gpg: Allow generation of card keys up to 4096 bit.
* gpgsm: Preliminary support for the validation model "steed".
* gpgsm: Improved certificate creation.
* agent: Support the SSH confirm flag.
* agent: New option to select a passphrase mode. The loopback
mode may be used to bypass Pinentry.
* agent: The Assuan commands KILLAGENT and KILLSCD are working again.
* scdaemon: Does not anymore block after changing a card (regression
fix).
* tools: gpg-connect-agent does now proberly display the help output
for "SCD HELP" commands.
[Noteworthy changes in version 2.1.0beta2 (2011-03-08)]
* gpg: ECC support as described by draft-jivsov-openpgp-ecc-06.txt
[Update: now known as RFC-6637].
* gpg: Print "AES128" instead of "AES". This change introduces a
little incompatibility for tools using "gpg --list-config". We
hope that these tools are written robust enough to accept this new
algorithm name as well.
* gpgsm: New feature to create certificates from a parameter file.
Add prompt to the --gen-key UI to create self-signed certificates.
* agent: TMPDIR is now also honored when creating a socket using
the --no-standard-socket option and with symcryptrun's temp files.
* scdaemon: Fixed a bug where scdaemon sends a signal to gpg-agent
running in non-daemon mode.
* dirmngr: Fixed CRL loading under W32 (bug#1010).
* Dirmngr has taken over the function of the keyserver helpers. Thus
we now have a specified direct interface to keyservers via Dirmngr.
LDAP, DNS and mail backends are not yet implemented.
* Fixed TTY management for pinentries and session variable update
problem.
[Noteworthy changes in version 2.1.0beta1 (2010-10-26)]
* gpg: secring.gpg is not anymore used but all secret key operations
are delegated to gpg-agent. The import command moves secret keys
to the agent.
* gpg: The OpenPGP import command is now able to merge secret keys.
* gpg: Encrypted OpenPGP messages with trailing data (e.g. other
OpenPGP packets) are now correctly parsed.
* gpg: Given sufficient permissions Dirmngr is started automagically.
* gpg: Fixed output of "gpgconf --check-options".
* gpg: Removed options --export-options(export-secret-subkey-passwd)
and --simple-sk-checksum.
* gpg: New options --try-secret-key.
* gpg: Support DNS lookups for SRV, PKA and CERT on W32.
* gpgsm: The --audit-log feature is now more complete.
* gpgsm: The default for --include-cert is now to include all
certificates in the chain except for the root certificate.
* gpgsm: New option --ignore-cert-extension.
* g13: The G13 tool for disk encryption key management has been
added.
* agent: If the agent's --use-standard-socket option is active, all
tools try to start and daemonize the agent on the fly. In the past
this was only supported on W32; on non-W32 systems the new
configure option --disable-standard-socket may now be used to
disable this new default.
* agent: New and changed passphrases are now created with an
iteration count requiring about 100ms of CPU work.
* dirmngr: Dirmngr is now a part of this package. It is now also
expected to run as a system service and the configuration
directories are changed to the GnuPG name space. [Update: 2.1.0
starts dirmngr on demand as user daemon.]
* Support for Windows CE. [Update: This has not been tested for the
2.1.0 release]
* Numerical values may now be used as an alternative to the
debug-level keywords.
See-also: gnupg-announce/2014q4/000358.html
Version 2.0.28 (2015-06-02)
Version 2.0.27 (2015-02-18)
Version 2.0.26 (2014-08-12)
Version 2.0.25 (2014-06-30)
Version 2.0.24 (2014-06-24)
Version 2.0.23 (2014-06-03)
Version 2.0.22 (2013-10-04)
Version 2.0.21 (2013-08-19)
Version 2.0.20 (2013-05-10)
Version 2.0.19 (2012-03-27)
Version 2.0.18 (2011-08-04)
Version 2.0.17 (2011-01-13)
Version 2.0.16 (2010-07-19)
Version 2.0.15 (2010-03-09)
Version 2.0.14 (2009-12-21)
Noteworthy changes in version 2.0.13 (2009-09-04)
-------------------------------------------------
* GPG now generates 2048 bit RSA keys by default. The default hash
algorithm preferences has changed to prefer SHA-256 over SHA-1.
2048 bit DSA keys are now generated to use a 256 bit hash algorithm
* The envvars XMODIFIERS, GTK_IM_MODULE and QT_IM_MODULE are now
passed to the Pinentry to make SCIM work.
* The GPGSM command --gen-key features a --batch mode and implements
all features of gpgsm-gencert.sh in standard mode.
* New option --re-import for GPGSM's IMPORT server command.
* Enhanced writing of existing keys to OpenPGP v2 cards.
* Add hack to the internal CCID driver to allow the use of some
Omnikey based card readers with 2048 bit keys.
* GPG now repeatly asks the user to insert the requested OpenPGP
card. This can be disabled with --limit-card-insert-tries=1.
* Minor bug fixes.
See-also: gnupg-announce/2009q3/000294.html
Noteworthy changes in version 2.0.12 (2009-06-17)
-------------------------------------------------
* GPGSM now always lists ephemeral certificates if specified by
fingerprint or keygrip.
* New command "KEYINFO" for GPG_AGENT. GPGSM now also returns
information about smartcards.
* Made sure not to leak file descriptors if running gpg-agent with a
command. Restore the signal mask to solve a problem in Mono.
* Changed order of the confirmation questions for root certificates
and store negative answers in trustlist.txt.
* Better synchronization of concurrent smartcard sessions.
* Support 2048 bit OpenPGP cards.
* Support Telesec Netkey 3 cards.
* The gpg-protect-tool now uses gpg-agent via libassuan. Under
Windows the Pinentry will now be put into the foreground.
* Changed code to avoid a possible Mac OS X system freeze.
See-also: gnupg-announce/2009q2/000288.html
Noteworthy changes in version 2.0.11 (2009-03-03)
-------------------------------------------------
* Fixed a problem in SCDAEMON which caused unexpected card resets.
* SCDAEMON is now aware of the Geldkarte.
* The SCDAEMON option --allow-admin is now used by default.
* GPGCONF now restarts SCdaemon if necessary.
* The default cipher algorithm in GPGSM is now again 3DES. This is
due to interoperability problems with Outlook 2003 which still
can't cope with AES.
See-also: gnupg-announce/2009q1/000287.html
Noteworthy changes in version 2.0.10 (2009-01-12)
-------------------------------------------------
* [gpg] New keyserver helper gpg2keys_kdns as generic DNS CERT
lookup. Run with --help for a short description. Requires the
ADNS library.
* [gpg] New mechanisms "local" and "nodefault" for --auto-key-locate.
Fixed a few problems with this option.
* [gpg] New command --locate-keys.
* [gpg] New options --with-sig-list and --with-sig-check.
* [gpg] The option "-sat" is no longer an alias for --clearsign.
* [gpg] The option --fixed-list-mode is now implicitly used and obsolete.
* [gpg] New control statement %ask-passphrase for the unattended key
generation.
* [gpg] The algorithm to compute the SIG_ID status has been changed.
* [gpgsm] Now uses AES by default.
* [gpgsm] Made --output option work with --export-secret-key-p12.
* [gpg-agent] Terminate process if the own listening socket is not
anymore served by ourself.
* [scdaemon] Made it more robust on W32.
* [gpg-connect-agent] Accept commands given as command line arguments.
* [w32] Initialized the socket subsystem for all keyserver helpers.
* [w32] The sysconf directory has been moved from a subdirectory of
the installation directory to %CSIDL_COMMON_APPDATA%/GNU/etc/gnupg.
* [w32] The gnupg2.nls directory is not anymore used. The standard
locale directory is now used.
* [w32] Fixed a race condition between gpg and gpgsm in the use of
temporary file names.
* The gpg-preset-passphrase mechanism works again. An arbitrary
string may now be used for a custom cache ID.
* Admin PINs are cached again (bug in 2.0.9).
* Support for version 2 OpenPGP cards.
* Libgcrypt 1.4 is now required.
See-also: gnupg-announce/2009q1/000284.html
Noteworthy changes in version 2.0.9 (2008-03-26)
------------------------------------------------
* Gpgsm always tries to locate missing certificates from a running
Dirmngr's cache.
* Tweaks for Windows.
* The Admin PIN for OpenPGP cards may now be entered with the pinpad.
* Improved certificate chain construction.
* Extended the PKITS framework.
* Fixed a bug in the ambigious name detection.
* Fixed possible memory corruption while importing OpenPGP keys (bug
introduced with 2.0.8). [CVE-2008-1530]
* Minor bug fixes.
Noteworthy changes in version 2.0.8 (2007-12-20)
------------------------------------------------
* Enhanced gpg-connect-agent with a small scripting language.
* New option --list-config for gpgconf.
* Fixed a crash in gpgconf.
* Gpg-agent now supports the passphrase quality bar of the latest
Pinentry.
* The envvars XAUTHORITY and PINENTRY_USER_DATA are now passed to the
Pinentry.
* Fixed the auto creation of the key stub for smartcards.
* Fixed a rare bug in decryption using the OpenPGP card.
* Creating DSA2 keys is now possible.
* New option --extra-digest-algo for gpgsm to allow verification of
broken signatures.
* Allow encryption with legacy Elgamal sign+encrypt keys with option
--rfc2440.
* Windows is now a supported platform.
* Made sure that under Windows the file permissions of the socket are
taken into account. This required a change of our socket emulation
code and changed the IPC protocol under Windows.
See-also: gnupg-announce/2007q4/000267.html
Noteworthy changes in version 2.0.7 (2007-09-10)
------------------------------------------------
* Fixed encryption problem if duplicate certificates are in the
keybox.
* Made it work on Windows Vista. Note that the entire Windows port
is still considered Beta.
* Add new options min-passphrase-nonalpha, check-passphrase-pattern,
enforce-passphrase-constraints and max-passphrase-days to
gpg-agent.
* Add command --check-components to gpgconf. Gpgconf now uses the
installed versions of the programs and does not anymore search via
PATH for them.
See-also: gnupg-announce/2007q3/000259.html
Noteworthy changes in version 2.0.6 (2007-08-16)
------------------------------------------------
* GPGSM does now grok --default-key.
* GPGCONF is now aware of --default-key and --encrypt-to.
* GPGSM does again correctly print the serial number as well the the
various keyids. This was broken since 2.0.4.
* New option --validation-model and support for the chain-model.
* Improved Windows support.
See-also: gnupg-announce/2007q3/000258.html
Noteworthy changes in version 2.0.5 (2007-07-05)
------------------------------------------------
* Switched license to GPLv3.
* Basic support for Windows. Run "./autogen.sh --build-w32" to build
it. As usual the mingw cross compiling toolchain is required.
* Fixed bug when using the --p12-charset without --armor.
* The command --gen-key may now be used instead of the
gpgsm-gencert.sh script.
* Changed key generation to reveal less information about the
machine. Bug fixes for gpg2's card key generation.
See-also: gnupg-announce/2007q3/000255.html
Noteworthy changes in version 2.0.4 (2007-05-09)
------------------------------------------------
* The server mode key listing commands are now also working for
systems without the funopen/fopencookie API.
* PKCS#12 import now tries several encodings in case the passphrase
was not utf-8 encoded. New option --p12-charset for gpgsm.
* Improved the libgcrypt logging support in all modules.
See-also: gnupg-announce/2007q2/000254.html
Noteworthy changes in version 2.0.3 (2007-03-08)
------------------------------------------------
* By default, do not allow processing multiple plaintexts in a single
stream. Many programs that called GnuPG were assuming that GnuPG
did not permit this, and were thus not using the plaintext boundary
status tags that GnuPG provides. This change makes GnuPG reject
such messages by default which makes those programs safe again.
--allow-multiple-messages returns to the old behavior. [CVE-2007-1263].
* New --verify-option show-primary-uid-only.
* gpgconf may now reads a global configuration file to select which
options are changeable by a frontend. The new applygnupgdefaults
tool may be used by an admin to set default options for all users.
* The PIN pad of the Cherry XX44 keyboard is now supported. The
DINSIG and the NKS applications are now also aware of PIN pads.
See-also: gnupg-announce/2007q1/000252.html
Noteworthy changes in version 2.0.2 (2007-01-31)
------------------------------------------------
* Fixed a serious and exploitable bug in processing encrypted
packages. [CVE-2006-6235].
* Added --passphrase-repeat to set the number of times GPG will
prompt for a new passphrase to be repeated. This is useful to help
memorize a new passphrase. The default is 1 repetition.
* Using a PIN pad does now also work for the signing key.
* A warning is displayed by gpg-agent if a new passphrase is too
short. New option --min-passphrase-len defaults to 8.
* The status code BEGIN_SIGNING now shows the used hash algorithms.
See-also: gnupg-announce/2007q1/000249.html
Noteworthy changes in version 2.0.1 (2006-11-28)
------------------------------------------------
* Experimental support for the PIN pads of the SPR 532 and the Kaan
Advanced card readers. Add "disable-keypad" scdaemon.conf if you
don't want it. Does currently only work for the OpenPGP card and
its authentication and decrypt keys.
* Fixed build problems on some some platforms and crashes on amd64.
* Fixed a buffer overflow in gpg2. [bug#728,CVE-2006-6169]
See-also: gnupg-announce/2006q4/000242.html
Noteworthy changes in version 2.0.0 (2006-11-11)
------------------------------------------------
* First stable version of a GnuPG integrating OpenPGP and S/MIME.
See-also: gnupg-announce/2006q4/000239.html
Noteworthy changes in version 1.9.95 (2006-11-06)
-------------------------------------------------
* Minor bug fixes.
Noteworthy changes in version 1.9.94 (2006-10-24)
-------------------------------------------------
* Keys for gpgsm may now be specified using a keygrip. A keygrip is
indicated by a prefixing it with an ampersand.
* gpgconf now supports switching the CMS cipher algo (e.g. to AES).
* New command --gpgconf-test for all major tools. This may be used to
check whether the configuration file is sane.
Noteworthy changes in version 1.9.93 (2006-10-18)
-------------------------------------------------
* In --with-validation mode gpgsm will now also ask whether a root
certificate should be trusted.
* Link to Pth only if really necessary.
* Fixed a pubring corruption bug in gpg2 occurring when importing
signatures or keys with insane lengths.
* Fixed v3 keyID calculation bug in gpg2.
* More tweaks for certificates without extensions.
Noteworthy changes in version 1.9.92 (2006-10-11)
-------------------------------------------------
* Bug fixes.
See-also: gnupg-announce/2006q4/000236.html
Noteworthy changes in version 1.9.91 (2006-10-04)
-------------------------------------------------
* New "relax" flag for trustlist.txt to allow root CA certificates
without BasicContraints.
* [gpg2] Removed the -k PGP 2 compatibility hack. -k is now an
alias for --list-keys.
* [gpg2] Print a warning if "-sat" is used instead of "--clearsign".
Noteworthy changes in version 1.9.90 (2006-09-25)
-------------------------------------------------
* Made readline work for gpg.
* Cleanups und minor bug fixes.
* Included translations from gnupg 1.4.5.
Noteworthy changes in version 1.9.23 (2006-09-18)
-------------------------------------------------
* Regular man pages for most tools are now build directly from the
Texinfo source.
* The gpg code from 1.4.5 has been fully merged into this release.
The configure option --enable-gpg is still required to build this
gpg part. For production use of OpenPGP the gpg version 1.4.5 is
still recommended. Note, that gpg will be installed under the name
gpg2 to allow coexisting with an 1.4.x gpg.
* API change in gpg-agent's pkdecrypt command. Thus an older gpgsm
may not be used with the current gpg-agent.
* The scdaemon will now call a script on reader status changes.
* gpgsm now allows file descriptor passing for "INPUT", "OUTPUT" and
"MESSAGE".
* The gpgsm server may now output a key listing to the output file
handle. This needs to be enabled using "OPTION list-to-output=1".
* The --output option of gpgsm has now an effect on list-keys.
* New gpgsm commands --dump-chain and list-chain.
* gpg-connect-agent has new options to utilize descriptor passing.
* A global trustlist may now be used. See doc/examples/trustlist.txt.
* When creating a new pubring.kbx keybox common certificates are
imported.
Noteworthy changes in version 1.9.22 (2006-07-27)
-------------------------------------------------
* Enhanced pkcs#12 support to allow import from simple keyBags.
* Exporting to pkcs#12 now create bag attributes so that Mozilla is
able to import the files.
* Fixed uploading of certain keys to the smart card.
Noteworthy changes in version 1.9.21 (2006-06-20)
-------------------------------------------------
* New command APDU for scdaemon to allow using it for general card
access. Might be used through gpg-connect-agent by using the SCD
prefix command.
* Support for the CardMan 4040 PCMCIA reader (Linux 2.6.15 required).
* Scdaemon does not anymore reset cards at the end of a connection.
* Kludge to allow use of Bundesnetzagentur issued X.509 certificates.
* Added --hash=xxx option to scdaemon's PKSIGN command.
* Pkcs#12 files are now created with a MAC. This is for better
interoperability.
* Collected bug fixes and minor other changes.
Noteworthy changes in version 1.9.20 (2005-12-20)
-------------------------------------------------
* Importing pkcs#12 files created be recent versions of Mozilla works
again.
* Basic support for qualified signatures.
* New debug tool gpgparsemail.
Noteworthy changes in version 1.9.19 (2005-09-12)
-------------------------------------------------
* The Belgian eID card is now supported for signatures and ssh.
Other pkcs#15 cards should work as well.
* Fixed bug in --export-secret-key-p12 so that certificates are again
included.
Noteworthy changes in version 1.9.18 (2005-08-01)
-------------------------------------------------
* [gpgsm] Now allows for more than one email address as well as URIs
and dnsNames in certificate request generation. A keygrip may be
given to create a request from an existing key.
* A couple of minor bug fixes.
Noteworthy changes in version 1.9.17 (2005-06-20)
-------------------------------------------------
* gpg-connect-agent has now features to handle Assuan INQUIRE
commands.
* Internal changes for OpenPGP cards. New Assuan command WRITEKEY.
* GNU Pth is now a hard requirement.
* [scdaemon] Support for OpenSC has been removed. Instead a new and
straightforward pkcs#15 modules has been written. As of now it
does allows only signing using TCOS cards but we are going to
enhance it to match all the old capabilities.
* [gpg-agent] New option --write-env-file and Assuan command
UPDATESTARTUPTTY.
* [gpg-agent] New option --default-cache-ttl-ssh to set the TTL for
SSH passphrase caching independent from the other passphrases.
Noteworthy changes in version 1.9.16 (2005-04-21)
-------------------------------------------------
* gpg-agent does now support the ssh-agent protocol and thus allows
to use the pinentry as well as the OpenPGP smartcard with ssh.
* New tool gpg-connect-agent as a general client for the gpg-agent.
* New tool symcryptrun as a wrapper for certain encryption tools.
* The gpg tool is not anymore build by default because those gpg
versions available in the gnupg 1.4 series are far more matured.
Noteworthy changes in version 1.9.15 (2005-01-13)
-------------------------------------------------
* Fixed passphrase caching bug.
* Better support for CCID readers; the reader from Cherry RS 6700 USB
does now work.
Noteworthy changes in version 1.9.14 (2004-12-22)
-------------------------------------------------
* [gpg-agent] New option --use-standard-socket to allow the use of a
fixed socket. gpgsm falls back to this socket if GPG_AGENT_INFO
has not been set.
* Ported to MS Windows with some functional limitations.
* New tool gpg-preset-passphrase.
Noteworthy changes in version 1.9.13 (2004-12-03)
-------------------------------------------------
* [gpgsm] New option --prefer-system-dirmngr.
* Minor cleanups and debugging aids.
Noteworthy changes in version 1.9.12 (2004-10-22)
-------------------------------------------------
* [scdaemon] Partly rewrote the PC/SC code.
* Removed the sc-investigate tool. It is now in a separate package
available at ftp://ftp.g10code.com/g10code/gscutils/ .
* [gpg-agent] Fixed logging problem.
Noteworthy changes in version 1.9.11 (2004-10-01)
-------------------------------------------------
* When using --import along with --with-validation, the imported
certificates are validated and only imported if they are fully
valid.
* [gpg-agent] New option --max-cache-ttl.
* [gpg-agent] When used without --daemon or --server, gpg-agent now
check whether a agent is already running and usable.
* Fixed some i18n problems.
Noteworthy changes in version 1.9.10 (2004-07-22)
-------------------------------------------------
* Fixed a serious bug in the checking of trusted root certificates.
* New configure option --enable-agent-pnly allows to build and
install just the agent.
* Fixed a problem with the log file handling.
Noteworthy changes in version 1.9.9 (2004-06-08)
------------------------------------------------
* [gpg-agent] The new option --allow-mark-trusted is now required to
allow gpg-agent to add a key to the trustlist.txt after user
confirmation.
* Creating PKCS#10 requests does now honor the key usage.
Noteworthy changes in version 1.9.8 (2004-04-29)
------------------------------------------------
* [scdaemon] Overhauled the internal CCID driver.
* [scdaemon] Status files named ~/.gnupg/reader_<n>.status are now
written when using the internal CCID driver.
* [gpgsm] New commands --dump-{,secret,external}-keys to show a very
detailed view of the certificates.
* The keybox gets now compressed after 3 hours and ephemeral
stored certificates are deleted after about a day.
* [gpg] Usability fixes for --card-edit. Note, that this has already
been ported back to gnupg-1.3
Noteworthy changes in version 1.9.7 (2004-04-06)
------------------------------------------------
* Instrumented the modules for gpgconf.
* Added support for DINSIG card applications.
* Include the smimeCapabilities attribute with signed messages.
* Now uses the gettext domain "gnupg2" to avoid conflicts with gnupg
versions < 1.9.
Noteworthy changes in version 1.9.6 (2004-03-06)
------------------------------------------------
* Code cleanups and bug fixes.
Noteworthy changes in version 1.9.5 (2004-02-21)
------------------------------------------------
* gpg-protect-tool gets now installed into libexec as it ought to be.
Cleaned up the build system to better comply with the coding
standards.
* [gpgsm] The --import command is now able to autodetect pkcs#12
files and import secret and private keys from this file format.
A new command --export-secret-key-p12 is provided to allow
exporting of secret keys in PKCS\#12 format.
* [gpgsm] The pinentry will now present a description of the key for
whom the passphrase is requested.
* [gpgsm] New option --with-validation to check the validity of key
while listing it.
* New option --debug-level={none,basic,advanced,expert,guru} to map
the debug flags to sensitive levels on a per program base.
Noteworthy changes in version 1.9.4 (2004-01-30)
------------------------------------------------
* Added support for the Telesec NKS 2.0 card application.
* Added simple tool addgnupghome to create .gnupg directories from
/etc/skel/.gnupg.
* Various minor bug fixes and cleanups; mainly gpgsm and gpg-agent
related.
Noteworthy changes in version 1.9.3 (2003-12-23)
------------------------------------------------
* New gpgsm options --{enable,disable}-ocsp to validate keys using
OCSP. This option requires a not yet released DirMngr version.
Default is disabled.
* The --log-file option may now be used to print logs to a socket.
Prefix the socket name with "socket://" to enable this. This does
not work on all systems and falls back to stderr if there is a
problem with the socket.
* The options --encrypt-to and --no-encrypt-to now work the same in
gpgsm as in gpg. Note, they are also used in server mode.
* Duplicated recipients are now silently removed in gpgsm.
Noteworthy changes in version 1.9.2 (2003-11-17)
------------------------------------------------
* On card key generation is no longer done using the --gen-key
command but from the menu provided by the new --card-edit command.
* PINs are now properly cached and there are only 2 PINs visible.
The 3rd PIN (CHV2) is internally synchronized with the regular PIN.
* All kind of other internal stuff.
Noteworthy changes in version 1.9.1 (2003-09-06)
------------------------------------------------
* Support for OpenSC is back. scdaemon supports a --disable-opensc to
disable OpenSC use at runtime, so that PC/SC or ct-API can still be
used directly.
* Rudimentary support for the SCR335 smartcard reader using an
internal driver. Requires current libusb from CVS.
* Bug fixes.
Noteworthy changes in version 1.9.0 (2003-08-05)
------------------------------------------------
====== PLEASE SEE README-alpha =======
* gpg has been renamed to gpg2 and gpgv to gpgv2. This is a
temporary change to allow co-existing with stable gpg versions.
* ~/.gnupg/gpg.conf-1.9.0 is fist tried as config file before the
usual gpg.conf.
* Removed the -k, -kv and -kvv commands. -k is now an alias to
--list-keys. New command -K as alias for --list-secret-keys.
* Removed --run-as-shm-coprocess feature.
* gpg does now also use libgcrypt, libgpg-error is required.
* New gpgsm commands --call-dirmngr and --call-protect-tool.
* Changing a passphrase is now possible using "gpgsm --passwd"
* The content-type attribute is now recognized and created.
* The agent does now reread certain options on receiving a HUP.
* The pinentry is now forked for each request so that clients with
different environments are supported. When running in daemon mode
and --keep-display is not used the DISPLAY variable is ignored.
* Merged stuff from the newpg branch and started this new
development branch.
Version 1.4.19 (2015-02-27)
Version 1.4.18 (2014-06-30)
Version 1.4.17 (2014-06-23)
Version 1.4.16 (2013-12-18)
Version 1.4.15 (2013-10-04)
Version 1.4.14 (2013-07-25)
Version 1.4.13 (2012-12-20)
Version 1.4.12 (2012-01-30)
Version 1.4.11 (2010-10-18)
Version 1.4.10 (2009-09-02)
Version 1.4.9 (2008-03-26)
Version 1.4.8 (2007-12-20)
Version 1.4.7 (2007-03-05)
Version 1.4.6 (2006-12-06)
Version 1.4.5 (2006-08-01)
Version 1.4.4 (2006-06-25)
Version 1.4.3 (2006-04-03)
Version 1.4.2 (2005-07-26)
Version 1.4.1 (2005-03-15)
Version 1.4.0 (2004-12-16)
Noteworthy changes in version 1.3.2 (2003-05-27)
------------------------------------------------
* New "--gnupg" option (set by default) that disables --openpgp,
and the various --pgpX emulation options. This replaces
--no-openpgp, and --no-pgpX, and also means that GnuPG has
finally grown a --gnupg option to make GnuPG act like GnuPG.
* A bug in key validation has been fixed. This bug only affects
keys with more than one user ID (photo IDs do not count here),
and results in all user IDs on a given key being treated with
the validity of the most-valid user ID on that key.
* Notation names that do not contain a '@' are no longer allowed
unless --expert is set. This is to help prevent pollution of
the (as yet unused) IETF notation namespace.
* Multiple trust models are now supported via the --trust-model
option. The options are "pgp" (web-of-trust plus trust
signatures), "classic" (web-of-trust only), and "always"
(identical to the --always-trust option).
* The --personal-{cipher|digest|compression}-preferences are now
consulted to get default algorithms before resorting to the
last-ditch defaults of --s2k-cipher-algo, SHA1, and ZIP
respectively. This allows a user to set algorithms to use in a
safe manner so they are used when legal to do so, without
forcing them on for all messages.
* New --primary-keyring option to designate the keyring that the
user wants new keys imported into.
* --s2k-digest-algo is now used for all password mangling.
Earlier versions used both --s2k-digest-algo and --digest-algo
for passphrase mangling.
* Handling of --hidden-recipient or --throw-keyid messages is now
easier - the user only needs to give their passphrase once, and
GnuPG will try it against all of the available secret keys.
* Care is taken to prevent compiler optimization from removing
memory wiping code.
* New option --no-mangle-dos-filenames so that filenames are not
truncated in the W32 version.
* A "convert-from-106" script has been added. This is a simple
script that automates the conversion from a 1.0.6 or earlier
version of GnuPG to a 1.0.7 or later version.
* Disabled keys are now skipped when selecting keys for
encryption. If you are using the --with-colons key listings to
detect disabled keys, please see doc/DETAILS for a minor format
change in this release.
* Minor trustdb changes to make the trust calculations match
common usage.
* New command "revuid" in the --edit-key menu to revoke a user ID.
This is a simpler interface to the old method (which still
works) of revoking the user ID self-signature.
* Status VALIDSIG does now also print the primary key's
fingerprint, as well as the signature version, pubkey algorithm,
hash algorithm, and signature class.
* Add read-only support for the SHA-256 hash, and optional
read-only support for the SHA-384 and SHA-512 hashes.
* New option --enable-progress-filter for use with frontends.
* DNS SRV records are used in HKP keyserver lookups to allow
administrators to load balance and select keyserver ports
automatically. This is as specified in
draft-shaw-openpgp-hkp-00.txt.
* When using the "keyid!" syntax during a key export, only that
specified key is exported. If the key in question is a subkey,
the primary key plus only that subkey is exported.
* configure --disable-xxx options to disable individual algorithms
at build time. This can be used to build a smaller gpg binary
for embedded uses where space is tight. See the README file for
the algorithms that can be used with this option, or use
--enable-minimal to build the smallest gpg possible (disables
all optional algorithms, disables keyserver access, and disables
photo IDs).
* The keyserver no-modify flag on a key can now be displayed and
modified.
* Note that the TIGER/192 digest algorithm is in the process of
being dropped from the OpenPGP standard. While this release of
GnuPG still contains it, it is disabled by default. To ensure
you will still be able to use your messages with future versions
of GnuPG and other OpenPGP programs, please do not use this
algorithm.
See-also: gnupg-announce/2003q2/000153.html
Noteworthy changes in version 1.3.1 (2002-11-12)
------------------------------------------------
* Trust signature support. This is based on the Maurer trust
model where a user can specify the trust level along with the
signature with multiple levels so users can delegate
certification ability to other users, possibly restricted by a
regular expression on the user ID. Note that full trust
signature support requires a regular expression parsing library.
The regexp code from glibc 2.3.1 is included for those platforms
that don't have working regexp functions available. The
configure option --disable-regex may be used to disable any
regular expression code, which will make GnuPG ignore any trust
signature with a regular expression included.
* Two new commands --hidden-recipient (-R) and --hidden-encrypt-to
encrypt to a user, but hide the identity of that user. This is
the same functionality as --throw-keyid, but can be used on a
per-user basis.
* Full algorithm names (e.g. "3DES", "SHA1", "ZIP") can now be
used interchangeably with the short algorithm names (e.g. "S2",
"H2", "Z1") anywhere algorithm names are used in GnuPG.
Noteworthy changes in version 1.3.0 (2002-10-18)
------------------------------------------------
* The last piece of internal keyserver support has been removed,
and now all keyserver access is done via the keyserver plugins.
There is also a newer keyserver protocol used between GnuPG and
the plugins, so plugins from earlier versions of GnuPG may not
work properly.
* The HKP keyserver plugin supports the new machine-readable key
listing format for those keyservers that provide it.
* When using a HKP keyserver with multiple DNS records (such as
wwwkeys.pgp.net which has the addresses of multiple servers
around the world), try all records until one succeeds. Note
that it depends on the LDAP library used whether the LDAP
keyserver plugin does this as well.
* The library dependencies for OpenLDAP seem to change fairly
frequently, and GnuPG's configure script cannot guess all the
combinations. Use ./configure LDAPLIBS="-L libdir -l libs" to
override the script and use the libraries selected.
* Secret keys generated with --export-secret-subkeys are now
indicated in key listings with a '#' after the "sec", and in
--with-colons listings by showing no capabilities (no lowercase
characters).
* --trusted-key has been un-obsoleted, as it is useful for adding
ultimately trusted keys from the config file. It is identical
to using --edit and "trust" to change a key to ultimately
trusted.
* Translations other than de are no longer distributed with the
development branch. This is due to the frequent text changes
during development, which cause the translations to rapidly go
out of date.
Version 1.2.8 (2006-12-07)
Version 1.2.7 (2004-12-27)
Version 1.2.6 (2004-08-25)
Version 1.2.5 (2004-07-26)
Version 1.2.4 (2003-12-23)
Version 1.2.3 (2003-08-21)
Version 1.2.2 (2003-05-01)
Version 1.2.1 (2002-10-25)
Version 1.2.0 (2002-09-21)
Noteworthy changes in version 1.1.92 (2002-09-11)
-------------------------------------------------
* [IMPORTANT] The default configuration file is now
~/.gnupg/gpg.conf. If an old ~/.gnupg/options is found it will
still be used. This change is required to have a more
consistent naming scheme with forthcoming tools.
* The use of MDCs have increased. A MDC will be used if the
recipients directly request it, if the recipients have AES,
AES192, AES256, or TWOFISH in their cipher preferences, or if
the chosen cipher has a blocksize not equal to 64 bits
(currently this is also AES, AES192, AES256, and TWOFISH).
* GnuPG will no longer automatically disable compression when
processing an already-compressed file unless a MDC is being
used. This is to give the message a certain amount of
resistance to the chosen-ciphertext attack while communicating
with other programs (most commonly PGP earlier than version 7.x)
that do not support MDCs.
* The option --interactive now has the desired effect when
importing keys.
* The file permission and ownership checks on files have been
clarified. Specifically, the homedir (usually ~/.gnupg) is
checked to protect everything within it. If the user specifies
keyrings outside this homedir, they are presumed to be shared
keyrings and therefore *not* checked. Configuration files
specified with the --options option and the IDEA cipher
extension specified with --load-extension are checked, along
with their enclosing directories.
* The configure option --with-static-rnd=auto allows to build gpg
with all available entropy gathering modules included. At
runtime the best usable one will be selected from the list
linux, egd, unix. This is also the default for systems lacking
a /dev/random device.
* The default character set is now taken from the current locale;
it can still be overridden by the --charset option. Using the
option -vvv shows the used character set.
* [REMOVED] --emulate-checksum-bug and --emulate-3des-s2k-bug have
been removed.
Noteworthy changes in version 1.1.91 (2002-08-04)
-------------------------------------------------
* All modules are now linked statically; the --load-extension
option is in general not useful anymore. The only exception is
to specify the deprecated idea cipher.
* The IDEA plugin has changed. Previous versions of the IDEA
plugin will no longer work with GnuPG. However, the current
version of the plugin will work with earlier GnuPG versions.
* When using --batch with one of the --delete-key commands, the
key must be specified by fingerprint. See the man page for
details.
* There are now various ways to restrict the ability GnuPG has to
exec external programs (for the keyserver helpers or photo ID
viewers). Read the README file for the complete list.
* New export option to leave off attribute packets (photo IDs)
during export. This is useful when exporting to HKP keyservers
which do not understand attribute packets.
* New import option to repair during import the HKP keyserver
mangling multiple subkeys bug. Note that this cannot completely
repair the damaged key as some crucial data is removed by the
keyserver, but it does at least give you back one subkey. This
is on by default for keyserver --recv-keys, and off by default
for regular --import.
* The keyserver helper programs now live in
/usr/[local/]libexec/gnupg by default. If you are upgrading
from 1.0.7, you might want to delete your old copies in
/usr/[local/]bin. If you use an OS that does not use libexec
for whatever reason, use configure --libexecdir=/usr/local/lib
to place the keyserver helpers there.
* The LDAP keyserver handler now works properly with very old
(version 1) LDAP keyservers.
Noteworthy changes in version 1.1.90 (2002-07-01)
-------------------------------------------------
* New commands: --personal-cipher-preferences,
--personal-digest-preferences, and
--personal-compress-preferences allow the user to specify which
algorithms are to be preferred. Note that this does not permit
using an algorithm that is not present in the recipient's
preferences (which would violate the OpenPGP standard). This
just allows sorting the preferences differently.
* New "group" command to refer to several keys with one name.
* A warning is issued if the user forces the use of an algorithm
that is not listed in the recipient's preferences.
* Full revocation key (aka "designated revoker") support.
* The preferred hash algorithms on a key are consulted when
encrypting a signed message to that key. Note that this is
disabled by default by a SHA1 preference in
--personal-digest-preferences.
* --cert-digest-algo allows the user to specify the hash algorithm
to use when signing a key rather than the default SHA1 (or MD5
for PGP2 keys). Do not use this feature unless you fully
understand the implications of this.
* --pgp7 mode automatically sets all necessary options to ensure
that the resulting message will be usable by a user of PGP 7.x.
* New --attribute-fd command for frontends and scripts to get the
contents of attribute packets (i.e. photos)
* In expert mode, the user can now re-sign a v3 key with a v4
self-signature. This does not change the v3 key into a v4 key,
but it does allow the user to use preferences, primary ID flags,
etc.
* Significantly improved photo ID support on non-unixlike
platforms.
* The version number has jumped ahead to 1.1.90 to skip over the
old version 1.1 and to get ready for the upcoming 1.2.
* ElGamal sign and encrypt is not anymore allowed in the key
generation dialog unless in expert mode. RSA sign and encrypt
has been added with the same restrictions.
* [W32] Keyserver access does work with Windows NT.
Noteworthy changes in version 1.0.7 (2002-04-29)
------------------------------------------------
* Secret keys are now stored and exported in a new format which
uses SHA-1 for integrity checks. This format renders the
Rosa/Klima attack useless. Other OpenPGP implementations might
not yet support this, so the option --simple-sk-checksum creates
the old vulnerable format.
* The default cipher algorithm for encryption is now CAST5,
default hash algorithm is SHA-1. This will give us better
interoperability with other OpenPGP implementations.
* Symmetric encrypted messages now use a fixed file size if
possible. This is a tradeoff: it breaks PGP 5, but fixes PGP 2,
6, and 7. Note this was only an issue with RFC-1991 style
symmetric messages.
* Photographic user ID support. This uses an external program to
view the images.
* Enhanced keyserver support via keyserver "plugins". GnuPG comes
with plugins for the NAI LDAP keyserver as well as the HKP email
keyserver. It retains internal support for the HKP HTTP
keyserver.
* Nonrevocable signatures are now supported. If a user signs a
key nonrevocably, this signature cannot be taken back so be
careful!
* Multiple signature classes are usable when signing a key to
specify how carefully the key information (fingerprint, photo
ID, etc) was checked.
* --pgp2 mode automatically sets all necessary options to ensure
that the resulting message will be usable by a user of PGP 2.x.
* --pgp6 mode automatically sets all necessary options to ensure
that the resulting message will be usable by a user of PGP 6.x.
* Signatures may now be given an expiration date. When signing a
key with an expiration date, the user is prompted whether they
want their signature to expire at the same time.
* Revocation keys (designated revokers) are now supported if
present. There is currently no way to designate new keys as
designated revokers.
* Permissions on the .gnupg directory and its files are checked
for safety.
* --expert mode enables certain silly things such as signing a
revoked user id, expired key, or revoked key.
* Some fixes to build cleanly under Cygwin32.
* New tool gpgsplit to split OpenPGP data formats into packets.
* New option --preserve-permissions.
* Subkeys created in the future are not used for encryption or
signing unless the new option --ignore-valid-from is used.
* Revoked user-IDs are not listed unless signatures are listed too
or we are in verbose mode.
* There is no default comment string with ascii armors anymore
except for revocation certificates and --enarmor mode.
* The command "primary" in the edit menu can be used to change the
primary UID, "setpref" and "updpref" can be used to change the
preferences.
* Fixed the preference handling; since 1.0.5 they were erroneously
matched against against the latest user ID and not the given one.
* RSA key generation.
* Merged Stefan's patches for RISC OS in. See comments in
scripts/build-riscos.
* It is now possible to sign and conventional encrypt a message (-cs).
* The MDC feature flag is supported and can be set by using
the "updpref" edit command.
* The status messages GOODSIG and BADSIG are now returning the primary
UID, encoded using %XX escaping (but with spaces left as spaces,
so that it should not break too much)
* Support for GDBM based keyrings has been removed.
* The entire keyring management has been revamped.
* The way signature stati are store has changed so that v3
signatures can be supported. To increase the speed of many
operations for existing keyrings you can use the new
--rebuild-keydb-caches command.
* The entire key validation process (trustdb) has been revamped.
See the man page entries for --update-trustdb, --check-trustdb
and --no-auto-check-trustdb.
* --trusted-keys is again obsolete, --edit can be used to set the
ownertrust of any key to ultimately trusted.
* A subkey is never used to sign keys.
* Read only keyrings are now handled as expected.
See-also: gnupg-announce/2002q2/000135.html
Noteworthy changes in version 1.0.6 (2001-05-29)
------------------------------------------------
* Security fix for a format string bug in the tty code.
* Fixed format string bugs in all PO files.
* Removed Russian translation due to too many bugs. The FTP
server has an unofficial but better translation in the contrib
directory.
* Fixed expire time calculation and keyserver access.
* The usual set of minor bug fixes and enhancements.
* non-writable keyrings are now correctly handled.
See-also: gnupg-announce/2001q2/000123.html
Noteworthy changes in version 1.0.5 (2001-04-29)
------------------------------------------------
* WARNING: The semantics of --verify have changed to address a
problem with detached signature detection. --verify now ignores
signed material given on stdin unless this is requested by using
a "-" as the name for the file with the signed material. Please
check all your detached signature handling applications and make
sure that they don't pipe the signed material to stdin without
using a filename together with "-" on the the command line.
* WARNING: Corrected hash calculation for input data larger than
512M - it was just wrong, so you might notice bad signature in
some very big files. It may be wise to keep an old copy of
GnuPG around.
* Secret keys are no longer imported unless you use the new option
--allow-secret-key-import. This is a kludge and future versions will
handle it in another way.
* New command "showpref" in the --edit-key menu to show an easier
to understand preference listing.
* There is now the notation of a primary user ID. For example, it
is printed with a signature verification as the first user ID;
revoked user IDs are not printed there anymore. In general the
primary user ID is the one with the latest self-signature.
* New --charset=utf-8 to bypass all internal conversions.
* Large File Support (LFS) is now working.
* New options: --ignore-crc-error, --no-sig-create-check,
--no-sig-cache, --fixed-list-mode, --no-expensive-trust-checks,
--enable-special-filenames and --use-agent. See man page.
* New command --pipemode, which can be used to run gpg as a
co-process. Currently only the verification of detached
signatures are working. See doc/DETAILS.
* Keyserver support for the W32 version.
* Rewritten key selection code so that GnuPG can better cope with
multiple subkeys, expire dates and so. The drawback is that it
is slower.
* A whole lot of bug fixes.
* The verification status of self-signatures are now cached. To
increase the speed of key list operations for existing keys you
can do the following in your GnuPG homedir (~/.gnupg):
cp pubring.gpg pubring.gpg.save && gpg --export-all >x && \
rm pubring.gpg && gpg --import x
Only v4 keys (i.e not the old RSA keys) benefit from this caching.
* New translations: Estonian, Turkish.
See-also: gnupg-announce/2001q2/000122.html
Noteworthy changes in version 1.0.4 (2000-10-17)
------------------------------------------------
* Fixed a serious bug which could lead to false signature verification
results when more than one signature is fed to gpg. This is the
primary reason for releasing this version.
* New utility gpgv which is a stripped down version of gpg to
be used to verify signatures against a list of trusted keys.
* Rijndael (AES) is now supported and listed with top preference.
* --with-colons now works with --print-md[s].
See-also: gnupg-announce/2000q4/000082.html
Noteworthy changes in version 1.0.3 (2000-09-18)
------------------------------------------------
* Fixed problems with piping to/from other MS-Windows software
* Expiration time of the primary key can be changed again.
* Revoked user IDs are now marked in the output of --list-key
* New options --show-session-key and --override-session-key
to help the British folks to somewhat minimize the danger
of this Orwellian RIP bill.
* New options --merge-only and --try-all-secrets.
* New configuration option --with-egd-socket.
* The --trusted-key option is back after it left us with 0.9.5
* RSA is supported. Key generation does not yet work but will come
soon.
* CAST5 and SHA-1 are now the default algorithms to protect the key
and for symmetric-only encryption. This should solve a couple
of compatibility problems because the old algorithms are optional
according to RFC2440
* Twofish and MDC enhanced encryption is now used. PGP 7 supports
this. Older versions of GnuPG don't support it, so they should be
upgraded to at least 1.0.2
See-also: gnupg-announce/2000q3/000075.html
Noteworthy changes in version 1.0.2 (2000-07-12)
----------------------------------------------
* Fixed expiration handling of encryption keys.
* Add an experimental feature to do unattended key generation.
* The user is now asked for the reason of revocation as required
by the new OpenPGP draft.
* There is a ~/.gnupg/random_seed file now which saves the
state of the internal RNG and increases system performance
somewhat. This way the full entropy source is only used in
cases were it is really required.
Use the option --no-random-seed-file to disable this feature.
* New options --ignore-time-conflict and --lock-never.
* Some fixes for the W32 version.
* The entropy.dll is not anymore used by the W32 version but replaced
by code derived from Cryptlib.
* Encryption is now much faster: About 2 times for 1k bit keys
and 8 times for 4k keys.
* New encryption keys are generated in a way which allows a much
faster decryption.
* New command --export-secret-subkeys which outputs the
the _primary_ key with it's secret parts deleted. This is
useful for automated decryption/signature creation as it
allows to keep the real secret primary key offline and
thereby protecting the key certificates and allowing to
create revocations for the subkeys. See the FAQ for a
procedure to install such secret keys.
* Keygeneration now writes to the first writeable keyring or
as default to the one in the homedirectory. Prior versions
ignored all --keyring options.
* New option --command-fd to take user input from a file descriptor;
to be used with --status-fd by software which uses GnuPG as a backend.
* There is a new status PROGRESS which is used to show progress during
key generation.
* Support for the new MDC encryption packets. To create them either
--force-mdc must be use or cipher algorithm with a blocksize other
than 64 bits is to be used. --openpgp currently disables MDC packets
entirely. This option should not yet be used.
* New option --no-auto-key-retrieve to disable retrieving of
a missing public key from a keyserver, when a keyserver has been set.
* Danish translation
See-also: gnupg-announce/2000q3/000069.html
Noteworthy changes in version 1.0.1 (1999-12-16)
-----------------------------------
* New command --verify-files. New option --fast-list-mode.
* $http_proxy is now used when --honor-http-proxy is set.
* Fixed some minor bugs and the problem with conventional encrypted
packets which did use the gpg v3 partial length headers.
* Add Indonesian and Portugese translations.
* Fixed a bug with symmetric-only encryption using the non-default 3DES.
The option --emulate-3des-s2k-bug may be used to decrypt documents
which have been encrypted this way; this should be done immediately
as this workaround will be remove in 1.1
* Can now handle (but not display) PGP's photo IDs. I don't know the
format of that packet but after stripping a few bytes from the start
it looks like a JPEG (at least my test data). Handling of this
package is required because otherwise it would mix up the
self signatures and you can't import those keys.
* Passing non-ascii user IDs on the commandline should now work in all
cases.
* New keys are now generated with an additional preference to Blowfish.
* Removed the GNU Privacy Handbook from the distribution as it will go
into a separate one.
See-also: gnupg-announce/1999q4/000050.html
Noteworthy changes in version 1.0.0 (1999-09-07)
-----------------------------------
* Add a very preliminary version of the GNU Privacy Handbook to
the distribution (lynx doc/gph/index.html).
* Changed the version number to GnuPG 2001 ;-)
See-also: gnupg-announce/1999q3/000037.html
Noteworthy changes in version 0.9.11 (1999-09-03)
------------------------------------
* UTF-8 strings are now correctly printed (if --charset is set correctly).
Output of --with-colons remains C-style escaped UTF-8.
* Workaround for a problem with PGP 5 detached signature in textmode.
* Fixed a problem when importing new subkeys (duplicated signatures).
See-also: gnupg-announce/1999q3/000036.html
Noteworthy changes in version 0.9.10 (1999-07-23)
------------------------------------
* Some strange new options to help pgpgpg
* Cleaned up the dox a bit.
See-also: gnupg-announce/1999q3/000034.html
Noteworthy changes in version 0.9.9
-----------------------------------
* New options --[no-]utf8-strings.
* New edit-menu commands "enable" and "disable" for entire keys.
* You will be asked for a filename if gpg cannot deduce one.
* Changes to support libtool which is needed for the development
of libgcrypt.
* New script tools/lspgpot to help transferring assigned
trustvalues from PGP to GnuPG.
* New commands --lsign-key and made --sign-key a shortcut for --edit
and sign.
* New options (#122--126 ;-) --[no-]default-recipient[-self],
--disable-{cipher,pubkey}-algo. See the man page.
* Enhanced info output in case of multiple recipients and fixed exit code.
* New option --allow-non-selfsigned-uid to work around a problem with
the German IN way of separating signing and encryption keys.
See-also: gnupg-announce/1999q3/000028.html
Noteworthy changes in version 0.9.8 (1999-06-26)
-----------------------------------
* New subcommand "delsig" in the edit menu.
* The name of the output file is not anymore the one which is
embedded in the processed message, but the used filename with
the extension stripped. To revert to the old behaviour you can
use the option --use-embedded-filename.
* Another hack to cope with pgp2 generated detached signatures.
* latin-2 character set works (--charset=iso-8859-2).
* New option --with-key-data to list the public key parameters.
New option -N to insert notations and a --set-policy-url.
A couple of other options to allow reseting of options.
* Better support for HPUX.
See-also: gnupg-announce/1999q2/000016.html
Noteworthy changes in version 0.9.7 (1999-05-23)
-----------------------------------
* Add some work arounds for a bugs in pgp 2 which led to bad signatures
when used with canonical texts in some cases.
* Enhanced some status outputs.
See-also: gnupg-announce/1999q2/000000.html
Noteworthy changes in version 0.9.6 (1999-05-06)
-----------------------------------
* Twofish is now statically linked by default. The experimental 128 bit
version is now disabled. Full support will be available as soon as
the OpenPGP WG has decided on an interpretation of rfc2440.
* Dropped support for the ancient Blowfish160 which is not OpenPGP.
* Merged gpgm and gpg into one binary.
* Add "revsig" and "revkey" commands to the edit menu. It is now
possible to revoke signature and subkeys.
Noteworthy changes in version 0.9.5 (1999-03-20)
-----------------------------------
* New command "lsign" in the keyedit menu to create non-exportable
signatures. Removed --trusted-keys option.
* A bunch of changes to the key validation code.
* --list-trust-path now has an optional --with-colons format.
* New command --recv-keys to import keys from an keyserver.
Noteworthy changes in version 0.9.4 (1999-03-08)
-----------------------------------
* New configure option --enable-static-rnd=[egd|linux|unix|none]
to select a random gathering module for static linking.
* The original text is now verbatim copied to a cleartext signed message.
* Bugfixes but there are still a couple of bugs.
Noteworthy changes in version 0.9.3 (1999-02-19)
-----------------------------------
* Changed the internal design of getkey which now allows a
efficient lookup of multiple keys and add a word match mode.
* New options --[no-]encrypt-to.
* Some changes to the configure stuff. Switched to automake 1.4.
Removed intl/ from CVS, autogen.sh now uses gettextize.
* Preferences now include Twofish. Removed preference to Blowfish with
a special hack to suppress the "not listed in preferences" warning;
this is to allow us to switch completely to Twofish in the near future.
* Changed the locking stuff.
* Print all user ids of a good signature.
Noteworthy changes in version 0.9.2 (1999-01-01)
-----------------------------------
* add some additional time warp checks.
* Option --keyserver and command --send-keys to utilize HKP servers.
* Upgraded to zlib 1.1.3 and fixed an inflate bug
* More cleanup on the cleartext signatures.
Noteworthy changes in version 0.9.1 (1999-01-01)
-----------------------------------
* Polish language support.
* When querying the passphrase, the key ID of the primary key is
displayed along with the one of the used secondary key.
* Fixed a bug occurring when decrypting pgp 5 encrypted messages,
fixed an infinite loop bug in the 3DES code and in the code
which looks for trusted signatures.
* Fixed a bug in the mpi library which caused signatures not to
compare okay.
* Rewrote the handling of cleartext signatures; the code is now
better maintainable (I hope so).
* New status output VALIDSIG only for valid signatures together
with the fingerprint of the signer's key.
Noteworthy changes in version 0.9.0 (1998-12-23)
-----------------------------------
* --export does now only exports rfc2440 compatible keys; the
old behaviour is available with --export-all.
Generation of v3 ElGamal (sign and encrypt) keys is not longer
supported.
* Fixed the uncompress bug.
* Rewrote the rndunix module. There are two environment variables
used for debugging now: GNUPG_RNDUNIX_DBG give the file to write
debugging information (use "-" for stdout) and if GNUPG_RNDUNIX_DBGALL
is set, all programs which are only tried are also printed.
* New option --escape-from-lines to "dash-escape" "From " lines to
prevent mailers to change them to ">From ". This is not enabled by
default because it is not in compliance with rfc2440 - however, you
should turn it on.
Noteworthy changes in version 0.4.5 (1998-12-08)
-----------------------------------
* The keyrings and the trustdb is now locked, so that
other GnuPG processes won't damage these files. You
may want to put the option --lock-once into your options file.
* The latest self-signatures are now used; this enables --import
to see updated preferences etc.
* Import of subkeys should now work.
* Random gathering modules may now be loaded as extensions. Add
such a module for most Unices but it is very experimental!
* Brazilian language support.
Noteworthy changes in version 0.4.4 (1998-11-20)
-----------------------------------
* Fixed the way the key expiration time is stored. If you have
an expiration time on your key you should fix it with --edit-key
and the command "expire". I apologize for this inconvenience.
* Add option --charset to support "koi8-r" encoding of user ids.
(Not yet tested).
* Preferences should now work again. You should run
"gpgm --check-trustdb \*" to rebuild all preferences.
* Checking of certificates should now work but this needs a lot
of testing. Key validation values are now cached in the
trustdb; they should be recalculated as needed, but you may
use --check-trustdb or --update-trustdb to do this.
* Spanish translation by Urko Lusa.
* Patch files are from now on signed. See the man page
for the new option --not-dash-escaped.
* New syntax: --edit-key <userID> [<commands>]
If you run it without --batch the commands are executed and then
you are put into normal mode unless you use "quit" or "save" as
one of the commands. When in batch mode, the program quits after
the last command, so you have to use "save" if you did some changes.
It does not yet work completely, but may be used to list so the
keys etc.
Noteworthy changes in version 0.4.3 (1998-11-08)
-----------------------------------
* Fixed the gettext configure bug.
* Kludge for RSA keys: keyid and length of a RSA key are
correctly reported, but you get an error if you try to use
this key (If you do not have the non-US version).
* Experimental support for keyrings stored in a GDBM database.
This is *much* faster than a standard keyring. You will notice
that the import gets slower with time; the reason is that all
new keys are used to verify signatures of previous inserted
keys. Use "--keyring gnupg-gdbm:<name-of-gdbm-file>". This is
not (yet) supported for secret keys.
* A Russian language file in the distribution (alternatives are in
the contrib directory of the FTP servers)
* commandline option processing now works as expected for GNU programs
with the exception that you can't mix options and normal arguments.
* Now --list-key lists all matching keys. This is needed in some
other places too.
Noteworthy changes in version 0.4.2 (1998-10-18)
-----------------------------------
* This is only a snapshot: There are still a few bugs.
* Fixed this huge memory leak.
* Redesigned the trust database: You should run "gpgm --check-trustdb".
New command --update-trustdb, which adds new key from the public
keyring into your trustdb
* Fixed a bug in the armor code, leading to invalid packet errors.
(a workaround for this was to use --no-armor). The shorten line
length (64 instead of 72) fixes a problem with pgp5 and keyservers.
* comment packets are not anymore generated. "--export" filters
them out. One Exception: The comment packets in a secret keyring
are still used because they carry the factorization of the public
prime product.
* --import now only looks for KEYBLOCK headers, so you can now simply
remove the "- " in front of such a header if someone accidentally signed
such a message or the keyblock is part of a cleartext signed message.
* --with-colons now lists the key expiration time and not anymore
the valid period.
* Some keyblocks created with old releases have a wrong sequence
of packets, so that the keyservers don't accept these keys.
Simply using "--edit-key" fixes the problem.
* New option --force-v3-sigs to generate signed messages which are
compatible to PGP 5.
* Add some code to support DLD (for non ELF systems) - but this is
not tested because my BSD box is currently broken.
* New command "expire" in the edit-key menu.
Noteworthy changes in version 0.4.1 (1998-10-07)
-----------------------------------
* A secondary key is used when the primary key is specified but cannot
be used for the operation (if it is a sign-only key).
* GNUPG can now handle concatenated armored messages: There is still a
bug if different kinds of messages are mixed.
* Iterated+Salted passphrases now work. If want to be sure that PGP5
is able to handle them you may want to use the options
"--s2k-mode 3 --s2k-cipher-algo cast5 --s2k-digest-algo sha1"
when changing a passphrase.
* doc/OpenPGP talks about OpenPGP compliance, doc/HACKING gives
a few hints about the internal structure.
* Checked gnupg against the August 1998 draft (07) and I believe
it is in compliance with this document (except for one point).
* Fixed some bugs in the import merging code and rewrote some
code for the trustdb.
Noteworthy changes in version 0.4.0 (1998-09-18)
-----------------------------------
* Triple DES is now supported. Michael Roth did this piece of
needed work. We have now all the coded needed to be OpenPGP
compliant.
* Added a simple rpm spec file (see INSTALL).
* detached and armored signatures are now using "PGP SIGNATURE",
except when --rfc1991 is used.
* All times which are not in the yyyy-mm-dd format are now printed
in local time.
Noteworthy changes in version 0.3.5 (1998-09-14)
-----------------------------------
* New option --throw-keyid to create anonymous enciphered messages.
If gpg detects such a message it tires all available secret keys
in turn so decode it. This is a gnupg extension and not in OpenPGP
but it has been discussed there and afaik some products use this
scheme too (Suggested by Nimrod Zimmerman).
* Fixed a bug with 5 byte length headers.
* --delete-[secret-]key is now also available in gpgm.
* cleartext signatures are not anymore converted to LF only.
* Fixed a trustdb problem. Run "gpgm --check-trustdb" to fix old
trust dbs.
* Building in another directory should now work.
* Weak key detection mechanism (Niklas Hernaeus).
Noteworthy changes in version 0.3.4 (1998-08-11)
-----------------------------------
* New options --comment and --set-filename; see g10/OPTIONS
* yes/no, y/n localized.
* Fixed some bugs.
Noteworthy changes in version 0.3.3 (1998-08-08)
-----------------------------------
* IMPORTANT: I found yet another bug in the way the secret keys
are encrypted - I did it the way pgp 2.x did it, but OpenPGP
and pgp 5.x specify another (in some aspects simpler) method.
To convert your secret keys you have to do this:
1. Build the new release but don't install it and keep
a copy of the old program.
2. Disable the network, make sure that you are the only
user, be sure that there are no Trojan horses etc ....
3. Use your old gpg (version 0.3.1 or 0.3.2) and set the
passphrases of ALL your secret keys to empty!
(gpg --change-passphrase your-user-id).
4. Save your ownertrusts (see the next point)
5. rm ~/.gnupg/trustdb.gpg
6. install the new version of gpg (0.3.3)
7. For every secret key call "gpg --edit-key your-user-id",
enter "passwd" at the prompt, follow the instructions and
change your password back, enter "save" to store it.
8. Restore the ownertrust (see next point).
* The format of the trust database has changed; you must delete
the old one, so gnupg can create a new one.
IMPORTANT: Use version 0.3.1 or .2 to save your assigned ownertrusts
("gpgm --list-ownertrust >saved-trust"); then build this new version
and restore the ownertrust with this new version
("gpgm --import-ownertrust saved-trust"). Please note that
--list-ownertrust has been renamed to --export-ownertrust in this
release and it does now only export defined ownertrusts.
* The command --edit-key now provides a commandline driven menu
which can be used for various tasks. --sign-key is only an
an alias to --edit-key and maybe removed in future: use the
command "sign" of this new menu - you can select which user ids
you want to sign.
* Alternate user ids can now be created an signed.
* Owner trust values can now be changed with --edit-key (trust)
* GNUPG can now run as a coprocess; this enables sophisticated
frontends. tools/shmtest.c is a simple sample implementation.
This needs some more work: all tty_xxx() are to be replaced
by cpr_xxx() and some changes in the display logics is needed.
* Removed options --gen-prime and --gen-random.
* Removed option --add-key; use --edit-key instead.
* Removed option --change-passphrase; use --edit-key instead.
* Signatures are now checked even if the output file could not
be created. Command "--verify" tries to find the detached data.
* gpg now disables core dumps.
* compress and symmetric cipher preferences are now used.
Because there is no 3DES yet, this is replaced by Blowfish.
* We have added the Twofish as an experimental cipher algorithm.
Many thanks to Matthew Skala for doing this work.
Twofish is the AES submission from Schneier et al.; see
"www.counterpane.com/twofish.html" for more information.
* Started with a help system: If you enter a question mark at some
prompt; you should get a specific help for this prompt.
* There is no more backup copy of the secret keyring.
* A lot of new bugs. I think this release is not as stable as
the previous one.
Noteworthy changes in version 0.3.2 (1998-07-09)
-----------------------------------
* Fixed some bugs when using --textmode (-seat)
* Now displays the trust status of a positive verified message.
* Keyrings are now scanned in the sequence they are added with
--[secret-]keyring. Note that the default keyring is implicitly
added as the very first one unless --no-default-keyring is used.
* Fixed setuid and dlopen bug.
Noteworthy changes in version 0.3.1 (1998-07-06)
-----------------------------------
* Partial headers are now written in the OpenPGP format if
a key in a v4 packet is used.
* Removed some unused options, removed the gnupg.sig stuff.
* Key lookup by name now returns a key which can be used for
the desired action.
* New options --list-ownertrust (gpgm) to make a backup copy
of the ownertrust values you assigned.
* clear signature headers are now in compliance with OpenPGP.
Noteworthy changes in version 0.3.0 (1998-06-25)
-----------------------------------
* New option --emulate-checksum-bug. If your passphrase does not
work anymore, use this option and --change-passphrase to rewrite
your passphrase.
* More complete v4 key support: Preferences and expiration time
is set into the self signature.
* Key generation defaults to DSA/ElGamal keys, so that new keys are
interoperable with pgp5
* DSA key generation is faster and key generation does not anymore
remove entropy from the random generator (the primes are public
parameters, so there is really no need for a cryptographic secure
prime number generator which we had used).
* A complete new structure for representing the key parameters.
* Removed most public key knowledge into the cipher library.
* Support for dynamic loading of new algorithms.
* Moved tiger to an extension module.
Noteworthy changes in version 0.2.19 (1998-05-29)
------------------------------------
* Replaced /dev/urandom in checks with new tool mk-tdata.
* Some assembler file cleanups; some more functions for the Alpha.
* Tiger has now the OpenPGP assigned number 6. Because the OID has
changed, old signatures using this algorithm can't be verified.
* gnupg now encrypts the compressed packed and not any longer in the
reverse order; anyway it can decrypt both versions. Thanks to Tom
for telling me this (not security related) bug.
* --add-key works and you are now able to generate subkeys.
* It is now possible to generate ElGamal keys in v4 packets to create
valid OpenPGP keys.
* Some new features for better integration into MUAs.
Noteworthy changes in version 0.2.18 (1998-05-15)
------------------------------------
* Splitted cipher/random.c, add new option "--disable-dev-random"
to configure to support the development of a random source for
other systems. Prepared sourcefiles rand-unix.c, rand-w32.c
and rand-dummy.c (which is used to allow compilation on systems
without a random source).
* Fixed a small bug in the key generation (it was possible that 48 bits
of a key were not taken from the random pool)
* Add key generation for DSA and v4 signatures.
* Add a function trap_unaligned(), so that a SIGBUS is issued on
Alphas and not the slow emulation code is used. And success: rmd160
raised a SIGBUS.
* Enhanced the formatting facility of argparse and changed the use of
\r,\v to @ because gettext does not like it.
* New option "--compress-algo 1" to allow the creation of compressed
messages which are readable by PGP and "--print-md" (gpgm) to make
speed measurement easier.
Noteworthy changes in version 0.2.17 (1998-05-04)
------------------------------------
* Comment packets are now of private type 61.
* Passphrase code still used a 160 bit blowfish key, added a
silly workaround. Please change your passphrase again - sorry.
* Conventional encryption now uses a type 3 packet to describe the
used algorithms.
* The new algorithm number for Blowfish is 20, 16 is still used for
encryption only; for signing it is only used when it is in a v3 packet,
so that GNUPG keys are still valid.
Noteworthy changes in version 0.2.16 (1998-04-28)
------------------------------------
* Add experimental support for the TIGER/192 message digest algorithm.
(But there is only a dummy ASN OID).
* Standard cipher is now Blowfish with 128 bit key in OpenPGP's CFB
mode. I renamed the old cipher to Blowfish160. Because the OpenPGP
group refused to assign me a number for Blowfish160, I have to
drop support for this in the future. You should use
"--change-passphrase" to recode your current passphrase with 128
bit Blowfish.
Noteworthy changes in version 0.2.15 (1998-04-09)
------------------------------------
* Fixed a bug with the old checksum calculation for secret keys.
If you run the program without --batch, a warning does inform
you if your secret key needs to be converted; simply use
--change-passphrase to recalculate the checksum. Please do this
soon, as the compatible mode will be removed sometime in the future.
* CAST5 works (using the PGP's special CFB mode).
* Again somewhat more PGP 5 compatible.
* Some new test cases
Noteworthy changes in version 0.2.14 (1998-04-02)
------------------------------------
* Changed the internal handling of keyrings.
* Add support to list PGP 5 keyrings with subkeys
* Timestamps of signatures are now verified.
* A expiration time can now be specified during key generation.
* Some speedups for Blowfish and SHA-1, rewrote SHA-1 transform.
Reduced the amount of random bytes needed for key generation in
some cases.
Noteworthy changes in version 0.2.13 (1998-03-10)
------------------------------------
* Verify of DSA signatures works.
* Re-implemented the slower random number generator.
Noteworthy changes in version 0.2.12 (1998-03-07)
------------------------------------
* --delete-key checks that there is no secret key. The new
option --delete-secret-key maybe used to delete a secret key.
* "-kv" now works as expected. Options "--list-{keys,sigs]"
and "--check-sigs" are now working.
* New options "--verify" and "--decrypt" to better support integration
into MUAs (partly done for Mutt).
* New option "--with-colons" to make parsing of key lists easier.
Noteworthy changes in version 0.2.11 (1998-03-02)
------------------------------------
* GPG now asks for a recipient's name if option "-r" is not used.
* If there is no good trust path, the program asks whether to use
the public keys anyway.
* "--delete-key" works for public keys. What semantics shall I use
when there is a secret key too? Delete the secret key or leave him
and auto-regenerate the public key, next time the secret key is used?
Noteworthy changes in version 0.2.10 (1998-02-27)
------------------------------------
* Code for the alpha is much faster (about 20 times); the data
was misaligned and the kernel traps this, so nearly all time
was used by system to trap the misalignments and to write
syslog messages. Shame on me and thanks to Ralph for
pointing me at this while drinking some beer yesterday.
* Changed some configure options and add an option
--disable-m-guard to remove the memory checking code
and to compile everything with optimization on.
* New environment variable GNUPGHOME, which can be used to set
another homedir than ~/.gnupg. Changed default homedir for
Windoze version to c:/gnupg.
* Fixed detached signatures; detached PGP signatures caused a SEGV.
* The Windoze version works (as usual w/o a strong RNG).
Noteworthy changes in version 0.2.9 (1998-02-26)
-----------------------------------
* Fixed FreeBSD bug.
* Added a simple man page.
* Switched to automake1.2f and a newer gettext.
Noteworthy changes in version 0.2.8 (1998-02-24)
-----------------------------------
* Changed the name to GNUPG, the binaries are called gpg and gpgm.
You must rename rename the directory "~/.g10" to ~/.gnupg/, rename
{pub,sec}ring.g10 to {pub,sec}ring.gpg, trustdb.g10 to trustdb.gpg
and g10.sig to gnupg.sig.
* New or changed passphrases are now salted.
Noteworthy changes in version 0.2.7 (1998-02-18)
-----------------------------------
* New command "gen-revoke" to create a key revocation certificate.
* New option "homedir" to set the homedir (which defaults to "~/.g10").
This directory is created if it does not exists (only the last
part of the name and not the complete hierarchy)
* Command "import" works. (Try: "finger gcrypt@ftp.guug.de|g10 --import")
* New commands "dearmor/enarmor" for g10maint. These are mainly
used for internal test purposes.
* Option --version now conforming to the GNU standards and lists
the available ciphers, message digests and public key algorithms.
* Assembler code for m68k (not tested).
* "make check" works.
Noteworthy changes in version 0.2.6 (1998-02-13)
-----------------------------------
* Option "--export" works.
Noteworthy changes in version 0.2.5 (1998-02-12)
-----------------------------------
* Added zlib for systems which don't have it.
Use "./configure --with-zlib" to link with the static version.
* Generalized some more functions and rewrote the encoding of
message digests into MPIs.
* Enhanced the checkit script
Noteworthy changes in version 0.2.4 (1998-02-11)
-----------------------------------
* nearly doubled the speed of the ElGamal signature verification.
* backup copies of keyrings are created.
* assembler stuff for Pentium; gives about 15% better performance.
* fixed a lot of bugs.
Noteworthy changes in version 0.2.3 (1998-02-09)
-----------------------------------
* Found a bug in the calculation of ELG fingerprints. This is now
fixed, but all existing fingerprints and keyids for ELG keys
are not any more valid.
* armor should now work; including clear signed text.
* moved some options to the new program g10maint
* It's now 64 bit clean and runs fine on an alpha--linux.
* Key generation is much faster now. I fixed this by using not
so strong random number for the primes (this was a bug because the
ElGamal primes are public parameters and it does not make sense
to generate them from strong random). The real secret is the x value
which is still generated from strong (okay: /dev/random) random bits.
* added option "--status-fd": see g10/OPTIONS
* We have secure memory on systems which support mlock().
It is not complete yet, because we do not have signal handler
which does a cleanup in very case.
We should also check the ulimit for the user in the case
that the admin does not have set a limit on locked pages.
* started with internationalization support.
* The logic to handle the web of trust is now implemented. It is
has some bugs; but I'm going to change the algorithm anyway.
It works by calculating the trustlevel on the fly. It may ask
you to provide trust parameters if the calculated trust probability
is too low. I will write a paper which discusses this new approach.
* a couple of changes to the configure script.
* New option "--quick-random" which uses a much quicker random
number generator. Keys generated while this option is in effect
are flags with "INSECURE!" in the user-id. This is a development
only option.
* Read support for new version packets (OpenPGP).
* Comment packets are now of correct OpenPGP type 16. Old comment
packets written by G10 are detected because they always start with
a hash which is an invalid version byte.
* The string "(INSECURE!)" is appended to a new user-id if this
is generated on a system without a good random number generator.
Version 0.2.2 (1998-02-09)
Version 0.2.1 (1998-01-28)
Version 0.2.0 (1998-01-25)
Version 0.1.3 (1998-01-12)
Version 0.1.2 (1998-01-07)
Version 0.1.1 (1998-01-07)
Version 0.1.0 (1998-01-05)
Version 0.0.0 (1997-12-20)
Copyright (C) 1998-2017 Free Software Foundation, Inc.
Copyright (C) 1997-2017 Werner Koch
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
modifications, as long as this notice is preserved.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/common/ksba-io-support.c b/common/ksba-io-support.c
index 2832a4f3d..a23608164 100644
--- a/common/ksba-io-support.c
+++ b/common/ksba-io-support.c
@@ -1,762 +1,848 @@
/* kska-io-support.c - Supporting functions for ksba reader and writer
* Copyright (C) 2001-2005, 2007, 2010-2011, 2017 Werner Koch
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include <ksba.h>
#include "util.h"
#include "i18n.h"
#include "ksba-io-support.h"
#ifdef HAVE_DOSISH_SYSTEM
#define LF "\r\n"
#else
#define LF "\n"
#endif
/* Data used by the reader callbacks. */
struct reader_cb_parm_s
{
estream_t fp;
unsigned char line[1024];
int linelen;
int readpos;
int have_lf;
unsigned long line_counter;
int allow_multi_pem; /* Allow processing of multiple PEM objects. */
int autodetect; /* Try to detect the input encoding. */
int assume_pem; /* Assume input encoding is PEM. */
int assume_base64; /* Assume input is base64 encoded. */
int identified;
int is_pem;
int is_base64;
int stop_seen;
int might_be_smime;
int eof_seen;
struct {
int idx;
unsigned char val;
int stop_seen;
} base64;
};
/* Data used by the writer callbacks. */
struct writer_cb_parm_s
{
estream_t stream; /* Output stream. */
char *pem_name; /* Malloced. */
+ struct {
+ gnupg_ksba_progress_cb_t cb;
+ ctrl_t ctrl;
+ u32 last_time; /* last time reported */
+ uint64_t last; /* last amount reported */
+ uint64_t current; /* current amount */
+ uint64_t total; /* total amount */
+ } progress;
+
int wrote_begin;
int did_finish;
struct {
int idx;
int quad_count;
unsigned char radbuf[4];
} base64;
};
/* Context for this module's functions. */
struct gnupg_ksba_io_s {
+ int is_writer; /* True if this context refers a writer object. */
union {
struct reader_cb_parm_s rparm;
struct writer_cb_parm_s wparm;
} u;
union {
ksba_reader_t reader;
ksba_writer_t writer;
} u2;
};
/* The base-64 character list */
static char bintoasc[64] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* The reverse base-64 list */
static unsigned char asctobin[256] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff
};
static int
has_only_base64 (const unsigned char *line, int linelen)
{
if (linelen < 20)
return 0;
for (; linelen; line++, linelen--)
{
if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n'))
break;
if ( !strchr (bintoasc, *line) )
return 0;
}
return 1; /* yes */
}
static int
is_empty_line (const unsigned char *line, int linelen)
{
if (linelen >= 2 && *line == '\r' && line[1] == '\n')
return 1;
if (linelen >= 1 && *line == '\n')
return 1;
return 0;
}
static int
base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
{
struct reader_cb_parm_s *parm = cb_value;
size_t n;
int c, c2;
*nread = 0;
if (!buffer)
return -1; /* not supported */
next:
if (!parm->linelen)
{
/* read an entire line or up to the size of the buffer */
parm->line_counter++;
parm->have_lf = 0;
for (n=0; n < DIM(parm->line);)
{
c = es_getc (parm->fp);
if (c == EOF)
{
parm->eof_seen = 1;
if (es_ferror (parm->fp))
return -1;
break;
}
parm->line[n++] = c;
if (c == '\n')
{
parm->have_lf = 1;
/* Fixme: we need to skip overlong lines while detecting
the dashed lines */
break;
}
}
parm->linelen = n;
if (!n)
return -1; /* eof */
parm->readpos = 0;
}
if (!parm->identified)
{
if (!parm->autodetect)
{
if (parm->assume_pem)
{
/* wait for the header line */
parm->linelen = parm->readpos = 0;
if (!parm->have_lf
|| strncmp ((char*)parm->line, "-----BEGIN ", 11)
|| !strncmp ((char*)parm->line+11, "PGP ", 4))
goto next;
parm->is_pem = 1;
}
else if (parm->assume_base64)
parm->is_base64 = 1;
}
else if (parm->line_counter == 1 && !parm->have_lf)
{
/* first line too long - assume DER encoding */
parm->is_pem = 0;
}
else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
{
/* the very first byte does pretty much look like a SEQUENCE tag*/
parm->is_pem = 0;
}
else if ( parm->have_lf
&& !strncmp ((char*)parm->line, "-----BEGIN ", 11)
&& strncmp ((char *)parm->line+11, "PGP ", 4) )
{
/* Fixme: we must only compare if the line really starts at
the beginning */
parm->is_pem = 1;
parm->linelen = parm->readpos = 0;
}
else if ( parm->have_lf && parm->line_counter == 1
&& parm->linelen >= 13
&& !ascii_memcasecmp (parm->line, "Content-Type:", 13))
{ /* might be a S/MIME body */
parm->might_be_smime = 1;
parm->linelen = parm->readpos = 0;
goto next;
}
else if (parm->might_be_smime == 1
&& is_empty_line (parm->line, parm->linelen))
{
parm->might_be_smime = 2;
parm->linelen = parm->readpos = 0;
goto next;
}
else if (parm->might_be_smime == 2)
{
parm->might_be_smime = 0;
if ( !has_only_base64 (parm->line, parm->linelen))
{
parm->linelen = parm->readpos = 0;
goto next;
}
parm->is_pem = 1;
}
else
{
parm->linelen = parm->readpos = 0;
goto next;
}
parm->identified = 1;
parm->base64.stop_seen = 0;
parm->base64.idx = 0;
}
n = 0;
if (parm->is_pem || parm->is_base64)
{
if (parm->is_pem && parm->have_lf
&& !strncmp ((char*)parm->line, "-----END ", 9))
{
parm->identified = 0;
parm->linelen = parm->readpos = 0;
/* If the caller want to read multiple PEM objects from one
file, we have to reset our internal state and return a
EOF immediately. The caller is the expected to use
ksba_reader_clear to clear the EOF condition and continue
to read. If we don't want to do that we just return 0
bytes which will force the ksba_reader to skip until
EOF. */
if (parm->allow_multi_pem)
{
parm->identified = 0;
parm->autodetect = 0;
parm->assume_pem = 1;
parm->stop_seen = 0;
return -1; /* Send EOF now. */
}
}
else if (parm->stop_seen)
{ /* skip the rest of the line */
parm->linelen = parm->readpos = 0;
}
else
{
int idx = parm->base64.idx;
unsigned char val = parm->base64.val;
while (n < count && parm->readpos < parm->linelen )
{
c = parm->line[parm->readpos++];
if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
continue;
if ((c = asctobin[(c2=c)]) == 255)
{
if (c2 == '=')
{ /* pad character: stop */
if (idx == 1)
buffer[n++] = val;
parm->stop_seen = 1;
break;
}
else if (c2 == '-'
&& parm->readpos == 1
&& parm->readpos-1+9 < parm->linelen
&& !strncmp ((char*)parm->line + parm->readpos-1,
"-----END ", 9))
{ /* END line seen (padding was not needed). */
parm->stop_seen = 1;
break;
}
log_error (_("invalid radix64 character %02x skipped\n"),
c2);
continue;
}
switch (idx)
{
case 0:
val = c << 2;
break;
case 1:
val |= (c>>4)&3;
buffer[n++] = val;
val = (c<<4)&0xf0;
break;
case 2:
val |= (c>>2)&15;
buffer[n++] = val;
val = (c<<6)&0xc0;
break;
case 3:
val |= c&0x3f;
buffer[n++] = val;
break;
}
idx = (idx+1) % 4;
}
if (parm->readpos == parm->linelen)
parm->linelen = parm->readpos = 0;
parm->base64.idx = idx;
parm->base64.val = val;
}
}
else
{ /* DER encoded */
while (n < count && parm->readpos < parm->linelen)
buffer[n++] = parm->line[parm->readpos++];
if (parm->readpos == parm->linelen)
parm->linelen = parm->readpos = 0;
}
*nread = n;
return 0;
}
static int
simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
{
struct reader_cb_parm_s *parm = cb_value;
size_t n;
int c = 0;
*nread = 0;
if (!buffer)
return -1; /* not supported */
for (n=0; n < count; n++)
{
c = es_getc (parm->fp);
if (c == EOF)
{
parm->eof_seen = 1;
if (es_ferror (parm->fp))
return -1;
if (n)
break; /* Return what we have before an EOF. */
return -1;
}
*(byte *)buffer++ = c;
}
*nread = n;
return 0;
}
+/* Call the progress callback if its time. We do this very 2 seconds
+ * or if FORCE is set. However, we also require that at least 64KiB
+ * have been written to avoid unnecessary progress lines for small
+ * files. */
+static gpg_error_t
+update_write_progress (struct writer_cb_parm_s *parm, size_t count, int force)
+{
+ gpg_error_t err = 0;
+ u32 timestamp;
+
+ parm->progress.current += count;
+ if (parm->progress.current >= (64*1024))
+ {
+ timestamp = make_timestamp ();
+ if (force || (timestamp - parm->progress.last_time > 1))
+ {
+ parm->progress.last = parm->progress.current;
+ parm->progress.last_time = timestamp;
+ err = parm->progress.cb (parm->progress.ctrl,
+ parm->progress.current,
+ parm->progress.total);
+ }
+ }
+ return err;
+}
+
+
static int
base64_writer_cb (void *cb_value, const void *buffer, size_t count)
{
struct writer_cb_parm_s *parm = cb_value;
unsigned char radbuf[4];
int i, c, idx, quad_count;
const unsigned char *p;
estream_t stream = parm->stream;
+ int rc;
+ size_t nleft;
if (!count)
return 0;
if (!parm->wrote_begin)
{
if (parm->pem_name)
{
es_fputs ("-----BEGIN ", stream);
es_fputs (parm->pem_name, stream);
es_fputs ("-----\n", stream);
}
parm->wrote_begin = 1;
parm->base64.idx = 0;
parm->base64.quad_count = 0;
}
idx = parm->base64.idx;
quad_count = parm->base64.quad_count;
for (i=0; i < idx; i++)
radbuf[i] = parm->base64.radbuf[i];
- for (p=buffer; count; p++, count--)
+ for (p=buffer, nleft = count; nleft; p++, nleft--)
{
radbuf[idx++] = *p;
if (idx > 2)
{
idx = 0;
c = bintoasc[(*radbuf >> 2) & 077];
es_putc (c, stream);
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
es_putc (c, stream);
c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
es_putc (c, stream);
c = bintoasc[radbuf[2]&077];
es_putc (c, stream);
if (++quad_count >= (64/4))
{
es_fputs (LF, stream);
quad_count = 0;
}
}
}
for (i=0; i < idx; i++)
parm->base64.radbuf[i] = radbuf[i];
parm->base64.idx = idx;
parm->base64.quad_count = quad_count;
- return es_ferror (stream)? gpg_error_from_syserror () : 0;
+ rc = es_ferror (stream)? gpg_error_from_syserror () : 0;
+ /* Note that we use the unencoded count for the progress. */
+ if (!rc && parm->progress.cb)
+ rc = update_write_progress (parm, count, 0);
+ return rc;
}
/* This callback is only used in stream mode. However, we don't
restrict it to this. */
static int
plain_writer_cb (void *cb_value, const void *buffer, size_t count)
{
struct writer_cb_parm_s *parm = cb_value;
estream_t stream = parm->stream;
+ int rc;
if (!count)
return 0;
es_write (stream, buffer, count, NULL);
-
- return es_ferror (stream)? gpg_error_from_syserror () : 0;
+ rc = es_ferror (stream)? gpg_error_from_syserror () : 0;
+ if (!rc && parm->progress.cb)
+ rc = update_write_progress (parm, count, 0);
+ return rc;
}
static int
base64_finish_write (struct writer_cb_parm_s *parm)
{
unsigned char *radbuf;
int c, idx, quad_count;
estream_t stream = parm->stream;
+ int rc;
if (!parm->wrote_begin)
return 0; /* Nothing written or we are not called in base-64 mode. */
/* flush the base64 encoding */
idx = parm->base64.idx;
quad_count = parm->base64.quad_count;
if (idx)
{
radbuf = parm->base64.radbuf;
c = bintoasc[(*radbuf>>2)&077];
es_putc (c, stream);
if (idx == 1)
{
c = bintoasc[((*radbuf << 4) & 060) & 077];
es_putc (c, stream);
es_putc ('=', stream);
es_putc ('=', stream);
}
else
{
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
es_putc (c, stream);
c = bintoasc[((radbuf[1] << 2) & 074) & 077];
es_putc (c, stream);
es_putc ('=', stream);
}
if (++quad_count >= (64/4))
{
es_fputs (LF, stream);
quad_count = 0;
}
}
if (quad_count)
es_fputs (LF, stream);
if (parm->pem_name)
{
es_fputs ("-----END ", stream);
es_fputs (parm->pem_name, stream);
es_fputs ("-----\n", stream);
}
- return es_ferror (stream)? gpg_error_from_syserror () : 0;
+ rc = es_ferror (stream)? gpg_error_from_syserror () : 0;
+ if (!rc && parm->progress.cb)
+ rc = update_write_progress (parm, 0, 1);
+ return rc;
}
/* Create a reader for the stream FP. FLAGS can be used to specify
* the expected input encoding.
*
* The function returns a gnupg_ksba_io_t object which must be passed to
* the gpgme_destroy_reader function. The created ksba_reader_t
* object is stored at R_READER - the caller must not call the
* ksba_reader_release function on.
*
* The supported flags are:
*
* GNUPG_KSBA_IO_PEM - Assume the input is PEM encoded
* GNUPG_KSBA_IO_BASE64 - Assume the input is Base64 encoded.
* GNUPG_KSBA_IO_AUTODETECT - The reader tries to detect the encoding.
* GNUPG_KSBA_IO_MULTIPEM - The reader expects that the caller uses
* ksba_reader_clear after EOF until no more
* objects were found.
*
* Note that the PEM flag has a higher priority than the BASE64 flag
* which in turn has a gight priority than the AUTODETECT flag.
*/
gpg_error_t
gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx,
unsigned int flags, estream_t fp,
ksba_reader_t *r_reader)
{
int rc;
ksba_reader_t r;
*r_reader = NULL;
*ctx = xtrycalloc (1, sizeof **ctx);
if (!*ctx)
return out_of_core ();
(*ctx)->u.rparm.allow_multi_pem = !!(flags & GNUPG_KSBA_IO_MULTIPEM);
rc = ksba_reader_new (&r);
if (rc)
{
xfree (*ctx); *ctx = NULL;
return rc;
}
(*ctx)->u.rparm.fp = fp;
if ((flags & GNUPG_KSBA_IO_PEM))
{
(*ctx)->u.rparm.assume_pem = 1;
(*ctx)->u.rparm.assume_base64 = 1;
rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
}
else if ((flags & GNUPG_KSBA_IO_BASE64))
{
(*ctx)->u.rparm.assume_base64 = 1;
rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
}
else if ((flags & GNUPG_KSBA_IO_AUTODETECT))
{
(*ctx)->u.rparm.autodetect = 1;
rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm);
}
else
rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->u.rparm);
if (rc)
{
ksba_reader_release (r);
xfree (*ctx); *ctx = NULL;
return rc;
}
(*ctx)->u2.reader = r;
*r_reader = r;
return 0;
}
/* Return True if an EOF as been seen. */
int
gnupg_ksba_reader_eof_seen (gnupg_ksba_io_t ctx)
{
return ctx && ctx->u.rparm.eof_seen;
}
/* Destroy a reader object. */
void
gnupg_ksba_destroy_reader (gnupg_ksba_io_t ctx)
{
if (!ctx)
return;
ksba_reader_release (ctx->u2.reader);
xfree (ctx);
}
/* Create a writer for the given STREAM. Depending on FLAGS an output
* encoding is chosen. In PEM mode PEM_NAME is used for the header
* and footer lines; if PEM_NAME is NULL the string "CMS OBJECT" is
* used.
*
* The function returns a gnupg_ksba_io_t object which must be passed to
* the gpgme_destroy_writer function. The created ksba_writer_t
* object is stored at R_WRITER - the caller must not call the
* ksba_reader_release function on it.
*
* The supported flags are:
*
* GNUPG_KSBA_IO_PEM - Write output as PEM
* GNUPG_KSBA_IO_BASE64 - Write output as plain Base64; note that the PEM
* flag overrides this flag.
*
*/
gpg_error_t
gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, unsigned int flags,
const char *pem_name, estream_t stream,
ksba_writer_t *r_writer)
{
int rc;
ksba_writer_t w;
*r_writer = NULL;
*ctx = xtrycalloc (1, sizeof **ctx);
if (!*ctx)
return gpg_error_from_syserror ();
+ (*ctx)->is_writer = 1;
rc = ksba_writer_new (&w);
if (rc)
{
xfree (*ctx); *ctx = NULL;
return rc;
}
if ((flags & GNUPG_KSBA_IO_PEM) || (flags & GNUPG_KSBA_IO_BASE64))
{
(*ctx)->u.wparm.stream = stream;
if ((flags & GNUPG_KSBA_IO_PEM))
{
(*ctx)->u.wparm.pem_name = xtrystrdup (pem_name
? pem_name
: "CMS OBJECT");
if (!(*ctx)->u.wparm.pem_name)
{
rc = gpg_error_from_syserror ();
ksba_writer_release (w);
xfree (*ctx); *ctx = NULL;
return rc;
}
}
rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm);
}
else if (stream)
{
(*ctx)->u.wparm.stream = stream;
rc = ksba_writer_set_cb (w, plain_writer_cb, &(*ctx)->u.wparm);
}
else
rc = gpg_error (GPG_ERR_INV_ARG);
if (rc)
{
ksba_writer_release (w);
xfree (*ctx); *ctx = NULL;
return rc;
}
(*ctx)->u2.writer = w;
*r_writer = w;
return 0;
}
/* Flush a writer. This is for example required to write the padding
* or the PEM footer. */
gpg_error_t
gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx)
{
struct writer_cb_parm_s *parm;
if (!ctx)
return gpg_error (GPG_ERR_INV_VALUE);
parm = &ctx->u.wparm;
if (parm->did_finish)
return 0; /* Already done. */
parm->did_finish = 1;
if (!parm->stream)
return 0; /* Callback was not used. */
return base64_finish_write (parm);
}
/* Destroy a writer object. */
void
gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx)
{
if (!ctx)
return;
ksba_writer_release (ctx->u2.writer);
xfree (ctx->u.wparm.pem_name);
xfree (ctx);
}
+
+
+/* Set a callback to the writer object. CTRL will be bassed to the
+ * callback. */
+void
+gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx,
+ gnupg_ksba_progress_cb_t cb, ctrl_t ctrl)
+{
+ struct writer_cb_parm_s *parm;
+
+ if (!ctx || !ctx->is_writer)
+ return; /* Currently only supported for writer objects. */
+ parm = &ctx->u.wparm;
+
+ parm->progress.cb = cb;
+ parm->progress.ctrl = ctrl;
+ parm->progress.last_time = 0;
+ parm->progress.last = 0;
+ parm->progress.current = 0;
+ parm->progress.total = 0;
+}
+
+
+/* Update the total count for the progress thingy. */
+void
+gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total)
+{
+ struct writer_cb_parm_s *parm;
+
+ if (!ctx || !ctx->is_writer)
+ return; /* Currently only supported for writer objects. */
+ parm = &ctx->u.wparm;
+ parm->progress.total = total;
+}
diff --git a/common/ksba-io-support.h b/common/ksba-io-support.h
index e33e0ed74..f309e6647 100644
--- a/common/ksba-io-support.h
+++ b/common/ksba-io-support.h
@@ -1,66 +1,74 @@
/* ksba-io-support.h - Supporting functions for ksba reader and writer
* Copyright (C) 2017 Werner Koch
*
* This file is part of GnuPG.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* This file 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
*/
#ifndef GNUPG_KSBA_IO_SUPPORT_H
#define GNUPG_KSBA_IO_SUPPORT_H
/* Flags used with gnupg_ksba_create_reader and
* gnupg_ksba_create_writer. */
#define GNUPG_KSBA_IO_PEM 1 /* X.509 PEM format. */
#define GNUPG_KSBA_IO_BASE64 2 /* Plain Base64 format. */
#define GNUPG_KSBA_IO_AUTODETECT 4 /* Try to autodetect the format. */
#define GNUPG_KSBA_IO_MULTIPEM 8 /* Allow more than one PEM chunk. */
/* Context object. */
typedef struct gnupg_ksba_io_s *gnupg_ksba_io_t;
+/* Progress callback type. */
+typedef gpg_error_t (*gnupg_ksba_progress_cb_t)(ctrl_t ctrl,
+ uint64_t current,
+ uint64_t total);
gpg_error_t gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx,
unsigned int flags,
estream_t fp,
ksba_reader_t *r_reader);
int gnupg_ksba_reader_eof_seen (gnupg_ksba_io_t ctx);
void gnupg_ksba_destroy_reader (gnupg_ksba_io_t ctx);
gpg_error_t gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx,
unsigned int flags,
const char *pem_name,
estream_t stream,
ksba_writer_t *r_writer);
-
gpg_error_t gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx);
void gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx);
+void gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx,
+ gnupg_ksba_progress_cb_t cb, ctrl_t ctrl);
+void gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total);
+
#endif /*GNUPG_KSBA_IO_SUPPORT_H*/
diff --git a/sm/decrypt.c b/sm/decrypt.c
index 01260f599..9f8175c75 100644
--- a/sm/decrypt.c
+++ b/sm/decrypt.c
@@ -1,1528 +1,1530 @@
/* decrypt.c - Decrypt a message
* Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc.
* Copyright (C) 2001-2019 Werner Koch
* Copyright (C) 2015-2020 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 <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include "gpgsm.h"
#include <gcrypt.h>
#include <ksba.h>
#include "keydb.h"
#include "../common/i18n.h"
#include "../common/compliance.h"
#include "../common/tlv.h"
/* We can provide an enum value which is only availabale with KSBA
* 1.6.0 so that we can compile even against older versions. Some
* calls will of course return an error in this case. This value is
* currently not used because the cipher mode is sufficient here. */
/* #if KSBA_VERSION_NUMBER < 0x010600 /\* 1.6.0 *\/ */
/* # define KSBA_CT_AUTHENVELOPED_DATA 10 */
/* #endif */
struct decrypt_filter_parm_s
{
int algo;
int mode;
int blklen;
gcry_cipher_hd_t hd;
char iv[16];
size_t ivlen;
int any_data; /* did we push anything through the filter at all? */
unsigned char lastblock[16]; /* to strip the padding we have to
keep this one */
char helpblock[16]; /* needed because there is no block buffering in
libgcrypt (yet) */
int helpblocklen;
int is_de_vs; /* Helper to track CO_DE_VS state. */
};
/* Return the hash algorithm's algo id from its name given in the
* non-null termnated string in (buffer,buflen). Returns 0 on failure
* or if the algo is not known. */
static char *
string_from_gcry_buffer (gcry_buffer_t *buffer)
{
char *string;
string = xtrymalloc (buffer->len + 1);
if (!string)
return NULL;
memcpy (string, buffer->data, buffer->len);
string[buffer->len] = 0;
return string;
}
/* Helper to construct and hash the
* ECC-CMS-SharedInfo ::= SEQUENCE {
* keyInfo AlgorithmIdentifier,
* entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
* suppPubInfo [2] EXPLICIT OCTET STRING }
* as described in RFC-5753, 7.2. */
gpg_error_t
hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str,
unsigned int keylen,
const void *ukm, unsigned int ukmlen)
{
gpg_error_t err;
void *p;
unsigned char *oid;
size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo;
unsigned char keylenbuf[6];
membuf_t mb = MEMBUF_ZERO;
err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen);
if (err)
return err;
toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen);
tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0;
keylen *= 8;
keylenbuf[0] = TAG_OCTET_STRING;
keylenbuf[1] = 4;
keylenbuf[2] = (keylen >> 24);
keylenbuf[3] = (keylen >> 16);
keylenbuf[4] = (keylen >> 8);
keylenbuf[5] = keylen;
tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
tkeyinfo + tukmlen + tsupppubinfo);
put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
toidlen);
put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
put_membuf (&mb, oid, oidlen);
ksba_free (oid);
if (ukm)
{
put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen);
put_membuf (&mb, ukm, ukmlen);
}
put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
put_membuf (&mb, keylenbuf, sizeof keylenbuf);
p = get_membuf (&mb, &n);
if (!p)
return gpg_error_from_syserror ();
gcry_md_write (hash_hd, p, n);
xfree (p);
return 0;
}
/* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an
* optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal
* dotted form, and the hash algorithm HASH_ALGO. On success a key of
* length KEYLEN is stored at KEY. */
gpg_error_t
ecdh_derive_kek (unsigned char *key, unsigned int keylen,
int hash_algo, const char *wrap_algo_str,
const void *secret, unsigned int secretlen,
const void *ukm, unsigned int ukmlen)
{
gpg_error_t err = 0;
unsigned int hashlen;
gcry_md_hd_t hash_hd;
unsigned char counter;
unsigned int n, ncopy;
hashlen = gcry_md_get_algo_dlen (hash_algo);
if (!hashlen)
return gpg_error (GPG_ERR_INV_ARG);
err = gcry_md_open (&hash_hd, hash_algo, 0);
if (err)
return err;
/* According to SEC1 3.6.1 we should check that
* SECRETLEN + UKMLEN + 4 < maxhashlen
* However, we have no practical limit on the hash length and thus
* there is no point in checking this. The second check that
* KEYLEN < hashlen*(2^32-1)
* is obviously also not needed.
*/
for (n=0, counter=1; n < keylen; counter++)
{
if (counter > 1)
gcry_md_reset (hash_hd);
gcry_md_write (hash_hd, secret, secretlen);
gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */
gcry_md_write (hash_hd, &counter, 1);
err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen,
ukm, ukmlen);
if (err)
break;
gcry_md_final (hash_hd);
if (n + hashlen > keylen)
ncopy = keylen - n;
else
ncopy = hashlen;
memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy);
n += ncopy;
}
gcry_md_close (hash_hd);
return err;
}
/* This function will modify SECRET. NBITS is the size of the curve
* which which we took from the certificate. */
static gpg_error_t
ecdh_decrypt (unsigned char *secret, size_t secretlen,
unsigned int nbits, gcry_sexp_t enc_val,
unsigned char **r_result, unsigned int *r_resultlen)
{
gpg_error_t err;
gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} };
char *encr_algo_str = NULL;
char *wrap_algo_str = NULL;
int hash_algo, cipher_algo;
const unsigned char *ukm; /* Alias for ioarray[2]. */
unsigned int ukmlen;
const unsigned char *data; /* Alias for ioarray[3]. */
unsigned int datalen;
unsigned int keylen;
unsigned char key[32];
gcry_cipher_hd_t cipher_hd = NULL;
unsigned char *result = NULL;
unsigned int resultlen;
*r_resultlen = 0;
*r_result = NULL;
/* Extract X from SECRET; this is the actual secret. Unless a
* smartcard diretcly returns X, it must be in the format of:
*
* 04 || X || Y
* 40 || X
* 41 || X
*/
if (secretlen < 2)
return gpg_error (GPG_ERR_BAD_DATA);
if (secretlen == (nbits+7)/8)
; /* Matches curve length - this is already the X coordinate. */
else if (*secret == 0x04)
{
secretlen--;
memmove (secret, secret+1, secretlen);
if ((secretlen & 1))
return gpg_error (GPG_ERR_BAD_DATA);
secretlen /= 2;
}
else if (*secret == 0x40 || *secret == 0x41)
{
secretlen--;
memmove (secret, secret+1, secretlen);
}
else
return gpg_error (GPG_ERR_BAD_DATA);
if (!secretlen)
return gpg_error (GPG_ERR_BAD_DATA);
if (DBG_CRYPTO)
log_printhex (secret, secretlen, "ECDH X ..:");
/* We have now the shared secret bytes in (SECRET,SECRETLEN). Now
* we will compute the KEK using a value dervied from the secret
* bytes. */
err = gcry_sexp_extract_param (enc_val, "enc-val",
"&'encr-algo''wrap-algo''ukm'?s",
ioarray+0, ioarray+1,
ioarray+2, ioarray+3, NULL);
if (err)
{
log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err));
goto leave;
}
encr_algo_str = string_from_gcry_buffer (ioarray);
if (!encr_algo_str)
{
err = gpg_error_from_syserror ();
goto leave;
}
wrap_algo_str = string_from_gcry_buffer (ioarray+1);
if (!wrap_algo_str)
{
err = gpg_error_from_syserror ();
goto leave;
}
ukm = ioarray[2].data;
ukmlen = ioarray[2].len;
data = ioarray[3].data;
datalen = ioarray[3].len;
/* Check parameters. */
if (DBG_CRYPTO)
{
log_debug ("encr_algo: %s\n", encr_algo_str);
log_debug ("wrap_algo: %s\n", wrap_algo_str);
log_printhex (ukm, ukmlen, "ukm .....:");
log_printhex (data, datalen, "data ....:");
}
if (!strcmp (encr_algo_str, "1.3.132.1.11.1"))
{
/* dhSinglePass-stdDH-sha256kdf-scheme */
hash_algo = GCRY_MD_SHA256;
}
else if (!strcmp (encr_algo_str, "1.3.132.1.11.2"))
{
/* dhSinglePass-stdDH-sha384kdf-scheme */
hash_algo = GCRY_MD_SHA384;
}
else if (!strcmp (encr_algo_str, "1.3.132.1.11.3"))
{
/* dhSinglePass-stdDH-sha512kdf-scheme */
hash_algo = GCRY_MD_SHA512;
}
else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2"))
{
/* dhSinglePass-stdDH-sha1kdf-scheme */
hash_algo = GCRY_MD_SHA1;
}
else
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5"))
{
cipher_algo = GCRY_CIPHER_AES128;
keylen = 16;
}
else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25"))
{
cipher_algo = GCRY_CIPHER_AES192;
keylen = 24;
}
else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45"))
{
cipher_algo = GCRY_CIPHER_AES256;
keylen = 32;
}
else
{
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str,
secret, secretlen, ukm, ukmlen);
if (err)
goto leave;
if (DBG_CRYPTO)
log_printhex (key, keylen, "KEK .....:");
/* Unwrap the key. */
if ((datalen % 8) || datalen < 16)
{
log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen);
err = gpg_error (GPG_ERR_BAD_DATA);
goto leave;
}
resultlen = datalen - 8;
result = xtrymalloc_secure (resultlen);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
if (err)
{
log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err));
goto leave;
}
err = gcry_cipher_setkey (cipher_hd, key, keylen);
wipememory (key, sizeof key);
if (err)
{
log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err));
goto leave;
}
err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen);
if (err)
{
log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err));
goto leave;
}
*r_resultlen = resultlen;
*r_result = result;
result = NULL;
leave:
if (result)
{
wipememory (result, resultlen);
xfree (result);
}
gcry_cipher_close (cipher_hd);
xfree (encr_algo_str);
xfree (wrap_algo_str);
xfree (ioarray[0].data);
xfree (ioarray[1].data);
xfree (ioarray[2].data);
xfree (ioarray[3].data);
return err;
}
/* Helper for pwri_decrypt to parse the derive info.
* Example data for (DER,DERLEN):
* SEQUENCE {
* OCTET STRING
* 60 76 4B E9 5E DF 3C F8 B2 F9 B6 C2 7D 5A FB 90
* 23 B6 47 DF
* INTEGER 10000
* SEQUENCE {
* OBJECT IDENTIFIER
* hmacWithSHA512 (1 2 840 113549 2 11)
* NULL
* }
* }
*/
static gpg_error_t
pwri_parse_pbkdf2 (const unsigned char *der, size_t derlen,
unsigned char const **r_salt, unsigned int *r_saltlen,
unsigned long *r_iterations,
enum gcry_md_algos *r_digest)
{
gpg_error_t err;
size_t objlen, hdrlen;
int class, tag, constructed, ndef;
char *oidstr;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_SEQUENCE
|| !constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
derlen = objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_OCTET_STRING
|| constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
*r_salt = der;
*r_saltlen = objlen;
der += objlen;
derlen -= objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_INTEGER
|| constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
*r_iterations = 0;
for (; objlen; objlen--)
{
*r_iterations <<= 8;
*r_iterations |= (*der++) & 0xff;
derlen--;
}
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_SEQUENCE
|| !constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
derlen = objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_OBJECT_ID
|| constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
oidstr = ksba_oid_to_str (der, objlen);
if (!oidstr)
return gpg_error_from_syserror ();
*r_digest = gcry_md_map_name (oidstr);
if (*r_digest)
;
else if (!strcmp (oidstr, "1.2.840.113549.2.7"))
*r_digest = GCRY_MD_SHA1;
else if (!strcmp (oidstr, "1.2.840.113549.2.8"))
*r_digest = GCRY_MD_SHA224;
else if (!strcmp (oidstr, "1.2.840.113549.2.9"))
*r_digest = GCRY_MD_SHA256;
else if (!strcmp (oidstr, "1.2.840.113549.2.10"))
*r_digest = GCRY_MD_SHA384;
else if (!strcmp (oidstr, "1.2.840.113549.2.11"))
*r_digest = GCRY_MD_SHA512;
else
err = gpg_error (GPG_ERR_DIGEST_ALGO);
ksba_free (oidstr);
return err;
}
/* Password based decryption.
* ENC_VAL has the form:
* (enc-val
* (pwri
* (derive-algo <oid>) --| both are optional
* (derive-parm <der>) --|
* (encr-algo <oid>)
* (encr-parm <iv>)
* (encr-key <key>))) -- this is the encrypted session key
*
*/
static gpg_error_t
pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val,
unsigned char **r_result, unsigned int *r_resultlen,
struct decrypt_filter_parm_s *parm)
{
gpg_error_t err;
gcry_buffer_t ioarray[5] = { {0} };
char *derive_algo_str = NULL;
char *encr_algo_str = NULL;
const unsigned char *dparm; /* Alias for ioarray[1]. */
unsigned int dparmlen;
const unsigned char *eparm; /* Alias for ioarray[3]. */
unsigned int eparmlen;
const unsigned char *ekey; /* Alias for ioarray[4]. */
unsigned int ekeylen;
unsigned char kek[32];
unsigned int keklen;
enum gcry_cipher_algos encr_algo;
enum gcry_cipher_modes encr_mode;
gcry_cipher_hd_t encr_hd = NULL;
unsigned char *result = NULL;
unsigned int resultlen;
unsigned int blklen;
const unsigned char *salt; /* Points int dparm. */
unsigned int saltlen;
unsigned long iterations;
enum gcry_md_algos digest_algo;
char *passphrase = NULL;
*r_resultlen = 0;
*r_result = NULL;
err = gcry_sexp_extract_param (enc_val, "enc-val!pwri",
"&'derive-algo'?'derive-parm'?"
"'encr-algo''encr-parm''encr-key'",
ioarray+0, ioarray+1,
ioarray+2, ioarray+3, ioarray+4, NULL);
if (err)
{
/* If this is not pwri element, it is likly a kekri element
* which we do not yet support. Change the error back to the
* original as returned by ksba_cms_get_issuer. */
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
err = gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
else
log_error ("extracting PWRI parameter failed: %s\n",
gpg_strerror (err));
goto leave;
}
if (ioarray[0].data)
{
derive_algo_str = string_from_gcry_buffer (ioarray+0);
if (!derive_algo_str)
{
err = gpg_error_from_syserror ();
goto leave;
}
}
dparm = ioarray[1].data;
dparmlen = ioarray[1].len;
encr_algo_str = string_from_gcry_buffer (ioarray+2);
if (!encr_algo_str)
{
err = gpg_error_from_syserror ();
goto leave;
}
eparm = ioarray[3].data;
eparmlen = ioarray[3].len;
ekey = ioarray[4].data;
ekeylen = ioarray[4].len;
/* Check parameters. */
if (DBG_CRYPTO)
{
if (derive_algo_str)
{
log_debug ("derive algo: %s\n", derive_algo_str);
log_printhex (dparm, dparmlen, "derive parm:");
}
log_debug ("encr algo .: %s\n", encr_algo_str);
log_printhex (eparm, eparmlen, "encr parm .:");
log_printhex (ekey, ekeylen, "encr key .:");
}
if (!derive_algo_str)
{
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
log_info ("PWRI with no key derivation detected\n");
goto leave;
}
if (strcmp (derive_algo_str, "1.2.840.113549.1.5.12"))
{
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
log_info ("PWRI does not use PBKDF2 (but %s)\n", derive_algo_str);
goto leave;
}
digest_algo = 0; /*(silence cc warning)*/
err = pwri_parse_pbkdf2 (dparm, dparmlen,
&salt, &saltlen, &iterations, &digest_algo);
if (err)
{
log_error ("parsing PWRI parameter failed: %s\n", gpg_strerror (err));
goto leave;
}
parm->is_de_vs = (parm->is_de_vs
&& gnupg_digest_is_compliant (CO_DE_VS, digest_algo));
encr_algo = gcry_cipher_map_name (encr_algo_str);
encr_mode = gcry_cipher_mode_from_oid (encr_algo_str);
if (!encr_algo || !encr_mode)
{
log_error ("PWRI uses unknown algorithm %s\n", encr_algo_str);
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
parm->is_de_vs =
(parm->is_de_vs
&& gnupg_cipher_is_compliant (CO_DE_VS, encr_algo, encr_mode));
keklen = gcry_cipher_get_algo_keylen (encr_algo);
blklen = gcry_cipher_get_algo_blklen (encr_algo);
if (!keklen || keklen > sizeof kek || blklen != 16 )
{
log_error ("PWRI algorithm %s cannot be used\n", encr_algo_str);
err = gpg_error (GPG_ERR_INV_KEYLEN);
goto leave;
}
if ((ekeylen % blklen) || (ekeylen / blklen < 2))
{
/* Note that we need at least two full blocks. */
log_error ("PWRI uses a wrong length of encrypted key\n");
err = gpg_error (GPG_ERR_INV_KEYLEN);
goto leave;
}
err = gpgsm_agent_ask_passphrase
(ctrl,
i18n_utf8 (N_("Please enter the passphrase for decryption.")),
0, &passphrase);
if (err)
goto leave;
err = gcry_kdf_derive (passphrase, strlen (passphrase),
GCRY_KDF_PBKDF2, digest_algo,
salt, saltlen, iterations,
keklen, kek);
if (passphrase)
{
wipememory (passphrase, strlen (passphrase));
xfree (passphrase);
passphrase = NULL;
}
if (err)
{
log_error ("deriving key from passphrase failed: %s\n",
gpg_strerror (err));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (kek, keklen, "KEK .......:");
/* Unwrap the key. */
resultlen = ekeylen;
result = xtrymalloc_secure (resultlen);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_open (&encr_hd, encr_algo, encr_mode, 0);
if (err)
{
log_error ("PWRI failed to open cipher: %s\n", gpg_strerror (err));
goto leave;
}
err = gcry_cipher_setkey (encr_hd, kek, keklen);
wipememory (kek, sizeof kek);
if (!err)
err = gcry_cipher_setiv (encr_hd, ekey + ekeylen - 2 * blklen, blklen);
if (!err)
err = gcry_cipher_decrypt (encr_hd, result + ekeylen - blklen, blklen,
ekey + ekeylen - blklen, blklen);
if (!err)
err = gcry_cipher_setiv (encr_hd, result + ekeylen - blklen, blklen);
if (!err)
err = gcry_cipher_decrypt (encr_hd, result, ekeylen - blklen,
ekey, ekeylen - blklen);
/* (We assume that that eparm is the octet string with the IV) */
if (!err)
err = gcry_cipher_setiv (encr_hd, eparm, eparmlen);
if (!err)
err = gcry_cipher_decrypt (encr_hd, result, resultlen, NULL, 0);
if (err)
{
log_error ("KEK decryption failed for PWRI: %s\n", gpg_strerror (err));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (result, resultlen, "Frame .....:");
if (result[0] < 8 /* At least 64 bits */
|| (result[0] % 8) /* Multiple of 64 bits */
|| result[0] > resultlen - 4 /* Not more than the size of the input */
|| ( (result[1] ^ result[4]) /* Matching check bytes. */
& (result[2] ^ result[5])
& (result[3] ^ result[6]) ) != 0xff)
{
err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
goto leave;
}
*r_resultlen = result[0];
*r_result = memmove (result, result + 4, result[0]);
result = NULL;
leave:
if (result)
{
wipememory (result, resultlen);
xfree (result);
}
if (passphrase)
{
wipememory (passphrase, strlen (passphrase));
xfree (passphrase);
}
gcry_cipher_close (encr_hd);
xfree (derive_algo_str);
xfree (encr_algo_str);
xfree (ioarray[0].data);
xfree (ioarray[1].data);
xfree (ioarray[2].data);
xfree (ioarray[3].data);
xfree (ioarray[4].data);
return err;
}
/* Decrypt the session key and fill in the parm structure. The
algo and the IV is expected to be already in PARM. */
static int
prepare_decryption (ctrl_t ctrl, const char *hexkeygrip,
int pk_algo, unsigned int nbits, const char *desc,
ksba_const_sexp_t enc_val,
struct decrypt_filter_parm_s *parm)
{
char *seskey = NULL;
size_t n, seskeylen;
int pwri = !hexkeygrip && !pk_algo;
int rc;
if (DBG_CRYPTO)
log_printcanon ("decrypting:", enc_val, 0);
if (!pwri)
{
rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
&seskey, &seskeylen);
if (rc)
{
log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (seskey, seskeylen, "DEK frame:");
}
n=0;
if (pwri) /* Password based encryption. */
{
gcry_sexp_t s_enc_val;
unsigned char *decrypted;
unsigned int decryptedlen;
rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val,
gcry_sexp_canon_len (enc_val, 0, NULL, NULL));
if (rc)
goto leave;
rc = pwri_decrypt (ctrl, s_enc_val, &decrypted, &decryptedlen, parm);
gcry_sexp_release (s_enc_val);
if (rc)
goto leave;
xfree (seskey);
seskey = decrypted;
seskeylen = decryptedlen;
}
else if (pk_algo == GCRY_PK_ECC)
{
gcry_sexp_t s_enc_val;
unsigned char *decrypted;
unsigned int decryptedlen;
rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val,
gcry_sexp_canon_len (enc_val, 0, NULL, NULL));
if (rc)
goto leave;
rc = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val,
&decrypted, &decryptedlen);
gcry_sexp_release (s_enc_val);
if (rc)
goto leave;
xfree (seskey);
seskey = decrypted;
seskeylen = decryptedlen;
}
else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16)
{
/* Smells like an AES-128, 3-DES, or AES-256 key. This might
* happen because a SC has already done the unpacking. A better
* solution would be to test for this only after we triggered
* the GPG_ERR_INV_SESSION_KEY. */
}
else
{
if (n + 7 > seskeylen )
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
/* FIXME: Actually the leading zero is required but due to the way
we encode the output in libgcrypt as an MPI we are not able to
encode that leading zero. However, when using a Smartcard we are
doing it the right way and therefore we have to skip the zero. This
should be fixed in gpg-agent of course. */
if (!seskey[n])
n++;
if (seskey[n] != 2 ) /* Wrong block type version. */
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */
;
n++; /* and the zero byte */
if (n >= seskeylen )
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
}
if (DBG_CRYPTO)
{
log_printhex (seskey+n, seskeylen-n, "CEK .......:");
log_printhex (parm->iv, parm->ivlen, "IV ........:");
}
if (opt.verbose)
log_info (_("%s.%s encrypted data\n"),
gcry_cipher_algo_name (parm->algo),
cipher_mode_to_string (parm->mode));
rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0);
if (rc)
{
log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
goto leave;
}
rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
{
log_info (_("WARNING: message was encrypted with "
"a weak key in the symmetric cipher.\n"));
rc = 0;
}
if (rc)
{
log_error("key setup failed: %s\n", gpg_strerror(rc) );
goto leave;
}
rc = gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
if (rc)
{
log_error("IV setup failed: %s\n", gpg_strerror(rc) );
goto leave;
}
if (parm->mode == GCRY_CIPHER_MODE_GCM)
{
/* GCM mode really sucks in CMS. We need to know the AAD before
* we start decrypting but CMS puts the AAD after the content.
* Thus temporary files are required. Let's hope that no real
* messages with actual AAD are ever used. OCB Rules! */
}
leave:
xfree (seskey);
return rc;
}
/* This function is called by the KSBA writer just before the actual
write is done. The function must take INLEN bytes from INBUF,
decrypt it and store it inoutbuf which has a maximum size of
maxoutlen. The valid bytes in outbuf should be return in outlen.
Due to different buffer sizes or different length of input and
output, it may happen that fewer bytes are processed or fewer bytes
are written. */
static gpg_error_t
decrypt_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
int blklen = parm->blklen;
size_t orig_inlen = inlen;
/* fixme: Should we issue an error when we have not seen one full block? */
if (!inlen)
return gpg_error (GPG_ERR_BUG);
if (maxoutlen < 2*parm->blklen)
return gpg_error (GPG_ERR_BUG);
/* Make some space because we will later need an extra block at the end. */
maxoutlen -= blklen;
if (parm->helpblocklen)
{
int i, j;
for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
parm->helpblock[i] = ((const char*)inbuf)[j];
inlen -= j;
if (blklen > maxoutlen)
return gpg_error (GPG_ERR_BUG);
if (i < blklen)
{
parm->helpblocklen = i;
*outlen = 0;
}
else
{
parm->helpblocklen = 0;
if (parm->any_data)
{
memcpy (outbuf, parm->lastblock, blklen);
*outlen =blklen;
}
else
*outlen = 0;
gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
parm->helpblock, blklen);
parm->any_data = 1;
}
*inused = orig_inlen - inlen;
return 0;
}
if (inlen > maxoutlen)
inlen = maxoutlen;
if (inlen % blklen)
{ /* store the remainder away */
parm->helpblocklen = inlen%blklen;
inlen = inlen/blklen*blklen;
memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
}
*inused = inlen + parm->helpblocklen;
if (inlen)
{
assert (inlen >= blklen);
if (parm->any_data)
{
gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
inbuf, inlen);
memcpy (outbuf, parm->lastblock, blklen);
memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
*outlen = inlen;
}
else
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
*outlen = inlen - blklen;
parm->any_data = 1;
}
}
else
*outlen = 0;
return 0;
}
/* This is the GCM version of decrypt_filter. */
static gpg_error_t
decrypt_gcm_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
if (!inlen)
return gpg_error (GPG_ERR_BUG);
if (maxoutlen < parm->blklen)
return gpg_error (GPG_ERR_BUG);
if (inlen > maxoutlen)
inlen = maxoutlen;
*inused = inlen;
if (inlen)
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
*outlen = inlen;
parm->any_data = 1;
}
else
*outlen = 0;
return 0;
}
/* Perform a decrypt operation. */
int
gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
{
int rc;
gnupg_ksba_io_t b64reader = NULL;
gnupg_ksba_io_t b64writer = NULL;
ksba_reader_t reader;
ksba_writer_t writer;
ksba_cms_t cms = NULL;
ksba_stop_reason_t stopreason;
KEYDB_HANDLE kh;
int recp;
estream_t in_fp = NULL;
struct decrypt_filter_parm_s dfparm;
memset (&dfparm, 0, sizeof dfparm);
audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT);
kh = keydb_new ();
if (!kh)
{
log_error (_("failed to allocate keyDB handle\n"));
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
in_fp = es_fdopen_nc (in_fd, "rb");
if (!in_fp)
{
rc = gpg_error_from_syserror ();
log_error ("fdopen() failed: %s\n", strerror (errno));
goto leave;
}
rc = gnupg_ksba_create_reader
(&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0)
| (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)),
in_fp, &reader);
if (rc)
{
log_error ("can't create reader: %s\n", gpg_strerror (rc));
goto leave;
}
rc = gnupg_ksba_create_writer
(&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
ctrl->pem_name, out_fp, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gpg_strerror (rc));
goto leave;
}
+ gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
+
rc = ksba_cms_new (&cms);
if (rc)
goto leave;
rc = ksba_cms_set_reader_writer (cms, reader, writer);
if (rc)
{
log_debug ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (rc));
goto leave;
}
audit_log (ctrl->audit, AUDIT_SETUP_READY);
/* Parser loop. */
do
{
rc = ksba_cms_parse (cms, &stopreason);
if (rc)
{
log_debug ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA
|| stopreason == KSBA_SR_DETACHED_DATA)
{
int algo, mode;
const char *algoid;
int any_key = 0;
audit_log (ctrl->audit, AUDIT_GOT_DATA);
algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
algo = gcry_cipher_map_name (algoid);
mode = gcry_cipher_mode_from_oid (algoid);
if (!algo || !mode)
{
rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?");
if (algoid && !strcmp (algoid, "1.2.840.113549.3.2"))
log_info (_("(this is the RC2 algorithm)\n"));
else if (!algoid)
log_info (_("(this does not seem to be an encrypted"
" message)\n"));
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm",
numbuf, algoid?algoid:"?", NULL);
audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid);
}
/* If it seems that this is not an encrypted message we
return a more sensible error code. */
if (!algoid)
rc = gpg_error (GPG_ERR_NO_DATA);
goto leave;
}
/* Check compliance. */
if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode))
{
log_error (_("cipher algorithm '%s'"
" may not be used in %s mode\n"),
gcry_cipher_algo_name (algo),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
/* For CMS, CO_DE_VS demands CBC mode. */
dfparm.is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode);
audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo);
dfparm.algo = algo;
dfparm.mode = mode;
dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
if (dfparm.blklen > sizeof (dfparm.helpblock))
{
rc = gpg_error (GPG_ERR_BUG);
goto leave;
}
rc = ksba_cms_get_content_enc_iv (cms,
dfparm.iv,
sizeof (dfparm.iv),
&dfparm.ivlen);
if (rc)
{
log_error ("error getting IV: %s\n", gpg_strerror (rc));
goto leave;
}
for (recp=0; !any_key; recp++)
{
char *issuer;
ksba_sexp_t serial;
ksba_sexp_t enc_val;
char *hexkeygrip = NULL;
char *pkalgostr = NULL;
char *pkfpr = NULL;
char *desc = NULL;
char kidbuf[16+1];
int tmp_rc;
ksba_cert_t cert = NULL;
unsigned int nbits;
int pk_algo = 0;
int maybe_pwri = 0;
*kidbuf = 0;
tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
if (tmp_rc == -1 && recp)
break; /* no more recipients */
audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
if (gpg_err_code (tmp_rc) == GPG_ERR_UNSUPPORTED_CMS_OBJ)
{
maybe_pwri = 1;
}
else if (tmp_rc)
{
log_error ("recp %d - error getting info: %s\n",
recp, gpg_strerror (tmp_rc));
}
else
{
if (opt.verbose)
{
log_debug ("recp %d - issuer: '%s'\n",
recp, issuer? issuer:"[NONE]");
log_debug ("recp %d - serial: ", recp);
gpgsm_dump_serial (serial);
log_printf ("\n");
}
if (ctrl->audit)
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
xfree (tmpstr);
}
keydb_search_reset (kh);
rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial);
if (rc)
{
log_error ("failed to find the certificate: %s\n",
gpg_strerror(rc));
goto oops;
}
rc = keydb_get_cert (kh, &cert);
if (rc)
{
log_error ("failed to get cert: %s\n", gpg_strerror (rc));
goto oops;
}
/* Print the ENC_TO status line. Note that we can
do so only if we have the certificate. This is
in contrast to gpg where the keyID is commonly
included in the encrypted messages. It is too
cumbersome to retrieve the used algorithm, thus
we don't print it for now. We also record the
keyid for later use. */
{
unsigned long kid[2];
kid[0] = gpgsm_get_short_fingerprint (cert, kid+1);
snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
kid[1], kid[0]);
gpgsm_status2 (ctrl, STATUS_ENC_TO,
kidbuf, "0", "0", NULL);
}
/* Put the certificate into the audit log. */
audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0);
/* Just in case there is a problem with the own
certificate we print this message - should never
happen of course */
rc = gpgsm_cert_use_decrypt_p (cert);
if (rc)
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage",
numbuf, NULL);
rc = 0;
}
hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
desc = gpgsm_format_keydesc (cert);
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
pk_algo = gpgsm_get_key_algo_info (cert, &nbits);
if (!opt.quiet)
log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr);
/* Check compliance. */
if (!gnupg_pk_is_allowed (opt.compliance,
PK_USE_DECRYPTION,
pk_algo, 0, NULL, nbits, NULL))
{
char kidstr[10+1];
snprintf (kidstr, sizeof kidstr, "0x%08lX",
gpgsm_get_short_fingerprint (cert, NULL));
log_info (_("key %s is not suitable for decryption"
" in %s mode\n"),
kidstr,
gnupg_compliance_option_string(opt.compliance));
rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto oops;
}
/* Check that all certs are compliant with CO_DE_VS. */
dfparm.is_de_vs =
(dfparm.is_de_vs
&& gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0,
NULL, nbits, NULL));
oops:
if (rc)
{
/* We cannot check compliance of certs that we
* don't have. */
dfparm.is_de_vs = 0;
}
xfree (issuer);
xfree (serial);
ksba_cert_release (cert);
}
if ((!hexkeygrip || !pk_algo) && !maybe_pwri)
;
else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
{
log_error ("recp %d - error getting encrypted session key\n",
recp);
if (maybe_pwri)
log_info ("(possibly unsupported KEK info)\n");
}
else
{
if (maybe_pwri && opt.verbose)
log_info ("recp %d - KEKRI or PWRI\n", recp);
rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits,
desc, enc_val, &dfparm);
xfree (enc_val);
if (rc)
{
log_info ("decrypting session key failed: %s\n",
gpg_strerror (rc));
if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf)
gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL);
}
else
{ /* setup the bulk decrypter */
any_key = 1;
ksba_writer_set_filter
(writer,
dfparm.mode == GCRY_CIPHER_MODE_GCM?
decrypt_gcm_filter : decrypt_filter,
&dfparm);
if (dfparm.is_de_vs
&& gnupg_gcrypt_is_compliant (CO_DE_VS))
gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS));
else if (opt.require_compliance
&& opt.compliance == CO_DE_VS)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
gpgsm_errors_seen = 1;
}
}
audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc);
}
xfree (pkalgostr);
xfree (pkfpr);
xfree (hexkeygrip);
xfree (desc);
}
/* If we write an audit log add the unused recipients to the
log as well. */
if (ctrl->audit && any_key)
{
for (;; recp++)
{
char *issuer;
ksba_sexp_t serial;
int tmp_rc;
tmp_rc = ksba_cms_get_issuer_serial (cms, recp,
&issuer, &serial);
if (tmp_rc == -1)
break; /* no more recipients */
audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
if (tmp_rc)
log_error ("recp %d - error getting info: %s\n",
recp, gpg_strerror (rc));
else
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
xfree (tmpstr);
xfree (issuer);
xfree (serial);
}
}
}
if (!any_key)
{
if (!rc)
rc = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
}
else if (stopreason == KSBA_SR_END_DATA)
{
ksba_writer_set_filter (writer, NULL, NULL);
if (dfparm.mode == GCRY_CIPHER_MODE_GCM)
{
/* Nothing yet to do. We wait for the ready event. */
}
else if (dfparm.any_data )
{ /* write the last block with padding removed */
int i, npadding = dfparm.lastblock[dfparm.blklen-1];
if (!npadding || npadding > dfparm.blklen)
{
log_error ("invalid padding with value %d\n", npadding);
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
rc = ksba_writer_write (writer,
dfparm.lastblock,
dfparm.blklen - npadding);
if (rc)
goto leave;
for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
{
if (dfparm.lastblock[i] != npadding)
{
log_error ("inconsistent padding\n");
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
}
}
}
else if (stopreason == KSBA_SR_READY)
{
if (dfparm.mode == GCRY_CIPHER_MODE_GCM)
{
char *authtag;
size_t authtaglen;
rc = ksba_cms_get_message_digest (cms, 0, &authtag, &authtaglen);
if (rc)
{
log_error ("error getting authtag: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (authtag, authtaglen, "Authtag ...:");
rc = gcry_cipher_checktag (dfparm.hd, authtag, authtaglen);
xfree (authtag);
if (rc)
log_error ("data is not authentic: %s\n", gpg_strerror (rc));
goto leave;
}
}
}
while (stopreason != KSBA_SR_READY);
rc = gnupg_ksba_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
goto leave;
}
gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
leave:
audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc);
if (rc)
{
gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
log_error ("message decryption failed: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc));
}
ksba_cms_release (cms);
gnupg_ksba_destroy_reader (b64reader);
gnupg_ksba_destroy_writer (b64writer);
keydb_release (kh);
es_fclose (in_fp);
if (dfparm.hd)
gcry_cipher_close (dfparm.hd);
return rc;
}
diff --git a/sm/encrypt.c b/sm/encrypt.c
index fb36977af..ed5cd2f90 100644
--- a/sm/encrypt.c
+++ b/sm/encrypt.c
@@ -1,884 +1,886 @@
/* encrypt.c - Encrypt a message
* Copyright (C) 2001, 2003, 2004, 2007, 2008,
* 2010 Free Software Foundation, Inc.
* Copyright (C) 2001-2019 Werner Koch
* Copyright (C) 2015-2020 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 <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include "gpgsm.h"
#include <gcrypt.h>
#include <ksba.h>
#include "keydb.h"
#include "../common/i18n.h"
#include "../common/compliance.h"
struct dek_s {
const char *algoid;
int algo;
gcry_cipher_hd_t chd;
char key[32];
int keylen;
char iv[32];
int ivlen;
};
typedef struct dek_s *DEK;
/* Callback parameters for the encryption. */
struct encrypt_cb_parm_s
{
estream_t fp;
DEK dek;
int eof_seen;
int ready;
int readerror;
int bufsize;
unsigned char *buffer;
int buflen;
};
/* Initialize the data encryption key (session key). */
static int
init_dek (DEK dek)
{
int rc=0, mode, i;
dek->algo = gcry_cipher_map_name (dek->algoid);
mode = gcry_cipher_mode_from_oid (dek->algoid);
if (!dek->algo || !mode)
{
log_error ("unsupported algorithm '%s'\n", dek->algoid);
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
}
/* Extra check for algorithms we consider to be too weak for
encryption, although we support them for decryption. Note that
there is another check below discriminating on the key length. */
switch (dek->algo)
{
case GCRY_CIPHER_DES:
case GCRY_CIPHER_RFC2268_40:
log_error ("cipher algorithm '%s' not allowed: too weak\n",
gnupg_cipher_algo_name (dek->algo));
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
default:
break;
}
dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
if (!dek->keylen || dek->keylen > sizeof (dek->key))
return gpg_error (GPG_ERR_BUG);
dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo);
if (!dek->ivlen || dek->ivlen > sizeof (dek->iv))
return gpg_error (GPG_ERR_BUG);
/* Make sure we don't use weak keys. */
if (dek->keylen < 100/8)
{
log_error ("key length of '%s' too small\n", dek->algoid);
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
}
rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE);
if (rc)
{
log_error ("failed to create cipher context: %s\n", gpg_strerror (rc));
return rc;
}
for (i=0; i < 8; i++)
{
gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM );
rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen);
if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY)
break;
log_info(_("weak key created - retrying\n") );
}
if (rc)
{
log_error ("failed to set the key: %s\n", gpg_strerror (rc));
gcry_cipher_close (dek->chd);
dek->chd = NULL;
return rc;
}
gcry_create_nonce (dek->iv, dek->ivlen);
rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen);
if (rc)
{
log_error ("failed to set the IV: %s\n", gpg_strerror (rc));
gcry_cipher_close (dek->chd);
dek->chd = NULL;
return rc;
}
return 0;
}
/* Encrypt an RSA session key. */
static int
encode_session_key (DEK dek, gcry_sexp_t * r_data)
{
gcry_sexp_t data;
char *p;
int rc;
p = xtrymalloc (64 + 2 * dek->keylen);
if (!p)
return gpg_error_from_syserror ();
strcpy (p, "(data\n (flags pkcs1)\n (value #");
bin2hex (dek->key, dek->keylen, p + strlen (p));
strcat (p, "#))\n");
rc = gcry_sexp_sscan (&data, NULL, p, strlen (p));
xfree (p);
*r_data = data;
return rc;
}
/* Encrypt DEK using ECDH. S_PKEY is the public key. On success the
* result is stored at R_ENCVAL. Example of a public key:
*
* (public-key (ecc (curve "1.3.132.0.34") (q #04B0[...]B8#)))
*
*/
static gpg_error_t
ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval)
{
gpg_error_t err;
gcry_sexp_t l1;
char *curvebuf = NULL;
const char *curve;
unsigned int curvebits;
const char *encr_algo_str;
const char *wrap_algo_str;
int hash_algo, cipher_algo;
unsigned int keylen, hashlen;
unsigned char key[32];
gcry_sexp_t s_data = NULL;
gcry_sexp_t s_encr = NULL;
gcry_buffer_t ioarray[2] = { {0}, {0} };
unsigned char *secret; /* Alias for ioarray[0]. */
unsigned int secretlen;
unsigned char *pubkey; /* Alias for ioarray[1]. */
unsigned int pubkeylen;
gcry_cipher_hd_t cipher_hd = NULL;
unsigned char *result = NULL;
unsigned int resultlen;
*r_encval = NULL;
/* Figure out the encryption and wrap algo OIDs. */
/* Get the curve name if any, */
l1 = gcry_sexp_find_token (s_pkey, "curve", 0);
if (l1)
{
curvebuf = gcry_sexp_nth_string (l1, 1);
gcry_sexp_release (l1);
}
if (!curvebuf)
{
err = gpg_error (GPG_ERR_INV_CURVE);
log_error ("%s: invalid public key: no curve\n", __func__);
goto leave;
}
/* We need to use our OpenPGP mapping to turn a curve name into its
* canonical numerical OID. We also use this to get the size of the
* curve which we need to figure out a suitable hash algo. We
* should have a Libgcrypt function to do this; see bug report #4926. */
curve = openpgp_curve_to_oid (curvebuf, &curvebits, NULL);
if (!curve)
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err));
goto leave;
}
xfree (curvebuf);
curvebuf = NULL;
/* Our mapping matches the recommended algorithms from RFC-5753 but
* not supporing the short curves which would require 3DES. */
if (curvebits < 255)
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
log_error ("%s: curve '%s' is not supported\n", __func__, curve);
goto leave;
}
else if (curvebits <= 256)
{
/* dhSinglePass-stdDH-sha256kdf-scheme */
encr_algo_str = "1.3.132.1.11.1";
wrap_algo_str = "2.16.840.1.101.3.4.1.5";
hash_algo = GCRY_MD_SHA256;
hashlen = 32;
cipher_algo = GCRY_CIPHER_AES128;
keylen = 16;
}
else if (curvebits <= 384)
{
/* dhSinglePass-stdDH-sha384kdf-scheme */
encr_algo_str = "1.3.132.1.11.2";
wrap_algo_str = "2.16.840.1.101.3.4.1.25";
hash_algo = GCRY_MD_SHA384;
hashlen = 48;
cipher_algo = GCRY_CIPHER_AES256;
keylen = 24;
}
else
{
/* dhSinglePass-stdDH-sha512kdf-scheme*/
encr_algo_str = "1.3.132.1.11.3";
wrap_algo_str = "2.16.840.1.101.3.4.1.45";
hash_algo = GCRY_MD_SHA512;
hashlen = 64;
cipher_algo = GCRY_CIPHER_AES256;
keylen = 32;
}
/* Create a secret and an ephemeral key. */
{
char *k;
k = gcry_random_bytes_secure ((curvebits+7)/8, GCRY_STRONG_RANDOM);
if (DBG_CRYPTO)
log_printhex (k, (curvebits+7)/8, "ephm. k .:");
err = gcry_sexp_build (&s_data, NULL, "%b", (int)(curvebits+7)/8, k);
xfree (k);
}
if (err)
{
log_error ("%s: error building ephemeral secret: %s\n",
__func__, gpg_strerror (err));
goto leave;
}
err = gcry_pk_encrypt (&s_encr, s_data, s_pkey);
if (err)
{
log_error ("%s: error encrypting ephemeral secret: %s\n",
__func__, gpg_strerror (err));
goto leave;
}
err = gcry_sexp_extract_param (s_encr, NULL, "&se",
&ioarray+0, ioarray+1, NULL);
if (err)
{
log_error ("%s: error extracting ephemeral key and secret: %s\n",
__func__, gpg_strerror (err));
goto leave;
}
secret = ioarray[0].data;
secretlen = ioarray[0].len;
pubkey = ioarray[1].data;
pubkeylen = ioarray[1].len;
if (DBG_CRYPTO)
{
log_printhex (pubkey, pubkeylen, "pubkey ..:");
log_printhex (secret, secretlen, "secret ..:");
}
/* Extract X coordinate from SECRET. */
if (secretlen < 5) /* 5 because N could be reduced to (n-1)/2. */
err = gpg_error (GPG_ERR_BAD_DATA);
else if (*secret == 0x04)
{
secretlen--;
memmove (secret, secret+1, secretlen);
if ((secretlen & 1))
{
err = gpg_error (GPG_ERR_BAD_DATA);
goto leave;
}
secretlen /= 2;
}
else if (*secret == 0x40 || *secret == 0x41)
{
secretlen--;
memmove (secret, secret+1, secretlen);
}
else
err = gpg_error (GPG_ERR_BAD_DATA);
if (err)
goto leave;
if (DBG_CRYPTO)
log_printhex (secret, secretlen, "ECDH X ..:");
/* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X.
* According to SEC1 3.6.1 we should check that
* SECRETLEN + UKMLEN + 4 < maxhashlen
* However, we have no practical limit on the hash length and thus
* there is no point in checking this. The second check that
* KEYLEN < hashlen*(2^32-1)
* is obviously also not needed. Because with our allowed
* parameters KEYLEN is always less or equal to HASHLEN so that we
* do not need to iterate at all.
*/
log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen);
{
gcry_md_hd_t hash_hd;
err = gcry_md_open (&hash_hd, hash_algo, 0);
if (err)
goto leave;
gcry_md_write(hash_hd, secret, secretlen);
gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */
err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, NULL, 0);
gcry_md_final (hash_hd);
log_assert (keylen <= sizeof key && keylen <= hashlen);
memcpy (key, gcry_md_read (hash_hd, 0), keylen);
gcry_md_close (hash_hd);
if (err)
goto leave;
}
if (DBG_CRYPTO)
log_printhex (key, keylen, "KEK .....:");
/* Wrap the key. */
if ((dek->keylen % 8) || dek->keylen < 16)
{
log_error ("%s: can't use a session key of %u bytes\n",
__func__, dek->keylen);
err = gpg_error (GPG_ERR_BAD_DATA);
goto leave;
}
resultlen = dek->keylen + 8;
result = xtrymalloc_secure (resultlen);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
if (err)
{
log_error ("%s: failed to initialize AESWRAP: %s\n",
__func__, gpg_strerror (err));
goto leave;
}
err = gcry_cipher_setkey (cipher_hd, key, keylen);
wipememory (key, sizeof key);
if (err)
{
log_error ("%s: failed in gcry_cipher_setkey: %s\n",
__func__, gpg_strerror (err));
goto leave;
}
err = gcry_cipher_encrypt (cipher_hd, result, resultlen,
dek->key, dek->keylen);
if (err)
{
log_error ("%s: failed in gcry_cipher_encrypt: %s\n",
__func__, gpg_strerror (err));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (result, resultlen, "w(CEK) ..:");
err = gcry_sexp_build (r_encval, NULL,
"(enc-val(ecdh(e%b)(s%b)(encr-algo%s)(wrap-algo%s)))",
(int)pubkeylen, pubkey,
(int)resultlen, result,
encr_algo_str,
wrap_algo_str,
NULL);
if (err)
log_error ("%s: failed building final S-exp: %s\n",
__func__, gpg_strerror (err));
leave:
gcry_cipher_close (cipher_hd);
wipememory (key, sizeof key);
xfree (result);
xfree (ioarray[0].data);
xfree (ioarray[1].data);
gcry_sexp_release (s_data);
gcry_sexp_release (s_encr);
xfree (curvebuf);
return err;
}
/* Encrypt the DEK under the key contained in CERT and return it as a
* canonical S-expressions at ENCVAL. PK_ALGO is the public key
* algorithm which the caller has already retrieved from CERT. */
static int
encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo,
unsigned char **encval)
{
gcry_sexp_t s_ciph, s_data, s_pkey;
int rc;
ksba_sexp_t buf;
size_t len;
*encval = NULL;
/* get the key from the cert */
buf = ksba_cert_get_public_key (cert);
if (!buf)
{
log_error ("no public key for recipient\n");
return gpg_error (GPG_ERR_NO_PUBKEY);
}
len = gcry_sexp_canon_len (buf, 0, NULL, NULL);
if (!len)
{
log_error ("libksba did not return a proper S-Exp\n");
return gpg_error (GPG_ERR_BUG);
}
rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)buf, len);
xfree (buf); buf = NULL;
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
return rc;
}
if (DBG_CRYPTO)
{
log_printsexp (" pubkey:", s_pkey);
log_printhex (dek->key, dek->keylen, "CEK .....:");
}
/* Put the encoded cleartext into a simple list. */
s_data = NULL; /* (avoid compiler warning) */
if (pk_algo == GCRY_PK_ECC)
{
if (!(opt.compat_flags & COMPAT_ALLOW_ECC_ENCR))
rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
rc = ecdh_encrypt (dek, s_pkey, &s_ciph);
}
else
{
rc = encode_session_key (dek, &s_data);
if (rc)
{
log_error ("encode_session_key failed: %s\n", gpg_strerror (rc));
return rc;
}
if (DBG_CRYPTO)
log_printsexp (" data:", s_data);
/* pass it to libgcrypt */
rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
}
gcry_sexp_release (s_data);
gcry_sexp_release (s_pkey);
if (DBG_CRYPTO)
log_printsexp ("enc-val:", s_ciph);
/* Reformat it. */
if (!rc)
{
rc = make_canon_sexp (s_ciph, encval, NULL);
gcry_sexp_release (s_ciph);
}
return rc;
}
/* do the actual encryption */
static int
encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
{
struct encrypt_cb_parm_s *parm = cb_value;
int blklen = parm->dek->ivlen;
unsigned char *p;
size_t n;
*nread = 0;
if (!buffer)
return -1; /* not supported */
if (parm->ready)
return -1;
if (count < blklen)
BUG ();
if (!parm->eof_seen)
{ /* fillup the buffer */
p = parm->buffer;
for (n=parm->buflen; n < parm->bufsize; n++)
{
int c = es_getc (parm->fp);
if (c == EOF)
{
if (es_ferror (parm->fp))
{
parm->readerror = errno;
return -1;
}
parm->eof_seen = 1;
break;
}
p[n] = c;
}
parm->buflen = n;
}
n = parm->buflen < count? parm->buflen : count;
n = n/blklen * blklen;
if (n)
{ /* encrypt the stuff */
gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n);
*nread = n;
/* Who cares about cycles, take the easy way and shift the buffer */
parm->buflen -= n;
memmove (parm->buffer, parm->buffer+n, parm->buflen);
}
else if (parm->eof_seen)
{ /* no complete block but eof: add padding */
/* fixme: we should try to do this also in the above code path */
int i, npad = blklen - (parm->buflen % blklen);
p = parm->buffer;
for (n=parm->buflen, i=0; n < parm->bufsize && i < npad; n++, i++)
p[n] = npad;
gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n);
*nread = n;
parm->ready = 1;
}
return 0;
}
/* Perform an encrypt operation.
Encrypt the data received on DATA-FD and write it to OUT_FP. The
recipients are take from the certificate given in recplist; if this
is NULL it will be encrypted for a default recipient */
int
gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp)
{
int rc = 0;
gnupg_ksba_io_t b64writer = NULL;
gpg_error_t err;
ksba_writer_t writer;
ksba_reader_t reader = NULL;
ksba_cms_t cms = NULL;
ksba_stop_reason_t stopreason;
KEYDB_HANDLE kh = NULL;
struct encrypt_cb_parm_s encparm;
DEK dek = NULL;
int recpno;
estream_t data_fp = NULL;
certlist_t cl;
int count;
int compliant;
memset (&encparm, 0, sizeof encparm);
audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT);
/* Check that the certificate list is not empty and that at least
one certificate is not flagged as encrypt_to; i.e. is a real
recipient. */
for (cl = recplist; cl; cl = cl->next)
if (!cl->is_encrypt_to)
break;
if (!cl)
{
log_error(_("no valid recipients given\n"));
gpgsm_status (ctrl, STATUS_NO_RECP, "0");
audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0);
rc = gpg_error (GPG_ERR_NO_PUBKEY);
goto leave;
}
for (count = 0, cl = recplist; cl; cl = cl->next)
count++;
audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count);
kh = keydb_new ();
if (!kh)
{
log_error (_("failed to allocate keyDB handle\n"));
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
/* Fixme: We should use the unlocked version of the es functions. */
data_fp = es_fdopen_nc (data_fd, "rb");
if (!data_fp)
{
rc = gpg_error_from_syserror ();
log_error ("fdopen() failed: %s\n", strerror (errno));
goto leave;
}
err = ksba_reader_new (&reader);
if (err)
rc = err;
if (!rc)
rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm);
if (rc)
goto leave;
encparm.fp = data_fp;
ctrl->pem_name = "ENCRYPTED MESSAGE";
rc = gnupg_ksba_create_writer
(&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
ctrl->pem_name, out_fp, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gpg_strerror (rc));
goto leave;
}
+ gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
+
err = ksba_cms_new (&cms);
if (err)
{
rc = err;
goto leave;
}
err = ksba_cms_set_reader_writer (cms, reader, writer);
if (err)
{
log_error ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
audit_log (ctrl->audit, AUDIT_GOT_DATA);
/* We are going to create enveloped data with uninterpreted data as
inner content */
err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA);
if (!err)
err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA);
if (err)
{
log_error ("ksba_cms_set_content_type failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
/* Check compliance. */
if (!gnupg_cipher_is_allowed
(opt.compliance, 1, gcry_cipher_map_name (opt.def_cipher_algoid),
gcry_cipher_mode_from_oid (opt.def_cipher_algoid)))
{
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
opt.def_cipher_algoid,
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
gpgsm_status_with_error (ctrl, STATUS_ERROR,
"random-compliance", rc);
goto leave;
}
/* Create a session key */
dek = xtrycalloc_secure (1, sizeof *dek);
if (!dek)
rc = out_of_core ();
else
{
dek->algoid = opt.def_cipher_algoid;
rc = init_dek (dek);
}
if (rc)
{
log_error ("failed to create the session key: %s\n",
gpg_strerror (rc));
goto leave;
}
err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen);
if (err)
{
log_error ("ksba_cms_set_content_enc_algo failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
encparm.dek = dek;
/* Use a ~8k (AES) or ~4k (3DES) buffer */
encparm.bufsize = 500 * dek->ivlen;
encparm.buffer = xtrymalloc (encparm.bufsize);
if (!encparm.buffer)
{
rc = out_of_core ();
goto leave;
}
audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid);
compliant = gnupg_cipher_is_compliant (CO_DE_VS, dek->algo,
GCRY_CIPHER_MODE_CBC);
/* Gather certificates of recipients, encrypt the session key for
each and store them in the CMS object */
for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next)
{
unsigned char *encval;
unsigned int nbits;
int pk_algo;
/* Check compliance. */
pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits);
if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, 0,
NULL, nbits, NULL))
{
char kidstr[10+1];
snprintf (kidstr, sizeof kidstr, "0x%08lX",
gpgsm_get_short_fingerprint (cl->cert, NULL));
log_info (_("WARNING: key %s is not suitable for encryption"
" in %s mode\n"),
kidstr,
gnupg_compliance_option_string (opt.compliance));
}
/* Fixme: When adding ECC we need to provide the curvename and
* the key to gnupg_pk_is_compliant. */
if (compliant
&& !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL))
compliant = 0;
rc = encrypt_dek (dek, cl->cert, pk_algo, &encval);
if (rc)
{
audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc);
log_error ("encryption failed for recipient no. %d: %s\n",
recpno, gpg_strerror (rc));
goto leave;
}
err = ksba_cms_add_recipient (cms, cl->cert);
if (err)
{
audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err);
log_error ("ksba_cms_add_recipient failed: %s\n",
gpg_strerror (err));
rc = err;
xfree (encval);
goto leave;
}
err = ksba_cms_set_enc_val (cms, recpno, encval);
xfree (encval);
audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err);
if (err)
{
log_error ("ksba_cms_set_enc_val failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
}
if (compliant && gnupg_gcrypt_is_compliant (CO_DE_VS))
gpgsm_status (ctrl, STATUS_ENCRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS));
else if (opt.require_compliance
&& opt.compliance == CO_DE_VS)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
gpgsm_errors_seen = 1;
rc = gpg_error (GPG_ERR_FORBIDDEN);
goto leave;
}
/* Main control loop for encryption. */
recpno = 0;
do
{
err = ksba_cms_build (cms, &stopreason);
if (err)
{
- log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
+ log_error ("creating CMS object failed: %s\n", gpg_strerror (err));
rc = err;
goto leave;
}
}
while (stopreason != KSBA_SR_READY);
if (encparm.readerror)
{
log_error ("error reading input: %s\n", strerror (encparm.readerror));
rc = gpg_error (gpg_err_code_from_errno (encparm.readerror));
goto leave;
}
rc = gnupg_ksba_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
goto leave;
}
audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE);
if (!opt.quiet)
log_info ("encrypted data created\n");
leave:
ksba_cms_release (cms);
gnupg_ksba_destroy_writer (b64writer);
ksba_reader_release (reader);
keydb_release (kh);
xfree (dek);
es_fclose (data_fp);
xfree (encparm.buffer);
return rc;
}
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index e1be5ca55..5790a64f4 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -1,479 +1,480 @@
/* gpgsm.h - Global definitions for GpgSM
* Copyright (C) 2001, 2003, 2004, 2007, 2009,
* 2010 Free Software Foundation, Inc.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef GPGSM_H
#define GPGSM_H
#ifdef GPG_ERR_SOURCE_DEFAULT
#error GPG_ERR_SOURCE_DEFAULT already defined
#endif
#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM
#include <gpg-error.h>
#include <ksba.h>
#include "../common/util.h"
#include "../common/status.h"
#include "../common/audit.h"
#include "../common/session-env.h"
#include "../common/ksba-io-support.h"
#include "../common/compliance.h"
#define MAX_DIGEST_LEN 64
/* A large struct named "opt" to keep global flags. */
EXTERN_UNLESS_MAIN_MODULE
struct
{
unsigned int debug; /* debug flags (DBG_foo_VALUE) */
int verbose; /* verbosity level */
int quiet; /* be as quiet as possible */
int batch; /* run in batch mode, i.e w/o any user interaction */
int answer_yes; /* assume yes on most questions */
int answer_no; /* assume no on most questions */
int dry_run; /* don't change any persistent data */
int no_homedir_creation;
const char *config_filename; /* Name of the used config file. */
const char *agent_program;
session_env_t session_env;
char *lc_ctype;
char *lc_messages;
int autostart;
const char *dirmngr_program;
int disable_dirmngr; /* Do not do any dirmngr calls. */
const char *protect_tool_program;
char *outfile; /* name of output file */
int with_key_data;/* include raw key in the column delimted output */
int fingerprint; /* list fingerprints in all key listings */
int with_md5_fingerprint; /* Also print an MD5 fingerprint for
standard key listings. */
int with_keygrip; /* Option --with-keygrip active. */
int pinentry_mode;
int request_origin;
int armor; /* force base64 armoring (see also ctrl.with_base64) */
int no_armor; /* don't try to figure out whether data is base64 armored*/
const char *p12_charset; /* Use this charset for encoding the
pkcs#12 passphrase. */
const char *def_cipher_algoid; /* cipher algorithm to use if
nothing else is specified */
int def_compress_algo; /* Ditto for compress algorithm */
int forced_digest_algo; /* User forced hash algorithm. */
char *def_recipient; /* userID of the default recipient */
int def_recipient_self; /* The default recipient is the default key */
int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */
char *local_user; /* NULL or argument to -u */
int extra_digest_algo; /* A digest algorithm also used for
verification of signatures. */
int always_trust; /* Trust the given keys even if there is no
valid certification chain */
int skip_verify; /* do not check signatures on data */
int lock_once; /* Keep lock once they are set */
int ignore_time_conflict; /* Ignore certain time conflicts */
int no_crl_check; /* Don't do a CRL check */
int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */
int force_crl_refresh; /* Force refreshing the CRL. */
int enable_issuer_based_crl_check; /* Backward compatibility hack. */
int enable_ocsp; /* Default to use OCSP checks. */
char *policy_file; /* full pathname of policy file */
int no_policy_check; /* ignore certificate policies */
int no_chain_validation; /* Bypass all cert chain validity tests */
int ignore_expiration; /* Ignore the notAfter validity checks. */
int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */
int qualsig_approval; /* Set to true if this software has
officially been approved to create an
verify qualified signatures. This is a
runtime option in case we want to check
the integrity of the software at
runtime. */
unsigned int min_rsa_length; /* Used for compliance checks. */
strlist_t keyserver;
/* A list of certificate extension OIDs which are ignored so that
one can claim that a critical extension has been handled. One
OID per string. */
strlist_t ignored_cert_extensions;
/* A list of OIDs which will be used to ignore certificates with
* sunch an OID during --learn-card. */
strlist_t ignore_cert_with_oid;
/* The current compliance mode. */
enum gnupg_compliance_mode compliance;
/* Fail if an operation can't be done in the requested compliance
* mode. */
int require_compliance;
/* Compatibility flags (COMPAT_FLAG_xxxx). */
unsigned int compat_flags;
} opt;
/* Debug values and macros. */
#define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CACHE_VALUE 64 /* debug the caching */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_IPC_VALUE 1024 /* debug assuan communication */
#define DBG_X509 (opt.debug & DBG_X509_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
/* Compatibility flags */
/* Telesec RSA cards produced for NRW in 2022 came with only the
* keyAgreement bit set. This flag allows there use for encryption
* anyway. Example cert:
* Issuer: /CN=DOI CA 10a/OU=DOI/O=PKI-1-Verwaltung/C=DE
* key usage: digitalSignature nonRepudiation keyAgreement
* policies: 1.3.6.1.4.1.7924.1.1:N:
*/
#define COMPAT_ALLOW_KA_TO_ENCR 1
#define COMPAT_ALLOW_ECC_ENCR 2
/* Forward declaration for an object defined in server.c */
struct server_local_s;
/* Session control object. This object is passed down to most
functions. Note that the default values for it are set by
gpgsm_init_default_ctrl(). */
struct server_control_s
{
int no_server; /* We are not running under server control */
int status_fd; /* Only for non-server mode */
struct server_local_s *server_local;
audit_ctx_t audit; /* NULL or a context for the audit subsystem. */
int agent_seen; /* Flag indicating that the gpg-agent has been
accessed. */
int with_colons; /* Use column delimited output format */
int with_secret; /* Mark secret keys in a public key listing. */
int with_chain; /* Include the certifying certs in a listing */
int with_validation;/* Validate each key while listing. */
int with_ephemeral_keys; /* Include ephemeral flagged keys in the
keylisting. */
int autodetect_encoding; /* Try to detect the input encoding */
int is_pem; /* Is in PEM format */
int is_base64; /* is in plain base-64 format */
int create_base64; /* Create base64 encoded output */
int create_pem; /* create PEM output */
const char *pem_name; /* PEM name to use */
int include_certs; /* -1 to send all certificates in the chain
along with a signature or the number of
certificates up the chain (0 = none, 1 = only
signer) */
int use_ocsp; /* Set to true if OCSP should be used. */
int validation_model; /* 0 := standard model (shell),
1 := chain model,
2 := STEED model. */
int offline; /* If true gpgsm won't do any network access. */
/* The current time. Used as a helper in certchain.c. */
ksba_isotime_t current_time;
};
/* An object to keep a list of certificates. */
struct certlist_s
{
struct certlist_s *next;
ksba_cert_t cert;
int is_encrypt_to; /* True if the certificate has been set through
the --encrypto-to option. */
int pk_algo; /* The PK_ALGO from CERT or 0 if not yet known. */
int hash_algo; /* Used to track the hash algorithm to use. */
const char *hash_algo_oid; /* And the corresponding OID. */
};
typedef struct certlist_s *certlist_t;
/* A structure carrying information about trusted root certificates. */
struct rootca_flags_s
{
unsigned int valid:1; /* The rest of the structure has valid
information. */
unsigned int relax:1; /* Relax checking of root certificates. */
unsigned int chain_model:1; /* Root requires the use of the chain model. */
};
/*-- gpgsm.c --*/
extern int gpgsm_errors_seen;
void gpgsm_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
int gpgsm_parse_validation_model (const char *model);
/*-- server.c --*/
void gpgsm_server (certlist_t default_recplist);
gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text);
gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0);
gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
gpg_err_code_t ec);
gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text,
gpg_error_t err);
+gpg_error_t gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total);
gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl,
const unsigned char *line);
/*-- fingerprint --*/
unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
unsigned char *array, int *r_len);
char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo);
char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo);
unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert,
unsigned long *r_high);
unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array);
char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert);
int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits);
int gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits,
char **r_curve);
int gpgsm_is_ecc_key (ksba_cert_t cert);
char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid);
char *gpgsm_get_certid (ksba_cert_t cert);
/*-- certdump.c --*/
void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p);
void gpgsm_print_serial_decimal (estream_t fp, ksba_const_sexp_t sn);
void gpgsm_print_time (estream_t fp, ksba_isotime_t t);
void gpgsm_print_name2 (FILE *fp, const char *string, int translate);
void gpgsm_print_name (FILE *fp, const char *string);
void gpgsm_es_print_name (estream_t fp, const char *string);
void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate);
void gpgsm_cert_log_name (const char *text, ksba_cert_t cert);
void gpgsm_dump_cert (const char *text, ksba_cert_t cert);
void gpgsm_dump_serial (ksba_const_sexp_t p);
void gpgsm_dump_time (ksba_isotime_t t);
void gpgsm_dump_string (const char *string);
char *gpgsm_format_serial (ksba_const_sexp_t p);
char *gpgsm_format_name2 (const char *name, int translate);
char *gpgsm_format_name (const char *name);
char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer);
char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert);
char *gpgsm_format_keydesc (ksba_cert_t cert);
/*-- certcheck.c --*/
int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert);
int gpgsm_check_cms_signature (ksba_cert_t cert, gcry_sexp_t sigval,
gcry_md_hd_t md,
int hash_algo, unsigned int pkalgoflags,
int *r_pkalgo);
/* fixme: move create functions to another file */
int gpgsm_create_cms_signature (ctrl_t ctrl,
ksba_cert_t cert, gcry_md_hd_t md, int mdalgo,
unsigned char **r_sigval);
/*-- certchain.c --*/
/* Flags used with gpgsm_validate_chain. */
#define VALIDATE_FLAG_NO_DIRMNGR 1
#define VALIDATE_FLAG_CHAIN_MODEL 2
#define VALIDATE_FLAG_STEED 4
int gpgsm_walk_cert_chain (ctrl_t ctrl,
ksba_cert_t start, ksba_cert_t *r_next);
int gpgsm_is_root_cert (ksba_cert_t cert);
int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert,
ksba_isotime_t checktime,
ksba_isotime_t r_exptime,
int listmode, estream_t listfp,
unsigned int flags, unsigned int *retflags);
int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert);
/*-- certlist.c --*/
int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent);
int gpgsm_cert_use_encrypt_p (ksba_cert_t cert);
int gpgsm_cert_use_verify_p (ksba_cert_t cert);
int gpgsm_cert_use_decrypt_p (ksba_cert_t cert);
int gpgsm_cert_use_cert_p (ksba_cert_t cert);
int gpgsm_cert_use_ocsp_p (ksba_cert_t cert);
int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert);
int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b);
int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert,
certlist_t *listaddr, int is_encrypt_to);
int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
certlist_t *listaddr, int is_encrypt_to);
void gpgsm_release_certlist (certlist_t list);
#define FIND_CERT_ALLOW_AMBIG 1
#define FIND_CERT_WITH_EPHEM 2
int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid,
ksba_cert_t *r_cert, unsigned int flags);
/*-- keylist.c --*/
gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names,
estream_t fp, unsigned int mode);
/*-- import.c --*/
int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode);
int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
int (*of)(const char *fname));
/*-- export.c --*/
void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream);
void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream,
int rawmode);
/*-- delete.c --*/
int gpgsm_delete (ctrl_t ctrl, strlist_t names);
/*-- verify.c --*/
int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp);
/*-- sign.c --*/
int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert);
int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
int data_fd, int detached, estream_t out_fp);
/*-- encrypt.c --*/
int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist,
int in_fd, estream_t out_fp);
/*-- decrypt.c --*/
gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd,
const char *wrap_algo_str,
unsigned int keylen,
const void *ukm, unsigned int ukmlen);
int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp);
/*-- certreqgen.c --*/
int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream);
/*-- certreqgen-ui.c --*/
void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t out_stream);
/*-- qualified.c --*/
gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert,
char *country);
gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert);
gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert);
/*-- call-agent.c --*/
int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
unsigned char *digest,
size_t digestlen,
int digestalgo,
unsigned char **r_buf, size_t *r_buflen);
int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
unsigned char *digest, size_t digestlen, int digestalgo,
unsigned char **r_buf, size_t *r_buflen);
int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
ksba_const_sexp_t ciphertext,
char **r_buf, size_t *r_buflen);
int gpgsm_agent_genkey (ctrl_t ctrl,
ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey);
int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
ksba_sexp_t *r_pubkey);
int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno);
int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list);
int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
struct rootca_flags_s *rootca_flags);
int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip);
int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
int gpgsm_agent_learn (ctrl_t ctrl);
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
char **r_serialno);
gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg,
int repeat, char **r_passphrase);
gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
void **r_kek, size_t *r_keklen);
gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl,
const void *key, size_t keylen);
gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip,
const char *desc,
unsigned char **r_result,
size_t *r_resultlen);
/*-- call-dirmngr.c --*/
int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
ksba_cert_t cert, ksba_cert_t issuer_cert,
int use_ocsp);
int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri,
int cache_only,
void (*cb)(void*, ksba_cert_t), void *cb_value);
int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
int argc, char **argv);
/*-- misc.c --*/
void setup_pinentry_env (void);
gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen,
int mdalgo,
unsigned char **r_newsigval,
size_t *r_newsigvallen);
gcry_sexp_t gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx);
int gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval,
unsigned int *r_pkalgo_flags);
#endif /*GPGSM_H*/
diff --git a/sm/server.c b/sm/server.c
index a0cd652e0..dc1bce246 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -1,1508 +1,1554 @@
/* server.c - Server mode and main entry point
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2010 Free Software Foundation, Inc.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <unistd.h>
#include "gpgsm.h"
#include <assuan.h>
#include "../common/sysutils.h"
#include "../common/server-help.h"
#include "../common/asshelp.h"
#include "../common/shareddefs.h"
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
/* The filepointer for status message used in non-server mode */
static FILE *statusfp;
/* Data used to assuciate an Assuan context with local server data */
struct server_local_s {
assuan_context_t assuan_ctx;
int message_fd;
int list_internal;
int list_external;
int list_to_output; /* Write keylistings to the output fd. */
int enable_audit_log; /* Use an audit log. */
certlist_t recplist;
certlist_t signerlist;
certlist_t default_recplist; /* As set by main() - don't release. */
int allow_pinentry_notify; /* Set if pinentry notifications should
be passed back to the client. */
int no_encrypt_to; /* Local version of option. */
};
/* Cookie definition for assuan data line output. */
static gpgrt_ssize_t data_line_cookie_write (void *cookie,
const void *buffer, size_t size);
static int data_line_cookie_close (void *cookie);
static es_cookie_io_functions_t data_line_cookie_functions =
{
NULL,
data_line_cookie_write,
NULL,
data_line_cookie_close
};
static int command_has_option (const char *cmd, const char *cmdopt);
/* Note that it is sufficient to allocate the target string D as
long as the source string S, i.e.: strlen(s)+1; */
static void
strcpy_escaped_plus (char *d, const char *s)
{
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d++ = xtoi_2 (s);
s += 2;
}
else if (*s == '+')
*d++ = ' ', s++;
else
*d++ = *s++;
}
*d = 0;
}
/* A write handler used by es_fopencookie to write assuan data
lines. */
static gpgrt_ssize_t
data_line_cookie_write (void *cookie, const void *buffer, size_t size)
{
assuan_context_t ctx = cookie;
if (assuan_send_data (ctx, buffer, size))
{
gpg_err_set_errno (EIO);
return -1;
}
return (gpgrt_ssize_t)size;
}
static int
data_line_cookie_close (void *cookie)
{
assuan_context_t ctx = cookie;
if (assuan_send_data (ctx, NULL, 0))
{
gpg_err_set_errno (EIO);
return -1;
}
return 0;
}
static void
close_message_fd (ctrl_t ctrl)
{
if (ctrl->server_local->message_fd != -1)
{
#ifdef HAVE_W32CE_SYSTEM
#warning Is this correct for W32/W32CE?
#endif
close (ctrl->server_local->message_fd);
ctrl->server_local->message_fd = -1;
}
}
/* Start a new audit session if this has been enabled. */
static gpg_error_t
start_audit_session (ctrl_t ctrl)
{
audit_release (ctrl->audit);
ctrl->audit = NULL;
if (ctrl->server_local->enable_audit_log && !(ctrl->audit = audit_new ()) )
return gpg_error_from_syserror ();
return 0;
}
static gpg_error_t
option_handler (assuan_context_t ctx, const char *key, const char *value)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
if (!strcmp (key, "putenv"))
{
/* Change the session's environment to be used for the
Pinentry. Valid values are:
<NAME> Delete envvar NAME
<KEY>= Set envvar NAME to the empty string
<KEY>=<VALUE> Set envvar NAME to VALUE
*/
err = session_env_putenv (opt.session_env, value);
}
else if (!strcmp (key, "display"))
{
err = session_env_setenv (opt.session_env, "DISPLAY", value);
}
else if (!strcmp (key, "ttyname"))
{
err = session_env_setenv (opt.session_env, "GPG_TTY", value);
}
else if (!strcmp (key, "ttytype"))
{
err = session_env_setenv (opt.session_env, "TERM", value);
}
else if (!strcmp (key, "lc-ctype"))
{
xfree (opt.lc_ctype);
opt.lc_ctype = xtrystrdup (value);
if (!opt.lc_ctype)
err = gpg_error_from_syserror ();
}
else if (!strcmp (key, "lc-messages"))
{
xfree (opt.lc_messages);
opt.lc_messages = xtrystrdup (value);
if (!opt.lc_messages)
err = gpg_error_from_syserror ();
}
else if (!strcmp (key, "xauthority"))
{
err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
}
else if (!strcmp (key, "pinentry-user-data"))
{
err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
}
else if (!strcmp (key, "include-certs"))
{
int i = *value? atoi (value) : -1;
if (ctrl->include_certs < -2)
err = gpg_error (GPG_ERR_ASS_PARAMETER);
else
ctrl->include_certs = i;
}
else if (!strcmp (key, "list-mode"))
{
int i = *value? atoi (value) : 0;
if (!i || i == 1) /* default and mode 1 */
{
ctrl->server_local->list_internal = 1;
ctrl->server_local->list_external = 0;
}
else if (i == 2)
{
ctrl->server_local->list_internal = 0;
ctrl->server_local->list_external = 1;
}
else if (i == 3)
{
ctrl->server_local->list_internal = 1;
ctrl->server_local->list_external = 1;
}
else
err = gpg_error (GPG_ERR_ASS_PARAMETER);
}
else if (!strcmp (key, "list-to-output"))
{
int i = *value? atoi (value) : 0;
ctrl->server_local->list_to_output = i;
}
else if (!strcmp (key, "with-validation"))
{
int i = *value? atoi (value) : 0;
ctrl->with_validation = i;
}
else if (!strcmp (key, "with-secret"))
{
int i = *value? atoi (value) : 0;
ctrl->with_secret = i;
}
else if (!strcmp (key, "validation-model"))
{
int i = gpgsm_parse_validation_model (value);
if ( i >= 0 && i <= 2 )
ctrl->validation_model = i;
else
err = gpg_error (GPG_ERR_ASS_PARAMETER);
}
else if (!strcmp (key, "with-key-data"))
{
opt.with_key_data = 1;
}
else if (!strcmp (key, "enable-audit-log"))
{
int i = *value? atoi (value) : 0;
ctrl->server_local->enable_audit_log = i;
}
else if (!strcmp (key, "allow-pinentry-notify"))
{
ctrl->server_local->allow_pinentry_notify = 1;
}
else if (!strcmp (key, "with-ephemeral-keys"))
{
int i = *value? atoi (value) : 0;
ctrl->with_ephemeral_keys = i;
}
else if (!strcmp (key, "no-encrypt-to"))
{
ctrl->server_local->no_encrypt_to = 1;
}
else if (!strcmp (key, "offline"))
{
/* We ignore this option if gpgsm has been started with
--disable-dirmngr (which also sets offline). */
if (!opt.disable_dirmngr)
{
int i = *value? !!atoi (value) : 1;
ctrl->offline = i;
}
}
else if (!strcmp (key, "request-origin"))
{
if (!opt.request_origin)
{
int i = parse_request_origin (value);
if (i == -1)
err = gpg_error (GPG_ERR_INV_VALUE);
else
opt.request_origin = i;
}
}
else
err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
return err;
}
static gpg_error_t
reset_notify (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
(void) line;
gpgsm_release_certlist (ctrl->server_local->recplist);
gpgsm_release_certlist (ctrl->server_local->signerlist);
ctrl->server_local->recplist = NULL;
ctrl->server_local->signerlist = NULL;
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return 0;
}
static gpg_error_t
input_notify (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
ctrl->autodetect_encoding = 0;
ctrl->is_pem = 0;
ctrl->is_base64 = 0;
if (strstr (line, "--armor"))
ctrl->is_pem = 1;
else if (strstr (line, "--base64"))
ctrl->is_base64 = 1;
else if (strstr (line, "--binary"))
;
else
ctrl->autodetect_encoding = 1;
return 0;
}
static gpg_error_t
output_notify (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
ctrl->create_pem = 0;
ctrl->create_base64 = 0;
if (strstr (line, "--armor"))
ctrl->create_pem = 1;
else if (strstr (line, "--base64"))
ctrl->create_base64 = 1; /* just the raw output */
return 0;
}
static const char hlp_recipient[] =
"RECIPIENT <userID>\n"
"\n"
"Set the recipient for the encryption. USERID shall be the\n"
"internal representation of the key; the server may accept any other\n"
"way of specification [we will support this]. If this is a valid and\n"
"trusted recipient the server does respond with OK, otherwise the\n"
"return is an ERR with the reason why the recipient can't be used,\n"
"the encryption will then not be done for this recipient. If the\n"
"policy is not to encrypt at all if not all recipients are valid, the\n"
"client has to take care of this. All RECIPIENT commands are\n"
"cumulative until a RESET or an successful ENCRYPT command.";
static gpg_error_t
cmd_recipient (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
if (!ctrl->audit)
rc = start_audit_session (ctrl);
else
rc = 0;
if (!rc)
rc = gpgsm_add_to_certlist (ctrl, line, 0,
&ctrl->server_local->recplist, 0);
if (rc)
{
gpgsm_status2 (ctrl, STATUS_INV_RECP,
get_inv_recpsgnr_code (rc), line, NULL);
}
return rc;
}
static const char hlp_signer[] =
"SIGNER <userID>\n"
"\n"
"Set the signer's keys for the signature creation. USERID should\n"
"be the internal representation of the key; the server may accept any\n"
"other way of specification [we will support this]. If this is a\n"
"valid and usable signing key the server does respond with OK,\n"
"otherwise it returns an ERR with the reason why the key can't be\n"
"used, the signing will then not be done for this key. If the policy\n"
"is not to sign at all if not all signer keys are valid, the client\n"
"has to take care of this. All SIGNER commands are cumulative until\n"
"a RESET but they are *not* reset by an SIGN command because it can\n"
"be expected that set of signers are used for more than one sign\n"
"operation.";
static gpg_error_t
cmd_signer (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
rc = gpgsm_add_to_certlist (ctrl, line, 1,
&ctrl->server_local->signerlist, 0);
if (rc)
{
gpgsm_status2 (ctrl, STATUS_INV_SGNR,
get_inv_recpsgnr_code (rc), line, NULL);
/* For compatibility reasons we also issue the old code after the
new one. */
gpgsm_status2 (ctrl, STATUS_INV_RECP,
get_inv_recpsgnr_code (rc), line, NULL);
}
return rc;
}
static const char hlp_encrypt[] =
"ENCRYPT \n"
"\n"
"Do the actual encryption process. Takes the plaintext from the INPUT\n"
"command, writes to the ciphertext to the file descriptor set with\n"
"the OUTPUT command, take the recipients form all the recipients set\n"
"so far. If this command fails the clients should try to delete all\n"
"output currently done or otherwise mark it as invalid. GPGSM does\n"
"ensure that there won't be any security problem with leftover data\n"
"on the output in this case.\n"
"\n"
"This command should in general not fail, as all necessary checks\n"
"have been done while setting the recipients. The input and output\n"
"pipes are closed.";
static gpg_error_t
cmd_encrypt (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
certlist_t cl;
int inp_fd, out_fd;
estream_t out_fp;
int rc;
(void)line;
inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
if (inp_fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (out_fd == -1)
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
out_fp = es_fdopen_nc (out_fd, "w");
if (!out_fp)
return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
/* Now add all encrypt-to marked recipients from the default
list. */
rc = 0;
if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to)
{
for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
if (cl->is_encrypt_to)
rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
&ctrl->server_local->recplist, 1);
}
if (!rc)
rc = ctrl->audit? 0 : start_audit_session (ctrl);
if (!rc)
rc = gpgsm_encrypt (assuan_get_pointer (ctx),
ctrl->server_local->recplist,
inp_fd, out_fp);
es_fclose (out_fp);
gpgsm_release_certlist (ctrl->server_local->recplist);
ctrl->server_local->recplist = NULL;
/* Close and reset the fd */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_decrypt[] =
"DECRYPT\n"
"\n"
"This performs the decrypt operation after doing some check on the\n"
"internal state. (e.g. that only needed data has been set). Because\n"
"it utilizes the GPG-Agent for the session key decryption, there is\n"
"no need to ask the client for a protecting passphrase - GPG-Agent\n"
"does take care of this by requesting this from the user.";
static gpg_error_t
cmd_decrypt (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int inp_fd, out_fd;
estream_t out_fp;
int rc;
(void)line;
inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
if (inp_fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (out_fd == -1)
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
out_fp = es_fdopen_nc (out_fd, "w");
if (!out_fp)
return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
rc = start_audit_session (ctrl);
if (!rc)
rc = gpgsm_decrypt (ctrl, inp_fd, out_fp);
es_fclose (out_fp);
/* Close and reset the fds. */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_verify[] =
"VERIFY\n"
"\n"
"This does a verify operation on the message send to the input FD.\n"
"The result is written out using status lines. If an output FD was\n"
"given, the signed text will be written to that.\n"
"\n"
"If the signature is a detached one, the server will inquire about\n"
"the signed material and the client must provide it.";
static gpg_error_t
cmd_verify (assuan_context_t ctx, char *line)
{
int rc;
ctrl_t ctrl = assuan_get_pointer (ctx);
int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
estream_t out_fp = NULL;
(void)line;
if (fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
if (out_fd != -1)
{
out_fp = es_fdopen_nc (out_fd, "w");
if (!out_fp)
return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
}
rc = start_audit_session (ctrl);
if (!rc)
rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
ctrl->server_local->message_fd, out_fp);
es_fclose (out_fp);
/* Close and reset the fd. */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_sign[] =
"SIGN [--detached]\n"
"\n"
"Sign the data set with the INPUT command and write it to the sink\n"
"set by OUTPUT. With \"--detached\", a detached signature is\n"
"created (surprise).";
static gpg_error_t
cmd_sign (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int inp_fd, out_fd;
estream_t out_fp;
int detached;
int rc;
inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
if (inp_fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (out_fd == -1)
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
detached = has_option (line, "--detached");
out_fp = es_fdopen_nc (out_fd, "w");
if (!out_fp)
return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
rc = start_audit_session (ctrl);
if (!rc)
rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
inp_fd, detached, out_fp);
es_fclose (out_fp);
/* close and reset the fd */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_import[] =
"IMPORT [--re-import]\n"
"\n"
"Import the certificates read form the input-fd, return status\n"
"message for each imported one. The import checks the validity of\n"
"the certificate but not of the entire chain. It is possible to\n"
"import expired certificates.\n"
"\n"
"With the option --re-import the input data is expected to a be a LF\n"
"separated list of fingerprints. The command will re-import these\n"
"certificates, meaning that they are made permanent by removing\n"
"their ephemeral flag.";
static gpg_error_t
cmd_import (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
int reimport = has_option (line, "--re-import");
(void)line;
if (fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport);
/* close and reset the fd */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_export[] =
"EXPORT [--data [--armor|--base64]] [--secret [--(raw|pkcs12)] [--] <pattern>\n"
"\n"
"Export the certificates selected by PATTERN. With --data the output\n"
"is returned using Assuan D lines; the default is to use the sink given\n"
"by the last \"OUTPUT\" command. The options --armor or --base64 encode \n"
"the output using the PEM respective a plain base-64 format; the default\n"
"is a binary format which is only suitable for a single certificate.\n"
"With --secret the secret key is exported using the PKCS#8 format,\n"
"with --raw using PKCS#1, and with --pkcs12 as full PKCS#12 container.";
static gpg_error_t
cmd_export (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
char *p;
strlist_t list, sl;
int use_data;
int opt_secret;
int opt_raw = 0;
int opt_pkcs12 = 0;
use_data = has_option (line, "--data");
if (use_data)
{
/* We need to override any possible setting done by an OUTPUT command. */
ctrl->create_pem = has_option (line, "--armor");
ctrl->create_base64 = has_option (line, "--base64");
}
opt_secret = has_option (line, "--secret");
if (opt_secret)
{
opt_raw = has_option (line, "--raw");
opt_pkcs12 = has_option (line, "--pkcs12");
}
line = skip_options (line);
/* Break the line down into an strlist_t. */
list = NULL;
for (p=line; *p; line = p)
{
while (*p && *p != ' ')
p++;
if (*p)
*p++ = 0;
if (*line)
{
sl = xtrymalloc (sizeof *sl + strlen (line));
if (!sl)
{
free_strlist (list);
return out_of_core ();
}
sl->flags = 0;
strcpy_escaped_plus (sl->d, line);
sl->next = list;
list = sl;
}
}
if (opt_secret)
{
if (!list)
return set_error (GPG_ERR_NO_DATA, "No key given");
if (!*list->d)
{
free_strlist (list);
return set_error (GPG_ERR_NO_DATA, "No key given");
}
if (list->next)
return set_error (GPG_ERR_TOO_MANY, "Only one key allowed");
}
if (use_data)
{
estream_t stream;
stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
if (!stream)
{
free_strlist (list);
return set_error (GPG_ERR_ASS_GENERAL,
"error setting up a data stream");
}
if (opt_secret)
gpgsm_p12_export (ctrl, list->d, stream,
opt_raw? 2 : opt_pkcs12 ? 0 : 1);
else
gpgsm_export (ctrl, list, stream);
es_fclose (stream);
}
else
{
int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
estream_t out_fp;
if (fd == -1)
{
free_strlist (list);
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
}
out_fp = es_fdopen_nc (fd, "w");
if (!out_fp)
{
free_strlist (list);
return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
}
if (opt_secret)
gpgsm_p12_export (ctrl, list->d, out_fp,
opt_raw? 2 : opt_pkcs12 ? 0 : 1);
else
gpgsm_export (ctrl, list, out_fp);
es_fclose (out_fp);
}
free_strlist (list);
/* Close and reset the fds. */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return 0;
}
static const char hlp_delkeys[] =
"DELKEYS <patterns>\n"
"\n"
"Delete the certificates specified by PATTERNS. Each pattern shall be\n"
"a percent-plus escaped certificate specification. Usually a\n"
"fingerprint will be used for this.";
static gpg_error_t
cmd_delkeys (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
char *p;
strlist_t list, sl;
int rc;
/* break the line down into an strlist_t */
list = NULL;
for (p=line; *p; line = p)
{
while (*p && *p != ' ')
p++;
if (*p)
*p++ = 0;
if (*line)
{
sl = xtrymalloc (sizeof *sl + strlen (line));
if (!sl)
{
free_strlist (list);
return out_of_core ();
}
sl->flags = 0;
strcpy_escaped_plus (sl->d, line);
sl->next = list;
list = sl;
}
}
rc = gpgsm_delete (ctrl, list);
free_strlist (list);
/* close and reset the fd */
close_message_fd (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_output[] =
"OUTPUT FD[=<n>]\n"
"\n"
"Set the file descriptor to write the output data to N. If N is not\n"
"given and the operating system supports file descriptor passing, the\n"
"file descriptor currently in flight will be used. See also the\n"
"\"INPUT\" and \"MESSAGE\" commands.";
static const char hlp_input[] =
"INPUT FD[=<n>]\n"
"\n"
"Set the file descriptor to read the input data to N. If N is not\n"
"given and the operating system supports file descriptor passing, the\n"
"file descriptor currently in flight will be used. See also the\n"
"\"MESSAGE\" and \"OUTPUT\" commands.";
static const char hlp_message[] =
"MESSAGE FD[=<n>]\n"
"\n"
"Set the file descriptor to read the message for a detached\n"
"signatures to N. If N is not given and the operating system\n"
"supports file descriptor passing, the file descriptor currently in\n"
"flight will be used. See also the \"INPUT\" and \"OUTPUT\" commands.";
static gpg_error_t
cmd_message (assuan_context_t ctx, char *line)
{
int rc;
gnupg_fd_t sysfd;
int fd;
ctrl_t ctrl = assuan_get_pointer (ctx);
rc = assuan_command_parse_fd (ctx, line, &sysfd);
if (rc)
return rc;
#ifdef HAVE_W32CE_SYSTEM
sysfd = _assuan_w32ce_finish_pipe ((int)sysfd, 0);
if (sysfd == INVALID_HANDLE_VALUE)
return set_error (gpg_err_code_from_syserror (),
"rvid conversion failed");
#endif
fd = translate_sys2libc_fd (sysfd, 0);
if (fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
ctrl->server_local->message_fd = fd;
return 0;
}
static const char hlp_listkeys[] =
"LISTKEYS [<patterns>]\n"
"LISTSECRETKEYS [<patterns>]\n"
"DUMPKEYS [<patterns>]\n"
"DUMPSECRETKEYS [<patterns>]\n"
"\n"
"List all certificates or only those specified by PATTERNS. Each\n"
"pattern shall be a percent-plus escaped certificate specification.\n"
"The \"SECRET\" versions of the command filter the output to include\n"
"only certificates where the secret key is available or a corresponding\n"
"smartcard has been registered. The \"DUMP\" versions of the command\n"
"are only useful for debugging. The output format is a percent escaped\n"
"colon delimited listing as described in the manual.\n"
"\n"
"These \"OPTION\" command keys effect the output::\n"
"\n"
" \"list-mode\" set to 0: List only local certificates (default).\n"
" 1: Ditto.\n"
" 2: List only external certificates.\n"
" 3: List local and external certificates.\n"
"\n"
" \"with-validation\" set to true: Validate each certificate.\n"
"\n"
" \"with-ephemeral-key\" set to true: Always include ephemeral\n"
" certificates.\n"
"\n"
" \"list-to-output\" set to true: Write output to the file descriptor\n"
" given by the last \"OUTPUT\" command.";
static int
do_listkeys (assuan_context_t ctx, char *line, int mode)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
estream_t fp;
char *p;
strlist_t list, sl;
unsigned int listmode;
gpg_error_t err;
/* Break the line down into an strlist. */
list = NULL;
for (p=line; *p; line = p)
{
while (*p && *p != ' ')
p++;
if (*p)
*p++ = 0;
if (*line)
{
sl = xtrymalloc (sizeof *sl + strlen (line));
if (!sl)
{
free_strlist (list);
return out_of_core ();
}
sl->flags = 0;
strcpy_escaped_plus (sl->d, line);
sl->next = list;
list = sl;
}
}
if (ctrl->server_local->list_to_output)
{
int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if ( outfd == -1 )
{
free_strlist (list);
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
}
fp = es_fdopen_nc (outfd, "w");
if (!fp)
{
free_strlist (list);
return set_error (gpg_err_code_from_syserror (),
"es_fdopen() failed");
}
}
else
{
fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
if (!fp)
{
free_strlist (list);
return set_error (GPG_ERR_ASS_GENERAL,
"error setting up a data stream");
}
}
ctrl->with_colons = 1;
listmode = mode;
if (ctrl->server_local->list_internal)
listmode |= (1<<6);
if (ctrl->server_local->list_external)
listmode |= (1<<7);
err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
free_strlist (list);
es_fclose (fp);
if (ctrl->server_local->list_to_output)
assuan_close_output_fd (ctx);
return err;
}
static gpg_error_t
cmd_listkeys (assuan_context_t ctx, char *line)
{
return do_listkeys (ctx, line, 3);
}
static gpg_error_t
cmd_dumpkeys (assuan_context_t ctx, char *line)
{
return do_listkeys (ctx, line, 259);
}
static gpg_error_t
cmd_listsecretkeys (assuan_context_t ctx, char *line)
{
return do_listkeys (ctx, line, 2);
}
static gpg_error_t
cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
{
return do_listkeys (ctx, line, 258);
}
static const char hlp_genkey[] =
"GENKEY\n"
"\n"
"Read the parameters in native format from the input fd and write a\n"
"certificate request to the output.";
static gpg_error_t
cmd_genkey (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int inp_fd, out_fd;
estream_t in_stream, out_stream;
int rc;
(void)line;
inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
if (inp_fd == -1)
return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (out_fd == -1)
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
in_stream = es_fdopen_nc (inp_fd, "r");
if (!in_stream)
return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");
out_stream = es_fdopen_nc (out_fd, "w");
if (!out_stream)
{
es_fclose (in_stream);
return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
}
rc = gpgsm_genkey (ctrl, in_stream, out_stream);
es_fclose (out_stream);
es_fclose (in_stream);
/* close and reset the fds */
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_getauditlog[] =
"GETAUDITLOG [--data] [--html]\n"
"\n"
"If --data is used, the output is send using D-lines and not to the\n"
"file descriptor given by an OUTPUT command.\n"
"\n"
"If --html is used the output is formatted as an XHTML block. This is\n"
"designed to be incorporated into a HTML document.";
static gpg_error_t
cmd_getauditlog (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int out_fd;
estream_t out_stream;
int opt_data, opt_html;
int rc;
opt_data = has_option (line, "--data");
opt_html = has_option (line, "--html");
/* Not needed: line = skip_options (line); */
if (!ctrl->audit)
return gpg_error (GPG_ERR_NO_DATA);
if (opt_data)
{
out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
if (!out_stream)
return set_error (GPG_ERR_ASS_GENERAL,
"error setting up a data stream");
}
else
{
out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
if (out_fd == -1)
return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
out_stream = es_fdopen_nc (out_fd, "w");
if (!out_stream)
{
return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
}
}
audit_print_result (ctrl->audit, out_stream, opt_html);
rc = 0;
es_fclose (out_stream);
/* Close and reset the fd. */
if (!opt_data)
assuan_close_output_fd (ctx);
return rc;
}
static const char hlp_getinfo[] =
"GETINFO <what>\n"
"\n"
"Multipurpose function to return a variety of information.\n"
"Supported values for WHAT are:\n"
"\n"
" version - Return the version of the program.\n"
" pid - Return the process id of the server.\n"
" agent-check - Return success if the agent is running.\n"
" cmd_has_option CMD OPT\n"
" - Returns OK if the command CMD implements the option OPT.\n"
" offline - Returns OK if the connection is in offline mode.";
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc = 0;
if (!strcmp (line, "version"))
{
const char *s = VERSION;
rc = assuan_send_data (ctx, s, strlen (s));
}
else if (!strcmp (line, "pid"))
{
char numbuf[50];
snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
}
else if (!strcmp (line, "agent-check"))
{
rc = gpgsm_agent_send_nop (ctrl);
}
else if (!strncmp (line, "cmd_has_option", 14)
&& (line[14] == ' ' || line[14] == '\t' || !line[14]))
{
char *cmd, *cmdopt;
line += 14;
while (*line == ' ' || *line == '\t')
line++;
if (!*line)
rc = gpg_error (GPG_ERR_MISSING_VALUE);
else
{
cmd = line;
while (*line && (*line != ' ' && *line != '\t'))
line++;
if (!*line)
rc = gpg_error (GPG_ERR_MISSING_VALUE);
else
{
*line++ = 0;
while (*line == ' ' || *line == '\t')
line++;
if (!*line)
rc = gpg_error (GPG_ERR_MISSING_VALUE);
else
{
cmdopt = line;
if (!command_has_option (cmd, cmdopt))
rc = gpg_error (GPG_ERR_FALSE);
}
}
}
}
else if (!strcmp (line, "offline"))
{
rc = ctrl->offline? 0 : gpg_error (GPG_ERR_FALSE);
}
else
rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
return rc;
}
static const char hlp_passwd[] =
"PASSWD <userID>\n"
"\n"
"Change the passphrase of the secret key for USERID.";
static gpg_error_t
cmd_passwd (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
ksba_cert_t cert = NULL;
char *grip = NULL;
line = skip_options (line);
err = gpgsm_find_cert (ctrl, line, NULL, &cert, 0);
if (err)
;
else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
err = gpg_error (GPG_ERR_INTERNAL);
else
{
char *desc = gpgsm_format_keydesc (cert);
err = gpgsm_agent_passwd (ctrl, grip, desc);
xfree (desc);
}
xfree (grip);
ksba_cert_release (cert);
return err;
}
/* Return true if the command CMD implements the option OPT. */
static int
command_has_option (const char *cmd, const char *cmdopt)
{
if (!strcmp (cmd, "IMPORT"))
{
if (!strcmp (cmdopt, "re-import"))
return 1;
}
return 0;
}
/* Tell the assuan library about our commands */
static int
register_commands (assuan_context_t ctx)
{
static struct {
const char *name;
assuan_handler_t handler;
const char * const help;
} table[] = {
{ "RECIPIENT", cmd_recipient, hlp_recipient },
{ "SIGNER", cmd_signer, hlp_signer },
{ "ENCRYPT", cmd_encrypt, hlp_encrypt },
{ "DECRYPT", cmd_decrypt, hlp_decrypt },
{ "VERIFY", cmd_verify, hlp_verify },
{ "SIGN", cmd_sign, hlp_sign },
{ "IMPORT", cmd_import, hlp_import },
{ "EXPORT", cmd_export, hlp_export },
{ "INPUT", NULL, hlp_input },
{ "OUTPUT", NULL, hlp_output },
{ "MESSAGE", cmd_message, hlp_message },
{ "LISTKEYS", cmd_listkeys, hlp_listkeys },
{ "DUMPKEYS", cmd_dumpkeys, hlp_listkeys },
{ "LISTSECRETKEYS",cmd_listsecretkeys, hlp_listkeys },
{ "DUMPSECRETKEYS",cmd_dumpsecretkeys, hlp_listkeys },
{ "GENKEY", cmd_genkey, hlp_genkey },
{ "DELKEYS", cmd_delkeys, hlp_delkeys },
{ "GETAUDITLOG", cmd_getauditlog, hlp_getauditlog },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
{ "PASSWD", cmd_passwd, hlp_passwd },
{ NULL }
};
int i, rc;
for (i=0; table[i].name; i++)
{
rc = assuan_register_command (ctx, table[i].name, table[i].handler,
table[i].help);
if (rc)
return rc;
}
return 0;
}
/* Startup the server. DEFAULT_RECPLIST is the list of recipients as
set from the command line or config file. We only require those
marked as encrypt-to. */
void
gpgsm_server (certlist_t default_recplist)
{
int rc;
assuan_fd_t filedes[2];
assuan_context_t ctx;
struct server_control_s ctrl;
static const char hello[] = ("GNU Privacy Guard's S/M server "
VERSION " ready");
memset (&ctrl, 0, sizeof ctrl);
gpgsm_init_default_ctrl (&ctrl);
/* We use a pipe based server so that we can work from scripts.
assuan_init_pipe_server will automagically detect when we are
called with a socketpair and ignore FILEDES in this case. */
#ifdef HAVE_W32CE_SYSTEM
#define SERVER_STDIN es_fileno(es_stdin)
#define SERVER_STDOUT es_fileno(es_stdout)
#else
#define SERVER_STDIN 0
#define SERVER_STDOUT 1
#endif
filedes[0] = assuan_fdopen (SERVER_STDIN);
filedes[1] = assuan_fdopen (SERVER_STDOUT);
rc = assuan_new (&ctx);
if (rc)
{
log_error ("failed to allocate assuan context: %s\n",
gpg_strerror (rc));
gpgsm_exit (2);
}
rc = assuan_init_pipe_server (ctx, filedes);
if (rc)
{
log_error ("failed to initialize the server: %s\n",
gpg_strerror (rc));
gpgsm_exit (2);
}
rc = register_commands (ctx);
if (rc)
{
log_error ("failed to the register commands with Assuan: %s\n",
gpg_strerror(rc));
gpgsm_exit (2);
}
if (opt.verbose || opt.debug)
{
char *tmp;
/* Fixme: Use the really used socket name. */
if (asprintf (&tmp,
"Home: %s\n"
"Config: %s\n"
"DirmngrInfo: %s\n"
"%s",
gnupg_homedir (),
opt.config_filename,
dirmngr_socket_name (),
hello) > 0)
{
assuan_set_hello_line (ctx, tmp);
free (tmp);
}
}
else
assuan_set_hello_line (ctx, hello);
assuan_register_reset_notify (ctx, reset_notify);
assuan_register_input_notify (ctx, input_notify);
assuan_register_output_notify (ctx, output_notify);
assuan_register_option_handler (ctx, option_handler);
assuan_set_pointer (ctx, &ctrl);
ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
ctrl.server_local->assuan_ctx = ctx;
ctrl.server_local->message_fd = -1;
ctrl.server_local->list_internal = 1;
ctrl.server_local->list_external = 0;
ctrl.server_local->default_recplist = default_recplist;
for (;;)
{
rc = assuan_accept (ctx);
if (rc == -1)
{
break;
}
else if (rc)
{
log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
break;
}
rc = assuan_process (ctx);
if (rc)
{
log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
continue;
}
}
gpgsm_release_certlist (ctrl.server_local->recplist);
ctrl.server_local->recplist = NULL;
gpgsm_release_certlist (ctrl.server_local->signerlist);
ctrl.server_local->signerlist = NULL;
xfree (ctrl.server_local);
audit_release (ctrl.audit);
ctrl.audit = NULL;
assuan_release (ctx);
}
gpg_error_t
gpgsm_status2 (ctrl_t ctrl, int no, ...)
{
gpg_error_t err = 0;
va_list arg_ptr;
const char *text;
va_start (arg_ptr, no);
if (ctrl->no_server && ctrl->status_fd == -1)
; /* No status wanted. */
else if (ctrl->no_server)
{
if (!statusfp)
{
if (ctrl->status_fd == 1)
statusfp = stdout;
else if (ctrl->status_fd == 2)
statusfp = stderr;
else
statusfp = fdopen (ctrl->status_fd, "w");
if (!statusfp)
{
log_fatal ("can't open fd %d for status output: %s\n",
ctrl->status_fd, strerror(errno));
}
}
fputs ("[GNUPG:] ", statusfp);
fputs (get_status_string (no), statusfp);
while ( (text = va_arg (arg_ptr, const char*) ))
{
putc ( ' ', statusfp );
for (; *text; text++)
{
if (*text == '\n')
fputs ( "\\n", statusfp );
else if (*text == '\r')
fputs ( "\\r", statusfp );
else
putc ( *(const byte *)text, statusfp );
}
}
putc ('\n', statusfp);
- fflush (statusfp);
+ if (ferror (statusfp))
+ err = gpg_error_from_syserror ();
+ else
+ {
+ fflush (statusfp);
+ if (ferror (statusfp))
+ err = gpg_error_from_syserror ();
+ }
}
else
{
err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
get_status_string (no), arg_ptr);
}
va_end (arg_ptr);
return err;
}
gpg_error_t
gpgsm_status (ctrl_t ctrl, int no, const char *text)
{
return gpgsm_status2 (ctrl, no, text, NULL);
}
gpg_error_t
gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
gpg_err_code_t ec)
{
char buf[30];
snprintf (buf, sizeof buf, "%u", (unsigned int)ec);
if (text)
return gpgsm_status2 (ctrl, no, text, buf, NULL);
else
return gpgsm_status2 (ctrl, no, buf, NULL);
}
gpg_error_t
gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text,
gpg_error_t err)
{
char buf[30];
snprintf (buf, sizeof buf, "%u", err);
if (text)
return gpgsm_status2 (ctrl, no, text, buf, NULL);
else
return gpgsm_status2 (ctrl, no, buf, NULL);
}
+/* This callback is used to emit progress status lines. */
+gpg_error_t
+gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total)
+{
+ char buffer[60];
+ char units[] = "BKMGTPEZY?";
+ int unitidx = 0;
+
+ if (total)
+ {
+ if (current > total)
+ current = total;
+
+ while (total > 1024*1024)
+ {
+ total /= 1024;
+ current /= 1024;
+ unitidx++;
+ }
+ }
+ else
+ {
+ while (current > 1024*1024)
+ {
+ current /= 1024;
+ unitidx++;
+ }
+ }
+
+ if (unitidx > 9)
+ unitidx = 9;
+
+ snprintf (buffer, sizeof buffer, "? %lu %lu %c%s",
+ (unsigned long)current, (unsigned long)total,
+ units[unitidx], unitidx? "iB" : "");
+ return gpgsm_status2 (ctrl, STATUS_PROGRESS, "?", buffer, NULL);
+}
+
+
/* Helper to notify the client about Pinentry events. Because that
might disturb some older clients, this is only done when enabled
via an option. Returns an gpg error code. */
gpg_error_t
gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
{
if (!ctrl || !ctrl->server_local
|| !ctrl->server_local->allow_pinentry_notify)
return 0;
return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
}
diff --git a/sm/sign.c b/sm/sign.c
index db8ef0226..5b442b9a7 100644
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -1,850 +1,852 @@
/* sign.c - Sign a message
* Copyright (C) 2001, 2002, 2003, 2008,
* 2010 Free Software Foundation, Inc.
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include "gpgsm.h"
#include <gcrypt.h>
#include <ksba.h>
#include "keydb.h"
#include "../common/i18n.h"
/* Hash the data and return if something was hashed. Return -1 on error. */
static int
hash_data (int fd, gcry_md_hd_t md)
{
estream_t fp;
char buffer[4096];
int nread;
int rc = 0;
fp = es_fdopen_nc (fd, "rb");
if (!fp)
{
log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
return -1;
}
do
{
nread = es_fread (buffer, 1, DIM(buffer), fp);
gcry_md_write (md, buffer, nread);
}
while (nread);
if (es_ferror (fp))
{
log_error ("read error on fd %d: %s\n", fd, strerror (errno));
rc = -1;
}
es_fclose (fp);
return rc;
}
static int
hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer)
{
gpg_error_t err;
estream_t fp;
char buffer[4096];
int nread;
int rc = 0;
int any = 0;
fp = es_fdopen_nc (fd, "rb");
if (!fp)
{
gpg_error_t tmperr = gpg_error_from_syserror ();
log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
return tmperr;
}
do
{
nread = es_fread (buffer, 1, DIM(buffer), fp);
if (nread)
{
any = 1;
gcry_md_write (md, buffer, nread);
err = ksba_writer_write_octet_string (writer, buffer, nread, 0);
if (err)
{
log_error ("write failed: %s\n", gpg_strerror (err));
rc = err;
}
}
}
while (nread && !rc);
if (es_ferror (fp))
{
rc = gpg_error_from_syserror ();
log_error ("read error on fd %d: %s\n", fd, strerror (errno));
}
es_fclose (fp);
if (!any)
{
/* We can't allow signing an empty message because it does not
make much sense and more seriously, ksba_cms_build has
already written the tag for data and now expects an octet
string and an octet string of size 0 is illegal. */
log_error ("cannot sign an empty message\n");
rc = gpg_error (GPG_ERR_NO_DATA);
}
if (!rc)
{
err = ksba_writer_write_octet_string (writer, NULL, 0, 1);
if (err)
{
log_error ("write failed: %s\n", gpg_strerror (err));
rc = err;
}
}
return rc;
}
/* Get the default certificate which is defined as the first
certificate capable of signing returned by the keyDB and has a
secret key available. */
int
gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert)
{
KEYDB_HANDLE hd;
ksba_cert_t cert = NULL;
int rc;
char *p;
hd = keydb_new ();
if (!hd)
return gpg_error (GPG_ERR_GENERAL);
rc = keydb_search_first (ctrl, hd);
if (rc)
{
keydb_release (hd);
return rc;
}
do
{
rc = keydb_get_cert (hd, &cert);
if (rc)
{
log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
keydb_release (hd);
return rc;
}
if (!gpgsm_cert_use_sign_p (cert, 1))
{
p = gpgsm_get_keygrip_hexstring (cert);
if (p)
{
if (!gpgsm_agent_havekey (ctrl, p))
{
xfree (p);
keydb_release (hd);
*r_cert = cert;
return 0; /* got it */
}
xfree (p);
}
}
ksba_cert_release (cert);
cert = NULL;
}
while (!(rc = keydb_search_next (ctrl, hd)));
if (rc && rc != -1)
log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
ksba_cert_release (cert);
keydb_release (hd);
return rc;
}
static ksba_cert_t
get_default_signer (ctrl_t ctrl)
{
KEYDB_SEARCH_DESC desc;
ksba_cert_t cert = NULL;
KEYDB_HANDLE kh = NULL;
int rc;
if (!opt.local_user)
{
rc = gpgsm_get_default_cert (ctrl, &cert);
if (rc)
{
if (rc != -1)
log_debug ("failed to find default certificate: %s\n",
gpg_strerror (rc));
return NULL;
}
return cert;
}
rc = classify_user_id (opt.local_user, &desc, 0);
if (rc)
{
log_error ("failed to find default signer: %s\n", gpg_strerror (rc));
return NULL;
}
kh = keydb_new ();
if (!kh)
return NULL;
rc = keydb_search (ctrl, kh, &desc, 1);
if (rc)
{
log_debug ("failed to find default certificate: rc=%d\n", rc);
}
else
{
rc = keydb_get_cert (kh, &cert);
if (rc)
{
log_debug ("failed to get cert: rc=%d\n", rc);
}
}
keydb_release (kh);
return cert;
}
/* Depending on the options in CTRL add the certificate CERT as well as
other certificate up in the chain to the Root-CA to the CMS
object. */
static int
add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert)
{
gpg_error_t err;
int rc = 0;
ksba_cert_t next = NULL;
int n;
int not_root = 0;
ksba_cert_ref (cert);
n = ctrl->include_certs;
log_debug ("adding certificates at level %d\n", n);
if (n == -2)
{
not_root = 1;
n = -1;
}
if (n < 0 || n > 50)
n = 50; /* We better apply an upper bound */
/* First add my own certificate unless we don't want any certificate
included at all. */
if (n)
{
if (not_root && gpgsm_is_root_cert (cert))
err = 0;
else
err = ksba_cms_add_cert (cms, cert);
if (err)
goto ksba_failure;
if (n>0)
n--;
}
/* Walk the chain to include all other certificates. Note that a -1
used for N makes sure that there is no limit and all certs get
included. */
while ( n-- && !(rc = gpgsm_walk_cert_chain (ctrl, cert, &next)) )
{
if (not_root && gpgsm_is_root_cert (next))
err = 0;
else
err = ksba_cms_add_cert (cms, next);
ksba_cert_release (cert);
cert = next; next = NULL;
if (err)
goto ksba_failure;
}
ksba_cert_release (cert);
return gpg_err_code (rc) == GPG_ERR_NOT_FOUND? 0 : rc;
ksba_failure:
ksba_cert_release (cert);
log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err));
return err;
}
/* Perform a sign operation.
Sign the data received on DATA-FD in embedded mode or in detached
mode when DETACHED is true. Write the signature to OUT_FP. The
keys used to sign are taken from SIGNERLIST or the default one will
be used if the value of this argument is NULL. */
int
gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
int data_fd, int detached, estream_t out_fp)
{
int i, rc;
gpg_error_t err;
gnupg_ksba_io_t b64writer = NULL;
ksba_writer_t writer;
ksba_cms_t cms = NULL;
ksba_stop_reason_t stopreason;
KEYDB_HANDLE kh = NULL;
gcry_md_hd_t data_md = NULL;
int signer;
const char *algoid;
int algo;
ksba_isotime_t signed_at;
certlist_t cl;
int release_signerlist = 0;
audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN);
kh = keydb_new ();
if (!kh)
{
log_error (_("failed to allocate keyDB handle\n"));
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
if (!gnupg_rng_is_compliant (opt.compliance))
{
rc = gpg_error (GPG_ERR_FORBIDDEN);
log_error (_("%s is not compliant with %s mode\n"),
"RNG",
gnupg_compliance_option_string (opt.compliance));
gpgsm_status_with_error (ctrl, STATUS_ERROR,
"random-compliance", rc);
goto leave;
}
ctrl->pem_name = "SIGNED MESSAGE";
rc = gnupg_ksba_create_writer
(&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
ctrl->pem_name, out_fp, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gpg_strerror (rc));
goto leave;
}
+ gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
+
err = ksba_cms_new (&cms);
if (err)
{
rc = err;
goto leave;
}
err = ksba_cms_set_reader_writer (cms, NULL, writer);
if (err)
{
log_debug ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
/* We are going to create signed data with data as encap. content */
err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA);
if (!err)
err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA);
if (err)
{
log_debug ("ksba_cms_set_content_type failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
/* If no list of signers is given, use the default certificate. */
if (!signerlist)
{
ksba_cert_t cert = get_default_signer (ctrl);
if (!cert)
{
log_error ("no default signer found\n");
gpgsm_status2 (ctrl, STATUS_INV_SGNR,
get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL);
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
/* Although we don't check for ambiguous specification we will
check that the signer's certificate is usable and valid. */
rc = gpgsm_cert_use_sign_p (cert, 0);
if (!rc)
rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
if (rc)
{
char *tmpfpr;
tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0);
gpgsm_status2 (ctrl, STATUS_INV_SGNR,
get_inv_recpsgnr_code (rc), tmpfpr, NULL);
xfree (tmpfpr);
goto leave;
}
/* That one is fine - create signerlist. */
signerlist = xtrycalloc (1, sizeof *signerlist);
if (!signerlist)
{
rc = out_of_core ();
ksba_cert_release (cert);
goto leave;
}
signerlist->cert = cert;
release_signerlist = 1;
}
/* Figure out the hash algorithm to use. We do not want to use the
one for the certificate but if possible an OID for the plain
algorithm. */
if (opt.forced_digest_algo && opt.verbose)
log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo);
for (i=0, cl=signerlist; cl; cl = cl->next, i++)
{
const char *oid;
unsigned int nbits;
int pk_algo;
pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits);
cl->pk_algo = pk_algo;
if (opt.forced_digest_algo)
{
oid = NULL;
cl->hash_algo = opt.forced_digest_algo;
}
else
{
if (pk_algo == GCRY_PK_ECC)
{
/* Map the Curve to a corresponding hash algo. */
if (nbits <= 256)
oid = "2.16.840.1.101.3.4.2.1"; /* sha256 */
else if (nbits <= 384)
oid = "2.16.840.1.101.3.4.2.2"; /* sha384 */
else
oid = "2.16.840.1.101.3.4.2.3"; /* sha512 */
}
else
{
/* For RSA we reuse the hash algo used by the certificate. */
oid = ksba_cert_get_digest_algo (cl->cert);
}
cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
}
switch (cl->hash_algo)
{
case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break;
case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break;
case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break;
case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break;
case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break;
case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break;
case GCRY_MD_MD5: /* We don't want to use MD5. */
case 0: /* No algorithm found in cert. */
default: /* Other algorithms. */
log_info (_("hash algorithm %d (%s) for signer %d not supported;"
" using %s\n"),
cl->hash_algo, oid? oid: "?", i,
gcry_md_algo_name (GCRY_MD_SHA1));
cl->hash_algo = GCRY_MD_SHA1;
oid = "1.3.14.3.2.26";
break;
}
cl->hash_algo_oid = oid;
/* Check compliance. */
if (! gnupg_digest_is_allowed (opt.compliance, 1, cl->hash_algo))
{
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (cl->hash_algo),
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_DIGEST_ALGO);
goto leave;
}
if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0,
NULL, nbits, NULL))
{
char kidstr[10+1];
snprintf (kidstr, sizeof kidstr, "0x%08lX",
gpgsm_get_short_fingerprint (cl->cert, NULL));
log_error (_("key %s may not be used for signing in %s mode\n"),
kidstr,
gnupg_compliance_option_string (opt.compliance));
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto leave;
}
}
if (opt.verbose > 1 || opt.debug)
{
for (i=0, cl=signerlist; cl; cl = cl->next, i++)
log_info (_("hash algorithm used for signer %d: %s (%s)\n"),
i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid);
}
/* Gather certificates of signers and store them in the CMS object. */
for (cl=signerlist; cl; cl = cl->next)
{
rc = gpgsm_cert_use_sign_p (cl->cert, 0);
if (rc)
goto leave;
err = ksba_cms_add_signer (cms, cl->cert);
if (err)
{
log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err));
rc = err;
goto leave;
}
rc = add_certificate_list (ctrl, cms, cl->cert);
if (rc)
{
log_error ("failed to store list of certificates: %s\n",
gpg_strerror(rc));
goto leave;
}
/* Set the hash algorithm we are going to use */
err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid);
if (err)
{
log_debug ("ksba_cms_add_digest_algo failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
}
/* Check whether one of the certificates is qualified. Note that we
already validated the certificate and thus the user data stored
flag must be available. */
if (!opt.no_chain_validation)
{
for (cl=signerlist; cl; cl = cl->next)
{
size_t buflen;
char buffer[1];
err = ksba_cert_get_user_data (cl->cert, "is_qualified",
&buffer, sizeof (buffer), &buflen);
if (err || !buflen)
{
log_error (_("checking for qualified certificate failed: %s\n"),
gpg_strerror (err));
rc = err;
goto leave;
}
if (*buffer)
err = gpgsm_qualified_consent (ctrl, cl->cert);
else
err = gpgsm_not_qualified_warning (ctrl, cl->cert);
if (err)
{
rc = err;
goto leave;
}
}
}
/* Prepare hashing (actually we are figuring out what we have set
above). */
rc = gcry_md_open (&data_md, 0, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_HASHING)
gcry_md_debug (data_md, "sign.data");
for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
{
algo = gcry_md_map_name (algoid);
if (!algo)
{
log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?");
rc = gpg_error (GPG_ERR_BUG);
goto leave;
}
gcry_md_enable (data_md, algo);
audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
}
audit_log (ctrl->audit, AUDIT_SETUP_READY);
if (detached)
{ /* We hash the data right now so that we can store the message
digest. ksba_cms_build() takes this as an flag that detached
data is expected. */
unsigned char *digest;
size_t digest_len;
if (!hash_data (data_fd, data_md))
audit_log (ctrl->audit, AUDIT_GOT_DATA);
for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
{
digest = gcry_md_read (data_md, cl->hash_algo);
digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
if ( !digest || !digest_len )
{
log_error ("problem getting the hash of the data\n");
rc = gpg_error (GPG_ERR_BUG);
goto leave;
}
err = ksba_cms_set_message_digest (cms, signer, digest, digest_len);
if (err)
{
log_error ("ksba_cms_set_message_digest failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
}
}
gnupg_get_isotime (signed_at);
for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
{
err = ksba_cms_set_signing_time (cms, signer, signed_at);
if (err)
{
log_error ("ksba_cms_set_signing_time failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
}
/* We need to write at least a minimal list of our capabilities to
* try to convince some MUAs to use 3DES and not the crippled
* RC2. Our list is:
*
* aes256-CBC
* aes128-CBC
* des-EDE3-CBC
*/
err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.42", NULL,0);
if (!err)
err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL,0);
if (!err)
err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0);
if (err)
{
log_error ("ksba_cms_add_smime_capability failed: %s\n",
gpg_strerror (err));
goto leave;
}
/* Main building loop. */
do
{
err = ksba_cms_build (cms, &stopreason);
if (err)
{
- log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err));
+ log_error ("creating CMS object failed: %s\n", gpg_strerror (err));
rc = err;
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA)
{
/* Hash the data and store the message digest. */
unsigned char *digest;
size_t digest_len;
assert (!detached);
rc = hash_and_copy_data (data_fd, data_md, writer);
if (rc)
goto leave;
audit_log (ctrl->audit, AUDIT_GOT_DATA);
for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
{
digest = gcry_md_read (data_md, cl->hash_algo);
digest_len = gcry_md_get_algo_dlen (cl->hash_algo);
if ( !digest || !digest_len )
{
log_error ("problem getting the hash of the data\n");
rc = gpg_error (GPG_ERR_BUG);
goto leave;
}
err = ksba_cms_set_message_digest (cms, signer,
digest, digest_len);
if (err)
{
log_error ("ksba_cms_set_message_digest failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
}
}
}
else if (stopreason == KSBA_SR_NEED_SIG)
{
/* Compute the signature for all signers. */
gcry_md_hd_t md;
rc = gcry_md_open (&md, 0, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_HASHING)
gcry_md_debug (md, "sign.attr");
ksba_cms_set_hash_function (cms, HASH_FNC, md);
for (cl=signerlist,signer=0; cl; cl = cl->next, signer++)
{
unsigned char *sigval = NULL;
char *buf, *fpr;
audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer);
if (signer)
gcry_md_reset (md);
{
certlist_t cl_tmp;
for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next)
{
gcry_md_enable (md, cl_tmp->hash_algo);
audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO,
cl_tmp->hash_algo);
}
}
rc = ksba_cms_hash_signed_attrs (cms, signer);
if (rc)
{
log_debug ("hashing signed attrs failed: %s\n",
gpg_strerror (rc));
gcry_md_close (md);
goto leave;
}
rc = gpgsm_create_cms_signature (ctrl, cl->cert,
md, cl->hash_algo, &sigval);
if (rc)
{
audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc);
gcry_md_close (md);
goto leave;
}
err = ksba_cms_set_sig_val (cms, signer, sigval);
xfree (sigval);
if (err)
{
audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err);
log_error ("failed to store the signature: %s\n",
gpg_strerror (err));
rc = err;
gcry_md_close (md);
goto leave;
}
/* write a status message */
fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1);
if (!fpr)
{
rc = gpg_error (GPG_ERR_ENOMEM);
gcry_md_close (md);
goto leave;
}
rc = 0;
if (opt.verbose)
{
char *pkalgostr = gpgsm_pubkey_algo_string (cl->cert, NULL);
log_info (_("%s/%s signature using %s key %s\n"),
pubkey_algo_to_string (cl->pk_algo),
gcry_md_algo_name (cl->hash_algo),
pkalgostr, fpr);
xfree (pkalgostr);
}
buf = xtryasprintf ("%c %d %d 00 %s %s",
detached? 'D':'S',
cl->pk_algo,
cl->hash_algo,
signed_at,
fpr);
if (!buf)
rc = gpg_error_from_syserror ();
xfree (fpr);
if (rc)
{
gcry_md_close (md);
goto leave;
}
gpgsm_status (ctrl, STATUS_SIG_CREATED, buf);
xfree (buf);
audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, 0);
}
gcry_md_close (md);
}
}
while (stopreason != KSBA_SR_READY);
rc = gnupg_ksba_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
goto leave;
}
audit_log (ctrl->audit, AUDIT_SIGNING_DONE);
log_info ("signature created\n");
leave:
if (rc)
log_error ("error creating signature: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc) );
if (release_signerlist)
gpgsm_release_certlist (signerlist);
ksba_cms_release (cms);
gnupg_ksba_destroy_writer (b64writer);
keydb_release (kh);
gcry_md_close (data_md);
return rc;
}
diff --git a/sm/verify.c b/sm/verify.c
index 0e94463f3..8fc073c2f 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -1,746 +1,748 @@
/* verify.c - Verify a messages signature
* Copyright (C) 2001, 2002, 2003, 2007,
* 2010 Free Software Foundation, Inc.
* Copyright (C) 2001-2019 Werner Koch
* Copyright (C) 2015-2020 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 <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include "gpgsm.h"
#include <gcrypt.h>
#include <ksba.h>
#include "keydb.h"
#include "../common/i18n.h"
#include "../common/compliance.h"
static char *
strtimestamp_r (ksba_isotime_t atime)
{
char *buffer = xmalloc (15);
if (!atime || !*atime)
strcpy (buffer, "none");
else
sprintf (buffer, "%.4s-%.2s-%.2s", atime, atime+4, atime+6);
return buffer;
}
/* Hash the data for a detached signature. Returns 0 on success. */
static gpg_error_t
hash_data (int fd, gcry_md_hd_t md)
{
gpg_error_t err = 0;
estream_t fp;
char buffer[4096];
int nread;
fp = es_fdopen_nc (fd, "rb");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("fdopen(%d) failed: %s\n", fd, gpg_strerror (err));
return err;
}
do
{
nread = es_fread (buffer, 1, DIM(buffer), fp);
gcry_md_write (md, buffer, nread);
}
while (nread);
if (es_ferror (fp))
{
err = gpg_error_from_syserror ();
log_error ("read error on fd %d: %s\n", fd, gpg_strerror (err));
}
es_fclose (fp);
return err;
}
/* Perform a verify operation. To verify detached signatures, DATA_FD
must be different than -1. With OUT_FP given and a non-detached
signature, the signed material is written to that stream. */
int
gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp)
{
int i, rc;
gnupg_ksba_io_t b64reader = NULL;
gnupg_ksba_io_t b64writer = NULL;
ksba_reader_t reader;
ksba_writer_t writer = NULL;
ksba_cms_t cms = NULL;
ksba_stop_reason_t stopreason;
ksba_cert_t cert;
KEYDB_HANDLE kh;
gcry_md_hd_t data_md = NULL;
int signer;
const char *algoid;
int algo;
int is_detached;
estream_t in_fp = NULL;
char *p;
audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY);
kh = keydb_new ();
if (!kh)
{
log_error (_("failed to allocate keyDB handle\n"));
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
in_fp = es_fdopen_nc (in_fd, "rb");
if (!in_fp)
{
rc = gpg_error_from_syserror ();
log_error ("fdopen() failed: %s\n", strerror (errno));
goto leave;
}
rc = gnupg_ksba_create_reader
(&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0)
| (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)),
in_fp, &reader);
if (rc)
{
log_error ("can't create reader: %s\n", gpg_strerror (rc));
goto leave;
}
if (out_fp)
{
rc = gnupg_ksba_create_writer
(&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)),
ctrl->pem_name, out_fp, &writer);
if (rc)
{
log_error ("can't create writer: %s\n", gpg_strerror (rc));
goto leave;
}
}
+ gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
+
rc = ksba_cms_new (&cms);
if (rc)
goto leave;
rc = ksba_cms_set_reader_writer (cms, reader, writer);
if (rc)
{
log_error ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (rc));
goto leave;
}
rc = gcry_md_open (&data_md, 0, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_HASHING)
gcry_md_debug (data_md, "vrfy.data");
audit_log (ctrl->audit, AUDIT_SETUP_READY);
is_detached = 0;
do
{
rc = ksba_cms_parse (cms, &stopreason);
if (rc)
{
log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (stopreason == KSBA_SR_NEED_HASH)
{
is_detached = 1;
audit_log (ctrl->audit, AUDIT_DETACHED_SIGNATURE);
if (opt.verbose)
log_info ("detached signature\n");
}
if (stopreason == KSBA_SR_NEED_HASH
|| stopreason == KSBA_SR_BEGIN_DATA)
{
audit_log (ctrl->audit, AUDIT_GOT_DATA);
/* We are now able to enable the hash algorithms */
for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++)
{
algo = gcry_md_map_name (algoid);
if (!algo)
{
log_error ("unknown hash algorithm '%s'\n",
algoid? algoid:"?");
if (algoid
&& ( !strcmp (algoid, "1.2.840.113549.1.1.2")
||!strcmp (algoid, "1.2.840.113549.2.2")))
log_info (_("(this is the MD2 algorithm)\n"));
audit_log_s (ctrl->audit, AUDIT_BAD_DATA_HASH_ALGO, algoid);
}
else
{
if (DBG_X509)
log_debug ("enabling hash algorithm %d (%s)\n",
algo, algoid? algoid:"");
gcry_md_enable (data_md, algo);
audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
}
}
if (opt.extra_digest_algo)
{
if (DBG_X509)
log_debug ("enabling extra hash algorithm %d\n",
opt.extra_digest_algo);
gcry_md_enable (data_md, opt.extra_digest_algo);
audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO,
opt.extra_digest_algo);
}
if (is_detached)
{
if (data_fd == -1)
{
log_info ("detached signature w/o data "
"- assuming certs-only\n");
audit_log (ctrl->audit, AUDIT_CERT_ONLY_SIG);
}
else
audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING,
hash_data (data_fd, data_md));
}
else
{
ksba_cms_set_hash_function (cms, HASH_FNC, data_md);
}
}
else if (stopreason == KSBA_SR_END_DATA)
{ /* The data bas been hashed */
audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, 0);
}
}
while (stopreason != KSBA_SR_READY);
if (b64writer)
{
rc = gnupg_ksba_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
audit_log_ok (ctrl->audit, AUDIT_WRITE_ERROR, rc);
goto leave;
}
}
if (data_fd != -1 && !is_detached)
{
log_error ("data given for a non-detached signature\n");
rc = gpg_error (GPG_ERR_CONFLICT);
audit_log (ctrl->audit, AUDIT_USAGE_ERROR);
goto leave;
}
for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
{
/* Fixme: it might be better to check the validity of the
certificate first before entering it into the DB. This way
we would avoid cluttering the DB with invalid
certificates. */
audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert,
keydb_store_cert (ctrl, cert, 0, NULL));
ksba_cert_release (cert);
}
cert = NULL;
for (signer=0; ; signer++)
{
char *issuer = NULL;
gcry_sexp_t sigval = NULL;
ksba_isotime_t sigtime, keyexptime;
ksba_sexp_t serial;
char *msgdigest = NULL;
size_t msgdigestlen;
char *ctattr;
int sigval_hash_algo;
int info_pkalgo;
unsigned int nbits;
int pkalgo;
char *pkalgostr = NULL;
char *pkcurve = NULL;
char *pkfpr = NULL;
unsigned int pkalgoflags, verifyflags;
rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial);
if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA
&& data_fd == -1 && is_detached)
{
log_info ("certs-only message accepted\n");
rc = 0;
break;
}
if (rc)
{
if (signer && rc == -1)
rc = 0;
break;
}
gpgsm_status (ctrl, STATUS_NEWSIG, NULL);
audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer);
if (DBG_X509)
{
log_debug ("signer %d - issuer: '%s'\n",
signer, issuer? issuer:"[NONE]");
log_debug ("signer %d - serial: ", signer);
gpgsm_dump_serial (serial);
log_printf ("\n");
}
if (ctrl->audit)
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_SIG_NAME, tmpstr);
xfree (tmpstr);
}
rc = ksba_cms_get_signing_time (cms, signer, sigtime);
if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
*sigtime = 0;
else if (rc)
{
log_error ("error getting signing time: %s\n", gpg_strerror (rc));
*sigtime = 0; /* (we can't encode an error in the time string.) */
}
rc = ksba_cms_get_message_digest (cms, signer,
&msgdigest, &msgdigestlen);
if (!rc)
{
algoid = ksba_cms_get_digest_algo (cms, signer);
algo = gcry_md_map_name (algoid);
if (DBG_X509)
log_debug ("signer %d - digest algo: %d\n", signer, algo);
if (! gcry_md_is_enabled (data_md, algo))
{
log_error ("digest algo %d (%s) has not been enabled\n",
algo, algoid?algoid:"");
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "unsupported");
goto next_signer;
}
}
else if (gpg_err_code (rc) == GPG_ERR_NO_DATA)
{
assert (!msgdigest);
rc = 0;
algoid = NULL;
algo = 0;
}
else /* real error */
{
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
break;
}
rc = ksba_cms_get_sigattr_oids (cms, signer,
"1.2.840.113549.1.9.3", &ctattr);
if (!rc)
{
const char *s;
if (DBG_X509)
log_debug ("signer %d - content-type attribute: %s",
signer, ctattr);
s = ksba_cms_get_content_oid (cms, 1);
if (!s || strcmp (ctattr, s))
{
log_error ("content-type attribute does not match "
"actual content-type\n");
ksba_free (ctattr);
ctattr = NULL;
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
ksba_free (ctattr);
ctattr = NULL;
}
else if (rc != -1)
{
log_error ("error getting content-type attribute: %s\n",
gpg_strerror (rc));
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
rc = 0;
sigval = gpgsm_ksba_cms_get_sig_val (cms, signer);
if (!sigval)
{
log_error ("no signature value available\n");
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
sigval_hash_algo = gpgsm_get_hash_algo_from_sigval (sigval, &pkalgoflags);
if (DBG_X509)
{
log_debug ("signer %d - signature available (sigval hash=%d pkaf=%u)",
signer, sigval_hash_algo, pkalgoflags);
}
if (!sigval_hash_algo)
sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */
/* Find the certificate of the signer */
keydb_search_reset (kh);
rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial);
if (rc)
{
if (rc == -1)
{
log_error ("certificate not found\n");
rc = gpg_error (GPG_ERR_NO_PUBKEY);
}
else
log_error ("failed to find the certificate: %s\n",
gpg_strerror(rc));
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "verify.findkey",
numbuf, NULL);
}
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "no-cert");
goto next_signer;
}
rc = keydb_get_cert (kh, &cert);
if (rc)
{
log_error ("failed to get cert: %s\n", gpg_strerror (rc));
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
goto next_signer;
}
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
pkalgo = gpgsm_get_key_algo_info2 (cert, &nbits, &pkcurve);
/* Remap the ECC algo to the algo we use. Note that EdDSA has
* already been mapped. */
if (pkalgo == GCRY_PK_ECC)
pkalgo = GCRY_PK_ECDSA;
log_info (_("Signature made "));
if (*sigtime)
{
/* We take the freedom as noted in RFC3339 to use a space
* instead of the "T" delimiter between date and time. We
* also append a separate UTC instead of a "Z" or "+00:00"
* suffix because that makes it clear to everyone what kind
* of time this is. */
dump_isotime (sigtime);
log_printf (" UTC");
}
else
log_printf (_("[date not given]"));
log_info (_(" using %s key %s\n"), pkalgostr, pkfpr);
if (opt.verbose)
{
log_info (_("algorithm:"));
log_printf (" %s + %s",
pubkey_algo_to_string (pkalgo),
gcry_md_algo_name (sigval_hash_algo));
if (algo != sigval_hash_algo)
log_printf (" (%s)", gcry_md_algo_name (algo));
log_printf ("\n");
}
audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
pkalgo, pkalgoflags, NULL, nbits, NULL))
{
char kidstr[10+1];
snprintf (kidstr, sizeof kidstr, "0x%08lX",
gpgsm_get_short_fingerprint (cert, NULL));
log_error (_("key %s may not be used for signing in %s mode\n"),
kidstr,
gnupg_compliance_option_string (opt.compliance));
goto next_signer;
}
if (!gnupg_digest_is_allowed (opt.compliance, 0, sigval_hash_algo))
{
log_error (_("digest algorithm '%s' may not be used in %s mode\n"),
gcry_md_algo_name (sigval_hash_algo),
gnupg_compliance_option_string (opt.compliance));
goto next_signer;
}
/* Print compliance warning for the key. */
if (!opt.quiet
&& !gnupg_pk_is_compliant (opt.compliance, pkalgo, pkalgoflags,
NULL, nbits, pkcurve))
{
log_info (_("WARNING: This key is not suitable for signing"
" in %s mode\n"),
gnupg_compliance_option_string (opt.compliance));
}
/* Check compliance with CO_DE_VS. */
if (gnupg_pk_is_compliant (CO_DE_VS, pkalgo, pkalgoflags,
NULL, nbits, pkcurve)
&& gnupg_gcrypt_is_compliant (CO_DE_VS)
&& gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo))
gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS));
else if (opt.require_compliance
&& opt.compliance == CO_DE_VS)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
gpgsm_errors_seen = 1;
}
/* Now we can check the signature. */
if (msgdigest)
{ /* Signed attributes are available. */
gcry_md_hd_t md;
unsigned char *s;
/* Check that the message digest in the signed attributes
matches the one we calculated on the data. */
s = gcry_md_read (data_md, algo);
if ( !s || !msgdigestlen
|| gcry_md_get_algo_dlen (algo) != msgdigestlen
|| memcmp (s, msgdigest, msgdigestlen) )
{
char *fpr;
log_error (_("invalid signature: message digest attribute "
"does not match computed one\n"));
if (DBG_X509)
{
if (msgdigest)
log_printhex (msgdigest, msgdigestlen, "message: ");
if (s)
log_printhex (s, gcry_md_get_algo_dlen (algo),
"computed: ");
}
fpr = gpgsm_fpr_and_name_for_status (cert);
gpgsm_status (ctrl, STATUS_BADSIG, fpr);
xfree (fpr);
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, sigval_hash_algo);
rc = gcry_md_open (&md, sigval_hash_algo, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
goto next_signer;
}
if (DBG_HASHING)
gcry_md_debug (md, "vrfy.attr");
ksba_cms_set_hash_function (cms, HASH_FNC, md);
rc = ksba_cms_hash_signed_attrs (cms, signer);
if (rc)
{
log_error ("hashing signed attrs failed: %s\n",
gpg_strerror (rc));
gcry_md_close (md);
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
goto next_signer;
}
rc = gpgsm_check_cms_signature (cert, sigval, md, sigval_hash_algo,
pkalgoflags, &info_pkalgo);
gcry_md_close (md);
}
else
{
rc = gpgsm_check_cms_signature (cert, sigval, data_md,
algo, pkalgoflags, &info_pkalgo);
}
if (rc)
{
char *fpr;
log_error ("invalid signature: %s\n", gpg_strerror (rc));
fpr = gpgsm_fpr_and_name_for_status (cert);
gpgsm_status (ctrl, STATUS_BADSIG, fpr);
xfree (fpr);
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/
if (rc)
{
gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "verify.keyusage",
gpg_err_code (rc));
rc = 0;
}
if (DBG_X509)
log_debug ("signature okay - checking certs\n");
audit_log (ctrl->audit, AUDIT_VALIDATE_CHAIN);
rc = gpgsm_validate_chain (ctrl, cert,
*sigtime? sigtime : "19700101T000000",
keyexptime, 0,
NULL, 0, &verifyflags);
{
char *fpr, *buf, *tstr;
fpr = gpgsm_fpr_and_name_for_status (cert);
if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED)
{
gpgsm_status (ctrl, STATUS_EXPKEYSIG, fpr);
rc = 0;
}
else
gpgsm_status (ctrl, STATUS_GOODSIG, fpr);
xfree (fpr);
fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
tstr = strtimestamp_r (sigtime);
buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr,
*sigtime? sigtime : "0",
*keyexptime? keyexptime : "0",
info_pkalgo, algo);
xfree (tstr);
xfree (fpr);
gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
xfree (buf);
}
audit_log_ok (ctrl->audit, AUDIT_CHAIN_STATUS, rc);
if (rc) /* of validate_chain */
{
log_error ("invalid certification chain: %s\n", gpg_strerror (rc));
if (gpg_err_code (rc) == GPG_ERR_BAD_CERT_CHAIN
|| gpg_err_code (rc) == GPG_ERR_BAD_CERT
|| gpg_err_code (rc) == GPG_ERR_BAD_CA_CERT
|| gpg_err_code (rc) == GPG_ERR_CERT_REVOKED)
gpgsm_status_with_err_code (ctrl, STATUS_TRUST_NEVER, NULL,
gpg_err_code (rc));
else
gpgsm_status_with_err_code (ctrl, STATUS_TRUST_UNDEFINED, NULL,
gpg_err_code (rc));
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "good");
for (i=0; (p = ksba_cert_get_subject (cert, i)); i++)
{
log_info (!i? _("Good signature from")
: _(" aka"));
log_printf (" \"");
gpgsm_es_print_name (log_get_stream (), p);
log_printf ("\"\n");
ksba_free (p);
}
/* Print a note if this is a qualified signature. */
{
size_t qualbuflen;
char qualbuffer[1];
rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer,
sizeof (qualbuffer), &qualbuflen);
if (!rc && qualbuflen)
{
if (*qualbuffer)
{
log_info (_("This is a qualified signature\n"));
if (!opt.qualsig_approval)
log_info
(_("Note, that this software is not officially approved "
"to create or verify such signatures.\n"));
}
}
else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("get_user_data(is_qualified) failed: %s\n",
gpg_strerror (rc));
}
gpgsm_status (ctrl, STATUS_TRUST_FULLY,
(verifyflags & VALIDATE_FLAG_STEED)?
"0 steed":
(verifyflags & VALIDATE_FLAG_CHAIN_MODEL)?
"0 chain": "0 shell");
next_signer:
rc = 0;
xfree (issuer);
xfree (serial);
gcry_sexp_release (sigval);
xfree (msgdigest);
xfree (pkalgostr);
xfree (pkcurve);
xfree (pkfpr);
ksba_cert_release (cert);
cert = NULL;
}
rc = 0;
leave:
ksba_cms_release (cms);
gnupg_ksba_destroy_reader (b64reader);
gnupg_ksba_destroy_writer (b64writer);
keydb_release (kh);
gcry_md_close (data_md);
es_fclose (in_fp);
if (rc)
{
char numbuf[50];
sprintf (numbuf, "%d", rc );
gpgsm_status2 (ctrl, STATUS_ERROR, "verify.leave",
numbuf, NULL);
}
return rc;
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Aug 6, 9:43 PM (4 h, 51 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
55/0a/759cce51f2e2714fcbf9a88a94b0

Event Timeline