diff --git a/NEWS b/NEWS
index a720408..2212e99 100644
--- a/NEWS
+++ b/NEWS
@@ -1,578 +1,583 @@
-Noteworthy changes in version 1.5.2 (unreleased) [C21/A13/R_]
+Noteworthy changes in version 1.6.0 (unreleased) [C21/A13/R_]
------------------------------------------------
+ * Interface changes relative to the 1.5.0 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ KSBA_CT_AUTHENVELOPED_DATA NEW.
+
+
Noteworthy changes in version 1.5.1 (2021-04-06) [C21/A13/R1]
------------------------------------------------
* Support Brainpool curves specified by ECDomainParameters.
Release-info: https://dev.gnupg.org/T5379
Noteworthy changes in version 1.5.0 (2020-11-18) [C21/A13/R0]
------------------------------------------------
* ksba_cms_identify now identifies OpenPGP keyblock content.
* Supports TR-03111 plain format ECDSA signature verification.
* Fixes a CMS signed data parser bug exhibited by a somewhat strange
CMS message. [b6438e768c]
* Interface changes relative to the 1.4.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
KSBA_CT_OPENPGP_KEYBLOCK NEW.
Release-info: https://dev.gnupg.org/T5146
Noteworthy changes in version 1.4.0 (2020-05-19) [C20/A12/R0]
------------------------------------------------
* Supports ECDSA and EdDSA certificate creation and parsing. [#4896]
* Supports ECDH enveloped data. [#4920]
* Supports ECDSA and EdDSA signed data. [#4920]
* Supports rsaPSS signature verification. [#4538]
* Supports standard file descriptors in ksba_reader_read. [#3072]
* New configure flag --disable-doc.
* Improves supports for reproducible builds. [#4801]
* Allows for optional elements in keyinfo objects. [#4892]
* Updates the config and M4 scripts to the latest version.
* Fixes error detection in the CMS parser. [#4207]
* Fixes memory leak in ksba_cms_identify.
* Fixes build warnings on macOS. [#2910]
* Uses --disable-new-dtags if LD_LIBRARY_PATH is defined. [#4298]
* New constants KSBA_VERSION and KSBA_VERSION_NUMBER.
* New API to make creation of DER objects easy.
* Interface changes relative to the 1.3.5 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
KSBA_VERSION NEW.
KSBA_VERSION_NUMBER NEW.
KSBA_CT_SPC_IND_DATA_CTX NEW.
KSBA_CLASS_* NEW.
KSBA_TYPE_* NEW.
ksba_der_t NEW.
ksba_der_release NEW.
ksba_der_builder_new NEW.
ksba_der_builder_reset NEW.
ksba_der_add_ptr NEW.
ksba_der_add_val NEW.
ksba_der_add_int NEW.
ksba_der_add_oid NEW.
ksba_der_add_bts NEW.
ksba_der_add_der NEW.
ksba_der_add_tag NEW.
ksba_der_add_end NEW.
ksba_der_builder_get NEW.
Release-info: https://dev.gnupg.org/T4943
Noteworthy changes in version 1.3.5 (2016-08-22) [C19/A11/R6]
------------------------------------------------
* Limit the allowed size of complex ASN.1 objects (e.g. certificates)
to 16MiB.
* Avoid read access to unitialized memory.
* Improve detection of invalid RDNs.
* Encode the OCSP nonce value as an octet string as described by
RFC-6960.
Noteworthy changes in version 1.3.4 (2016-05-03) [C19/A11/R5]
------------------------------------------------
* Fixed two OOB read access bugs which could be used to force a DoS.
* Fixed a crash due to faulty curve OID lookup code.
* Synced the list of supported curves with those of Libgcrypt.
* New configure option --enable-build-timestamp; a build timestamp is
not anymore used by default.
Noteworthy changes in version 1.3.3 (2015-04-10) [C19/A11/R4]
------------------------------------------------
* Fixed an integer overflow in the DN decoder.
* Now returns an error instead of terminating the process for certain
bad BER encodings.
* Improved the parsing of utf-8 strings in DNs.
* Allow building with newer versions of Bison.
* Improvement building on Windows with newer versions of Mingw.
Noteworthy changes in version 1.3.2 (2014-11-25) [C19/A11/R3]
------------------------------------------------
* Fixed a buffer overflow in ksba_oid_to_str. [CVE-2014-9087]
Noteworthy changes in version 1.3.1 (2014-09-18)
------------------------------------------------
* Fixed memory leak in CRL parsing.
* Build fixes for Windows, Android, and ppc64el.
Noteworthy changes in version 1.3.0 (2012-09-27)
------------------------------------------------
* Changed the license of the library from GPLv3 to LGPLv3/GPLv2; see
the file AUTHORS for details.
* Minor bug fixes.
Noteworthy changes in version 1.2.0 (2011-03-01)
------------------------------------------------
* New functions to allow the creation of X.509 certificates.
* Interface changes relative to the 1.1.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_certreq_set_serial NEW.
ksba_certreq_set_issuer NEW.
ksba_certreq_set_validity NEW.
ksba_certreq_set_siginfo NEW.
Noteworthy changes in version 1.1.0 (2010-10-26)
------------------------------------------------
* New functions to fix a leak in dirmngr.
* Interface changes relative to the 1.0.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_reader_set_release_notify NEW
ksba_writer_set_release_notify NEW
Noteworthy changes in version 1.0.8 (2010-07-15)
------------------------------------------------
* Support for WindowsCE.
* Builds cleanly from SVN even when cross-compiling.
* Fixed a CMS parsing bug exhibited by Lotus Notes.
Noteworthy changes in version 1.0.7 (2009-07-03)
------------------------------------------------
* Detect overflow while parsing OIDs. Map BER encoded OIDs to well
known names.
* Allow mixed case names in DNs.
Noteworthy changes in version 1.0.6 (2009-06-05)
------------------------------------------------
* Support SHA-{384,512} based signature generation.
* The RSA algorithmIdentifier ASN.1 sequence is now emitted with an
explicit NULL parameter. Despite the interop testing we did in the
past, some software still requires this and thus we better follow
the best current practise.
Noteworthy changes in version 1.0.5 (2009-01-09)
------------------------------------------------
* Minor bug fixes.
Noteworthy changes in version 1.0.4 (2008-09-22)
------------------------------------------------
* Write smimeCapabilities according to RFC3851 to help Mozilla.
* Support DSA.
* The visibility attribute is now used if supported by the toolchain.
Noteworthy changes in version 1.0.3 (2008-02-12)
------------------------------------------------
* Minor bug fixes.
* Include the used hash algorithm in sig-val structures.
* Fix for unknown tags in issuerAltName and subjectAltName.
Noteworthy changes in version 1.0.2 (2007-07-04)
------------------------------------------------
* Support for SHA-2.
* Fixed a couple of memory leaks.
* Experimental support for ECDSA.
* Minor portability fixes.
* Switched to GPLv3.
Noteworthy changes in version 1.0.1 (2006-11-29)
------------------------------------------------
* Fixes for certificates lacking certain objects.
* Fixes to allow building on systems with a broken ar.
Noteworthy changes in version 1.0.0 (2006-08-31)
------------------------------------------------
* OCSP nonces are now checked to detect replay attacks.
* OCSP extensions may no be retrieved.
* Implemented ksba_ocsp_get_responder_id which used to always return
an error code not_implemented. Thus we can assume that the
function has never been used and we don't need to see this as an
API break.
* Interface changes relative to the 0.9.16 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_ocsp_get_extension NEW
ksba_ocsp_get_responder_id CHANGED: No ABI break.
Noteworthy changes in version 0.9.16 (2006-08-01)
-------------------------------------------------
* Fixed a character set conversion bug in BMPStrings.
* New function for better error reporting of DNs.
* Interface changes relative to the 0.9.13 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_dn_teststr NEW
ksba_dn_str2der NEW
ksba_dn_der2str NEW
Noteworthy changes in version 0.9.15 (2006-06-20)
-------------------------------------------------
* Fixed BER parser which was broken in the last release.
Noteworthy changes in version 0.9.14 (2006-05-16)
-------------------------------------------------
* Fixed broken OCSP requests.
* Ignore invalid bytes appended to a certificate.
Noteworthy changes in version 0.9.13 (2005-11-24)
-------------------------------------------------
* New functions to associate user data with a certificate object.
* Interface changes relative to the 0.9.12 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_cert_set_user_data NEW
ksba_cert_get_user_data NEW
Noteworthy changes in version 0.9.12 (2005-08-01)
-------------------------------------------------
* GeneralNames types dNSName and Uri are now supported.
* Minor changes to some function declarations. This should not
affect any compilation.
* Interface changes relative to the 0.9.7 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_certreq_add_extension CHANGED: Argument DER is now a void*.
ksba_cms_set_content_enc_algo CHANGED: Argument IV is now void*.
ksba_cms_get_content_enc_iv CHANGED: Argument IV is now void*.
ksba_cms_set_message_digest CHANGED: Argument DIGEST is now
unsigned char*.
Noteworthy changes in version 0.9.11 (2005-04-20)
-------------------------------------------------
* New convenience API function for the subjectKeyIdentifier.
* Implemented the keyIdentifier part for authorityKeyIdentifier of
CRLs and certificates.
* Reason codes for CRL items are now returned.
* Interface changes relative to the 0.9.7 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_cert_get_subj_key_id NEW.
Noteworthy changes in version 0.9.10 (2004-12-03)
-------------------------------------------------
* Fixed a CMS parsing bug.
Noteworthy changes in version 0.9.9 (2004-09-27)
------------------------------------------------
* Fixed a couple of bugs which caused parsing errors with some
certificates.
Noteworthy changes in version 0.9.8 (2004-07-22)
------------------------------------------------
* Fixed a bug in the OCSP request generation.
Noteworthy changes in version 0.9.7 (2004-06-08)
------------------------------------------------
* New API function to add arbitrary extensions to pkcs#10 requests.
* Interface changes relative to the 0.9.6 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_certreq_add_extension NEW.
Noteworthy changes in version 0.9.6 (2004-04-29)
------------------------------------------------
* New API functions to support v2 CRLs.
* Interface changes relative to the 0.9.5 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_crl_get_extension NEW.
ksba_crl_get_auth_key_id NEW.
ksba_crl_get_crl_number NEW.
Noteworthy changes in version 0.9.5 (2004-04-06)
------------------------------------------------
* New APIs to get hands on some more information.
* Interface changes relative to the 0.9.4 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_ocsp_get_responder_id NEW.
ksba_ocsp_get_cert NEW.
ksba_cert_get_authority_info_access NEW.
ksba_cert_get_subject_info_access NEW.
ksba_cms_add_smime_capability NEW.
Noteworthy changes in version 0.9.4 (2004-02-20)
------------------------------------------------
* Support for Extended Key Usage.
* ksba_cms_identify may no return a pseudo content type for pkcs#12
files.
* Interface changes relative to the 0.9.3 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_reader_clear NEW.
ksba_cert_get_ext_key_usages NEW.
KSBA_CT_PKCS12 NEW.
Noteworthy changes in version 0.9.3 (2004-01-30)
------------------------------------------------
* Fixed a serious bug shortly after the last release :-(.
Noteworthy changes in version 0.9.2 (2004-01-29)
------------------------------------------------
* Cleaned up the DN label table.
* Fixed a bug in creating CMS signed data.
Noteworthy changes in version 0.9.1 (2003-12-19)
------------------------------------------------
* Support for OCSP (rfc2560).
* The new function ksba_set_hash_buffer_function may be used during
intialization to register a simple hash fucntion for internal use
by libksba.
* Changed the license of the manual to GPL.
* Interface changes relative to the 0.9.0 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_status_t NEW.
ksba_ocsp_t NEW.
ksba_ocsp_response_status_t NEW.
ksba_ocsp_new NEW.
void ksba_ocsp_release NEW.
ksba_ocsp_set_digest_algo NEW.
ksba_ocsp_set_requestor NEW.
ksba_ocsp_add_target NEW.
ksba_ocsp_set_nonce NEW.
ksba_ocsp_prepare_request NEW.
ksba_ocsp_hash_request NEW.
ksba_ocsp_set_sig_val NEW.
ksba_ocsp_add_cert NEW.
ksba_ocsp_build_request NEW.
ksba_ocsp_parse_response NEW.
ksba_ocsp_get_digest_algo NEW.
ksba_ocsp_hash_respons NEW.
ksba_ocsp_get_sig_val NEW.
ksba_ocsp_get_status NEW.
ksba_set_hash_buffer_function NEW.
Noteworthy changes in version 0.9.0 (2003-11-17)
------------------------------------------------
* The time is not any longer described by time_t but through the new
type ksba_isotime_t which is string of excactly 15 characters in
ISO 8601 format (e.g. "19611107T152010") and always stored as
UTC. This is to allow representation of dates beyond the year 2038.
Comparing is a mere strcmp.
* All type names are nom conforming to the GNU coding standards, the
old names are still available as aliases but flagged as deprecated.
* All error codes have been replaced by libgpg-error ones. Libksba
now depends on this package. Remember to use the gpg_err_code
function when testing for error values other than success.
* Interface changes relative to the 0.4.7 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_cert_get_validity CHANGED: Uses ksba_isotime_t instead of time_t.
ksba_crl_get_update_times CHANGED: Uses ksba_isotime_t instead of time_t.
ksba_crl_get_item CHANGED: Uses ksba_isotime_t instead of time_t.
ksba_cms_get_signing_time CHANGED: Uses ksba_isotime_t instead of time_t.
ksba_cms_set_signing_time CHANGED: Uses ksba_isotime_t instead of time_t.
ksba_cert_new CHANGED: Returns an error code now.
ksba_cms_new CHANGED: Returns an error code now.
ksba_name_new CHANGED: Returns an error code now.
ksba_writer_new CHANGED: Returns an error code now.
ksba_reader_new CHANGED: Returns an error code now.
ksba_certreq_new CHANGED: Returns an error code now.
ksba_crl_new CHANGED: Returns an error code now.
ksba_isotime_t NEW.
ksba_error_t NEW: Should be used instead of KsbaError.
ksba_cert_t NEW: Should be used instead of KsbaCert.
ksba_certreq_t NEW: Should be used instead of KsbaCertreq.
ksba_cms_t NEW: Should be used instead of KsbaCMS.
ksba_crl_t NEW: Should be used instead of KsbaCRL.
ksba_name_t NEW: Should be used instead of KsbaName.
ksba_sexp_t NEW: Should be used instead of KsbaSexp.
ksba_reader_t NEW: Should be used instead of KsbaReader.
ksba_writer_t NEW: Should be used instead of KsbaWriter.
ksba_strerror REMOVED: use gpg_strerror instead.
Noteworthy changes in version 0.4.7 (2003-03-17)
------------------------------------------------
* Fixed type detection in creating DNs.
Noteworthy changes in version 0.4.6 (2002-12-04)
------------------------------------------------
* DNs in pkcs#10 request are now created in reversed order as
specified by rfc2253.
* The content-type signed attribute is created.
* Fixed a parser bug with a id-aa-encrypKeyPref attribute.
* Interface changes relative to the 0.4.3 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_cms_get_sigattr_oids NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.4.5 (2002-08-23)
------------------------------------------------
* Removed some debugging output.
* Added an autoconf macro.
Noteworthy changes in version 0.4.4 (2002-08-09)
------------------------------------------------
* Multiple signatures can now be created and parsed.
Noteworthy changes in version 0.4.3 (2002-06-25)
------------------------------------------------
* More bug fixes.
* Interface changes relative to the 0.4.2 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_writer_write_octet_string NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.4.2 (2002-06-04)
------------------------------------------------
* Some bug fixes and a new function.
* Interface changes relative to the 0.4.1 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ksba_cms_identify NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Noteworthy changes in version 0.4.1 (2002-05-03)
------------------------------------------------
* Minor fixes.
Noteworthy changes in version 0.4.0 (2002-04-15)
------------------------------------------------
* Nearly all stuff needed for the Aegypten project is now in place.
Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009 g10 Code GmbH
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/configure.ac b/configure.ac
index d3fa775..9225bbb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,493 +1,493 @@
# configure.ac - for libksba
# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
# 2010, 2011, 2012, 2018, 2019 g10 Code GmbH
#
# This file is part of KSBA
#
# KSBA 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.
#
# KSBA 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 .
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.61])
min_automake_version="1.14"
# To build a release you need to create a tag with the version number
# (git tag -s libksba-n.m.k) and run "./autogen.sh --force". Please
# bump the version number immediately after the release and do another
# commit and push so that the git magic is able to work. See below
# for the LT versions.
m4_define([mym4_package],[libksba])
m4_define([mym4_major], [1])
-m4_define([mym4_minor], [5])
-m4_define([mym4_micro], [2])
+m4_define([mym4_minor], [6])
+m4_define([mym4_micro], [0])
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
# flag indicating a development version (mym4_isbeta). Note that the
# m4 processing is done by autoconf and not during the configure run.
m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \
mym4_package mym4_major mym4_minor mym4_micro),[:]))
m4_define([mym4_isbeta], m4_argn(2, mym4_verslist))
m4_define([mym4_version], m4_argn(4, mym4_verslist))
m4_define([mym4_revision], m4_argn(7, mym4_verslist))
m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist))
m4_esyscmd([echo ]mym4_version[>VERSION])
AC_INIT([mym4_package],[mym4_version],[https://bugs.gnupg.org])
# LT Version numbers: Remember to change them just *before* a release.
# (Interfaces removed: CURRENT++, AGE=0, REVISION=0)
# (Interfaces added: CURRENT++, AGE++, REVISION=0)
# (No interfaces changed: REVISION++)
# Please remember to document interface changes in the NEWS file.
LIBKSBA_LT_CURRENT=21
LIBKSBA_LT_AGE=13
LIBKSBA_LT_REVISION=1
#-------------------
# If the API is changed in an incompatible way: increment the next counter.
KSBA_CONFIG_API_VERSION=1
NEED_GPG_ERROR_VERSION=1.8
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_SRCDIR([src/ksba.h.in])
AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST
AB_INIT
AC_USE_SYSTEM_EXTENSIONS
# Taken from mpfr-4.0.1, then modified for LDADD_FOR_TESTS_KLUDGE
dnl Under Linux, make sure that the old dtags are used if LD_LIBRARY_PATH
dnl is defined. The issue is that with the new dtags, LD_LIBRARY_PATH has
dnl the precedence over the run path, so that if a compatible MPFR library
dnl is installed in some directory from $LD_LIBRARY_PATH, then the tested
dnl MPFR library will be this library instead of the MPFR library from the
dnl build tree. Other OS with the same issue might be added later.
dnl
dnl References:
dnl https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859732
dnl http://lists.gnu.org/archive/html/libtool/2017-05/msg00000.html
dnl
dnl We need to check whether --disable-new-dtags is supported as alternate
dnl linkers may be used (e.g., with tcc: CC=tcc LD=tcc).
dnl
case $host in
*-*-linux*)
if test -n "$LD_LIBRARY_PATH"; then
saved_LDFLAGS="$LDFLAGS"
LDADD_FOR_TESTS_KLUDGE="-Wl,--disable-new-dtags"
LDFLAGS="$LDFLAGS $LDADD_FOR_TESTS_KLUDGE"
AC_MSG_CHECKING(whether --disable-new-dtags is supported by the linker)
AC_LINK_IFELSE([AC_LANG_SOURCE([[
int main (void) { return 0; }
]])],
[AC_MSG_RESULT(yes (use it since LD_LIBRARY_PATH is set))],
[AC_MSG_RESULT(no)
LDADD_FOR_TESTS_KLUDGE=""
])
LDFLAGS="$saved_LDFLAGS"
fi
;;
esac
AC_SUBST([LDADD_FOR_TESTS_KLUDGE])
LT_PREREQ([2.2.6])
LT_INIT([win32-dll disable-static])
LT_LANG([Windows Resource])
AM_MAINTAINER_MODE
AC_SUBST(LIBKSBA_LT_CURRENT)
AC_SUBST(LIBKSBA_LT_AGE)
AC_SUBST(LIBKSBA_LT_REVISION)
VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_major \
mym4_minor mym4_micro)
AC_SUBST(VERSION_NUMBER)
AH_TOP([
#ifndef _KSBA_CONFIG_H_INCLUDED
#define _KSBA_CONFIG_H_INCLUDED
/* Enable gpg-error's strerror macro for W32CE. */
#define GPG_ERR_ENABLE_ERRNO_MACROS 1
])
AH_BOTTOM([
#endif /*_KSBA_CONFIG_H_INCLUDED*/
])
# Checks for programs.
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AM_SILENT_RULES
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AM_PROG_CC_C_O
if test "x$ac_cv_prog_cc_c89" = "xno" ; then
AC_MSG_ERROR([[No C-89 compiler found]])
fi
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
gl_EARLY
#AC_ARG_PROGRAM
AC_PROG_YACC
AX_PROG_BISON([have_bison=yes],[have_bison=no])
if test "$USE_MAINTAINER_MODE" = "yes"; then
AC_CHECK_PROGS(GCOV, [gcov], gcov)
# gcov coverage reporting
AC_TDD_GCOV
AC_SUBST(COVERAGE_CFLAGS)
AC_SUBST(COVERAGE_LDFLAGS)
else
AM_CONDITIONAL(HAVE_GCOV, false)
fi
AC_C_INLINE
# We need to compile and run a program on the build machine.
# The AC_PROG_CC_FOR_BUILD macro in the AC archive is broken for
# autoconf 2.57.
AC_MSG_CHECKING(for cc for build)
if test "$cross_compiling" = "yes"; then
CC_FOR_BUILD="${CC_FOR_BUILD-cc}"
else
CC_FOR_BUILD="${CC_FOR_BUILD-$CC}"
fi
AC_MSG_RESULT($CC_FOR_BUILD)
AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler])
# This is handy for debugging so the compiler doesn't rearrange
# things and eliminate variables.
AC_ARG_ENABLE(optimization,
AS_HELP_STRING([--disable-optimization],
[disable compiler optimization]),
[if test $enableval = no ; then
CFLAGS=`echo $CFLAGS | sed 's/-O[[0-9]]//'`
fi])
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes"
if test "$USE_MAINTAINER_MODE" = "yes"; then
CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security"
# We use -W only if -Wno-missing-field-initializers is supported.
# -W is important because it detects errors like "if (foo);"
AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-missing-field-initializers"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers"
fi
AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wdeclaration-after-statement"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
fi
fi
AC_MSG_CHECKING([if gcc supports -Wpointer-arith])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wpointer-arith"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
AC_MSG_RESULT($_gcc_wopt)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -Wpointer-arith"
fi
AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-pointer-sign"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no)
AC_MSG_RESULT($_gcc_psign)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_psign" = xyes ; then
CFLAGS="$CFLAGS -Wno-pointer-sign"
fi
fi
# Setup some stuff depending on host.
have_w32_system=no
have_w32ce_system=no
case "${host}" in
*-*-mingw32ce*)
have_w32_system=yes
have_w32ce_system=yes
;;
*-*-mingw32*)
have_w32_system=yes
;;
*-apple-darwin*)
AC_DEFINE(_DARWIN_C_SOURCE, 1,
Expose all libc features (__DARWIN_C_FULL).)
;;
*)
;;
esac
if test "$have_w32_system" = yes; then
AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
if test "$have_w32ce_system" = yes; then
AC_DEFINE(HAVE_W32CE_SYSTEM,1,[Defined if we run on WindowsCE])
fi
fi
AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes)
# For some systems we know that we have ld_version scripts.
# Use it then as default.
have_ld_version_script=no
case "${host}" in
*-*-linux*)
have_ld_version_script=yes
;;
*-*-gnu*)
have_ld_version_script=yes
;;
esac
AC_ARG_ENABLE([ld-version-script],
AS_HELP_STRING([--enable-ld-version-script],
[enable/disable use of linker version script.
(default is system dependent)]),
[have_ld_version_script=$enableval],
[ : ] )
AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
#
# Check for ELF visibility support.
#
AC_CACHE_CHECK(whether the visibility attribute is supported,
ksba_cv_visibility_attribute,
[ksba_cv_visibility_attribute=no
AC_LANG_CONFTEST([AC_LANG_SOURCE(
[[int foo __attribute__ ((visibility ("hidden"))) = 1;
int bar __attribute__ ((visibility ("protected"))) = 1;
]])])
if ${CC-cc} -Werror -S conftest.c -o conftest.s \
1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then
if grep '\.hidden.*foo' conftest.s >/dev/null 2>&1 ; then
if grep '\.protected.*bar' conftest.s >/dev/null 2>&1; then
ksba_cv_visibility_attribute=yes
fi
fi
fi
])
if test "$ksba_cv_visibility_attribute" = "yes"; then
AC_CACHE_CHECK(for broken visibility attribute,
ksba_cv_broken_visibility_attribute,
[ksba_cv_broken_visibility_attribute=yes
AC_LANG_CONFTEST([AC_LANG_SOURCE(
[[int foo (int x);
int bar (int x) __asm__ ("foo")
__attribute__ ((visibility ("hidden")));
int bar (int x) { return x; }
]])])
if ${CC-cc} -Werror -S conftest.c -o conftest.s \
1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then
if grep '\.hidden@<:@ _@:>@foo' conftest.s >/dev/null 2>&1;
then
ksba_cv_broken_visibility_attribute=no
fi
fi
])
fi
if test "$ksba_cv_visibility_attribute" = "yes"; then
AC_CACHE_CHECK(for broken alias attribute,
ksba_cv_broken_alias_attribute,
[ksba_cv_broken_alias_attribute=yes
AC_LANG_CONFTEST([AC_LANG_SOURCE(
[[extern int foo (int x) __asm ("xyzzy");
int bar (int x) { return x; }
extern __typeof (bar) foo __attribute ((weak, alias ("bar")));
extern int dfoo;
extern __typeof (dfoo) dfoo __asm ("abccb");
int dfoo = 1;
]])])
if ${CC-cc} -Werror -S conftest.c -o conftest.s \
1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then
if grep 'xyzzy' conftest.s >/dev/null 2>&1 && \
grep 'abccb' conftest.s >/dev/null 2>&1; then
ksba_cv_broken_alias_attribute=no
fi
fi
])
fi
if test "$ksba_cv_visibility_attribute" = "yes"; then
AC_CACHE_CHECK(if gcc supports -fvisibility=hidden,
ksba_cv_gcc_has_f_visibility,
[ksba_cv_gcc_has_f_visibility=no
_gcc_cflags_save=$CFLAGS
CFLAGS="-fvisibility=hidden"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
ksba_cv_gcc_has_f_visibility=yes)
CFLAGS=$_gcc_cflags_save;
])
fi
if test "$ksba_cv_visibility_attribute" = "yes" \
&& test "$ksba_cv_broken_visibility_attribute" != "yes" \
&& test "$ksba_cv_broken_alias_attribute" != "yes" \
&& test "$ksba_cv_gcc_has_f_visibility" = "yes"
then
AC_DEFINE(KSBA_USE_VISIBILITY, 1,
[Define to use the GNU C visibility attribute.])
CFLAGS="$CFLAGS -fvisibility=hidden"
fi
#
# Checks for libraries.
#
AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION")
if test "x$GPG_ERROR_LIBS" = "x"; then
AC_MSG_ERROR([libgpg-error is needed.
See ftp://ftp.gnupg.org/gcrypt/libgpg-error/ .])
fi
AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_KSBA,
[The default error source for libksba.])
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([string.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_BIGENDIAN
AC_CHECK_SIZEOF(unsigned int)
AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_TYPES([u32])
# Checks for library functions.
AC_CHECK_FUNCS([memmove strchr strtol strtoul stpcpy gmtime_r getenv])
# GNUlib checks
gl_SOURCE_BASE(gl)
gl_M4_BASE(gl/m4)
gl_MODULES(alloca valgrind-tests)
gl_INIT
# To be used in ksba-config
KSBA_CONFIG_LIBS="-lksba"
KSBA_CONFIG_CFLAGS=""
KSBA_CONFIG_HOST="$host"
AC_SUBST(KSBA_CONFIG_LIBS)
AC_SUBST(KSBA_CONFIG_CFLAGS)
AC_SUBST(KSBA_CONFIG_API_VERSION)
AC_SUBST(KSBA_CONFIG_HOST)
# The Makefiles need to know about cross compiling
AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling != xno)
# Generate extended version information for W32.
if test "$have_w32_system" = yes; then
changequote(,)dnl
BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'`
changequote([,])dnl
BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec"
fi
AC_SUBST(BUILD_REVISION)
AC_SUBST(BUILD_FILEVERSION)
BUILD_REVISION="mym4_revision"
AC_SUBST(BUILD_REVISION)
AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION",
[GIT commit id revision used to build this package])
AC_ARG_ENABLE([build-timestamp],
AS_HELP_STRING([--enable-build-timestamp],
[set an explicit build timestamp for reproducibility.
(default is the current time in ISO-8601 format)]),
[if test "$enableval" = "yes"; then
BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date`
else
BUILD_TIMESTAMP="$enableval"
fi],
[BUILD_TIMESTAMP=""])
AC_SUBST(BUILD_TIMESTAMP)
AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP",
[The time this package was configured for a build])
build_doc=yes
AC_ARG_ENABLE([doc], AS_HELP_STRING([--disable-doc],
[do not build the documentation]),
build_doc=$enableval, build_doc=yes)
AM_CONDITIONAL([BUILD_DOC], [test "x$build_doc" != xno])
AC_CONFIG_FILES([
Makefile
m4/Makefile
gl/Makefile
src/Makefile
src/ksba.h
src/ksba-config
src/ksba.pc
src/versioninfo.rc
tests/Makefile
doc/Makefile
])
AC_OUTPUT
if test x"$have_bison" = xno; then
AC_MSG_NOTICE([[
***
*** Note: The installed yacc version is not GNU Bison. You need
*** to install Bison if you want to change any grammar (.y) file.
***]])
fi
echo "
Libksba v${VERSION} has been configured as follows:
Revision: mym4_revision (mym4_revision_dec)
Platform: $host
"
diff --git a/src/cms-parser.c b/src/cms-parser.c
index b44a04a..d75cd73 100644
--- a/src/cms-parser.c
+++ b/src/cms-parser.c
@@ -1,975 +1,1084 @@
/* cms-parse.c - parse cryptographic message syntax
* Copyright (C) 2001, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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.
*
* KSBA 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 copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
/*
We handle CMS by using a handcrafted parser for the outer
structures and the generic parser of the parts we can handle in
memory. Extending the generic parser to allow hooks for indefinite
length objects and to auto select the object depending on the
content type OID is too complicated.
*/
#include
#include
#include
#include
#include
#include "util.h"
#include "cms.h"
#include "asn1-func.h" /* need some constants */
#include "ber-decoder.h"
#include "ber-help.h"
#include "keyinfo.h"
static int
read_byte (ksba_reader_t reader)
{
unsigned char buf;
size_t nread;
int rc;
do
rc = ksba_reader_read (reader, &buf, 1, &nread);
while (!rc && !nread);
return rc? -1: buf;
}
/* read COUNT bytes into buffer. Return 0 on success */
static int
read_buffer (ksba_reader_t reader, char *buffer, size_t count)
{
size_t nread;
while (count)
{
if (ksba_reader_read (reader, buffer, count, &nread))
return -1;
buffer += nread;
count -= nread;
}
return 0;
}
/* Create a new decoder and run it for the given element */
static gpg_error_t
create_and_run_decoder (ksba_reader_t reader, const char *elem_name,
unsigned int flags,
AsnNode *r_root,
unsigned char **r_image, size_t *r_imagelen)
{
gpg_error_t err;
ksba_asn_tree_t cms_tree;
BerDecoder decoder;
err = ksba_asn_create_tree ("cms", &cms_tree);
if (err)
return err;
decoder = _ksba_ber_decoder_new ();
if (!decoder)
{
ksba_asn_tree_release (cms_tree);
return gpg_error (GPG_ERR_ENOMEM);
}
err = _ksba_ber_decoder_set_reader (decoder, reader);
if (err)
{
ksba_asn_tree_release (cms_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_set_module (decoder, cms_tree);
if (err)
{
ksba_asn_tree_release (cms_tree);
_ksba_ber_decoder_release (decoder);
return err;
}
err = _ksba_ber_decoder_decode (decoder, elem_name, flags,
r_root, r_image, r_imagelen);
_ksba_ber_decoder_release (decoder);
ksba_asn_tree_release (cms_tree);
return err;
}
/* Parse this structure and return the oid of the content. The read
position is then located at the value of content. This fucntion is
the core for parsing ContentInfo and EncapsulatedContentInfo.
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType
}
ContentType ::= OBJECT IDENTIFIER
Returns: 0 on success or an error code. Other values are returned
by the parameters.
*/
static gpg_error_t
parse_content_info (ksba_reader_t reader,
unsigned long *r_len, int *r_ndef,
char **r_oid, int *has_content)
{
struct tag_info ti;
gpg_error_t err;
int content_ndef;
unsigned long content_len;
unsigned char oidbuf[100]; /* pretty large for an OID */
char *oid = NULL;
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
content_len = ti.length;
content_ndef = ti.ndef;
if (!content_ndef && content_len < 3)
return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */
/* read the OID */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed && ti.length) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
content_len -= ti.length;
}
if (ti.length >= DIM(oidbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
err = read_buffer (reader, oidbuf, ti.length);
if (err)
return err;
oid = ksba_oid_to_str (oidbuf, ti.length);
if (!oid)
return gpg_error (GPG_ERR_ENOMEM);
if (!content_ndef && !content_len)
{ /* no data */
*has_content = 0;
}
else
{ /* now read the explicit tag 0 which is optional */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
{
xfree (oid);
return err;
}
if ( ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed )
{
*has_content = 1;
}
else if ( ti.class == CLASS_UNIVERSAL && ti.tag == 0 && !ti.is_constructed )
{
*has_content = 0; /* this is optional - allow NUL tag */
}
else /* neither [0] nor NULL */
{
xfree (oid);
return gpg_error (GPG_ERR_INV_CMS_OBJ);
}
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (!ti.ndef && content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
}
}
*r_len = content_len;
*r_ndef = content_ndef;
*r_oid = oid;
return 0;
}
/* Parse this structure and return the oid of the content as well as
the algorithm identifier. The read position is then located at the
value of the octect string.
EncryptedContentInfo ::= SEQUENCE {
contentType OBJECT IDENTIFIER,
contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL }
Returns: 0 on success or an error code. Other values are returned
by the parameters.
*/
static gpg_error_t
parse_encrypted_content_info (ksba_reader_t reader,
unsigned long *r_len, int *r_ndef,
char **r_cont_oid, char **r_algo_oid,
char **r_algo_parm, size_t *r_algo_parmlen,
+ int *r_algo_parmtype,
int *has_content)
{
struct tag_info ti;
gpg_error_t err;
int content_ndef;
unsigned long content_len;
unsigned char tmpbuf[500]; /* for OID or algorithmIdentifier */
char *cont_oid = NULL;
char *algo_oid = NULL;
char *algo_parm = NULL;
size_t algo_parmlen;
size_t nread;
+ int algo_parmtype;
/* Fixme: release oids in case of errors */
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
content_len = ti.length;
content_ndef = ti.ndef;
if (!content_ndef && content_len < 3)
return gpg_error (GPG_ERR_TOO_SHORT); /* to encode an OID */
/* read the OID */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed && ti.length) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
content_len -= ti.length;
}
if (ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
err = read_buffer (reader, tmpbuf, ti.length);
if (err)
return err;
cont_oid = ksba_oid_to_str (tmpbuf, ti.length);
if (!cont_oid)
return gpg_error (GPG_ERR_ENOMEM);
/* read the algorithmIdentifier */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
content_len -= ti.nhdr;
if (content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
content_len -= ti.length;
}
if (ti.nhdr + ti.length >= DIM(tmpbuf))
return gpg_error (GPG_ERR_TOO_LARGE);
memcpy (tmpbuf, ti.buf, ti.nhdr);
err = read_buffer (reader, tmpbuf+ti.nhdr, ti.length);
if (err)
return err;
- err = _ksba_parse_algorithm_identifier2 (tmpbuf, ti.nhdr+ti.length,
- &nread,&algo_oid,
- &algo_parm, &algo_parmlen);
+ err = _ksba_parse_algorithm_identifier3 (tmpbuf, ti.nhdr+ti.length,
+ 0x30,
+ &nread, &algo_oid,
+ &algo_parm, &algo_parmlen,
+ &algo_parmtype);
if (err)
return err;
assert (nread <= ti.nhdr + ti.length);
if (nread < ti.nhdr + ti.length)
return gpg_error (GPG_ERR_TOO_SHORT);
/* the optional encryptedDataInfo */
*has_content = 0;
if (content_ndef || content_len)
{ /* now read the implicit tag 0. Actually this is optional but
in that case we don't expect to have a content_len - well, it
may be the end tag */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
{
xfree (cont_oid);
xfree (algo_oid);
return err;
}
/* Note: the tag may either denote a constructed or a primitve
object. Actually this should match the use of NDEF header
but we don't ceck that */
if ( ti.class == CLASS_CONTEXT && ti.tag == 0 )
{
*has_content = 1;
if (!content_ndef)
{
if (content_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER);
content_len -= ti.nhdr;
if (!ti.ndef && content_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER);
}
}
else /* not what we want - push it back */
{
*has_content = 0;
err = ksba_reader_unread (reader, ti.buf, ti.nhdr);
if (err)
return err;
}
}
*r_len = content_len;
*r_ndef = content_ndef;
*r_cont_oid = cont_oid;
*r_algo_oid = algo_oid;
*r_algo_parm = algo_parm;
*r_algo_parmlen = algo_parmlen;
+ *r_algo_parmtype = algo_parmtype;
return 0;
}
/* Parse this structure and return the oid of the content. The read
position is then located at the value of content.
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType
}
ContentType ::= OBJECT IDENTIFIER
Returns: 0 on success or an error code. On success the OID and the
length values are stored in the cms structure.
*/
gpg_error_t
_ksba_cms_parse_content_info (ksba_cms_t cms)
{
gpg_error_t err;
int has_content;
int content_ndef;
unsigned long content_len;
char *oid;
err = parse_content_info (cms->reader, &content_len, &content_ndef,
&oid, &has_content);
if (err)
{ /* return a more meaningful error message. This way the caller
can pass arbitrary data to the function and get back an error
that this is not CMS instead of the the not very detailed BER
Error. */
if (gpg_err_code (err) == GPG_ERR_BAD_BER
|| gpg_err_code (err) == GPG_ERR_INV_CMS_OBJ
|| gpg_err_code (err) == GPG_ERR_TOO_SHORT)
err = gpg_error (GPG_ERR_NO_CMS_OBJ);
return err;
}
if (!has_content)
return gpg_error (GPG_ERR_NO_CMS_OBJ); /* It is not optional here */
cms->content.length = content_len;
cms->content.ndef = content_ndef;
xfree (cms->content.oid);
cms->content.oid = oid;
return 0;
}
/* parse a SEQUENCE and the first element which is expected to be the
CMS version. Return the version and the length info */
static gpg_error_t
parse_cms_version (ksba_reader_t reader, int *r_version,
unsigned long *r_len, int *r_ndef)
{
struct tag_info ti;
gpg_error_t err;
unsigned long data_len;
int data_ndef;
int c;
/* read the sequence triplet */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
data_len = ti.length;
data_ndef = ti.ndef;
if (!data_ndef && data_len < 3)
return gpg_error (GPG_ERR_TOO_SHORT); /*to encode the version*/
/* read the version integer */
err = _ksba_ber_read_tl (reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER
&& !ti.is_constructed && ti.length) )
return gpg_error (GPG_ERR_INV_CMS_OBJ);
if (!data_ndef)
{
if (data_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
data_len -= ti.nhdr;
if (data_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
data_len -= ti.length;
}
if (ti.length != 1)
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION);
if ( (c=read_byte (reader)) == -1)
{
err = ksba_reader_error (reader);
return err? err : gpg_error (GPG_ERR_GENERAL);
}
if ( !(c == 0 || c == 1 || c == 2 || c == 3 || c == 4) )
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_VERSION);
*r_version = c;
*r_len = data_len;
*r_ndef = data_ndef;
return 0;
}
/* Parse a structure:
SignedData ::= SEQUENCE {
version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }),
digestAlgorithms SET OF AlgorithmIdentifier,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos }
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
*/
gpg_error_t
_ksba_cms_parse_signed_data_part_1 (ksba_cms_t cms)
{
struct tag_info ti;
gpg_error_t err;
int signed_data_ndef;
unsigned long signed_data_len;
int algo_set_ndef;
unsigned long algo_set_len;
int encap_cont_ndef;
unsigned long encap_cont_len;
int has_content;
char *oid;
char *p, *buffer;
unsigned long off, len;
err = parse_cms_version (cms->reader, &cms->cms_version,
&signed_data_len, &signed_data_ndef);
if (err)
return err;
/* read the SET OF algorithmIdentifiers */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SET
&& ti.is_constructed) )
return gpg_error (GPG_ERR_INV_CMS_OBJ); /* not the expected SET tag */
if (!signed_data_ndef)
{
if (signed_data_len < ti.nhdr)
return gpg_error (GPG_ERR_BAD_BER); /* triplet header larger that sequence */
signed_data_len -= ti.nhdr;
if (!ti.ndef && signed_data_len < ti.length)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
signed_data_len -= ti.length;
}
algo_set_len = ti.length;
algo_set_ndef = ti.ndef;
/* fixme: we are not able to read ndef length algorithm indentifiers. */
if (algo_set_ndef)
return gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
/* read the entire sequence into a buffer (add one to avoid malloc(0)) */
buffer = xtrymalloc (algo_set_len + 1);
if (!buffer)
return gpg_error (GPG_ERR_ENOMEM);
if (read_buffer (cms->reader, buffer, algo_set_len))
{
xfree (buffer);
err = ksba_reader_error (cms->reader);
return err? err: gpg_error (GPG_ERR_GENERAL);
}
p = buffer;
while (algo_set_len)
{
size_t nread;
struct oidlist_s *ol;
err = _ksba_parse_algorithm_identifier (p, algo_set_len, &nread, &oid);
if (err)
{
xfree (buffer);
return err;
}
assert (nread <= algo_set_len);
algo_set_len -= nread;
p += nread;
/* store the oid */
ol = xtrymalloc (sizeof *ol);
if (!ol)
{
xfree (oid);
return gpg_error (GPG_ERR_ENOMEM);
}
ol->oid = oid;
ol->next = cms->digest_algos;
cms->digest_algos = ol;
}
xfree (buffer); buffer = NULL;
/* Now for the encapsulatedContentInfo */
off = ksba_reader_tell (cms->reader);
err = parse_content_info (cms->reader,
&encap_cont_len, &encap_cont_ndef,
&oid, &has_content);
if (err)
return err;
cms->inner_cont_len = encap_cont_len;
cms->inner_cont_ndef = encap_cont_ndef;
cms->inner_cont_oid = oid;
cms->detached_data = !has_content;
if (!signed_data_ndef)
{
len = ksba_reader_tell (cms->reader) - off;
if (signed_data_len < len)
return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */
signed_data_len -= len;
if (!encap_cont_ndef && signed_data_len < encap_cont_len)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
}
/* We have to stop here so that the caller can set up the hashing etc. */
return 0;
}
/* Continue parsing of the structure we started to parse with the
part_1 function. We expect to be right at the certificates tag. */
gpg_error_t
_ksba_cms_parse_signed_data_part_2 (ksba_cms_t cms)
{
struct tag_info ti;
gpg_error_t err;
struct signer_info_s *si, **si_tail;
/* read the next triplet which is either a [0], a [1] or a SET OF
(signerInfo) */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
{
/* well, there might be still an end tag pending; eat it -
fixme: we should keep track of this to catch invalid
encodings */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
}
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
{ /* Implicit SET OF certificateSet with elements of CHOICE, but
we assume the first choice which is a Certificate; all other
choices are obsolete. We are now parsing a set of
certificates which we do by utilizing the ksba_cert code. */
ksba_cert_t cert;
int expect_endtag;
expect_endtag = !!ti.ndef;
for (;;)
{
struct certlist_s *cl;
/* First see whether this is really a sequence */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (expect_endtag && !ti.class && !ti.tag)
{
/* This is an end tag. Read the next tag but don't fail
if this is just an EOF. */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
return err;
}
break;
}
if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed))
break; /* not a sequence, so we are ready with the set */
/* We must unread so that the standard parser sees the sequence */
err = ksba_reader_unread (cms->reader, ti.buf, ti.nhdr);
if (err)
return err;
/* Use the standard certificate parser */
err = ksba_cert_new (&cert);
if (err)
return err;
err = ksba_cert_read_der (cert, cms->reader);
if (err)
{
ksba_cert_release (cert);
return err;
}
cl = xtrycalloc (1, sizeof *cl);
if (!cl)
{
ksba_cert_release (cert);
return gpg_error (GPG_ERR_ENOMEM);
}
cl->cert = cert;
cl->next = cms->cert_list;
cms->cert_list = cl;
}
}
if (ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed)
{ /* implicit SET OF certificateList. We should delegate the
parsing to a - not yet existing - ksba_crl module. CRLs are
quite important for other applications too so we should
provide a nice interface */
int expect_endtag;
expect_endtag = !!ti.ndef;
/* FIXME this is just dummy read code */
/* fprintf (stderr,"WARNING: Can't handle CRLs yet\n"); */
for (;;)
{
/* first see whether this is really a sequence */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (expect_endtag && !ti.class && !ti.tag)
{
/* This is an end tag. Read the next tag but don't fail
if this is just an EOF. */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_EOF)
err = 0;
return err;
}
break;
}
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed))
break; /* not a sequence, so we are ready with the set */
while (ti.length)
{
size_t n, nread;
char dummy[256];
n = ti.length > DIM(dummy) ? DIM(dummy): ti.length;
err = ksba_reader_read (cms->reader, dummy, n, &nread);
if (err)
return err;
ti.length -= nread;
}
}
}
/* expect a SET OF signerInfo */
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SET && ti.is_constructed))
return gpg_error (GPG_ERR_INV_CMS_OBJ);
si_tail = &cms->signer_info;
while (ti.length)
{
size_t off1, off2;
off1 = ksba_reader_tell (cms->reader);
si = xtrycalloc (1, sizeof *si);
if (!si)
return gpg_error (GPG_ERR_ENOMEM);
err = create_and_run_decoder (cms->reader,
"CryptographicMessageSyntax.SignerInfo",
0,
&si->root, &si->image, &si->imagelen);
/* The signerInfo might be an empty set in the case of a certs-only
signature. Thus we have to allow for EOF here */
if (gpg_err_code (err) == GPG_ERR_EOF)
{
xfree (si);
err = 0;
break;
}
if (err)
{
xfree (si);
return err;
}
*si_tail = si;
si_tail = &si->next;
off2 = ksba_reader_tell (cms->reader);
if ( (off2 - off1) > ti.length )
ti.length = 0;
else
ti.length -= off2 - off1;
}
return 0;
}
/* Parse the structure:
-
- EnvelopedData ::= SEQUENCE {
- version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }),
- originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
- recipientInfos RecipientInfos,
- encryptedContentInfo EncryptedContentInfo,
- unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
-
- OriginatorInfo ::= SEQUENCE {
- certs [0] IMPLICIT CertificateSet OPTIONAL,
- crls [1] IMPLICIT CertificateRevocationLists OPTIONAL }
-
- RecipientInfos ::= SET OF RecipientInfo
-
- EncryptedContentInfo ::= SEQUENCE {
- contentType ContentType,
- contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
- encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
-
- EncryptedContent ::= OCTET STRING
-
- We stop parsing so that the next read will be the first byte of the
- encryptedContent or (if there is no content) the unprotectedAttrs.
-*/
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }),
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
+ *
+ * or this one:
+ *
+ * AuthEnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * authEncryptedContentInfo EncryptedContentInfo,
+ * authAttrs [1] IMPLICIT AuthAttributes OPTIONAL, -- not in above
+ * mac MessageAuthenticationCode, -- not in above
+ * unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL } -- different tag
+ *
+ * where
+ *
+ * OriginatorInfo ::= SEQUENCE {
+ * certs [0] IMPLICIT CertificateSet OPTIONAL,
+ * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL }
+ *
+ * RecipientInfos ::= SET OF RecipientInfo
+ *
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
+ *
+ * EncryptedContent ::= OCTET STRING
+ *
+ * MessageAuthenticationCode ::= OCTET STRING
+ *
+ * We stop parsing so that the next read will be the first byte of the
+ * encryptedContent or (if there is no content) the unprotectedAttrs
+ * respective the authAttrs.
+ */
gpg_error_t
_ksba_cms_parse_enveloped_data_part_1 (ksba_cms_t cms)
{
struct tag_info ti;
gpg_error_t err;
int env_data_ndef;
unsigned long env_data_len;
int encr_cont_ndef = 0;
unsigned long encr_cont_len = 0;
int has_content = 0;
unsigned long off, len;
char *cont_oid = NULL;
char *algo_oid = NULL;
char *algo_parm = NULL;
size_t algo_parmlen = 0;
+ int algo_parmtype = 0;
struct value_tree_s *vt, **vtend;
/* get the version */
err = parse_cms_version (cms->reader, &cms->cms_version,
&env_data_len, &env_data_ndef);
if (err)
return err;
/* read the next triplet which is either a [0] for originatorInfos
or a SET_OF (recipientInfo) */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed)
{ /* originatorInfo - but we skip it for now */
/* well, raise an error */
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
}
/* Next one is the SET OF RecipientInfo:
* RecipientInfo ::= CHOICE {
* ktri KeyTransRecipientInfo,
* kari [1] KeyAgreeRecipientInfo,
* kekri [2] KEKRecipientInfo,
* pwri [3] PasswordRecipientInfo
* } */
if ( !(ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_SET && ti.is_constructed))
return gpg_error (GPG_ERR_INV_CMS_OBJ);
vtend = &cms->recp_info;
if (ti.ndef)
{
for (;;)
{
struct tag_info ti2;
err = _ksba_ber_read_tl (cms->reader, &ti2);
if (err)
return err;
if (!ti2.class && !ti2.tag)
break; /* End tag found: ready. */
/* Not an end tag: Push it back and run the decoder. */
err = ksba_reader_unread (cms->reader, ti2.buf, ti2.nhdr);
if (err)
return err;
vt = xtrycalloc (1, sizeof *vt);
if (!vt)
return gpg_error_from_syserror ();
err = create_and_run_decoder
(cms->reader,
"CryptographicMessageSyntax.RecipientInfo",
BER_DECODER_FLAG_FAST_STOP,
&vt->root, &vt->image, &vt->imagelen);
if (err)
{
xfree (vt);
return err;
}
*vtend = vt;
vtend = &vt->next;
}
}
else
{
while (ti.length)
{
size_t off1, off2;
off1 = ksba_reader_tell (cms->reader);
vt = xtrycalloc (1, sizeof *vt);
if (!vt)
return gpg_error_from_syserror ();
err = create_and_run_decoder
(cms->reader,
"CryptographicMessageSyntax.RecipientInfo",
BER_DECODER_FLAG_FAST_STOP,
&vt->root, &vt->image, &vt->imagelen);
if (err)
{
xfree (vt);
return err;
}
*vtend = vt;
vtend = &vt->next;
off2 = ksba_reader_tell (cms->reader);
if ( (off2 - off1) > ti.length )
ti.length = 0;
else
ti.length -= off2 - off1;
}
}
/* Now for the encryptedContentInfo */
off = ksba_reader_tell (cms->reader);
err = parse_encrypted_content_info (cms->reader,
&encr_cont_len, &encr_cont_ndef,
&cont_oid,
&algo_oid,
- &algo_parm, &algo_parmlen,
+ &algo_parm, &algo_parmlen, &algo_parmtype,
&has_content);
if (err)
return err;
+
+ /* If this is AES with GCM the parameter should be
+ *
+ * GCMParameters ::= SEQUENCE {
+ * aes-nonce OCTET STRING, -- recommended size is 12 octets
+ * aes-ICVlen AES-GCM-ICVlen DEFAULT 12 }
+ *
+ * Under the assumption that the IV is at max 16 bytes (i.e. the
+ * blocksize of AES) and the default ICVlen is used, we modify the
+ * parameter to have just the nonce without any encoding. */
+ if (algo_parmlen > 4 && algo_parm[0] == 0x30 /* Sequence. */
+ && algo_oid
+ && (!strcmp (algo_oid, "2.16.840.1.101.3.4.1.46") /*AES256.GCM*/
+ || !strcmp (algo_oid, "2.16.840.1.101.3.4.1.26") /*AES192.GCM*/
+ || !strcmp (algo_oid, "2.16.840.1.101.3.4.1.6"))) /*AES128.GCM*/
+ {
+ if (algo_parmlen == algo_parm[1] + 2
+ && algo_parm[1] == algo_parm[3] + 2
+ && algo_parm[2] == 0x04
+ && algo_parm[3] && algo_parm[3] <= 16)
+ {
+ algo_parmlen = algo_parm[3];
+ memmove (algo_parm, algo_parm+4, algo_parmlen);
+ }
+ }
+
cms->inner_cont_len = encr_cont_len;
cms->inner_cont_ndef = encr_cont_ndef;
cms->inner_cont_oid = cont_oid;
cms->detached_data = !has_content;
cms->encr_algo_oid = algo_oid;
cms->encr_iv = algo_parm; algo_parm = NULL;
cms->encr_ivlen = algo_parmlen;
if (!env_data_ndef)
{
len = ksba_reader_tell (cms->reader) - off;
if (env_data_len < len)
return gpg_error (GPG_ERR_BAD_BER); /* parsed content info larger that sequence */
env_data_len -= len;
if (!encr_cont_ndef && env_data_len < encr_cont_len)
return gpg_error (GPG_ERR_BAD_BER); /* triplet larger that sequence */
}
return 0;
}
-/* handle the unprotected attributes */
+/* Handle the unprotected attributes and more important
+ *
+ * authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+ * mac MessageAuthenticationCode,
+ * unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL
+ *
+ * if case of cms->content.ct == KSBA_CT_AUTHENVELOPED_DATA
+ */
gpg_error_t
_ksba_cms_parse_enveloped_data_part_2 (ksba_cms_t cms)
{
- (void)cms;
- /* FIXME */
+ gpg_error_t err;
+ struct tag_info ti;
+
+ if (cms->content.ct != KSBA_CT_AUTHENVELOPED_DATA)
+ return 0; /* We don't yet support unprotectedAttrs. */
+
+ /* Shall we use ksba_cms_get_message_digest to return the mac? To
+ * return the authAttrs we need a new function: A generic get_attr
+ * function which can be used for all kind of attributes would be
+ * best. */
+
+ /* Read authAttr if availabale. */
+ err = _ksba_ber_read_tl (cms->reader, &ti);
+ if (err)
+ return err;
+ /* Skip an end tag. */
+ if (!ti.class && !ti.tag && (err = _ksba_ber_read_tl (cms->reader, &ti)))
+ return err;
+
+ if ((ti.class == CLASS_CONTEXT && ti.tag == 1 && ti.is_constructed))
+ {
+ /* Okay, we got an authAttr. We need to do something with it.
+ * However, without sample data it does not make sense to handle
+ * it. Further it is currently useless because in gpgsm we need
+ * to get access to authAttrs before we decrypt the content.
+ * This will require the use of temp files in gpgsm and thus a
+ * larger rework.*/
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ /* err = _ksba_ber_read_tl (cms->reader, &ti); */
+ /* if (err) */
+ /* return err; */
+ /* /\* Skip an end tag. *\/ */
+ /* if (!ti.class && !ti.tag && (err = _ksba_ber_read_tl (cms->reader, &ti))) */
+ /* return err; */
+ }
+
+
+ /* Next comes the mandatory mac. We store it in the CMS. */
+ if (!(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
+ && !ti.is_constructed) || !ti.length)
+ {
+ return gpg_error (GPG_ERR_INV_CMS_OBJ);
+ }
+ xfree (cms->authdata.mac);
+ cms->authdata.mac_len = ti.length;
+ cms->authdata.mac = xtrymalloc (ti.length);
+ if (!cms->authdata.mac)
+ return gpg_error_from_syserror ();
+
+ err = read_buffer (cms->reader, cms->authdata.mac, ti.length);
+ if (err)
+ return err;
+
+ /* No support for unauthAttr. */
+
return 0;
}
diff --git a/src/cms.asn b/src/cms.asn
index 928e9b7..c85b7cd 100644
--- a/src/cms.asn
+++ b/src/cms.asn
@@ -1,472 +1,483 @@
-- cms.asn - CryptographicMessageSyntax ASN.1 Module
-- Copyright (C) 2001 g10 Code GmbH
--
-- This file is part of KSBA.
--
-- KSBA 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.
--
-- KSBA 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 copies of the GNU General Public License
-- and the GNU Lesser General Public License along with this program;
-- if not, see .
--
-----------------------------------------------------------------------
-- This module is based on the one given in appendix A of RFC2630 which
-- exhibits this copyright notice:
--
-- Copyright (C) The Internet Society (1999). All Rights Reserved.
--
-- This document and translations of it may be copied and furnished to
-- others, and derivative works that comment on or otherwise explain it
-- or assist in its implementation may be prepared, copied, published
-- and distributed, in whole or in part, without restriction of any
-- kind, provided that the above copyright notice and this paragraph are
-- included on all such copies and derivative works. However, this
-- document itself may not be modified in any way, such as by removing
-- the copyright notice or references to the Internet Society or other
-- Internet organizations, except as needed for the purpose of
-- developing Internet standards in which case the procedures for
-- copyrights defined in the Internet Standards process must be
-- followed, or as required to translate it into languages other than
-- English.
--
-- The limited permissions granted above are perpetual and will not be
-- revoked by the Internet Society or its successors or assigns.
--
-- This document and the information contained herein is provided on an
-- "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
-- TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
-- BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
-- HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
-- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
------------------------------------------------------------------------
CryptographicMessageSyntax
{ iso(1) member-body(2) us(840) rsadsi(113549)
pkcs(1) pkcs-9(9) smime(16) modules(0) cms(1) }
DEFINITIONS IMPLICIT TAGS ::=
BEGIN
-- EXPORTS All
-- The types and values defined in this module are exported for use in
-- the other ASN.1 modules. Other applications may use them for their
-- own purposes.
-- Out parser does not support IMPORTS, instead we copy them verbatim
-- at the end of this file
--IMPORTS
-- Directory Information Framework (X.501)
-- Name
-- FROM InformationFramework { joint-iso-itu-t ds(5) modules(1)
-- informationFramework(1) 3 }
-- Directory Authentication Framework (X.509)
-- AlgorithmIdentifier, AttributeCertificate, Certificate,
-- CertificateList, CertificateSerialNumber
-- FROM AuthenticationFramework { joint-iso-itu-t ds(5)
-- module(1) authenticationFramework(7) 3 } ;
-- Cryptographic Message Syntax
ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT ANY DEFINED BY contentType }
ContentType ::= OBJECT IDENTIFIER
SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
signerInfos SignerInfos }
DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
SignerInfos ::= SET OF SignerInfo
EncapsulatedContentInfo ::= SEQUENCE {
eContentType ContentType,
eContent [0] EXPLICIT OCTET STRING OPTIONAL }
SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
SignerIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier }
SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
Attribute ::= SEQUENCE {
attrType OBJECT IDENTIFIER,
attrValues SET OF AttributeValue }
AttributeValue ::= ANY
SignatureValue ::= OCTET STRING
EnvelopedData ::= SEQUENCE {
version CMSVersion,
originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
recipientInfos RecipientInfos,
encryptedContentInfo EncryptedContentInfo,
unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
+AuthEnvelopedData ::= SEQUENCE {
+ version CMSVersion,
+ originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ recipientInfos RecipientInfos,
+ authEncryptedContentInfo EncryptedContentInfo,
+ authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+ mac MessageAuthenticationCode,
+ unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
+
OriginatorInfo ::= SEQUENCE {
certs [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT CertificateRevocationLists OPTIONAL }
RecipientInfos ::= SET OF RecipientInfo
EncryptedContentInfo ::= SEQUENCE {
contentType ContentType,
contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL }
EncryptedContent ::= OCTET STRING
UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute
RecipientInfo ::= CHOICE {
ktri KeyTransRecipientInfo,
kari [1] KeyAgreeRecipientInfo,
kekri [2] KEKRecipientInfo,
pwri [3] PasswordRecipientInfo }
EncryptedKey ::= OCTET STRING
KeyTransRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 0 or 2
rid RecipientIdentifier,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
encryptedKey EncryptedKey }
RecipientIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier }
KeyAgreeRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 3
originator [0] EXPLICIT OriginatorIdentifierOrKey,
ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
recipientEncryptedKeys RecipientEncryptedKeys }
OriginatorIdentifierOrKey ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier,
originatorKey [1] OriginatorPublicKey }
OriginatorPublicKey ::= SEQUENCE {
algorithm AlgorithmIdentifier,
publicKey BIT STRING }
RecipientEncryptedKeys ::= SEQUENCE OF RecipientEncryptedKey
RecipientEncryptedKey ::= SEQUENCE {
rid KeyAgreeRecipientIdentifier,
encryptedKey EncryptedKey }
KeyAgreeRecipientIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
rKeyId [0] IMPLICIT RecipientKeyIdentifier }
RecipientKeyIdentifier ::= SEQUENCE {
subjectKeyIdentifier SubjectKeyIdentifier,
date GeneralizedTime OPTIONAL,
other OtherKeyAttribute OPTIONAL }
SubjectKeyIdentifier ::= OCTET STRING
KEKRecipientInfo ::= SEQUENCE {
version CMSVersion, -- always set to 4
kekid KEKIdentifier,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
encryptedKey EncryptedKey }
KEKIdentifier ::= SEQUENCE {
keyIdentifier OCTET STRING,
date GeneralizedTime OPTIONAL,
other OtherKeyAttribute OPTIONAL }
PasswordRecipientInfo ::= SEQUENCE {
version CMSVersion, -- Always set to 0
keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
encryptedKey EncryptedKey }
DigestedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithm DigestAlgorithmIdentifier,
encapContentInfo EncapsulatedContentInfo,
digest Digest }
Digest ::= OCTET STRING
EncryptedData ::= SEQUENCE {
version CMSVersion,
encryptedContentInfo EncryptedContentInfo,
unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
AuthenticatedData ::= SEQUENCE {
version CMSVersion,
originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
recipientInfos RecipientInfos,
macAlgorithm MessageAuthenticationCodeAlgorithm,
digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
encapContentInfo EncapsulatedContentInfo,
authenticatedAttributes [2] IMPLICIT AuthAttributes OPTIONAL,
mac MessageAuthenticationCode,
unauthenticatedAttributes [3] IMPLICIT UnauthAttributes OPTIONAL }
AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
MessageAuthenticationCode ::= OCTET STRING
DigestAlgorithmIdentifier ::= AlgorithmIdentifier
SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier
ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier
CertificateRevocationLists ::= SET OF CertificateList
CertificateChoices ::= CHOICE {
certificate Certificate, -- See X.509
extendedCertificate [0] IMPLICIT ExtendedCertificate -- Obsolete
-- FIXME attrCert [1] IMPLICIT AttributeCertificate -- See X.509 & X9.57
}
CertificateSet ::= SET OF CertificateChoices
IssuerAndSerialNumber ::= SEQUENCE {
issuer Name,
serialNumber CertificateSerialNumber }
CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4) }
UserKeyingMaterial ::= OCTET STRING
OtherKeyAttribute ::= SEQUENCE {
keyAttrId OBJECT IDENTIFIER,
keyAttr ANY DEFINED BY keyAttrId OPTIONAL }
-- CMS Attributes
MessageDigest ::= OCTET STRING
SigningTime ::= Time
Time ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime }
Countersignature ::= SignerInfo
-- Algorithm Identifiers
sha-1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
oiw(14) secsig(3) algorithm(2) 26 }
md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
rsadsi(113549) digestAlgorithm(2) 5 }
id-dsa-with-sha1 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) x9-57 (10040) x9cm(4) 3 }
rsaEncryption OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 1 }
dh-public-number OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) ansi-x942(10046) number-type(2) 1 }
id-alg-ESDH OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) alg(3) 5 }
id-alg-CMS3DESwrap OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) alg(3) 6 }
id-alg-CMSRC2wrap OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) alg(3) 7 }
des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) encryptionAlgorithm(3) 7 }
rc2-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
rsadsi(113549) encryptionAlgorithm(3) 2 }
hMAC-SHA1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
dod(6) internet(1) security(5) mechanisms(5) 8 1 2 }
-- Algorithm Parameters
KeyWrapAlgorithm ::= AlgorithmIdentifier
RC2wrapParameter ::= RC2ParameterVersion
RC2ParameterVersion ::= INTEGER
CBCParameter ::= IV
IV ::= OCTET STRING -- exactly 8 octets
RC2CBCParameter ::= SEQUENCE {
rc2ParameterVersion INTEGER,
iv OCTET STRING } -- exactly 8 octets
-- Content Type Object Identifiers
id-ct-contentInfo OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16)
ct(1) 6 }
id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 }
id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
id-envelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 3 }
id-digestedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 5 }
id-encryptedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs7(7) 6 }
id-ct-authData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
- us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16)
- ct(1) 2 }
+ us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 2 }
+
+id-authEnvelopedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ us(840) rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1) 23 }
-- Attribute Object Identifiers
id-contentType OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 3 }
id-messageDigest OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 4 }
id-signingTime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 5 }
id-countersignature OBJECT IDENTIFIER ::= { iso(1) member-body(2)
us(840) rsadsi(113549) pkcs(1) pkcs9(9) 6 }
-- Obsolete Extended Certificate syntax from PKCS#6
ExtendedCertificate ::= SEQUENCE {
extendedCertificateInfo ExtendedCertificateInfo,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature Signature }
ExtendedCertificateInfo ::= SEQUENCE {
version CMSVersion,
certificate Certificate,
attributes UnauthAttributes }
Signature ::= BIT STRING
--*******************************************
--*********** Included imports ************
--*******************************************
-- Directory Information Framework (X.501)
-- Name
-- FROM InformationFramework { joint-iso-itu-t ds(5) modules(1)
-- informationFramework(1) 3 }
Name ::= CHOICE {
rdnSequence RDNSequence
}
RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
AttributeTypeAndValue ::= SEQUENCE {
type AttributeType,
value AttributeValue
}
AttributeType ::= OBJECT IDENTIFIER
AttributeValue ::= ANY
-- Directory Authentication Framework (X.509)
-- AlgorithmIdentifier, AttributeCertificate, Certificate,
-- CertificateList, CertificateSerialNumber
-- FROM AuthenticationFramework { joint-iso-itu-t ds(5)
-- module(1) authenticationFramework(7) 3 } ;
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
-- Note: AttributeCertificate is not needed
-- We handle a certificate by utilizing our ksba_cert_ functions
Certificate ::= ANY
-- Likewise
CertificateList ::= ANY
CertificateSerialNumber ::= INTEGER
END -- of CryptographicMessageSyntax
diff --git a/src/cms.c b/src/cms.c
index 17a74c8..a5fe693 100644
--- a/src/cms.c
+++ b/src/cms.c
@@ -1,3868 +1,3907 @@
/* cms.c - cryptographic message syntax main functions
* Copyright (C) 2001, 2003, 2004, 2008, 2012, 2020 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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.
*
* KSBA 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 copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
/* References:
+ * RFC-5083 := CMS - Authenticated-Enveloped-Data
+ * RFC-5084 := CMS - AES-GCM
* RFC-5652 := Cryptographic Message Syntax (CMS) (aka STD0070)
* SPHINX := CMS profile developed by the German BSI.
* (see also https://lwn.net/2001/1011/a/german-smime.php3)
* PKCS#7 := Original specification of CMS
*/
#include
#include
#include
#include
#include
#include
#include "util.h"
#include "cms.h"
#include "convert.h"
#include "keyinfo.h"
#include "der-encoder.h"
#include "ber-help.h"
#include "sexp-parse.h"
#include "cert.h"
#include "der-builder.h"
#include "stringbuf.h"
static gpg_error_t ct_parse_data (ksba_cms_t cms);
static gpg_error_t ct_parse_signed_data (ksba_cms_t cms);
static gpg_error_t ct_parse_enveloped_data (ksba_cms_t cms);
static gpg_error_t ct_parse_digested_data (ksba_cms_t cms);
static gpg_error_t ct_parse_encrypted_data (ksba_cms_t cms);
static gpg_error_t ct_build_data (ksba_cms_t cms);
static gpg_error_t ct_build_signed_data (ksba_cms_t cms);
static gpg_error_t ct_build_enveloped_data (ksba_cms_t cms);
static gpg_error_t ct_build_digested_data (ksba_cms_t cms);
static gpg_error_t ct_build_encrypted_data (ksba_cms_t cms);
static struct {
const char *oid;
ksba_content_type_t ct;
gpg_error_t (*parse_handler)(ksba_cms_t);
gpg_error_t (*build_handler)(ksba_cms_t);
} content_handlers[] = {
{ "1.2.840.113549.1.7.1", KSBA_CT_DATA,
ct_parse_data , ct_build_data },
{ "1.2.840.113549.1.7.2", KSBA_CT_SIGNED_DATA,
ct_parse_signed_data , ct_build_signed_data },
{ "1.2.840.113549.1.7.3", KSBA_CT_ENVELOPED_DATA,
ct_parse_enveloped_data, ct_build_enveloped_data },
+ { "1.2.840.113549.1.9.16.1.23", KSBA_CT_AUTHENVELOPED_DATA,
+ ct_parse_enveloped_data, ct_build_enveloped_data },
{ "1.2.840.113549.1.7.5", KSBA_CT_DIGESTED_DATA,
ct_parse_digested_data , ct_build_digested_data },
{ "1.2.840.113549.1.7.6", KSBA_CT_ENCRYPTED_DATA,
ct_parse_encrypted_data, ct_build_encrypted_data },
{ "1.2.840.113549.1.9.16.1.2", KSBA_CT_AUTH_DATA },
{ "1.3.6.1.4.1.311.2.1.4", KSBA_CT_SPC_IND_DATA_CTX,
ct_parse_data , ct_build_data },
{ "1.3.6.1.4.1.11591.2.3.1", KSBA_CT_OPENPGP_KEYBLOCK,
ct_parse_data , ct_build_data },
{ NULL }
};
static const char oidstr_contentType[] = "1.2.840.113549.1.9.3";
/*static char oid_contentType[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x03";*/
static const char oidstr_messageDigest[] = "1.2.840.113549.1.9.4";
static const char oid_messageDigest[9] ="\x2A\x86\x48\x86\xF7\x0D\x01\x09\x04";
static const char oidstr_signingTime[] = "1.2.840.113549.1.9.5";
static const char oid_signingTime[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x05";
static const char oidstr_smimeCapabilities[] = "1.2.840.113549.1.9.15";
#if 0 /* Set to 1 to use this debug helper. */
static void
log_sexp (const char *text, ksba_const_sexp_t p)
{
int level = 0;
gpgrt_log_debug ("%s: ", text);
if (!p)
gpgrt_log_printf ("[none]");
else
{
for (;;)
{
if (*p == '(')
{
gpgrt_log_printf ("%c", *p);
p++;
level++;
}
else if (*p == ')')
{
gpgrt_log_printf ("%c", *p);
p++;
if (--level <= 0 )
return;
}
else if (!digitp (p))
{
gpgrt_log_printf ("[invalid s-exp]");
return;
}
else
{
char *endp;
const unsigned char *s;
unsigned long len, n;
len = strtoul (p, &endp, 10);
p = endp;
if (*p != ':')
{
gpgrt_log_printf ("[invalid s-exp]");
return;
}
p++;
for (s=p,n=0; n < len; n++, s++)
if ( !((*s >= 'a' && *s <= 'z')
|| (*s >= 'A' && *s <= 'Z')
|| (*s >= '0' && *s <= '9')
|| *s == '-' || *s == '.'))
break;
if (n < len)
{
gpgrt_log_printf ("#");
for (n=0; n < len; n++, p++)
gpgrt_log_printf ("%02X", *p);
gpgrt_log_printf ("#");
}
else
{
for (n=0; n < len; n++, p++)
gpgrt_log_printf ("%c", *p);
}
}
}
}
gpgrt_log_printf ("\n");
}
#endif /* debug helper */
/* Helper for read_and_hash_cont(). */
static gpg_error_t
read_hash_block (ksba_cms_t cms, unsigned long nleft)
{
gpg_error_t err;
char buffer[4096];
size_t n, nread;
while (nleft)
{
n = nleft < sizeof (buffer)? nleft : sizeof (buffer);
err = ksba_reader_read (cms->reader, buffer, n, &nread);
if (err)
return err;
nleft -= nread;
if (cms->hash_fnc)
cms->hash_fnc (cms->hash_fnc_arg, buffer, nread);
if (cms->writer)
err = ksba_writer_write (cms->writer, buffer, nread);
if (err)
return err;
}
return 0;
}
/* Copy all the bytes from the reader to the writer and hash them if a
a hash function has been set. The writer may be NULL to just do
the hashing */
static gpg_error_t
read_and_hash_cont (ksba_cms_t cms)
{
gpg_error_t err = 0;
unsigned long nleft;
struct tag_info ti;
if (cms->inner_cont_ndef)
{
for (;;)
{
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
&& !ti.is_constructed)
{ /* next chunk */
nleft = ti.length;
err = read_hash_block (cms, nleft);
if (err)
return err;
}
else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
&& ti.is_constructed)
{ /* next chunk is constructed */
for (;;)
{
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_OCTET_STRING
&& !ti.is_constructed)
{
nleft = ti.length;
err = read_hash_block (cms, nleft);
if (err)
return err;
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag
&& !ti.is_constructed)
break; /* ready with this chunk */
else
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
}
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag
&& !ti.is_constructed)
return 0; /* ready */
else
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
}
}
else
{
/* This is basically the same as above but we allow for
arbitrary types. Not sure whether it is really needed but
right in the beginning of gnupg 1.9 we had at least one
message with didn't used octet strings. Not ethat we don't
do proper NLEFT checking but well why should we validate
these things? Well, it might be nice to have such a feature
but then we should write a more general mechanism to do
that. */
nleft = cms->inner_cont_len;
/* First read the octet string but allow all types here */
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (nleft < ti.nhdr)
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
nleft -= ti.nhdr;
if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
&& ti.is_constructed)
{ /* Next chunk is constructed */
for (;;)
{
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_OCTET_STRING
&& !ti.is_constructed)
{
nleft = ti.length;
err = read_hash_block (cms, nleft);
if (err)
return err;
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag
&& !ti.is_constructed)
break; /* Ready with this chunk */
else
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
}
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag
&& !ti.is_constructed)
return 0; /* ready */
else
{
err = read_hash_block (cms, nleft);
if (err)
return err;
}
}
return 0;
}
/* Copy all the encrypted bytes from the reader to the writer.
Handles indefinite length encoding */
static gpg_error_t
read_encrypted_cont (ksba_cms_t cms)
{
gpg_error_t err = 0;
unsigned long nleft;
char buffer[4096];
size_t n, nread;
if (cms->inner_cont_ndef)
{
struct tag_info ti;
/* fixme: this ist mostly a duplicate of the code in
read_and_hash_cont(). */
for (;;)
{
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
&& !ti.is_constructed)
{ /* next chunk */
nleft = ti.length;
while (nleft)
{
n = nleft < sizeof (buffer)? nleft : sizeof (buffer);
err = ksba_reader_read (cms->reader, buffer, n, &nread);
if (err)
return err;
nleft -= nread;
err = ksba_writer_write (cms->writer, buffer, nread);
if (err)
return err;
}
}
else if (ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OCTET_STRING
&& ti.is_constructed)
{ /* next chunk is constructed */
for (;;)
{
err = _ksba_ber_read_tl (cms->reader, &ti);
if (err)
return err;
if (ti.class == CLASS_UNIVERSAL
&& ti.tag == TYPE_OCTET_STRING
&& !ti.is_constructed)
{
nleft = ti.length;
while (nleft)
{
n = nleft < sizeof (buffer)? nleft : sizeof (buffer);
err = ksba_reader_read (cms->reader, buffer, n, &nread);
if (err)
return err;
nleft -= nread;
if (cms->writer)
err = ksba_writer_write (cms->writer, buffer, nread);
if (err)
return err;
}
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag
&& !ti.is_constructed)
break; /* ready with this chunk */
else
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
}
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag
&& !ti.is_constructed)
return 0; /* ready */
else
return gpg_error (GPG_ERR_ENCODING_PROBLEM);
}
}
else
{
nleft = cms->inner_cont_len;
while (nleft)
{
n = nleft < sizeof (buffer)? nleft : sizeof (buffer);
err = ksba_reader_read (cms->reader, buffer, n, &nread);
if (err)
return err;
nleft -= nread;
err = ksba_writer_write (cms->writer, buffer, nread);
if (err)
return err;
}
}
return 0;
}
/* copy data from reader to writer. Assume that it is an octet string
and insert undefinite length headers where needed */
static gpg_error_t
write_encrypted_cont (ksba_cms_t cms)
{
gpg_error_t err = 0;
char buffer[4096];
size_t nread;
/* we do it the simple way: the parts are made up from the chunks we
got from the read function.
Fixme: We should write the tag here, and write a definite length
header if everything fits into our local buffer. Actually pretty
simple to do, but I am too lazy right now. */
while (!(err = ksba_reader_read (cms->reader, buffer,
sizeof buffer, &nread)) )
{
err = _ksba_ber_write_tl (cms->writer, TYPE_OCTET_STRING,
CLASS_UNIVERSAL, 0, nread);
if (!err)
err = ksba_writer_write (cms->writer, buffer, nread);
}
if (gpg_err_code (err) == GPG_ERR_EOF) /* write the end tag */
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
return err;
}
/* Figure out whether the data read from READER is a CMS object and
return its content type. This function does only peek at the
READER and tries to identify the type with best effort. Because of
the ubiquity of the stupid and insecure pkcs#12 format, the
function will also identify those files and return KSBA_CT_PKCS12;
there is and will be no other pkcs#12 support in this library. */
ksba_content_type_t
ksba_cms_identify (ksba_reader_t reader)
{
struct tag_info ti;
unsigned char buffer[24];
const unsigned char*p;
size_t n, count;
char *oid;
int i;
int maybe_p12 = 0;
if (!reader)
return KSBA_CT_NONE; /* oops */
/* This is a common example of a CMS object - it is obvious that we
only need to read a few bytes to get to the OID:
30 82 0B 59 06 09 2A 86 48 86 F7 0D 01 07 02 A0 82 0B 4A 30 82 0B 46 02
----------- ++++++++++++++++++++++++++++++++
SEQUENCE OID (signedData)
(2 byte len)
For a pkcs12 message we have this:
30 82 08 59 02 01 03 30 82 08 1F 06 09 2A 86 48 86 F7 0D 01 07 01 A0 82
----------- ++++++++ ----------- ++++++++++++++++++++++++++++++++
SEQUENCE INTEGER SEQUENCE OID (data)
This we need to read at least 22 bytes, we add 2 bytes to cope with
length headers store with 4 bytes.
*/
for (count = sizeof buffer; count; count -= n)
{
if (ksba_reader_read (reader, buffer+sizeof (buffer)-count, count, &n))
return KSBA_CT_NONE; /* too short */
}
n = sizeof buffer;
if (ksba_reader_unread (reader, buffer, n))
return KSBA_CT_NONE; /* oops */
p = buffer;
if (_ksba_ber_parse_tl (&p, &n, &ti))
return KSBA_CT_NONE;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return KSBA_CT_NONE;
if (_ksba_ber_parse_tl (&p, &n, &ti))
return KSBA_CT_NONE;
if ( ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_INTEGER
&& !ti.is_constructed && ti.length == 1 && n && *p == 3)
{
maybe_p12 = 1;
p++;
n--;
if (_ksba_ber_parse_tl (&p, &n, &ti))
return KSBA_CT_NONE;
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_SEQUENCE
&& ti.is_constructed) )
return KSBA_CT_NONE;
if (_ksba_ber_parse_tl (&p, &n, &ti))
return KSBA_CT_NONE;
}
if ( !(ti.class == CLASS_UNIVERSAL && ti.tag == TYPE_OBJECT_ID
&& !ti.is_constructed && ti.length) || ti.length > n)
return KSBA_CT_NONE;
oid = ksba_oid_to_str (p, ti.length);
if (!oid)
return KSBA_CT_NONE; /* out of core */
for (i=0; content_handlers[i].oid; i++)
{
if (!strcmp (content_handlers[i].oid, oid))
break;
}
ksba_free(oid);
if (!content_handlers[i].oid)
return KSBA_CT_NONE; /* unknown */
if (maybe_p12 && (content_handlers[i].ct == KSBA_CT_DATA
|| content_handlers[i].ct == KSBA_CT_SIGNED_DATA))
return KSBA_CT_PKCS12;
return content_handlers[i].ct;
}
/**
* ksba_cms_new:
*
* Create a new and empty CMS object
*
* Return value: A CMS object or an error code.
**/
gpg_error_t
ksba_cms_new (ksba_cms_t *r_cms)
{
*r_cms = xtrycalloc (1, sizeof **r_cms);
if (!*r_cms)
return gpg_error_from_errno (errno);
return 0;
}
/* Release a list of value trees. */
static void
release_value_tree (struct value_tree_s *tree)
{
while (tree)
{
struct value_tree_s *tmp = tree->next;
_ksba_asn_release_nodes (tree->root);
xfree (tree->image);
xfree (tree);
tree = tmp;
}
}
/**
* ksba_cms_release:
* @cms: A CMS object
*
* Release a CMS object.
**/
void
ksba_cms_release (ksba_cms_t cms)
{
if (!cms)
return;
xfree (cms->content.oid);
while (cms->digest_algos)
{
struct oidlist_s *ol = cms->digest_algos->next;
xfree (cms->digest_algos->oid);
xfree (cms->digest_algos);
cms->digest_algos = ol;
}
while (cms->cert_list)
{
struct certlist_s *cl = cms->cert_list->next;
ksba_cert_release (cms->cert_list->cert);
xfree (cms->cert_list->enc_val.algo);
xfree (cms->cert_list->enc_val.value);
xfree (cms->cert_list->enc_val.ecdh.e);
xfree (cms->cert_list->enc_val.ecdh.wrap_algo);
xfree (cms->cert_list->enc_val.ecdh.encr_algo);
xfree (cms->cert_list);
cms->cert_list = cl;
}
while (cms->cert_info_list)
{
struct certlist_s *cl = cms->cert_info_list->next;
ksba_cert_release (cms->cert_info_list->cert);
xfree (cms->cert_info_list->enc_val.algo);
xfree (cms->cert_info_list->enc_val.value);
xfree (cms->cert_info_list);
cms->cert_info_list = cl;
}
xfree (cms->inner_cont_oid);
xfree (cms->encr_algo_oid);
xfree (cms->encr_iv);
- xfree (cms->data.digest);
+ xfree (cms->authdata.mac);
+ xfree (cms->authdata.attr);
while (cms->signer_info)
{
struct signer_info_s *tmp = cms->signer_info->next;
_ksba_asn_release_nodes (cms->signer_info->root);
xfree (cms->signer_info->image);
xfree (cms->signer_info->cache.digest_algo);
xfree (cms->signer_info);
cms->signer_info = tmp;
}
release_value_tree (cms->recp_info);
while (cms->sig_val)
{
struct sig_val_s *tmp = cms->sig_val->next;
xfree (cms->sig_val->algo);
xfree (cms->sig_val->value);
xfree (cms->sig_val->ecc.r);
xfree (cms->sig_val);
cms->sig_val = tmp;
}
while (cms->capability_list)
{
struct oidparmlist_s *tmp = cms->capability_list->next;
xfree (cms->capability_list->oid);
xfree (cms->capability_list);
cms->capability_list = tmp;
}
xfree (cms);
}
gpg_error_t
ksba_cms_set_reader_writer (ksba_cms_t cms, ksba_reader_t r, ksba_writer_t w)
{
if (!cms || !(r || w))
return gpg_error (GPG_ERR_INV_VALUE);
if ((r && cms->reader) || (w && cms->writer) )
return gpg_error (GPG_ERR_CONFLICT); /* already set */
cms->reader = r;
cms->writer = w;
return 0;
}
gpg_error_t
ksba_cms_parse (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason)
{
gpg_error_t err;
int i;
if (!cms || !r_stopreason)
return gpg_error (GPG_ERR_INV_VALUE);
*r_stopreason = KSBA_SR_RUNNING;
if (!cms->stop_reason)
{ /* Initial state: start parsing */
err = _ksba_cms_parse_content_info (cms);
if (err)
return err;
for (i=0; content_handlers[i].oid; i++)
{
if (!strcmp (content_handlers[i].oid, cms->content.oid))
break;
}
if (!content_handlers[i].oid)
return gpg_error (GPG_ERR_UNKNOWN_CMS_OBJ);
if (!content_handlers[i].parse_handler)
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
cms->content.ct = content_handlers[i].ct;
cms->content.handler = content_handlers[i].parse_handler;
cms->stop_reason = KSBA_SR_GOT_CONTENT;
}
else if (cms->content.handler)
{
err = cms->content.handler (cms);
if (err)
return err;
}
else
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
*r_stopreason = cms->stop_reason;
return 0;
}
gpg_error_t
ksba_cms_build (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason)
{
gpg_error_t err;
if (!cms || !r_stopreason)
return gpg_error (GPG_ERR_INV_VALUE);
*r_stopreason = KSBA_SR_RUNNING;
if (!cms->stop_reason)
{ /* Initial state: check that the content handler is known */
if (!cms->writer)
return gpg_error (GPG_ERR_MISSING_ACTION);
if (!cms->content.handler)
return gpg_error (GPG_ERR_MISSING_ACTION);
if (!cms->inner_cont_oid)
return gpg_error (GPG_ERR_MISSING_ACTION);
cms->stop_reason = KSBA_SR_GOT_CONTENT;
}
else if (cms->content.handler)
{
err = cms->content.handler (cms);
if (err)
return err;
}
else
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
*r_stopreason = cms->stop_reason;
return 0;
}
/* Return the content type. A WHAT of 0 returns the real content type
whereas a 1 returns the inner content type.
*/
ksba_content_type_t
ksba_cms_get_content_type (ksba_cms_t cms, int what)
{
int i;
if (!cms)
return 0;
if (!what)
return cms->content.ct;
if (what == 1 && cms->inner_cont_oid)
{
for (i=0; content_handlers[i].oid; i++)
{
if (!strcmp (content_handlers[i].oid, cms->inner_cont_oid))
return content_handlers[i].ct;
}
}
return 0;
}
/* Return the object ID of the current cms. This is a constant string
valid as long as the context is valid and no new parse is
started. */
const char *
ksba_cms_get_content_oid (ksba_cms_t cms, int what)
{
if (!cms)
return NULL;
if (!what)
return cms->content.oid;
if (what == 1)
return cms->inner_cont_oid;
if (what == 2)
return cms->encr_algo_oid;
return NULL;
}
/* Copy the initialization vector into iv and its len into ivlen.
- The caller should provide a suitable large buffer */
+ The caller should proncrvide a suitable large buffer */
gpg_error_t
ksba_cms_get_content_enc_iv (ksba_cms_t cms, void *iv,
size_t maxivlen, size_t *ivlen)
{
if (!cms || !iv || !ivlen)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cms->encr_ivlen)
return gpg_error (GPG_ERR_NO_DATA);
if (cms->encr_ivlen > maxivlen)
return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
memcpy (iv, cms->encr_iv, cms->encr_ivlen);
*ivlen = cms->encr_ivlen;
return 0;
}
/**
* ksba_cert_get_digest_algo_list:
- * @cert: Initialized certificate object
+ * @cms: CMS object
* @idx: enumerator
*
* Figure out the the digest algorithm used for the signature and
* return its OID. Note that the algos returned are just hints on
* what to hash.
*
* Return value: NULL for no more algorithms or a string valid as long
* as the the cms object is valid.
**/
const char *
ksba_cms_get_digest_algo_list (ksba_cms_t cms, int idx)
{
struct oidlist_s *ol;
if (!cms)
return NULL;
for (ol=cms->digest_algos; ol && idx; ol = ol->next, idx-- )
;
if (!ol)
return NULL;
return ol->oid;
}
/**
* ksba_cms_get_issuer_serial:
* @cms: CMS object
* @idx: index number
* @r_issuer: returns the issuer
* @r_serial: returns the serial number
*
* This functions returns the issuer and serial number either from the
* sid or the rid elements of a CMS object.
*
* Return value: 0 on success or an error code. An error code of -1
* is returned to indicate that there is no issuer with that idx,
* GPG_ERR_NO_DATA is returned to indicate that there is no issuer at
* all.
**/
gpg_error_t
ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx,
char **r_issuer, ksba_sexp_t *r_serial)
{
gpg_error_t err;
const char *issuer_path, *serial_path;
AsnNode root;
const unsigned char *image;
AsnNode n;
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
if (cms->signer_info)
{
struct signer_info_s *si;
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return -1;
root = si->root;
image = si->image;
}
else if (cms->recp_info)
{
struct value_tree_s *tmp;
for (tmp=cms->recp_info; tmp && idx; tmp=tmp->next, idx-- )
;
if (!tmp)
return -1;
root = tmp->root;
image = tmp->image;
}
else
return gpg_error (GPG_ERR_NO_DATA);
if (cms->signer_info)
{
issuer_path = "SignerInfo.sid.issuerAndSerialNumber.issuer";
serial_path = "SignerInfo.sid.issuerAndSerialNumber.serialNumber";
}
else if (cms->recp_info)
{
/* Find the choice to use. */
n = _ksba_asn_find_node (root, "RecipientInfo.+");
if (!n || !n->name)
return gpg_error (GPG_ERR_NO_VALUE);
if (!strcmp (n->name, "ktri"))
{
issuer_path = "ktri.rid.issuerAndSerialNumber.issuer";
serial_path = "ktri.rid.issuerAndSerialNumber.serialNumber";
}
else if (!strcmp (n->name, "kari"))
{
issuer_path = ("kari..recipientEncryptedKeys"
"..rid.issuerAndSerialNumber.issuer");
serial_path = ("kari..recipientEncryptedKeys"
"..rid.issuerAndSerialNumber.serialNumber");
}
else if (!strcmp (n->name, "kekri"))
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
else if (!strcmp (n->name, "pwri"))
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
else
return gpg_error (GPG_ERR_INV_CMS_OBJ);
root = n;
}
if (r_issuer)
{
n = _ksba_asn_find_node (root, issuer_path);
if (!n || !n->down)
return gpg_error (GPG_ERR_NO_VALUE);
n = n->down; /* dereference the choice node */
if (n->off == -1)
{
/* fputs ("get_issuer problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return gpg_error (GPG_ERR_GENERAL);
}
err = _ksba_dn_to_str (image, n, r_issuer);
if (err)
return err;
}
if (r_serial)
{
char numbuf[22];
int numbuflen;
unsigned char *p;
/* fixme: we do not release the r_issuer stuff on error */
n = _ksba_asn_find_node (root, serial_path);
if (!n)
return gpg_error (GPG_ERR_NO_VALUE);
if (n->off == -1)
{
/* fputs ("get_serial problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return gpg_error (GPG_ERR_GENERAL);
}
sprintf (numbuf,"(%u:", (unsigned int)n->len);
numbuflen = strlen (numbuf);
p = xtrymalloc (numbuflen + n->len + 2);
if (!p)
return gpg_error (GPG_ERR_ENOMEM);
strcpy (p, numbuf);
memcpy (p+numbuflen, image + n->off + n->nhdr, n->len);
p[numbuflen + n->len] = ')';
p[numbuflen + n->len + 1] = 0;
*r_serial = p;
}
return 0;
}
/**
* ksba_cms_get_digest_algo:
* @cms: CMS object
* @idx: index of signer
*
* Figure out the the digest algorithm used by the signer @idx return
* its OID. This is the algorithm acually used to calculate the
* signature.
*
* Return value: NULL for no such signer or a constn string valid as
* long as the CMS object lives.
**/
const char *
ksba_cms_get_digest_algo (ksba_cms_t cms, int idx)
{
AsnNode n;
char *algo;
struct signer_info_s *si;
if (!cms)
return NULL;
if (!cms->signer_info)
return NULL;
if (idx < 0)
return NULL;
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return NULL;
if (si->cache.digest_algo)
return si->cache.digest_algo;
n = _ksba_asn_find_node (si->root, "SignerInfo.digestAlgorithm.algorithm");
algo = _ksba_oid_node_to_str (si->image, n);
if (algo)
{
si->cache.digest_algo = algo;
}
return algo;
}
/**
* ksba_cms_get_cert:
* @cms: CMS object
* @idx: enumerator
*
* Get the certificate out of a CMS. The caller should use this in a
* loop to get all certificates. The returned certificate is a
* shallow copy of the original one; the caller must still use
* ksba_cert_release() to free it.
*
* Return value: A Certificate object or NULL for end of list or error
**/
ksba_cert_t
ksba_cms_get_cert (ksba_cms_t cms, int idx)
{
struct certlist_s *cl;
if (!cms || idx < 0)
return NULL;
for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
;
if (!cl)
return NULL;
ksba_cert_ref (cl->cert);
return cl->cert;
}
/*
- Return the extension attribute messageDigest
-*/
+ * Return the extension attribute messageDigest
+ * or for authenvelopeddata the MAC.
+ */
gpg_error_t
ksba_cms_get_message_digest (ksba_cms_t cms, int idx,
char **r_digest, size_t *r_digest_len)
{
AsnNode nsiginfo, n;
struct signer_info_s *si;
if (!cms || !r_digest || !r_digest_len)
return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Hack to return the MAC/authtag value or the authAttr. */
+ if (cms->content.ct == KSBA_CT_AUTHENVELOPED_DATA)
+ {
+ if (!idx) /* Return authtag. */
+ {
+ if (!cms->authdata.mac || !cms->authdata.mac_len)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ *r_digest = xtrymalloc (cms->authdata.mac_len);
+ if (!*r_digest)
+ return gpg_error_from_syserror ();
+ memcpy (*r_digest, cms->authdata.mac, cms->authdata.mac_len);
+ *r_digest_len = cms->authdata.mac_len;
+ }
+ else if (idx == 1) /* Return authAttr. */
+ {
+ if (!cms->authdata.attr || !cms->authdata.attr_len)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ *r_digest = xtrymalloc (cms->authdata.attr_len);
+ if (!*r_digest)
+ return gpg_error_from_syserror ();
+ memcpy (*r_digest, cms->authdata.attr, cms->authdata.attr_len);
+ *r_digest_len = cms->authdata.attr_len;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_INDEX);
+
+ return 0;
+ }
+
+
if (!cms->signer_info)
return gpg_error (GPG_ERR_NO_DATA);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return -1;
*r_digest = NULL;
*r_digest_len = 0;
nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs");
if (!nsiginfo)
return gpg_error (GPG_ERR_BUG);
n = _ksba_asn_find_type_value (si->image, nsiginfo, 0,
oid_messageDigest, DIM(oid_messageDigest));
if (!n)
return 0; /* this is okay, because the element is optional */
/* check that there is only one */
if (_ksba_asn_find_type_value (si->image, nsiginfo, 1,
oid_messageDigest, DIM(oid_messageDigest)))
return gpg_error (GPG_ERR_DUP_VALUE);
/* the value is is a SET OF OCTECT STRING but the set must have
excactly one OCTECT STRING. (rfc2630 11.2) */
if ( !(n->type == TYPE_SET_OF && n->down
&& n->down->type == TYPE_OCTET_STRING && !n->down->right))
return gpg_error (GPG_ERR_INV_CMS_OBJ);
n = n->down;
if (n->off == -1)
return gpg_error (GPG_ERR_BUG);
*r_digest_len = n->len;
*r_digest = xtrymalloc (n->len);
if (!*r_digest)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (*r_digest, si->image + n->off + n->nhdr, n->len);
return 0;
}
/* Return the extension attribute signing time, which may be empty for no
signing time available. */
gpg_error_t
ksba_cms_get_signing_time (ksba_cms_t cms, int idx, ksba_isotime_t r_sigtime)
{
AsnNode nsiginfo, n;
struct signer_info_s *si;
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
*r_sigtime = 0;
if (!cms->signer_info)
return gpg_error (GPG_ERR_NO_DATA);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return -1;
*r_sigtime = 0;
nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs");
if (!nsiginfo)
return 0; /* This is okay because signedAttribs are optional. */
n = _ksba_asn_find_type_value (si->image, nsiginfo, 0,
oid_signingTime, DIM(oid_signingTime));
if (!n)
return 0; /* This is okay because signing time is optional. */
/* check that there is only one */
if (_ksba_asn_find_type_value (si->image, nsiginfo, 1,
oid_signingTime, DIM(oid_signingTime)))
return gpg_error (GPG_ERR_DUP_VALUE);
/* the value is is a SET OF CHOICE but the set must have
excactly one CHOICE of generalized or utctime. (rfc2630 11.3) */
if ( !(n->type == TYPE_SET_OF && n->down
&& (n->down->type == TYPE_GENERALIZED_TIME
|| n->down->type == TYPE_UTC_TIME)
&& !n->down->right))
return gpg_error (GPG_ERR_INV_CMS_OBJ);
n = n->down;
if (n->off == -1)
return gpg_error (GPG_ERR_BUG);
return _ksba_asntime_to_iso (si->image + n->off + n->nhdr, n->len,
n->type == TYPE_UTC_TIME, r_sigtime);
}
/* Return a list of OIDs stored as signed attributes for the signature
number IDX. All the values (OIDs) for the the requested OID REQOID
are returned delimited by a linefeed. Caller must free that
list. -1 is returned when IDX is larger than the number of
signatures, GPG_ERR_No_Data is returned when there is no such
attribute for the given signer. */
gpg_error_t
ksba_cms_get_sigattr_oids (ksba_cms_t cms, int idx,
const char *reqoid, char **r_value)
{
gpg_error_t err;
AsnNode nsiginfo, n;
struct signer_info_s *si;
unsigned char *reqoidbuf;
size_t reqoidlen;
char *retstr = NULL;
int i;
if (!cms || !r_value)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cms->signer_info)
return gpg_error (GPG_ERR_NO_DATA);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
*r_value = NULL;
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return -1; /* no more signers */
nsiginfo = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs");
if (!nsiginfo)
return -1; /* this is okay, because signedAttribs are optional */
err = ksba_oid_from_str (reqoid, &reqoidbuf, &reqoidlen);
if(err)
return err;
for (i=0; (n = _ksba_asn_find_type_value (si->image, nsiginfo,
i, reqoidbuf, reqoidlen)); i++)
{
char *line, *p;
/* the value is is a SET OF OBJECT ID but the set must have
excactly one OBJECT ID. (rfc2630 11.1) */
if ( !(n->type == TYPE_SET_OF && n->down
&& n->down->type == TYPE_OBJECT_ID && !n->down->right))
{
xfree (reqoidbuf);
xfree (retstr);
return gpg_error (GPG_ERR_INV_CMS_OBJ);
}
n = n->down;
if (n->off == -1)
{
xfree (reqoidbuf);
xfree (retstr);
return gpg_error (GPG_ERR_BUG);
}
p = _ksba_oid_node_to_str (si->image, n);
if (!p)
{
xfree (reqoidbuf);
xfree (retstr);
return gpg_error (GPG_ERR_INV_CMS_OBJ);
}
if (!retstr)
line = retstr = xtrymalloc (strlen (p) + 2);
else
{
char *tmp = xtryrealloc (retstr,
strlen (retstr) + 1 + strlen (p) + 2);
if (!tmp)
line = NULL;
else
{
retstr = tmp;
line = stpcpy (retstr + strlen (retstr), "\n");
}
}
if (!line)
{
xfree (reqoidbuf);
xfree (retstr);
xfree (p);
return gpg_error (GPG_ERR_ENOMEM);
}
strcpy (line, p);
xfree (p);
}
xfree (reqoidbuf);
if (!n && !i)
return -1; /* no such attribute */
*r_value = retstr;
return 0;
}
/**
* ksba_cms_get_sig_val:
* @cms: CMS object
* @idx: index of signer
*
* Return the actual signature of signer @idx in a format suitable to
* be used as input to Libgcrypt's verification function. The caller
* must free the returned string.
*
* Return value: NULL or a string with a S-Exp.
**/
ksba_sexp_t
ksba_cms_get_sig_val (ksba_cms_t cms, int idx)
{
AsnNode n, n2;
gpg_error_t err;
ksba_sexp_t string;
struct signer_info_s *si;
if (!cms)
return NULL;
if (!cms->signer_info)
return NULL;
if (idx < 0)
return NULL;
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return NULL;
n = _ksba_asn_find_node (si->root, "SignerInfo.signatureAlgorithm");
if (!n)
return NULL;
if (n->off == -1)
{
/* fputs ("ksba_cms_get_sig_val problem at node:\n", stderr); */
/* _ksba_asn_node_dump_all (n, stderr); */
return NULL;
}
n2 = n->right; /* point to the actual value */
err = _ksba_sigval_to_sexp (si->image + n->off,
n->nhdr + n->len
+ ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)),
&string);
if (err)
return NULL;
return string;
}
/* Helper to dump a S-expression. */
#if 0
static void
dbg_print_sexp (ksba_const_sexp_t p)
{
int level = 0;
if (!p)
fputs ("[none]", stdout);
else
{
for (;;)
{
if (*p == '(')
{
putchar (*p);
p++;
level++;
}
else if (*p == ')')
{
putchar (*p);
p++;
if (--level <= 0 )
{
putchar ('\n');
return;
}
}
else if (!digitp (p))
{
fputs ("[invalid s-exp]\n", stdout);
return;
}
else
{
const unsigned char *s;
char *endp;
unsigned long len, n;
len = strtoul (p, &endp, 10);
p = endp;
if (*p != ':')
{
fputs ("[invalid s-exp]\n", stdout);
return;
}
p++;
for (s=p,n=0; n < len; n++, s++)
if ( !((*s >= 'a' && *s <= 'z')
|| (*s >= 'A' && *s <= 'Z')
|| (*s >= '0' && *s <= '9')
|| *s == '-' || *s == '.'))
break;
if (n < len)
{
putchar('#');
for (n=0; n < len; n++, p++)
printf ("%02X", *p);
putchar('#');
}
else
{
for (n=0; n < len; n++, p++)
putchar (*p);
}
}
}
}
putchar ('\n');
}
#endif /* 0 */
/**
* ksba_cms_get_enc_val:
* @cms: CMS object
* @idx: index of recipient info
*
* Return the encrypted value (the session key) of recipient @idx in a
* format suitable to be used as input to Libgcrypt's decryption
* function. The caller must free the returned string.
*
* Return value: NULL or a string with a S-Exp.
**/
ksba_sexp_t
ksba_cms_get_enc_val (ksba_cms_t cms, int idx)
{
AsnNode root, n, n2;
gpg_error_t err;
ksba_sexp_t string = NULL;
struct value_tree_s *vt;
char *keyencralgo = NULL; /* Key encryption algo. */
char *parm = NULL; /* Helper to get the parms of kencralgo. */
size_t parmlen;
char *parm2 = NULL;
size_t parm2len;
char *parm3 = NULL;
size_t parm3len;
char *keywrapalgo = NULL; /* Key wrap algo. */
char *keyderivealgo = NULL; /* Key derive algo. */
struct tag_info ti;
const unsigned char *der;
size_t derlen;
if (!cms)
return NULL;
if (!cms->recp_info)
return NULL;
if (idx < 0)
return NULL;
for (vt=cms->recp_info; vt && idx; vt=vt->next, idx--)
;
if (!vt)
return NULL; /* No value at this IDX */
/* Find the choice to use. */
root = _ksba_asn_find_node (vt->root, "RecipientInfo.+");
if (!root || !root->name)
return NULL;
if (!strcmp (root->name, "ktri"))
{
n = _ksba_asn_find_node (root, "ktri.keyEncryptionAlgorithm");
if (!n || n->off == -1)
return NULL;
n2 = n->right; /* point to the actual value */
err = _ksba_encval_to_sexp
(vt->image + n->off,
n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)),
&string);
}
else if (!strcmp (root->name, "kari"))
{
/* _ksba_asn_node_dump_all (root, stderr); */
/* Get the encrypted key. Result is in (DER,DERLEN) */
n = _ksba_asn_find_node (root, ("kari..recipientEncryptedKeys"
"..encryptedKey"));
if (!n || n->off == -1)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
der = vt->image + n->off;
derlen = n->nhdr + n->len;
err = parse_octet_string (&der, &derlen, &ti);
if (err)
goto leave;
derlen = ti.length;
/* gpgrt_log_printhex (der, derlen, "%s: encryptedKey", __func__); */
/* Get the KEK algos. */
n = _ksba_asn_find_node (root, "kari..keyEncryptionAlgorithm");
if (!n || n->off == -1)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
err = _ksba_parse_algorithm_identifier2 (vt->image + n->off,
n->nhdr + n->len, NULL,
&keyencralgo, &parm, &parmlen);
if (err)
goto leave;
if (!parm)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
err = _ksba_parse_algorithm_identifier (parm, parmlen,NULL, &keywrapalgo);
if (err)
goto leave;
/* gpgrt_log_debug ("%s: keyencralgo='%s'\n", __func__, keyencralgo); */
/* gpgrt_log_debug ("%s: keywrapalgo='%s'\n", __func__, keywrapalgo); */
/* Get the ephemeral public key. */
n = _ksba_asn_find_node (root, "kari..originator..originatorKey");
if (!n || n->off == -1)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
err = _ksba_encval_kari_to_sexp (vt->image + n->off, n->nhdr + n->len,
keyencralgo, keywrapalgo, der, derlen,
&string);
if (err)
goto leave;
/* gpgrt_log_debug ("%s: encryptedKey:\n", __func__); */
/* dbg_print_sexp (string); */
}
else if (!strcmp (root->name, "kekri"))
return NULL; /*GPG_ERR_UNSUPPORTED_CMS_OBJ*/
else if (!strcmp (root->name, "pwri"))
{
/* _ksba_asn_node_dump_all (root, stderr); */
n = _ksba_asn_find_node (root, "pwri..keyEncryptionAlgorithm");
if (!n || n->off == -1)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
err = _ksba_parse_algorithm_identifier2 (vt->image + n->off,
n->nhdr + n->len, NULL,
&keyencralgo, &parm, &parmlen);
if (err)
goto leave;
if (strcmp (keyencralgo, "1.2.840.113549.1.9.16.3.9"))
{
/* pwri requires this and only this OID. */
err = gpg_error (GPG_ERR_INV_CMS_OBJ);
goto leave;
}
if (!parm)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
/* gpgrt_log_printhex (parm, parmlen, "parms"); */
err = _ksba_parse_algorithm_identifier2 (parm, parmlen, NULL,
&keywrapalgo, &parm2, &parm2len);
if (err)
goto leave;
/* gpgrt_log_debug ("%s: keywrapalgo='%s'\n", __func__, keywrapalgo); */
/* gpgrt_log_printhex (parm2, parm2len, "parm:"); */
n = _ksba_asn_find_node (root, "pwri..keyDerivationAlgorithm");
if (!n || n->off == -1)
{
/* Not found but that is okay becuase it is optional. */
}
else
{
err = _ksba_parse_algorithm_identifier3 (vt->image + n->off,
n->nhdr + n->len, 0xa0, NULL,
&keyderivealgo,
&parm3, &parm3len, NULL);
if (err)
goto leave;
}
n = _ksba_asn_find_node (root, "pwri..encryptedKey");
if (!n || n->off == -1)
{
err = gpg_error (GPG_ERR_INV_KEYINFO);
goto leave;
}
der = vt->image + n->off;
derlen = n->nhdr + n->len;
err = parse_octet_string (&der, &derlen, &ti);
if (err)
goto leave;
derlen = ti.length;
/* gpgrt_log_printhex (der, derlen, "encryptedKey:"); */
/* Build the s-expression:
* (enc-val
* (pwri
* (derive-algo ) --| both are optional
* (derive-parm ) --|
* (encr-algo )
* (encr-parm )
* (encr-key ))) -- this is the encrypted session key
*/
{
struct stringbuf sb;
init_stringbuf (&sb, 200);
put_stringbuf (&sb, "(7:enc-val(4:pwri");
if (keyderivealgo && parm3)
{
put_stringbuf (&sb, "(11:derive-algo");
put_stringbuf_sexp (&sb, keyderivealgo);
put_stringbuf (&sb, ")(11:derive-parm");
put_stringbuf_mem_sexp (&sb, parm3, parm3len);
put_stringbuf (&sb, ")");
}
put_stringbuf (&sb, "(9:encr-algo");
put_stringbuf_sexp (&sb, keywrapalgo);
put_stringbuf (&sb, ")(9:encr-parm");
put_stringbuf_mem_sexp (&sb, parm2, parm2len);
put_stringbuf (&sb, ")(8:encr-key");
put_stringbuf_mem_sexp (&sb, der, derlen);
put_stringbuf (&sb, ")))");
string = get_stringbuf (&sb);
if (!string)
err = gpg_error_from_syserror ();
}
}
else
return NULL; /*GPG_ERR_INV_CMS_OBJ*/
leave:
xfree (keyencralgo);
xfree (keywrapalgo);
xfree (keyderivealgo);
xfree (parm);
xfree (parm2);
xfree (parm3);
if (err)
{
/* gpgrt_log_debug ("%s: error: %s\n", __func__, gpg_strerror (err)); */
return NULL;
}
return string;
}
/* Provide a hash function so that we are able to hash the data */
void
ksba_cms_set_hash_function (ksba_cms_t cms,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg)
{
if (cms)
{
cms->hash_fnc = hash_fnc;
cms->hash_fnc_arg = hash_fnc_arg;
}
}
/* hash the signed attributes of the given signer */
gpg_error_t
ksba_cms_hash_signed_attrs (ksba_cms_t cms, int idx)
{
AsnNode n;
struct signer_info_s *si;
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cms->hash_fnc)
return gpg_error (GPG_ERR_MISSING_ACTION);
if (idx < 0)
return -1;
for (si=cms->signer_info; si && idx; si = si->next, idx-- )
;
if (!si)
return -1;
n = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs");
if (!n || n->off == -1)
return gpg_error (GPG_ERR_NO_VALUE);
/* We don't hash the implicit tag [0] but a SET tag */
cms->hash_fnc (cms->hash_fnc_arg, "\x31", 1);
cms->hash_fnc (cms->hash_fnc_arg,
si->image + n->off + 1, n->nhdr + n->len - 1);
return 0;
}
/*
Code to create CMS structures
*/
/**
* ksba_cms_set_content_type:
* @cms: A CMS object
* @what: 0 for content type, 1 for inner content type
* @type: Type constant
*
* Set the content type used for build operations. This should be the
* first operation before starting to create a CMS message.
*
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_cms_set_content_type (ksba_cms_t cms, int what, ksba_content_type_t type)
{
int i;
char *oid;
if (!cms || what < 0 || what > 1 )
return gpg_error (GPG_ERR_INV_VALUE);
for (i=0; content_handlers[i].oid; i++)
{
if (content_handlers[i].ct == type)
break;
}
if (!content_handlers[i].oid)
return gpg_error (GPG_ERR_UNKNOWN_CMS_OBJ);
if (!content_handlers[i].build_handler)
return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
oid = xtrystrdup (content_handlers[i].oid);
if (!oid)
return gpg_error (GPG_ERR_ENOMEM);
if (!what)
{
cms->content.oid = oid;
cms->content.ct = content_handlers[i].ct;
cms->content.handler = content_handlers[i].build_handler;
}
else
{
cms->inner_cont_oid = oid;
}
return 0;
}
/**
* ksba_cms_add_digest_algo:
* @cms: A CMS object
* @oid: A stringified object OID describing the hash algorithm
*
* Set the algorithm to be used for creating the hash. Note, that we
* currently can't do a per-signer hash.
*
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_cms_add_digest_algo (ksba_cms_t cms, const char *oid)
{
struct oidlist_s *ol;
if (!cms || !oid)
return gpg_error (GPG_ERR_INV_VALUE);
ol = xtrymalloc (sizeof *ol);
if (!ol)
return gpg_error (GPG_ERR_ENOMEM);
ol->oid = xtrystrdup (oid);
if (!ol->oid)
{
xfree (ol);
return gpg_error (GPG_ERR_ENOMEM);
}
ol->next = cms->digest_algos;
cms->digest_algos = ol;
return 0;
}
/**
* ksba_cms_add_signer:
* @cms: A CMS object
* @cert: A certificate used to describe the signer.
*
* This functions starts assembly of a new signed data content or adds
* another signer to the list of signers.
*
* Return value: 0 on success or an error code.
**/
gpg_error_t
ksba_cms_add_signer (ksba_cms_t cms, ksba_cert_t cert)
{
struct certlist_s *cl, *cl2;
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
cl = xtrycalloc (1,sizeof *cl);
if (!cl)
return gpg_error (GPG_ERR_ENOMEM);
ksba_cert_ref (cert);
cl->cert = cert;
if (!cms->cert_list)
cms->cert_list = cl;
else
{
for (cl2=cms->cert_list; cl2->next; cl2 = cl2->next)
;
cl2->next = cl;
}
return 0;
}
/**
* ksba_cms_add_cert:
* @cms: A CMS object
* @cert: A certificate to be send along with the signed data.
*
* This functions adds a certificate to the list of certificates send
* along with the signed data. Using this is optional but it is very
* common to include at least the certificate of the signer it self.
*
* Return value: 0 on success or an error code.
**/
gpg_error_t
ksba_cms_add_cert (ksba_cms_t cms, ksba_cert_t cert)
{
struct certlist_s *cl;
if (!cms || !cert)
return gpg_error (GPG_ERR_INV_VALUE);
/* first check whether this is a duplicate. */
for (cl = cms->cert_info_list; cl; cl = cl->next)
{
if (!_ksba_cert_cmp (cert, cl->cert))
return 0; /* duplicate */
}
/* Okay, add it. */
cl = xtrycalloc (1,sizeof *cl);
if (!cl)
return gpg_error (GPG_ERR_ENOMEM);
ksba_cert_ref (cert);
cl->cert = cert;
cl->next = cms->cert_info_list;
cms->cert_info_list = cl;
return 0;
}
/* Add an S/MIME capability as an extended attribute to the message.
This function is to be called for each capability in turn. The
first capability added will receive the highest priority. CMS is
the context, OID the object identifier of the capability and if DER
is not NULL it is used as the DER-encoded parameters of the
capability; the length of that DER object is given in DERLEN.
DERLEN should be 0 if DER is NULL.
The function returns 0 on success or an error code.
*/
gpg_error_t
ksba_cms_add_smime_capability (ksba_cms_t cms, const char *oid,
const unsigned char *der, size_t derlen)
{
gpg_error_t err;
struct oidparmlist_s *opl, *opl2;
if (!cms || !oid)
return gpg_error (GPG_ERR_INV_VALUE);
if (!der)
derlen = 0;
opl = xtrymalloc (sizeof *opl + derlen - 1);
if (!opl)
return gpg_error_from_errno (errno);
opl->next = NULL;
opl->oid = xtrystrdup (oid);
if (!opl->oid)
{
err = gpg_error_from_errno (errno);
xfree (opl);
return err;
}
opl->parmlen = derlen;
if (der)
memcpy (opl->parm, der, derlen);
/* Append it to maintain the desired order. */
if (!cms->capability_list)
cms->capability_list = opl;
else
{
for (opl2=cms->capability_list; opl2->next; opl2 = opl2->next)
;
opl2->next = opl;
}
return 0;
}
/**
* ksba_cms_set_message_digest:
* @cms: A CMS object
* @idx: The index of the signer
* @digest: a message digest
* @digest_len: the length of the message digest
*
* Set a message digest into the signedAttributes of the signer with
* the index IDX. The index of a signer is determined by the sequence
* of ksba_cms_add_signer() calls; the first signer has the index 0.
* This function is to be used when the hash value of the data has
* been calculated and before the create function requests the sign
* operation.
*
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_cms_set_message_digest (ksba_cms_t cms, int idx,
const unsigned char *digest, size_t digest_len)
{
struct certlist_s *cl;
if (!cms || !digest)
return gpg_error (GPG_ERR_INV_VALUE);
if (!digest_len || digest_len > DIM(cl->msg_digest))
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
;
if (!cl)
return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store it */
cl->msg_digest_len = digest_len;
memcpy (cl->msg_digest, digest, digest_len);
return 0;
}
/**
* ksba_cms_set_signing_time:
* @cms: A CMS object
* @idx: The index of the signer
* @sigtime: a time or an empty value to use the current time
*
* Set a signing time into the signedAttributes of the signer with
* the index IDX. The index of a signer is determined by the sequence
* of ksba_cms_add_signer() calls; the first signer has the index 0.
*
* Return value: 0 on success or an error code
**/
gpg_error_t
ksba_cms_set_signing_time (ksba_cms_t cms, int idx, const ksba_isotime_t sigtime)
{
struct certlist_s *cl;
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
;
if (!cl)
return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store it */
/* Fixme: We might want to check the validity of the passed time
string. */
if (!*sigtime)
_ksba_current_time (cl->signing_time);
else
_ksba_copy_time (cl->signing_time, sigtime);
return 0;
}
/* Set the signature value as a canonical encoded s-expression.
*
* r_sig = (sig-val
* (
* ( )
* ...
* ( )
* ))
*
* must be given as a stringified OID or the special string
* "rsa". For ECC must either be "ecdsa" or the OID matching the used
* hash algorithm; the expected parameters are "r" and "s".
*
* Note that IDX is only used for consistency checks.
*/
gpg_error_t
ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval)
{
gpg_error_t err;
unsigned long n, namelen;
struct sig_val_s *sv, **sv_tail;
const unsigned char *s, *endp, *name;
int ecc; /* True for ECC algos. */
int i;
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX); /* only one signer for now */
/* log_sexp ("sigval:", sigval); */
s = sigval;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
for (i=0, sv_tail=&cms->sig_val; *sv_tail; sv_tail=&(*sv_tail)->next, i++)
;
if (i != idx)
return gpg_error (GPG_ERR_INV_INDEX);
if (!(n = snext (&s)))
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, 7, "sig-val"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if (*s != '(')
return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
/* Break out the algorithm ID. */
if (!(n = snext (&s)))
return gpg_error (GPG_ERR_INV_SEXP);
sv = xtrycalloc (1, sizeof *sv);
if (!sv)
return gpg_error (GPG_ERR_ENOMEM);
if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a')
{
sv->algo = xtrystrdup ("1.2.840.113549.1.1.1"); /* rsa */
if (!sv->algo)
{
xfree (sv);
return gpg_error (GPG_ERR_ENOMEM);
}
}
else if (n==5 && !memcmp (s, "ecdsa", 5))
{
/* Use a placeholder for later fixup. */
sv->algo = xtrystrdup ("ecdsa");
if (!sv->algo)
{
xfree (sv);
return gpg_error (GPG_ERR_ENOMEM);
}
}
else
{
sv->algo = xtrymalloc (n+1);
if (!sv->algo)
{
xfree (sv);
return gpg_error (GPG_ERR_ENOMEM);
}
memcpy (sv->algo, s, n);
sv->algo[n] = 0;
}
s += n;
ecc = (!strcmp (sv->algo, "ecdsa") /* placeholder */
|| !strcmp (sv->algo, "1.2.840.10045.4.3.2") /* ecdsa-with-SHA256 */
|| !strcmp (sv->algo, "1.2.840.10045.4.3.3") /* ecdsa-with-SHA384 */
|| !strcmp (sv->algo, "1.2.840.10045.4.3.4") /* ecdsa-with-SHA512 */
);
xfree (sv->value); sv->value = NULL;
xfree (sv->ecc.r); sv->ecc.r = NULL;
while (*s == '(')
{
s++;
n = strtoul (s, (char**)&endp, 10);
s = endp;
if (!n || *s != ':')
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
s++;
name = s;
namelen = n;
s += n;
if (!digitp(s))
{
err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
goto leave;
}
n = strtoul (s, (char**)&endp, 10);
s = endp;
if (!n || *s != ':')
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
s++;
if (namelen == 1 && *name == 's')
{
/* Store the "main" parameter into value. */
xfree (sv->value);
sv->value = xtrymalloc (n);
if (!sv->value)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (sv->value, s, n);
sv->valuelen = n;
}
else if (ecc && namelen == 1 && *name == 'r')
{
xfree (sv->ecc.r);
sv->ecc.r = xtrymalloc (n);
if (!sv->ecc.r)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (sv->ecc.r, s, n);
sv->ecc.rlen = n;
}
/* (We ignore all other parameter of the (key value) form.) */
s += n;
if ( *s != ')')
{
err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
goto leave;
}
s++;
}
/* Expect two closing parenthesis. */
if (*s != ')')
{
err = gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
goto leave;
}
s++;
if ( *s != ')')
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
/* Check that we have all required data. */
if (!sv->value)
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
if (ecc && (!sv->ecc.r || !sv->ecc.rlen))
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
*sv_tail = sv;
return 0; /* Success. */
leave: /* Note: This is an error-only label. */
xfree (sv->value);
xfree (sv->algo);
xfree (sv->ecc.r);
xfree (sv);
return err;
}
/* Set the content encryption algorithm to OID and optionally set the
initialization vector to IV */
gpg_error_t
ksba_cms_set_content_enc_algo (ksba_cms_t cms,
const char *oid,
const void *iv, size_t ivlen)
{
if (!cms || !oid)
return gpg_error (GPG_ERR_INV_VALUE);
xfree (cms->encr_iv);
cms->encr_iv = NULL;
cms->encr_ivlen = 0;
cms->encr_algo_oid = xtrystrdup (oid);
if (!cms->encr_algo_oid)
return gpg_error (GPG_ERR_ENOMEM);
if (iv)
{
cms->encr_iv = xtrymalloc (ivlen);
if (!cms->encr_iv)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cms->encr_iv, iv, ivlen);
cms->encr_ivlen = ivlen;
}
return 0;
}
/*
* encval is expected to be a canonical encoded S-Exp of this form:
* (enc-val
* (
* ( )
* ...
* ( )
* (encr-algo )
* (wrap-algo )
* ))
*
* Note the must be given as a stringified OID or the special
* string "rsa". For RSA there is just one parameter named "a";
* encr-algo and wrap-algo are also not used. For ECC must be
* "ecdh", the parameter "s" gives the encrypted key, "e" specified
* the ephemeral public key, and wrap-algo algo and encr-algo are the
* stringified OIDs for the ECDH algorithm parameters. */
gpg_error_t
ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval)
{
/*FIXME: This shares most code with ...set_sig_val */
struct certlist_s *cl;
const char *s, *endp, *name;
unsigned long n, namelen;
int ecdh = 0; /* We expect ECC parameters. */
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX);
for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
;
if (!cl)
return gpg_error (GPG_ERR_INV_INDEX); /* No cert to store the value. */
/* log_sexp ("encval", encval); */
s = encval;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
n = strtoul (s, (char**)&endp, 10);
s = endp;
if (!n || *s!=':')
return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */
s++;
if (n != 7 || memcmp (s, "enc-val", 7))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s += 7;
if (*s != '(')
return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
/* break out the algorithm ID */
n = strtoul (s, (char**)&endp, 10);
s = endp;
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */
s++;
xfree (cl->enc_val.algo);
if (n==3 && !memcmp (s, "rsa", 3))
{ /* kludge to allow "rsa" to be passed as algorithm name */
cl->enc_val.algo = xtrystrdup ("1.2.840.113549.1.1.1");
if (!cl->enc_val.algo)
return gpg_error (GPG_ERR_ENOMEM);
}
else if (n==4 && !memcmp (s, "ecdh", 4))
{
cl->enc_val.algo = xtrystrdup ("1.2.840.10045.2.1"); /* ecPublicKey */
if (!cl->enc_val.algo)
return gpg_error (GPG_ERR_ENOMEM);
}
else
{
cl->enc_val.algo = xtrymalloc (n+1);
if (!cl->enc_val.algo)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cl->enc_val.algo, s, n);
cl->enc_val.algo[n] = 0;
}
s += n;
ecdh = !strcmp (cl->enc_val.algo, "1.2.840.10045.2.1");
xfree (cl->enc_val.value); cl->enc_val.value = NULL;
xfree (cl->enc_val.ecdh.e); cl->enc_val.ecdh.e = NULL;
xfree (cl->enc_val.ecdh.encr_algo); cl->enc_val.ecdh.encr_algo = NULL;
xfree (cl->enc_val.ecdh.wrap_algo); cl->enc_val.ecdh.wrap_algo = NULL;
while (*s == '(')
{
s++;
n = strtoul (s, (char**)&endp, 10);
s = endp;
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
name = s;
namelen = n;
s += n;
if (!digitp(s))
return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
n = strtoul (s, (char**)&endp, 10);
s = endp;
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
if (namelen == 1 && ((!ecdh && *name == 'a') || (ecdh && *name == 's')))
{
/* Store the "main" parameter into value. */
xfree (cl->enc_val.value);
cl->enc_val.value = xtrymalloc (n);
if (!cl->enc_val.value)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cl->enc_val.value, s, n);
cl->enc_val.valuelen = n;
}
else if (!ecdh)
; /* Ignore all other parameters for RSA. */
else if (namelen == 1 && *name == 'e')
{
xfree (cl->enc_val.ecdh.e);
cl->enc_val.ecdh.e = xtrymalloc (n);
if (!cl->enc_val.ecdh.e)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cl->enc_val.ecdh.e, s, n);
cl->enc_val.ecdh.elen = n;
}
else if (namelen == 9 && !memcmp (name, "encr-algo", 9))
{
xfree (cl->enc_val.ecdh.encr_algo);
cl->enc_val.ecdh.encr_algo = xtrymalloc (n+1);
if (!cl->enc_val.ecdh.encr_algo)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cl->enc_val.ecdh.encr_algo, s, n);
cl->enc_val.ecdh.encr_algo[n] = 0;
}
else if (namelen == 9 && !memcmp (name, "wrap-algo", 9))
{
xfree (cl->enc_val.ecdh.wrap_algo);
cl->enc_val.ecdh.wrap_algo = xtrymalloc (n+1);
if (!cl->enc_val.ecdh.wrap_algo)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cl->enc_val.ecdh.wrap_algo, s, n);
cl->enc_val.ecdh.wrap_algo[n] = 0;
}
/* (We ignore all other parameter of the (key value) form.) */
s += n;
if ( *s != ')')
return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
s++;
}
/* Expect two closing parenthesis. */
if (*s != ')')
return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
if ( *s != ')')
return gpg_error (GPG_ERR_INV_SEXP);
/* Check that we have all required data. */
if (!cl->enc_val.value)
return gpg_error (GPG_ERR_INV_SEXP);
if (ecdh && (!cl->enc_val.ecdh.e
|| !cl->enc_val.ecdh.elen
|| !cl->enc_val.ecdh.encr_algo
|| !cl->enc_val.ecdh.wrap_algo))
return gpg_error (GPG_ERR_INV_SEXP);
return 0;
}
/**
* ksba_cms_add_recipient:
* @cms: A CMS object
* @cert: A certificate used to describe the recipient.
*
* This functions starts assembly of a new enveloped data content or adds
* another recipient to the list of recipients.
*
* Note: after successful completion of this function ownership of
* @cert is transferred to @cms.
*
* Return value: 0 on success or an error code.
**/
gpg_error_t
ksba_cms_add_recipient (ksba_cms_t cms, ksba_cert_t cert)
{
/* for now we use the same structure */
return ksba_cms_add_signer (cms, cert);
}
/*
Content handler for parsing messages
*/
static gpg_error_t
ct_parse_data (ksba_cms_t cms)
{
(void)cms;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpg_error_t
ct_parse_signed_data (ksba_cms_t cms)
{
enum {
sSTART,
sGOT_HASH,
sIN_DATA,
sERROR
} state = sERROR;
ksba_stop_reason_t stop_reason = cms->stop_reason;
gpg_error_t err = 0;
cms->stop_reason = KSBA_SR_RUNNING;
/* Calculate state from last reason and do some checks */
if (stop_reason == KSBA_SR_GOT_CONTENT)
{
state = sSTART;
}
else if (stop_reason == KSBA_SR_NEED_HASH)
{
state = sGOT_HASH;
}
else if (stop_reason == KSBA_SR_BEGIN_DATA)
{
if (!cms->hash_fnc)
err = gpg_error (GPG_ERR_MISSING_ACTION);
else
state = sIN_DATA;
}
else if (stop_reason == KSBA_SR_END_DATA)
{
state = sGOT_HASH;
}
else if (stop_reason == KSBA_SR_RUNNING)
err = gpg_error (GPG_ERR_INV_STATE);
else if (stop_reason)
err = gpg_error (GPG_ERR_BUG);
if (err)
return err;
/* Do the action */
if (state == sSTART)
err = _ksba_cms_parse_signed_data_part_1 (cms);
else if (state == sGOT_HASH)
err = _ksba_cms_parse_signed_data_part_2 (cms);
else if (state == sIN_DATA)
err = read_and_hash_cont (cms);
else
err = gpg_error (GPG_ERR_INV_STATE);
if (err)
return err;
/* Calculate new stop reason */
if (state == sSTART)
{
- if (cms->detached_data && !cms->data.digest)
+ if (cms->detached_data)
{ /* We use this stop reason to inform the caller about a
detached signatures. Actually there is no need for him
to hash the data now, he can do this also later. */
stop_reason = KSBA_SR_NEED_HASH;
}
else
{ /* The user must now provide a hash function so that we can
hash the data in the next round */
stop_reason = KSBA_SR_BEGIN_DATA;
}
}
else if (state == sIN_DATA)
stop_reason = KSBA_SR_END_DATA;
else if (state ==sGOT_HASH)
stop_reason = KSBA_SR_READY;
cms->stop_reason = stop_reason;
return 0;
}
static gpg_error_t
ct_parse_enveloped_data (ksba_cms_t cms)
{
enum {
sSTART,
sREST,
sINDATA,
sERROR
} state = sERROR;
ksba_stop_reason_t stop_reason = cms->stop_reason;
gpg_error_t err = 0;
cms->stop_reason = KSBA_SR_RUNNING;
/* Calculate state from last reason and do some checks */
if (stop_reason == KSBA_SR_GOT_CONTENT)
{
state = sSTART;
}
else if (stop_reason == KSBA_SR_DETACHED_DATA)
{
state = sREST;
}
else if (stop_reason == KSBA_SR_BEGIN_DATA)
{
state = sINDATA;
}
else if (stop_reason == KSBA_SR_END_DATA)
{
state = sREST;
}
else if (stop_reason == KSBA_SR_RUNNING)
err = gpg_error (GPG_ERR_INV_STATE);
else if (stop_reason)
err = gpg_error (GPG_ERR_BUG);
if (err)
return err;
/* Do the action */
if (state == sSTART)
err = _ksba_cms_parse_enveloped_data_part_1 (cms);
else if (state == sREST)
err = _ksba_cms_parse_enveloped_data_part_2 (cms);
else if (state == sINDATA)
err = read_encrypted_cont (cms);
else
err = gpg_error (GPG_ERR_INV_STATE);
if (err)
return err;
/* Calculate new stop reason */
if (state == sSTART)
{
stop_reason = cms->detached_data? KSBA_SR_DETACHED_DATA
: KSBA_SR_BEGIN_DATA;
}
else if (state == sINDATA)
stop_reason = KSBA_SR_END_DATA;
else if (state ==sREST)
stop_reason = KSBA_SR_READY;
cms->stop_reason = stop_reason;
return 0;
}
static gpg_error_t
ct_parse_digested_data (ksba_cms_t cms)
{
(void)cms;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpg_error_t
ct_parse_encrypted_data (ksba_cms_t cms)
{
(void)cms;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
/*
Content handlers for building messages
*/
static gpg_error_t
ct_build_data (ksba_cms_t cms)
{
(void)cms;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
/* Write everything up to the encapsulated data content type. */
static gpg_error_t
build_signed_data_header (ksba_cms_t cms)
{
gpg_error_t err;
unsigned char *buf;
const char *s;
size_t len;
int i;
/* Write the outer contentInfo. */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
return err;
err = ksba_oid_from_str (cms->content.oid, &buf, &len);
if (err)
return err;
err = _ksba_ber_write_tl (cms->writer,
TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
if (!err)
err = ksba_writer_write (cms->writer, buf, len);
xfree (buf);
if (err)
return err;
err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0);
if (err)
return err;
/* The SEQUENCE */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
return err;
/* figure out the CMSVersion to be used */
if (0 /* fixme: have_attribute_certificates
|| encapsulated_content != data
|| any_signer_info_is_version_3*/ )
s = "\x03";
else
s = "\x01";
err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1);
if (err)
return err;
err = ksba_writer_write (cms->writer, s, 1);
if (err)
return err;
/* SET OF DigestAlgorithmIdentifier */
{
unsigned char *value;
size_t valuelen;
ksba_writer_t tmpwrt;
err = ksba_writer_new (&tmpwrt);
if (err)
return err;
err = ksba_writer_set_mem (tmpwrt, 512);
if (err)
{
ksba_writer_release (tmpwrt);
return err;
}
for (i=0; (s = ksba_cms_get_digest_algo_list (cms, i)); i++)
{
int j;
const char *s2;
/* (make sure not to write duplicates) */
for (j=0; j < i && (s2=ksba_cms_get_digest_algo_list (cms, j)); j++)
{
if (!strcmp (s, s2))
break;
}
if (j == i)
{
err = _ksba_der_write_algorithm_identifier (tmpwrt, s, NULL, 0);
if (err)
{
ksba_writer_release (tmpwrt);
return err;
}
}
}
value = ksba_writer_snatch_mem (tmpwrt, &valuelen);
ksba_writer_release (tmpwrt);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
return err;
}
err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL,
1, valuelen);
if (!err)
err = ksba_writer_write (cms->writer, value, valuelen);
xfree (value);
if (err)
return err;
}
/* Write the (inner) encapsulatedContentInfo */
/* if we have a detached signature we don't need to use undefinite
length here - but it doesn't matter either */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
return err;
err = ksba_oid_from_str (cms->inner_cont_oid, &buf, &len);
if (err)
return err;
err = _ksba_ber_write_tl (cms->writer,
TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
if (!err)
err = ksba_writer_write (cms->writer, buf, len);
xfree (buf);
if (err)
return err;
if ( !cms->detached_data)
{ /* write the tag */
err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0);
if (err)
return err;
}
return err;
}
/* Set the issuer/serial from the cert to the node.
mode 0: sid
mode 1: rid
*/
static gpg_error_t
set_issuer_serial (AsnNode info, ksba_cert_t cert, int mode)
{
gpg_error_t err;
AsnNode dst, src;
if (!info || !cert)
return gpg_error (GPG_ERR_INV_VALUE);
src = _ksba_asn_find_node (cert->root,
"Certificate.tbsCertificate.serialNumber");
dst = _ksba_asn_find_node (info,
mode?
"rid.issuerAndSerialNumber.serialNumber":
"sid.issuerAndSerialNumber.serialNumber");
err = _ksba_der_copy_tree (dst, src, cert->image);
if (err)
return err;
src = _ksba_asn_find_node (cert->root,
"Certificate.tbsCertificate.issuer");
dst = _ksba_asn_find_node (info,
mode?
"rid.issuerAndSerialNumber.issuer":
"sid.issuerAndSerialNumber.issuer");
err = _ksba_der_copy_tree (dst, src, cert->image);
if (err)
return err;
return 0;
}
/* Store the sequence of capabilities at NODE */
static gpg_error_t
store_smime_capability_sequence (AsnNode node,
struct oidparmlist_s *capabilities)
{
gpg_error_t err;
struct oidparmlist_s *cap, *cap2;
unsigned char *value;
size_t valuelen;
ksba_writer_t tmpwrt;
err = ksba_writer_new (&tmpwrt);
if (err)
return err;
err = ksba_writer_set_mem (tmpwrt, 512);
if (err)
{
ksba_writer_release (tmpwrt);
return err;
}
for (cap=capabilities; cap; cap = cap->next)
{
/* (avoid writing duplicates) */
for (cap2=capabilities; cap2 != cap; cap2 = cap2->next)
{
if (!strcmp (cap->oid, cap2->oid)
&& cap->parmlen && cap->parmlen == cap2->parmlen
&& !memcmp (cap->parm, cap2->parm, cap->parmlen))
break; /* Duplicate found. */
}
if (cap2 == cap)
{
/* RFC3851 requires that a missing parameter must not be
encoded as NULL. This is in contrast to all other usages
of the algorithm identifier where ist is allowed and in
some profiles (e.g. tmttv2) even explicitly suggested to
use NULL. */
err = _ksba_der_write_algorithm_identifier
(tmpwrt, cap->oid,
cap->parmlen?cap->parm:(const void*)"", cap->parmlen);
if (err)
{
ksba_writer_release (tmpwrt);
return err;
}
}
}
value = ksba_writer_snatch_mem (tmpwrt, &valuelen);
if (!value)
err = gpg_error (GPG_ERR_ENOMEM);
if (!err)
err = _ksba_der_store_sequence (node, value, valuelen);
xfree (value);
ksba_writer_release (tmpwrt);
return err;
}
/* An object used to construct the signed attributes. */
struct attrarray_s {
AsnNode root;
unsigned char *image;
size_t imagelen;
};
/* Thank you ASN.1 committee for allowing us to employ a sort to make
that DER encoding even more complicate. */
static int
compare_attrarray (const void *a_v, const void *b_v)
{
const struct attrarray_s *a = a_v;
const struct attrarray_s *b = b_v;
const unsigned char *ap, *bp;
size_t an, bn;
ap = a->image;
an = a->imagelen;
bp = b->image;
bn = b->imagelen;
for (; an && bn; an--, bn--, ap++, bp++ )
if (*ap != *bp)
return *ap - *bp;
return (an == bn)? 0 : (an > bn)? 1 : -1;
}
/* Write the END of data NULL tag and everything we can write before
the user can calculate the signature */
static gpg_error_t
build_signed_data_attributes (ksba_cms_t cms)
{
gpg_error_t err;
int signer;
ksba_asn_tree_t cms_tree = NULL;
struct certlist_s *certlist;
struct oidlist_s *digestlist;
struct signer_info_s *si, **si_tail;
AsnNode root = NULL;
struct attrarray_s attrarray[4];
int attridx = 0;
int i;
memset (attrarray, 0, sizeof (attrarray));
/* Write the End tag */
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (err)
return err;
if (cms->signer_info)
return gpg_error (GPG_ERR_CONFLICT); /* This list must be empty at
this point. */
/* Write optional certificates */
if (cms->cert_info_list)
{
unsigned long totallen = 0;
const unsigned char *der;
size_t n;
for (certlist = cms->cert_info_list; certlist; certlist = certlist->next)
{
if (!ksba_cert_get_image (certlist->cert, &n))
return gpg_error (GPG_ERR_GENERAL); /* User passed an
unitialized cert */
totallen += n;
}
err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, totallen);
if (err)
return err;
for (certlist = cms->cert_info_list; certlist; certlist = certlist->next)
{
if (!(der=ksba_cert_get_image (certlist->cert, &n)))
return gpg_error (GPG_ERR_BUG);
err = ksba_writer_write (cms->writer, der, n);
if (err )
return err;
}
}
/* If we ever support it, here is the right place to do it:
Write the optional CRLs */
/* Now we have to prepare the signer info. For now we will just build the
signedAttributes, so that the user can do the signature calculation */
err = ksba_asn_create_tree ("cms", &cms_tree);
if (err)
return err;
certlist = cms->cert_list;
if (!certlist)
{
err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */
goto leave;
}
digestlist = cms->digest_algos;
if (!digestlist)
{
err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */
goto leave;
}
si_tail = &cms->signer_info;
for (signer=0; certlist;
signer++, certlist = certlist->next, digestlist = digestlist->next)
{
AsnNode attr;
AsnNode n;
unsigned char *image;
size_t imagelen;
for (i = 0; i < attridx; i++)
{
_ksba_asn_release_nodes (attrarray[i].root);
xfree (attrarray[i].image);
}
attridx = 0;
memset (attrarray, 0, sizeof (attrarray));
if (!digestlist)
{
err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */
goto leave;
}
if (!certlist->cert || !digestlist->oid)
{
err = gpg_error (GPG_ERR_BUG);
goto leave;
}
/* Include the pretty important message digest. */
attr = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.Attribute");
if (!attr)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = _ksba_asn_find_node (attr, "Attribute.attrType");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_oid (n, oidstr_messageDigest);
if (err)
goto leave;
n = _ksba_asn_find_node (attr, "Attribute.attrValues");
if (!n || !n->down)
return gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
n = n->down; /* fixme: ugly hack */
assert (certlist && certlist->msg_digest_len);
err = _ksba_der_store_octet_string (n, certlist->msg_digest,
certlist->msg_digest_len);
if (err)
goto leave;
err = _ksba_der_encode_tree (attr, &image, &imagelen);
if (err)
goto leave;
attrarray[attridx].root = attr;
attrarray[attridx].image = image;
attrarray[attridx].imagelen = imagelen;
attridx++;
/* Include the content-type attribute. */
attr = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.Attribute");
if (!attr)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = _ksba_asn_find_node (attr, "Attribute.attrType");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_oid (n, oidstr_contentType);
if (err)
goto leave;
n = _ksba_asn_find_node (attr, "Attribute.attrValues");
if (!n || !n->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = n->down; /* fixme: ugly hack */
err = _ksba_der_store_oid (n, cms->inner_cont_oid);
if (err)
goto leave;
err = _ksba_der_encode_tree (attr, &image, &imagelen);
if (err)
goto leave;
attrarray[attridx].root = attr;
attrarray[attridx].image = image;
attrarray[attridx].imagelen = imagelen;
attridx++;
/* Include the signing time */
if (*certlist->signing_time)
{
attr = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.Attribute");
if (!attr)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = _ksba_asn_find_node (attr, "Attribute.attrType");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_oid (n, oidstr_signingTime);
if (err)
goto leave;
n = _ksba_asn_find_node (attr, "Attribute.attrValues");
if (!n || !n->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = n->down; /* fixme: ugly hack */
err = _ksba_der_store_time (n, certlist->signing_time);
if (err)
goto leave;
err = _ksba_der_encode_tree (attr, &image, &imagelen);
if (err)
goto leave;
/* We will use the attributes again - so save them */
attrarray[attridx].root = attr;
attrarray[attridx].image = image;
attrarray[attridx].imagelen = imagelen;
attridx++;
}
/* Include the S/MIME capabilities with the first signer. */
if (cms->capability_list && !signer)
{
attr = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.Attribute");
if (!attr)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = _ksba_asn_find_node (attr, "Attribute.attrType");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_oid (n, oidstr_smimeCapabilities);
if (err)
goto leave;
n = _ksba_asn_find_node (attr, "Attribute.attrValues");
if (!n || !n->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
n = n->down; /* fixme: ugly hack */
err = store_smime_capability_sequence (n, cms->capability_list);
if (err)
goto leave;
err = _ksba_der_encode_tree (attr, &image, &imagelen);
if (err)
goto leave;
attrarray[attridx].root = attr;
attrarray[attridx].image = image;
attrarray[attridx].imagelen = imagelen;
attridx++;
}
/* Arggh. That silly ASN.1 DER encoding rules: We need to sort
the SET values. */
qsort (attrarray, attridx, sizeof (struct attrarray_s),
compare_attrarray);
/* Now copy them to an SignerInfo tree. This tree is not
complete but suitable for ksba_cms_hash_signed_attributes() */
root = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.SignerInfo");
n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs");
if (!n || !n->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
/* This is another ugly hack to move to the element we want */
for (n = n->down->down; n && n->type != TYPE_SEQUENCE; n = n->right)
;
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
assert (attridx <= DIM (attrarray));
for (i=0; i < attridx; i++)
{
if (i)
{
if ( !(n=_ksba_asn_insert_copy (n)))
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
}
err = _ksba_der_copy_tree (n, attrarray[i].root, attrarray[i].image);
if (err)
goto leave;
_ksba_asn_release_nodes (attrarray[i].root);
free (attrarray[i].image);
attrarray[i].root = NULL;
attrarray[i].image = NULL;
}
err = _ksba_der_encode_tree (root, &image, NULL);
if (err)
goto leave;
si = xtrycalloc (1, sizeof *si);
if (!si)
return gpg_error (GPG_ERR_ENOMEM);
si->root = root;
root = NULL;
si->image = image;
/* Hmmm, we don't set the length of the image. */
*si_tail = si;
si_tail = &si->next;
}
leave:
_ksba_asn_release_nodes (root);
ksba_asn_tree_release (cms_tree);
for (i = 0; i < attridx; i++)
{
_ksba_asn_release_nodes (attrarray[i].root);
xfree (attrarray[i].image);
}
return err;
}
/* The user has calculated the signatures and we can therefore write
everything left over to do. */
static gpg_error_t
build_signed_data_rest (ksba_cms_t cms)
{
gpg_error_t err;
int signer;
ksba_asn_tree_t cms_tree = NULL;
struct certlist_s *certlist;
struct oidlist_s *digestlist;
struct signer_info_s *si;
struct sig_val_s *sv;
ksba_writer_t tmpwrt = NULL;
AsnNode root = NULL;
ksba_der_t dbld = NULL;
/* Now we can really write the signer info */
err = ksba_asn_create_tree ("cms", &cms_tree);
if (err)
return err;
certlist = cms->cert_list;
if (!certlist)
{
err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */
return err;
}
/* To construct the set we use a temporary writer object. */
err = ksba_writer_new (&tmpwrt);
if (err)
goto leave;
err = ksba_writer_set_mem (tmpwrt, 2048);
if (err)
goto leave;
digestlist = cms->digest_algos;
si = cms->signer_info;
sv = cms->sig_val;
for (signer=0; certlist;
signer++,
certlist = certlist->next,
digestlist = digestlist->next,
si = si->next,
sv = sv->next)
{
AsnNode n, n2;
unsigned char *image;
size_t imagelen;
const char *oid;
if (!digestlist || !si || !sv)
{
err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */
goto leave;
}
if (!certlist->cert || !digestlist->oid)
{
err = gpg_error (GPG_ERR_BUG);
goto leave;
}
root = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.SignerInfo");
/* We store a version of 1 because we use the issuerAndSerialNumber */
n = _ksba_asn_find_node (root, "SignerInfo.version");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_integer (n, "\x00\x00\x00\x01\x01");
if (err)
goto leave;
/* Store the sid */
n = _ksba_asn_find_node (root, "SignerInfo.sid");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = set_issuer_serial (n, certlist->cert, 0);
if (err)
goto leave;
/* store the digestAlgorithm */
n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.algorithm");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_oid (n, digestlist->oid);
if (err)
goto leave;
n = _ksba_asn_find_node (root, "SignerInfo.digestAlgorithm.parameters");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_null (n);
if (err)
goto leave;
/* and the signed attributes */
n = _ksba_asn_find_node (root, "SignerInfo.signedAttrs");
if (!n || !n->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
assert (si->root);
assert (si->image);
n2 = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs");
if (!n2 || !n2->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_copy_tree (n, n2, si->image);
if (err)
goto leave;
image = NULL;
/* store the signatureAlgorithm */
n = _ksba_asn_find_node (root,
"SignerInfo.signatureAlgorithm.algorithm");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
if (!sv->algo)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
if (!strcmp (sv->algo, "ecdsa"))
{
/* Look at the digest algorithm and replace accordingly. */
if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.1"))
oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-SHA256 */
else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.2"))
oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-SHA384 */
else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.3"))
oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-SHA512 */
else
{
err = gpg_error (GPG_ERR_DIGEST_ALGO);
goto leave;
}
}
else
oid = sv->algo;
err = _ksba_der_store_oid (n, oid);
if (err)
goto leave;
n = _ksba_asn_find_node (root,
"SignerInfo.signatureAlgorithm.parameters");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
err = _ksba_der_store_null (n);
if (err)
goto leave;
/* store the signature */
if (!sv->value)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
n = _ksba_asn_find_node (root, "SignerInfo.signature");
if (!n)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
if (sv->ecc.r) /* ECDSA */
{
unsigned char *tmpder;
size_t tmpderlen;
_ksba_der_release (dbld);
dbld = _ksba_der_builder_new (0);
if (!dbld)
{
err = gpg_error_from_syserror ();
goto leave;
}
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
_ksba_der_add_int (dbld, sv->ecc.r, sv->ecc.rlen, 1);
_ksba_der_add_int (dbld, sv->value, sv->valuelen, 1);
_ksba_der_add_end (dbld);
err = _ksba_der_builder_get (dbld, &tmpder, &tmpderlen);
if (err)
goto leave;
err = _ksba_der_store_octet_string (n, tmpder, tmpderlen);
xfree (tmpder);
if (err)
goto leave;
}
else /* RSA */
{
err = _ksba_der_store_octet_string (n, sv->value, sv->valuelen);
if (err)
goto leave;
}
/* Make the DER encoding and write it out. */
err = _ksba_der_encode_tree (root, &image, &imagelen);
if (err)
goto leave;
err = ksba_writer_write (tmpwrt, image, imagelen);
xfree (image);
if (err)
goto leave;
}
/* Write out the SET filled with all signer infos */
{
unsigned char *value;
size_t valuelen;
value = ksba_writer_snatch_mem (tmpwrt, &valuelen);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL,
1, valuelen);
if (!err)
err = ksba_writer_write (cms->writer, value, valuelen);
xfree (value);
if (err)
goto leave;
}
/* Write 3 end tags */
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (!err)
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (!err)
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
leave:
ksba_asn_tree_release (cms_tree);
_ksba_asn_release_nodes (root);
ksba_writer_release (tmpwrt);
_ksba_der_release (dbld);
return err;
}
static gpg_error_t
ct_build_signed_data (ksba_cms_t cms)
{
enum {
sSTART,
sDATAREADY,
sGOTSIG,
sERROR
} state = sERROR;
ksba_stop_reason_t stop_reason;
gpg_error_t err = 0;
stop_reason = cms->stop_reason;
cms->stop_reason = KSBA_SR_RUNNING;
/* Calculate state from last reason and do some checks */
if (stop_reason == KSBA_SR_GOT_CONTENT)
{
state = sSTART;
}
else if (stop_reason == KSBA_SR_BEGIN_DATA)
{
/* fixme: check that the message digest has been set */
state = sDATAREADY;
}
else if (stop_reason == KSBA_SR_END_DATA)
state = sDATAREADY;
else if (stop_reason == KSBA_SR_NEED_SIG)
{
if (!cms->sig_val)
err = gpg_error (GPG_ERR_MISSING_ACTION); /* No ksba_cms_set_sig_val () called */
state = sGOTSIG;
}
else if (stop_reason == KSBA_SR_RUNNING)
err = gpg_error (GPG_ERR_INV_STATE);
else if (stop_reason)
err = gpg_error (GPG_ERR_BUG);
if (err)
return err;
/* Do the action */
if (state == sSTART)
{
/* figure out whether a detached signature is requested */
if (cms->cert_list && cms->cert_list->msg_digest_len)
cms->detached_data = 1;
else
cms->detached_data = 0;
/* and start encoding */
err = build_signed_data_header (cms);
}
else if (state == sDATAREADY)
{
if (!cms->detached_data)
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (!err)
err = build_signed_data_attributes (cms);
}
else if (state == sGOTSIG)
err = build_signed_data_rest (cms);
else
err = gpg_error (GPG_ERR_INV_STATE);
if (err)
return err;
/* Calculate new stop reason */
if (state == sSTART)
{
/* user should write the data and calculate the hash or do
nothing in case of END_DATA */
stop_reason = cms->detached_data? KSBA_SR_END_DATA
: KSBA_SR_BEGIN_DATA;
}
else if (state == sDATAREADY)
stop_reason = KSBA_SR_NEED_SIG;
else if (state == sGOTSIG)
stop_reason = KSBA_SR_READY;
cms->stop_reason = stop_reason;
return 0;
}
/* write everything up to the encryptedContentInfo including the tag */
static gpg_error_t
build_enveloped_data_header (ksba_cms_t cms)
{
gpg_error_t err;
int recpno;
struct certlist_s *certlist;
unsigned char *buf;
const char *s;
size_t len;
ksba_der_t dbld = NULL;
int any_ecdh = 0;
/* See whether we have any ECDH recipients. */
for (certlist = cms->cert_list; certlist; certlist = certlist->next)
if (certlist->enc_val.ecdh.e)
{
any_ecdh = 1;
break;
}
/* Write the outer contentInfo */
/* fixme: code is shared with signed_data_header */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
return err;
err = ksba_oid_from_str (cms->content.oid, &buf, &len);
if (err)
return err;
err = _ksba_ber_write_tl (cms->writer,
TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
if (!err)
err = ksba_writer_write (cms->writer, buf, len);
xfree (buf);
if (err)
return err;
err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0);
if (err)
return err;
/* The SEQUENCE */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
return err;
/* figure out the CMSVersion to be used (from rfc2630):
version is the syntax version number. If originatorInfo is
present, then version shall be 2. If any of the RecipientInfo
structures included have a version other than 0, then the version
shall be 2. If unprotectedAttrs is present, then version shall
be 2. If originatorInfo is absent, all of the RecipientInfo
structures are version 0, and unprotectedAttrs is absent, then
version shall be 0.
For SPHINX the version number must be 0.
*/
s = any_ecdh? "\x02" :"\x00";
err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1);
if (err)
return err;
err = ksba_writer_write (cms->writer, s, 1);
if (err)
return err;
/* Note: originatorInfo is not yet implemented and must not be used
for SPHINX */
certlist = cms->cert_list;
if (!certlist)
{
err = gpg_error (GPG_ERR_MISSING_VALUE); /* oops */
goto leave;
}
dbld = _ksba_der_builder_new (0);
if (!dbld)
{
err = gpg_error_from_syserror ();
goto leave;
}
_ksba_der_add_tag (dbld, 0, TYPE_SET);
for (recpno=0; certlist; recpno++, certlist = certlist->next)
{
const unsigned char *der;
size_t derlen;
if (!certlist->cert)
{
err = gpg_error (GPG_ERR_BUG);
goto leave;
}
if (!certlist->enc_val.ecdh.e) /* RSA (ktri) */
{
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
/* We store a version of 0 because we are only allowed to
* use the issuerAndSerialNumber for SPHINX */
_ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "", 1);
/* rid.issuerAndSerialNumber */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
/* rid.issuerAndSerialNumber.issuer */
err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen);
if (err)
goto leave;
_ksba_der_add_der (dbld, der, derlen);
/* rid.issuerAndSerialNumber.serialNumber */
err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen);
if (err)
goto leave;
_ksba_der_add_der (dbld, der, derlen);
_ksba_der_add_end (dbld);
/* Store the keyEncryptionAlgorithm */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
if (!certlist->enc_val.algo || !certlist->enc_val.value)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
_ksba_der_add_oid (dbld, certlist->enc_val.algo);
/* Now store NULL for the optional parameters. From Peter
* Gutmann's X.509 style guide:
*
* Another pitfall to be aware of is that algorithms which
* have no parameters have this specified as a NULL value
* rather than omitting the parameters field entirely. The
* reason for this is that when the 1988 syntax for
* AlgorithmIdentifier was translated into the 1997 syntax,
* the OPTIONAL associated with the AlgorithmIdentifier
* parameters got lost. Later it was recovered via a defect
* report, but by then everyone thought that algorithm
* parameters were mandatory. Because of this the algorithm
* parameters should be specified as NULL, regardless of what
* you read elsewhere.
*
* The trouble is that things *never* get better, they just
* stay the same, only more so
* -- Terry Pratchett, "Eric"
*
* Although this is about signing, we always do it. Versions of
* Libksba before 1.0.6 had a bug writing out the NULL tag here,
* thus in reality we used to be correct according to the
* standards despite we didn't intended so.
*/
_ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0);
_ksba_der_add_end (dbld);
/* Store the encryptedKey */
if (!certlist->enc_val.value)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
_ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING,
certlist->enc_val.value,
certlist->enc_val.valuelen);
}
else /* ECDH */
{
_ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* kari */
_ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "\x03", 1);
_ksba_der_add_tag (dbld, CLASS_CONTEXT, 0); /* originator */
_ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* originatorKey */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* algorithm */
_ksba_der_add_oid (dbld, certlist->enc_val.algo);
_ksba_der_add_end (dbld);
_ksba_der_add_bts (dbld, certlist->enc_val.ecdh.e,
certlist->enc_val.ecdh.elen, 0);
_ksba_der_add_end (dbld); /* end originatorKey */
_ksba_der_add_end (dbld); /* end originator */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* keyEncrAlgo */
_ksba_der_add_oid (dbld, certlist->enc_val.ecdh.encr_algo);
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
_ksba_der_add_oid (dbld, certlist->enc_val.ecdh.wrap_algo);
_ksba_der_add_end (dbld);
_ksba_der_add_end (dbld); /* end keyEncrAlgo */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKeys */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKey */
/* rid.issuerAndSerialNumber */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen);
if (err)
goto leave;
_ksba_der_add_der (dbld, der, derlen);
err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen);
if (err)
goto leave;
_ksba_der_add_der (dbld, der, derlen);
_ksba_der_add_end (dbld);
/* encryptedKey */
if (!certlist->enc_val.value)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
_ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING,
certlist->enc_val.value,
certlist->enc_val.valuelen);
_ksba_der_add_end (dbld); /* end recpEncrKey */
_ksba_der_add_end (dbld); /* end recpEncrKeys */
}
_ksba_der_add_end (dbld); /* End SEQUENCE (ktri or kari) */
}
_ksba_der_add_end (dbld); /* End SET */
/* Write out the SET filled with all recipient infos */
{
unsigned char *image;
size_t imagelen;
err = _ksba_der_builder_get (dbld, &image, &imagelen);
if (err)
goto leave;
err = ksba_writer_write (cms->writer, image, imagelen);
xfree (image);
if (err)
goto leave;
}
/* Write the (inner) encryptedContentInfo */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
return err;
err = ksba_oid_from_str (cms->inner_cont_oid, &buf, &len);
if (err)
return err;
err = _ksba_ber_write_tl (cms->writer,
TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, len);
if (!err)
err = ksba_writer_write (cms->writer, buf, len);
xfree (buf);
if (err)
return err;
/* and the encryptionAlgorithm */
err = _ksba_der_write_algorithm_identifier (cms->writer,
cms->encr_algo_oid,
cms->encr_iv,
cms->encr_ivlen);
if (err)
return err;
/* write the tag for the encrypted data, it is an implicit octect
string in constructed form and indefinite length */
err = _ksba_ber_write_tl (cms->writer, 0, CLASS_CONTEXT, 1, 0);
if (err)
return err;
/* Now the encrypted data should be written */
leave:
_ksba_der_release (dbld);
return err;
}
static gpg_error_t
ct_build_enveloped_data (ksba_cms_t cms)
{
enum {
sSTART,
sINDATA,
sREST,
sERROR
} state = sERROR;
ksba_stop_reason_t stop_reason;
gpg_error_t err = 0;
stop_reason = cms->stop_reason;
cms->stop_reason = KSBA_SR_RUNNING;
/* Calculate state from last reason and do some checks */
if (stop_reason == KSBA_SR_GOT_CONTENT)
state = sSTART;
else if (stop_reason == KSBA_SR_BEGIN_DATA)
state = sINDATA;
else if (stop_reason == KSBA_SR_END_DATA)
state = sREST;
else if (stop_reason == KSBA_SR_RUNNING)
err = gpg_error (GPG_ERR_INV_STATE);
else if (stop_reason)
err = gpg_error (GPG_ERR_BUG);
if (err)
return err;
/* Do the action */
if (state == sSTART)
err = build_enveloped_data_header (cms);
else if (state == sINDATA)
err = write_encrypted_cont (cms);
else if (state == sREST)
{
/* SPHINX does not allow for unprotectedAttributes */
/* Write 5 end tags */
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (!err)
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (!err)
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
if (!err)
err = _ksba_ber_write_tl (cms->writer, 0, 0, 0, 0);
}
else
err = gpg_error (GPG_ERR_INV_STATE);
if (err)
return err;
/* Calculate new stop reason */
if (state == sSTART)
{ /* user should now write the encrypted data */
stop_reason = KSBA_SR_BEGIN_DATA;
}
else if (state == sINDATA)
{ /* tell the user that we wrote everything */
stop_reason = KSBA_SR_END_DATA;
}
else if (state == sREST)
{
stop_reason = KSBA_SR_READY;
}
cms->stop_reason = stop_reason;
return 0;
}
static gpg_error_t
ct_build_digested_data (ksba_cms_t cms)
{
(void)cms;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
static gpg_error_t
ct_build_encrypted_data (ksba_cms_t cms)
{
(void)cms;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
diff --git a/src/cms.h b/src/cms.h
index f1d7149..2a0f5c7 100644
--- a/src/cms.h
+++ b/src/cms.h
@@ -1,178 +1,181 @@
/* cms.h - Internal definitions for the CMS functions
* Copyright (C) 2001, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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.
*
* KSBA 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 copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#ifndef CMS_H
#define CMS_H 1
#include "ksba.h"
#ifndef HAVE_TYPEDEFD_ASNNODE
typedef struct asn_node_struct *AsnNode; /* FIXME: should not go here */
#define HAVE_TYPEDEFD_ASNNODE
#endif
/* This structure is used to store the results of a BER parser run. */
struct value_tree_s {
struct value_tree_s *next;
AsnNode root; /* root of the tree with the values */
unsigned char *image;
size_t imagelen;
};
struct enc_val_s {
char *algo;
unsigned char *value; /* RSA's "a" or ECDH's "s". (malloced) */
size_t valuelen;
struct {
unsigned char *e; /* Malloced buffer. */
size_t elen; /* Length of E. */
char *encr_algo; /* Malloced OID string. */
char *wrap_algo; /* Malloced OID string. */
} ecdh;
};
struct oidlist_s {
struct oidlist_s *next;
char *oid;
};
/* A structure to store an OID and a parameter. */
struct oidparmlist_s {
struct oidparmlist_s *next;
char *oid;
size_t parmlen;
unsigned char parm[1];
};
struct certlist_s {
struct certlist_s *next;
ksba_cert_t cert;
int msg_digest_len; /* used length of .. */
char msg_digest[64]; /* enough space to store a SHA-512 hash */
ksba_isotime_t signing_time;
struct {
AsnNode root;
unsigned char *image;
} sa;
struct enc_val_s enc_val; /* used for creating enveloped data */
};
struct signer_info_s {
struct signer_info_s *next;
AsnNode root; /* root of the tree with the values */
unsigned char *image;
size_t imagelen;
struct {
char *digest_algo;
} cache;
};
struct sig_val_s {
struct sig_val_s *next;
char *algo;
unsigned char *value; /* Malloced buffer for parameter "s". */
size_t valuelen; /* Used length of VALUE. */
struct {
unsigned char *r; /* Malloced buffer for parameter "r". */
size_t rlen; /* Length of R. */
} ecc;
};
struct ksba_cms_s {
gpg_error_t last_error;
ksba_reader_t reader;
ksba_writer_t writer;
void (*hash_fnc)(void *, const void *, size_t);
void *hash_fnc_arg;
ksba_stop_reason_t stop_reason;
struct {
char *oid;
unsigned long length;
int ndef;
ksba_content_type_t ct;
gpg_error_t (*handler)(ksba_cms_t);
} content;
+ /* Data used with auth enveloped data. */
struct {
- unsigned char *digest;
- int digest_len;
- } data;
+ unsigned char *mac; /* The MAC (authtag) */
+ int mac_len;
+ unsigned char *attr; /* DER encoded authAttr. */
+ int attr_len;
+ } authdata;
int cms_version;
struct oidlist_s *digest_algos;
struct certlist_s *cert_list;
char *inner_cont_oid; /* Encapsulated or Encrypted
ContentInfo.contentType as string */
unsigned long inner_cont_len;
int inner_cont_ndef;
int detached_data; /* no actual data */
char *encr_algo_oid;
char *encr_iv;
size_t encr_ivlen;
struct certlist_s *cert_info_list; /* A list with certificates intended
to be send with a signed message */
struct oidparmlist_s *capability_list; /* A list of S/MIME capabilities. */
struct signer_info_s *signer_info;
struct value_tree_s *recp_info;
struct sig_val_s *sig_val;
struct enc_val_s *enc_val;
};
/*-- cms.c --*/
/*-- cms-parser.c --*/
gpg_error_t _ksba_cms_parse_content_info (ksba_cms_t cms);
gpg_error_t _ksba_cms_parse_signed_data_part_1 (ksba_cms_t cms);
gpg_error_t _ksba_cms_parse_signed_data_part_2 (ksba_cms_t cms);
gpg_error_t _ksba_cms_parse_enveloped_data_part_1 (ksba_cms_t cms);
gpg_error_t _ksba_cms_parse_enveloped_data_part_2 (ksba_cms_t cms);
#endif /*CMS_H*/
diff --git a/src/ksba.h.in b/src/ksba.h.in
index d6b8660..4b0f908 100644
--- a/src/ksba.h.in
+++ b/src/ksba.h.in
@@ -1,639 +1,640 @@
/* ksba.h - X.509 library used by GnuPG
* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2010, 2011
* 2012, 2013, 2104, 2015, 2019, 2020, 2021 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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.
*
* KSBA 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 copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
* SPDX-License-Identifier: LGPL-3.0-or-later OR GPL-2.0-or-later
*/
#ifndef KSBA_H
#define KSBA_H 1
#include
#include
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
/* The version of this header should match the one of the library. Do
* not use this symbol in your application; use assuan_check_version
* instead. */
#define KSBA_VERSION "@VERSION@"
/* The version number of this header. It may be used to handle minor
* API incompatibilities. */
#define KSBA_VERSION_NUMBER @VERSION_NUMBER@
/* Check for compiler features. */
#ifdef __GNUC__
#define _KSBA_GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if _KSBA_GCC_VERSION > 30100
#define _KSBA_DEPRECATED __attribute__ ((__deprecated__))
#endif
#endif /*__GNUC__*/
#ifndef _KSBA_DEPRECATED
#define _KSBA_DEPRECATED
#endif
#define KSBA_CLASS_UNIVERSAL 0
#define KSBA_CLASS_APPLICATION 1
#define KSBA_CLASS_CONTEXT 2
#define KSBA_CLASS_PRIVATE 3
#define KSBA_CLASS_ENCAPSULATE 0x80 /* Pseudo class. */
#define KSBA_TYPE_BOOLEAN 1
#define KSBA_TYPE_INTEGER 2
#define KSBA_TYPE_BIT_STRING 3
#define KSBA_TYPE_OCTET_STRING 4
#define KSBA_TYPE_NULL 5
#define KSBA_TYPE_OBJECT_ID 6
#define KSBA_TYPE_OBJECT_DESCRIPTOR 7
#define KSBA_TYPE_EXTERNAL 8
#define KSBA_TYPE_REAL 9
#define KSBA_TYPE_ENUMERATED 10
#define KSBA_TYPE_EMBEDDED_PDV 11
#define KSBA_TYPE_UTF8_STRING 12
#define KSBA_TYPE_RELATIVE_OID 13
#define KSBA_TYPE_TIME 14
#define KSBA_TYPE_SEQUENCE 16
#define KSBA_TYPE_SET 17
#define KSBA_TYPE_NUMERIC_STRING 18
#define KSBA_TYPE_PRINTABLE_STRING 19
#define KSBA_TYPE_TELETEX_STRING 20
#define KSBA_TYPE_VIDEOTEX_STRING 21
#define KSBA_TYPE_IA5_STRING 22
#define KSBA_TYPE_UTC_TIME 23
#define KSBA_TYPE_GENERALIZED_TIME 24
#define KSBA_TYPE_GRAPHIC_STRING 25
#define KSBA_TYPE_VISIBLE_STRING 26
#define KSBA_TYPE_GENERAL_STRING 27
#define KSBA_TYPE_UNIVERSAL_STRING 28
#define KSBA_TYPE_CHARACTER_STRING 29
#define KSBA_TYPE_BMP_STRING 30
#define KSBA_TYPE_DATE 31
#define KSBA_TYPE_TIME_OF_DAY 32
#define KSBA_TYPE_DATE_TIME 33
#define KSBA_TYPE_DURATION 34
#define KSBA_TYPE_OID_IRI 35
#define KSBA_TYPE_RELATIVE_OID_IRI 36
typedef gpg_error_t KsbaError _KSBA_DEPRECATED;
typedef enum
{
KSBA_CT_NONE = 0,
KSBA_CT_DATA = 1,
KSBA_CT_SIGNED_DATA = 2,
KSBA_CT_ENVELOPED_DATA = 3,
KSBA_CT_DIGESTED_DATA = 4,
KSBA_CT_ENCRYPTED_DATA = 5,
KSBA_CT_AUTH_DATA = 6,
KSBA_CT_PKCS12 = 7,
KSBA_CT_SPC_IND_DATA_CTX = 8,
- KSBA_CT_OPENPGP_KEYBLOCK = 9
+ KSBA_CT_OPENPGP_KEYBLOCK = 9,
+ KSBA_CT_AUTHENVELOPED_DATA = 10
}
ksba_content_type_t;
typedef ksba_content_type_t KsbaContentType _KSBA_DEPRECATED;
typedef enum
{
KSBA_SR_NONE = 0, /* Never seen by libksba user. */
KSBA_SR_RUNNING = 1, /* Never seen by libksba user. */
KSBA_SR_GOT_CONTENT = 2,
KSBA_SR_NEED_HASH = 3,
KSBA_SR_BEGIN_DATA = 4,
KSBA_SR_END_DATA = 5,
KSBA_SR_READY = 6,
KSBA_SR_NEED_SIG = 7,
KSBA_SR_DETACHED_DATA = 8,
KSBA_SR_BEGIN_ITEMS = 9,
KSBA_SR_GOT_ITEM = 10,
KSBA_SR_END_ITEMS = 11
}
ksba_stop_reason_t;
typedef ksba_stop_reason_t KsbaStopReason _KSBA_DEPRECATED;
typedef enum
{
KSBA_CRLREASON_UNSPECIFIED = 1,
KSBA_CRLREASON_KEY_COMPROMISE = 2,
KSBA_CRLREASON_CA_COMPROMISE = 4,
KSBA_CRLREASON_AFFILIATION_CHANGED = 8,
KSBA_CRLREASON_SUPERSEDED = 16,
KSBA_CRLREASON_CESSATION_OF_OPERATION = 32,
KSBA_CRLREASON_CERTIFICATE_HOLD = 64,
KSBA_CRLREASON_REMOVE_FROM_CRL = 256,
KSBA_CRLREASON_PRIVILEGE_WITHDRAWN = 512,
KSBA_CRLREASON_AA_COMPROMISE = 1024,
KSBA_CRLREASON_OTHER = 32768
}
ksba_crl_reason_t;
typedef ksba_crl_reason_t KsbaCRLReason _KSBA_DEPRECATED;
typedef enum
{
KSBA_OCSP_RSPSTATUS_SUCCESS = 0,
KSBA_OCSP_RSPSTATUS_MALFORMED = 1,
KSBA_OCSP_RSPSTATUS_INTERNAL = 2,
KSBA_OCSP_RSPSTATUS_TRYLATER = 3,
KSBA_OCSP_RSPSTATUS_SIGREQUIRED = 5,
KSBA_OCSP_RSPSTATUS_UNAUTHORIZED = 6,
KSBA_OCSP_RSPSTATUS_REPLAYED = 253,
KSBA_OCSP_RSPSTATUS_OTHER = 254,
KSBA_OCSP_RSPSTATUS_NONE = 255
}
ksba_ocsp_response_status_t;
typedef enum
{
KSBA_STATUS_NONE = 0,
KSBA_STATUS_UNKNOWN = 1,
KSBA_STATUS_GOOD = 2,
KSBA_STATUS_REVOKED = 4
}
ksba_status_t;
typedef enum
{
KSBA_KEYUSAGE_DIGITAL_SIGNATURE = 1,
KSBA_KEYUSAGE_NON_REPUDIATION = 2,
KSBA_KEYUSAGE_KEY_ENCIPHERMENT = 4,
KSBA_KEYUSAGE_DATA_ENCIPHERMENT = 8,
KSBA_KEYUSAGE_KEY_AGREEMENT = 16,
KSBA_KEYUSAGE_KEY_CERT_SIGN = 32,
KSBA_KEYUSAGE_CRL_SIGN = 64,
KSBA_KEYUSAGE_ENCIPHER_ONLY = 128,
KSBA_KEYUSAGE_DECIPHER_ONLY = 256
}
ksba_key_usage_t;
typedef ksba_key_usage_t KsbaKeyUsage _KSBA_DEPRECATED;
/* ISO format, e.g. "19610711T172059", assumed to be UTC. */
typedef char ksba_isotime_t[16];
/* X.509 certificates are represented by this object.
ksba_cert_new() creates such an object */
struct ksba_cert_s;
typedef struct ksba_cert_s *ksba_cert_t;
typedef struct ksba_cert_s *KsbaCert _KSBA_DEPRECATED;
/* CMS objects are controlled by this object.
ksba_cms_new() creates it */
struct ksba_cms_s;
typedef struct ksba_cms_s *ksba_cms_t;
typedef struct ksba_cms_s *KsbaCMS _KSBA_DEPRECATED;
/* CRL objects are controlled by this object.
ksba_crl_new() creates it */
struct ksba_crl_s;
typedef struct ksba_crl_s *ksba_crl_t;
typedef struct ksba_crl_s *KsbaCRL _KSBA_DEPRECATED;
/* OCSP objects are controlled by this object.
ksba_ocsp_new() creates it. */
struct ksba_ocsp_s;
typedef struct ksba_ocsp_s *ksba_ocsp_t;
/* PKCS-10 creation is controlled by this object.
ksba_certreq_new() creates it */
struct ksba_certreq_s;
typedef struct ksba_certreq_s *ksba_certreq_t;
typedef struct ksba_certreq_s *KsbaCertreq _KSBA_DEPRECATED;
/* This is a reader object for various purposes
see ksba_reader_new et al. */
struct ksba_reader_s;
typedef struct ksba_reader_s *ksba_reader_t;
typedef struct ksba_reader_s *KsbaReader _KSBA_DEPRECATED;
/* This is a writer object for various purposes
see ksba_writer_new et al. */
struct ksba_writer_s;
typedef struct ksba_writer_s *ksba_writer_t;
typedef struct ksba_writer_s *KsbaWriter _KSBA_DEPRECATED;
/* This is an object to store an ASN.1 parse tree as
create by ksba_asn_parse_file() */
struct ksba_asn_tree_s;
typedef struct ksba_asn_tree_s *ksba_asn_tree_t;
typedef struct ksba_asn_tree_s *KsbaAsnTree _KSBA_DEPRECATED;
/* This is an object to reference a General Name. Such an object is
returned by several functions. */
struct ksba_name_s;
typedef struct ksba_name_s *ksba_name_t;
typedef struct ksba_name_s *KsbaName _KSBA_DEPRECATED;
/* KsbaSexp is just an unsigned char * which should be used for
documentation purpose. The S-expressions returned by libksba are
always in canonical representation with an extra 0 byte at the end,
so that one can print the values in the debugger and at least see
the first bytes */
typedef unsigned char *ksba_sexp_t;
typedef unsigned char *KsbaSexp _KSBA_DEPRECATED;
typedef const unsigned char *ksba_const_sexp_t;
typedef const unsigned char *KsbaConstSexp _KSBA_DEPRECATED;
/* This is a generic object used by various functions. */
struct ksba_der_s;
typedef struct ksba_der_s *ksba_der_t;
/*-- cert.c --*/
gpg_error_t ksba_cert_new (ksba_cert_t *acert);
void ksba_cert_ref (ksba_cert_t cert);
void ksba_cert_release (ksba_cert_t cert);
gpg_error_t ksba_cert_set_user_data (ksba_cert_t cert, const char *key,
const void *data, size_t datalen);
gpg_error_t ksba_cert_get_user_data (ksba_cert_t cert, const char *key,
void *buffer, size_t bufferlen,
size_t *datalen);
gpg_error_t ksba_cert_read_der (ksba_cert_t cert, ksba_reader_t reader);
gpg_error_t ksba_cert_init_from_mem (ksba_cert_t cert,
const void *buffer, size_t length);
const unsigned char *ksba_cert_get_image (ksba_cert_t cert, size_t *r_length);
gpg_error_t ksba_cert_hash (ksba_cert_t cert,
int what,
void (*hasher)(void *,
const void *,
size_t length),
void *hasher_arg);
const char *ksba_cert_get_digest_algo (ksba_cert_t cert);
ksba_sexp_t ksba_cert_get_serial (ksba_cert_t cert);
char *ksba_cert_get_issuer (ksba_cert_t cert, int idx);
gpg_error_t ksba_cert_get_validity (ksba_cert_t cert, int what,
ksba_isotime_t r_time);
char *ksba_cert_get_subject (ksba_cert_t cert, int idx);
ksba_sexp_t ksba_cert_get_public_key (ksba_cert_t cert);
ksba_sexp_t ksba_cert_get_sig_val (ksba_cert_t cert);
gpg_error_t ksba_cert_get_extension (ksba_cert_t cert, int idx,
char const **r_oid, int *r_crit,
size_t *r_deroff, size_t *r_derlen);
gpg_error_t ksba_cert_is_ca (ksba_cert_t cert, int *r_ca, int *r_pathlen);
gpg_error_t ksba_cert_get_key_usage (ksba_cert_t cert, unsigned int *r_flags);
gpg_error_t ksba_cert_get_cert_policies (ksba_cert_t cert, char **r_policies);
gpg_error_t ksba_cert_get_ext_key_usages (ksba_cert_t cert, char **result);
gpg_error_t ksba_cert_get_crl_dist_point (ksba_cert_t cert, int idx,
ksba_name_t *r_distpoint,
ksba_name_t *r_issuer,
ksba_crl_reason_t *r_reason);
gpg_error_t ksba_cert_get_auth_key_id (ksba_cert_t cert,
ksba_sexp_t *r_keyid,
ksba_name_t *r_name,
ksba_sexp_t *r_serial);
gpg_error_t ksba_cert_get_subj_key_id (ksba_cert_t cert,
int *r_crit,
ksba_sexp_t *r_keyid);
gpg_error_t ksba_cert_get_authority_info_access (ksba_cert_t cert, int idx,
char **r_method,
ksba_name_t *r_location);
gpg_error_t ksba_cert_get_subject_info_access (ksba_cert_t cert, int idx,
char **r_method,
ksba_name_t *r_location);
/*-- cms.c --*/
ksba_content_type_t ksba_cms_identify (ksba_reader_t reader);
gpg_error_t ksba_cms_new (ksba_cms_t *r_cms);
void ksba_cms_release (ksba_cms_t cms);
gpg_error_t ksba_cms_set_reader_writer (ksba_cms_t cms,
ksba_reader_t r, ksba_writer_t w);
gpg_error_t ksba_cms_parse (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason);
gpg_error_t ksba_cms_build (ksba_cms_t cms, ksba_stop_reason_t *r_stopreason);
ksba_content_type_t ksba_cms_get_content_type (ksba_cms_t cms, int what);
const char *ksba_cms_get_content_oid (ksba_cms_t cms, int what);
gpg_error_t ksba_cms_get_content_enc_iv (ksba_cms_t cms, void *iv,
size_t maxivlen, size_t *ivlen);
const char *ksba_cms_get_digest_algo_list (ksba_cms_t cms, int idx);
gpg_error_t ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx,
char **r_issuer,
ksba_sexp_t *r_serial);
const char *ksba_cms_get_digest_algo (ksba_cms_t cms, int idx);
ksba_cert_t ksba_cms_get_cert (ksba_cms_t cms, int idx);
gpg_error_t ksba_cms_get_message_digest (ksba_cms_t cms, int idx,
char **r_digest, size_t *r_digest_len);
gpg_error_t ksba_cms_get_signing_time (ksba_cms_t cms, int idx,
ksba_isotime_t r_sigtime);
gpg_error_t ksba_cms_get_sigattr_oids (ksba_cms_t cms, int idx,
const char *reqoid, char **r_value);
ksba_sexp_t ksba_cms_get_sig_val (ksba_cms_t cms, int idx);
ksba_sexp_t ksba_cms_get_enc_val (ksba_cms_t cms, int idx);
void ksba_cms_set_hash_function (ksba_cms_t cms,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg);
gpg_error_t ksba_cms_hash_signed_attrs (ksba_cms_t cms, int idx);
gpg_error_t ksba_cms_set_content_type (ksba_cms_t cms, int what,
ksba_content_type_t type);
gpg_error_t ksba_cms_add_digest_algo (ksba_cms_t cms, const char *oid);
gpg_error_t ksba_cms_add_signer (ksba_cms_t cms, ksba_cert_t cert);
gpg_error_t ksba_cms_add_cert (ksba_cms_t cms, ksba_cert_t cert);
gpg_error_t ksba_cms_add_smime_capability (ksba_cms_t cms, const char *oid,
const unsigned char *der,
size_t derlen);
gpg_error_t ksba_cms_set_message_digest (ksba_cms_t cms, int idx,
const unsigned char *digest,
size_t digest_len);
gpg_error_t ksba_cms_set_signing_time (ksba_cms_t cms, int idx,
const ksba_isotime_t sigtime);
gpg_error_t ksba_cms_set_sig_val (ksba_cms_t cms,
int idx, ksba_const_sexp_t sigval);
gpg_error_t ksba_cms_set_content_enc_algo (ksba_cms_t cms,
const char *oid,
const void *iv,
size_t ivlen);
gpg_error_t ksba_cms_add_recipient (ksba_cms_t cms, ksba_cert_t cert);
gpg_error_t ksba_cms_set_enc_val (ksba_cms_t cms,
int idx, ksba_const_sexp_t encval);
/*-- crl.c --*/
gpg_error_t ksba_crl_new (ksba_crl_t *r_crl);
void ksba_crl_release (ksba_crl_t crl);
gpg_error_t ksba_crl_set_reader (ksba_crl_t crl, ksba_reader_t r);
void ksba_crl_set_hash_function (ksba_crl_t crl,
void (*hash_fnc)(void *,
const void *, size_t),
void *hash_fnc_arg);
const char *ksba_crl_get_digest_algo (ksba_crl_t crl);
gpg_error_t ksba_crl_get_issuer (ksba_crl_t crl, char **r_issuer);
gpg_error_t ksba_crl_get_extension (ksba_crl_t crl, int idx,
char const **oid, int *critical,
unsigned char const **der, size_t *derlen);
gpg_error_t ksba_crl_get_auth_key_id (ksba_crl_t crl,
ksba_sexp_t *r_keyid,
ksba_name_t *r_name,
ksba_sexp_t *r_serial);
gpg_error_t ksba_crl_get_crl_number (ksba_crl_t crl, ksba_sexp_t *number);
gpg_error_t ksba_crl_get_update_times (ksba_crl_t crl,
ksba_isotime_t this_update,
ksba_isotime_t next_update);
gpg_error_t ksba_crl_get_item (ksba_crl_t crl,
ksba_sexp_t *r_serial,
ksba_isotime_t r_revocation_date,
ksba_crl_reason_t *r_reason);
ksba_sexp_t ksba_crl_get_sig_val (ksba_crl_t crl);
gpg_error_t ksba_crl_parse (ksba_crl_t crl, ksba_stop_reason_t *r_stopreason);
/*-- ocsp.c --*/
gpg_error_t ksba_ocsp_new (ksba_ocsp_t *r_oscp);
void ksba_ocsp_release (ksba_ocsp_t ocsp);
gpg_error_t ksba_ocsp_set_digest_algo (ksba_ocsp_t ocsp, const char *oid);
gpg_error_t ksba_ocsp_set_requestor (ksba_ocsp_t ocsp, ksba_cert_t cert);
gpg_error_t ksba_ocsp_add_target (ksba_ocsp_t ocsp,
ksba_cert_t cert, ksba_cert_t issuer_cert);
size_t ksba_ocsp_set_nonce (ksba_ocsp_t ocsp,
unsigned char *nonce, size_t noncelen);
gpg_error_t ksba_ocsp_prepare_request (ksba_ocsp_t ocsp);
gpg_error_t ksba_ocsp_hash_request (ksba_ocsp_t ocsp,
void (*hasher)(void *, const void *,
size_t length),
void *hasher_arg);
gpg_error_t ksba_ocsp_set_sig_val (ksba_ocsp_t ocsp,
ksba_const_sexp_t sigval);
gpg_error_t ksba_ocsp_add_cert (ksba_ocsp_t ocsp, ksba_cert_t cert);
gpg_error_t ksba_ocsp_build_request (ksba_ocsp_t ocsp,
unsigned char **r_buffer,
size_t *r_buflen);
gpg_error_t ksba_ocsp_parse_response (ksba_ocsp_t ocsp,
const unsigned char *msg, size_t msglen,
ksba_ocsp_response_status_t *resp_status);
const char *ksba_ocsp_get_digest_algo (ksba_ocsp_t ocsp);
gpg_error_t ksba_ocsp_hash_response (ksba_ocsp_t ocsp,
const unsigned char *msg, size_t msglen,
void (*hasher)(void *, const void *,
size_t length),
void *hasher_arg);
ksba_sexp_t ksba_ocsp_get_sig_val (ksba_ocsp_t ocsp,
ksba_isotime_t produced_at);
gpg_error_t ksba_ocsp_get_responder_id (ksba_ocsp_t ocsp,
char **r_name,
ksba_sexp_t *r_keyid);
ksba_cert_t ksba_ocsp_get_cert (ksba_ocsp_t ocsp, int idx);
gpg_error_t ksba_ocsp_get_status (ksba_ocsp_t ocsp, ksba_cert_t cert,
ksba_status_t *r_status,
ksba_isotime_t r_this_update,
ksba_isotime_t r_next_update,
ksba_isotime_t r_revocation_time,
ksba_crl_reason_t *r_reason);
gpg_error_t ksba_ocsp_get_extension (ksba_ocsp_t ocsp, ksba_cert_t cert,
int idx,
char const **r_oid, int *r_crit,
unsigned char const **r_der,
size_t *r_derlen);
/*-- certreq.c --*/
gpg_error_t ksba_certreq_new (ksba_certreq_t *r_cr);
void ksba_certreq_release (ksba_certreq_t cr);
gpg_error_t ksba_certreq_set_writer (ksba_certreq_t cr, ksba_writer_t w);
void ksba_certreq_set_hash_function (
ksba_certreq_t cr,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg);
gpg_error_t ksba_certreq_add_subject (ksba_certreq_t cr, const char *name);
gpg_error_t ksba_certreq_set_public_key (ksba_certreq_t cr,
ksba_const_sexp_t key);
gpg_error_t ksba_certreq_add_extension (ksba_certreq_t cr,
const char *oid, int is_crit,
const void *der,
size_t derlen);
gpg_error_t ksba_certreq_set_sig_val (ksba_certreq_t cr,
ksba_const_sexp_t sigval);
gpg_error_t ksba_certreq_build (ksba_certreq_t cr,
ksba_stop_reason_t *r_stopreason);
/* The functions below are used to switch to X.509 certificate creation. */
gpg_error_t ksba_certreq_set_serial (ksba_certreq_t cr, ksba_const_sexp_t sn);
gpg_error_t ksba_certreq_set_issuer (ksba_certreq_t cr, const char *name);
gpg_error_t ksba_certreq_set_validity (ksba_certreq_t cr, int what,
const ksba_isotime_t timebuf);
gpg_error_t ksba_certreq_set_siginfo (ksba_certreq_t cr,
ksba_const_sexp_t siginfo);
/*-- reader.c --*/
gpg_error_t ksba_reader_new (ksba_reader_t *r_r);
void ksba_reader_release (ksba_reader_t r);
gpg_error_t ksba_reader_set_release_notify (ksba_reader_t r,
void (*notify)(void*,ksba_reader_t),
void *notify_value);
gpg_error_t ksba_reader_clear (ksba_reader_t r,
unsigned char **buffer, size_t *buflen);
gpg_error_t ksba_reader_error (ksba_reader_t r);
gpg_error_t ksba_reader_set_mem (ksba_reader_t r,
const void *buffer, size_t length);
gpg_error_t ksba_reader_set_fd (ksba_reader_t r, int fd);
gpg_error_t ksba_reader_set_file (ksba_reader_t r, FILE *fp);
gpg_error_t ksba_reader_set_cb (ksba_reader_t r,
int (*cb)(void*,char *,size_t,size_t*),
void *cb_value );
gpg_error_t ksba_reader_read (ksba_reader_t r,
char *buffer, size_t length, size_t *nread);
gpg_error_t ksba_reader_unread (ksba_reader_t r, const void *buffer, size_t count);
unsigned long ksba_reader_tell (ksba_reader_t r);
/*-- writer.c --*/
gpg_error_t ksba_writer_new (ksba_writer_t *r_w);
void ksba_writer_release (ksba_writer_t w);
gpg_error_t ksba_writer_set_release_notify (ksba_writer_t w,
void (*notify)(void*,ksba_writer_t),
void *notify_value);
int ksba_writer_error (ksba_writer_t w);
unsigned long ksba_writer_tell (ksba_writer_t w);
gpg_error_t ksba_writer_set_fd (ksba_writer_t w, int fd);
gpg_error_t ksba_writer_set_file (ksba_writer_t w, FILE *fp);
gpg_error_t ksba_writer_set_cb (ksba_writer_t w,
int (*cb)(void*,const void *,size_t),
void *cb_value);
gpg_error_t ksba_writer_set_mem (ksba_writer_t w, size_t initial_size);
const void *ksba_writer_get_mem (ksba_writer_t w, size_t *nbytes);
void * ksba_writer_snatch_mem (ksba_writer_t w, size_t *nbytes);
gpg_error_t ksba_writer_set_filter (ksba_writer_t w,
gpg_error_t (*filter)(void*,
const void *,size_t, size_t *,
void *, size_t, size_t *),
void *filter_arg);
gpg_error_t ksba_writer_write (ksba_writer_t w, const void *buffer, size_t length);
gpg_error_t ksba_writer_write_octet_string (ksba_writer_t w,
const void *buffer, size_t length,
int flush);
/*-- asn1-parse.y --*/
int ksba_asn_parse_file (const char *filename, ksba_asn_tree_t *result,
int debug);
void ksba_asn_tree_release (ksba_asn_tree_t tree);
/*-- asn1-func.c --*/
void ksba_asn_tree_dump (ksba_asn_tree_t tree, const char *name, FILE *fp);
gpg_error_t ksba_asn_create_tree (const char *mod_name, ksba_asn_tree_t *result);
/*-- oid.c --*/
char *ksba_oid_to_str (const char *buffer, size_t length);
gpg_error_t ksba_oid_from_str (const char *string,
unsigned char **rbuf, size_t *rlength);
/*-- dn.c --*/
gpg_error_t ksba_dn_der2str (const void *der, size_t derlen, char **r_string);
gpg_error_t ksba_dn_str2der (const char *string,
unsigned char **rder, size_t *rderlen);
gpg_error_t ksba_dn_teststr (const char *string, int seq,
size_t *rerroff, size_t *rerrlen);
/*-- name.c --*/
gpg_error_t ksba_name_new (ksba_name_t *r_name);
void ksba_name_ref (ksba_name_t name);
void ksba_name_release (ksba_name_t name);
const char *ksba_name_enum (ksba_name_t name, int idx);
char *ksba_name_get_uri (ksba_name_t name, int idx);
/*-- der-builder.c --*/
void ksba_der_release (ksba_der_t d);
ksba_der_t ksba_der_builder_new (unsigned int nitems);
void ksba_der_builder_reset (ksba_der_t d);
void ksba_der_add_ptr (ksba_der_t d, int cls, int tag,
void *value, size_t valuelen);
void ksba_der_add_val (ksba_der_t d, int cls, int tag,
const void *value, size_t valuelen);
void ksba_der_add_int (ksba_der_t d, const void *value, size_t valuelen,
int force_positive);
void ksba_der_add_oid (ksba_der_t d, const char *oidstr);
void ksba_der_add_bts (ksba_der_t d, const void *value, size_t valuelen,
unsigned int unusedbits);
void ksba_der_add_der (ksba_der_t d, const void *der, size_t derlen);
void ksba_der_add_tag (ksba_der_t d, int cls, int tag);
void ksba_der_add_end (ksba_der_t d);
gpg_error_t ksba_der_builder_get (ksba_der_t d,
unsigned char **r_obj, size_t *r_objlen);
/*-- util.c --*/
void ksba_set_malloc_hooks ( void *(*new_alloc_func)(size_t n),
void *(*new_realloc_func)(void *p, size_t n),
void (*new_free_func)(void*) );
void ksba_set_hash_buffer_function ( gpg_error_t (*fnc)
(void *arg, const char *oid,
const void *buffer, size_t length,
size_t resultsize,
unsigned char *result,
size_t *resultlen),
void *fnc_arg);
void *ksba_malloc (size_t n );
void *ksba_calloc (size_t n, size_t m );
void *ksba_realloc (void *p, size_t n);
char *ksba_strdup (const char *p);
void ksba_free ( void *a );
/*--version.c --*/
const char *ksba_check_version (const char *req_version);
#ifdef __cplusplus
}
#endif
#endif /*KSBA_H*/
diff --git a/tests/t-cms-parser.c b/tests/t-cms-parser.c
index f8a731c..8e3d574 100644
--- a/tests/t-cms-parser.c
+++ b/tests/t-cms-parser.c
@@ -1,336 +1,339 @@
/* t-cms-parser.c - basic test for the CMS parser.
* Copyright (C) 2001 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA 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.
*
* KSBA 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 .
*/
#include
#include
#include
#include
#include
#include
#include "../src/ksba.h"
#include "t-common.h"
static int quiet;
static int verbose;
void
dummy_hash_fnc (void *arg, const void *buffer, size_t length)
{
(void)arg;
(void)buffer;
(void)length;
}
static int
dummy_writer_cb (void *cb_value, const void *buffer, size_t count)
{
(void)cb_value;
(void)buffer;
(void)count;
return 0;
}
static void
one_file (const char *fname)
{
gpg_error_t err;
FILE *fp;
ksba_reader_t r;
ksba_writer_t w;
ksba_cms_t cms;
+ ksba_content_type_t ct;
int i;
const char *algoid;
ksba_stop_reason_t stopreason;
const char *s;
size_t n;
ksba_sexp_t p;
char *dn;
int idx;
if (!quiet)
printf ("*** checking `%s' ***\n", fname);
fp = fopen (fname, "r");
if (!fp)
{
fprintf (stderr, "%s:%d: can't open `%s': %s\n",
__FILE__, __LINE__, fname, strerror (errno));
exit (1);
}
err = ksba_reader_new (&r);
if (err)
fail_if_err (err);
err = ksba_reader_set_file (r, fp);
fail_if_err (err);
/* Also create a writer so that cms.c won't return an error when
writing processed content. */
err = ksba_writer_new (&w);
if (err)
fail_if_err (err);
err = ksba_writer_set_cb (w, dummy_writer_cb, NULL);
fail_if_err (err);
switch (ksba_cms_identify (r))
{
case KSBA_CT_DATA: s = "data"; break;
case KSBA_CT_SIGNED_DATA: s = "signed data"; break;
case KSBA_CT_ENVELOPED_DATA: s = "enveloped data"; break;
+ case KSBA_CT_AUTHENVELOPED_DATA: s = "auth enveloped data"; break;
case KSBA_CT_DIGESTED_DATA: s = "digested data"; break;
case KSBA_CT_ENCRYPTED_DATA: s = "encrypted data"; break;
case KSBA_CT_AUTH_DATA: s = "auth data"; break;
case KSBA_CT_SPC_IND_DATA_CTX:s = "spc indirect data context"; break;
case KSBA_CT_OPENPGP_KEYBLOCK:s = "openpgp keyblock"; break;
default: s = "unknown"; break;
}
if (!quiet)
printf ("identified as: %s\n", s);
err = ksba_cms_new (&cms);
if (err)
fail_if_err (err);
err = ksba_cms_set_reader_writer (cms, r, w);
fail_if_err (err);
err = ksba_cms_parse (cms, &stopreason);
fail_if_err2 (fname, err);
if (!quiet)
printf ("stop reason: %d\n", stopreason);
s = ksba_cms_get_content_oid (cms, 0);
if (!quiet)
printf ("ContentType: %s\n", s?s:"none");
err = ksba_cms_parse (cms, &stopreason);
fail_if_err2 (fname, err);
if (!quiet)
printf ("stop reason: %d\n", stopreason);
s = ksba_cms_get_content_oid (cms, 1);
if (!quiet)
{
printf ("EncapsulatedContentType: %s\n", s?s:"none");
printf ("DigestAlgorithms:");
}
for (i=0; (algoid = ksba_cms_get_digest_algo_list (cms, i)); i++)
if (!quiet)
printf (" %s", algoid);
if (!quiet)
putchar('\n');
if (stopreason == KSBA_SR_NEED_HASH)
if (!quiet)
printf("Detached signature\n");
ksba_cms_set_hash_function (cms, dummy_hash_fnc, NULL);
do
{
err = ksba_cms_parse (cms, &stopreason);
fail_if_err2 (fname, err);
if (!quiet)
printf ("stop reason: %d\n", stopreason);
}
while (stopreason != KSBA_SR_READY);
- if (ksba_cms_get_content_type (cms, 0) == KSBA_CT_ENVELOPED_DATA)
+ ct = ksba_cms_get_content_type (cms, 0);
+ if (ct == KSBA_CT_ENVELOPED_DATA || ct == KSBA_CT_AUTHENVELOPED_DATA)
{
for (idx=0; ; idx++)
{
err = ksba_cms_get_issuer_serial (cms, idx, &dn, &p);
if (err == -1)
break; /* ready */
if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_CMS_OBJ)
{
printf ("recipient %d"
" - kekri or pwri detected\n", idx);
err = 0;
}
else
{
fail_if_err2 (fname, err);
if (!quiet)
{
printf ("recipient %d - issuer: ", idx);
print_dn (dn);
}
ksba_free (dn);
if (!quiet)
{
putchar ('\n');
printf ("recipient %d - serial: ", idx);
print_sexp_hex (p);
putchar ('\n');
}
ksba_free (p);
}
dn = ksba_cms_get_enc_val (cms, idx);
if (!quiet)
{
printf ("recipient %d - enc_val: ", idx);
print_sexp (dn);
putchar ('\n');
}
ksba_free (dn);
}
}
else
{
for (idx=0; idx < 1; idx++)
{
err = ksba_cms_get_issuer_serial (cms, idx, &dn, &p);
if (gpg_err_code (err) == GPG_ERR_NO_DATA && !idx)
{
if (!quiet)
printf ("this is a certs-only message\n");
break;
}
fail_if_err2 (fname, err);
if (!quiet)
{
printf ("signer %d - issuer: ", idx);
print_dn (dn);
putchar ('\n');
}
ksba_free (dn);
if (!quiet)
{
printf ("signer %d - serial: ", idx);
print_sexp_hex (p);
putchar ('\n');
}
ksba_free (p);
err = ksba_cms_get_message_digest (cms, idx, &dn, &n);
fail_if_err2 (fname, err);
if (!quiet)
{
printf ("signer %d - messageDigest: ", idx);
print_hex (dn, n);
putchar ('\n');
}
ksba_free (dn);
err = ksba_cms_get_sigattr_oids (cms, idx,
"1.2.840.113549.1.9.3",&dn);
if (err && err != -1)
fail_if_err2 (fname, err);
if (err != -1)
{
char *tmp;
for (tmp=dn; *tmp; tmp++)
if (*tmp == '\n')
*tmp = ' ';
if (!quiet)
printf ("signer %d - content-type: %s\n", idx, dn);
ksba_free (dn);
}
algoid = ksba_cms_get_digest_algo (cms, idx);
if (!quiet)
printf ("signer %d - digest algo: %s\n", idx, algoid?algoid:"?");
dn = ksba_cms_get_sig_val (cms, idx);
if (dn)
{
if (!quiet)
{
printf ("signer %d - signature: ", idx);
print_sexp (dn);
putchar ('\n');
}
}
else
{
if (!quiet)
printf ("signer %d - signature not found\n", idx);
}
ksba_free (dn);
}
}
ksba_cms_release (cms);
ksba_writer_release (w);
ksba_reader_release (r);
fclose (fp);
}
int
main (int argc, char **argv)
{
if (argc)
{
argc--; argv++;
}
if (argc && !strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
if (argc)
{
for (; argc; argc--, argv++)
one_file (*argv);
}
else
{
static char *testfiles[] =
{
"samples/detached-sig.cms",
"samples/ecdh-sample1.p7m",
"samples/ecdsa-sample1.p7s",
"samples/rsa-sample1.p7m",
"samples/rsa-sample1.p7s",
NULL
};
char *fname;
int idx;
if (!verbose)
quiet = 1;
for (idx=0; testfiles[idx]; idx++)
{
fname = prepend_srcdir (testfiles[idx]);
one_file (fname);
free(fname);
}
}
if (!quiet)
printf ("*** all checks done\n");
return 0;
}