summaryrefslogtreecommitdiffstats
path: root/lib/x509
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/x509.h46
-rw-r--r--lib/x509/Makefile.am91
-rw-r--r--lib/x509/Makefile.in2575
-rw-r--r--lib/x509/attributes.c299
-rw-r--r--lib/x509/attributes.h35
-rw-r--r--lib/x509/common.c1948
-rw-r--r--lib/x509/common.h308
-rw-r--r--lib/x509/crl.c1425
-rw-r--r--lib/x509/crl_write.c521
-rw-r--r--lib/x509/crq.c3099
-rw-r--r--lib/x509/dn.c1051
-rw-r--r--lib/x509/email-verify.c153
-rw-r--r--lib/x509/extensions.c911
-rw-r--r--lib/x509/hostname-verify.c298
-rw-r--r--lib/x509/ip-in-cidr.h58
-rw-r--r--lib/x509/ip.c268
-rw-r--r--lib/x509/ip.h39
-rw-r--r--lib/x509/key_decode.c678
-rw-r--r--lib/x509/key_encode.c1087
-rw-r--r--lib/x509/krb5.c338
-rw-r--r--lib/x509/krb5.h29
-rw-r--r--lib/x509/mpi.c451
-rw-r--r--lib/x509/name_constraints.c1404
-rw-r--r--lib/x509/ocsp.c2642
-rw-r--r--lib/x509/ocsp.h37
-rw-r--r--lib/x509/ocsp_output.c658
-rw-r--r--lib/x509/output.c3083
-rw-r--r--lib/x509/pkcs12.c2063
-rw-r--r--lib/x509/pkcs12_bag.c906
-rw-r--r--lib/x509/pkcs12_encr.c227
-rw-r--r--lib/x509/pkcs7-attrs.c162
-rw-r--r--lib/x509/pkcs7-crypt.c1793
-rw-r--r--lib/x509/pkcs7-output.c333
-rw-r--r--lib/x509/pkcs7.c2564
-rw-r--r--lib/x509/pkcs7_int.h134
-rw-r--r--lib/x509/privkey.c2388
-rw-r--r--lib/x509/privkey_openssl.c347
-rw-r--r--lib/x509/privkey_pkcs8.c1675
-rw-r--r--lib/x509/privkey_pkcs8_pbes1.c189
-rw-r--r--lib/x509/prov-seed.c141
-rw-r--r--lib/x509/prov-seed.h29
-rw-r--r--lib/x509/sign.c218
-rw-r--r--lib/x509/spki.c130
-rw-r--r--lib/x509/supported_exts.gperf40
-rw-r--r--lib/x509/supported_exts.h161
-rw-r--r--lib/x509/time.c468
-rw-r--r--lib/x509/tls_features.c265
-rw-r--r--lib/x509/verify-high.c1834
-rw-r--r--lib/x509/verify-high.h55
-rw-r--r--lib/x509/verify-high2.c568
-rw-r--r--lib/x509/verify.c1752
-rw-r--r--lib/x509/virt-san.c191
-rw-r--r--lib/x509/virt-san.h29
-rw-r--r--lib/x509/x509.c4689
-rw-r--r--lib/x509/x509_dn.c702
-rw-r--r--lib/x509/x509_ext.c4050
-rw-r--r--lib/x509/x509_ext_int.h34
-rw-r--r--lib/x509/x509_int.h558
-rw-r--r--lib/x509/x509_write.c2125
-rw-r--r--lib/x509_b64.c536
-rw-r--r--lib/x509_b64.h44
61 files changed, 54932 insertions, 0 deletions
diff --git a/lib/x509.h b/lib/x509.h
new file mode 100644
index 0000000..3ad21f5
--- /dev/null
+++ b/lib/x509.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2000-2012 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_H
+#define GNUTLS_LIB_X509_H
+
+#include <libtasn1.h>
+
+
+int _gnutls_x509_cert_verify_peers(gnutls_session_t session,
+ gnutls_typed_vdata_st * data,
+ unsigned int elements,
+ unsigned int *status);
+
+#define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE"
+#define PEM_CERT_SEP "-----BEGIN CERTIFICATE"
+#define PEM_OCSP_RESPONSE "-----BEGIN OCSP RESPONSE"
+#define BARE_PEM_OCSP_RESPONSE "OCSP RESPONSE"
+
+#define PEM_CRL_SEP "-----BEGIN X509 CRL"
+
+
+int _gnutls_x509_raw_privkey_to_gkey(gnutls_privkey_t * privkey,
+ const gnutls_datum_t * raw_key,
+ gnutls_x509_crt_fmt_t type);
+
+#endif /* GNUTLS_LIB_X509_H */
diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am
new file mode 100644
index 0000000..5caf8f8
--- /dev/null
+++ b/lib/x509/Makefile.am
@@ -0,0 +1,91 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2003-2012 Free Software Foundation, Inc.
+#
+# This file is part of GnuTLS.
+#
+# The GnuTLS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 3 of
+# the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>
+
+include $(top_srcdir)/lib/common.mk
+
+AM_CPPFLAGS = \
+ -I$(srcdir)/../../gl \
+ -I$(builddir)/../../gl \
+ -I$(srcdir)/../includes \
+ -I$(builddir)/../includes \
+ -I$(srcdir)/..
+
+if ENABLE_MINITASN1
+AM_CPPFLAGS += -I$(srcdir)/../minitasn1
+endif
+
+EXTRA_DIST = supported_exts.gperf
+BUILT_SOURCES = supported_exts.h
+
+noinst_LTLIBRARIES = libgnutls_x509.la
+
+libgnutls_x509_la_SOURCES = \
+ common.c key_encode.c \
+ common.h key_decode.c \
+ time.c \
+ crl.c \
+ crl_write.c \
+ crq.c \
+ dn.c \
+ attributes.c \
+ attributes.h \
+ prov-seed.c \
+ prov-seed.h \
+ extensions.c \
+ mpi.c \
+ output.c \
+ pkcs12.c \
+ pkcs12_bag.c \
+ pkcs12_encr.c \
+ pkcs7.c \
+ pkcs7-attrs.c \
+ pkcs7-crypt.c pkcs7_int.h \
+ privkey.c \
+ privkey_pkcs8.c \
+ privkey_pkcs8_pbes1.c \
+ privkey_openssl.c \
+ hostname-verify.c \
+ sign.c \
+ verify.c \
+ x509.c x509_dn.c \
+ x509_int.h \
+ x509_write.c \
+ name_constraints.c \
+ verify-high.c \
+ verify-high2.c \
+ verify-high.h \
+ x509_ext.c \
+ email-verify.c \
+ pkcs7-output.c \
+ virt-san.c \
+ virt-san.h \
+ spki.c \
+ x509_ext_int.h \
+ tls_features.c \
+ krb5.c krb5.h \
+ ip.c ip.h ip-in-cidr.h \
+ supported_exts.h ocsp.h
+
+if ENABLE_OCSP
+libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c
+endif
+
+supported_exts.h: $(srcdir)/supported_exts.gperf
+ $(V_GPERF)$(GPERF) --global-table -t $^ > $@-tmp \
+ && sed 's/^const struct supported_exts_st \*/static const struct supported_exts_st \*/' <$@-tmp >$@ \
+ && rm -f $@-tmp
diff --git a/lib/x509/Makefile.in b/lib/x509/Makefile.in
new file mode 100644
index 0000000..84242e0
--- /dev/null
+++ b/lib/x509/Makefile.in
@@ -0,0 +1,2575 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (C) 2003-2012 Free Software Foundation, Inc.
+#
+# This file is part of GnuTLS.
+#
+# The GnuTLS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 3 of
+# the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@ENABLE_MINITASN1_TRUE@am__append_1 = -I$(srcdir)/../minitasn1
+@ENABLE_OCSP_TRUE@am__append_2 = ocsp.c ocsp_output.c
+subdir = lib/x509
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/lib/unistring/m4/gnulib-comp.m4 \
+ $(top_srcdir)/lib/unistring/m4/inline.m4 \
+ $(top_srcdir)/lib/unistring/m4/libunistring-base.m4 \
+ $(top_srcdir)/src/gl/m4/atoll.m4 \
+ $(top_srcdir)/src/gl/m4/bison.m4 \
+ $(top_srcdir)/src/gl/m4/calloc.m4 \
+ $(top_srcdir)/src/gl/m4/clock_time.m4 \
+ $(top_srcdir)/src/gl/m4/codeset.m4 \
+ $(top_srcdir)/src/gl/m4/ctype_h.m4 \
+ $(top_srcdir)/src/gl/m4/environ.m4 \
+ $(top_srcdir)/src/gl/m4/error.m4 \
+ $(top_srcdir)/src/gl/m4/fdopen.m4 \
+ $(top_srcdir)/src/gl/m4/flexmember.m4 \
+ $(top_srcdir)/src/gl/m4/fpending.m4 \
+ $(top_srcdir)/src/gl/m4/fpieee.m4 \
+ $(top_srcdir)/src/gl/m4/fseek.m4 \
+ $(top_srcdir)/src/gl/m4/ftruncate.m4 \
+ $(top_srcdir)/src/gl/m4/getaddrinfo.m4 \
+ $(top_srcdir)/src/gl/m4/getcwd.m4 \
+ $(top_srcdir)/src/gl/m4/getpagesize.m4 \
+ $(top_srcdir)/src/gl/m4/getpass.m4 \
+ $(top_srcdir)/src/gl/m4/getprogname.m4 \
+ $(top_srcdir)/src/gl/m4/gettime.m4 \
+ $(top_srcdir)/src/gl/m4/gnulib-comp.m4 \
+ $(top_srcdir)/src/gl/m4/hostent.m4 \
+ $(top_srcdir)/src/gl/m4/intl-thread-locale.m4 \
+ $(top_srcdir)/src/gl/m4/inttostr.m4 \
+ $(top_srcdir)/src/gl/m4/ioctl.m4 \
+ $(top_srcdir)/src/gl/m4/isblank.m4 \
+ $(top_srcdir)/src/gl/m4/langinfo_h.m4 \
+ $(top_srcdir)/src/gl/m4/lcmessage.m4 \
+ $(top_srcdir)/src/gl/m4/locale-fr.m4 \
+ $(top_srcdir)/src/gl/m4/locale-ja.m4 \
+ $(top_srcdir)/src/gl/m4/locale-tr.m4 \
+ $(top_srcdir)/src/gl/m4/locale-zh.m4 \
+ $(top_srcdir)/src/gl/m4/locale_h.m4 \
+ $(top_srcdir)/src/gl/m4/localename.m4 \
+ $(top_srcdir)/src/gl/m4/lstat.m4 \
+ $(top_srcdir)/src/gl/m4/mktime.m4 \
+ $(top_srcdir)/src/gl/m4/nanosleep.m4 \
+ $(top_srcdir)/src/gl/m4/nstrftime.m4 \
+ $(top_srcdir)/src/gl/m4/parse-datetime.m4 \
+ $(top_srcdir)/src/gl/m4/perror.m4 \
+ $(top_srcdir)/src/gl/m4/pipe.m4 \
+ $(top_srcdir)/src/gl/m4/pthread-thread.m4 \
+ $(top_srcdir)/src/gl/m4/pthread_h.m4 \
+ $(top_srcdir)/src/gl/m4/pthread_sigmask.m4 \
+ $(top_srcdir)/src/gl/m4/putenv.m4 \
+ $(top_srcdir)/src/gl/m4/raise.m4 \
+ $(top_srcdir)/src/gl/m4/reallocarray.m4 \
+ $(top_srcdir)/src/gl/m4/sched_h.m4 \
+ $(top_srcdir)/src/gl/m4/sched_yield.m4 \
+ $(top_srcdir)/src/gl/m4/select.m4 \
+ $(top_srcdir)/src/gl/m4/semaphore.m4 \
+ $(top_srcdir)/src/gl/m4/servent.m4 \
+ $(top_srcdir)/src/gl/m4/setenv.m4 \
+ $(top_srcdir)/src/gl/m4/setlocale.m4 \
+ $(top_srcdir)/src/gl/m4/setlocale_null.m4 \
+ $(top_srcdir)/src/gl/m4/sigaction.m4 \
+ $(top_srcdir)/src/gl/m4/signal_h.m4 \
+ $(top_srcdir)/src/gl/m4/signalblocking.m4 \
+ $(top_srcdir)/src/gl/m4/sleep.m4 \
+ $(top_srcdir)/src/gl/m4/sockets.m4 \
+ $(top_srcdir)/src/gl/m4/strerror.m4 \
+ $(top_srcdir)/src/gl/m4/strerror_r.m4 \
+ $(top_srcdir)/src/gl/m4/strtoll.m4 \
+ $(top_srcdir)/src/gl/m4/symlink.m4 \
+ $(top_srcdir)/src/gl/m4/sys_ioctl_h.m4 \
+ $(top_srcdir)/src/gl/m4/sys_select_h.m4 \
+ $(top_srcdir)/src/gl/m4/thread.m4 \
+ $(top_srcdir)/src/gl/m4/time_rz.m4 \
+ $(top_srcdir)/src/gl/m4/timegm.m4 \
+ $(top_srcdir)/src/gl/m4/timespec.m4 \
+ $(top_srcdir)/src/gl/m4/tm_gmtoff.m4 \
+ $(top_srcdir)/src/gl/m4/tzset.m4 \
+ $(top_srcdir)/src/gl/m4/usleep.m4 \
+ $(top_srcdir)/src/gl/m4/visibility.m4 \
+ $(top_srcdir)/src/gl/m4/xalloc.m4 \
+ $(top_srcdir)/src/gl/m4/yield.m4 $(top_srcdir)/m4/00gnulib.m4 \
+ $(top_srcdir)/m4/__inline.m4 \
+ $(top_srcdir)/m4/absolute-header.m4 $(top_srcdir)/m4/alloca.m4 \
+ $(top_srcdir)/m4/arpa_inet_h.m4 \
+ $(top_srcdir)/m4/ax_ac_append_to_file.m4 \
+ $(top_srcdir)/m4/ax_ac_print_to_file.m4 \
+ $(top_srcdir)/m4/ax_add_am_macro_static.m4 \
+ $(top_srcdir)/m4/ax_am_macros_static.m4 \
+ $(top_srcdir)/m4/ax_check_gnu_make.m4 \
+ $(top_srcdir)/m4/ax_code_coverage.m4 \
+ $(top_srcdir)/m4/ax_file_escapes.m4 \
+ $(top_srcdir)/m4/builtin-expect.m4 \
+ $(top_srcdir)/m4/byteswap.m4 $(top_srcdir)/m4/close.m4 \
+ $(top_srcdir)/m4/double-slash-root.m4 $(top_srcdir)/m4/dup2.m4 \
+ $(top_srcdir)/m4/eealloc.m4 $(top_srcdir)/m4/errno_h.m4 \
+ $(top_srcdir)/m4/explicit_bzero.m4 \
+ $(top_srcdir)/m4/exponentd.m4 $(top_srcdir)/m4/extensions.m4 \
+ $(top_srcdir)/m4/extern-inline.m4 $(top_srcdir)/m4/fcntl-o.m4 \
+ $(top_srcdir)/m4/fcntl.m4 $(top_srcdir)/m4/fcntl_h.m4 \
+ $(top_srcdir)/m4/float_h.m4 $(top_srcdir)/m4/fopen.m4 \
+ $(top_srcdir)/m4/free.m4 $(top_srcdir)/m4/fseeko.m4 \
+ $(top_srcdir)/m4/fstat.m4 $(top_srcdir)/m4/ftell.m4 \
+ $(top_srcdir)/m4/ftello.m4 $(top_srcdir)/m4/func.m4 \
+ $(top_srcdir)/m4/getdelim.m4 $(top_srcdir)/m4/getdtablesize.m4 \
+ $(top_srcdir)/m4/getline.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/gettimeofday.m4 \
+ $(top_srcdir)/m4/gnulib-common.m4 \
+ $(top_srcdir)/m4/gnulib-comp.m4 $(top_srcdir)/m4/gtk-doc.m4 \
+ $(top_srcdir)/m4/guile.m4 $(top_srcdir)/m4/hooks.m4 \
+ $(top_srcdir)/m4/host-cpu-c-abi.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/inet_ntop.m4 \
+ $(top_srcdir)/m4/inet_pton.m4 $(top_srcdir)/m4/intlmacosx.m4 \
+ $(top_srcdir)/m4/intmax_t.m4 $(top_srcdir)/m4/inttypes.m4 \
+ $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/largefile.m4 \
+ $(top_srcdir)/m4/ld-output-def.m4 \
+ $(top_srcdir)/m4/ld-version-script.m4 $(top_srcdir)/m4/ldd.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/limits-h.m4 $(top_srcdir)/m4/lock.m4 \
+ $(top_srcdir)/m4/lseek.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/malloc.m4 \
+ $(top_srcdir)/m4/malloca.m4 $(top_srcdir)/m4/manywarnings.m4 \
+ $(top_srcdir)/m4/memchr.m4 $(top_srcdir)/m4/memmem.m4 \
+ $(top_srcdir)/m4/minmax.m4 $(top_srcdir)/m4/mmap-anon.m4 \
+ $(top_srcdir)/m4/mode_t.m4 $(top_srcdir)/m4/msvc-inval.m4 \
+ $(top_srcdir)/m4/msvc-nothrow.m4 $(top_srcdir)/m4/multiarch.m4 \
+ $(top_srcdir)/m4/netdb_h.m4 $(top_srcdir)/m4/netinet_in_h.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/off_t.m4 \
+ $(top_srcdir)/m4/open-cloexec.m4 \
+ $(top_srcdir)/m4/open-slash.m4 $(top_srcdir)/m4/open.m4 \
+ $(top_srcdir)/m4/pathmax.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/printf.m4 \
+ $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/pthread_rwlock_rdlock.m4 \
+ $(top_srcdir)/m4/read-file.m4 $(top_srcdir)/m4/realloc.m4 \
+ $(top_srcdir)/m4/secure_getenv.m4 $(top_srcdir)/m4/size_max.m4 \
+ $(top_srcdir)/m4/snprintf.m4 $(top_srcdir)/m4/socketlib.m4 \
+ $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sockpfaf.m4 \
+ $(top_srcdir)/m4/ssize_t.m4 $(top_srcdir)/m4/stat-time.m4 \
+ $(top_srcdir)/m4/stat.m4 $(top_srcdir)/m4/stdalign.m4 \
+ $(top_srcdir)/m4/stdbool.m4 $(top_srcdir)/m4/stddef_h.m4 \
+ $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \
+ $(top_srcdir)/m4/stdio_h.m4 $(top_srcdir)/m4/stdlib_h.m4 \
+ $(top_srcdir)/m4/stpcpy.m4 $(top_srcdir)/m4/strcase.m4 \
+ $(top_srcdir)/m4/strdup.m4 $(top_srcdir)/m4/string_h.m4 \
+ $(top_srcdir)/m4/strings_h.m4 $(top_srcdir)/m4/strndup.m4 \
+ $(top_srcdir)/m4/strnlen.m4 $(top_srcdir)/m4/strtok_r.m4 \
+ $(top_srcdir)/m4/strverscmp.m4 \
+ $(top_srcdir)/m4/sys_socket_h.m4 \
+ $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \
+ $(top_srcdir)/m4/sys_types_h.m4 $(top_srcdir)/m4/sys_uio_h.m4 \
+ $(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/time_h.m4 \
+ $(top_srcdir)/m4/time_r.m4 $(top_srcdir)/m4/ungetc.m4 \
+ $(top_srcdir)/m4/unistd_h.m4 \
+ $(top_srcdir)/m4/valgrind-tests.m4 \
+ $(top_srcdir)/m4/vasnprintf.m4 $(top_srcdir)/m4/vasprintf.m4 \
+ $(top_srcdir)/m4/vsnprintf.m4 $(top_srcdir)/m4/warn-on-use.m4 \
+ $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/m4/wchar_h.m4 \
+ $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wint_t.m4 \
+ $(top_srcdir)/m4/xsize.m4 $(top_srcdir)/m4/zzgnulib.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libgnutls_x509_la_LIBADD =
+am__libgnutls_x509_la_SOURCES_DIST = common.c key_encode.c common.h \
+ key_decode.c time.c crl.c crl_write.c crq.c dn.c attributes.c \
+ attributes.h prov-seed.c prov-seed.h extensions.c mpi.c \
+ output.c pkcs12.c pkcs12_bag.c pkcs12_encr.c pkcs7.c \
+ pkcs7-attrs.c pkcs7-crypt.c pkcs7_int.h privkey.c \
+ privkey_pkcs8.c privkey_pkcs8_pbes1.c privkey_openssl.c \
+ hostname-verify.c sign.c verify.c x509.c x509_dn.c x509_int.h \
+ x509_write.c name_constraints.c verify-high.c verify-high2.c \
+ verify-high.h x509_ext.c email-verify.c pkcs7-output.c \
+ virt-san.c virt-san.h spki.c x509_ext_int.h tls_features.c \
+ krb5.c krb5.h ip.c ip.h ip-in-cidr.h supported_exts.h ocsp.h \
+ ocsp.c ocsp_output.c
+@ENABLE_OCSP_TRUE@am__objects_1 = ocsp.lo ocsp_output.lo
+am_libgnutls_x509_la_OBJECTS = common.lo key_encode.lo key_decode.lo \
+ time.lo crl.lo crl_write.lo crq.lo dn.lo attributes.lo \
+ prov-seed.lo extensions.lo mpi.lo output.lo pkcs12.lo \
+ pkcs12_bag.lo pkcs12_encr.lo pkcs7.lo pkcs7-attrs.lo \
+ pkcs7-crypt.lo privkey.lo privkey_pkcs8.lo \
+ privkey_pkcs8_pbes1.lo privkey_openssl.lo hostname-verify.lo \
+ sign.lo verify.lo x509.lo x509_dn.lo x509_write.lo \
+ name_constraints.lo verify-high.lo verify-high2.lo x509_ext.lo \
+ email-verify.lo pkcs7-output.lo virt-san.lo spki.lo \
+ tls_features.lo krb5.lo ip.lo $(am__objects_1)
+libgnutls_x509_la_OBJECTS = $(am_libgnutls_x509_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/attributes.Plo \
+ ./$(DEPDIR)/common.Plo ./$(DEPDIR)/crl.Plo \
+ ./$(DEPDIR)/crl_write.Plo ./$(DEPDIR)/crq.Plo \
+ ./$(DEPDIR)/dn.Plo ./$(DEPDIR)/email-verify.Plo \
+ ./$(DEPDIR)/extensions.Plo ./$(DEPDIR)/hostname-verify.Plo \
+ ./$(DEPDIR)/ip.Plo ./$(DEPDIR)/key_decode.Plo \
+ ./$(DEPDIR)/key_encode.Plo ./$(DEPDIR)/krb5.Plo \
+ ./$(DEPDIR)/mpi.Plo ./$(DEPDIR)/name_constraints.Plo \
+ ./$(DEPDIR)/ocsp.Plo ./$(DEPDIR)/ocsp_output.Plo \
+ ./$(DEPDIR)/output.Plo ./$(DEPDIR)/pkcs12.Plo \
+ ./$(DEPDIR)/pkcs12_bag.Plo ./$(DEPDIR)/pkcs12_encr.Plo \
+ ./$(DEPDIR)/pkcs7-attrs.Plo ./$(DEPDIR)/pkcs7-crypt.Plo \
+ ./$(DEPDIR)/pkcs7-output.Plo ./$(DEPDIR)/pkcs7.Plo \
+ ./$(DEPDIR)/privkey.Plo ./$(DEPDIR)/privkey_openssl.Plo \
+ ./$(DEPDIR)/privkey_pkcs8.Plo \
+ ./$(DEPDIR)/privkey_pkcs8_pbes1.Plo ./$(DEPDIR)/prov-seed.Plo \
+ ./$(DEPDIR)/sign.Plo ./$(DEPDIR)/spki.Plo ./$(DEPDIR)/time.Plo \
+ ./$(DEPDIR)/tls_features.Plo ./$(DEPDIR)/verify-high.Plo \
+ ./$(DEPDIR)/verify-high2.Plo ./$(DEPDIR)/verify.Plo \
+ ./$(DEPDIR)/virt-san.Plo ./$(DEPDIR)/x509.Plo \
+ ./$(DEPDIR)/x509_dn.Plo ./$(DEPDIR)/x509_ext.Plo \
+ ./$(DEPDIR)/x509_write.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgnutls_x509_la_SOURCES)
+DIST_SOURCES = $(am__libgnutls_x509_la_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build-aux/depcomp $(top_srcdir)/lib/common.mk
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AARCH64_CCASFLAGS = @AARCH64_CCASFLAGS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALLOCA_H = @ALLOCA_H@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AM_VALGRINDFLAGS = @AM_VALGRINDFLAGS@
+APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+ASN1PARSER = @ASN1PARSER@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
+BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
+BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
+BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
+BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+BYTESWAP_H = @BYTESWAP_H@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@
+CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@
+CONFIG_INCLUDE = @CONFIG_INCLUDE@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYWRAP_PATCHLEVEL = @CRYWRAP_PATCHLEVEL@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXX_LT_AGE = @CXX_LT_AGE@
+CXX_LT_CURRENT = @CXX_LT_CURRENT@
+CXX_LT_REVISION = @CXX_LT_REVISION@
+CYGPATH_W = @CYGPATH_W@
+DEFAULT_VALGRINDFLAGS = @DEFAULT_VALGRINDFLAGS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DLL_SSL_VERSION = @DLL_SSL_VERSION@
+DLL_VERSION = @DLL_VERSION@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@
+EMULTIHOP_VALUE = @EMULTIHOP_VALUE@
+ENABLE_PADLOCK = @ENABLE_PADLOCK@
+ENOLINK_HIDDEN = @ENOLINK_HIDDEN@
+ENOLINK_VALUE = @ENOLINK_VALUE@
+EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@
+EOVERFLOW_VALUE = @EOVERFLOW_VALUE@
+ERRNO_H = @ERRNO_H@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+FIPS140_LIBS = @FIPS140_LIBS@
+FLOAT_H = @FLOAT_H@
+GCOV = @GCOV@
+GENHTML = @GENHTML@
+GETADDRINFO_LIB = @GETADDRINFO_LIB@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GL_GGL_GNULIB_ACCEPT = @GL_GGL_GNULIB_ACCEPT@
+GL_GGL_GNULIB_ACCEPT4 = @GL_GGL_GNULIB_ACCEPT4@
+GL_GGL_GNULIB_ACCESS = @GL_GGL_GNULIB_ACCESS@
+GL_GGL_GNULIB_ALIGNED_ALLOC = @GL_GGL_GNULIB_ALIGNED_ALLOC@
+GL_GGL_GNULIB_ATOLL = @GL_GGL_GNULIB_ATOLL@
+GL_GGL_GNULIB_BIND = @GL_GGL_GNULIB_BIND@
+GL_GGL_GNULIB_BTOWC = @GL_GGL_GNULIB_BTOWC@
+GL_GGL_GNULIB_CALLOC_POSIX = @GL_GGL_GNULIB_CALLOC_POSIX@
+GL_GGL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GGL_GNULIB_CANONICALIZE_FILE_NAME@
+GL_GGL_GNULIB_CHDIR = @GL_GGL_GNULIB_CHDIR@
+GL_GGL_GNULIB_CHOWN = @GL_GGL_GNULIB_CHOWN@
+GL_GGL_GNULIB_CLOSE = @GL_GGL_GNULIB_CLOSE@
+GL_GGL_GNULIB_CONNECT = @GL_GGL_GNULIB_CONNECT@
+GL_GGL_GNULIB_COPY_FILE_RANGE = @GL_GGL_GNULIB_COPY_FILE_RANGE@
+GL_GGL_GNULIB_CREAT = @GL_GGL_GNULIB_CREAT@
+GL_GGL_GNULIB_CTIME = @GL_GGL_GNULIB_CTIME@
+GL_GGL_GNULIB_DPRINTF = @GL_GGL_GNULIB_DPRINTF@
+GL_GGL_GNULIB_DUP = @GL_GGL_GNULIB_DUP@
+GL_GGL_GNULIB_DUP2 = @GL_GGL_GNULIB_DUP2@
+GL_GGL_GNULIB_DUP3 = @GL_GGL_GNULIB_DUP3@
+GL_GGL_GNULIB_DUPLOCALE = @GL_GGL_GNULIB_DUPLOCALE@
+GL_GGL_GNULIB_ENVIRON = @GL_GGL_GNULIB_ENVIRON@
+GL_GGL_GNULIB_EUIDACCESS = @GL_GGL_GNULIB_EUIDACCESS@
+GL_GGL_GNULIB_EXECL = @GL_GGL_GNULIB_EXECL@
+GL_GGL_GNULIB_EXECLE = @GL_GGL_GNULIB_EXECLE@
+GL_GGL_GNULIB_EXECLP = @GL_GGL_GNULIB_EXECLP@
+GL_GGL_GNULIB_EXECV = @GL_GGL_GNULIB_EXECV@
+GL_GGL_GNULIB_EXECVE = @GL_GGL_GNULIB_EXECVE@
+GL_GGL_GNULIB_EXECVP = @GL_GGL_GNULIB_EXECVP@
+GL_GGL_GNULIB_EXECVPE = @GL_GGL_GNULIB_EXECVPE@
+GL_GGL_GNULIB_EXPLICIT_BZERO = @GL_GGL_GNULIB_EXPLICIT_BZERO@
+GL_GGL_GNULIB_FACCESSAT = @GL_GGL_GNULIB_FACCESSAT@
+GL_GGL_GNULIB_FCHDIR = @GL_GGL_GNULIB_FCHDIR@
+GL_GGL_GNULIB_FCHMODAT = @GL_GGL_GNULIB_FCHMODAT@
+GL_GGL_GNULIB_FCHOWNAT = @GL_GGL_GNULIB_FCHOWNAT@
+GL_GGL_GNULIB_FCLOSE = @GL_GGL_GNULIB_FCLOSE@
+GL_GGL_GNULIB_FCNTL = @GL_GGL_GNULIB_FCNTL@
+GL_GGL_GNULIB_FDATASYNC = @GL_GGL_GNULIB_FDATASYNC@
+GL_GGL_GNULIB_FDOPEN = @GL_GGL_GNULIB_FDOPEN@
+GL_GGL_GNULIB_FFLUSH = @GL_GGL_GNULIB_FFLUSH@
+GL_GGL_GNULIB_FFS = @GL_GGL_GNULIB_FFS@
+GL_GGL_GNULIB_FFSL = @GL_GGL_GNULIB_FFSL@
+GL_GGL_GNULIB_FFSLL = @GL_GGL_GNULIB_FFSLL@
+GL_GGL_GNULIB_FGETC = @GL_GGL_GNULIB_FGETC@
+GL_GGL_GNULIB_FGETS = @GL_GGL_GNULIB_FGETS@
+GL_GGL_GNULIB_FOPEN = @GL_GGL_GNULIB_FOPEN@
+GL_GGL_GNULIB_FPRINTF = @GL_GGL_GNULIB_FPRINTF@
+GL_GGL_GNULIB_FPRINTF_POSIX = @GL_GGL_GNULIB_FPRINTF_POSIX@
+GL_GGL_GNULIB_FPURGE = @GL_GGL_GNULIB_FPURGE@
+GL_GGL_GNULIB_FPUTC = @GL_GGL_GNULIB_FPUTC@
+GL_GGL_GNULIB_FPUTS = @GL_GGL_GNULIB_FPUTS@
+GL_GGL_GNULIB_FREAD = @GL_GGL_GNULIB_FREAD@
+GL_GGL_GNULIB_FREE_POSIX = @GL_GGL_GNULIB_FREE_POSIX@
+GL_GGL_GNULIB_FREOPEN = @GL_GGL_GNULIB_FREOPEN@
+GL_GGL_GNULIB_FSCANF = @GL_GGL_GNULIB_FSCANF@
+GL_GGL_GNULIB_FSEEK = @GL_GGL_GNULIB_FSEEK@
+GL_GGL_GNULIB_FSEEKO = @GL_GGL_GNULIB_FSEEKO@
+GL_GGL_GNULIB_FSTAT = @GL_GGL_GNULIB_FSTAT@
+GL_GGL_GNULIB_FSTATAT = @GL_GGL_GNULIB_FSTATAT@
+GL_GGL_GNULIB_FSYNC = @GL_GGL_GNULIB_FSYNC@
+GL_GGL_GNULIB_FTELL = @GL_GGL_GNULIB_FTELL@
+GL_GGL_GNULIB_FTELLO = @GL_GGL_GNULIB_FTELLO@
+GL_GGL_GNULIB_FTRUNCATE = @GL_GGL_GNULIB_FTRUNCATE@
+GL_GGL_GNULIB_FUTIMENS = @GL_GGL_GNULIB_FUTIMENS@
+GL_GGL_GNULIB_FWRITE = @GL_GGL_GNULIB_FWRITE@
+GL_GGL_GNULIB_GETADDRINFO = @GL_GGL_GNULIB_GETADDRINFO@
+GL_GGL_GNULIB_GETC = @GL_GGL_GNULIB_GETC@
+GL_GGL_GNULIB_GETCHAR = @GL_GGL_GNULIB_GETCHAR@
+GL_GGL_GNULIB_GETCWD = @GL_GGL_GNULIB_GETCWD@
+GL_GGL_GNULIB_GETDELIM = @GL_GGL_GNULIB_GETDELIM@
+GL_GGL_GNULIB_GETDOMAINNAME = @GL_GGL_GNULIB_GETDOMAINNAME@
+GL_GGL_GNULIB_GETDTABLESIZE = @GL_GGL_GNULIB_GETDTABLESIZE@
+GL_GGL_GNULIB_GETENTROPY = @GL_GGL_GNULIB_GETENTROPY@
+GL_GGL_GNULIB_GETGROUPS = @GL_GGL_GNULIB_GETGROUPS@
+GL_GGL_GNULIB_GETHOSTNAME = @GL_GGL_GNULIB_GETHOSTNAME@
+GL_GGL_GNULIB_GETLINE = @GL_GGL_GNULIB_GETLINE@
+GL_GGL_GNULIB_GETLOADAVG = @GL_GGL_GNULIB_GETLOADAVG@
+GL_GGL_GNULIB_GETLOGIN = @GL_GGL_GNULIB_GETLOGIN@
+GL_GGL_GNULIB_GETLOGIN_R = @GL_GGL_GNULIB_GETLOGIN_R@
+GL_GGL_GNULIB_GETOPT_POSIX = @GL_GGL_GNULIB_GETOPT_POSIX@
+GL_GGL_GNULIB_GETPAGESIZE = @GL_GGL_GNULIB_GETPAGESIZE@
+GL_GGL_GNULIB_GETPASS = @GL_GGL_GNULIB_GETPASS@
+GL_GGL_GNULIB_GETPEERNAME = @GL_GGL_GNULIB_GETPEERNAME@
+GL_GGL_GNULIB_GETSOCKNAME = @GL_GGL_GNULIB_GETSOCKNAME@
+GL_GGL_GNULIB_GETSOCKOPT = @GL_GGL_GNULIB_GETSOCKOPT@
+GL_GGL_GNULIB_GETSUBOPT = @GL_GGL_GNULIB_GETSUBOPT@
+GL_GGL_GNULIB_GETTIMEOFDAY = @GL_GGL_GNULIB_GETTIMEOFDAY@
+GL_GGL_GNULIB_GETUMASK = @GL_GGL_GNULIB_GETUMASK@
+GL_GGL_GNULIB_GETUSERSHELL = @GL_GGL_GNULIB_GETUSERSHELL@
+GL_GGL_GNULIB_GRANTPT = @GL_GGL_GNULIB_GRANTPT@
+GL_GGL_GNULIB_GROUP_MEMBER = @GL_GGL_GNULIB_GROUP_MEMBER@
+GL_GGL_GNULIB_IMAXABS = @GL_GGL_GNULIB_IMAXABS@
+GL_GGL_GNULIB_IMAXDIV = @GL_GGL_GNULIB_IMAXDIV@
+GL_GGL_GNULIB_INET_NTOP = @GL_GGL_GNULIB_INET_NTOP@
+GL_GGL_GNULIB_INET_PTON = @GL_GGL_GNULIB_INET_PTON@
+GL_GGL_GNULIB_IOCTL = @GL_GGL_GNULIB_IOCTL@
+GL_GGL_GNULIB_ISATTY = @GL_GGL_GNULIB_ISATTY@
+GL_GGL_GNULIB_ISBLANK = @GL_GGL_GNULIB_ISBLANK@
+GL_GGL_GNULIB_LCHMOD = @GL_GGL_GNULIB_LCHMOD@
+GL_GGL_GNULIB_LCHOWN = @GL_GGL_GNULIB_LCHOWN@
+GL_GGL_GNULIB_LINK = @GL_GGL_GNULIB_LINK@
+GL_GGL_GNULIB_LINKAT = @GL_GGL_GNULIB_LINKAT@
+GL_GGL_GNULIB_LISTEN = @GL_GGL_GNULIB_LISTEN@
+GL_GGL_GNULIB_LOCALECONV = @GL_GGL_GNULIB_LOCALECONV@
+GL_GGL_GNULIB_LOCALENAME = @GL_GGL_GNULIB_LOCALENAME@
+GL_GGL_GNULIB_LOCALTIME = @GL_GGL_GNULIB_LOCALTIME@
+GL_GGL_GNULIB_LSEEK = @GL_GGL_GNULIB_LSEEK@
+GL_GGL_GNULIB_LSTAT = @GL_GGL_GNULIB_LSTAT@
+GL_GGL_GNULIB_MALLOC_POSIX = @GL_GGL_GNULIB_MALLOC_POSIX@
+GL_GGL_GNULIB_MBRLEN = @GL_GGL_GNULIB_MBRLEN@
+GL_GGL_GNULIB_MBRTOWC = @GL_GGL_GNULIB_MBRTOWC@
+GL_GGL_GNULIB_MBSCASECMP = @GL_GGL_GNULIB_MBSCASECMP@
+GL_GGL_GNULIB_MBSCASESTR = @GL_GGL_GNULIB_MBSCASESTR@
+GL_GGL_GNULIB_MBSCHR = @GL_GGL_GNULIB_MBSCHR@
+GL_GGL_GNULIB_MBSCSPN = @GL_GGL_GNULIB_MBSCSPN@
+GL_GGL_GNULIB_MBSINIT = @GL_GGL_GNULIB_MBSINIT@
+GL_GGL_GNULIB_MBSLEN = @GL_GGL_GNULIB_MBSLEN@
+GL_GGL_GNULIB_MBSNCASECMP = @GL_GGL_GNULIB_MBSNCASECMP@
+GL_GGL_GNULIB_MBSNLEN = @GL_GGL_GNULIB_MBSNLEN@
+GL_GGL_GNULIB_MBSNRTOWCS = @GL_GGL_GNULIB_MBSNRTOWCS@
+GL_GGL_GNULIB_MBSPBRK = @GL_GGL_GNULIB_MBSPBRK@
+GL_GGL_GNULIB_MBSPCASECMP = @GL_GGL_GNULIB_MBSPCASECMP@
+GL_GGL_GNULIB_MBSRCHR = @GL_GGL_GNULIB_MBSRCHR@
+GL_GGL_GNULIB_MBSRTOWCS = @GL_GGL_GNULIB_MBSRTOWCS@
+GL_GGL_GNULIB_MBSSEP = @GL_GGL_GNULIB_MBSSEP@
+GL_GGL_GNULIB_MBSSPN = @GL_GGL_GNULIB_MBSSPN@
+GL_GGL_GNULIB_MBSSTR = @GL_GGL_GNULIB_MBSSTR@
+GL_GGL_GNULIB_MBSTOK_R = @GL_GGL_GNULIB_MBSTOK_R@
+GL_GGL_GNULIB_MBTOWC = @GL_GGL_GNULIB_MBTOWC@
+GL_GGL_GNULIB_MDA_ACCESS = @GL_GGL_GNULIB_MDA_ACCESS@
+GL_GGL_GNULIB_MDA_CHDIR = @GL_GGL_GNULIB_MDA_CHDIR@
+GL_GGL_GNULIB_MDA_CHMOD = @GL_GGL_GNULIB_MDA_CHMOD@
+GL_GGL_GNULIB_MDA_CLOSE = @GL_GGL_GNULIB_MDA_CLOSE@
+GL_GGL_GNULIB_MDA_CREAT = @GL_GGL_GNULIB_MDA_CREAT@
+GL_GGL_GNULIB_MDA_DUP = @GL_GGL_GNULIB_MDA_DUP@
+GL_GGL_GNULIB_MDA_DUP2 = @GL_GGL_GNULIB_MDA_DUP2@
+GL_GGL_GNULIB_MDA_ECVT = @GL_GGL_GNULIB_MDA_ECVT@
+GL_GGL_GNULIB_MDA_EXECL = @GL_GGL_GNULIB_MDA_EXECL@
+GL_GGL_GNULIB_MDA_EXECLE = @GL_GGL_GNULIB_MDA_EXECLE@
+GL_GGL_GNULIB_MDA_EXECLP = @GL_GGL_GNULIB_MDA_EXECLP@
+GL_GGL_GNULIB_MDA_EXECV = @GL_GGL_GNULIB_MDA_EXECV@
+GL_GGL_GNULIB_MDA_EXECVE = @GL_GGL_GNULIB_MDA_EXECVE@
+GL_GGL_GNULIB_MDA_EXECVP = @GL_GGL_GNULIB_MDA_EXECVP@
+GL_GGL_GNULIB_MDA_EXECVPE = @GL_GGL_GNULIB_MDA_EXECVPE@
+GL_GGL_GNULIB_MDA_FCLOSEALL = @GL_GGL_GNULIB_MDA_FCLOSEALL@
+GL_GGL_GNULIB_MDA_FCVT = @GL_GGL_GNULIB_MDA_FCVT@
+GL_GGL_GNULIB_MDA_FDOPEN = @GL_GGL_GNULIB_MDA_FDOPEN@
+GL_GGL_GNULIB_MDA_FILENO = @GL_GGL_GNULIB_MDA_FILENO@
+GL_GGL_GNULIB_MDA_GCVT = @GL_GGL_GNULIB_MDA_GCVT@
+GL_GGL_GNULIB_MDA_GETCWD = @GL_GGL_GNULIB_MDA_GETCWD@
+GL_GGL_GNULIB_MDA_GETPID = @GL_GGL_GNULIB_MDA_GETPID@
+GL_GGL_GNULIB_MDA_GETW = @GL_GGL_GNULIB_MDA_GETW@
+GL_GGL_GNULIB_MDA_ISATTY = @GL_GGL_GNULIB_MDA_ISATTY@
+GL_GGL_GNULIB_MDA_LSEEK = @GL_GGL_GNULIB_MDA_LSEEK@
+GL_GGL_GNULIB_MDA_MEMCCPY = @GL_GGL_GNULIB_MDA_MEMCCPY@
+GL_GGL_GNULIB_MDA_MKDIR = @GL_GGL_GNULIB_MDA_MKDIR@
+GL_GGL_GNULIB_MDA_MKTEMP = @GL_GGL_GNULIB_MDA_MKTEMP@
+GL_GGL_GNULIB_MDA_OPEN = @GL_GGL_GNULIB_MDA_OPEN@
+GL_GGL_GNULIB_MDA_PUTENV = @GL_GGL_GNULIB_MDA_PUTENV@
+GL_GGL_GNULIB_MDA_PUTW = @GL_GGL_GNULIB_MDA_PUTW@
+GL_GGL_GNULIB_MDA_READ = @GL_GGL_GNULIB_MDA_READ@
+GL_GGL_GNULIB_MDA_RMDIR = @GL_GGL_GNULIB_MDA_RMDIR@
+GL_GGL_GNULIB_MDA_STRDUP = @GL_GGL_GNULIB_MDA_STRDUP@
+GL_GGL_GNULIB_MDA_SWAB = @GL_GGL_GNULIB_MDA_SWAB@
+GL_GGL_GNULIB_MDA_TEMPNAM = @GL_GGL_GNULIB_MDA_TEMPNAM@
+GL_GGL_GNULIB_MDA_TZSET = @GL_GGL_GNULIB_MDA_TZSET@
+GL_GGL_GNULIB_MDA_UMASK = @GL_GGL_GNULIB_MDA_UMASK@
+GL_GGL_GNULIB_MDA_UNLINK = @GL_GGL_GNULIB_MDA_UNLINK@
+GL_GGL_GNULIB_MDA_WCSDUP = @GL_GGL_GNULIB_MDA_WCSDUP@
+GL_GGL_GNULIB_MDA_WRITE = @GL_GGL_GNULIB_MDA_WRITE@
+GL_GGL_GNULIB_MEMCHR = @GL_GGL_GNULIB_MEMCHR@
+GL_GGL_GNULIB_MEMMEM = @GL_GGL_GNULIB_MEMMEM@
+GL_GGL_GNULIB_MEMPCPY = @GL_GGL_GNULIB_MEMPCPY@
+GL_GGL_GNULIB_MEMRCHR = @GL_GGL_GNULIB_MEMRCHR@
+GL_GGL_GNULIB_MKDIR = @GL_GGL_GNULIB_MKDIR@
+GL_GGL_GNULIB_MKDIRAT = @GL_GGL_GNULIB_MKDIRAT@
+GL_GGL_GNULIB_MKDTEMP = @GL_GGL_GNULIB_MKDTEMP@
+GL_GGL_GNULIB_MKFIFO = @GL_GGL_GNULIB_MKFIFO@
+GL_GGL_GNULIB_MKFIFOAT = @GL_GGL_GNULIB_MKFIFOAT@
+GL_GGL_GNULIB_MKNOD = @GL_GGL_GNULIB_MKNOD@
+GL_GGL_GNULIB_MKNODAT = @GL_GGL_GNULIB_MKNODAT@
+GL_GGL_GNULIB_MKOSTEMP = @GL_GGL_GNULIB_MKOSTEMP@
+GL_GGL_GNULIB_MKOSTEMPS = @GL_GGL_GNULIB_MKOSTEMPS@
+GL_GGL_GNULIB_MKSTEMP = @GL_GGL_GNULIB_MKSTEMP@
+GL_GGL_GNULIB_MKSTEMPS = @GL_GGL_GNULIB_MKSTEMPS@
+GL_GGL_GNULIB_MKTIME = @GL_GGL_GNULIB_MKTIME@
+GL_GGL_GNULIB_NANOSLEEP = @GL_GGL_GNULIB_NANOSLEEP@
+GL_GGL_GNULIB_NL_LANGINFO = @GL_GGL_GNULIB_NL_LANGINFO@
+GL_GGL_GNULIB_NONBLOCKING = @GL_GGL_GNULIB_NONBLOCKING@
+GL_GGL_GNULIB_OBSTACK_PRINTF = @GL_GGL_GNULIB_OBSTACK_PRINTF@
+GL_GGL_GNULIB_OBSTACK_PRINTF_POSIX = @GL_GGL_GNULIB_OBSTACK_PRINTF_POSIX@
+GL_GGL_GNULIB_OPEN = @GL_GGL_GNULIB_OPEN@
+GL_GGL_GNULIB_OPENAT = @GL_GGL_GNULIB_OPENAT@
+GL_GGL_GNULIB_OVERRIDES_STRUCT_STAT = @GL_GGL_GNULIB_OVERRIDES_STRUCT_STAT@
+GL_GGL_GNULIB_PCLOSE = @GL_GGL_GNULIB_PCLOSE@
+GL_GGL_GNULIB_PERROR = @GL_GGL_GNULIB_PERROR@
+GL_GGL_GNULIB_PIPE = @GL_GGL_GNULIB_PIPE@
+GL_GGL_GNULIB_PIPE2 = @GL_GGL_GNULIB_PIPE2@
+GL_GGL_GNULIB_POPEN = @GL_GGL_GNULIB_POPEN@
+GL_GGL_GNULIB_POSIX_MEMALIGN = @GL_GGL_GNULIB_POSIX_MEMALIGN@
+GL_GGL_GNULIB_POSIX_OPENPT = @GL_GGL_GNULIB_POSIX_OPENPT@
+GL_GGL_GNULIB_PREAD = @GL_GGL_GNULIB_PREAD@
+GL_GGL_GNULIB_PRINTF = @GL_GGL_GNULIB_PRINTF@
+GL_GGL_GNULIB_PRINTF_POSIX = @GL_GGL_GNULIB_PRINTF_POSIX@
+GL_GGL_GNULIB_PSELECT = @GL_GGL_GNULIB_PSELECT@
+GL_GGL_GNULIB_PTHREAD_COND = @GL_GGL_GNULIB_PTHREAD_COND@
+GL_GGL_GNULIB_PTHREAD_MUTEX = @GL_GGL_GNULIB_PTHREAD_MUTEX@
+GL_GGL_GNULIB_PTHREAD_MUTEX_TIMEDLOCK = @GL_GGL_GNULIB_PTHREAD_MUTEX_TIMEDLOCK@
+GL_GGL_GNULIB_PTHREAD_ONCE = @GL_GGL_GNULIB_PTHREAD_ONCE@
+GL_GGL_GNULIB_PTHREAD_RWLOCK = @GL_GGL_GNULIB_PTHREAD_RWLOCK@
+GL_GGL_GNULIB_PTHREAD_SIGMASK = @GL_GGL_GNULIB_PTHREAD_SIGMASK@
+GL_GGL_GNULIB_PTHREAD_SPIN = @GL_GGL_GNULIB_PTHREAD_SPIN@
+GL_GGL_GNULIB_PTHREAD_THREAD = @GL_GGL_GNULIB_PTHREAD_THREAD@
+GL_GGL_GNULIB_PTHREAD_TSS = @GL_GGL_GNULIB_PTHREAD_TSS@
+GL_GGL_GNULIB_PTSNAME = @GL_GGL_GNULIB_PTSNAME@
+GL_GGL_GNULIB_PTSNAME_R = @GL_GGL_GNULIB_PTSNAME_R@
+GL_GGL_GNULIB_PUTC = @GL_GGL_GNULIB_PUTC@
+GL_GGL_GNULIB_PUTCHAR = @GL_GGL_GNULIB_PUTCHAR@
+GL_GGL_GNULIB_PUTENV = @GL_GGL_GNULIB_PUTENV@
+GL_GGL_GNULIB_PUTS = @GL_GGL_GNULIB_PUTS@
+GL_GGL_GNULIB_PWRITE = @GL_GGL_GNULIB_PWRITE@
+GL_GGL_GNULIB_QSORT_R = @GL_GGL_GNULIB_QSORT_R@
+GL_GGL_GNULIB_RAISE = @GL_GGL_GNULIB_RAISE@
+GL_GGL_GNULIB_RANDOM = @GL_GGL_GNULIB_RANDOM@
+GL_GGL_GNULIB_RANDOM_R = @GL_GGL_GNULIB_RANDOM_R@
+GL_GGL_GNULIB_RAWMEMCHR = @GL_GGL_GNULIB_RAWMEMCHR@
+GL_GGL_GNULIB_READ = @GL_GGL_GNULIB_READ@
+GL_GGL_GNULIB_READLINK = @GL_GGL_GNULIB_READLINK@
+GL_GGL_GNULIB_READLINKAT = @GL_GGL_GNULIB_READLINKAT@
+GL_GGL_GNULIB_REALLOCARRAY = @GL_GGL_GNULIB_REALLOCARRAY@
+GL_GGL_GNULIB_REALLOC_POSIX = @GL_GGL_GNULIB_REALLOC_POSIX@
+GL_GGL_GNULIB_REALPATH = @GL_GGL_GNULIB_REALPATH@
+GL_GGL_GNULIB_RECV = @GL_GGL_GNULIB_RECV@
+GL_GGL_GNULIB_RECVFROM = @GL_GGL_GNULIB_RECVFROM@
+GL_GGL_GNULIB_REMOVE = @GL_GGL_GNULIB_REMOVE@
+GL_GGL_GNULIB_RENAME = @GL_GGL_GNULIB_RENAME@
+GL_GGL_GNULIB_RENAMEAT = @GL_GGL_GNULIB_RENAMEAT@
+GL_GGL_GNULIB_RMDIR = @GL_GGL_GNULIB_RMDIR@
+GL_GGL_GNULIB_RPMATCH = @GL_GGL_GNULIB_RPMATCH@
+GL_GGL_GNULIB_SCANF = @GL_GGL_GNULIB_SCANF@
+GL_GGL_GNULIB_SCHED_YIELD = @GL_GGL_GNULIB_SCHED_YIELD@
+GL_GGL_GNULIB_SECURE_GETENV = @GL_GGL_GNULIB_SECURE_GETENV@
+GL_GGL_GNULIB_SELECT = @GL_GGL_GNULIB_SELECT@
+GL_GGL_GNULIB_SEND = @GL_GGL_GNULIB_SEND@
+GL_GGL_GNULIB_SENDTO = @GL_GGL_GNULIB_SENDTO@
+GL_GGL_GNULIB_SETENV = @GL_GGL_GNULIB_SETENV@
+GL_GGL_GNULIB_SETHOSTNAME = @GL_GGL_GNULIB_SETHOSTNAME@
+GL_GGL_GNULIB_SETLOCALE = @GL_GGL_GNULIB_SETLOCALE@
+GL_GGL_GNULIB_SETLOCALE_NULL = @GL_GGL_GNULIB_SETLOCALE_NULL@
+GL_GGL_GNULIB_SETSOCKOPT = @GL_GGL_GNULIB_SETSOCKOPT@
+GL_GGL_GNULIB_SHUTDOWN = @GL_GGL_GNULIB_SHUTDOWN@
+GL_GGL_GNULIB_SIGABBREV_NP = @GL_GGL_GNULIB_SIGABBREV_NP@
+GL_GGL_GNULIB_SIGACTION = @GL_GGL_GNULIB_SIGACTION@
+GL_GGL_GNULIB_SIGDESCR_NP = @GL_GGL_GNULIB_SIGDESCR_NP@
+GL_GGL_GNULIB_SIGNAL_H_SIGPIPE = @GL_GGL_GNULIB_SIGNAL_H_SIGPIPE@
+GL_GGL_GNULIB_SIGPROCMASK = @GL_GGL_GNULIB_SIGPROCMASK@
+GL_GGL_GNULIB_SLEEP = @GL_GGL_GNULIB_SLEEP@
+GL_GGL_GNULIB_SNPRINTF = @GL_GGL_GNULIB_SNPRINTF@
+GL_GGL_GNULIB_SOCKET = @GL_GGL_GNULIB_SOCKET@
+GL_GGL_GNULIB_SPRINTF_POSIX = @GL_GGL_GNULIB_SPRINTF_POSIX@
+GL_GGL_GNULIB_STAT = @GL_GGL_GNULIB_STAT@
+GL_GGL_GNULIB_STDIO_H_NONBLOCKING = @GL_GGL_GNULIB_STDIO_H_NONBLOCKING@
+GL_GGL_GNULIB_STDIO_H_SIGPIPE = @GL_GGL_GNULIB_STDIO_H_SIGPIPE@
+GL_GGL_GNULIB_STPCPY = @GL_GGL_GNULIB_STPCPY@
+GL_GGL_GNULIB_STPNCPY = @GL_GGL_GNULIB_STPNCPY@
+GL_GGL_GNULIB_STRCASESTR = @GL_GGL_GNULIB_STRCASESTR@
+GL_GGL_GNULIB_STRCHRNUL = @GL_GGL_GNULIB_STRCHRNUL@
+GL_GGL_GNULIB_STRDUP = @GL_GGL_GNULIB_STRDUP@
+GL_GGL_GNULIB_STRERROR = @GL_GGL_GNULIB_STRERROR@
+GL_GGL_GNULIB_STRERRORNAME_NP = @GL_GGL_GNULIB_STRERRORNAME_NP@
+GL_GGL_GNULIB_STRERROR_R = @GL_GGL_GNULIB_STRERROR_R@
+GL_GGL_GNULIB_STRFTIME = @GL_GGL_GNULIB_STRFTIME@
+GL_GGL_GNULIB_STRNCAT = @GL_GGL_GNULIB_STRNCAT@
+GL_GGL_GNULIB_STRNDUP = @GL_GGL_GNULIB_STRNDUP@
+GL_GGL_GNULIB_STRNLEN = @GL_GGL_GNULIB_STRNLEN@
+GL_GGL_GNULIB_STRPBRK = @GL_GGL_GNULIB_STRPBRK@
+GL_GGL_GNULIB_STRPTIME = @GL_GGL_GNULIB_STRPTIME@
+GL_GGL_GNULIB_STRSEP = @GL_GGL_GNULIB_STRSEP@
+GL_GGL_GNULIB_STRSIGNAL = @GL_GGL_GNULIB_STRSIGNAL@
+GL_GGL_GNULIB_STRSTR = @GL_GGL_GNULIB_STRSTR@
+GL_GGL_GNULIB_STRTOD = @GL_GGL_GNULIB_STRTOD@
+GL_GGL_GNULIB_STRTOIMAX = @GL_GGL_GNULIB_STRTOIMAX@
+GL_GGL_GNULIB_STRTOK_R = @GL_GGL_GNULIB_STRTOK_R@
+GL_GGL_GNULIB_STRTOL = @GL_GGL_GNULIB_STRTOL@
+GL_GGL_GNULIB_STRTOLD = @GL_GGL_GNULIB_STRTOLD@
+GL_GGL_GNULIB_STRTOLL = @GL_GGL_GNULIB_STRTOLL@
+GL_GGL_GNULIB_STRTOUL = @GL_GGL_GNULIB_STRTOUL@
+GL_GGL_GNULIB_STRTOULL = @GL_GGL_GNULIB_STRTOULL@
+GL_GGL_GNULIB_STRTOUMAX = @GL_GGL_GNULIB_STRTOUMAX@
+GL_GGL_GNULIB_STRVERSCMP = @GL_GGL_GNULIB_STRVERSCMP@
+GL_GGL_GNULIB_SYMLINK = @GL_GGL_GNULIB_SYMLINK@
+GL_GGL_GNULIB_SYMLINKAT = @GL_GGL_GNULIB_SYMLINKAT@
+GL_GGL_GNULIB_SYSTEM_POSIX = @GL_GGL_GNULIB_SYSTEM_POSIX@
+GL_GGL_GNULIB_TIMEGM = @GL_GGL_GNULIB_TIMEGM@
+GL_GGL_GNULIB_TIMESPEC_GET = @GL_GGL_GNULIB_TIMESPEC_GET@
+GL_GGL_GNULIB_TIME_R = @GL_GGL_GNULIB_TIME_R@
+GL_GGL_GNULIB_TIME_RZ = @GL_GGL_GNULIB_TIME_RZ@
+GL_GGL_GNULIB_TMPFILE = @GL_GGL_GNULIB_TMPFILE@
+GL_GGL_GNULIB_TRUNCATE = @GL_GGL_GNULIB_TRUNCATE@
+GL_GGL_GNULIB_TTYNAME_R = @GL_GGL_GNULIB_TTYNAME_R@
+GL_GGL_GNULIB_TZSET = @GL_GGL_GNULIB_TZSET@
+GL_GGL_GNULIB_UNISTD_H_NONBLOCKING = @GL_GGL_GNULIB_UNISTD_H_NONBLOCKING@
+GL_GGL_GNULIB_UNISTD_H_SIGPIPE = @GL_GGL_GNULIB_UNISTD_H_SIGPIPE@
+GL_GGL_GNULIB_UNLINK = @GL_GGL_GNULIB_UNLINK@
+GL_GGL_GNULIB_UNLINKAT = @GL_GGL_GNULIB_UNLINKAT@
+GL_GGL_GNULIB_UNLOCKPT = @GL_GGL_GNULIB_UNLOCKPT@
+GL_GGL_GNULIB_UNSETENV = @GL_GGL_GNULIB_UNSETENV@
+GL_GGL_GNULIB_USLEEP = @GL_GGL_GNULIB_USLEEP@
+GL_GGL_GNULIB_UTIMENSAT = @GL_GGL_GNULIB_UTIMENSAT@
+GL_GGL_GNULIB_VASPRINTF = @GL_GGL_GNULIB_VASPRINTF@
+GL_GGL_GNULIB_VDPRINTF = @GL_GGL_GNULIB_VDPRINTF@
+GL_GGL_GNULIB_VFPRINTF = @GL_GGL_GNULIB_VFPRINTF@
+GL_GGL_GNULIB_VFPRINTF_POSIX = @GL_GGL_GNULIB_VFPRINTF_POSIX@
+GL_GGL_GNULIB_VFSCANF = @GL_GGL_GNULIB_VFSCANF@
+GL_GGL_GNULIB_VPRINTF = @GL_GGL_GNULIB_VPRINTF@
+GL_GGL_GNULIB_VPRINTF_POSIX = @GL_GGL_GNULIB_VPRINTF_POSIX@
+GL_GGL_GNULIB_VSCANF = @GL_GGL_GNULIB_VSCANF@
+GL_GGL_GNULIB_VSNPRINTF = @GL_GGL_GNULIB_VSNPRINTF@
+GL_GGL_GNULIB_VSPRINTF_POSIX = @GL_GGL_GNULIB_VSPRINTF_POSIX@
+GL_GGL_GNULIB_WCPCPY = @GL_GGL_GNULIB_WCPCPY@
+GL_GGL_GNULIB_WCPNCPY = @GL_GGL_GNULIB_WCPNCPY@
+GL_GGL_GNULIB_WCRTOMB = @GL_GGL_GNULIB_WCRTOMB@
+GL_GGL_GNULIB_WCSCASECMP = @GL_GGL_GNULIB_WCSCASECMP@
+GL_GGL_GNULIB_WCSCAT = @GL_GGL_GNULIB_WCSCAT@
+GL_GGL_GNULIB_WCSCHR = @GL_GGL_GNULIB_WCSCHR@
+GL_GGL_GNULIB_WCSCMP = @GL_GGL_GNULIB_WCSCMP@
+GL_GGL_GNULIB_WCSCOLL = @GL_GGL_GNULIB_WCSCOLL@
+GL_GGL_GNULIB_WCSCPY = @GL_GGL_GNULIB_WCSCPY@
+GL_GGL_GNULIB_WCSCSPN = @GL_GGL_GNULIB_WCSCSPN@
+GL_GGL_GNULIB_WCSDUP = @GL_GGL_GNULIB_WCSDUP@
+GL_GGL_GNULIB_WCSFTIME = @GL_GGL_GNULIB_WCSFTIME@
+GL_GGL_GNULIB_WCSLEN = @GL_GGL_GNULIB_WCSLEN@
+GL_GGL_GNULIB_WCSNCASECMP = @GL_GGL_GNULIB_WCSNCASECMP@
+GL_GGL_GNULIB_WCSNCAT = @GL_GGL_GNULIB_WCSNCAT@
+GL_GGL_GNULIB_WCSNCMP = @GL_GGL_GNULIB_WCSNCMP@
+GL_GGL_GNULIB_WCSNCPY = @GL_GGL_GNULIB_WCSNCPY@
+GL_GGL_GNULIB_WCSNLEN = @GL_GGL_GNULIB_WCSNLEN@
+GL_GGL_GNULIB_WCSNRTOMBS = @GL_GGL_GNULIB_WCSNRTOMBS@
+GL_GGL_GNULIB_WCSPBRK = @GL_GGL_GNULIB_WCSPBRK@
+GL_GGL_GNULIB_WCSRCHR = @GL_GGL_GNULIB_WCSRCHR@
+GL_GGL_GNULIB_WCSRTOMBS = @GL_GGL_GNULIB_WCSRTOMBS@
+GL_GGL_GNULIB_WCSSPN = @GL_GGL_GNULIB_WCSSPN@
+GL_GGL_GNULIB_WCSSTR = @GL_GGL_GNULIB_WCSSTR@
+GL_GGL_GNULIB_WCSTOK = @GL_GGL_GNULIB_WCSTOK@
+GL_GGL_GNULIB_WCSWIDTH = @GL_GGL_GNULIB_WCSWIDTH@
+GL_GGL_GNULIB_WCSXFRM = @GL_GGL_GNULIB_WCSXFRM@
+GL_GGL_GNULIB_WCTOB = @GL_GGL_GNULIB_WCTOB@
+GL_GGL_GNULIB_WCTOMB = @GL_GGL_GNULIB_WCTOMB@
+GL_GGL_GNULIB_WCWIDTH = @GL_GGL_GNULIB_WCWIDTH@
+GL_GGL_GNULIB_WMEMCHR = @GL_GGL_GNULIB_WMEMCHR@
+GL_GGL_GNULIB_WMEMCMP = @GL_GGL_GNULIB_WMEMCMP@
+GL_GGL_GNULIB_WMEMCPY = @GL_GGL_GNULIB_WMEMCPY@
+GL_GGL_GNULIB_WMEMMOVE = @GL_GGL_GNULIB_WMEMMOVE@
+GL_GGL_GNULIB_WMEMPCPY = @GL_GGL_GNULIB_WMEMPCPY@
+GL_GGL_GNULIB_WMEMSET = @GL_GGL_GNULIB_WMEMSET@
+GL_GGL_GNULIB_WRITE = @GL_GGL_GNULIB_WRITE@
+GL_GGL_GNULIB__EXIT = @GL_GGL_GNULIB__EXIT@
+GL_GNULIB_ACCEPT = @GL_GNULIB_ACCEPT@
+GL_GNULIB_ACCEPT4 = @GL_GNULIB_ACCEPT4@
+GL_GNULIB_ACCESS = @GL_GNULIB_ACCESS@
+GL_GNULIB_ALIGNED_ALLOC = @GL_GNULIB_ALIGNED_ALLOC@
+GL_GNULIB_ATOLL = @GL_GNULIB_ATOLL@
+GL_GNULIB_BIND = @GL_GNULIB_BIND@
+GL_GNULIB_BTOWC = @GL_GNULIB_BTOWC@
+GL_GNULIB_CALLOC_POSIX = @GL_GNULIB_CALLOC_POSIX@
+GL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GNULIB_CANONICALIZE_FILE_NAME@
+GL_GNULIB_CHDIR = @GL_GNULIB_CHDIR@
+GL_GNULIB_CHOWN = @GL_GNULIB_CHOWN@
+GL_GNULIB_CLOSE = @GL_GNULIB_CLOSE@
+GL_GNULIB_CONNECT = @GL_GNULIB_CONNECT@
+GL_GNULIB_COPY_FILE_RANGE = @GL_GNULIB_COPY_FILE_RANGE@
+GL_GNULIB_CREAT = @GL_GNULIB_CREAT@
+GL_GNULIB_CTIME = @GL_GNULIB_CTIME@
+GL_GNULIB_DPRINTF = @GL_GNULIB_DPRINTF@
+GL_GNULIB_DUP = @GL_GNULIB_DUP@
+GL_GNULIB_DUP2 = @GL_GNULIB_DUP2@
+GL_GNULIB_DUP3 = @GL_GNULIB_DUP3@
+GL_GNULIB_ENVIRON = @GL_GNULIB_ENVIRON@
+GL_GNULIB_EUIDACCESS = @GL_GNULIB_EUIDACCESS@
+GL_GNULIB_EXECL = @GL_GNULIB_EXECL@
+GL_GNULIB_EXECLE = @GL_GNULIB_EXECLE@
+GL_GNULIB_EXECLP = @GL_GNULIB_EXECLP@
+GL_GNULIB_EXECV = @GL_GNULIB_EXECV@
+GL_GNULIB_EXECVE = @GL_GNULIB_EXECVE@
+GL_GNULIB_EXECVP = @GL_GNULIB_EXECVP@
+GL_GNULIB_EXECVPE = @GL_GNULIB_EXECVPE@
+GL_GNULIB_EXPLICIT_BZERO = @GL_GNULIB_EXPLICIT_BZERO@
+GL_GNULIB_FACCESSAT = @GL_GNULIB_FACCESSAT@
+GL_GNULIB_FCHDIR = @GL_GNULIB_FCHDIR@
+GL_GNULIB_FCHMODAT = @GL_GNULIB_FCHMODAT@
+GL_GNULIB_FCHOWNAT = @GL_GNULIB_FCHOWNAT@
+GL_GNULIB_FCLOSE = @GL_GNULIB_FCLOSE@
+GL_GNULIB_FCNTL = @GL_GNULIB_FCNTL@
+GL_GNULIB_FDATASYNC = @GL_GNULIB_FDATASYNC@
+GL_GNULIB_FDOPEN = @GL_GNULIB_FDOPEN@
+GL_GNULIB_FFLUSH = @GL_GNULIB_FFLUSH@
+GL_GNULIB_FFS = @GL_GNULIB_FFS@
+GL_GNULIB_FFSL = @GL_GNULIB_FFSL@
+GL_GNULIB_FFSLL = @GL_GNULIB_FFSLL@
+GL_GNULIB_FGETC = @GL_GNULIB_FGETC@
+GL_GNULIB_FGETS = @GL_GNULIB_FGETS@
+GL_GNULIB_FOPEN = @GL_GNULIB_FOPEN@
+GL_GNULIB_FPRINTF = @GL_GNULIB_FPRINTF@
+GL_GNULIB_FPRINTF_POSIX = @GL_GNULIB_FPRINTF_POSIX@
+GL_GNULIB_FPURGE = @GL_GNULIB_FPURGE@
+GL_GNULIB_FPUTC = @GL_GNULIB_FPUTC@
+GL_GNULIB_FPUTS = @GL_GNULIB_FPUTS@
+GL_GNULIB_FREAD = @GL_GNULIB_FREAD@
+GL_GNULIB_FREE_POSIX = @GL_GNULIB_FREE_POSIX@
+GL_GNULIB_FREOPEN = @GL_GNULIB_FREOPEN@
+GL_GNULIB_FSCANF = @GL_GNULIB_FSCANF@
+GL_GNULIB_FSEEK = @GL_GNULIB_FSEEK@
+GL_GNULIB_FSEEKO = @GL_GNULIB_FSEEKO@
+GL_GNULIB_FSTAT = @GL_GNULIB_FSTAT@
+GL_GNULIB_FSTATAT = @GL_GNULIB_FSTATAT@
+GL_GNULIB_FSYNC = @GL_GNULIB_FSYNC@
+GL_GNULIB_FTELL = @GL_GNULIB_FTELL@
+GL_GNULIB_FTELLO = @GL_GNULIB_FTELLO@
+GL_GNULIB_FTRUNCATE = @GL_GNULIB_FTRUNCATE@
+GL_GNULIB_FUTIMENS = @GL_GNULIB_FUTIMENS@
+GL_GNULIB_FWRITE = @GL_GNULIB_FWRITE@
+GL_GNULIB_GETADDRINFO = @GL_GNULIB_GETADDRINFO@
+GL_GNULIB_GETC = @GL_GNULIB_GETC@
+GL_GNULIB_GETCHAR = @GL_GNULIB_GETCHAR@
+GL_GNULIB_GETCWD = @GL_GNULIB_GETCWD@
+GL_GNULIB_GETDELIM = @GL_GNULIB_GETDELIM@
+GL_GNULIB_GETDOMAINNAME = @GL_GNULIB_GETDOMAINNAME@
+GL_GNULIB_GETDTABLESIZE = @GL_GNULIB_GETDTABLESIZE@
+GL_GNULIB_GETENTROPY = @GL_GNULIB_GETENTROPY@
+GL_GNULIB_GETGROUPS = @GL_GNULIB_GETGROUPS@
+GL_GNULIB_GETHOSTNAME = @GL_GNULIB_GETHOSTNAME@
+GL_GNULIB_GETLINE = @GL_GNULIB_GETLINE@
+GL_GNULIB_GETLOADAVG = @GL_GNULIB_GETLOADAVG@
+GL_GNULIB_GETLOGIN = @GL_GNULIB_GETLOGIN@
+GL_GNULIB_GETLOGIN_R = @GL_GNULIB_GETLOGIN_R@
+GL_GNULIB_GETOPT_POSIX = @GL_GNULIB_GETOPT_POSIX@
+GL_GNULIB_GETPAGESIZE = @GL_GNULIB_GETPAGESIZE@
+GL_GNULIB_GETPASS = @GL_GNULIB_GETPASS@
+GL_GNULIB_GETPEERNAME = @GL_GNULIB_GETPEERNAME@
+GL_GNULIB_GETSOCKNAME = @GL_GNULIB_GETSOCKNAME@
+GL_GNULIB_GETSOCKOPT = @GL_GNULIB_GETSOCKOPT@
+GL_GNULIB_GETSUBOPT = @GL_GNULIB_GETSUBOPT@
+GL_GNULIB_GETTIMEOFDAY = @GL_GNULIB_GETTIMEOFDAY@
+GL_GNULIB_GETUMASK = @GL_GNULIB_GETUMASK@
+GL_GNULIB_GETUSERSHELL = @GL_GNULIB_GETUSERSHELL@
+GL_GNULIB_GRANTPT = @GL_GNULIB_GRANTPT@
+GL_GNULIB_GROUP_MEMBER = @GL_GNULIB_GROUP_MEMBER@
+GL_GNULIB_IMAXABS = @GL_GNULIB_IMAXABS@
+GL_GNULIB_IMAXDIV = @GL_GNULIB_IMAXDIV@
+GL_GNULIB_INET_NTOP = @GL_GNULIB_INET_NTOP@
+GL_GNULIB_INET_PTON = @GL_GNULIB_INET_PTON@
+GL_GNULIB_ISATTY = @GL_GNULIB_ISATTY@
+GL_GNULIB_LCHMOD = @GL_GNULIB_LCHMOD@
+GL_GNULIB_LCHOWN = @GL_GNULIB_LCHOWN@
+GL_GNULIB_LINK = @GL_GNULIB_LINK@
+GL_GNULIB_LINKAT = @GL_GNULIB_LINKAT@
+GL_GNULIB_LISTEN = @GL_GNULIB_LISTEN@
+GL_GNULIB_LOCALTIME = @GL_GNULIB_LOCALTIME@
+GL_GNULIB_LSEEK = @GL_GNULIB_LSEEK@
+GL_GNULIB_LSTAT = @GL_GNULIB_LSTAT@
+GL_GNULIB_MALLOC_POSIX = @GL_GNULIB_MALLOC_POSIX@
+GL_GNULIB_MBRLEN = @GL_GNULIB_MBRLEN@
+GL_GNULIB_MBRTOWC = @GL_GNULIB_MBRTOWC@
+GL_GNULIB_MBSCASECMP = @GL_GNULIB_MBSCASECMP@
+GL_GNULIB_MBSCASESTR = @GL_GNULIB_MBSCASESTR@
+GL_GNULIB_MBSCHR = @GL_GNULIB_MBSCHR@
+GL_GNULIB_MBSCSPN = @GL_GNULIB_MBSCSPN@
+GL_GNULIB_MBSINIT = @GL_GNULIB_MBSINIT@
+GL_GNULIB_MBSLEN = @GL_GNULIB_MBSLEN@
+GL_GNULIB_MBSNCASECMP = @GL_GNULIB_MBSNCASECMP@
+GL_GNULIB_MBSNLEN = @GL_GNULIB_MBSNLEN@
+GL_GNULIB_MBSNRTOWCS = @GL_GNULIB_MBSNRTOWCS@
+GL_GNULIB_MBSPBRK = @GL_GNULIB_MBSPBRK@
+GL_GNULIB_MBSPCASECMP = @GL_GNULIB_MBSPCASECMP@
+GL_GNULIB_MBSRCHR = @GL_GNULIB_MBSRCHR@
+GL_GNULIB_MBSRTOWCS = @GL_GNULIB_MBSRTOWCS@
+GL_GNULIB_MBSSEP = @GL_GNULIB_MBSSEP@
+GL_GNULIB_MBSSPN = @GL_GNULIB_MBSSPN@
+GL_GNULIB_MBSSTR = @GL_GNULIB_MBSSTR@
+GL_GNULIB_MBSTOK_R = @GL_GNULIB_MBSTOK_R@
+GL_GNULIB_MBTOWC = @GL_GNULIB_MBTOWC@
+GL_GNULIB_MDA_ACCESS = @GL_GNULIB_MDA_ACCESS@
+GL_GNULIB_MDA_CHDIR = @GL_GNULIB_MDA_CHDIR@
+GL_GNULIB_MDA_CHMOD = @GL_GNULIB_MDA_CHMOD@
+GL_GNULIB_MDA_CLOSE = @GL_GNULIB_MDA_CLOSE@
+GL_GNULIB_MDA_CREAT = @GL_GNULIB_MDA_CREAT@
+GL_GNULIB_MDA_DUP = @GL_GNULIB_MDA_DUP@
+GL_GNULIB_MDA_DUP2 = @GL_GNULIB_MDA_DUP2@
+GL_GNULIB_MDA_ECVT = @GL_GNULIB_MDA_ECVT@
+GL_GNULIB_MDA_EXECL = @GL_GNULIB_MDA_EXECL@
+GL_GNULIB_MDA_EXECLE = @GL_GNULIB_MDA_EXECLE@
+GL_GNULIB_MDA_EXECLP = @GL_GNULIB_MDA_EXECLP@
+GL_GNULIB_MDA_EXECV = @GL_GNULIB_MDA_EXECV@
+GL_GNULIB_MDA_EXECVE = @GL_GNULIB_MDA_EXECVE@
+GL_GNULIB_MDA_EXECVP = @GL_GNULIB_MDA_EXECVP@
+GL_GNULIB_MDA_EXECVPE = @GL_GNULIB_MDA_EXECVPE@
+GL_GNULIB_MDA_FCLOSEALL = @GL_GNULIB_MDA_FCLOSEALL@
+GL_GNULIB_MDA_FCVT = @GL_GNULIB_MDA_FCVT@
+GL_GNULIB_MDA_FDOPEN = @GL_GNULIB_MDA_FDOPEN@
+GL_GNULIB_MDA_FILENO = @GL_GNULIB_MDA_FILENO@
+GL_GNULIB_MDA_GCVT = @GL_GNULIB_MDA_GCVT@
+GL_GNULIB_MDA_GETCWD = @GL_GNULIB_MDA_GETCWD@
+GL_GNULIB_MDA_GETPID = @GL_GNULIB_MDA_GETPID@
+GL_GNULIB_MDA_GETW = @GL_GNULIB_MDA_GETW@
+GL_GNULIB_MDA_ISATTY = @GL_GNULIB_MDA_ISATTY@
+GL_GNULIB_MDA_LSEEK = @GL_GNULIB_MDA_LSEEK@
+GL_GNULIB_MDA_MEMCCPY = @GL_GNULIB_MDA_MEMCCPY@
+GL_GNULIB_MDA_MKDIR = @GL_GNULIB_MDA_MKDIR@
+GL_GNULIB_MDA_MKTEMP = @GL_GNULIB_MDA_MKTEMP@
+GL_GNULIB_MDA_OPEN = @GL_GNULIB_MDA_OPEN@
+GL_GNULIB_MDA_PUTENV = @GL_GNULIB_MDA_PUTENV@
+GL_GNULIB_MDA_PUTW = @GL_GNULIB_MDA_PUTW@
+GL_GNULIB_MDA_READ = @GL_GNULIB_MDA_READ@
+GL_GNULIB_MDA_RMDIR = @GL_GNULIB_MDA_RMDIR@
+GL_GNULIB_MDA_STRDUP = @GL_GNULIB_MDA_STRDUP@
+GL_GNULIB_MDA_SWAB = @GL_GNULIB_MDA_SWAB@
+GL_GNULIB_MDA_TEMPNAM = @GL_GNULIB_MDA_TEMPNAM@
+GL_GNULIB_MDA_TZSET = @GL_GNULIB_MDA_TZSET@
+GL_GNULIB_MDA_UMASK = @GL_GNULIB_MDA_UMASK@
+GL_GNULIB_MDA_UNLINK = @GL_GNULIB_MDA_UNLINK@
+GL_GNULIB_MDA_WCSDUP = @GL_GNULIB_MDA_WCSDUP@
+GL_GNULIB_MDA_WRITE = @GL_GNULIB_MDA_WRITE@
+GL_GNULIB_MEMCHR = @GL_GNULIB_MEMCHR@
+GL_GNULIB_MEMMEM = @GL_GNULIB_MEMMEM@
+GL_GNULIB_MEMPCPY = @GL_GNULIB_MEMPCPY@
+GL_GNULIB_MEMRCHR = @GL_GNULIB_MEMRCHR@
+GL_GNULIB_MKDIR = @GL_GNULIB_MKDIR@
+GL_GNULIB_MKDIRAT = @GL_GNULIB_MKDIRAT@
+GL_GNULIB_MKDTEMP = @GL_GNULIB_MKDTEMP@
+GL_GNULIB_MKFIFO = @GL_GNULIB_MKFIFO@
+GL_GNULIB_MKFIFOAT = @GL_GNULIB_MKFIFOAT@
+GL_GNULIB_MKNOD = @GL_GNULIB_MKNOD@
+GL_GNULIB_MKNODAT = @GL_GNULIB_MKNODAT@
+GL_GNULIB_MKOSTEMP = @GL_GNULIB_MKOSTEMP@
+GL_GNULIB_MKOSTEMPS = @GL_GNULIB_MKOSTEMPS@
+GL_GNULIB_MKSTEMP = @GL_GNULIB_MKSTEMP@
+GL_GNULIB_MKSTEMPS = @GL_GNULIB_MKSTEMPS@
+GL_GNULIB_MKTIME = @GL_GNULIB_MKTIME@
+GL_GNULIB_NANOSLEEP = @GL_GNULIB_NANOSLEEP@
+GL_GNULIB_NONBLOCKING = @GL_GNULIB_NONBLOCKING@
+GL_GNULIB_OBSTACK_PRINTF = @GL_GNULIB_OBSTACK_PRINTF@
+GL_GNULIB_OBSTACK_PRINTF_POSIX = @GL_GNULIB_OBSTACK_PRINTF_POSIX@
+GL_GNULIB_OPEN = @GL_GNULIB_OPEN@
+GL_GNULIB_OPENAT = @GL_GNULIB_OPENAT@
+GL_GNULIB_OVERRIDES_STRUCT_STAT = @GL_GNULIB_OVERRIDES_STRUCT_STAT@
+GL_GNULIB_PCLOSE = @GL_GNULIB_PCLOSE@
+GL_GNULIB_PERROR = @GL_GNULIB_PERROR@
+GL_GNULIB_PIPE = @GL_GNULIB_PIPE@
+GL_GNULIB_PIPE2 = @GL_GNULIB_PIPE2@
+GL_GNULIB_POPEN = @GL_GNULIB_POPEN@
+GL_GNULIB_POSIX_MEMALIGN = @GL_GNULIB_POSIX_MEMALIGN@
+GL_GNULIB_POSIX_OPENPT = @GL_GNULIB_POSIX_OPENPT@
+GL_GNULIB_PREAD = @GL_GNULIB_PREAD@
+GL_GNULIB_PRINTF = @GL_GNULIB_PRINTF@
+GL_GNULIB_PRINTF_POSIX = @GL_GNULIB_PRINTF_POSIX@
+GL_GNULIB_PTSNAME = @GL_GNULIB_PTSNAME@
+GL_GNULIB_PTSNAME_R = @GL_GNULIB_PTSNAME_R@
+GL_GNULIB_PUTC = @GL_GNULIB_PUTC@
+GL_GNULIB_PUTCHAR = @GL_GNULIB_PUTCHAR@
+GL_GNULIB_PUTENV = @GL_GNULIB_PUTENV@
+GL_GNULIB_PUTS = @GL_GNULIB_PUTS@
+GL_GNULIB_PWRITE = @GL_GNULIB_PWRITE@
+GL_GNULIB_QSORT_R = @GL_GNULIB_QSORT_R@
+GL_GNULIB_RANDOM = @GL_GNULIB_RANDOM@
+GL_GNULIB_RANDOM_R = @GL_GNULIB_RANDOM_R@
+GL_GNULIB_RAWMEMCHR = @GL_GNULIB_RAWMEMCHR@
+GL_GNULIB_READ = @GL_GNULIB_READ@
+GL_GNULIB_READLINK = @GL_GNULIB_READLINK@
+GL_GNULIB_READLINKAT = @GL_GNULIB_READLINKAT@
+GL_GNULIB_REALLOCARRAY = @GL_GNULIB_REALLOCARRAY@
+GL_GNULIB_REALLOC_POSIX = @GL_GNULIB_REALLOC_POSIX@
+GL_GNULIB_REALPATH = @GL_GNULIB_REALPATH@
+GL_GNULIB_RECV = @GL_GNULIB_RECV@
+GL_GNULIB_RECVFROM = @GL_GNULIB_RECVFROM@
+GL_GNULIB_REMOVE = @GL_GNULIB_REMOVE@
+GL_GNULIB_RENAME = @GL_GNULIB_RENAME@
+GL_GNULIB_RENAMEAT = @GL_GNULIB_RENAMEAT@
+GL_GNULIB_RMDIR = @GL_GNULIB_RMDIR@
+GL_GNULIB_RPMATCH = @GL_GNULIB_RPMATCH@
+GL_GNULIB_SCANF = @GL_GNULIB_SCANF@
+GL_GNULIB_SECURE_GETENV = @GL_GNULIB_SECURE_GETENV@
+GL_GNULIB_SEND = @GL_GNULIB_SEND@
+GL_GNULIB_SENDTO = @GL_GNULIB_SENDTO@
+GL_GNULIB_SETENV = @GL_GNULIB_SETENV@
+GL_GNULIB_SETHOSTNAME = @GL_GNULIB_SETHOSTNAME@
+GL_GNULIB_SETSOCKOPT = @GL_GNULIB_SETSOCKOPT@
+GL_GNULIB_SHUTDOWN = @GL_GNULIB_SHUTDOWN@
+GL_GNULIB_SIGABBREV_NP = @GL_GNULIB_SIGABBREV_NP@
+GL_GNULIB_SIGDESCR_NP = @GL_GNULIB_SIGDESCR_NP@
+GL_GNULIB_SLEEP = @GL_GNULIB_SLEEP@
+GL_GNULIB_SNPRINTF = @GL_GNULIB_SNPRINTF@
+GL_GNULIB_SOCKET = @GL_GNULIB_SOCKET@
+GL_GNULIB_SPRINTF_POSIX = @GL_GNULIB_SPRINTF_POSIX@
+GL_GNULIB_STAT = @GL_GNULIB_STAT@
+GL_GNULIB_STDIO_H_NONBLOCKING = @GL_GNULIB_STDIO_H_NONBLOCKING@
+GL_GNULIB_STDIO_H_SIGPIPE = @GL_GNULIB_STDIO_H_SIGPIPE@
+GL_GNULIB_STPCPY = @GL_GNULIB_STPCPY@
+GL_GNULIB_STPNCPY = @GL_GNULIB_STPNCPY@
+GL_GNULIB_STRCASESTR = @GL_GNULIB_STRCASESTR@
+GL_GNULIB_STRCHRNUL = @GL_GNULIB_STRCHRNUL@
+GL_GNULIB_STRDUP = @GL_GNULIB_STRDUP@
+GL_GNULIB_STRERROR = @GL_GNULIB_STRERROR@
+GL_GNULIB_STRERRORNAME_NP = @GL_GNULIB_STRERRORNAME_NP@
+GL_GNULIB_STRERROR_R = @GL_GNULIB_STRERROR_R@
+GL_GNULIB_STRFTIME = @GL_GNULIB_STRFTIME@
+GL_GNULIB_STRNCAT = @GL_GNULIB_STRNCAT@
+GL_GNULIB_STRNDUP = @GL_GNULIB_STRNDUP@
+GL_GNULIB_STRNLEN = @GL_GNULIB_STRNLEN@
+GL_GNULIB_STRPBRK = @GL_GNULIB_STRPBRK@
+GL_GNULIB_STRPTIME = @GL_GNULIB_STRPTIME@
+GL_GNULIB_STRSEP = @GL_GNULIB_STRSEP@
+GL_GNULIB_STRSIGNAL = @GL_GNULIB_STRSIGNAL@
+GL_GNULIB_STRSTR = @GL_GNULIB_STRSTR@
+GL_GNULIB_STRTOD = @GL_GNULIB_STRTOD@
+GL_GNULIB_STRTOIMAX = @GL_GNULIB_STRTOIMAX@
+GL_GNULIB_STRTOK_R = @GL_GNULIB_STRTOK_R@
+GL_GNULIB_STRTOL = @GL_GNULIB_STRTOL@
+GL_GNULIB_STRTOLD = @GL_GNULIB_STRTOLD@
+GL_GNULIB_STRTOLL = @GL_GNULIB_STRTOLL@
+GL_GNULIB_STRTOUL = @GL_GNULIB_STRTOUL@
+GL_GNULIB_STRTOULL = @GL_GNULIB_STRTOULL@
+GL_GNULIB_STRTOUMAX = @GL_GNULIB_STRTOUMAX@
+GL_GNULIB_STRVERSCMP = @GL_GNULIB_STRVERSCMP@
+GL_GNULIB_SYMLINK = @GL_GNULIB_SYMLINK@
+GL_GNULIB_SYMLINKAT = @GL_GNULIB_SYMLINKAT@
+GL_GNULIB_SYSTEM_POSIX = @GL_GNULIB_SYSTEM_POSIX@
+GL_GNULIB_TIMEGM = @GL_GNULIB_TIMEGM@
+GL_GNULIB_TIMESPEC_GET = @GL_GNULIB_TIMESPEC_GET@
+GL_GNULIB_TIME_R = @GL_GNULIB_TIME_R@
+GL_GNULIB_TIME_RZ = @GL_GNULIB_TIME_RZ@
+GL_GNULIB_TMPFILE = @GL_GNULIB_TMPFILE@
+GL_GNULIB_TRUNCATE = @GL_GNULIB_TRUNCATE@
+GL_GNULIB_TTYNAME_R = @GL_GNULIB_TTYNAME_R@
+GL_GNULIB_TZSET = @GL_GNULIB_TZSET@
+GL_GNULIB_UNISTD_H_NONBLOCKING = @GL_GNULIB_UNISTD_H_NONBLOCKING@
+GL_GNULIB_UNISTD_H_SIGPIPE = @GL_GNULIB_UNISTD_H_SIGPIPE@
+GL_GNULIB_UNLINK = @GL_GNULIB_UNLINK@
+GL_GNULIB_UNLINKAT = @GL_GNULIB_UNLINKAT@
+GL_GNULIB_UNLOCKPT = @GL_GNULIB_UNLOCKPT@
+GL_GNULIB_UNSETENV = @GL_GNULIB_UNSETENV@
+GL_GNULIB_USLEEP = @GL_GNULIB_USLEEP@
+GL_GNULIB_UTIMENSAT = @GL_GNULIB_UTIMENSAT@
+GL_GNULIB_VASPRINTF = @GL_GNULIB_VASPRINTF@
+GL_GNULIB_VDPRINTF = @GL_GNULIB_VDPRINTF@
+GL_GNULIB_VFPRINTF = @GL_GNULIB_VFPRINTF@
+GL_GNULIB_VFPRINTF_POSIX = @GL_GNULIB_VFPRINTF_POSIX@
+GL_GNULIB_VFSCANF = @GL_GNULIB_VFSCANF@
+GL_GNULIB_VPRINTF = @GL_GNULIB_VPRINTF@
+GL_GNULIB_VPRINTF_POSIX = @GL_GNULIB_VPRINTF_POSIX@
+GL_GNULIB_VSCANF = @GL_GNULIB_VSCANF@
+GL_GNULIB_VSNPRINTF = @GL_GNULIB_VSNPRINTF@
+GL_GNULIB_VSPRINTF_POSIX = @GL_GNULIB_VSPRINTF_POSIX@
+GL_GNULIB_WCPCPY = @GL_GNULIB_WCPCPY@
+GL_GNULIB_WCPNCPY = @GL_GNULIB_WCPNCPY@
+GL_GNULIB_WCRTOMB = @GL_GNULIB_WCRTOMB@
+GL_GNULIB_WCSCASECMP = @GL_GNULIB_WCSCASECMP@
+GL_GNULIB_WCSCAT = @GL_GNULIB_WCSCAT@
+GL_GNULIB_WCSCHR = @GL_GNULIB_WCSCHR@
+GL_GNULIB_WCSCMP = @GL_GNULIB_WCSCMP@
+GL_GNULIB_WCSCOLL = @GL_GNULIB_WCSCOLL@
+GL_GNULIB_WCSCPY = @GL_GNULIB_WCSCPY@
+GL_GNULIB_WCSCSPN = @GL_GNULIB_WCSCSPN@
+GL_GNULIB_WCSDUP = @GL_GNULIB_WCSDUP@
+GL_GNULIB_WCSFTIME = @GL_GNULIB_WCSFTIME@
+GL_GNULIB_WCSLEN = @GL_GNULIB_WCSLEN@
+GL_GNULIB_WCSNCASECMP = @GL_GNULIB_WCSNCASECMP@
+GL_GNULIB_WCSNCAT = @GL_GNULIB_WCSNCAT@
+GL_GNULIB_WCSNCMP = @GL_GNULIB_WCSNCMP@
+GL_GNULIB_WCSNCPY = @GL_GNULIB_WCSNCPY@
+GL_GNULIB_WCSNLEN = @GL_GNULIB_WCSNLEN@
+GL_GNULIB_WCSNRTOMBS = @GL_GNULIB_WCSNRTOMBS@
+GL_GNULIB_WCSPBRK = @GL_GNULIB_WCSPBRK@
+GL_GNULIB_WCSRCHR = @GL_GNULIB_WCSRCHR@
+GL_GNULIB_WCSRTOMBS = @GL_GNULIB_WCSRTOMBS@
+GL_GNULIB_WCSSPN = @GL_GNULIB_WCSSPN@
+GL_GNULIB_WCSSTR = @GL_GNULIB_WCSSTR@
+GL_GNULIB_WCSTOK = @GL_GNULIB_WCSTOK@
+GL_GNULIB_WCSWIDTH = @GL_GNULIB_WCSWIDTH@
+GL_GNULIB_WCSXFRM = @GL_GNULIB_WCSXFRM@
+GL_GNULIB_WCTOB = @GL_GNULIB_WCTOB@
+GL_GNULIB_WCTOMB = @GL_GNULIB_WCTOMB@
+GL_GNULIB_WCWIDTH = @GL_GNULIB_WCWIDTH@
+GL_GNULIB_WMEMCHR = @GL_GNULIB_WMEMCHR@
+GL_GNULIB_WMEMCMP = @GL_GNULIB_WMEMCMP@
+GL_GNULIB_WMEMCPY = @GL_GNULIB_WMEMCPY@
+GL_GNULIB_WMEMMOVE = @GL_GNULIB_WMEMMOVE@
+GL_GNULIB_WMEMPCPY = @GL_GNULIB_WMEMPCPY@
+GL_GNULIB_WMEMSET = @GL_GNULIB_WMEMSET@
+GL_GNULIB_WRITE = @GL_GNULIB_WRITE@
+GL_GNULIB__EXIT = @GL_GNULIB__EXIT@
+GMP_CFLAGS = @GMP_CFLAGS@
+GMP_LIBS = @GMP_LIBS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNULIBHEADERS_OVERRIDE_WINT_T = @GNULIBHEADERS_OVERRIDE_WINT_T@
+GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@
+GNUTLS_LIBS_PRIVATE = @GNUTLS_LIBS_PRIVATE@
+GNUTLS_REQUIRES_PRIVATE = @GNUTLS_REQUIRES_PRIVATE@
+GPERF = @GPERF@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUILD = @GUILD@
+GUILE = @GUILE@
+GUILE_CFLAGS = @GUILE_CFLAGS@
+GUILE_CONFIG = @GUILE_CONFIG@
+GUILE_EFFECTIVE_VERSION = @GUILE_EFFECTIVE_VERSION@
+GUILE_EXTENSION = @GUILE_EXTENSION@
+GUILE_LDFLAGS = @GUILE_LDFLAGS@
+GUILE_LIBS = @GUILE_LIBS@
+GUILE_LTLIBS = @GUILE_LTLIBS@
+GUILE_SITE = @GUILE_SITE@
+GUILE_SITE_CCACHE = @GUILE_SITE_CCACHE@
+GUILE_TOOLS = @GUILE_TOOLS@
+HAVE_ACCEPT4 = @HAVE_ACCEPT4@
+HAVE_ALIGNED_ALLOC = @HAVE_ALIGNED_ALLOC@
+HAVE_ALLOCA_H = @HAVE_ALLOCA_H@
+HAVE_ARPA_INET_H = @HAVE_ARPA_INET_H@
+HAVE_ATOLL = @HAVE_ATOLL@
+HAVE_BTOWC = @HAVE_BTOWC@
+HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@
+HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@
+HAVE_CHOWN = @HAVE_CHOWN@
+HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@
+HAVE_CRTDEFS_H = @HAVE_CRTDEFS_H@
+HAVE_DECL_ECVT = @HAVE_DECL_ECVT@
+HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
+HAVE_DECL_EXECVPE = @HAVE_DECL_EXECVPE@
+HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@
+HAVE_DECL_FCLOSEALL = @HAVE_DECL_FCLOSEALL@
+HAVE_DECL_FCVT = @HAVE_DECL_FCVT@
+HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@
+HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@
+HAVE_DECL_FREEADDRINFO = @HAVE_DECL_FREEADDRINFO@
+HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@
+HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@
+HAVE_DECL_GAI_STRERROR = @HAVE_DECL_GAI_STRERROR@
+HAVE_DECL_GCVT = @HAVE_DECL_GCVT@
+HAVE_DECL_GETADDRINFO = @HAVE_DECL_GETADDRINFO@
+HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@
+HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@
+HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@
+HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@
+HAVE_DECL_GETLOGIN = @HAVE_DECL_GETLOGIN@
+HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@
+HAVE_DECL_GETNAMEINFO = @HAVE_DECL_GETNAMEINFO@
+HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@
+HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@
+HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@
+HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@
+HAVE_DECL_INET_NTOP = @HAVE_DECL_INET_NTOP@
+HAVE_DECL_INET_PTON = @HAVE_DECL_INET_PTON@
+HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@
+HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@
+HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
+HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
+HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@
+HAVE_DECL_SETENV = @HAVE_DECL_SETENV@
+HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@
+HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@
+HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@
+HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
+HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@
+HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@
+HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
+HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@
+HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
+HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@
+HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
+HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@
+HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@
+HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@
+HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@
+HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@
+HAVE_DECL_WCSDUP = @HAVE_DECL_WCSDUP@
+HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@
+HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@
+HAVE_DPRINTF = @HAVE_DPRINTF@
+HAVE_DUP3 = @HAVE_DUP3@
+HAVE_DUPLOCALE = @HAVE_DUPLOCALE@
+HAVE_EUIDACCESS = @HAVE_EUIDACCESS@
+HAVE_EXECVPE = @HAVE_EXECVPE@
+HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@
+HAVE_FACCESSAT = @HAVE_FACCESSAT@
+HAVE_FCHDIR = @HAVE_FCHDIR@
+HAVE_FCHMODAT = @HAVE_FCHMODAT@
+HAVE_FCHOWNAT = @HAVE_FCHOWNAT@
+HAVE_FCNTL = @HAVE_FCNTL@
+HAVE_FDATASYNC = @HAVE_FDATASYNC@
+HAVE_FEATURES_H = @HAVE_FEATURES_H@
+HAVE_FFS = @HAVE_FFS@
+HAVE_FFSL = @HAVE_FFSL@
+HAVE_FFSLL = @HAVE_FFSLL@
+HAVE_FREELOCALE = @HAVE_FREELOCALE@
+HAVE_FSEEKO = @HAVE_FSEEKO@
+HAVE_FSTATAT = @HAVE_FSTATAT@
+HAVE_FSYNC = @HAVE_FSYNC@
+HAVE_FTELLO = @HAVE_FTELLO@
+HAVE_FTRUNCATE = @HAVE_FTRUNCATE@
+HAVE_FUTIMENS = @HAVE_FUTIMENS@
+HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@
+HAVE_GETENTROPY = @HAVE_GETENTROPY@
+HAVE_GETGROUPS = @HAVE_GETGROUPS@
+HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@
+HAVE_GETLOGIN = @HAVE_GETLOGIN@
+HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@
+HAVE_GETPASS = @HAVE_GETPASS@
+HAVE_GETSUBOPT = @HAVE_GETSUBOPT@
+HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@
+HAVE_GETUMASK = @HAVE_GETUMASK@
+HAVE_GRANTPT = @HAVE_GRANTPT@
+HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@
+HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@
+HAVE_INITSTATE = @HAVE_INITSTATE@
+HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISBLANK = @HAVE_ISBLANK@
+HAVE_LANGINFO_ALTMON = @HAVE_LANGINFO_ALTMON@
+HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@
+HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@
+HAVE_LANGINFO_H = @HAVE_LANGINFO_H@
+HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@
+HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@
+HAVE_LCHMOD = @HAVE_LCHMOD@
+HAVE_LCHOWN = @HAVE_LCHOWN@
+HAVE_LIBCRYPTO = @HAVE_LIBCRYPTO@
+HAVE_LIBDL = @HAVE_LIBDL@
+HAVE_LIBEV = @HAVE_LIBEV@
+HAVE_LIBPTHREAD = @HAVE_LIBPTHREAD@
+HAVE_LIBRT = @HAVE_LIBRT@
+HAVE_LIBSECCOMP = @HAVE_LIBSECCOMP@
+HAVE_LIBZ = @HAVE_LIBZ@
+HAVE_LINK = @HAVE_LINK@
+HAVE_LINKAT = @HAVE_LINKAT@
+HAVE_LSTAT = @HAVE_LSTAT@
+HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@
+HAVE_MBRLEN = @HAVE_MBRLEN@
+HAVE_MBRTOWC = @HAVE_MBRTOWC@
+HAVE_MBSINIT = @HAVE_MBSINIT@
+HAVE_MBSLEN = @HAVE_MBSLEN@
+HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@
+HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@
+HAVE_MBTOWC = @HAVE_MBTOWC@
+HAVE_MEMPCPY = @HAVE_MEMPCPY@
+HAVE_MKDIRAT = @HAVE_MKDIRAT@
+HAVE_MKDTEMP = @HAVE_MKDTEMP@
+HAVE_MKFIFO = @HAVE_MKFIFO@
+HAVE_MKFIFOAT = @HAVE_MKFIFOAT@
+HAVE_MKNOD = @HAVE_MKNOD@
+HAVE_MKNODAT = @HAVE_MKNODAT@
+HAVE_MKOSTEMP = @HAVE_MKOSTEMP@
+HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@
+HAVE_MKSTEMP = @HAVE_MKSTEMP@
+HAVE_MKSTEMPS = @HAVE_MKSTEMPS@
+HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@
+HAVE_NANOSLEEP = @HAVE_NANOSLEEP@
+HAVE_NETDB_H = @HAVE_NETDB_H@
+HAVE_NETINET_IN_H = @HAVE_NETINET_IN_H@
+HAVE_NEWLOCALE = @HAVE_NEWLOCALE@
+HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@
+HAVE_OPENAT = @HAVE_OPENAT@
+HAVE_OS_H = @HAVE_OS_H@
+HAVE_PCLOSE = @HAVE_PCLOSE@
+HAVE_PIPE = @HAVE_PIPE@
+HAVE_PIPE2 = @HAVE_PIPE2@
+HAVE_POPEN = @HAVE_POPEN@
+HAVE_POSIX_MEMALIGN = @HAVE_POSIX_MEMALIGN@
+HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@
+HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@
+HAVE_PREAD = @HAVE_PREAD@
+HAVE_PSELECT = @HAVE_PSELECT@
+HAVE_PTHREAD_ATTR_DESTROY = @HAVE_PTHREAD_ATTR_DESTROY@
+HAVE_PTHREAD_ATTR_GETDETACHSTATE = @HAVE_PTHREAD_ATTR_GETDETACHSTATE@
+HAVE_PTHREAD_ATTR_INIT = @HAVE_PTHREAD_ATTR_INIT@
+HAVE_PTHREAD_ATTR_SETDETACHSTATE = @HAVE_PTHREAD_ATTR_SETDETACHSTATE@
+HAVE_PTHREAD_CONDATTR_DESTROY = @HAVE_PTHREAD_CONDATTR_DESTROY@
+HAVE_PTHREAD_CONDATTR_INIT = @HAVE_PTHREAD_CONDATTR_INIT@
+HAVE_PTHREAD_COND_BROADCAST = @HAVE_PTHREAD_COND_BROADCAST@
+HAVE_PTHREAD_COND_DESTROY = @HAVE_PTHREAD_COND_DESTROY@
+HAVE_PTHREAD_COND_INIT = @HAVE_PTHREAD_COND_INIT@
+HAVE_PTHREAD_COND_SIGNAL = @HAVE_PTHREAD_COND_SIGNAL@
+HAVE_PTHREAD_COND_TIMEDWAIT = @HAVE_PTHREAD_COND_TIMEDWAIT@
+HAVE_PTHREAD_COND_WAIT = @HAVE_PTHREAD_COND_WAIT@
+HAVE_PTHREAD_CREATE = @HAVE_PTHREAD_CREATE@
+HAVE_PTHREAD_CREATE_DETACHED = @HAVE_PTHREAD_CREATE_DETACHED@
+HAVE_PTHREAD_DETACH = @HAVE_PTHREAD_DETACH@
+HAVE_PTHREAD_EQUAL = @HAVE_PTHREAD_EQUAL@
+HAVE_PTHREAD_EXIT = @HAVE_PTHREAD_EXIT@
+HAVE_PTHREAD_GETSPECIFIC = @HAVE_PTHREAD_GETSPECIFIC@
+HAVE_PTHREAD_H = @HAVE_PTHREAD_H@
+HAVE_PTHREAD_JOIN = @HAVE_PTHREAD_JOIN@
+HAVE_PTHREAD_KEY_CREATE = @HAVE_PTHREAD_KEY_CREATE@
+HAVE_PTHREAD_KEY_DELETE = @HAVE_PTHREAD_KEY_DELETE@
+HAVE_PTHREAD_MUTEXATTR_DESTROY = @HAVE_PTHREAD_MUTEXATTR_DESTROY@
+HAVE_PTHREAD_MUTEXATTR_GETROBUST = @HAVE_PTHREAD_MUTEXATTR_GETROBUST@
+HAVE_PTHREAD_MUTEXATTR_GETTYPE = @HAVE_PTHREAD_MUTEXATTR_GETTYPE@
+HAVE_PTHREAD_MUTEXATTR_INIT = @HAVE_PTHREAD_MUTEXATTR_INIT@
+HAVE_PTHREAD_MUTEXATTR_SETROBUST = @HAVE_PTHREAD_MUTEXATTR_SETROBUST@
+HAVE_PTHREAD_MUTEXATTR_SETTYPE = @HAVE_PTHREAD_MUTEXATTR_SETTYPE@
+HAVE_PTHREAD_MUTEX_DESTROY = @HAVE_PTHREAD_MUTEX_DESTROY@
+HAVE_PTHREAD_MUTEX_INIT = @HAVE_PTHREAD_MUTEX_INIT@
+HAVE_PTHREAD_MUTEX_LOCK = @HAVE_PTHREAD_MUTEX_LOCK@
+HAVE_PTHREAD_MUTEX_RECURSIVE = @HAVE_PTHREAD_MUTEX_RECURSIVE@
+HAVE_PTHREAD_MUTEX_ROBUST = @HAVE_PTHREAD_MUTEX_ROBUST@
+HAVE_PTHREAD_MUTEX_TIMEDLOCK = @HAVE_PTHREAD_MUTEX_TIMEDLOCK@
+HAVE_PTHREAD_MUTEX_TRYLOCK = @HAVE_PTHREAD_MUTEX_TRYLOCK@
+HAVE_PTHREAD_MUTEX_UNLOCK = @HAVE_PTHREAD_MUTEX_UNLOCK@
+HAVE_PTHREAD_ONCE = @HAVE_PTHREAD_ONCE@
+HAVE_PTHREAD_PROCESS_SHARED = @HAVE_PTHREAD_PROCESS_SHARED@
+HAVE_PTHREAD_RWLOCKATTR_DESTROY = @HAVE_PTHREAD_RWLOCKATTR_DESTROY@
+HAVE_PTHREAD_RWLOCKATTR_INIT = @HAVE_PTHREAD_RWLOCKATTR_INIT@
+HAVE_PTHREAD_RWLOCK_DESTROY = @HAVE_PTHREAD_RWLOCK_DESTROY@
+HAVE_PTHREAD_RWLOCK_INIT = @HAVE_PTHREAD_RWLOCK_INIT@
+HAVE_PTHREAD_RWLOCK_RDLOCK = @HAVE_PTHREAD_RWLOCK_RDLOCK@
+HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK@
+HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK@
+HAVE_PTHREAD_RWLOCK_TRYRDLOCK = @HAVE_PTHREAD_RWLOCK_TRYRDLOCK@
+HAVE_PTHREAD_RWLOCK_TRYWRLOCK = @HAVE_PTHREAD_RWLOCK_TRYWRLOCK@
+HAVE_PTHREAD_RWLOCK_UNLOCK = @HAVE_PTHREAD_RWLOCK_UNLOCK@
+HAVE_PTHREAD_RWLOCK_WRLOCK = @HAVE_PTHREAD_RWLOCK_WRLOCK@
+HAVE_PTHREAD_SELF = @HAVE_PTHREAD_SELF@
+HAVE_PTHREAD_SETSPECIFIC = @HAVE_PTHREAD_SETSPECIFIC@
+HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@
+HAVE_PTHREAD_SPINLOCK_T = @HAVE_PTHREAD_SPINLOCK_T@
+HAVE_PTHREAD_SPIN_DESTROY = @HAVE_PTHREAD_SPIN_DESTROY@
+HAVE_PTHREAD_SPIN_INIT = @HAVE_PTHREAD_SPIN_INIT@
+HAVE_PTHREAD_SPIN_LOCK = @HAVE_PTHREAD_SPIN_LOCK@
+HAVE_PTHREAD_SPIN_TRYLOCK = @HAVE_PTHREAD_SPIN_TRYLOCK@
+HAVE_PTHREAD_SPIN_UNLOCK = @HAVE_PTHREAD_SPIN_UNLOCK@
+HAVE_PTHREAD_T = @HAVE_PTHREAD_T@
+HAVE_PTSNAME = @HAVE_PTSNAME@
+HAVE_PTSNAME_R = @HAVE_PTSNAME_R@
+HAVE_PWRITE = @HAVE_PWRITE@
+HAVE_QSORT_R = @HAVE_QSORT_R@
+HAVE_RAISE = @HAVE_RAISE@
+HAVE_RANDOM = @HAVE_RANDOM@
+HAVE_RANDOM_H = @HAVE_RANDOM_H@
+HAVE_RANDOM_R = @HAVE_RANDOM_R@
+HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@
+HAVE_READLINK = @HAVE_READLINK@
+HAVE_READLINKAT = @HAVE_READLINKAT@
+HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@
+HAVE_REALPATH = @HAVE_REALPATH@
+HAVE_RENAMEAT = @HAVE_RENAMEAT@
+HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@
+HAVE_SCHED_H = @HAVE_SCHED_H@
+HAVE_SCHED_YIELD = @HAVE_SCHED_YIELD@
+HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@
+HAVE_SETENV = @HAVE_SETENV@
+HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@
+HAVE_SETSTATE = @HAVE_SETSTATE@
+HAVE_SIGABBREV_NP = @HAVE_SIGABBREV_NP@
+HAVE_SIGACTION = @HAVE_SIGACTION@
+HAVE_SIGDESCR_NP = @HAVE_SIGDESCR_NP@
+HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@
+HAVE_SIGINFO_T = @HAVE_SIGINFO_T@
+HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
+HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
+HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
+HAVE_SIGSET_T = @HAVE_SIGSET_T@
+HAVE_SLEEP = @HAVE_SLEEP@
+HAVE_STDINT_H = @HAVE_STDINT_H@
+HAVE_STPCPY = @HAVE_STPCPY@
+HAVE_STPNCPY = @HAVE_STPNCPY@
+HAVE_STRCASECMP = @HAVE_STRCASECMP@
+HAVE_STRCASESTR = @HAVE_STRCASESTR@
+HAVE_STRCHRNUL = @HAVE_STRCHRNUL@
+HAVE_STRERRORNAME_NP = @HAVE_STRERRORNAME_NP@
+HAVE_STRINGS_H = @HAVE_STRINGS_H@
+HAVE_STRPBRK = @HAVE_STRPBRK@
+HAVE_STRPTIME = @HAVE_STRPTIME@
+HAVE_STRSEP = @HAVE_STRSEP@
+HAVE_STRTOD = @HAVE_STRTOD@
+HAVE_STRTOL = @HAVE_STRTOL@
+HAVE_STRTOLD = @HAVE_STRTOLD@
+HAVE_STRTOLL = @HAVE_STRTOLL@
+HAVE_STRTOUL = @HAVE_STRTOUL@
+HAVE_STRTOULL = @HAVE_STRTOULL@
+HAVE_STRUCT_ADDRINFO = @HAVE_STRUCT_ADDRINFO@
+HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@
+HAVE_STRUCT_SCHED_PARAM = @HAVE_STRUCT_SCHED_PARAM@
+HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@
+HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@
+HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@
+HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@
+HAVE_STRVERSCMP = @HAVE_STRVERSCMP@
+HAVE_SYMLINK = @HAVE_SYMLINK@
+HAVE_SYMLINKAT = @HAVE_SYMLINKAT@
+HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@
+HAVE_SYS_CDEFS_H = @HAVE_SYS_CDEFS_H@
+HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@
+HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@
+HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@
+HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@
+HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@
+HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@
+HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
+HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@
+HAVE_TIMEGM = @HAVE_TIMEGM@
+HAVE_TIMESPEC_GET = @HAVE_TIMESPEC_GET@
+HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@
+HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@
+HAVE_UNISTD_H = @HAVE_UNISTD_H@
+HAVE_UNLINKAT = @HAVE_UNLINKAT@
+HAVE_UNLOCKPT = @HAVE_UNLOCKPT@
+HAVE_USLEEP = @HAVE_USLEEP@
+HAVE_UTIMENSAT = @HAVE_UTIMENSAT@
+HAVE_VASPRINTF = @HAVE_VASPRINTF@
+HAVE_VDPRINTF = @HAVE_VDPRINTF@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+HAVE_WCHAR_H = @HAVE_WCHAR_H@
+HAVE_WCHAR_T = @HAVE_WCHAR_T@
+HAVE_WCPCPY = @HAVE_WCPCPY@
+HAVE_WCPNCPY = @HAVE_WCPNCPY@
+HAVE_WCRTOMB = @HAVE_WCRTOMB@
+HAVE_WCSCASECMP = @HAVE_WCSCASECMP@
+HAVE_WCSCAT = @HAVE_WCSCAT@
+HAVE_WCSCHR = @HAVE_WCSCHR@
+HAVE_WCSCMP = @HAVE_WCSCMP@
+HAVE_WCSCOLL = @HAVE_WCSCOLL@
+HAVE_WCSCPY = @HAVE_WCSCPY@
+HAVE_WCSCSPN = @HAVE_WCSCSPN@
+HAVE_WCSDUP = @HAVE_WCSDUP@
+HAVE_WCSFTIME = @HAVE_WCSFTIME@
+HAVE_WCSLEN = @HAVE_WCSLEN@
+HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@
+HAVE_WCSNCAT = @HAVE_WCSNCAT@
+HAVE_WCSNCMP = @HAVE_WCSNCMP@
+HAVE_WCSNCPY = @HAVE_WCSNCPY@
+HAVE_WCSNLEN = @HAVE_WCSNLEN@
+HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@
+HAVE_WCSPBRK = @HAVE_WCSPBRK@
+HAVE_WCSRCHR = @HAVE_WCSRCHR@
+HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@
+HAVE_WCSSPN = @HAVE_WCSSPN@
+HAVE_WCSSTR = @HAVE_WCSSTR@
+HAVE_WCSTOK = @HAVE_WCSTOK@
+HAVE_WCSWIDTH = @HAVE_WCSWIDTH@
+HAVE_WCSXFRM = @HAVE_WCSXFRM@
+HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@
+HAVE_WINT_T = @HAVE_WINT_T@
+HAVE_WMEMCHR = @HAVE_WMEMCHR@
+HAVE_WMEMCMP = @HAVE_WMEMCMP@
+HAVE_WMEMCPY = @HAVE_WMEMCPY@
+HAVE_WMEMMOVE = @HAVE_WMEMMOVE@
+HAVE_WMEMPCPY = @HAVE_WMEMPCPY@
+HAVE_WMEMSET = @HAVE_WMEMSET@
+HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@
+HAVE_XLOCALE_H = @HAVE_XLOCALE_H@
+HAVE__BOOL = @HAVE__BOOL@
+HAVE__EXIT = @HAVE__EXIT@
+HOGWEED_CFLAGS = @HOGWEED_CFLAGS@
+HOGWEED_LIBS = @HOGWEED_LIBS@
+HOSTENT_LIB = @HOSTENT_LIB@
+HTML_DIR = @HTML_DIR@
+INCLUDE_NEXT = @INCLUDE_NEXT@
+INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@
+INET_NTOP_LIB = @INET_NTOP_LIB@
+INET_PTON_LIB = @INET_PTON_LIB@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@
+INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LCOV = @LCOV@
+LD = @LD@
+LDDPOSTPROC = @LDDPOSTPROC@
+LDDPROG = @LDDPROG@
+LDFLAGS = @LDFLAGS@
+LIBATOMIC_LIBS = @LIBATOMIC_LIBS@
+LIBBROTLIDEC_CFLAGS = @LIBBROTLIDEC_CFLAGS@
+LIBBROTLIDEC_LIBS = @LIBBROTLIDEC_LIBS@
+LIBBROTLIENC_CFLAGS = @LIBBROTLIENC_CFLAGS@
+LIBBROTLIENC_LIBS = @LIBBROTLIENC_LIBS@
+LIBCRYPTO = @LIBCRYPTO@
+LIBCRYPTO_PREFIX = @LIBCRYPTO_PREFIX@
+LIBDL = @LIBDL@
+LIBDL_PREFIX = @LIBDL_PREFIX@
+LIBEV = @LIBEV@
+LIBEV_LIBS = @LIBEV_LIBS@
+LIBEV_PREFIX = @LIBEV_PREFIX@
+LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
+LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
+LIBICONV = @LIBICONV@
+LIBIDN2_CFLAGS = @LIBIDN2_CFLAGS@
+LIBIDN2_LIBS = @LIBIDN2_LIBS@
+LIBINTL = @LIBINTL@
+LIBKCAPI_CFLAGS = @LIBKCAPI_CFLAGS@
+LIBKCAPI_LIBS = @LIBKCAPI_LIBS@
+LIBMULTITHREAD = @LIBMULTITHREAD@
+LIBOBJS = @LIBOBJS@
+LIBPMULTITHREAD = @LIBPMULTITHREAD@
+LIBPTHREAD = @LIBPTHREAD@
+LIBPTHREAD_PREFIX = @LIBPTHREAD_PREFIX@
+LIBRT = @LIBRT@
+LIBRT_PREFIX = @LIBRT_PREFIX@
+LIBS = @LIBS@
+LIBSECCOMP = @LIBSECCOMP@
+LIBSECCOMP_PREFIX = @LIBSECCOMP_PREFIX@
+LIBSOCKET = @LIBSOCKET@
+LIBSTDTHREAD = @LIBSTDTHREAD@
+LIBTASN1_CFLAGS = @LIBTASN1_CFLAGS@
+LIBTASN1_LIBS = @LIBTASN1_LIBS@
+LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@
+LIBTHREAD = @LIBTHREAD@
+LIBTOOL = @LIBTOOL@
+LIBUNISTRING = @LIBUNISTRING@
+LIBUNISTRING_UNICTYPE_H = @LIBUNISTRING_UNICTYPE_H@
+LIBUNISTRING_UNINORM_H = @LIBUNISTRING_UNINORM_H@
+LIBUNISTRING_UNISTR_H = @LIBUNISTRING_UNISTR_H@
+LIBUNISTRING_UNITYPES_H = @LIBUNISTRING_UNITYPES_H@
+LIBZ = @LIBZ@
+LIBZSTD_CFLAGS = @LIBZSTD_CFLAGS@
+LIBZSTD_LIBS = @LIBZSTD_LIBS@
+LIBZ_PC = @LIBZ_PC@
+LIBZ_PREFIX = @LIBZ_PREFIX@
+LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@
+LIB_NANOSLEEP = @LIB_NANOSLEEP@
+LIB_PTHREAD = @LIB_PTHREAD@
+LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@
+LIB_SCHED_YIELD = @LIB_SCHED_YIELD@
+LIB_SELECT = @LIB_SELECT@
+LIB_SEMAPHORE = @LIB_SEMAPHORE@
+LIB_SETLOCALE = @LIB_SETLOCALE@
+LIB_SETLOCALE_NULL = @LIB_SETLOCALE_NULL@
+LIMITS_H = @LIMITS_H@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOCALENAME_ENHANCE_LOCALE_FUNCS = @LOCALENAME_ENHANCE_LOCALE_FUNCS@
+LOCALE_FR = @LOCALE_FR@
+LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@
+LOCALE_JA = @LOCALE_JA@
+LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@
+LOCALE_ZH_CN = @LOCALE_ZH_CN@
+LOG_VALGRIND = @LOG_VALGRIND@
+LTALLOCA = @LTALLOCA@
+LTLIBCRYPTO = @LTLIBCRYPTO@
+LTLIBDL = @LTLIBDL@
+LTLIBEV = @LTLIBEV@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBMULTITHREAD = @LTLIBMULTITHREAD@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBPTHREAD = @LTLIBPTHREAD@
+LTLIBRT = @LTLIBRT@
+LTLIBSECCOMP = @LTLIBSECCOMP@
+LTLIBTHREAD = @LTLIBTHREAD@
+LTLIBZ = @LTLIBZ@
+LT_AGE = @LT_AGE@
+LT_CURRENT = @LT_CURRENT@
+LT_DANE_AGE = @LT_DANE_AGE@
+LT_DANE_CURRENT = @LT_DANE_CURRENT@
+LT_DANE_REVISION = @LT_DANE_REVISION@
+LT_REVISION = @LT_REVISION@
+LT_SSL_AGE = @LT_SSL_AGE@
+LT_SSL_CURRENT = @LT_SSL_CURRENT@
+LT_SSL_REVISION = @LT_SSL_REVISION@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_XSSL_AGE = @LT_XSSL_AGE@
+LT_XSSL_CURRENT = @LT_XSSL_CURRENT@
+LT_XSSL_REVISION = @LT_XSSL_REVISION@
+MAINT = @MAINT@
+MAJOR_VERSION = @MAJOR_VERSION@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MINOR_VERSION = @MINOR_VERSION@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@
+NETINET_IN_H = @NETINET_IN_H@
+NETTLE_CFLAGS = @NETTLE_CFLAGS@
+NETTLE_LIBS = @NETTLE_LIBS@
+NEXT_ARPA_INET_H = @NEXT_ARPA_INET_H@
+NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H = @NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H@
+NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@
+NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@
+NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@
+NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@
+NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@
+NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@
+NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@
+NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@
+NEXT_AS_FIRST_DIRECTIVE_NETDB_H = @NEXT_AS_FIRST_DIRECTIVE_NETDB_H@
+NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H = @NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H@
+NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H = @NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H@
+NEXT_AS_FIRST_DIRECTIVE_SCHED_H = @NEXT_AS_FIRST_DIRECTIVE_SCHED_H@
+NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@
+NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@
+NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@
+NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@
+NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@
+NEXT_AS_FIRST_DIRECTIVE_STRINGS_H = @NEXT_AS_FIRST_DIRECTIVE_STRINGS_H@
+NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@
+NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@
+NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@
+NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@
+NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@
+NEXT_CTYPE_H = @NEXT_CTYPE_H@
+NEXT_ERRNO_H = @NEXT_ERRNO_H@
+NEXT_FCNTL_H = @NEXT_FCNTL_H@
+NEXT_FLOAT_H = @NEXT_FLOAT_H@
+NEXT_INTTYPES_H = @NEXT_INTTYPES_H@
+NEXT_LANGINFO_H = @NEXT_LANGINFO_H@
+NEXT_LIMITS_H = @NEXT_LIMITS_H@
+NEXT_LOCALE_H = @NEXT_LOCALE_H@
+NEXT_NETDB_H = @NEXT_NETDB_H@
+NEXT_NETINET_IN_H = @NEXT_NETINET_IN_H@
+NEXT_PTHREAD_H = @NEXT_PTHREAD_H@
+NEXT_SCHED_H = @NEXT_SCHED_H@
+NEXT_SIGNAL_H = @NEXT_SIGNAL_H@
+NEXT_STDDEF_H = @NEXT_STDDEF_H@
+NEXT_STDINT_H = @NEXT_STDINT_H@
+NEXT_STDIO_H = @NEXT_STDIO_H@
+NEXT_STDLIB_H = @NEXT_STDLIB_H@
+NEXT_STRINGS_H = @NEXT_STRINGS_H@
+NEXT_STRING_H = @NEXT_STRING_H@
+NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@
+NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@
+NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@
+NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@
+NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@
+NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@
+NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@
+NEXT_TIME_H = @NEXT_TIME_H@
+NEXT_UNISTD_H = @NEXT_UNISTD_H@
+NEXT_WCHAR_H = @NEXT_WCHAR_H@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NUMBER_VERSION = @NUMBER_VERSION@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+P11_KIT_CFLAGS = @P11_KIT_CFLAGS@
+P11_KIT_LIBS = @P11_KIT_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PARSE_DATETIME_BISON = @PARSE_DATETIME_BISON@
+PATCH_VERSION = @PATCH_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKCS12_ITER_COUNT = @PKCS12_ITER_COUNT@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PMCCABE = @PMCCABE@
+POSUB = @POSUB@
+PRAGMA_COLUMNS = @PRAGMA_COLUMNS@
+PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@
+PRIPTR_PREFIX = @PRIPTR_PREFIX@
+PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@
+PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+REPLACE_ACCESS = @REPLACE_ACCESS@
+REPLACE_ALIGNED_ALLOC = @REPLACE_ALIGNED_ALLOC@
+REPLACE_BTOWC = @REPLACE_BTOWC@
+REPLACE_CALLOC = @REPLACE_CALLOC@
+REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@
+REPLACE_CHOWN = @REPLACE_CHOWN@
+REPLACE_CLOSE = @REPLACE_CLOSE@
+REPLACE_CREAT = @REPLACE_CREAT@
+REPLACE_CTIME = @REPLACE_CTIME@
+REPLACE_DPRINTF = @REPLACE_DPRINTF@
+REPLACE_DUP = @REPLACE_DUP@
+REPLACE_DUP2 = @REPLACE_DUP2@
+REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@
+REPLACE_EXECL = @REPLACE_EXECL@
+REPLACE_EXECLE = @REPLACE_EXECLE@
+REPLACE_EXECLP = @REPLACE_EXECLP@
+REPLACE_EXECV = @REPLACE_EXECV@
+REPLACE_EXECVE = @REPLACE_EXECVE@
+REPLACE_EXECVP = @REPLACE_EXECVP@
+REPLACE_EXECVPE = @REPLACE_EXECVPE@
+REPLACE_FACCESSAT = @REPLACE_FACCESSAT@
+REPLACE_FCHMODAT = @REPLACE_FCHMODAT@
+REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@
+REPLACE_FCLOSE = @REPLACE_FCLOSE@
+REPLACE_FCNTL = @REPLACE_FCNTL@
+REPLACE_FDOPEN = @REPLACE_FDOPEN@
+REPLACE_FFLUSH = @REPLACE_FFLUSH@
+REPLACE_FFSLL = @REPLACE_FFSLL@
+REPLACE_FOPEN = @REPLACE_FOPEN@
+REPLACE_FPRINTF = @REPLACE_FPRINTF@
+REPLACE_FPURGE = @REPLACE_FPURGE@
+REPLACE_FREE = @REPLACE_FREE@
+REPLACE_FREELOCALE = @REPLACE_FREELOCALE@
+REPLACE_FREOPEN = @REPLACE_FREOPEN@
+REPLACE_FSEEK = @REPLACE_FSEEK@
+REPLACE_FSEEKO = @REPLACE_FSEEKO@
+REPLACE_FSTAT = @REPLACE_FSTAT@
+REPLACE_FSTATAT = @REPLACE_FSTATAT@
+REPLACE_FTELL = @REPLACE_FTELL@
+REPLACE_FTELLO = @REPLACE_FTELLO@
+REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@
+REPLACE_FUTIMENS = @REPLACE_FUTIMENS@
+REPLACE_GAI_STRERROR = @REPLACE_GAI_STRERROR@
+REPLACE_GETADDRINFO = @REPLACE_GETADDRINFO@
+REPLACE_GETCWD = @REPLACE_GETCWD@
+REPLACE_GETDELIM = @REPLACE_GETDELIM@
+REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@
+REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@
+REPLACE_GETGROUPS = @REPLACE_GETGROUPS@
+REPLACE_GETLINE = @REPLACE_GETLINE@
+REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@
+REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@
+REPLACE_GETPASS = @REPLACE_GETPASS@
+REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
+REPLACE_GMTIME = @REPLACE_GMTIME@
+REPLACE_INET_NTOP = @REPLACE_INET_NTOP@
+REPLACE_INET_PTON = @REPLACE_INET_PTON@
+REPLACE_INITSTATE = @REPLACE_INITSTATE@
+REPLACE_IOCTL = @REPLACE_IOCTL@
+REPLACE_ISATTY = @REPLACE_ISATTY@
+REPLACE_ITOLD = @REPLACE_ITOLD@
+REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LINK = @REPLACE_LINK@
+REPLACE_LINKAT = @REPLACE_LINKAT@
+REPLACE_LOCALECONV = @REPLACE_LOCALECONV@
+REPLACE_LOCALTIME = @REPLACE_LOCALTIME@
+REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@
+REPLACE_LSEEK = @REPLACE_LSEEK@
+REPLACE_LSTAT = @REPLACE_LSTAT@
+REPLACE_MALLOC = @REPLACE_MALLOC@
+REPLACE_MBRLEN = @REPLACE_MBRLEN@
+REPLACE_MBRTOWC = @REPLACE_MBRTOWC@
+REPLACE_MBSINIT = @REPLACE_MBSINIT@
+REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@
+REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@
+REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@
+REPLACE_MBTOWC = @REPLACE_MBTOWC@
+REPLACE_MEMCHR = @REPLACE_MEMCHR@
+REPLACE_MEMMEM = @REPLACE_MEMMEM@
+REPLACE_MKDIR = @REPLACE_MKDIR@
+REPLACE_MKFIFO = @REPLACE_MKFIFO@
+REPLACE_MKFIFOAT = @REPLACE_MKFIFOAT@
+REPLACE_MKNOD = @REPLACE_MKNOD@
+REPLACE_MKNODAT = @REPLACE_MKNODAT@
+REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
+REPLACE_MKTIME = @REPLACE_MKTIME@
+REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@
+REPLACE_NEWLOCALE = @REPLACE_NEWLOCALE@
+REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@
+REPLACE_NULL = @REPLACE_NULL@
+REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@
+REPLACE_OPEN = @REPLACE_OPEN@
+REPLACE_OPENAT = @REPLACE_OPENAT@
+REPLACE_PERROR = @REPLACE_PERROR@
+REPLACE_POPEN = @REPLACE_POPEN@
+REPLACE_POSIX_MEMALIGN = @REPLACE_POSIX_MEMALIGN@
+REPLACE_PREAD = @REPLACE_PREAD@
+REPLACE_PRINTF = @REPLACE_PRINTF@
+REPLACE_PSELECT = @REPLACE_PSELECT@
+REPLACE_PTHREAD_ATTR_DESTROY = @REPLACE_PTHREAD_ATTR_DESTROY@
+REPLACE_PTHREAD_ATTR_GETDETACHSTATE = @REPLACE_PTHREAD_ATTR_GETDETACHSTATE@
+REPLACE_PTHREAD_ATTR_INIT = @REPLACE_PTHREAD_ATTR_INIT@
+REPLACE_PTHREAD_ATTR_SETDETACHSTATE = @REPLACE_PTHREAD_ATTR_SETDETACHSTATE@
+REPLACE_PTHREAD_CONDATTR_DESTROY = @REPLACE_PTHREAD_CONDATTR_DESTROY@
+REPLACE_PTHREAD_CONDATTR_INIT = @REPLACE_PTHREAD_CONDATTR_INIT@
+REPLACE_PTHREAD_COND_BROADCAST = @REPLACE_PTHREAD_COND_BROADCAST@
+REPLACE_PTHREAD_COND_DESTROY = @REPLACE_PTHREAD_COND_DESTROY@
+REPLACE_PTHREAD_COND_INIT = @REPLACE_PTHREAD_COND_INIT@
+REPLACE_PTHREAD_COND_SIGNAL = @REPLACE_PTHREAD_COND_SIGNAL@
+REPLACE_PTHREAD_COND_TIMEDWAIT = @REPLACE_PTHREAD_COND_TIMEDWAIT@
+REPLACE_PTHREAD_COND_WAIT = @REPLACE_PTHREAD_COND_WAIT@
+REPLACE_PTHREAD_CREATE = @REPLACE_PTHREAD_CREATE@
+REPLACE_PTHREAD_DETACH = @REPLACE_PTHREAD_DETACH@
+REPLACE_PTHREAD_EQUAL = @REPLACE_PTHREAD_EQUAL@
+REPLACE_PTHREAD_EXIT = @REPLACE_PTHREAD_EXIT@
+REPLACE_PTHREAD_GETSPECIFIC = @REPLACE_PTHREAD_GETSPECIFIC@
+REPLACE_PTHREAD_JOIN = @REPLACE_PTHREAD_JOIN@
+REPLACE_PTHREAD_KEY_CREATE = @REPLACE_PTHREAD_KEY_CREATE@
+REPLACE_PTHREAD_KEY_DELETE = @REPLACE_PTHREAD_KEY_DELETE@
+REPLACE_PTHREAD_MUTEXATTR_DESTROY = @REPLACE_PTHREAD_MUTEXATTR_DESTROY@
+REPLACE_PTHREAD_MUTEXATTR_GETROBUST = @REPLACE_PTHREAD_MUTEXATTR_GETROBUST@
+REPLACE_PTHREAD_MUTEXATTR_GETTYPE = @REPLACE_PTHREAD_MUTEXATTR_GETTYPE@
+REPLACE_PTHREAD_MUTEXATTR_INIT = @REPLACE_PTHREAD_MUTEXATTR_INIT@
+REPLACE_PTHREAD_MUTEXATTR_SETROBUST = @REPLACE_PTHREAD_MUTEXATTR_SETROBUST@
+REPLACE_PTHREAD_MUTEXATTR_SETTYPE = @REPLACE_PTHREAD_MUTEXATTR_SETTYPE@
+REPLACE_PTHREAD_MUTEX_DESTROY = @REPLACE_PTHREAD_MUTEX_DESTROY@
+REPLACE_PTHREAD_MUTEX_INIT = @REPLACE_PTHREAD_MUTEX_INIT@
+REPLACE_PTHREAD_MUTEX_LOCK = @REPLACE_PTHREAD_MUTEX_LOCK@
+REPLACE_PTHREAD_MUTEX_TIMEDLOCK = @REPLACE_PTHREAD_MUTEX_TIMEDLOCK@
+REPLACE_PTHREAD_MUTEX_TRYLOCK = @REPLACE_PTHREAD_MUTEX_TRYLOCK@
+REPLACE_PTHREAD_MUTEX_UNLOCK = @REPLACE_PTHREAD_MUTEX_UNLOCK@
+REPLACE_PTHREAD_ONCE = @REPLACE_PTHREAD_ONCE@
+REPLACE_PTHREAD_RWLOCKATTR_DESTROY = @REPLACE_PTHREAD_RWLOCKATTR_DESTROY@
+REPLACE_PTHREAD_RWLOCKATTR_INIT = @REPLACE_PTHREAD_RWLOCKATTR_INIT@
+REPLACE_PTHREAD_RWLOCK_DESTROY = @REPLACE_PTHREAD_RWLOCK_DESTROY@
+REPLACE_PTHREAD_RWLOCK_INIT = @REPLACE_PTHREAD_RWLOCK_INIT@
+REPLACE_PTHREAD_RWLOCK_RDLOCK = @REPLACE_PTHREAD_RWLOCK_RDLOCK@
+REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK@
+REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK@
+REPLACE_PTHREAD_RWLOCK_TRYRDLOCK = @REPLACE_PTHREAD_RWLOCK_TRYRDLOCK@
+REPLACE_PTHREAD_RWLOCK_TRYWRLOCK = @REPLACE_PTHREAD_RWLOCK_TRYWRLOCK@
+REPLACE_PTHREAD_RWLOCK_UNLOCK = @REPLACE_PTHREAD_RWLOCK_UNLOCK@
+REPLACE_PTHREAD_RWLOCK_WRLOCK = @REPLACE_PTHREAD_RWLOCK_WRLOCK@
+REPLACE_PTHREAD_SELF = @REPLACE_PTHREAD_SELF@
+REPLACE_PTHREAD_SETSPECIFIC = @REPLACE_PTHREAD_SETSPECIFIC@
+REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@
+REPLACE_PTHREAD_SPIN_DESTROY = @REPLACE_PTHREAD_SPIN_DESTROY@
+REPLACE_PTHREAD_SPIN_INIT = @REPLACE_PTHREAD_SPIN_INIT@
+REPLACE_PTHREAD_SPIN_LOCK = @REPLACE_PTHREAD_SPIN_LOCK@
+REPLACE_PTHREAD_SPIN_TRYLOCK = @REPLACE_PTHREAD_SPIN_TRYLOCK@
+REPLACE_PTHREAD_SPIN_UNLOCK = @REPLACE_PTHREAD_SPIN_UNLOCK@
+REPLACE_PTSNAME = @REPLACE_PTSNAME@
+REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@
+REPLACE_PUTENV = @REPLACE_PUTENV@
+REPLACE_PWRITE = @REPLACE_PWRITE@
+REPLACE_QSORT_R = @REPLACE_QSORT_R@
+REPLACE_RAISE = @REPLACE_RAISE@
+REPLACE_RANDOM = @REPLACE_RANDOM@
+REPLACE_RANDOM_R = @REPLACE_RANDOM_R@
+REPLACE_READ = @REPLACE_READ@
+REPLACE_READLINK = @REPLACE_READLINK@
+REPLACE_READLINKAT = @REPLACE_READLINKAT@
+REPLACE_REALLOC = @REPLACE_REALLOC@
+REPLACE_REALLOCARRAY = @REPLACE_REALLOCARRAY@
+REPLACE_REALPATH = @REPLACE_REALPATH@
+REPLACE_REMOVE = @REPLACE_REMOVE@
+REPLACE_RENAME = @REPLACE_RENAME@
+REPLACE_RENAMEAT = @REPLACE_RENAMEAT@
+REPLACE_RMDIR = @REPLACE_RMDIR@
+REPLACE_SCHED_YIELD = @REPLACE_SCHED_YIELD@
+REPLACE_SELECT = @REPLACE_SELECT@
+REPLACE_SETENV = @REPLACE_SETENV@
+REPLACE_SETLOCALE = @REPLACE_SETLOCALE@
+REPLACE_SETSTATE = @REPLACE_SETSTATE@
+REPLACE_SLEEP = @REPLACE_SLEEP@
+REPLACE_SNPRINTF = @REPLACE_SNPRINTF@
+REPLACE_SPRINTF = @REPLACE_SPRINTF@
+REPLACE_STAT = @REPLACE_STAT@
+REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@
+REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@
+REPLACE_STPNCPY = @REPLACE_STPNCPY@
+REPLACE_STRCASESTR = @REPLACE_STRCASESTR@
+REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@
+REPLACE_STRDUP = @REPLACE_STRDUP@
+REPLACE_STRERROR = @REPLACE_STRERROR@
+REPLACE_STRERRORNAME_NP = @REPLACE_STRERRORNAME_NP@
+REPLACE_STRERROR_R = @REPLACE_STRERROR_R@
+REPLACE_STRFTIME = @REPLACE_STRFTIME@
+REPLACE_STRNCAT = @REPLACE_STRNCAT@
+REPLACE_STRNDUP = @REPLACE_STRNDUP@
+REPLACE_STRNLEN = @REPLACE_STRNLEN@
+REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@
+REPLACE_STRSTR = @REPLACE_STRSTR@
+REPLACE_STRTOD = @REPLACE_STRTOD@
+REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@
+REPLACE_STRTOK_R = @REPLACE_STRTOK_R@
+REPLACE_STRTOL = @REPLACE_STRTOL@
+REPLACE_STRTOLD = @REPLACE_STRTOLD@
+REPLACE_STRTOLL = @REPLACE_STRTOLL@
+REPLACE_STRTOUL = @REPLACE_STRTOUL@
+REPLACE_STRTOULL = @REPLACE_STRTOULL@
+REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@
+REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@
+REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@
+REPLACE_SYMLINK = @REPLACE_SYMLINK@
+REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@
+REPLACE_TIMEGM = @REPLACE_TIMEGM@
+REPLACE_TMPFILE = @REPLACE_TMPFILE@
+REPLACE_TRUNCATE = @REPLACE_TRUNCATE@
+REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@
+REPLACE_TZSET = @REPLACE_TZSET@
+REPLACE_UNLINK = @REPLACE_UNLINK@
+REPLACE_UNLINKAT = @REPLACE_UNLINKAT@
+REPLACE_UNSETENV = @REPLACE_UNSETENV@
+REPLACE_USLEEP = @REPLACE_USLEEP@
+REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@
+REPLACE_VASPRINTF = @REPLACE_VASPRINTF@
+REPLACE_VDPRINTF = @REPLACE_VDPRINTF@
+REPLACE_VFPRINTF = @REPLACE_VFPRINTF@
+REPLACE_VPRINTF = @REPLACE_VPRINTF@
+REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@
+REPLACE_VSPRINTF = @REPLACE_VSPRINTF@
+REPLACE_WCRTOMB = @REPLACE_WCRTOMB@
+REPLACE_WCSFTIME = @REPLACE_WCSFTIME@
+REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@
+REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@
+REPLACE_WCSTOK = @REPLACE_WCSTOK@
+REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@
+REPLACE_WCTOB = @REPLACE_WCTOB@
+REPLACE_WCTOMB = @REPLACE_WCTOMB@
+REPLACE_WCWIDTH = @REPLACE_WCWIDTH@
+REPLACE_WRITE = @REPLACE_WRITE@
+SED = @SED@
+SERVENT_LIB = @SERVENT_LIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
+SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
+STDALIGN_H = @STDALIGN_H@
+STDBOOL_H = @STDBOOL_H@
+STDDEF_H = @STDDEF_H@
+STDINT_H = @STDINT_H@
+STRIP = @STRIP@
+SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@
+SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@
+SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
+TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
+TIME_H_DEFINES_TIME_UTC = @TIME_H_DEFINES_TIME_UTC@
+TROUSERS_LIB = @TROUSERS_LIB@
+TSS2_CFLAGS = @TSS2_CFLAGS@
+TSS2_LIBS = @TSS2_LIBS@
+TSS_CFLAGS = @TSS_CFLAGS@
+TSS_LIBS = @TSS_LIBS@
+UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@
+UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@
+UNBOUND_CFLAGS = @UNBOUND_CFLAGS@
+UNBOUND_LIBS = @UNBOUND_LIBS@
+UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@
+UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@
+UNISTD_H_HAVE_SYS_RANDOM_H = @UNISTD_H_HAVE_SYS_RANDOM_H@
+UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@
+UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@
+USE_NLS = @USE_NLS@
+VALGRIND = @VALGRIND@
+VALGRINDFLAGS = @VALGRINDFLAGS@
+VALGRIND_PROGRAM = @VALGRIND_PROGRAM@
+VERSION = @VERSION@
+WARN_CFLAGS = @WARN_CFLAGS@
+WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
+WERROR_CFLAGS = @WERROR_CFLAGS@
+WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@
+WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@
+WINDOWS_STAT_INODES = @WINDOWS_STAT_INODES@
+WINDOWS_STAT_TIMESPEC = @WINDOWS_STAT_TIMESPEC@
+WINT_T_SUFFIX = @WINT_T_SUFFIX@
+WSTACK_CFLAGS = @WSTACK_CFLAGS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+YIELD_LIB = @YIELD_LIB@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_cv_sizeof_time_t = @ac_cv_sizeof_time_t@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+ggl_LIBOBJS = @ggl_LIBOBJS@
+ggl_LTLIBOBJS = @ggl_LTLIBOBJS@
+ggltests_LIBOBJS = @ggltests_LIBOBJS@
+ggltests_LTLIBOBJS = @ggltests_LTLIBOBJS@
+ggltests_WITNESS = @ggltests_WITNESS@
+gl_LIBOBJS = @gl_LIBOBJS@
+gl_LTLIBOBJS = @gl_LTLIBOBJS@
+gltests_LIBOBJS = @gltests_LIBOBJS@
+gltests_LTLIBOBJS = @gltests_LTLIBOBJS@
+gltests_WITNESS = @gltests_WITNESS@
+gnutls_so = @gnutls_so@
+guile_snarf = @guile_snarf@
+guileextensiondir = @guileextensiondir@
+guilesiteccachedir = @guilesiteccachedir@
+guilesitedir = @guilesitedir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+ifGNUmake = @ifGNUmake@
+ifnGNUmake = @ifnGNUmake@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+maybe_guileextensiondir = @maybe_guileextensiondir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+unistring_LIBOBJS = @unistring_LIBOBJS@
+unistring_LTLIBOBJS = @unistring_LTLIBOBJS@
+unistringtests_LIBOBJS = @unistringtests_LIBOBJS@
+unistringtests_LTLIBOBJS = @unistringtests_LTLIBOBJS@
+unistringtests_WITNESS = @unistringtests_WITNESS@
+AM_CFLAGS = $(WERROR_CFLAGS) $(WSTACK_CFLAGS) $(WARN_CFLAGS) $(NETTLE_CFLAGS) \
+ $(LIBTASN1_CFLAGS) $(LIBIDN2_CFLAGS) $(P11_KIT_CFLAGS) $(LIBZSTD_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS)
+
+COMMON_LINK_FLAGS = $(CODE_COVERAGE_LDFLAGS)
+V_GPERF = $(V_GPERF_@AM_V@)
+V_GPERF_ = $(V_GPERF_@AM_DEFAULT_V@)
+V_GPERF_0 = @echo " GPERF " $@;
+AM_CPPFLAGS = -I$(srcdir)/../../gl -I$(builddir)/../../gl \
+ -I$(srcdir)/../includes -I$(builddir)/../includes \
+ -I$(srcdir)/.. $(am__append_1)
+EXTRA_DIST = supported_exts.gperf
+BUILT_SOURCES = supported_exts.h
+noinst_LTLIBRARIES = libgnutls_x509.la
+libgnutls_x509_la_SOURCES = common.c key_encode.c common.h \
+ key_decode.c time.c crl.c crl_write.c crq.c dn.c attributes.c \
+ attributes.h prov-seed.c prov-seed.h extensions.c mpi.c \
+ output.c pkcs12.c pkcs12_bag.c pkcs12_encr.c pkcs7.c \
+ pkcs7-attrs.c pkcs7-crypt.c pkcs7_int.h privkey.c \
+ privkey_pkcs8.c privkey_pkcs8_pbes1.c privkey_openssl.c \
+ hostname-verify.c sign.c verify.c x509.c x509_dn.c x509_int.h \
+ x509_write.c name_constraints.c verify-high.c verify-high2.c \
+ verify-high.h x509_ext.c email-verify.c pkcs7-output.c \
+ virt-san.c virt-san.h spki.c x509_ext_int.h tls_features.c \
+ krb5.c krb5.h ip.c ip.h ip-in-cidr.h supported_exts.h ocsp.h \
+ $(am__append_2)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/lib/common.mk $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/x509/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/x509/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/lib/common.mk $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libgnutls_x509.la: $(libgnutls_x509_la_OBJECTS) $(libgnutls_x509_la_DEPENDENCIES) $(EXTRA_libgnutls_x509_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libgnutls_x509_la_OBJECTS) $(libgnutls_x509_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/attributes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crl_write.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crq.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/email-verify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extensions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname-verify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key_decode.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key_encode.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/krb5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/name_constraints.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ocsp_output.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs12.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs12_bag.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs12_encr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7-attrs.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7-crypt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7-output.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs7.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey_openssl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey_pkcs8.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privkey_pkcs8_pbes1.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prov-seed.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spki.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_features.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify-high.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify-high2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virt-san.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509_dn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509_ext.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x509_write.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/attributes.Plo
+ -rm -f ./$(DEPDIR)/common.Plo
+ -rm -f ./$(DEPDIR)/crl.Plo
+ -rm -f ./$(DEPDIR)/crl_write.Plo
+ -rm -f ./$(DEPDIR)/crq.Plo
+ -rm -f ./$(DEPDIR)/dn.Plo
+ -rm -f ./$(DEPDIR)/email-verify.Plo
+ -rm -f ./$(DEPDIR)/extensions.Plo
+ -rm -f ./$(DEPDIR)/hostname-verify.Plo
+ -rm -f ./$(DEPDIR)/ip.Plo
+ -rm -f ./$(DEPDIR)/key_decode.Plo
+ -rm -f ./$(DEPDIR)/key_encode.Plo
+ -rm -f ./$(DEPDIR)/krb5.Plo
+ -rm -f ./$(DEPDIR)/mpi.Plo
+ -rm -f ./$(DEPDIR)/name_constraints.Plo
+ -rm -f ./$(DEPDIR)/ocsp.Plo
+ -rm -f ./$(DEPDIR)/ocsp_output.Plo
+ -rm -f ./$(DEPDIR)/output.Plo
+ -rm -f ./$(DEPDIR)/pkcs12.Plo
+ -rm -f ./$(DEPDIR)/pkcs12_bag.Plo
+ -rm -f ./$(DEPDIR)/pkcs12_encr.Plo
+ -rm -f ./$(DEPDIR)/pkcs7-attrs.Plo
+ -rm -f ./$(DEPDIR)/pkcs7-crypt.Plo
+ -rm -f ./$(DEPDIR)/pkcs7-output.Plo
+ -rm -f ./$(DEPDIR)/pkcs7.Plo
+ -rm -f ./$(DEPDIR)/privkey.Plo
+ -rm -f ./$(DEPDIR)/privkey_openssl.Plo
+ -rm -f ./$(DEPDIR)/privkey_pkcs8.Plo
+ -rm -f ./$(DEPDIR)/privkey_pkcs8_pbes1.Plo
+ -rm -f ./$(DEPDIR)/prov-seed.Plo
+ -rm -f ./$(DEPDIR)/sign.Plo
+ -rm -f ./$(DEPDIR)/spki.Plo
+ -rm -f ./$(DEPDIR)/time.Plo
+ -rm -f ./$(DEPDIR)/tls_features.Plo
+ -rm -f ./$(DEPDIR)/verify-high.Plo
+ -rm -f ./$(DEPDIR)/verify-high2.Plo
+ -rm -f ./$(DEPDIR)/verify.Plo
+ -rm -f ./$(DEPDIR)/virt-san.Plo
+ -rm -f ./$(DEPDIR)/x509.Plo
+ -rm -f ./$(DEPDIR)/x509_dn.Plo
+ -rm -f ./$(DEPDIR)/x509_ext.Plo
+ -rm -f ./$(DEPDIR)/x509_write.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/attributes.Plo
+ -rm -f ./$(DEPDIR)/common.Plo
+ -rm -f ./$(DEPDIR)/crl.Plo
+ -rm -f ./$(DEPDIR)/crl_write.Plo
+ -rm -f ./$(DEPDIR)/crq.Plo
+ -rm -f ./$(DEPDIR)/dn.Plo
+ -rm -f ./$(DEPDIR)/email-verify.Plo
+ -rm -f ./$(DEPDIR)/extensions.Plo
+ -rm -f ./$(DEPDIR)/hostname-verify.Plo
+ -rm -f ./$(DEPDIR)/ip.Plo
+ -rm -f ./$(DEPDIR)/key_decode.Plo
+ -rm -f ./$(DEPDIR)/key_encode.Plo
+ -rm -f ./$(DEPDIR)/krb5.Plo
+ -rm -f ./$(DEPDIR)/mpi.Plo
+ -rm -f ./$(DEPDIR)/name_constraints.Plo
+ -rm -f ./$(DEPDIR)/ocsp.Plo
+ -rm -f ./$(DEPDIR)/ocsp_output.Plo
+ -rm -f ./$(DEPDIR)/output.Plo
+ -rm -f ./$(DEPDIR)/pkcs12.Plo
+ -rm -f ./$(DEPDIR)/pkcs12_bag.Plo
+ -rm -f ./$(DEPDIR)/pkcs12_encr.Plo
+ -rm -f ./$(DEPDIR)/pkcs7-attrs.Plo
+ -rm -f ./$(DEPDIR)/pkcs7-crypt.Plo
+ -rm -f ./$(DEPDIR)/pkcs7-output.Plo
+ -rm -f ./$(DEPDIR)/pkcs7.Plo
+ -rm -f ./$(DEPDIR)/privkey.Plo
+ -rm -f ./$(DEPDIR)/privkey_openssl.Plo
+ -rm -f ./$(DEPDIR)/privkey_pkcs8.Plo
+ -rm -f ./$(DEPDIR)/privkey_pkcs8_pbes1.Plo
+ -rm -f ./$(DEPDIR)/prov-seed.Plo
+ -rm -f ./$(DEPDIR)/sign.Plo
+ -rm -f ./$(DEPDIR)/spki.Plo
+ -rm -f ./$(DEPDIR)/time.Plo
+ -rm -f ./$(DEPDIR)/tls_features.Plo
+ -rm -f ./$(DEPDIR)/verify-high.Plo
+ -rm -f ./$(DEPDIR)/verify-high2.Plo
+ -rm -f ./$(DEPDIR)/verify.Plo
+ -rm -f ./$(DEPDIR)/virt-san.Plo
+ -rm -f ./$(DEPDIR)/x509.Plo
+ -rm -f ./$(DEPDIR)/x509_dn.Plo
+ -rm -f ./$(DEPDIR)/x509_ext.Plo
+ -rm -f ./$(DEPDIR)/x509_write.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: all check install install-am install-exec install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+supported_exts.h: $(srcdir)/supported_exts.gperf
+ $(V_GPERF)$(GPERF) --global-table -t $^ > $@-tmp \
+ && sed 's/^const struct supported_exts_st \*/static const struct supported_exts_st \*/' <$@-tmp >$@ \
+ && rm -f $@-tmp
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/x509/attributes.c b/lib/x509/attributes.c
new file mode 100644
index 0000000..3aab65b
--- /dev/null
+++ b/lib/x509/attributes.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include "x509_int.h"
+#include "attributes.h"
+
+/* Functions to parse and set the PKIX1 Attributes structure.
+ */
+
+/* Overwrite the given attribute (using the index)
+ * index here starts from one.
+ */
+static int
+overwrite_attribute(asn1_node asn, const char *root, unsigned indx,
+ const gnutls_datum_t * ext_data)
+{
+ char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
+ int result;
+
+ snprintf(name, sizeof(name), "%s.?%u", root, indx);
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".values.?LAST");
+
+ result = _gnutls_x509_write_value(asn, name2, ext_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+
+ return 0;
+}
+
+/* Parses an Attribute list in the asn1_struct, and searches for the
+ * given OID. The index indicates the attribute value to be returned.
+ *
+ * If raw==0 only printable data are returned, or
+ * GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE.
+ *
+ * asn1_attr_name must be a string in the form
+ * "certificationRequestInfo.attributes"
+ *
+ */
+int
+_x509_parse_attribute(asn1_node asn1_struct,
+ const char *attr_name, const char *given_oid, unsigned indx,
+ int raw, gnutls_datum_t * out)
+{
+ int k1, result;
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ char value[200];
+ gnutls_datum_t td;
+ char oid[MAX_OID_SIZE];
+ int len;
+
+ k1 = 0;
+ do {
+
+ k1++;
+ /* create a string like "attribute.?1"
+ */
+ if (attr_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ attr_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ }
+
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Move to the attribute type and values
+ */
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer1);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3), ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (strcmp(oid, given_oid) == 0) { /* Found the OID */
+
+ /* Read the Value
+ */
+ snprintf(tmpbuffer3, sizeof(tmpbuffer3),
+ "%s.values.?%u", tmpbuffer1, indx + 1);
+
+ len = sizeof(value) - 1;
+ result =
+ _gnutls_x509_read_value(asn1_struct,
+ tmpbuffer3, &td);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (raw == 0) {
+ result =
+ _gnutls_x509_dn_to_string
+ (oid, td.data, td.size, out);
+
+ _gnutls_free_datum(&td);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ return 0;
+ } else { /* raw!=0 */
+ out->data = td.data;
+ out->size = td.size;
+
+ return 0;
+ }
+ }
+
+ }
+ while (1);
+
+ gnutls_assert();
+
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ cleanup:
+ return result;
+}
+
+/* This function will attempt to set the requested attribute in
+ * the given X509v3 certificate.
+ *
+ * Critical will be either 0 or 1.
+ */
+static int
+add_attribute(asn1_node asn, const char *root, const char *attribute_id,
+ const gnutls_datum_t * ext_data)
+{
+ int result;
+ char name[MAX_NAME_SIZE];
+
+ snprintf(name, sizeof(name), "%s", root);
+
+ /* Add a new attribute in the list.
+ */
+ result = asn1_write_value(asn, name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.type", root);
+
+ result = asn1_write_value(asn, name, attribute_id, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values", root);
+
+ result = asn1_write_value(asn, name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values.?LAST", root);
+
+ result = _gnutls_x509_write_value(asn, name, ext_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+
+int
+_x509_set_attribute(asn1_node asn, const char *root,
+ const char *ext_id, const gnutls_datum_t * ext_data)
+{
+ int result;
+ int k, len;
+ char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
+ char extnID[MAX_OID_SIZE];
+
+ /* Find the index of the given attribute.
+ */
+ k = 0;
+ do {
+ k++;
+
+ snprintf(name, sizeof(name), "%s.?%d", root, k);
+
+ len = sizeof(extnID) - 1;
+ result = asn1_read_value(asn, name, extnID, &len);
+
+ /* move to next
+ */
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+
+ do {
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".type");
+
+ len = sizeof(extnID) - 1;
+ result = asn1_read_value(asn, name2, extnID, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Handle Extension
+ */
+ if (strcmp(extnID, ext_id) == 0) {
+ /* attribute was found
+ */
+ return overwrite_attribute(asn, root, k,
+ ext_data);
+ }
+
+
+ }
+ while (0);
+ }
+ while (1);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return add_attribute(asn, root, ext_id, ext_data);
+ } else {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+
+ return 0;
+}
diff --git a/lib/x509/attributes.h b/lib/x509/attributes.h
new file mode 100644
index 0000000..4ebb7dd
--- /dev/null
+++ b/lib/x509/attributes.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_ATTRIBUTES_H
+#define GNUTLS_LIB_X509_ATTRIBUTES_H
+
+int
+_x509_parse_attribute(asn1_node asn1_struct,
+ const char *attr_name, const char *given_oid, unsigned indx,
+ int raw, gnutls_datum_t * out);
+
+int
+_x509_set_attribute(asn1_node asn, const char *root,
+ const char *ext_id, const gnutls_datum_t * ext_data);
+
+#endif /* GNUTLS_LIB_X509_ATTRIBUTES_H */
diff --git a/lib/x509/common.c b/lib/x509/common.c
new file mode 100644
index 0000000..ca0b71c
--- /dev/null
+++ b/lib/x509/common.c
@@ -0,0 +1,1948 @@
+/*
+ * Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <str.h>
+#include <x509.h>
+#include <num.h>
+#include <x509_b64.h>
+#include <c-strcase.h>
+#include "x509_int.h"
+#include "extras/hex.h"
+#include <common.h>
+
+static int
+data2hex(const void *data, size_t data_size,
+ gnutls_datum_t *out);
+
+#define ENTRY(oid, ldap, asn, etype) {oid, sizeof(oid)-1, ldap, sizeof(ldap)-1, asn, etype}
+
+/* when there is no name description */
+#define ENTRY_ND(oid, asn, etype) {oid, sizeof(oid)-1, NULL, 0, asn, etype}
+
+/* This list contains all the OIDs that may be
+ * contained in a rdnSequence and are printable.
+ */
+static const struct oid_to_string _oid2str[] = {
+ /* PKIX
+ */
+ ENTRY("1.3.6.1.5.5.7.9.2", "placeOfBirth", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("1.3.6.1.5.5.7.9.3", "gender", NULL, ASN1_ETYPE_PRINTABLE_STRING),
+ ENTRY("1.3.6.1.5.5.7.9.4", "countryOfCitizenship", NULL,
+ ASN1_ETYPE_PRINTABLE_STRING),
+ ENTRY("1.3.6.1.5.5.7.9.5", "countryOfResidence", NULL,
+ ASN1_ETYPE_PRINTABLE_STRING),
+
+ ENTRY("2.5.4.6", "C", NULL, ASN1_ETYPE_PRINTABLE_STRING),
+ ENTRY("2.5.4.9", "street", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.12", "title", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.10", "O", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.11", "OU", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.3", "CN", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.7", "L", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.8", "ST", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.13", "description", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+
+ ENTRY("2.5.4.5", "serialNumber", NULL, ASN1_ETYPE_PRINTABLE_STRING),
+ ENTRY("2.5.4.20", "telephoneNumber", NULL, ASN1_ETYPE_PRINTABLE_STRING),
+ ENTRY("2.5.4.4", "surName", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.43", "initials", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.44", "generationQualifier", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.42", "givenName", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.65", "pseudonym", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.46", "dnQualifier", NULL, ASN1_ETYPE_PRINTABLE_STRING),
+ ENTRY("2.5.4.17", "postalCode", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.41", "name", "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("2.5.4.15", "businessCategory", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+
+ ENTRY("0.9.2342.19200300.100.1.25", "DC", NULL, ASN1_ETYPE_IA5_STRING),
+ ENTRY("0.9.2342.19200300.100.1.1", "UID", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+ ENTRY("1.2.840.113556.1.4.656", "userPrincipalName", "PKIX1.DirectoryString",
+ ASN1_ETYPE_INVALID),
+
+ /* Extended validation
+ */
+ ENTRY("1.3.6.1.4.1.311.60.2.1.1",
+ "jurisdictionOfIncorporationLocalityName",
+ "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("1.3.6.1.4.1.311.60.2.1.2",
+ "jurisdictionOfIncorporationStateOrProvinceName",
+ "PKIX1.DirectoryString", ASN1_ETYPE_INVALID),
+ ENTRY("1.3.6.1.4.1.311.60.2.1.3",
+ "jurisdictionOfIncorporationCountryName",
+ NULL, ASN1_ETYPE_PRINTABLE_STRING),
+
+ /* PKCS #9
+ */
+ ENTRY("1.2.840.113549.1.9.1", "EMAIL", NULL, ASN1_ETYPE_IA5_STRING),
+ ENTRY_ND("1.2.840.113549.1.9.7", "PKIX1.pkcs-9-challengePassword",
+ ASN1_ETYPE_INVALID),
+
+ /* friendly name */
+ ENTRY_ND("1.2.840.113549.1.9.20", NULL, ASN1_ETYPE_BMP_STRING),
+ /* local key id */
+ ENTRY_ND("1.2.840.113549.1.9.21", NULL, ASN1_ETYPE_OCTET_STRING),
+ ENTRY_ND("1.2.840.113549.1.9.4", NULL, ASN1_ETYPE_OCTET_STRING),
+
+ /* rfc3920 section 5.1.1 */
+ ENTRY("1.3.6.1.5.5.7.8.5", "XmppAddr", NULL, ASN1_ETYPE_UTF8_STRING),
+
+ /* Russian things: https://cdnimg.rg.ru/pril/66/91/91/23041_pril.pdf */
+ /* Main state registration number */
+ ENTRY("1.2.643.100.1", "OGRN", NULL, ASN1_ETYPE_NUMERIC_STRING),
+ /* Individual insurance account number */
+ ENTRY("1.2.643.100.3", "SNILS", NULL, ASN1_ETYPE_NUMERIC_STRING),
+ /* Main state registration number for individual enterpreneurs */
+ ENTRY("1.2.643.100.5", "OGRNIP", NULL, ASN1_ETYPE_NUMERIC_STRING),
+ /* VAT identification number */
+ ENTRY("1.2.643.3.131.1.1", "INN", NULL, ASN1_ETYPE_NUMERIC_STRING),
+
+ {NULL, 0, NULL, 0, NULL, 0}
+};
+
+const struct oid_to_string *_gnutls_oid_get_entry(const struct oid_to_string *ots, const char *oid)
+{
+ unsigned int i = 0;
+ unsigned len = strlen(oid);
+
+ do {
+ if (len == ots[i].oid_size &&
+ strcmp(ots[i].oid, oid) == 0)
+ return &ots[i];
+ i++;
+ }
+ while (ots[i].oid != NULL);
+
+ return NULL;
+}
+
+const char *_gnutls_oid_get_asn_desc(const char *oid)
+{
+ const struct oid_to_string *entry = _gnutls_oid_get_entry(_oid2str, oid);
+ return entry ? entry->asn_desc : NULL;
+}
+
+const char *_gnutls_ldap_string_to_oid(const char *str, unsigned str_len)
+{
+ unsigned int i = 0;
+
+ do {
+ if ((_oid2str[i].name_desc != NULL) &&
+ (str_len == _oid2str[i].name_desc_size) &&
+ (c_strncasecmp(_oid2str[i].name_desc, str, str_len) ==
+ 0))
+ return _oid2str[i].oid;
+ i++;
+ }
+ while (_oid2str[i].oid != NULL);
+
+ return NULL;
+}
+
+/* Escapes a string following the rules from RFC4514.
+ */
+static int str_escape(const gnutls_datum_t * str, gnutls_datum_t * escaped)
+{
+ unsigned int j, i;
+ uint8_t *buffer = NULL;
+ int ret;
+
+ if (str == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* the string will be at most twice the original */
+ buffer = gnutls_malloc(str->size * 2 + 2);
+ if (buffer == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ for (i = j = 0; i < str->size; i++) {
+ if (str->data[i] == 0) {
+ /* this is handled earlier */
+ ret = gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+ goto cleanup;
+ }
+
+ if (str->data[i] == ',' || str->data[i] == '+'
+ || str->data[i] == '"' || str->data[i] == '\\'
+ || str->data[i] == '<' || str->data[i] == '>'
+ || str->data[i] == ';' || str->data[i] == 0)
+ buffer[j++] = '\\';
+ else if (i == 0 && str->data[i] == '#')
+ buffer[j++] = '\\';
+ else if (i == 0 && str->data[i] == ' ')
+ buffer[j++] = '\\';
+ else if (i == (str->size - 1) && str->data[i] == ' ')
+ buffer[j++] = '\\';
+
+ buffer[j++] = str->data[i];
+ }
+
+ /* null terminate the string */
+ buffer[j] = 0;
+ escaped->data = buffer;
+ escaped->size = j;
+
+ return 0;
+ cleanup:
+ gnutls_free(buffer);
+ return ret;
+}
+
+/**
+ * gnutls_x509_dn_oid_known:
+ * @oid: holds an Object Identifier in a null terminated string
+ *
+ * This function will inform about known DN OIDs. This is useful since
+ * functions like gnutls_x509_crt_set_dn_by_oid() use the information
+ * on known OIDs to properly encode their input. Object Identifiers
+ * that are not known are not encoded by these functions, and their
+ * input is stored directly into the ASN.1 structure. In that case of
+ * unknown OIDs, you have the responsibility of DER encoding your
+ * data.
+ *
+ * Returns: 1 on known OIDs and 0 otherwise.
+ **/
+int gnutls_x509_dn_oid_known(const char *oid)
+{
+ return _gnutls_oid_get_entry(_oid2str, oid) != NULL;
+}
+
+/**
+ * gnutls_x509_dn_oid_name:
+ * @oid: holds an Object Identifier in a null terminated string
+ * @flags: 0 or GNUTLS_X509_DN_OID_*
+ *
+ * This function will return the name of a known DN OID. If
+ * %GNUTLS_X509_DN_OID_RETURN_OID is specified this function
+ * will return the given OID if no descriptive name has been
+ * found.
+ *
+ * Returns: A null terminated string or NULL otherwise.
+ *
+ * Since: 3.0
+ **/
+const char *gnutls_x509_dn_oid_name(const char *oid, unsigned int flags)
+{
+ const struct oid_to_string *entry =_gnutls_oid_get_entry(_oid2str, oid);
+
+ if (entry && entry->name_desc)
+ return entry->name_desc;
+ if (flags & GNUTLS_X509_DN_OID_RETURN_OID)
+ return oid;
+ else
+ return NULL;
+}
+
+static int
+make_printable_string(unsigned etype, const gnutls_datum_t * input,
+ gnutls_datum_t * out)
+{
+ int printable = 0;
+ int ret;
+
+ /* empty input strings result to a null string */
+ if (input->data == NULL || input->size == 0) {
+ out->data = gnutls_calloc(1, 1);
+ if (out->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ out->size = 0;
+ return 0;
+ }
+
+ if (etype == ASN1_ETYPE_BMP_STRING) {
+ ret = _gnutls_ucs2_to_utf8(input->data, input->size, out, 1);
+ if (ret < 0) {
+ /* could not convert. Handle it as non-printable */
+ printable = 0;
+ } else
+ printable = 1;
+ } else if (etype == ASN1_ETYPE_TELETEX_STRING) {
+ /* HACK: if the teletex string contains only ascii
+ * characters then treat it as printable.
+ */
+ if (_gnutls_str_is_print((char*)input->data, input->size)) {
+ out->data = gnutls_malloc(input->size + 1);
+ if (out->data == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(out->data, input->data, input->size);
+ out->size = input->size;
+
+ out->data[out->size] = 0;
+
+ printable = 1;
+ }
+ } else if (etype != ASN1_ETYPE_UNIVERSAL_STRING) /* supported but not printable */
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if (printable == 0) { /* need to allocate out */
+ ret = data2hex(input->data, input->size, out);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int
+decode_complex_string(const struct oid_to_string *oentry, void *value,
+ int value_size, gnutls_datum_t * out)
+{
+ char str[MAX_STRING_LEN], tmpname[128];
+ int len = -1, result;
+ asn1_node tmpasn = NULL;
+ char asn1_err[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = "";
+ unsigned int etype;
+ gnutls_datum_t td = {NULL, 0};
+
+ if (oentry->asn_desc == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(), oentry->asn_desc,
+ &tmpasn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if ((result =
+ _asn1_strict_der_decode(&tmpasn, value, value_size,
+ asn1_err)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ _gnutls_debug_log("_asn1_strict_der_decode: %s\n", asn1_err);
+ asn1_delete_structure(&tmpasn);
+ return _gnutls_asn2err(result);
+ }
+
+ /* Read the type of choice.
+ */
+ len = sizeof(str) - 1;
+ if ((result = asn1_read_value(tmpasn, "", str, &len)) != ASN1_SUCCESS) { /* CHOICE */
+ gnutls_assert();
+ asn1_delete_structure(&tmpasn);
+ return _gnutls_asn2err(result);
+ }
+
+ str[len] = 0;
+
+ /* We set the etype on the strings that may need
+ * some conversion to UTF-8. The INVALID flag indicates
+ * no conversion needed */
+ if (strcmp(str, "teletexString") == 0)
+ etype = ASN1_ETYPE_TELETEX_STRING;
+ else if (strcmp(str, "bmpString") == 0)
+ etype = ASN1_ETYPE_BMP_STRING;
+ else if (strcmp(str, "universalString") == 0)
+ etype = ASN1_ETYPE_UNIVERSAL_STRING;
+ else
+ etype = ASN1_ETYPE_INVALID;
+
+ _gnutls_str_cpy(tmpname, sizeof(tmpname), str);
+
+ result = _gnutls_x509_read_value(tmpasn, tmpname, &td);
+ asn1_delete_structure(&tmpasn);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ if (etype != ASN1_ETYPE_INVALID) {
+ result = make_printable_string(etype, &td, out);
+
+ _gnutls_free_datum(&td);
+
+ if (result < 0)
+ return gnutls_assert_val(result);
+ } else {
+ out->data = td.data;
+ out->size = td.size;
+ /* _gnutls_x509_read_value always null terminates */
+ }
+
+ assert(out->data != NULL);
+
+ /* Refuse to deal with strings containing NULs. */
+ if (strlen((void *) out->data) != (size_t) out->size) {
+ _gnutls_free_datum(out);
+ return gnutls_assert_val(GNUTLS_E_ASN1_EMBEDDED_NULL_IN_STRING);
+ }
+
+ return 0;
+}
+
+
+/* This function will convert an attribute value, specified by the OID,
+ * to a string. The result will be a null terminated string.
+ *
+ * res may be null. This will just return the res_size, needed to
+ * hold the string.
+ */
+int
+_gnutls_x509_dn_to_string(const char *oid, void *value,
+ int value_size, gnutls_datum_t * str)
+{
+ const struct oid_to_string *oentry;
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ if (value == NULL || value_size <= 0) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ oentry = _gnutls_oid_get_entry(_oid2str, oid);
+ if (oentry == NULL) { /* unknown OID -> hex */
+ unknown_oid:
+ ret = data2hex(value, value_size, str);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ return 0;
+ }
+
+ if (oentry->asn_desc != NULL) { /* complex */
+ ret =
+ decode_complex_string(oentry, value, value_size, &tmp);
+ if (ret < 0) {
+ /* we failed decoding -> handle it as unknown OID */
+ goto unknown_oid;
+ }
+ } else {
+ ret =
+ _gnutls_x509_decode_string(oentry->etype, value,
+ value_size, &tmp, 0);
+ if (ret < 0) {
+ /* we failed decoding -> handle it as unknown OID */
+ goto unknown_oid;
+ }
+ }
+
+ ret = str_escape(&tmp, str);
+ _gnutls_free_datum(&tmp);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+/* Converts a data string to an LDAP rfc2253 hex string
+ * something like '#01020304'
+ */
+static int
+data2hex(const void *data, size_t data_size,
+ gnutls_datum_t *out)
+{
+ gnutls_datum_t tmp, td;
+ int ret;
+ size_t size;
+
+ td.size = hex_str_size(data_size) + 1; /* +1 for '#' */
+ td.data = gnutls_malloc(td.size);
+ if (td.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ tmp.data = (void*)data;
+ tmp.size = data_size;
+
+ td.data[0] = '#';
+ size = td.size-1; /* don't include '#' */
+ ret =
+ gnutls_hex_encode(&tmp,
+ (char*)&td.data[1], &size);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(td.data);
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ td.size--; /* don't include null */
+
+ out->data = td.data;
+ out->size = td.size;
+
+ return 0;
+}
+
+gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type(char *str_type)
+{
+ if (strcmp(str_type, "dNSName") == 0)
+ return GNUTLS_SAN_DNSNAME;
+ if (strcmp(str_type, "rfc822Name") == 0)
+ return GNUTLS_SAN_RFC822NAME;
+ if (strcmp(str_type, "uniformResourceIdentifier") == 0)
+ return GNUTLS_SAN_URI;
+ if (strcmp(str_type, "iPAddress") == 0)
+ return GNUTLS_SAN_IPADDRESS;
+ if (strcmp(str_type, "otherName") == 0)
+ return GNUTLS_SAN_OTHERNAME;
+ if (strcmp(str_type, "directoryName") == 0)
+ return GNUTLS_SAN_DN;
+ if (strcmp(str_type, "registeredID") == 0)
+ return GNUTLS_SAN_REGISTERED_ID;
+
+ return (gnutls_x509_subject_alt_name_t) - 1;
+}
+
+/* A generic export function. Will export the given ASN.1 encoded data
+ * to PEM or DER raw data.
+ */
+int
+_gnutls_x509_export_int_named(asn1_node asn1_data, const char *name,
+ gnutls_x509_crt_fmt_t format,
+ const char *pem_header,
+ unsigned char *output_data,
+ size_t * output_data_size)
+{
+ int ret;
+ gnutls_datum_t out = {NULL,0};
+ size_t size;
+
+ ret = _gnutls_x509_export_int_named2(asn1_data, name,
+ format, pem_header, &out);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (format == GNUTLS_X509_FMT_PEM)
+ size = out.size + 1;
+ else
+ size = out.size;
+
+ if (*output_data_size < size) {
+ *output_data_size = size;
+ ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ goto cleanup;
+ }
+
+ *output_data_size = (size_t) out.size;
+ if (output_data) {
+ if (out.size > 0) {
+ memcpy(output_data, out.data, (size_t) out.size);
+ }
+ if (format == GNUTLS_X509_FMT_PEM)
+ output_data[out.size] = 0;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(out.data);
+
+ return ret;
+}
+
+/* A generic export function. Will export the given ASN.1 encoded data
+ * to PEM or DER raw data.
+ */
+int
+_gnutls_x509_export_int_named2(asn1_node asn1_data, const char *name,
+ gnutls_x509_crt_fmt_t format,
+ const char *pem_header,
+ gnutls_datum_t * out)
+{
+ int ret;
+
+ if (format == GNUTLS_X509_FMT_DER) {
+ ret = _gnutls_x509_der_encode(asn1_data, name, out, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ } else { /* PEM */
+ gnutls_datum_t tmp;
+
+ ret = _gnutls_x509_der_encode(asn1_data, name, &tmp, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_fbase64_encode(pem_header, tmp.data, tmp.size,
+ out);
+ _gnutls_free_datum(&tmp);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+/* Decodes an octet string. The etype specifies the string type.
+ * The returned string is always null terminated (but null is not
+ * included in size).
+ */
+int
+_gnutls_x509_decode_string(unsigned int etype,
+ const uint8_t * der, size_t der_size,
+ gnutls_datum_t * output, unsigned allow_ber)
+{
+ int ret;
+ uint8_t *str;
+ unsigned int str_size, len;
+ gnutls_datum_t td;
+
+ output->data = NULL;
+ output->size = 0;
+
+ if (allow_ber)
+ ret =
+ asn1_decode_simple_ber(etype, der, der_size, &str, &str_size, NULL);
+ else
+ ret =
+ asn1_decode_simple_der(etype, der, der_size, (const uint8_t**)&str, &str_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ return ret;
+ }
+
+ td.size = str_size;
+ td.data = gnutls_malloc(str_size + 1);
+ if (td.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ if (str_size > 0)
+ memcpy(td.data, str, str_size);
+ td.data[str_size] = 0;
+
+ if (allow_ber)
+ free(str);
+
+ ret = make_printable_string(etype, &td, output);
+ if (ret == GNUTLS_E_INVALID_REQUEST) { /* unsupported etype */
+ output->data = td.data;
+ output->size = td.size;
+ ret = 0;
+ } else if (ret <= 0) {
+ _gnutls_free_datum(&td);
+ }
+
+ /* Refuse to deal with strings containing NULs. */
+ if (etype != ASN1_ETYPE_OCTET_STRING) {
+ if (output->data)
+ len = strlen((void *) output->data);
+ else
+ len = 0;
+
+ if (len != (size_t) output->size) {
+ _gnutls_free_datum(output);
+ ret = gnutls_assert_val(GNUTLS_E_ASN1_EMBEDDED_NULL_IN_STRING);
+ }
+ }
+
+ return ret;
+}
+
+
+/* Reads a value from an ASN1 tree, and puts the output
+ * in an allocated variable in the given datum.
+ *
+ * Note that this function always allocates one plus
+ * the required data size (and places a null byte).
+ */
+static int
+x509_read_value(asn1_node c, const char *root,
+ gnutls_datum_t * ret, unsigned allow_null)
+{
+ int len = 0, result;
+ uint8_t *tmp = NULL;
+ unsigned int etype;
+
+ result = asn1_read_value_type(c, root, NULL, &len, &etype);
+ if (result == 0 && allow_null == 0 && len == 0) {
+ /* don't allow null strings */
+ return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+ } else if (result == 0 && allow_null == 0 && etype == ASN1_ETYPE_OBJECT_ID && len == 1) {
+ return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+ }
+
+ if (result != ASN1_MEM_ERROR) {
+ if (result != ASN1_SUCCESS || allow_null == 0 || len != 0) {
+ result = _gnutls_asn2err(result);
+ return result;
+ }
+ }
+
+ if (etype == ASN1_ETYPE_BIT_STRING) {
+ len = (len + 7) / 8;
+ }
+
+ tmp = gnutls_malloc((size_t) len + 1);
+ if (tmp == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ if (len > 0) {
+ result = asn1_read_value(c, root, tmp, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ switch (etype) {
+ case ASN1_ETYPE_BIT_STRING:
+ ret->size = (len + 7) / 8;
+ break;
+ case ASN1_ETYPE_OBJECT_ID:
+ if (len > 0) {
+ ret->size = len - 1;
+ } else {
+ result = gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+ goto cleanup;
+ }
+ break;
+ default:
+ ret->size = (unsigned) len;
+ break;
+ }
+ } else {
+ ret->size = 0;
+ }
+
+ tmp[ret->size] = 0;
+ ret->data = tmp;
+
+ return 0;
+
+ cleanup:
+ gnutls_free(tmp);
+ return result;
+}
+
+int
+_gnutls_x509_read_value(asn1_node c, const char *root,
+ gnutls_datum_t * ret)
+{
+ return x509_read_value(c, root, ret, 0);
+}
+
+int
+_gnutls_x509_read_null_value(asn1_node c, const char *root,
+ gnutls_datum_t * ret)
+{
+ return x509_read_value(c, root, ret, 1);
+}
+
+/* Reads a value from an ASN1 tree, then interprets it as the provided
+ * type of string and returns the output in an allocated variable.
+ *
+ * Note that this function always places a null character
+ * at the end of a readable string value (which is not accounted into size)
+ */
+int
+_gnutls_x509_read_string(asn1_node c, const char *root,
+ gnutls_datum_t * ret, unsigned int etype, unsigned int allow_ber)
+{
+ int len = 0, result;
+ size_t slen;
+ uint8_t *tmp = NULL;
+ unsigned rtype;
+
+ result = asn1_read_value_type(c, root, NULL, &len, &rtype);
+ if (result != ASN1_MEM_ERROR) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ return result;
+ }
+
+ if (rtype == ASN1_ETYPE_BIT_STRING)
+ len /= 8;
+
+ tmp = gnutls_malloc((size_t) len + 1);
+ if (tmp == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ result = asn1_read_value(c, root, tmp, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (rtype == ASN1_ETYPE_BIT_STRING)
+ len /= 8;
+
+ /* Extract the STRING.
+ */
+ slen = (size_t) len;
+
+ result = _gnutls_x509_decode_string(etype, tmp, slen, ret, allow_ber);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ gnutls_free(tmp);
+
+ return 0;
+
+ cleanup:
+ gnutls_free(tmp);
+ return result;
+}
+
+/* The string type should be IA5String, UTF8String etc. Leave
+ * null for octet string */
+int _gnutls_x509_encode_string(unsigned int etype,
+ const void *input_data, size_t input_size,
+ gnutls_datum_t * output)
+{
+ uint8_t tl[ASN1_MAX_TL_SIZE];
+ unsigned int tl_size;
+ int ret;
+
+ tl_size = sizeof(tl);
+ ret =
+ asn1_encode_simple_der(etype, input_data, input_size, tl,
+ &tl_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ return ret;
+ }
+
+ output->data = gnutls_malloc(tl_size + input_size);
+ if (output->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(output->data, tl, tl_size);
+ memcpy(output->data + tl_size, input_data, input_size);
+
+ output->size = tl_size + input_size;
+
+ return 0;
+}
+
+/* DER Encodes the src asn1_node and stores it to
+ * the given datum. If str is non zero then the data are encoded as
+ * an OCTET STRING.
+ */
+int
+_gnutls_x509_der_encode(asn1_node src, const char *src_name,
+ gnutls_datum_t * res, int str)
+{
+ int size, result;
+ int asize;
+ uint8_t *data = NULL;
+ asn1_node c2 = NULL;
+
+ size = 0;
+ result = asn1_der_coding(src, src_name, NULL, &size, NULL);
+ /* this check explicitly covers the case where size == 0 && result == 0 */
+ if (result != ASN1_MEM_ERROR) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* allocate data for the der
+ */
+
+ if (str)
+ size += 16; /* for later to include the octet tags */
+ asize = size;
+
+ data = gnutls_malloc((size_t) size);
+ if (data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = asn1_der_coding(src, src_name, data, &size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (str) {
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-7-Data",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "", data, size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_der_coding(c2, "", data, &asize, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ size = asize;
+
+ asn1_delete_structure(&c2);
+ }
+
+ res->data = data;
+ res->size = (unsigned) size;
+ return 0;
+
+ cleanup:
+ gnutls_free(data);
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/* DER Encodes the src asn1_node and stores it to
+ * dest in dest_name. Useful to encode something and store it
+ * as OCTET. If str is non null then the data are encoded as
+ * an OCTET STRING.
+ */
+int
+_gnutls_x509_der_encode_and_copy(asn1_node src, const char *src_name,
+ asn1_node dest, const char *dest_name,
+ int str)
+{
+ int result;
+ gnutls_datum_t encoded = {NULL, 0};
+
+ result = _gnutls_x509_der_encode(src, src_name, &encoded, str);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Write the data.
+ */
+ result =
+ asn1_write_value(dest, dest_name, encoded.data,
+ (int) encoded.size);
+
+ _gnutls_free_datum(&encoded);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/* Writes the value of the datum in the given asn1_node.
+ */
+int
+_gnutls_x509_write_value(asn1_node c, const char *root,
+ const gnutls_datum_t * data)
+{
+ int ret;
+
+ /* Write the data.
+ */
+ ret = asn1_write_value(c, root, data->data, data->size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+/* Writes the value of the datum in the given asn1_node as a string.
+ */
+int
+_gnutls_x509_write_string(asn1_node c, const char *root,
+ const gnutls_datum_t * data, unsigned int etype)
+{
+ int ret;
+ gnutls_datum_t val = { NULL, 0 };
+
+ ret =
+ _gnutls_x509_encode_string(etype, data->data, data->size,
+ &val);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Write the data.
+ */
+ ret = asn1_write_value(c, root, val.data, val.size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ _gnutls_free_datum(&val);
+ return ret;
+}
+
+void
+_asnstr_append_name(char *name, size_t name_size, const char *part1,
+ const char *part2)
+{
+ if (part1[0] != 0) {
+ _gnutls_str_cpy(name, name_size, part1);
+ _gnutls_str_cat(name, name_size, part2);
+ } else
+ _gnutls_str_cpy(name, name_size,
+ part2 + 1 /* remove initial dot */ );
+}
+
+
+
+/* Encodes and copies the private key parameters into a
+ * subjectPublicKeyInfo structure.
+ *
+ */
+int
+_gnutls_x509_encode_and_copy_PKI_params(asn1_node dst,
+ const char *dst_name,
+ const gnutls_pk_params_st * params)
+{
+ const char *oid;
+ gnutls_datum_t der = { NULL, 0 };
+ int result;
+ char name[128];
+
+ oid = gnutls_pk_get_oid(params->algo);
+ if (oid == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
+ }
+
+ /* write the OID
+ */
+ _asnstr_append_name(name, sizeof(name), dst_name,
+ ".algorithm.algorithm");
+
+ result = asn1_write_value(dst, name, oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _gnutls_x509_write_pubkey_params(params, &der);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ _asnstr_append_name(name, sizeof(name), dst_name,
+ ".algorithm.parameters");
+
+ result = asn1_write_value(dst, name, der.data, der.size);
+ _gnutls_free_datum(&der);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_write_pubkey(params, &der);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Write the DER parameters. (in bits)
+ */
+ _asnstr_append_name(name, sizeof(name), dst_name,
+ ".subjectPublicKey");
+ result = asn1_write_value(dst, name, der.data, der.size * 8);
+ _gnutls_free_datum(&der);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/* Encodes and public key parameters into a
+ * subjectPublicKeyInfo structure and stores it in der.
+ */
+int
+_gnutls_x509_encode_PKI_params(gnutls_datum_t * der,
+ const gnutls_pk_params_st * params)
+{
+ int ret;
+ asn1_node tmp;
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Certificate", &tmp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _gnutls_x509_encode_and_copy_PKI_params(tmp,
+ "tbsCertificate.subjectPublicKeyInfo",
+ params);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_der_encode(tmp,
+ "tbsCertificate.subjectPublicKeyInfo",
+ der, 0);
+
+ cleanup:
+ asn1_delete_structure(&tmp);
+
+ return ret;
+}
+
+/* Reads and returns the PK algorithm of the given certificate-like
+ * ASN.1 structure. src_name should be something like "tbsCertificate.subjectPublicKeyInfo".
+ */
+int
+_gnutls_x509_get_pk_algorithm(asn1_node src, const char *src_name,
+ gnutls_ecc_curve_t *curve,
+ unsigned int *bits)
+{
+ int result;
+ int algo;
+ char oid[64];
+ int len;
+ gnutls_ecc_curve_t lcurve = GNUTLS_ECC_CURVE_INVALID;
+ char name[128];
+
+ _asnstr_append_name(name, sizeof(name), src_name,
+ ".algorithm.algorithm");
+ len = sizeof(oid);
+ result = asn1_read_value(src, name, oid, &len);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ algo = _gnutls_oid_to_pk_and_curve(oid, &lcurve);
+ if (algo == GNUTLS_PK_UNKNOWN) {
+ _gnutls_debug_log
+ ("%s: unknown public key algorithm: %s\n", __func__,
+ oid);
+ }
+
+ if (curve)
+ *curve = lcurve;
+
+ if (bits == NULL) {
+ return algo;
+ }
+
+ /* Now read the parameters' bits
+ */
+ if (lcurve != GNUTLS_ECC_CURVE_INVALID) { /* curve present */
+ bits[0] = gnutls_ecc_curve_get_size(lcurve)*8;
+ } else {
+ gnutls_pk_params_st params;
+ gnutls_pk_params_init(&params);
+
+ result = _gnutls_get_asn_mpis(src, src_name, &params);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ bits[0] = pubkey_to_bits(&params);
+ gnutls_pk_params_release(&params);
+ }
+
+ return algo;
+}
+
+/* Reads the DER signed data from the certificate and allocates space and
+ * returns them into signed_data.
+ */
+int
+_gnutls_x509_get_signed_data(asn1_node src, const gnutls_datum_t *der,
+ const char *src_name,
+ gnutls_datum_t * signed_data)
+{
+ int start, end, result;
+
+ if (der == NULL || der->size == 0) {
+ return _gnutls_x509_der_encode(src, src_name, signed_data, 0);
+ }
+
+ /* Get the signed data
+ */
+ result = asn1_der_decoding_startEnd(src, der->data, der->size,
+ src_name, &start, &end);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_set_datum(signed_data, &der->data[start],
+ end - start + 1);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ return result;
+}
+
+/*-
+ * gnutls_x509_get_signature_algorithm:
+ * @src: should contain an asn1_node structure
+ * @src_name: the description of the signature field
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm that has been used to
+ * sign this certificate.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value, or a negative error code on
+ * error.
+ -*/
+int
+_gnutls_x509_get_signature_algorithm(asn1_node src, const char *src_name)
+{
+ int result;
+ char name[128];
+ gnutls_datum_t sa = {NULL, 0};
+
+ _gnutls_str_cpy(name, sizeof(name), src_name);
+ _gnutls_str_cat(name, sizeof(name), ".algorithm");
+
+ /* Read the signature algorithm */
+ result = _gnutls_x509_read_value(src, name, &sa);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Read the signature parameters. Unless the algorithm is
+ * RSA-PSS, parameters are not read. They will be read from
+ * the issuer's certificate if needed.
+ */
+ if (sa.data && strcmp ((char *) sa.data, PK_PKIX1_RSA_PSS_OID) == 0) {
+ gnutls_datum_t der = {NULL, 0};
+ gnutls_x509_spki_st params;
+
+ _gnutls_str_cpy(name, sizeof(name), src_name);
+ _gnutls_str_cat(name, sizeof(name), ".parameters");
+
+ result = _gnutls_x509_read_value(src, name, &der);
+ if (result < 0) {
+ _gnutls_free_datum(&sa);
+ return gnutls_assert_val(result);
+ }
+
+ result = _gnutls_x509_read_rsa_pss_params(der.data, der.size,
+ &params);
+ _gnutls_free_datum(&der);
+
+ if (result == 0)
+ result = gnutls_pk_to_sign(params.pk, params.rsa_pss_dig);
+ } else if (sa.data) {
+ result = gnutls_oid_to_sign((char *) sa.data);
+ } else {
+ result = GNUTLS_E_UNKNOWN_ALGORITHM;
+ }
+
+ _gnutls_free_datum(&sa);
+
+ if (result == GNUTLS_SIGN_UNKNOWN)
+ result = GNUTLS_E_UNKNOWN_ALGORITHM;
+
+ return result;
+}
+
+
+/* Reads the DER signature from the certificate and allocates space and
+ * returns them into signed_data.
+ */
+int
+_gnutls_x509_get_signature(asn1_node src, const char *src_name,
+ gnutls_datum_t * signature)
+{
+ int result, len;
+ int bits;
+
+ signature->data = NULL;
+ signature->size = 0;
+
+ /* Read the signature
+ */
+ len = 0;
+ result = asn1_read_value(src, src_name, NULL, &len);
+
+ if (result != ASN1_MEM_ERROR) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bits = len;
+ if (bits % 8 != 0 || bits < 8) {
+ gnutls_assert();
+ result = GNUTLS_E_CERTIFICATE_ERROR;
+ goto cleanup;
+ }
+
+ len = bits / 8;
+
+ signature->data = gnutls_malloc(len);
+ if (signature->data == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_MEMORY_ERROR;
+ return result;
+ }
+
+ /* read the bit string of the signature
+ */
+ bits = len;
+ result =
+ asn1_read_value(src, src_name, signature->data, &bits);
+
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ signature->size = len;
+
+ return 0;
+
+ cleanup:
+ gnutls_free(signature->data);
+ return result;
+}
+
+/* ASN.1 PrintableString rules */
+static int is_printable(char p)
+{
+ if ((p >= 'a' && p <= 'z') || (p >= 'A' && p <= 'Z') ||
+ (p >= '0' && p <= '9') || p == ' ' || p == '(' || p == ')' ||
+ p == '+' || p == ',' || p == '-' || p == '.' || p == '/' ||
+ p == ':' || p == '=' || p == '?')
+ return 1;
+
+ return 0;
+}
+
+static int write_complex_string(asn1_node asn_struct, const char *where,
+ const struct oid_to_string *oentry,
+ const uint8_t * data, size_t data_size)
+{
+ char tmp[128];
+ asn1_node c2;
+ int result;
+ const char *string_type;
+ unsigned int i;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(), oentry->asn_desc, &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ tmp[0] = 0;
+
+ string_type = "printableString";
+
+ /* Check if the data is ASN.1 printable, and use
+ * the UTF8 string type if not.
+ */
+ for (i = 0; i < data_size; i++) {
+ if (!is_printable(data[i])) {
+ string_type = "utf8String";
+ break;
+ }
+ }
+
+ /* if the type is a CHOICE then write the
+ * type we'll use.
+ */
+ result = asn1_write_value(c2, "", string_type, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ _gnutls_str_cpy(tmp, sizeof(tmp), string_type);
+
+ result = asn1_write_value(c2, tmp, data, data_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result =
+ _gnutls_x509_der_encode_and_copy(c2, "", asn_struct, where, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&c2);
+ return result;
+}
+
+
+/* This will encode and write the AttributeTypeAndValue field.
+ * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute.
+ * In all cases only one value is written.
+ */
+int
+_gnutls_x509_encode_and_write_attribute(const char *given_oid,
+ asn1_node asn1_struct,
+ const char *where,
+ const void *_data,
+ int data_size, int multi)
+{
+ const uint8_t *data = _data;
+ char tmp[128];
+ int result;
+ const struct oid_to_string *oentry;
+
+ oentry = _gnutls_oid_get_entry(_oid2str, given_oid);
+ if (oentry == NULL) {
+ gnutls_assert();
+ _gnutls_debug_log("Cannot find OID: %s\n", given_oid);
+ return GNUTLS_E_X509_UNSUPPORTED_OID;
+ }
+
+ /* write the data (value)
+ */
+
+ _gnutls_str_cpy(tmp, sizeof(tmp), where);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".value");
+
+ if (multi != 0) { /* if not writing an AttributeTypeAndValue, but an Attribute */
+ _gnutls_str_cat(tmp, sizeof(tmp), "s"); /* values */
+
+ result = asn1_write_value(asn1_struct, tmp, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST");
+ }
+
+ if (oentry->asn_desc != NULL) { /* write a complex string API */
+ result =
+ write_complex_string(asn1_struct, tmp, oentry, data,
+ data_size);
+ if (result < 0)
+ return gnutls_assert_val(result);
+ } else { /* write a simple string */
+
+ gnutls_datum_t td;
+
+ td.data = (void *) data;
+ td.size = data_size;
+ result =
+ _gnutls_x509_write_string(asn1_struct, tmp, &td,
+ oentry->etype);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ /* write the type
+ */
+ _gnutls_str_cpy(tmp, sizeof(tmp), where);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".type");
+
+ result = asn1_write_value(asn1_struct, tmp, given_oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ return result;
+}
+
+/* copies a datum to a buffer. If it doesn't fit it returns
+ * GNUTLS_E_SHORT_MEMORY_BUFFER. It always deinitializes the datum
+ * after the copy.
+ *
+ * The buffer will always be null terminated.
+ */
+int _gnutls_strdatum_to_buf(gnutls_datum_t * d, void *buf,
+ size_t * buf_size)
+{
+ int ret;
+ uint8_t *_buf = buf;
+
+ if (buf == NULL || *buf_size < d->size + 1) {
+ *buf_size = d->size + 1;
+ ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ goto cleanup;
+ }
+ memcpy(buf, d->data, d->size);
+ _buf[d->size] = 0;
+
+ *buf_size = d->size;
+ ret = 0;
+
+ cleanup:
+ _gnutls_free_datum(d);
+
+ return ret;
+}
+
+int
+_gnutls_x509_get_raw_field2(asn1_node c2, const gnutls_datum_t * raw,
+ const char *whom, gnutls_datum_t * dn)
+{
+ int result, len1;
+ int start1, end1;
+ result =
+ asn1_der_decoding_startEnd(c2, raw->data, raw->size,
+ whom, &start1, &end1);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ len1 = end1 - start1 + 1;
+
+ dn->data = &raw->data[start1];
+ dn->size = len1;
+ result = 0;
+
+ cleanup:
+ return result;
+}
+
+int _gnutls_copy_string(const gnutls_datum_t* str, uint8_t *out, size_t *out_size)
+{
+unsigned size_to_check;
+
+ size_to_check = str->size + 1;
+
+ if ((unsigned) size_to_check > *out_size) {
+ gnutls_assert();
+ (*out_size) = size_to_check;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ if (out != NULL && str->data != NULL) {
+ memcpy(out, str->data, str->size);
+ out[str->size] = 0;
+ } else if (out != NULL) {
+ out[0] = 0;
+ }
+ *out_size = str->size;
+
+ return 0;
+}
+
+int _gnutls_copy_data(const gnutls_datum_t* str, uint8_t *out, size_t *out_size)
+{
+ if ((unsigned) str->size > *out_size) {
+ gnutls_assert();
+ (*out_size) = str->size;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ if (out != NULL && str->data != NULL) {
+ memcpy(out, str->data, str->size);
+ }
+ *out_size = str->size;
+
+ return 0;
+}
+
+/* Converts an X.509 certificate to subjectPublicKeyInfo */
+int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt,
+ gnutls_datum_t * rpubkey)
+{
+ gnutls_pubkey_t pubkey = NULL;
+ int ret;
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, rpubkey);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+ return ret;
+}
+
+/* Converts an X.509 certificate to subjectPublicKeyInfo */
+int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert,
+ gnutls_datum_t * rpubkey)
+{
+ gnutls_x509_crt_t crt = NULL;
+ int ret;
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_x509_crt_import(crt, cert, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = x509_crt_to_raw_pubkey(crt, rpubkey);
+ cleanup:
+ gnutls_x509_crt_deinit(crt);
+
+ return ret;
+}
+
+unsigned
+_gnutls_check_valid_key_id(const gnutls_datum_t *key_id,
+ gnutls_x509_crt_t cert, time_t now,
+ unsigned *has_ski)
+{
+ uint8_t id[MAX_KEY_ID_SIZE];
+ size_t id_size;
+ unsigned result = 0;
+
+ if (has_ski)
+ *has_ski = 0;
+
+ if (now > gnutls_x509_crt_get_expiration_time(cert) ||
+ now < gnutls_x509_crt_get_activation_time(cert)) {
+ /* don't bother, certificate is not yet activated or expired */
+ gnutls_assert();
+ goto out;
+ }
+
+ id_size = sizeof(id);
+ if (gnutls_x509_crt_get_subject_key_id(cert, id, &id_size, NULL) < 0) {
+ gnutls_assert();
+ goto out;
+ }
+
+ if (has_ski)
+ *has_ski = 1;
+
+ if (id_size == key_id->size && !memcmp(id, key_id->data, id_size))
+ result = 1;
+
+ out:
+ return result;
+}
+
+/* Sort a certificate list in place with subject, issuer order. @clist_size must
+ * be equal to or less than %DEFAULT_MAX_VERIFY_DEPTH.
+ *
+ * Returns the index in clist where the initial contiguous segment ends. If the
+ * chain contains multiple segments separated by gaps (e.g., missing issuers),
+ * the caller shall call this function multiple times.
+ *
+ * For example, suppose we want the following certificate chain as a result of
+ * sorting:
+ *
+ * A -> (B) -> C -> D -> E -> (F) -> G -> H -> I
+ *
+ * from the input [A, C, E, D, G, I, H] (B and F are missing).
+ *
+ * On the first call of this function:
+ *
+ * _gnutls_sort_clist(clist, clist_size)
+ *
+ * it returns 1, meaning that the first segment only contains A. The content of
+ * @clist will remain the same [A, C, E, D, G, I, H].
+ *
+ * Then the caller will call this function again, starting from the second
+ * element:
+ *
+ * _gnutls_sort_clist(&clist[1], clist_size - 1)
+ *
+ * This time it returns 3, meaning that the first segment contains [C, D, E].
+ * The content of @clist will be [A, C, D, E, G, I, H].
+ *
+ * Finally, after calling the function on the remaining elements:
+ *
+ * _gnutls_sort_clist(&clist[1 + 3], clist_size - (1 + 3))
+ *
+ * It will return 3, meaning that the first segment contains [G, H, I]. At this
+ * point, sorting of @clist is complete.
+ */
+unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist,
+ unsigned int clist_size)
+{
+ int prev;
+ unsigned int i, j, k;
+ int issuer[DEFAULT_MAX_VERIFY_DEPTH]; /* contain the index of the issuers */
+ bool insorted[DEFAULT_MAX_VERIFY_DEPTH]; /* non zero if clist[i] used in sorted list */
+ gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH];
+
+ assert(clist_size <= DEFAULT_MAX_VERIFY_DEPTH);
+
+ for (i = 0; i < DEFAULT_MAX_VERIFY_DEPTH; i++) {
+ issuer[i] = -1;
+ insorted[i] = 0;
+ }
+
+ /* Find the issuer of each certificate and store it
+ * in issuer array. O(n^2) so consider that before
+ * increasing DEFAULT_MAX_VERIFY_DEPTH.
+ */
+ for (i = 0; i < clist_size; i++) {
+ /* Self-signed certificate found in the chain; skip it
+ * as it should only appear in the trusted set.
+ */
+ if (gnutls_x509_crt_check_issuer(clist[i], clist[i])) {
+ _gnutls_cert_log("self-signed cert found", clist[i]);
+ continue;
+ }
+
+ for (j = 1; j < clist_size; j++) {
+ if (i == j)
+ continue;
+
+ if (gnutls_x509_crt_check_issuer(clist[i], clist[j])) {
+ issuer[i] = j;
+ break;
+ }
+ }
+ }
+
+ sorted[0] = clist[0];
+ insorted[0] = 1;
+
+ prev = 0;
+ for (i = 1; i < clist_size; i++) {
+ prev = issuer[prev];
+ if (prev < 0) { /* no issuer */
+ break;
+ }
+
+ sorted[i] = clist[prev];
+ insorted[prev] = 1;
+ }
+
+ /* append the remaining certs */
+ for (j = 1, k = i; j < clist_size; j++) {
+ if (!insorted[j]) {
+ sorted[k++] = clist[j];
+ }
+ }
+
+ /* write out the sorted list */
+ assert(k == clist_size);
+ for (j = 0; j < clist_size; j++) {
+ clist[j] = sorted[j];
+ }
+
+ return i;
+}
+
+int _gnutls_check_if_sorted(gnutls_x509_crt_t * crt, int nr)
+{
+ int i, ret;
+
+ /* check if the X.509 list is ordered */
+ if (nr > 1) {
+ for (i = 0; i < nr; i++) {
+ if (i > 0) {
+ if (!_gnutls_x509_compare_raw_dn(&crt[i]->raw_dn,
+ &crt[i-1]->raw_issuer_dn)) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_CERTIFICATE_LIST_UNSORTED);
+ goto cleanup;
+ }
+ }
+ }
+ }
+ ret = 0;
+
+cleanup:
+ return ret;
+}
+
+/**
+ * gnutls_gost_paramset_get_name:
+ * @param: is a GOST 28147 param set
+ *
+ * Convert a #gnutls_gost_paramset_t value to a string.
+ *
+ * Returns: a string that contains the name of the specified GOST param set,
+ * or %NULL.
+ *
+ * Since: 3.6.3
+ **/
+const char *gnutls_gost_paramset_get_name(gnutls_gost_paramset_t param)
+{
+ switch(param) {
+ case GNUTLS_GOST_PARAMSET_TC26_Z:
+ return "TC26-Z";
+ case GNUTLS_GOST_PARAMSET_CP_A:
+ return "CryptoPro-A";
+ case GNUTLS_GOST_PARAMSET_CP_B:
+ return "CryptoPro-B";
+ case GNUTLS_GOST_PARAMSET_CP_C:
+ return "CryptoPro-C";
+ case GNUTLS_GOST_PARAMSET_CP_D:
+ return "CryptoPro-D";
+ default:
+ gnutls_assert();
+ return "Unknown";
+ }
+}
+
+/**
+ * gnutls_gost_paramset_get_oid:
+ * @param: is a GOST 28147 param set
+ *
+ * Convert a #gnutls_gost_paramset_t value to its object identifier.
+ *
+ * Returns: a string that contains the object identifier of the specified GOST
+ * param set, or %NULL.
+ *
+ * Since: 3.6.3
+ **/
+const char *gnutls_gost_paramset_get_oid(gnutls_gost_paramset_t param)
+{
+ switch(param) {
+ case GNUTLS_GOST_PARAMSET_TC26_Z:
+ return GOST28147_89_TC26Z_OID;
+ case GNUTLS_GOST_PARAMSET_CP_A:
+ return GOST28147_89_CPA_OID;
+ case GNUTLS_GOST_PARAMSET_CP_B:
+ return GOST28147_89_CPB_OID;
+ case GNUTLS_GOST_PARAMSET_CP_C:
+ return GOST28147_89_CPC_OID;
+ case GNUTLS_GOST_PARAMSET_CP_D:
+ return GOST28147_89_CPD_OID;
+ default:
+ gnutls_assert();
+ return NULL;
+ }
+}
+
+/**
+ * gnutls_oid_to_gost_paramset:
+ * @oid: is an object identifier
+ *
+ * Converts a textual object identifier to a #gnutls_gost_paramset_t value.
+ *
+ * Returns: a #gnutls_gost_paramset_get_oid of the specified GOST 28147
+ * param st, or %GNUTLS_GOST_PARAMSET_UNKNOWN on failure.
+ *
+ * Since: 3.6.3
+ **/
+gnutls_gost_paramset_t gnutls_oid_to_gost_paramset(const char *oid)
+{
+ if (!strcmp(oid, GOST28147_89_TC26Z_OID))
+ return GNUTLS_GOST_PARAMSET_TC26_Z;
+ else if (!strcmp(oid, GOST28147_89_CPA_OID))
+ return GNUTLS_GOST_PARAMSET_CP_A;
+ else if (!strcmp(oid, GOST28147_89_CPB_OID))
+ return GNUTLS_GOST_PARAMSET_CP_B;
+ else if (!strcmp(oid, GOST28147_89_CPC_OID))
+ return GNUTLS_GOST_PARAMSET_CP_C;
+ else if (!strcmp(oid, GOST28147_89_CPD_OID))
+ return GNUTLS_GOST_PARAMSET_CP_D;
+ else
+ return gnutls_assert_val(GNUTLS_GOST_PARAMSET_UNKNOWN);
+}
+
+int _gnutls_x509_get_version(asn1_node root, const char *name)
+{
+ uint8_t version[8];
+ int len, result;
+
+ len = sizeof(version);
+ result = asn1_read_value(root, name, version, &len);
+ if (result != ASN1_SUCCESS) {
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return 1; /* the DEFAULT version */
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (len != 1 || version[0] >= 0x80)
+ return gnutls_assert_val(GNUTLS_E_ASN1_DER_ERROR);
+
+ return (int) version[0] + 1;
+}
diff --git a/lib/x509/common.h b/lib/x509/common.h
new file mode 100644
index 0000000..457d4c4
--- /dev/null
+++ b/lib/x509/common.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_COMMON_H
+#define GNUTLS_LIB_X509_COMMON_H
+
+#include <algorithms.h>
+#include <abstract_int.h>
+#include <x509/x509_int.h>
+#include <fips.h>
+
+#define MAX_STRING_LEN 512
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+# define MAX_ITER_COUNT 10*1024
+#else
+/* Set a maximum iteration count over which we refuse to
+ * decode a file. That is to prevent DoS. */
+# define MAX_ITER_COUNT (10*1024*1024)
+#endif
+
+#define GNUTLS_XML_SHOW_ALL 1
+
+#define PEM_CRL "X509 CRL"
+#define PEM_X509_CERT "X509 CERTIFICATE"
+#define PEM_X509_CERT2 "CERTIFICATE"
+#define PEM_PKCS7 "PKCS7"
+#define PEM_PKCS12 "PKCS12"
+#define PEM_PK "PUBLIC KEY"
+
+/* public key algorithm's OIDs
+ */
+#define PK_PKIX1_RSA_OID "1.2.840.113549.1.1.1"
+#define PK_PKIX1_RSA_PSS_OID "1.2.840.113549.1.1.10"
+#define PK_X509_RSA_OID "2.5.8.1.1"
+#define PK_DSA_OID "1.2.840.10040.4.1"
+#define PK_GOST_R3410_94_OID "1.2.643.2.2.20"
+#define PK_GOST_R3410_2001_OID "1.2.643.2.2.19"
+#define PK_GOST_R3410_2012_256_OID "1.2.643.7.1.1.1.1"
+#define PK_GOST_R3410_2012_512_OID "1.2.643.7.1.1.1.2"
+
+/* signature OIDs
+ */
+#define SIG_DSA_SHA1_OID "1.2.840.10040.4.3"
+/* those two from draft-ietf-pkix-sha2-dsa-ecdsa-06 */
+#define SIG_DSA_SHA224_OID "2.16.840.1.101.3.4.3.1"
+#define SIG_DSA_SHA256_OID "2.16.840.1.101.3.4.3.2"
+#define SIG_DSA_SHA384_OID "2.16.840.1.101.3.4.3.3"
+#define SIG_DSA_SHA512_OID "2.16.840.1.101.3.4.3.4"
+
+#define SIG_RSA_MD5_OID "1.2.840.113549.1.1.4"
+#define SIG_RSA_MD2_OID "1.2.840.113549.1.1.2"
+#define SIG_RSA_SHA1_OID "1.2.840.113549.1.1.5"
+#define SIG_RSA_SHA224_OID "1.2.840.113549.1.1.14"
+#define SIG_RSA_SHA256_OID "1.2.840.113549.1.1.11"
+#define SIG_RSA_SHA384_OID "1.2.840.113549.1.1.12"
+#define SIG_RSA_SHA512_OID "1.2.840.113549.1.1.13"
+#define SIG_RSA_RMD160_OID "1.3.36.3.3.1.2"
+#define SIG_GOST_R3410_94_OID "1.2.643.2.2.4"
+#define SIG_GOST_R3410_2001_OID "1.2.643.2.2.3"
+#define SIG_GOST_R3410_2012_256_OID "1.2.643.7.1.1.3.2"
+#define SIG_GOST_R3410_2012_512_OID "1.2.643.7.1.1.3.3"
+#define ISO_SIG_RSA_SHA1_OID "1.3.14.3.2.29"
+
+#define SIG_DSA_SHA3_224_OID "2.16.840.1.101.3.4.3.5"
+#define SIG_DSA_SHA3_256_OID "2.16.840.1.101.3.4.3.6"
+#define SIG_DSA_SHA3_384_OID "2.16.840.1.101.3.4.3.7"
+#define SIG_DSA_SHA3_512_OID "2.16.840.1.101.3.4.3.8"
+
+#define SIG_ECDSA_SHA3_224_OID "2.16.840.1.101.3.4.3.9"
+#define SIG_ECDSA_SHA3_256_OID "2.16.840.1.101.3.4.3.10"
+#define SIG_ECDSA_SHA3_384_OID "2.16.840.1.101.3.4.3.11"
+#define SIG_ECDSA_SHA3_512_OID "2.16.840.1.101.3.4.3.12"
+
+#define SIG_RSA_SHA3_224_OID "2.16.840.1.101.3.4.3.13"
+#define SIG_RSA_SHA3_256_OID "2.16.840.1.101.3.4.3.14"
+#define SIG_RSA_SHA3_384_OID "2.16.840.1.101.3.4.3.15"
+#define SIG_RSA_SHA3_512_OID "2.16.840.1.101.3.4.3.16"
+
+#define ECDH_X25519_OID "1.3.101.110"
+#define ECDH_X448_OID "1.3.101.111"
+
+#define SIG_EDDSA_SHA512_OID "1.3.101.112"
+#define SIG_ED448_OID "1.3.101.113"
+
+#define XMPP_OID "1.3.6.1.5.5.7.8.5"
+#define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2"
+#define MSUSER_PRINCIPAL_NAME_OID "1.3.6.1.4.1.311.20.2.3"
+#define PKIX1_RSA_PSS_MGF1_OID "1.2.840.113549.1.1.8"
+
+#define GOST28147_89_OID "1.2.643.2.2.21"
+#define GOST28147_89_TC26Z_OID "1.2.643.7.1.2.5.1.1"
+#define GOST28147_89_CPA_OID "1.2.643.2.2.31.1"
+#define GOST28147_89_CPB_OID "1.2.643.2.2.31.2"
+#define GOST28147_89_CPC_OID "1.2.643.2.2.31.3"
+#define GOST28147_89_CPD_OID "1.2.643.2.2.31.4"
+
+#define ASN1_NULL "\x05\x00"
+#define ASN1_NULL_SIZE 2
+
+struct oid_to_string {
+ const char *oid;
+ unsigned oid_size;
+ const char *name_desc;
+ unsigned name_desc_size;
+ const char *asn_desc; /* description in the pkix file if complex type */
+ unsigned int etype; /* the libtasn1 ASN1_ETYPE or INVALID
+ * if cannot be simply parsed */
+};
+
+const struct oid_to_string *_gnutls_oid_get_entry(const struct oid_to_string *ots, const char *oid);
+
+const char *_gnutls_oid_get_asn_desc(const char *oid);
+
+int _gnutls_x509_set_time(asn1_node c2, const char *where, time_t tim,
+ int force_general);
+int
+_gnutls_x509_set_raw_time(asn1_node c2, const char *where, time_t tim);
+
+int _gnutls_x509_decode_string(unsigned int etype,
+ const uint8_t * der, size_t der_size,
+ gnutls_datum_t * output,
+ unsigned allow_ber);
+
+int _gnutls_x509_encode_string(unsigned int etype,
+ const void *input_data, size_t input_size,
+ gnutls_datum_t * output);
+
+int _gnutls_x509_dn_to_string(const char *OID, void *value,
+ int value_size, gnutls_datum_t * out);
+const char *_gnutls_ldap_string_to_oid(const char *str, unsigned str_len);
+
+time_t _gnutls_x509_get_time(asn1_node c2, const char *when, int general);
+
+gnutls_x509_subject_alt_name_t _gnutls_x509_san_find_type(char *str_type);
+
+int _gnutls_x509_der_encode_and_copy(asn1_node src, const char *src_name,
+ asn1_node dest, const char *dest_name,
+ int str);
+int _gnutls_x509_der_encode(asn1_node src, const char *src_name,
+ gnutls_datum_t * res, int str);
+
+#define _gnutls_x509_export_int(asn1, format, header, out, out_size) \
+ _gnutls_x509_export_int_named(asn1, "", format, header, out, out_size)
+
+int _gnutls_x509_export_int_named(asn1_node asn1_data, const char *name,
+ gnutls_x509_crt_fmt_t format,
+ const char *pem_header,
+ unsigned char *output_data,
+ size_t * output_data_size);
+
+#define _gnutls_x509_export_int2(asn1, format, header, out) \
+ _gnutls_x509_export_int_named2(asn1, "", format, header, out)
+int _gnutls_x509_export_int_named2(asn1_node asn1_data, const char *name,
+ gnutls_x509_crt_fmt_t format,
+ const char *pem_header,
+ gnutls_datum_t * out);
+
+int _gnutls_x509_read_value(asn1_node c, const char *root,
+ gnutls_datum_t * ret);
+int _gnutls_x509_read_null_value(asn1_node c, const char *root,
+ gnutls_datum_t * ret);
+int _gnutls_x509_read_string(asn1_node c, const char *root,
+ gnutls_datum_t * ret, unsigned int etype,
+ unsigned allow_ber);
+int _gnutls_x509_write_value(asn1_node c, const char *root,
+ const gnutls_datum_t * data);
+
+int _gnutls_x509_write_string(asn1_node c, const char *root,
+ const gnutls_datum_t * data,
+ unsigned int etype);
+
+int _gnutls_x509_encode_and_write_attribute(const char *given_oid,
+ asn1_node asn1_struct,
+ const char *where,
+ const void *data,
+ int sizeof_data, int multi);
+int _gnutls_x509_decode_and_read_attribute(asn1_node asn1_struct,
+ const char *where, char *oid,
+ int oid_size,
+ gnutls_datum_t * value,
+ int multi, int octet);
+
+int _gnutls_x509_get_pk_algorithm(asn1_node src, const char *src_name,
+ gnutls_ecc_curve_t *curve,
+ unsigned int *bits);
+
+int
+_gnutls_x509_get_signature_algorithm(asn1_node src, const char *src_name);
+
+int _gnutls_x509_encode_and_copy_PKI_params(asn1_node dst,
+ const char *dst_name,
+ const gnutls_pk_params_st * params);
+int _gnutls_x509_encode_PKI_params(gnutls_datum_t * der,
+ const gnutls_pk_params_st * params);
+int _gnutls_asn1_copy_node(asn1_node * dst, const char *dst_name,
+ asn1_node src, const char *src_name);
+
+int _gnutls_x509_get_signed_data(asn1_node src, const gnutls_datum_t *der,
+ const char *src_name,
+ gnutls_datum_t * signed_data);
+int _gnutls_x509_get_signature(asn1_node src, const char *src_name,
+ gnutls_datum_t * signature);
+
+
+int _gnutls_get_asn_mpis(asn1_node asn, const char *root,
+ gnutls_pk_params_st * params);
+
+int _gnutls_get_key_id(gnutls_pk_params_st *,
+ unsigned char *output_data,
+ size_t * output_data_size, unsigned flags);
+
+void _asnstr_append_name(char *name, size_t name_size, const char *part1,
+ const char *part2);
+
+/* Given a @c2 which it returns an allocated DER encoding of @whom in @out */
+inline static int
+_gnutls_x509_get_raw_field(asn1_node c2, const char *whom, gnutls_datum_t *out)
+{
+ return _gnutls_x509_der_encode(c2, whom, out, 0);
+}
+
+int
+_gnutls_x509_get_raw_field2(asn1_node c2, const gnutls_datum_t * raw,
+ const char *whom, gnutls_datum_t * dn);
+
+unsigned
+_gnutls_check_if_same_key(gnutls_x509_crt_t cert1,
+ gnutls_x509_crt_t cert2,
+ unsigned is_ca);
+
+unsigned
+_gnutls_check_if_same_key2(gnutls_x509_crt_t cert1,
+ gnutls_datum_t *cert2bin);
+
+unsigned
+_gnutls_check_valid_key_id(const gnutls_datum_t *key_id,
+ gnutls_x509_crt_t cert, time_t now,
+ unsigned *has_ski);
+
+unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, unsigned no_any);
+
+time_t _gnutls_x509_generalTime2gtime(const char *ttime);
+
+int _gnutls_get_extension(asn1_node asn, const char *root,
+ const char *extension_id, int indx,
+ gnutls_datum_t * ret, unsigned int *_critical);
+
+int _gnutls_set_extension(asn1_node asn, const char *root,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data, unsigned int critical);
+
+int _gnutls_strdatum_to_buf(gnutls_datum_t * d, void *buf,
+ size_t * sizeof_buf);
+
+unsigned _gnutls_is_same_dn(gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2);
+
+int _gnutls_copy_string(const gnutls_datum_t* str, uint8_t *out, size_t *out_size);
+int _gnutls_copy_data(const gnutls_datum_t* str, uint8_t *out, size_t *out_size);
+
+int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out);
+int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert,
+ gnutls_datum_t * rpubkey);
+
+int _gnutls_x509_get_version(asn1_node root, const char *name);
+
+int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt,
+ gnutls_datum_t * rpubkey);
+
+typedef void (*gnutls_cert_vfunc)(gnutls_x509_crt_t);
+
+unsigned int _gnutls_sort_clist(gnutls_x509_crt_t *clist,
+ unsigned int clist_size);
+
+int _gnutls_check_if_sorted(gnutls_x509_crt_t * crt, int nr);
+
+inline static int _asn1_strict_der_decode (asn1_node * element, const void *ider,
+ int len, char *errorDescription)
+{
+#if defined(STRICT_DER_TIME) || !defined(ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME)
+# define _ASN1_DER_FLAGS ASN1_DECODE_FLAG_STRICT_DER
+#else
+# define _ASN1_DER_FLAGS (ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME|ASN1_DECODE_FLAG_STRICT_DER)
+#endif
+ return asn1_der_decoding2(element, ider, &len, _ASN1_DER_FLAGS, errorDescription);
+}
+
+#endif /* GNUTLS_LIB_X509_COMMON_H */
diff --git a/lib/x509/crl.c b/lib/x509/crl.c
new file mode 100644
index 0000000..56103e1
--- /dev/null
+++ b/lib/x509/crl.c
@@ -0,0 +1,1425 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <x509.h>
+
+static int crl_reinit(gnutls_x509_crl_t crl)
+{
+int result;
+
+ if (crl->crl)
+ asn1_delete_structure(&crl->crl);
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.CertificateList",
+ &crl->crl);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ crl->rcache = NULL;
+ crl->rcache_idx = 0;
+ crl->raw_issuer_dn.size = 0;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_init:
+ * @crl: A pointer to the type to be initialized
+ *
+ * This function will initialize a CRL structure. CRL stands for
+ * Certificate Revocation List. A revocation list usually contains
+ * lists of certificate serial numbers that have been revoked by an
+ * Authority. The revocation lists are always signed with the
+ * authority's private key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crl_init(gnutls_x509_crl_t * crl)
+{
+ FAIL_IF_LIB_ERROR;
+
+ *crl = gnutls_calloc(1, sizeof(gnutls_x509_crl_int));
+
+ if (*crl) {
+ int result = crl_reinit(*crl);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(*crl);
+ return result;
+ }
+ return 0; /* success */
+ }
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+/**
+ * gnutls_x509_crl_deinit:
+ * @crl: The data to be deinitialized
+ *
+ * This function will deinitialize a CRL structure.
+ **/
+void gnutls_x509_crl_deinit(gnutls_x509_crl_t crl)
+{
+ if (!crl)
+ return;
+
+ if (crl->crl)
+ asn1_delete_structure(&crl->crl);
+ gnutls_free(crl->der.data);
+
+ gnutls_free(crl);
+}
+
+/**
+ * gnutls_x509_crl_import:
+ * @crl: The data to store the parsed CRL.
+ * @data: The DER or PEM encoded CRL.
+ * @format: One of DER or PEM
+ *
+ * This function will convert the given DER or PEM encoded CRL
+ * to the native #gnutls_x509_crl_t format. The output will be stored in 'crl'.
+ *
+ * If the CRL is PEM encoded it should have a header of "X509 CRL".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_import(gnutls_x509_crl_t crl,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format)
+{
+ int result = 0;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ _gnutls_free_datum(&crl->der);
+
+ /* If the CRL is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ result =
+ _gnutls_fbase64_decode(PEM_CRL, data->data, data->size,
+ &crl->der);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ } else {
+ result = _gnutls_set_datum(&crl->der, data->data, data->size);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ if (crl->expanded) {
+ result = crl_reinit(crl);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ crl->expanded = 1;
+
+ result =
+ _asn1_strict_der_decode(&crl->crl, crl->der.data, crl->der.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_get_raw_field2(crl->crl, &crl->der,
+ "tbsCertList.issuer.rdnSequence",
+ &crl->raw_issuer_dn);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ _gnutls_free_datum(&crl->der);
+ return result;
+}
+
+
+/**
+ * gnutls_x509_crl_get_issuer_dn:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @buf: a pointer to a structure to hold the peer's name (may be null)
+ * @sizeof_buf: initially holds the size of @buf
+ *
+ * This function will copy the name of the CRL issuer in the provided
+ * buffer. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * If buf is %NULL then only the size will be filled.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crl_get_issuer_dn3().
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the sizeof_buf will be updated
+ * with the required size, and 0 on success.
+ *
+ **/
+int
+gnutls_x509_crl_get_issuer_dn(gnutls_x509_crl_t crl, char *buf,
+ size_t * sizeof_buf)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_parse_dn(crl->crl,
+ "tbsCertList.issuer.rdnSequence",
+ buf, sizeof_buf, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crl_get_issuer_dn_by_oid:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @oid: holds an Object Identified in null terminated string
+ * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one.
+ * @raw_flag: If non-zero returns the raw DER data of the DN part.
+ * @buf: a pointer to a structure to hold the peer's name (may be null)
+ * @sizeof_buf: initially holds the size of @buf
+ *
+ * This function will extract the part of the name of the CRL issuer
+ * specified by the given OID. The output will be encoded as described
+ * in RFC4514. The output string will be ASCII or UTF-8 encoded,
+ * depending on the certificate data.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * If raw flag is (0), this function will only return known OIDs as
+ * text. Other OIDs will be DER encoded, as described in RFC4514 -- in
+ * hex format with a '#' prefix. You can check about known OIDs
+ * using gnutls_x509_dn_oid_known().
+ *
+ * If buf is null then only the size will be filled.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the sizeof_buf will be updated
+ * with the required size, and 0 on success.
+ **/
+int
+gnutls_x509_crl_get_issuer_dn_by_oid(gnutls_x509_crl_t crl,
+ const char *oid, unsigned indx,
+ unsigned int raw_flag, void *buf,
+ size_t * sizeof_buf)
+{
+ gnutls_datum_t td;
+ int ret;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_parse_dn_oid(crl->crl,
+ "tbsCertList.issuer.rdnSequence",
+ oid, indx, raw_flag, &td);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_strdatum_to_buf(&td, buf, sizeof_buf);
+}
+
+
+/**
+ * gnutls_x509_crl_get_dn_oid:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @indx: Specifies which DN OID to send. Use (0) to get the first one.
+ * @oid: a pointer to store the OID (may be null)
+ * @sizeof_oid: initially holds the size of 'oid'
+ *
+ * This function will extract the requested OID of the name of the CRL
+ * issuer, specified by the given index.
+ *
+ * If oid is null then only the size will be filled.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the sizeof_oid will be updated
+ * with the required size. On success 0 is returned.
+ **/
+int
+gnutls_x509_crl_get_dn_oid(gnutls_x509_crl_t crl,
+ unsigned indx, void *oid, size_t * sizeof_oid)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn_oid(crl->crl,
+ "tbsCertList.issuer.rdnSequence",
+ indx, oid, sizeof_oid);
+}
+
+/**
+ * gnutls_x509_crl_get_issuer_dn2:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ *
+ * This function will allocate buffer and copy the name of the CRL issuer.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crl_get_issuer_dn3().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.10
+ **/
+int
+gnutls_x509_crl_get_issuer_dn2(gnutls_x509_crl_t crl, gnutls_datum_t * dn)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(crl->crl,
+ "tbsCertList.issuer.rdnSequence",
+ dn, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crl_get_issuer_dn3:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will allocate buffer and copy the name of the CRL issuer.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.7
+ **/
+int
+gnutls_x509_crl_get_issuer_dn3(gnutls_x509_crl_t crl, gnutls_datum_t * dn, unsigned flags)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(crl->crl,
+ "tbsCertList.issuer.rdnSequence",
+ dn, flags);
+}
+
+/**
+ * gnutls_x509_crl_get_signature_algorithm:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm.
+ *
+ * Since 3.6.0 this function never returns a negative error code.
+ * Error cases and unknown/unsupported signature algorithms are
+ * mapped to %GNUTLS_SIGN_UNKNOWN.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value
+ **/
+int gnutls_x509_crl_get_signature_algorithm(gnutls_x509_crl_t crl)
+{
+ return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(crl->crl,
+ "signatureAlgorithm"));
+}
+
+/**
+ * gnutls_x509_crl_get_signature_oid:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will return the OID of the signature algorithm
+ * that has been used to sign this CRL. This is function
+ * is useful in the case gnutls_x509_crl_get_signature_algorithm()
+ * returned %GNUTLS_SIGN_UNKNOWN.
+ *
+ * Returns: zero or a negative error code on error.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_crl_get_signature_oid(gnutls_x509_crl_t crl, char *oid, size_t *oid_size)
+{
+ char str[MAX_OID_SIZE];
+ int len, result, ret;
+ gnutls_datum_t out;
+
+ len = sizeof(str);
+ result = asn1_read_value(crl->crl, "signatureAlgorithm.algorithm", str, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ out.data = (void*)str;
+ out.size = len;
+
+ ret = _gnutls_copy_string(&out, (void*)oid, oid_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_get_signature:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @sig: a pointer where the signature part will be copied (may be null).
+ * @sizeof_sig: initially holds the size of @sig
+ *
+ * This function will extract the signature field of a CRL.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_get_signature(gnutls_x509_crl_t crl,
+ char *sig, size_t * sizeof_sig)
+{
+ int result;
+ unsigned int bits;
+ int len;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ len = 0;
+ result = asn1_read_value(crl->crl, "signature", NULL, &len);
+
+ if (result != ASN1_MEM_ERROR) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ bits = len;
+ if (bits % 8 != 0) {
+ gnutls_assert();
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ len = bits / 8;
+
+ if (*sizeof_sig < (unsigned) len) {
+ *sizeof_sig = bits / 8;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ result = asn1_read_value(crl->crl, "signature", sig, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_get_version:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ *
+ * This function will return the version of the specified CRL.
+ *
+ * Returns: The version number, or a negative error code on error.
+ **/
+int gnutls_x509_crl_get_version(gnutls_x509_crl_t crl)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_version(crl->crl, "tbsCertList.version");
+}
+
+/**
+ * gnutls_x509_crl_get_this_update:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ *
+ * This function will return the time this CRL was issued.
+ *
+ * Returns: when the CRL was issued, or (time_t)-1 on error.
+ **/
+time_t gnutls_x509_crl_get_this_update(gnutls_x509_crl_t crl)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+ return _gnutls_x509_get_time(crl->crl, "tbsCertList.thisUpdate",
+ 0);
+}
+
+/**
+ * gnutls_x509_crl_get_next_update:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ *
+ * This function will return the time the next CRL will be issued.
+ * This field is optional in a CRL so it might be normal to get an
+ * error instead.
+ *
+ * Returns: when the next CRL will be issued, or (time_t)-1 on error.
+ **/
+time_t gnutls_x509_crl_get_next_update(gnutls_x509_crl_t crl)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+ return _gnutls_x509_get_time(crl->crl, "tbsCertList.nextUpdate",
+ 0);
+}
+
+/**
+ * gnutls_x509_crl_get_crt_count:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ *
+ * This function will return the number of revoked certificates in the
+ * given CRL.
+ *
+ * Returns: number of certificates, a negative error code on failure.
+ **/
+int gnutls_x509_crl_get_crt_count(gnutls_x509_crl_t crl)
+{
+
+ int count, result;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ asn1_number_of_elements(crl->crl,
+ "tbsCertList.revokedCertificates",
+ &count);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return 0; /* no certificates */
+ }
+
+ return count;
+}
+
+/**
+ * gnutls_x509_crl_get_crt_serial:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @indx: the index of the certificate to extract (starting from 0)
+ * @serial: where the serial number will be copied
+ * @serial_size: initially holds the size of serial
+ * @t: if non null, will hold the time this certificate was revoked
+ *
+ * This function will retrieve the serial number of the specified, by
+ * the index, revoked certificate.
+ *
+ * Note that this function will have performance issues in large sequences
+ * of revoked certificates. In that case use gnutls_x509_crl_iter_crt_serial().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_get_crt_serial(gnutls_x509_crl_t crl, unsigned indx,
+ unsigned char *serial,
+ size_t * serial_size, time_t * t)
+{
+
+ int result, _serial_size;
+ char serial_name[MAX_NAME_SIZE];
+ char date_name[MAX_NAME_SIZE];
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(serial_name, sizeof(serial_name),
+ "tbsCertList.revokedCertificates.?%u.userCertificate",
+ indx + 1);
+ snprintf(date_name, sizeof(date_name),
+ "tbsCertList.revokedCertificates.?%u.revocationDate",
+ indx + 1);
+
+ _serial_size = *serial_size;
+ result =
+ asn1_read_value(crl->crl, serial_name, serial, &_serial_size);
+
+ *serial_size = _serial_size;
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ return _gnutls_asn2err(result);
+ }
+
+ if (t) {
+ *t = _gnutls_x509_get_time(crl->crl, date_name, 0);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_iter_deinit:
+ * @iter: The iterator to be deinitialized
+ *
+ * This function will deinitialize an iterator type.
+ **/
+void gnutls_x509_crl_iter_deinit(gnutls_x509_crl_iter_t iter)
+{
+ if (!iter)
+ return;
+
+ gnutls_free(iter);
+}
+
+/**
+ * gnutls_x509_crl_iter_crt_serial:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @iter: A pointer to an iterator (initially the iterator should be %NULL)
+ * @serial: where the serial number will be copied
+ * @serial_size: initially holds the size of serial
+ * @t: if non null, will hold the time this certificate was revoked
+ *
+ * This function performs the same as gnutls_x509_crl_get_crt_serial(),
+ * but reads sequentially and keeps state in the iterator
+ * between calls. That allows it to provide better performance in sequences
+ * with many elements (50000+).
+ *
+ * When past the last element is accessed %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * is returned and the iterator is reset.
+ *
+ * After use, the iterator must be deinitialized using gnutls_x509_crl_iter_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_iter_crt_serial(gnutls_x509_crl_t crl,
+ gnutls_x509_crl_iter_t *iter,
+ unsigned char *serial,
+ size_t * serial_size, time_t * t)
+{
+
+ int result, _serial_size;
+ char serial_name[MAX_NAME_SIZE];
+ char date_name[MAX_NAME_SIZE];
+
+ if (crl == NULL || iter == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (*iter == NULL) {
+ *iter = gnutls_calloc(1, sizeof(struct gnutls_x509_crl_iter));
+ if (*iter == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ if ((*iter)->rcache == NULL) {
+ (*iter)->rcache = asn1_find_node (crl->crl, "tbsCertList.revokedCertificates.?1");
+ (*iter)->rcache_idx = 1;
+ } else {
+ snprintf(serial_name, sizeof(serial_name),
+ "?%u", (*iter)->rcache_idx);
+ (*iter)->rcache = asn1_find_node ((*iter)->rcache, serial_name);
+ }
+ if ((*iter)->rcache == NULL) {
+ /* reset */
+ (*iter)->rcache = NULL;
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ }
+
+ snprintf(serial_name, sizeof(serial_name),
+ "?%u.userCertificate", (*iter)->rcache_idx);
+
+ _serial_size = *serial_size;
+ result =
+ asn1_read_value((*iter)->rcache, serial_name, serial, &_serial_size);
+
+ *serial_size = _serial_size;
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ /* reset */
+ (*iter)->rcache = NULL;
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+ return _gnutls_asn2err(result);
+ }
+
+ if (t) {
+ snprintf(date_name, sizeof(date_name),
+ "?%u.revocationDate", (*iter)->rcache_idx);
+ *t = _gnutls_x509_get_time((*iter)->rcache, date_name, 0);
+ }
+
+ (*iter)->rcache_idx++;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_get_raw_issuer_dn:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @dn: will hold the starting point of the DN
+ *
+ * This function will return a pointer to the DER encoded DN structure
+ * and the length.
+ *
+ * Returns: a negative error code on error, and (0) on success.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_x509_crl_get_raw_issuer_dn(gnutls_x509_crl_t crl,
+ gnutls_datum_t * dn)
+{
+ if (crl->raw_issuer_dn.size != 0) {
+ return _gnutls_set_datum(dn, crl->raw_issuer_dn.data,
+ crl->raw_issuer_dn.size);
+ } else {
+ return _gnutls_x509_get_raw_field(crl->crl, "tbsCertList.issuer.rdnSequence", dn);
+ }
+}
+
+/**
+ * gnutls_x509_crl_export:
+ * @crl: Holds the revocation list
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a private key PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will
+ * be replaced by the actual size of parameters)
+ *
+ * This function will export the revocation list to DER or PEM format.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN X509 CRL".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_export(gnutls_x509_crl_t crl,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_export_int(crl->crl, format, PEM_CRL,
+ output_data, output_data_size);
+}
+
+/**
+ * gnutls_x509_crl_export2:
+ * @crl: Holds the revocation list
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a private key PEM or DER encoded
+ *
+ * This function will export the revocation list to DER or PEM format.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN X509 CRL".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since 3.1.3
+ **/
+int
+gnutls_x509_crl_export2(gnutls_x509_crl_t crl,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_export_int2(crl->crl, format, PEM_CRL, out);
+}
+
+/*-
+ * _gnutls_x509_crl_cpy - This function copies a gnutls_x509_crl_t type
+ * @dest: The data where to copy
+ * @src: The data to be copied
+ *
+ * This function will copy an X.509 certificate structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ -*/
+int _gnutls_x509_crl_cpy(gnutls_x509_crl_t dest, gnutls_x509_crl_t src)
+{
+ int ret;
+ gnutls_datum_t tmp;
+
+ ret = gnutls_x509_crl_export2(src, GNUTLS_X509_FMT_DER, &tmp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_x509_crl_import(dest, &tmp, GNUTLS_X509_FMT_DER);
+
+ gnutls_free(tmp.data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+
+}
+
+static int
+_get_authority_key_id(gnutls_x509_crl_t cert, asn1_node * c2,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t id;
+
+ *c2 = NULL;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crl_get_extension(cert, "2.5.29.35", 0, &id,
+ critical)) < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ if (id.size == 0 || id.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.AuthorityKeyIdentifier", c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ _gnutls_free_datum(&id);
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _asn1_strict_der_decode(c2, id.data, id.size, NULL);
+ _gnutls_free_datum(&id);
+
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(c2);
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_get_authority_key_gn_serial:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @alt: is the place where the alternative name will be copied to
+ * @alt_size: holds the size of alt.
+ * @alt_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t).
+ * @serial: buffer to store the serial number (may be null)
+ * @serial_size: Holds the size of the serial field (may be null)
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the X.509 authority key
+ * identifier when stored as a general name (authorityCertIssuer)
+ * and serial number.
+ *
+ * Because more than one general names might be stored
+ * @seq can be used as a counter to request them all until
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Returns: Returns 0 on success, or an error code.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crl_get_authority_key_gn_serial(gnutls_x509_crl_t crl,
+ unsigned int seq,
+ void *alt,
+ size_t * alt_size,
+ unsigned int *alt_type,
+ void *serial,
+ size_t * serial_size,
+ unsigned int *critical)
+{
+ int ret, result, len;
+ asn1_node c2;
+
+ ret = _get_authority_key_id(crl, &c2, critical);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_parse_general_name(c2, "authorityCertIssuer", seq, alt,
+ alt_size, alt_type, 0);
+ if (ret < 0) {
+ ret = gnutls_assert_val(ret);
+ goto fail;
+ }
+
+ if (serial) {
+ len = *serial_size;
+ result =
+ asn1_read_value(c2, "authorityCertSerialNumber",
+ serial, &len);
+
+ *serial_size = len;
+
+ if (result < 0) {
+ ret = _gnutls_asn2err(result);
+ goto fail;
+ }
+
+ }
+
+ ret = 0;
+
+ fail:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crl_get_authority_key_id:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @id: The place where the identifier will be copied
+ * @id_size: Holds the size of the result field.
+ * @critical: will be non-zero if the extension is marked as critical
+ * (may be null)
+ *
+ * This function will return the CRL authority's key identifier. This
+ * is obtained by the X.509 Authority Key identifier extension field
+ * (2.5.29.35). Note that this function
+ * only returns the keyIdentifier field of the extension and
+ * %GNUTLS_E_X509_UNSUPPORTED_EXTENSION, if the extension contains
+ * the name and serial number of the certificate. In that case
+ * gnutls_x509_crl_get_authority_key_gn_serial() may be used.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_get_authority_key_id(gnutls_x509_crl_t crl, void *id,
+ size_t * id_size,
+ unsigned int *critical)
+{
+ int result, len, ret;
+ asn1_node c2;
+
+ ret = _get_authority_key_id(crl, &c2, critical);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ len = *id_size;
+ result = asn1_read_value(c2, "keyIdentifier", id, &len);
+
+ *id_size = len;
+ asn1_delete_structure(&c2);
+
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND)
+ return
+ gnutls_assert_val(GNUTLS_E_X509_UNSUPPORTED_EXTENSION);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_get_number:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @ret: The place where the number will be copied
+ * @ret_size: Holds the size of the result field.
+ * @critical: will be non-zero if the extension is marked as critical
+ * (may be null)
+ *
+ * This function will return the CRL number extension. This is
+ * obtained by the CRL Number extension field (2.5.29.20).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_get_number(gnutls_x509_crl_t crl, void *ret,
+ size_t * ret_size, unsigned int *critical)
+{
+ int result;
+ gnutls_datum_t id;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (ret)
+ memset(ret, 0, *ret_size);
+ else
+ *ret_size = 0;
+
+ if ((result =
+ _gnutls_x509_crl_get_extension(crl, "2.5.29.20", 0, &id,
+ critical)) < 0) {
+ return result;
+ }
+
+ if (id.size == 0 || id.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ result =
+ _gnutls_x509_ext_extract_number(ret, ret_size, id.data,
+ id.size);
+
+ _gnutls_free_datum(&id);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_get_extension_oid:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @indx: Specifies which extension OID to send, use (0) to get the first one.
+ * @oid: a pointer to store the OID (may be null)
+ * @sizeof_oid: initially holds the size of @oid
+ *
+ * This function will return the requested extension OID in the CRL.
+ * The extension OID will be stored as a string in the provided
+ * buffer.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl, unsigned indx,
+ void *oid, size_t * sizeof_oid)
+{
+ int result;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ _gnutls_x509_crl_get_extension_oid(crl, indx, oid, sizeof_oid);
+ if (result < 0) {
+ return result;
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crl_get_extension_info:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @indx: Specifies which extension OID to send, use (0) to get the first one.
+ * @oid: a pointer to store the OID
+ * @sizeof_oid: initially holds the maximum size of @oid, on return
+ * holds actual size of @oid.
+ * @critical: output variable with critical flag, may be NULL.
+ *
+ * This function will return the requested extension OID in the CRL,
+ * and the critical flag for it. The extension OID will be stored as
+ * a string in the provided buffer. Use
+ * gnutls_x509_crl_get_extension_data() to extract the data.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be
+ * returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_get_extension_info(gnutls_x509_crl_t crl, unsigned indx,
+ void *oid, size_t * sizeof_oid,
+ unsigned int *critical)
+{
+ int result;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!crl) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsCertList.crlExtensions.?%u.extnID", indx + 1);
+
+ len = *sizeof_oid;
+ result = asn1_read_value(crl->crl, name, oid, &len);
+ *sizeof_oid = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsCertList.crlExtensions.?%u.critical", indx + 1);
+ len = sizeof(str_critical);
+ result = asn1_read_value(crl->crl, name, str_critical, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (critical) {
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crl_get_extension_data:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @indx: Specifies which extension OID to send. Use (0) to get the first one.
+ * @data: a pointer to a structure to hold the data (may be null)
+ * @sizeof_data: initially holds the size of @oid
+ *
+ * This function will return the requested extension data in the CRL.
+ * The extension data will be stored as a string in the provided
+ * buffer.
+ *
+ * Use gnutls_x509_crl_get_extension_info() to extract the OID and
+ * critical flag. Use gnutls_x509_crl_get_extension_info() instead,
+ * if you want to get data indexed by the extension OID rather than
+ * sequence.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_get_extension_data(gnutls_x509_crl_t crl, unsigned indx,
+ void *data, size_t * sizeof_data)
+{
+ int result, len;
+ char name[MAX_NAME_SIZE];
+
+ if (!crl) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsCertList.crlExtensions.?%u.extnValue", indx + 1);
+
+ len = *sizeof_data;
+ result = asn1_read_value(crl->crl, name, data, &len);
+ *sizeof_data = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (result < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_list_import2:
+ * @crls: Will contain the parsed crl list.
+ * @size: It will contain the size of the list.
+ * @data: The PEM encoded CRL.
+ * @format: One of DER or PEM.
+ * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
+ *
+ * This function will convert the given PEM encoded CRL list
+ * to the native gnutls_x509_crl_t format. The output will be stored
+ * in @crls. They will be automatically initialized.
+ *
+ * If the Certificate is PEM encoded it should have a header of "X509
+ * CRL".
+ *
+ * Returns: the number of certificates read or a negative error value.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crl_list_import2(gnutls_x509_crl_t ** crls,
+ unsigned int *size,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int flags)
+{
+ unsigned int init = 1024;
+ int ret;
+
+ *crls = _gnutls_reallocarray(NULL, init, sizeof(gnutls_x509_crl_t));
+ if (*crls == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret =
+ gnutls_x509_crl_list_import(*crls, &init, data, format,
+ flags | GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ *crls = _gnutls_reallocarray_fast(*crls, init,
+ sizeof(gnutls_x509_crl_t));
+ if (*crls == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret =
+ gnutls_x509_crl_list_import(*crls, &init, data, format,
+ flags);
+ }
+
+ if (ret < 0) {
+ gnutls_free(*crls);
+ *crls = NULL;
+ return ret;
+ }
+
+ *size = init;
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_list_import:
+ * @crls: Indicates where the parsed CRLs will be copied to. Must not be initialized.
+ * @crl_max: Initially must hold the maximum number of crls. It will be updated with the number of crls available.
+ * @data: The PEM encoded CRLs
+ * @format: One of DER or PEM.
+ * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
+ *
+ * This function will convert the given PEM encoded CRL list
+ * to the native gnutls_x509_crl_t format. The output will be stored
+ * in @crls. They will be automatically initialized.
+ *
+ * If the Certificate is PEM encoded it should have a header of "X509 CRL".
+ *
+ * Returns: the number of certificates read or a negative error value.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crl_list_import(gnutls_x509_crl_t * crls,
+ unsigned int *crl_max,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int flags)
+{
+ int size;
+ const char *ptr;
+ gnutls_datum_t tmp;
+ int ret, nocopy = 0;
+ unsigned int count = 0, j;
+
+ if (format == GNUTLS_X509_FMT_DER) {
+ if (*crl_max < 1) {
+ *crl_max = 1;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ count = 1; /* import only the first one */
+
+ ret = gnutls_x509_crl_init(&crls[0]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = gnutls_x509_crl_import(crls[0], data, format);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ *crl_max = 1;
+ return 1;
+ }
+
+ /* move to the certificate
+ */
+ ptr = memmem(data->data, data->size,
+ PEM_CRL_SEP, sizeof(PEM_CRL_SEP) - 1);
+ if (ptr == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_BASE64_DECODING_ERROR;
+ }
+
+ count = 0;
+
+ do {
+ if (count >= *crl_max) {
+ if (!
+ (flags &
+ GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED)) {
+ break;
+ } else if (nocopy == 0) {
+ for (j = 0; j < count; j++)
+ gnutls_x509_crl_deinit(crls[j]);
+ nocopy = 1;
+ }
+ }
+
+ if (!nocopy) {
+ ret = gnutls_x509_crl_init(&crls[count]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ tmp.data = (void *) ptr;
+ tmp.size =
+ data->size - (ptr - (char *) data->data);
+ ret =
+ gnutls_x509_crl_import(crls[count], &tmp,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ gnutls_assert();
+ count++;
+ goto error;
+ }
+ }
+
+ /* now we move ptr after the pem header
+ */
+ ptr++;
+ /* find the next certificate (if any)
+ */
+ size = data->size - (ptr - (char *) data->data);
+
+ if (size > 0) {
+ ptr =
+ memmem(ptr, size, PEM_CRL_SEP,
+ sizeof(PEM_CRL_SEP) - 1);
+ } else
+ ptr = NULL;
+
+ count++;
+ }
+ while (ptr != NULL);
+
+ *crl_max = count;
+
+ if (nocopy == 0)
+ return count;
+ else
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+
+ error:
+ for (j = 0; j < count; j++)
+ gnutls_x509_crl_deinit(crls[j]);
+ return ret;
+}
diff --git a/lib/x509/crl_write.c b/lib/x509/crl_write.c
new file mode 100644
index 0000000..36ca452
--- /dev/null
+++ b/lib/x509/crl_write.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains functions to handle CRL generation.
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <libtasn1.h>
+
+static void disable_optional_stuff(gnutls_x509_crl_t crl);
+
+/**
+ * gnutls_x509_crl_set_version:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @version: holds the version number. For CRLv1 crls must be 1.
+ *
+ * This function will set the version of the CRL. This
+ * must be one for CRL version 1, and so on. The CRLs generated
+ * by gnutls should have a version number of 2.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_set_version(gnutls_x509_crl_t crl, unsigned int version)
+{
+ int result;
+ uint8_t null = version & 0xFF;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (null > 0)
+ null -= 1;
+
+ result =
+ asn1_write_value(crl->crl, "tbsCertList.version", &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_sign2:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ * @dig: The message digest to use. GNUTLS_DIG_SHA256 is the safe choice unless you know what you're doing.
+ * @flags: must be 0
+ *
+ * This function will sign the CRL with the issuer's private key, and
+ * will copy the issuer's information into the CRL.
+ *
+ * This must be the last step in a certificate CRL since all
+ * the previously set parameters are now signed.
+ *
+ * A known limitation of this function is, that a newly-signed CRL will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN,
+ * and in that case, a suitable but reasonable for the key algorithm will be selected.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ **/
+int
+gnutls_x509_crl_sign2(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer,
+ gnutls_x509_privkey_t issuer_key,
+ gnutls_digest_algorithm_t dig, unsigned int flags)
+{
+ int result;
+ gnutls_privkey_t privkey;
+
+ if (crl == NULL || issuer == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = gnutls_privkey_init(&privkey);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = gnutls_privkey_import_x509(privkey, issuer_key, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ result =
+ gnutls_x509_crl_privkey_sign(crl, issuer, privkey, dig, flags);
+ if (result < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ result = 0;
+
+ fail:
+ gnutls_privkey_deinit(privkey);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crl_sign:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ *
+ * This function is the same a gnutls_x509_crl_sign2() with no flags,
+ * and an appropriate hash algorithm. The hash algorithm used may
+ * vary between versions of GnuTLS, and it is tied to the security
+ * level of the issuer's public key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ */
+int
+gnutls_x509_crl_sign(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer,
+ gnutls_x509_privkey_t issuer_key)
+{
+ return gnutls_x509_crl_sign2(crl, issuer, issuer_key,
+ 0, 0);
+}
+
+/**
+ * gnutls_x509_crl_set_this_update:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @act_time: The actual time
+ *
+ * This function will set the time this CRL was issued.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crl_set_this_update(gnutls_x509_crl_t crl, time_t act_time)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_set_time(crl->crl, "tbsCertList.thisUpdate",
+ act_time, 0);
+}
+
+/**
+ * gnutls_x509_crl_set_next_update:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @exp_time: The actual time
+ *
+ * This function will set the time this CRL will be updated.
+ * This is an optional value to be set on a CRL and this call
+ * can be omitted when generating a CRL.
+ *
+ * Prior to GnuTLS 3.5.7, setting a nextUpdate field was required
+ * in order to generate a CRL.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crl_set_next_update(gnutls_x509_crl_t crl, time_t exp_time)
+{
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+ return _gnutls_x509_set_time(crl->crl, "tbsCertList.nextUpdate",
+ exp_time, 0);
+}
+
+/**
+ * gnutls_x509_crl_set_crt_serial:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @serial: The revoked certificate's serial number
+ * @serial_size: Holds the size of the serial field.
+ * @revocation_time: The time this certificate was revoked
+ *
+ * This function will set a revoked certificate's serial number to the CRL.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_set_crt_serial(gnutls_x509_crl_t crl,
+ const void *serial, size_t serial_size,
+ time_t revocation_time)
+{
+ int ret;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ asn1_write_value(crl->crl, "tbsCertList.revokedCertificates",
+ "NEW", 1);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret =
+ asn1_write_value(crl->crl,
+ "tbsCertList.revokedCertificates.?LAST.userCertificate",
+ serial, serial_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret =
+ _gnutls_x509_set_time(crl->crl,
+ "tbsCertList.revokedCertificates.?LAST.revocationDate",
+ revocation_time, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ asn1_write_value(crl->crl,
+ "tbsCertList.revokedCertificates.?LAST.crlEntryExtensions",
+ NULL, 0);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_set_crt:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @crt: a certificate of type #gnutls_x509_crt_t with the revoked certificate
+ * @revocation_time: The time this certificate was revoked
+ *
+ * This function will set a revoked certificate's serial number to the CRL.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_set_crt(gnutls_x509_crl_t crl, gnutls_x509_crt_t crt,
+ time_t revocation_time)
+{
+ int ret;
+ uint8_t serial[128];
+ size_t serial_size;
+
+ if (crl == NULL || crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ serial_size = sizeof(serial);
+ ret = gnutls_x509_crt_get_serial(crt, serial, &serial_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ gnutls_x509_crl_set_crt_serial(crl, serial, serial_size,
+ revocation_time);
+ if (ret < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+
+/* If OPTIONAL fields have not been initialized then
+ * disable them.
+ */
+static void disable_optional_stuff(gnutls_x509_crl_t crl)
+{
+ time_t t;
+
+ t = _gnutls_x509_get_time(crl->crl, "tbsCertList.nextUpdate", 0);
+ if (t == (time_t)-1) {
+ (void)asn1_write_value(crl->crl, "tbsCertList.nextUpdate", NULL, 0);
+ }
+
+ if (crl->use_extensions == 0) {
+ (void)asn1_write_value(crl->crl, "tbsCertList.crlExtensions",
+ NULL, 0);
+ }
+
+ return;
+}
+
+/**
+ * gnutls_x509_crl_set_authority_key_id:
+ * @crl: a CRL of type #gnutls_x509_crl_t
+ * @id: The key ID
+ * @id_size: Holds the size of the serial field.
+ *
+ * This function will set the CRL's authority key ID extension. Only
+ * the keyIdentifier field can be set with this function. This may
+ * be used by an authority that holds multiple private keys, to distinguish
+ * the used key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_set_authority_key_id(gnutls_x509_crl_t crl,
+ const void *id, size_t id_size)
+{
+ int result;
+ gnutls_datum_t old_id, der_data;
+ unsigned int critical;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ result =
+ _gnutls_x509_crl_get_extension(crl, "2.5.29.35", 0, &old_id,
+ &critical);
+
+ if (result >= 0) {
+ _gnutls_free_datum(&old_id);
+ } else if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result = _gnutls_x509_ext_gen_auth_key_id(id, id_size, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crl_set_extension(crl, "2.5.29.35", &der_data, 0);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ crl->use_extensions = 1;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_set_number:
+ * @crl: a CRL of type #gnutls_x509_crl_t
+ * @nr: The CRL number
+ * @nr_size: Holds the size of the nr field.
+ *
+ * This function will set the CRL's number extension. This
+ * is to be used as a unique and monotonic number assigned to
+ * the CRL by the authority.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crl_set_number(gnutls_x509_crl_t crl,
+ const void *nr, size_t nr_size)
+{
+ int result;
+ gnutls_datum_t old_id, der_data;
+ unsigned int critical;
+
+ if (crl == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ result =
+ _gnutls_x509_crl_get_extension(crl, "2.5.29.20", 0, &old_id,
+ &critical);
+
+ if (result >= 0) {
+ _gnutls_free_datum(&old_id);
+ } else if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result = _gnutls_x509_ext_gen_number(nr, nr_size, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crl_set_extension(crl, "2.5.29.20", &der_data, 0);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ crl->use_extensions = 1;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_privkey_sign:
+ * @crl: should contain a gnutls_x509_crl_t type
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ * @dig: The message digest to use. GNUTLS_DIG_SHA256 is the safe choice unless you know what you're doing.
+ * @flags: must be 0
+ *
+ * This function will sign the CRL with the issuer's private key, and
+ * will copy the issuer's information into the CRL.
+ *
+ * This must be the last step in a certificate CRL since all
+ * the previously set parameters are now signed.
+ *
+ * A known limitation of this function is, that a newly-signed CRL will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN,
+ * and in that case, a suitable but reasonable for the key algorithm will be selected.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since 2.12.0
+ **/
+int
+gnutls_x509_crl_privkey_sign(gnutls_x509_crl_t crl,
+ gnutls_x509_crt_t issuer,
+ gnutls_privkey_t issuer_key,
+ gnutls_digest_algorithm_t dig,
+ unsigned int flags)
+{
+ int result;
+
+ if (crl == NULL || issuer == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (dig == 0) {
+ result = gnutls_x509_crt_get_preferred_hash_algorithm(issuer, &dig, NULL);
+ if (result < 0)
+ return gnutls_assert_val(result);
+ }
+
+ /* disable all the unneeded OPTIONAL fields.
+ */
+ disable_optional_stuff(crl);
+
+ result = _gnutls_x509_pkix_sign(crl->crl, "tbsCertList",
+ dig, 0, issuer, issuer_key);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
diff --git a/lib/x509/crq.c b/lib/x509/crq.c
new file mode 100644
index 0000000..2603022
--- /dev/null
+++ b/lib/x509/crq.c
@@ -0,0 +1,3099 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains functions to handle PKCS #10 certificate
+ requests, see RFC 2986.
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include <gnutls/x509-ext.h>
+#include "x509_int.h"
+#include <libtasn1.h>
+#include <pk.h>
+#include "attributes.h"
+
+/**
+ * gnutls_x509_crq_init:
+ * @crq: A pointer to the type to be initialized
+ *
+ * This function will initialize a PKCS#10 certificate request
+ * structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crq_init(gnutls_x509_crq_t * crq)
+{
+ int result;
+
+ FAIL_IF_LIB_ERROR;
+
+ *crq = gnutls_calloc(1, sizeof(gnutls_x509_crq_int));
+ if (!*crq)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-10-CertificationRequest",
+ &((*crq)->crq));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(*crq);
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_deinit:
+ * @crq: the type to be deinitialized
+ *
+ * This function will deinitialize a PKCS#10 certificate request
+ * structure.
+ **/
+void gnutls_x509_crq_deinit(gnutls_x509_crq_t crq)
+{
+ if (!crq)
+ return;
+
+ if (crq->crq)
+ asn1_delete_structure(&crq->crq);
+
+ gnutls_free(crq);
+}
+
+#define PEM_CRQ "NEW CERTIFICATE REQUEST"
+#define PEM_CRQ2 "CERTIFICATE REQUEST"
+
+/**
+ * gnutls_x509_crq_import:
+ * @crq: The data to store the parsed certificate request.
+ * @data: The DER or PEM encoded certificate.
+ * @format: One of DER or PEM
+ *
+ * This function will convert the given DER or PEM encoded certificate
+ * request to a #gnutls_x509_crq_t type. The output will be
+ * stored in @crq.
+ *
+ * If the Certificate is PEM encoded it should have a header of "NEW
+ * CERTIFICATE REQUEST".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_import(gnutls_x509_crq_t crq,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format)
+{
+ int result = 0, need_free = 0;
+ gnutls_datum_t _data;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ /* If the Certificate is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ /* Try the first header */
+ result =
+ _gnutls_fbase64_decode(PEM_CRQ, data->data, data->size,
+ &_data);
+
+ if (result < 0) /* Go for the second header */
+ result =
+ _gnutls_fbase64_decode(PEM_CRQ2, data->data,
+ data->size, &_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ need_free = 1;
+ }
+
+ result =
+ _asn1_strict_der_decode(&crq->crq, _data.data, _data.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ if (need_free)
+ _gnutls_free_datum(&_data);
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_get_signature_algorithm:
+ * @crq: should contain a #gnutls_x509_cr_t type
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm that has been used to
+ * sign this certificate request.
+ *
+ * Since 3.6.0 this function never returns a negative error code.
+ * Error cases and unknown/unsupported signature algorithms are
+ * mapped to %GNUTLS_SIGN_UNKNOWN.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value
+ *
+ * Since: 3.4.0
+ **/
+int gnutls_x509_crq_get_signature_algorithm(gnutls_x509_crq_t crq)
+{
+ return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(crq->crq,
+ "signatureAlgorithm"));
+}
+
+/**
+ * gnutls_x509_crq_get_private_key_usage_period:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @activation: The activation time
+ * @expiration: The expiration time
+ * @critical: the extension status
+ *
+ * This function will return the expiration and activation
+ * times of the private key of the certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ **/
+int
+gnutls_x509_crq_get_private_key_usage_period(gnutls_x509_crq_t crq,
+ time_t * activation,
+ time_t * expiration,
+ unsigned int *critical)
+{
+ int result, ret;
+ asn1_node c2 = NULL;
+ uint8_t buf[128];
+ size_t buf_size = sizeof(buf);
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.16", 0,
+ buf, &buf_size,
+ critical);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _asn1_strict_der_decode(&c2, buf, buf_size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (activation)
+ *activation = _gnutls_x509_get_time(c2, "notBefore", 1);
+
+ if (expiration)
+ *expiration = _gnutls_x509_get_time(c2, "notAfter", 1);
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crq_get_dn:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @buf: a pointer to a structure to hold the name (may be %NULL)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will copy the name of the Certificate request subject
+ * to the provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC 2253. The output string
+ * @buf will be ASCII or UTF-8 encoded, depending on the certificate
+ * data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crq_get_dn3().
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the *@buf_size will be updated with
+ * the required size. On success 0 is returned.
+ **/
+int
+gnutls_x509_crq_get_dn(gnutls_x509_crq_t crq, char *buf, size_t * buf_size)
+{
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_parse_dn(crq->crq,
+ "certificationRequestInfo.subject.rdnSequence",
+ buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crq_get_dn2:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ *
+ * This function will allocate buffer and copy the name of the Certificate
+ * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crq_get_dn3().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. and a negative error code on error.
+ *
+ * Since: 3.1.10
+ **/
+int gnutls_x509_crq_get_dn2(gnutls_x509_crq_t crq, gnutls_datum_t * dn)
+{
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(crq->crq,
+ "certificationRequestInfo.subject.rdnSequence",
+ dn, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crq_get_dn3:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will allocate buffer and copy the name of the Certificate
+ * request. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. and a negative error code on error.
+ *
+ * Since: 3.5.7
+ **/
+int gnutls_x509_crq_get_dn3(gnutls_x509_crq_t crq, gnutls_datum_t * dn, unsigned flags)
+{
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(crq->crq,
+ "certificationRequestInfo.subject.rdnSequence",
+ dn, flags);
+}
+
+/**
+ * gnutls_x509_crq_get_dn_by_oid:
+ * @crq: should contain a gnutls_x509_crq_t type
+ * @oid: holds an Object Identifier in a null terminated string
+ * @indx: In case multiple same OIDs exist in the RDN, this specifies
+ * which to get. Use (0) to get the first one.
+ * @raw_flag: If non-zero returns the raw DER data of the DN part.
+ * @buf: a pointer to a structure to hold the name (may be %NULL)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will extract the part of the name of the Certificate
+ * request subject, specified by the given OID. The output will be
+ * encoded as described in RFC2253. The output string will be ASCII
+ * or UTF-8 encoded, depending on the certificate data.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * If raw flag is (0), this function will only return known OIDs as
+ * text. Other OIDs will be DER encoded, as described in RFC2253 --
+ * in hex format with a '\#' prefix. You can check about known OIDs
+ * using gnutls_x509_dn_oid_known().
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the *@buf_size will be
+ * updated with the required size. On success 0 is returned.
+ **/
+int
+gnutls_x509_crq_get_dn_by_oid(gnutls_x509_crq_t crq, const char *oid,
+ unsigned indx, unsigned int raw_flag,
+ void *buf, size_t * buf_size)
+{
+ gnutls_datum_t td;
+ int ret;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_parse_dn_oid
+ (crq->crq,
+ "certificationRequestInfo.subject.rdnSequence",
+ oid, indx, raw_flag, &td);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_strdatum_to_buf(&td, buf, buf_size);
+}
+
+/**
+ * gnutls_x509_crq_get_dn_oid:
+ * @crq: should contain a gnutls_x509_crq_t type
+ * @indx: Specifies which DN OID to get. Use (0) to get the first one.
+ * @oid: a pointer to a structure to hold the name (may be %NULL)
+ * @sizeof_oid: initially holds the size of @oid
+ *
+ * This function will extract the requested OID of the name of the
+ * certificate request subject, specified by the given index.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the *@sizeof_oid will be
+ * updated with the required size. On success 0 is returned.
+ **/
+int
+gnutls_x509_crq_get_dn_oid(gnutls_x509_crq_t crq,
+ unsigned indx, void *oid, size_t * sizeof_oid)
+{
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn_oid(crq->crq,
+ "certificationRequestInfo.subject.rdnSequence",
+ indx, oid, sizeof_oid);
+}
+
+/**
+ * gnutls_x509_crq_get_challenge_password:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @pass: will hold a (0)-terminated password string
+ * @pass_size: Initially holds the size of @pass.
+ *
+ * This function will return the challenge password in the request.
+ * The challenge password is intended to be used for requesting a
+ * revocation of the certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_get_challenge_password(gnutls_x509_crq_t crq,
+ char *pass, size_t * pass_size)
+{
+ gnutls_datum_t td;
+ int ret;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _x509_parse_attribute(crq->crq,
+ "certificationRequestInfo.attributes",
+ "1.2.840.113549.1.9.7", 0, 0, &td);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_strdatum_to_buf(&td, pass, pass_size);
+}
+
+/**
+ * gnutls_x509_crq_set_attribute_by_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: holds an Object Identifier in a null-terminated string
+ * @buf: a pointer to a structure that holds the attribute data
+ * @buf_size: holds the size of @buf
+ *
+ * This function will set the attribute in the certificate request
+ * specified by the given Object ID. The provided attribute must be be DER
+ * encoded.
+ *
+ * Attributes in a certificate request is an optional set of data
+ * appended to the request. Their interpretation depends on the CA policy.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_attribute_by_oid(gnutls_x509_crq_t crq,
+ const char *oid, void *buf,
+ size_t buf_size)
+{
+ gnutls_datum_t data;
+
+ data.data = buf;
+ data.size = buf_size;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _x509_set_attribute(crq->crq,
+ "certificationRequestInfo.attributes", oid,
+ &data);
+}
+
+/**
+ * gnutls_x509_crq_get_attribute_by_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: holds an Object Identifier in null-terminated string
+ * @indx: In case multiple same OIDs exist in the attribute list, this
+ * specifies which to get, use (0) to get the first one
+ * @buf: a pointer to a structure to hold the attribute data (may be %NULL)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will return the attribute in the certificate request
+ * specified by the given Object ID. The attribute will be DER
+ * encoded.
+ *
+ * Attributes in a certificate request is an optional set of data
+ * appended to the request. Their interpretation depends on the CA policy.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_get_attribute_by_oid(gnutls_x509_crq_t crq,
+ const char *oid, unsigned indx, void *buf,
+ size_t * buf_size)
+{
+ int ret;
+ gnutls_datum_t td;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _x509_parse_attribute(crq->crq,
+ "certificationRequestInfo.attributes", oid,
+ indx, 1, &td);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_strdatum_to_buf(&td, buf, buf_size);
+}
+
+/**
+ * gnutls_x509_crq_set_dn_by_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: holds an Object Identifier in a (0)-terminated string
+ * @raw_flag: must be 0, or 1 if the data are DER encoded
+ * @data: a pointer to the input data
+ * @sizeof_data: holds the size of @data
+ *
+ * This function will set the part of the name of the Certificate
+ * request subject, specified by the given OID. The input string
+ * should be ASCII or UTF-8 encoded.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * With this function you can only set the known OIDs. You can test
+ * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are
+ * not known (by gnutls) you should properly DER encode your data, and
+ * call this function with raw_flag set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_dn_by_oid(gnutls_x509_crq_t crq, const char *oid,
+ unsigned int raw_flag, const void *data,
+ unsigned int sizeof_data)
+{
+ if (sizeof_data == 0 || data == NULL || crq == NULL) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_set_dn_oid(crq->crq,
+ "certificationRequestInfo.subject",
+ oid, raw_flag, data, sizeof_data);
+}
+
+/**
+ * gnutls_x509_crq_set_version:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @version: holds the version number, for v1 Requests must be 1
+ *
+ * This function will set the version of the certificate request. For
+ * version 1 requests this must be one.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_version(gnutls_x509_crq_t crq, unsigned int version)
+{
+ int result;
+ unsigned char null = version;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (null > 0)
+ null--;
+
+ result =
+ asn1_write_value(crq->crq, "certificationRequestInfo.version",
+ &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_version:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ *
+ * This function will return the version of the specified Certificate
+ * request.
+ *
+ * Returns: version of certificate request, or a negative error code on
+ * error.
+ **/
+int gnutls_x509_crq_get_version(gnutls_x509_crq_t crq)
+{
+ uint8_t version[8];
+ int len, result;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ len = sizeof(version);
+ if ((result =
+ asn1_read_value(crq->crq, "certificationRequestInfo.version",
+ version, &len)) != ASN1_SUCCESS) {
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return 1; /* the DEFAULT version */
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return (int) version[0] + 1;
+}
+
+/**
+ * gnutls_x509_crq_set_key:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @key: holds a private key
+ *
+ * This function will set the public parameters from the given private
+ * key to the request.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_key(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key)
+{
+ int result;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = _gnutls_x509_encode_and_copy_PKI_params
+ (crq->crq,
+ "certificationRequestInfo.subjectPKInfo",
+ &key->params);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_key_rsa_raw:
+ * @crq: Holds the certificate
+ * @m: will hold the modulus
+ * @e: will hold the public exponent
+ *
+ * This function will export the RSA public key's parameters found in
+ * the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_key_rsa_raw(gnutls_x509_crq_t crq,
+ gnutls_datum_t * m, gnutls_datum_t * e)
+{
+ int ret;
+ gnutls_pk_params_st params;
+
+ gnutls_pk_params_init(&params);
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL);
+ if (ret != GNUTLS_PK_RSA) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_crq_get_mpis(crq, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_mpi_dprint(params.params[0], m);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_mpi_dprint(params.params[1], e);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_free_datum(m);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_pk_params_release(&params);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_set_key_rsa_raw:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @m: holds the modulus
+ * @e: holds the public exponent
+ *
+ * This function will set the public parameters from the given private
+ * key to the request. Only RSA keys are currently supported.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.6.0
+ **/
+int
+gnutls_x509_crq_set_key_rsa_raw(gnutls_x509_crq_t crq,
+ const gnutls_datum_t * m,
+ const gnutls_datum_t * e)
+{
+ int result, ret;
+ size_t siz = 0;
+ gnutls_pk_params_st temp_params;
+
+ gnutls_pk_params_init(&temp_params);
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ memset(&temp_params, 0, sizeof(temp_params));
+
+ siz = m->size;
+ if (_gnutls_mpi_init_scan_nz(&temp_params.params[0], m->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto error;
+ }
+
+ siz = e->size;
+ if (_gnutls_mpi_init_scan_nz(&temp_params.params[1], e->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto error;
+ }
+
+ temp_params.params_nr = RSA_PUBLIC_PARAMS;
+ temp_params.algo = GNUTLS_PK_RSA;
+
+ result = _gnutls_x509_encode_and_copy_PKI_params
+ (crq->crq,
+ "certificationRequestInfo.subjectPKInfo",
+ &temp_params);
+
+ if (result < 0) {
+ gnutls_assert();
+ ret = result;
+ goto error;
+ }
+
+ ret = 0;
+
+ error:
+ gnutls_pk_params_release(&temp_params);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_set_challenge_password:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @pass: holds a (0)-terminated password
+ *
+ * This function will set a challenge password to be used when
+ * revoking the request.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_challenge_password(gnutls_x509_crq_t crq,
+ const char *pass)
+{
+ int result;
+ char *password = NULL;
+
+ if (crq == NULL || pass == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Add the attribute.
+ */
+ result =
+ asn1_write_value(crq->crq,
+ "certificationRequestInfo.attributes", "NEW",
+ 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (pass) {
+ gnutls_datum_t out;
+ result = _gnutls_utf8_password_normalize(pass, strlen(pass), &out, 0);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ password = (char*)out.data;
+ }
+
+ assert(password != NULL);
+
+ result = _gnutls_x509_encode_and_write_attribute
+ ("1.2.840.113549.1.9.7", crq->crq,
+ "certificationRequestInfo.attributes.?LAST", password,
+ strlen(password), 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ gnutls_free(password);
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_sign2:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @key: holds a private key
+ * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA256
+ * @flags: must be 0
+ *
+ * This function will sign the certificate request with a private key.
+ * This must be the same key as the one used in
+ * gnutls_x509_crt_set_key() since a certificate request is self
+ * signed.
+ *
+ * This must be the last step in a certificate request generation
+ * since all the previously set parameters are now signed.
+ *
+ * A known limitation of this function is, that a newly-signed request will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN,
+ * and in that case, a suitable but reasonable for the key algorithm will be selected.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all
+ * information in the certificate request (e.g., the version using
+ * gnutls_x509_crq_set_version()).
+ *
+ **/
+int
+gnutls_x509_crq_sign2(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key,
+ gnutls_digest_algorithm_t dig, unsigned int flags)
+{
+ int result;
+ gnutls_privkey_t privkey;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = gnutls_privkey_init(&privkey);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = gnutls_privkey_import_x509(privkey, key, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ result = gnutls_x509_crq_privkey_sign(crq, privkey, dig, flags);
+ if (result < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ result = 0;
+
+ fail:
+ gnutls_privkey_deinit(privkey);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_sign:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @key: holds a private key
+ *
+ * This function is the same a gnutls_x509_crq_sign2() with no flags,
+ * and an appropriate hash algorithm. The hash algorithm used may
+ * vary between versions of GnuTLS, and it is tied to the security
+ * level of the issuer's public key.
+ *
+ * A known limitation of this function is, that a newly-signed request will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ */
+int gnutls_x509_crq_sign(gnutls_x509_crq_t crq, gnutls_x509_privkey_t key)
+{
+ return gnutls_x509_crq_sign2(crq, key, 0, 0);
+}
+
+/**
+ * gnutls_x509_crq_export:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a certificate request PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the certificate request to a PEM or DER
+ * encoded PKCS10 structure.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned and
+ * *@output_data_size will be updated.
+ *
+ * If the structure is PEM encoded, it will have a header of "BEGIN
+ * NEW CERTIFICATE REQUEST".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_export(gnutls_x509_crq_t crq,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_export_int(crq->crq, format, PEM_CRQ,
+ output_data, output_data_size);
+}
+
+/**
+ * gnutls_x509_crq_export2:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a certificate request PEM or DER encoded
+ *
+ * This function will export the certificate request to a PEM or DER
+ * encoded PKCS10 structure.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header of "BEGIN
+ * NEW CERTIFICATE REQUEST".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since 3.1.3
+ **/
+int
+gnutls_x509_crq_export2(gnutls_x509_crq_t crq,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_export_int2(crq->crq, format, PEM_CRQ, out);
+}
+
+/**
+ * gnutls_x509_crq_get_pk_algorithm:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @bits: if bits is non-%NULL it will hold the size of the parameters' in bits
+ *
+ * This function will return the public key algorithm of a PKCS#10
+ * certificate request.
+ *
+ * If bits is non-%NULL, it should have enough size to hold the
+ * parameters size in bits. For RSA the bits returned is the modulus.
+ * For DSA the bits returned are of the public exponent.
+ *
+ * Returns: a member of the #gnutls_pk_algorithm_t enumeration on
+ * success, or a negative error code on error.
+ **/
+int
+gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits)
+{
+ int result;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = _gnutls_x509_get_pk_algorithm
+ (crq->crq, "certificationRequestInfo.subjectPKInfo", NULL, bits);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_get_spki;
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t
+ * @flags: must be zero
+ *
+ * This function will return the public key information of a PKCS#10
+ * certificate request. The provided @spki must be initialized.
+ *
+ * Returns: Zero on success, or a negative error code on error.
+ **/
+int
+gnutls_x509_crq_get_spki(gnutls_x509_crq_t crq,
+ gnutls_x509_spki_t spki,
+ unsigned int flags)
+{
+ int result;
+ gnutls_x509_spki_st params;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ memset(&params, 0, sizeof(params));
+
+ spki->pk = gnutls_x509_crq_get_pk_algorithm(crq, NULL);
+
+ result = _gnutls_x509_crq_read_spki_params(crq, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if (params.pk == GNUTLS_PK_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ spki->rsa_pss_dig = params.rsa_pss_dig;
+ spki->salt_size = params.salt_size;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_signature_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will return the OID of the signature algorithm
+ * that has been used to sign this certificate request. This function
+ * is useful in the case gnutls_x509_crq_get_signature_algorithm()
+ * returned %GNUTLS_SIGN_UNKNOWN.
+ *
+ * Returns: zero or a negative error code on error.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_crq_get_signature_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size)
+{
+ char str[MAX_OID_SIZE];
+ int len, result, ret;
+ gnutls_datum_t out;
+
+ len = sizeof(str);
+ result = asn1_read_value(crq->crq, "signatureAlgorithm.algorithm", str, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ out.data = (void*)str;
+ out.size = len;
+
+ ret = _gnutls_copy_string(&out, (void*)oid, oid_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_pk_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will return the OID of the public key algorithm
+ * on that certificate request. This function
+ * is useful in the case gnutls_x509_crq_get_pk_algorithm()
+ * returned %GNUTLS_PK_UNKNOWN.
+ *
+ * Returns: zero or a negative error code on error.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_crq_get_pk_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size)
+{
+ char str[MAX_OID_SIZE];
+ int len, result, ret;
+ gnutls_datum_t out;
+
+ len = sizeof(str);
+ result = asn1_read_value(crq->crq, "certificationRequestInfo.subjectPKInfo.algorithm.algorithm", str, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ out.data = (void*)str;
+ out.size = len;
+
+ ret = _gnutls_copy_string(&out, (void*)oid, oid_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_attribute_info:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @indx: Specifies which attribute number to get. Use (0) to get the first one.
+ * @oid: a pointer to a structure to hold the OID
+ * @sizeof_oid: initially holds the maximum size of @oid, on return
+ * holds actual size of @oid.
+ *
+ * This function will return the requested attribute OID in the
+ * certificate, and the critical flag for it. The attribute OID will
+ * be stored as a string in the provided buffer. Use
+ * gnutls_x509_crq_get_attribute_data() to extract the data.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be
+ * returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_attribute_info(gnutls_x509_crq_t crq, unsigned indx,
+ void *oid, size_t * sizeof_oid)
+{
+ int result;
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!crq) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "certificationRequestInfo.attributes.?%u.type", indx + 1);
+
+ len = *sizeof_oid;
+ result = asn1_read_value(crq->crq, name, oid, &len);
+ *sizeof_oid = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (result < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crq_get_attribute_data:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @indx: Specifies which attribute number to get. Use (0) to get the first one.
+ * @data: a pointer to a structure to hold the data (may be null)
+ * @sizeof_data: initially holds the size of @oid
+ *
+ * This function will return the requested attribute data in the
+ * certificate request. The attribute data will be stored as a string in the
+ * provided buffer.
+ *
+ * Use gnutls_x509_crq_get_attribute_info() to extract the OID.
+ * Use gnutls_x509_crq_get_attribute_by_oid() instead,
+ * if you want to get data indexed by the attribute OID rather than
+ * sequence.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_attribute_data(gnutls_x509_crq_t crq, unsigned indx,
+ void *data, size_t * sizeof_data)
+{
+ int result, len;
+ char name[MAX_NAME_SIZE];
+
+ if (!crq) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "certificationRequestInfo.attributes.?%u.values.?1",
+ indx + 1);
+
+ len = *sizeof_data;
+ result = asn1_read_value(crq->crq, name, data, &len);
+ *sizeof_data = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (result < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_extension_info:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @indx: Specifies which extension number to get. Use (0) to get the first one.
+ * @oid: a pointer to store the OID
+ * @sizeof_oid: initially holds the maximum size of @oid, on return
+ * holds actual size of @oid.
+ * @critical: output variable with critical flag, may be NULL.
+ *
+ * This function will return the requested extension OID in the
+ * certificate, and the critical flag for it. The extension OID will
+ * be stored as a string in the provided buffer. Use
+ * gnutls_x509_crq_get_extension_data() to extract the data.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@sizeof_oid is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be
+ * returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_extension_info(gnutls_x509_crq_t crq, unsigned indx,
+ void *oid, size_t * sizeof_oid,
+ unsigned int *critical)
+{
+ int result;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ char *extensions = NULL;
+ size_t extensions_size = 0;
+ asn1_node c2;
+ int len;
+
+ if (!crq) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* read extensionRequest */
+ result =
+ gnutls_x509_crq_get_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ 0, NULL,
+ &extensions_size);
+ if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ extensions = gnutls_malloc(extensions_size);
+ if (extensions == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = gnutls_x509_crq_get_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ 0,
+ extensions,
+ &extensions_size);
+ }
+ if (result < 0) {
+ gnutls_assert();
+ goto out;
+ }
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions",
+ &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto out;
+ }
+
+ result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ result = _gnutls_asn2err(result);
+ goto out;
+ }
+
+ snprintf(name, sizeof(name), "?%u.extnID", indx + 1);
+
+ len = *sizeof_oid;
+ result = asn1_read_value(c2, name, oid, &len);
+ *sizeof_oid = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ asn1_delete_structure(&c2);
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto out;
+ } else if (result < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ result = _gnutls_asn2err(result);
+ goto out;
+ }
+
+ snprintf(name, sizeof(name), "?%u.critical", indx + 1);
+ len = sizeof(str_critical);
+ result = asn1_read_value(c2, name, str_critical, &len);
+
+ asn1_delete_structure(&c2);
+
+ if (result < 0) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto out;
+ }
+
+ if (critical) {
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ result = 0;
+
+ out:
+ gnutls_free(extensions);
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_get_extension_data:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @indx: Specifies which extension number to get. Use (0) to get the first one.
+ * @data: a pointer to a structure to hold the data (may be null)
+ * @sizeof_data: initially holds the size of @oid
+ *
+ * This function will return the requested extension data in the
+ * certificate. The extension data will be stored as a string in the
+ * provided buffer.
+ *
+ * Use gnutls_x509_crq_get_extension_info() to extract the OID and
+ * critical flag. Use gnutls_x509_crq_get_extension_by_oid() instead,
+ * if you want to get data indexed by the extension OID rather than
+ * sequence.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If your have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_extension_data(gnutls_x509_crq_t crq, unsigned indx,
+ void *data, size_t * sizeof_data)
+{
+ int ret;
+ gnutls_datum_t raw;
+
+ ret = gnutls_x509_crq_get_extension_data2(crq, indx, &raw);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_copy_data(&raw, data, sizeof_data);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && data == NULL)
+ ret = 0;
+ gnutls_free(raw.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_get_extension_data2:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @extension_id: An X.509 extension OID.
+ * @indx: Specifies which extension OID to read. Use (0) to get the first one.
+ * @data: will contain the extension DER-encoded data
+ *
+ * This function will return the requested extension data in the
+ * certificate request. The extension data will be allocated using
+ * gnutls_malloc().
+ *
+ * Use gnutls_x509_crq_get_extension_info() to extract the OID.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If you have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ *
+ * Since: 3.3.0
+ **/
+int
+gnutls_x509_crq_get_extension_data2(gnutls_x509_crq_t crq,
+ unsigned indx, gnutls_datum_t * data)
+{
+ int ret, result;
+ char name[MAX_NAME_SIZE];
+ unsigned char *extensions = NULL;
+ size_t extensions_size = 0;
+ asn1_node c2 = NULL;
+
+ if (!crq) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* read extensionRequest */
+ ret =
+ gnutls_x509_crq_get_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ 0, NULL,
+ &extensions_size);
+ if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ gnutls_assert();
+ if (ret == 0)
+ return GNUTLS_E_INTERNAL_ERROR;
+ return ret;
+ }
+
+ extensions = gnutls_malloc(extensions_size);
+ if (extensions == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret =
+ gnutls_x509_crq_get_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ 0, extensions,
+ &extensions_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions",
+ &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _asn1_strict_der_decode(&c2, extensions, extensions_size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ snprintf(name, sizeof(name), "?%u.extnValue", indx + 1);
+
+ ret = _gnutls_x509_read_value(c2, name, data);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ } else if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+ gnutls_free(extensions);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_get_key_usage:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @key_usage: where the key usage bits will be stored
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return certificate's key usage, by reading the
+ * keyUsage X.509 extension (2.5.29.15). The key usage value will
+ * ORed values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE,
+ * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT,
+ * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT,
+ * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN,
+ * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY.
+ *
+ * Returns: the certificate key usage, or a negative error code in case of
+ * parsing error. If the certificate does not contain the keyUsage
+ * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_key_usage(gnutls_x509_crq_t crq,
+ unsigned int *key_usage,
+ unsigned int *critical)
+{
+ int result;
+ uint8_t buf[128];
+ size_t buf_size = sizeof(buf);
+ gnutls_datum_t bd;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.15", 0,
+ buf, &buf_size,
+ critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ bd.data = buf;
+ bd.size = buf_size;
+ result = gnutls_x509_ext_import_key_usage(&bd, key_usage);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_basic_constraints:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @critical: will be non-zero if the extension is marked as critical
+ * @ca: pointer to output integer indicating CA status, may be NULL,
+ * value is 1 if the certificate CA flag is set, 0 otherwise.
+ * @pathlen: pointer to output integer indicating path length (may be
+ * NULL), non-negative error codes indicate a present pathLenConstraint
+ * field and the actual value, -1 indicate that the field is absent.
+ *
+ * This function will read the certificate's basic constraints, and
+ * return the certificates CA status. It reads the basicConstraints
+ * X.509 extension (2.5.29.19).
+ *
+ * Returns: If the certificate is a CA a positive value will be
+ * returned, or (0) if the certificate does not have CA flag set.
+ * A negative error code may be returned in case of errors. If the
+ * certificate does not contain the basicConstraints extension
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_basic_constraints(gnutls_x509_crq_t crq,
+ unsigned int *critical,
+ unsigned int *ca, int *pathlen)
+{
+ int result;
+ unsigned int tmp_ca;
+ uint8_t buf[256];
+ size_t buf_size = sizeof(buf);
+ gnutls_datum_t bd;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.19", 0,
+ buf, &buf_size,
+ critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ bd.data = buf;
+ bd.size = buf_size;
+ result = gnutls_x509_ext_import_basic_constraints(&bd, &tmp_ca, pathlen);
+ if (ca)
+ *ca = tmp_ca;
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return tmp_ca;
+}
+
+static int
+get_subject_alt_name(gnutls_x509_crq_t crq,
+ unsigned int seq, void *ret,
+ size_t * ret_size, unsigned int *ret_type,
+ unsigned int *critical, int othername_oid)
+{
+ int result;
+ asn1_node c2 = NULL;
+ gnutls_x509_subject_alt_name_t type;
+ gnutls_datum_t dnsname = { NULL, 0 };
+ size_t dns_size = 0;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (ret)
+ memset(ret, 0, *ret_size);
+ else
+ *ret_size = 0;
+
+ /* Extract extension.
+ */
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0,
+ NULL, &dns_size,
+ critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ dnsname.size = dns_size;
+ dnsname.data = gnutls_malloc(dnsname.size);
+ if (dnsname.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17", 0,
+ dnsname.data,
+ &dns_size, critical);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(dnsname.data);
+ return result;
+ }
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.SubjectAltName", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(dnsname.data);
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, dnsname.data, dnsname.size, NULL);
+ gnutls_free(dnsname.data);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_parse_general_name(c2, "", seq, ret, ret_size,
+ ret_type, othername_oid);
+ asn1_delete_structure(&c2);
+ if (result < 0) {
+ return result;
+ }
+
+ type = result;
+
+ return type;
+}
+
+/**
+ * gnutls_x509_crq_get_subject_alt_name:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @seq: specifies the sequence number of the alt name, 0 for the
+ * first one, 1 for the second etc.
+ * @ret: is the place where the alternative name will be copied to
+ * @ret_size: holds the size of ret.
+ * @ret_type: holds the #gnutls_x509_subject_alt_name_t name type
+ * @critical: will be non-zero if the extension is marked as critical
+ * (may be null)
+ *
+ * This function will return the alternative names, contained in the
+ * given certificate. It is the same as
+ * gnutls_x509_crq_get_subject_alt_name() except for the fact that it
+ * will return the type of the alternative name in @ret_type even if
+ * the function fails for some reason (i.e. the buffer provided is
+ * not enough).
+ *
+ * Returns: the alternative subject name type on success, one of the
+ * enumerated #gnutls_x509_subject_alt_name_t. It will return
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough to
+ * hold the value. In that case @ret_size will be updated with the
+ * required size. If the certificate request does not have an
+ * Alternative name with the specified sequence number then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_subject_alt_name(gnutls_x509_crq_t crq,
+ unsigned int seq, void *ret,
+ size_t * ret_size,
+ unsigned int *ret_type,
+ unsigned int *critical)
+{
+ return get_subject_alt_name(crq, seq, ret, ret_size, ret_type,
+ critical, 0);
+}
+
+/**
+ * gnutls_x509_crq_get_subject_alt_othername_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ret: is the place where the otherName OID will be copied to
+ * @ret_size: holds the size of ret.
+ *
+ * This function will extract the type OID of an otherName Subject
+ * Alternative Name, contained in the given certificate, and return
+ * the type as an enumerated element.
+ *
+ * This function is only useful if
+ * gnutls_x509_crq_get_subject_alt_name() returned
+ * %GNUTLS_SAN_OTHERNAME.
+ *
+ * Returns: the alternative subject name type on success, one of the
+ * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs,
+ * it will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types,
+ * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for
+ * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if
+ * @ret_size is not large enough to hold the value. In that case
+ * @ret_size will be updated with the required size. If the
+ * certificate does not have an Alternative name with the specified
+ * sequence number and with the otherName type then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_subject_alt_othername_oid(gnutls_x509_crq_t crq,
+ unsigned int seq,
+ void *ret, size_t * ret_size)
+{
+ return get_subject_alt_name(crq, seq, ret, ret_size, NULL, NULL,
+ 1);
+}
+
+/**
+ * gnutls_x509_crq_get_extension_by_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: holds an Object Identifier in a null terminated string
+ * @indx: In case multiple same OIDs exist in the extensions, this
+ * specifies which to get. Use (0) to get the first one.
+ * @buf: a pointer to a structure to hold the name (may be null)
+ * @buf_size: initially holds the size of @buf
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return the extension specified by the OID in
+ * the certificate. The extensions will be returned as binary data
+ * DER encoded, in the provided buffer.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If the certificate does not
+ * contain the specified extension
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_extension_by_oid(gnutls_x509_crq_t crq,
+ const char *oid, unsigned indx,
+ void *buf, size_t * buf_size,
+ unsigned int *critical)
+{
+ int result;
+ unsigned int i;
+ char _oid[MAX_OID_SIZE];
+ size_t oid_size;
+
+ for (i = 0;; i++) {
+ oid_size = sizeof(_oid);
+ result =
+ gnutls_x509_crq_get_extension_info(crq, i, _oid,
+ &oid_size,
+ critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if (strcmp(oid, _oid) == 0) { /* found */
+ if (indx == 0)
+ return
+ gnutls_x509_crq_get_extension_data(crq,
+ i,
+ buf,
+ buf_size);
+ else
+ indx--;
+ }
+ }
+
+
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+}
+
+/**
+ * gnutls_x509_crq_get_extension_by_oid2:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @oid: holds an Object Identifier in a null terminated string
+ * @indx: In case multiple same OIDs exist in the extensions, this
+ * specifies which to get. Use (0) to get the first one.
+ * @output: will hold the allocated extension data
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return the extension specified by the OID in
+ * the certificate. The extensions will be returned as binary data
+ * DER encoded, in the provided buffer.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code in case of an error. If the certificate does not
+ * contain the specified extension
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Since: 3.3.8
+ **/
+int
+gnutls_x509_crq_get_extension_by_oid2(gnutls_x509_crq_t crq,
+ const char *oid, unsigned indx,
+ gnutls_datum_t *output,
+ unsigned int *critical)
+{
+ int result;
+ unsigned int i;
+ char _oid[MAX_OID_SIZE];
+ size_t oid_size;
+
+ for (i = 0;; i++) {
+ oid_size = sizeof(_oid);
+ result =
+ gnutls_x509_crq_get_extension_info(crq, i, _oid,
+ &oid_size,
+ critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if (strcmp(oid, _oid) == 0) { /* found */
+ if (indx == 0)
+ return
+ gnutls_x509_crq_get_extension_data2(crq,
+ i,
+ output);
+ else
+ indx--;
+ }
+ }
+
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+}
+
+/**
+ * gnutls_x509_crq_set_subject_alt_name:
+ * @crq: a certificate request of type #gnutls_x509_crq_t
+ * @nt: is one of the #gnutls_x509_subject_alt_name_t enumerations
+ * @data: The data to be set
+ * @data_size: The size of data to be set
+ * @flags: %GNUTLS_FSAN_SET to clear previous data or
+ * %GNUTLS_FSAN_APPEND to append.
+ *
+ * This function will set the subject alternative name certificate
+ * extension. It can set the following types:
+ *
+ * %GNUTLS_SAN_DNSNAME: as a text string
+ *
+ * %GNUTLS_SAN_RFC822NAME: as a text string
+ *
+ * %GNUTLS_SAN_URI: as a text string
+ *
+ * %GNUTLS_SAN_IPADDRESS: as a binary IP address (4 or 16 bytes)
+ *
+ * %GNUTLS_SAN_OTHERNAME_XMPP: as a UTF8 string
+ *
+ * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
+ * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_set_subject_alt_name(gnutls_x509_crq_t crq,
+ gnutls_x509_subject_alt_name_t nt,
+ const void *data,
+ unsigned int data_size,
+ unsigned int flags)
+{
+ int result = 0;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t prev_der_data = { NULL, 0 };
+ unsigned int critical = 0;
+ size_t prev_data_size = 0;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ if (flags & GNUTLS_FSAN_APPEND) {
+ result =
+ gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17",
+ 0, NULL,
+ &prev_data_size,
+ &critical);
+ prev_der_data.size = prev_data_size;
+
+ switch (result) {
+ case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
+ /* Replacing non-existing data means the same as set data. */
+ break;
+
+ case GNUTLS_E_SUCCESS:
+ prev_der_data.data =
+ gnutls_malloc(prev_der_data.size);
+ if (prev_der_data.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result =
+ gnutls_x509_crq_get_extension_by_oid(crq,
+ "2.5.29.17",
+ 0,
+ prev_der_data.
+ data,
+ &prev_data_size,
+ &critical);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(prev_der_data.data);
+ return result;
+ }
+ break;
+
+ default:
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* generate the extension.
+ */
+ result = _gnutls_x509_ext_gen_subject_alt_name(nt, NULL, data, data_size,
+ &prev_der_data,
+ &der_data);
+ gnutls_free(prev_der_data.data);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result =
+ _gnutls_x509_crq_set_extension(crq, "2.5.29.17", &der_data,
+ critical);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+
+ finish:
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_set_subject_alt_othername:
+ * @crq: a certificate request of type #gnutls_x509_crq_t
+ * @oid: is the othername OID
+ * @data: The data to be set
+ * @data_size: The size of data to be set
+ * @flags: %GNUTLS_FSAN_SET to clear previous data or
+ * %GNUTLS_FSAN_APPEND to append.
+ *
+ * This function will set the subject alternative name certificate
+ * extension. It can set the following types:
+ *
+ * The values set must be binary values and must be properly DER encoded.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.0
+ **/
+int
+gnutls_x509_crq_set_subject_alt_othername(gnutls_x509_crq_t crq,
+ const char *oid,
+ const void *data,
+ unsigned int data_size,
+ unsigned int flags)
+{
+ int result = 0;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t encoded_data = { NULL, 0 };
+ gnutls_datum_t prev_der_data = { NULL, 0 };
+ unsigned int critical = 0;
+ size_t prev_data_size = 0;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ if (flags & GNUTLS_FSAN_APPEND) {
+ result =
+ gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.17",
+ 0, NULL,
+ &prev_data_size,
+ &critical);
+ prev_der_data.size = prev_data_size;
+
+ switch (result) {
+ case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
+ /* Replacing non-existing data means the same as set data. */
+ break;
+
+ case GNUTLS_E_SUCCESS:
+ prev_der_data.data =
+ gnutls_malloc(prev_der_data.size);
+ if (prev_der_data.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result =
+ gnutls_x509_crq_get_extension_by_oid(crq,
+ "2.5.29.17",
+ 0,
+ prev_der_data.
+ data,
+ &prev_data_size,
+ &critical);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+ break;
+
+ default:
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ /* generate the extension.
+ */
+ result = _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid,
+ encoded_data.data, encoded_data.size,
+ &prev_der_data,
+ &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result =
+ _gnutls_x509_crq_set_extension(crq, "2.5.29.17", &der_data,
+ critical);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result = 0;
+
+ finish:
+ _gnutls_free_datum(&prev_der_data);
+ _gnutls_free_datum(&der_data);
+ _gnutls_free_datum(&encoded_data);
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_set_basic_constraints:
+ * @crq: a certificate request of type #gnutls_x509_crq_t
+ * @ca: true(1) or false(0) depending on the Certificate authority status.
+ * @pathLenConstraint: non-negative error codes indicate maximum length of path,
+ * and negative error codes indicate that the pathLenConstraints field should
+ * not be present.
+ *
+ * This function will set the basicConstraints certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_set_basic_constraints(gnutls_x509_crq_t crq,
+ unsigned int ca,
+ int pathLenConstraint)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result = gnutls_x509_ext_export_basic_constraints(ca, pathLenConstraint, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crq_set_extension(crq, "2.5.29.19", &der_data, 1);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_set_key_usage:
+ * @crq: a certificate request of type #gnutls_x509_crq_t
+ * @usage: an ORed sequence of the GNUTLS_KEY_* elements.
+ *
+ * This function will set the keyUsage certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_set_key_usage(gnutls_x509_crq_t crq, unsigned int usage)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result =
+ gnutls_x509_ext_export_key_usage(usage, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crq_set_extension(crq, "2.5.29.15", &der_data, 1);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_key_purpose_oid:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @indx: This specifies which OID to return, use (0) to get the first one
+ * @oid: a pointer to store the OID (may be %NULL)
+ * @sizeof_oid: initially holds the size of @oid
+ * @critical: output variable with critical flag, may be %NULL.
+ *
+ * This function will extract the key purpose OIDs of the Certificate
+ * specified by the given index. These are stored in the Extended Key
+ * Usage extension (2.5.29.37). See the GNUTLS_KP_* definitions for
+ * human readable names.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the *@sizeof_oid will be
+ * updated with the required size. On success 0 is returned.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_key_purpose_oid(gnutls_x509_crq_t crq,
+ unsigned indx, void *oid,
+ size_t * sizeof_oid,
+ unsigned int *critical)
+{
+ char tmpstr[MAX_NAME_SIZE];
+ int result, len;
+ gnutls_datum_t prev = { NULL, 0 };
+ asn1_node c2 = NULL;
+ size_t prev_size = 0;
+
+ if (oid)
+ memset(oid, 0, *sizeof_oid);
+ else
+ *sizeof_oid = 0;
+
+ /* Extract extension.
+ */
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0,
+ NULL, &prev_size,
+ critical);
+ prev.size = prev_size;
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ prev.data = gnutls_malloc(prev.size);
+ if (prev.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0,
+ prev.data,
+ &prev_size,
+ critical);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(prev.data);
+ return result;
+ }
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(prev.data);
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL);
+ gnutls_free(prev.data);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ indx++;
+ /* create a string like "?1"
+ */
+ snprintf(tmpstr, sizeof(tmpstr), "?%u", indx);
+
+ len = *sizeof_oid;
+ result = asn1_read_value(c2, tmpstr, oid, &len);
+
+ *sizeof_oid = len;
+ asn1_delete_structure(&c2);
+
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ if (result != ASN1_SUCCESS) {
+ if (result != ASN1_MEM_ERROR)
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_set_key_purpose_oid:
+ * @crq: a certificate of type #gnutls_x509_crq_t
+ * @oid: a pointer to a null-terminated string that holds the OID
+ * @critical: Whether this extension will be critical or not
+ *
+ * This function will set the key purpose OIDs of the Certificate.
+ * These are stored in the Extended Key Usage extension (2.5.29.37)
+ * See the GNUTLS_KP_* definitions for human readable names.
+ *
+ * Subsequent calls to this function will append OIDs to the OID list.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_set_key_purpose_oid(gnutls_x509_crq_t crq,
+ const void *oid, unsigned int critical)
+{
+ int result;
+ gnutls_datum_t prev = { NULL, 0 }, der_data;
+ asn1_node c2 = NULL;
+ size_t prev_size = 0;
+
+ /* Read existing extension, if there is one.
+ */
+ result = gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37", 0,
+ NULL, &prev_size,
+ &critical);
+ prev.size = prev_size;
+
+ switch (result) {
+ case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
+ /* No existing extension, that's fine. */
+ break;
+
+ case GNUTLS_E_SUCCESS:
+ prev.data = gnutls_malloc(prev.size);
+ if (prev.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result =
+ gnutls_x509_crq_get_extension_by_oid(crq, "2.5.29.37",
+ 0, prev.data,
+ &prev_size,
+ &critical);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(prev.data);
+ return result;
+ }
+ break;
+
+ default:
+ gnutls_assert();
+ return result;
+ }
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.ExtKeyUsageSyntax", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(prev.data);
+ return _gnutls_asn2err(result);
+ }
+
+ if (prev.data) {
+ /* decode it.
+ */
+ result =
+ _asn1_strict_der_decode(&c2, prev.data, prev.size, NULL);
+ gnutls_free(prev.data);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+ }
+
+ /* generate the extension.
+ */
+ /* 1. create a new element.
+ */
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ /* 2. Add the OID.
+ */
+ result = asn1_write_value(c2, "?LAST", oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", &der_data, 0);
+ asn1_delete_structure(&c2);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_crq_set_extension(crq, "2.5.29.37",
+ &der_data, critical);
+ _gnutls_free_datum(&der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crq_get_key_id:
+ * @crq: a certificate of type #gnutls_x509_crq_t
+ * @flags: should be one of the flags from %gnutls_keyid_flags_t
+ * @output_data: will contain the key ID
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will return a unique ID that depends on the public key
+ * parameters. This ID can be used in checking whether a certificate
+ * corresponds to the given private key.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will
+ * be returned. The output will normally be a SHA-1 hash output,
+ * which is 20 bytes.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_get_key_id(gnutls_x509_crq_t crq, unsigned int flags,
+ unsigned char *output_data,
+ size_t * output_data_size)
+{
+ int ret = 0;
+ gnutls_pk_params_st params;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_crq_get_mpis(crq, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_get_key_id(&params, output_data, output_data_size, flags);
+
+ gnutls_pk_params_release(&params);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_privkey_sign:
+ * @crq: should contain a #gnutls_x509_crq_t type
+ * @key: holds a private key
+ * @dig: The message digest to use, i.e., %GNUTLS_DIG_SHA1
+ * @flags: must be 0
+ *
+ * This function will sign the certificate request with a private key.
+ * This must be the same key as the one used in
+ * gnutls_x509_crt_set_key() since a certificate request is self
+ * signed.
+ *
+ * This must be the last step in a certificate request generation
+ * since all the previously set parameters are now signed.
+ *
+ * A known limitation of this function is, that a newly-signed request will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN,
+ * and in that case, a suitable but reasonable for the key algorithm will be selected.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ * %GNUTLS_E_ASN1_VALUE_NOT_FOUND is returned if you didn't set all
+ * information in the certificate request (e.g., the version using
+ * gnutls_x509_crq_set_version()).
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key,
+ gnutls_digest_algorithm_t dig,
+ unsigned int flags)
+{
+ int result;
+ gnutls_datum_t signature;
+ gnutls_datum_t tbs;
+ gnutls_pk_algorithm_t pk;
+ gnutls_x509_spki_st params;
+ const gnutls_sign_entry_st *se;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Make sure version field is set. */
+ if (gnutls_x509_crq_get_version(crq) ==
+ GNUTLS_E_ASN1_VALUE_NOT_FOUND) {
+ result = gnutls_x509_crq_set_version(crq, 1);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ if (dig == 0) {
+ /* attempt to find a reasonable choice */
+ gnutls_pubkey_t pubkey;
+ int ret;
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_privkey(pubkey, key, 0, 0);
+ if (ret < 0) {
+ gnutls_pubkey_deinit(pubkey);
+ return gnutls_assert_val(ret);
+ }
+ ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, &dig, NULL);
+ gnutls_pubkey_deinit(pubkey);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ result = _gnutls_privkey_get_spki_params(key, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ pk = gnutls_privkey_get_pk_algorithm(key, NULL);
+ result = _gnutls_privkey_update_spki_params(key, pk, dig, 0, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 1. Self sign the request.
+ */
+ result =
+ _gnutls_x509_get_tbs(crq->crq, "certificationRequestInfo",
+ &tbs);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ se = _gnutls_pk_to_sign_entry(params.pk, dig);
+ if (se == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ FIX_SIGN_PARAMS(params, flags, dig);
+
+ result = privkey_sign_and_hash_data(key, se,
+ &tbs, &signature, &params);
+ gnutls_free(tbs.data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 2. write the signature (bits)
+ */
+ result =
+ asn1_write_value(crq->crq, "signature", signature.data,
+ signature.size * 8);
+
+ _gnutls_free_datum(&signature);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Step 3. Write the signatureAlgorithm field.
+ */
+ result =
+ _gnutls_x509_write_sign_params(crq->crq, "signatureAlgorithm",
+ se, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_x509_crq_verify:
+ * @crq: is the crq to be verified
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ *
+ * This function will verify self signature in the certificate
+ * request and return its status.
+ *
+ * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED
+ * is returned, and zero or positive code on success.
+ *
+ * Since 2.12.0
+ **/
+int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags)
+{
+ gnutls_datum_t data = { NULL, 0 };
+ gnutls_datum_t signature = { NULL, 0 };
+ gnutls_pk_params_st params;
+ gnutls_x509_spki_st sign_params;
+ const gnutls_sign_entry_st *se;
+ int ret;
+
+ gnutls_pk_params_init(&params);
+
+ ret =
+ _gnutls_x509_get_signed_data(crq->crq, NULL,
+ "certificationRequestInfo",
+ &data);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_get_signature_algorithm(crq->crq,
+ "signatureAlgorithm");
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ se = _gnutls_sign_to_entry(ret);
+ if (se == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_get_signature(crq->crq, "signature", &signature);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_crq_get_mpis(crq, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_read_sign_params(crq->crq,
+ "signatureAlgorithm",
+ &sign_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ pubkey_verify_data(se, hash_to_entry(se->hash), &data, &signature,
+ &params, &sign_params, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ _gnutls_free_datum(&data);
+ _gnutls_free_datum(&signature);
+ gnutls_pk_params_release(&params);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_set_private_key_usage_period:
+ * @crq: a certificate of type #gnutls_x509_crq_t
+ * @activation: The activation time
+ * @expiration: The expiration time
+ *
+ * This function will set the private key usage period extension (2.5.29.16).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_private_key_usage_period(gnutls_x509_crq_t crq,
+ time_t activation,
+ time_t expiration)
+{
+ int result;
+ gnutls_datum_t der_data;
+ asn1_node c2 = NULL;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.PrivateKeyUsagePeriod", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_set_time(c2, "notBefore", activation, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", &der_data, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_crq_set_extension(crq, "2.5.29.16",
+ &der_data, 0);
+
+ _gnutls_free_datum(&der_data);
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crq_get_tlsfeatures:
+ * @crq: An X.509 certificate request
+ * @features: If the function succeeds, the
+ * features will be stored in this variable.
+ * @flags: zero or %GNUTLS_EXT_FLAG_APPEND
+ * @critical: the extension status
+ *
+ * This function will get the X.509 TLS features
+ * extension structure from the certificate request.
+ * The returned structure needs to be freed using
+ * gnutls_x509_tlsfeatures_deinit().
+ *
+ * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND,
+ * then if the @features structure is empty this function will behave
+ * identically as if the flag was not set. Otherwise if there are elements
+ * in the @features structure then they will be merged with.
+ *
+ * Note that @features must be initialized prior to calling this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq,
+ gnutls_x509_tlsfeatures_t features,
+ unsigned int flags,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t der = {NULL, 0};
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ gnutls_x509_crq_get_extension_by_oid2(crq, GNUTLS_X509EXT_OID_TLSFEATURES, 0,
+ &der, critical)) < 0)
+ {
+ return ret;
+ }
+
+ if (der.size == 0 || der.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(der.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_set_tlsfeatures:
+ * @crq: An X.509 certificate request
+ * @features: If the function succeeds, the
+ * features will be added to the certificate
+ * request.
+ *
+ * This function will set the certificate request's
+ * X.509 TLS extension from the given structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_crq_set_tlsfeatures(gnutls_x509_crq_t crq,
+ gnutls_x509_tlsfeatures_t features)
+{
+ int ret;
+ gnutls_datum_t der;
+
+ if (crq == NULL || features == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_ext_export_tlsfeatures(features, &der);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_x509_crq_set_extension(crq, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0);
+
+ _gnutls_free_datum(&der);
+
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crq_set_extension_by_oid:
+ * @crq: a certificate of type #gnutls_x509_crq_t
+ * @oid: holds an Object Identifier in null terminated string
+ * @buf: a pointer to a DER encoded data
+ * @sizeof_buf: holds the size of @buf
+ * @critical: should be non-zero if the extension is to be marked as critical
+ *
+ * This function will set an the extension, by the specified OID, in
+ * the certificate request. The extension data should be binary data DER
+ * encoded.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_extension_by_oid(gnutls_x509_crq_t crq,
+ const char *oid, const void *buf,
+ size_t sizeof_buf,
+ unsigned int critical)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ der_data.data = (void *) buf;
+ der_data.size = sizeof_buf;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ _gnutls_x509_crq_set_extension(crq, oid, &der_data, critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crq_set_spki:
+ * @crq: a certificate request of type #gnutls_x509_crq_t
+ * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t
+ * @flags: must be zero
+ *
+ * This function will set the certificate request's subject public key
+ * information explicitly. This is intended to be used in the cases
+ * where a single public key (e.g., RSA) can be used for multiple
+ * signature algorithms (RSA PKCS1-1.5, and RSA-PSS).
+ *
+ * To export the public key (i.e., the SubjectPublicKeyInfo part), check
+ * gnutls_pubkey_import_x509().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.0
+ **/
+int
+gnutls_x509_crq_set_spki(gnutls_x509_crq_t crq,
+ const gnutls_x509_spki_t spki,
+ unsigned int flags)
+{
+ int ret;
+ gnutls_pk_algorithm_t crq_pk;
+ gnutls_x509_spki_st tpki;
+ gnutls_pk_params_st params;
+ unsigned bits;
+
+ if (crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_crq_get_mpis(crq, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ bits = pubkey_to_bits(&params);
+ crq_pk = params.algo;
+
+ if (!_gnutls_pk_are_compat(crq_pk, spki->pk)) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ if (spki->pk != GNUTLS_PK_RSA_PSS) {
+ if (crq_pk == spki->pk) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ memset(&tpki, 0, sizeof(gnutls_x509_spki_st));
+
+ if (crq_pk == GNUTLS_PK_RSA) {
+ const mac_entry_st *me;
+
+ me = hash_to_entry(spki->rsa_pss_dig);
+ if (unlikely(me == NULL)) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ tpki.pk = spki->pk;
+ tpki.rsa_pss_dig = spki->rsa_pss_dig;
+
+ /* If salt size is zero, find the optimal salt size. */
+ if (spki->salt_size == 0) {
+ ret =
+ _gnutls_find_rsa_pss_salt_size(bits, me,
+ spki->salt_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tpki.salt_size = ret;
+ } else
+ tpki.salt_size = spki->salt_size;
+ } else if (crq_pk == GNUTLS_PK_RSA_PSS) {
+ ret = _gnutls_x509_crq_read_spki_params(crq, &tpki);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ tpki.salt_size = spki->salt_size;
+ tpki.rsa_pss_dig = spki->rsa_pss_dig;
+ }
+
+ memcpy(&params.spki, &tpki, sizeof(tpki));
+ ret = _gnutls_x509_check_pubkey_params(&params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_write_spki_params(crq->crq,
+ "certificationRequestInfo."
+ "subjectPKInfo."
+ "algorithm",
+ &tpki);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_pk_params_release(&params);
+ return ret;
+}
diff --git a/lib/x509/dn.c b/lib/x509/dn.c
new file mode 100644
index 0000000..8d89f56
--- /dev/null
+++ b/lib/x509/dn.c
@@ -0,0 +1,1051 @@
+/*
+ * Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <str.h>
+#include <common.h>
+#include <num.h>
+
+/* This file includes all the required to parse an X.509 Distriguished
+ * Name (you need a parser just to read a name in the X.509 protocols!!!)
+ */
+
+static int append_elements(asn1_node asn1_struct, const char *asn1_rdn_name, gnutls_buffer_st *str, int k1, unsigned last)
+{
+ int k2, result, max_k2;
+ int len;
+ uint8_t value[MAX_STRING_LEN];
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer2[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ const char *ldap_desc;
+ char oid[MAX_OID_SIZE];
+ gnutls_datum_t td = { NULL, 0 };
+ gnutls_datum_t tvd = { NULL, 0 };
+
+ /* create a string like "tbsCertList.issuer.rdnSequence.?1"
+ */
+ if (asn1_rdn_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ asn1_rdn_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) { /* expected */
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ k2 = 0;
+
+ result = asn1_number_of_elements(asn1_struct, tmpbuffer1, &max_k2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ do { /* Move to the attribute type and values
+ */
+ k2++;
+
+ if (tmpbuffer1[0] != 0)
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "%s.?%d", tmpbuffer1, k2);
+ else
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "?%d", k2);
+
+ /* Try to read the RelativeDistinguishedName attributes.
+ */
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer2, value,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ if (result != ASN1_VALUE_NOT_FOUND && result != ASN1_SUCCESS) { /* expected */
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the Value
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".value");
+
+ len = 0;
+
+ result =
+ _gnutls_x509_read_value(asn1_struct,
+ tmpbuffer3, &tvd);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+#define STR_APPEND(y) if ((result=_gnutls_buffer_append_str( str, y)) < 0) { \
+ gnutls_assert(); \
+ goto cleanup; \
+}
+#define DATA_APPEND(x,y) if ((result=_gnutls_buffer_append_data( str, x,y)) < 0) { \
+ gnutls_assert(); \
+ goto cleanup; \
+}
+ /* The encodings of adjoining RelativeDistinguishedNames are separated
+ * by a comma character (',' ASCII 44).
+ */
+
+ ldap_desc =
+ gnutls_x509_dn_oid_name(oid,
+ GNUTLS_X509_DN_OID_RETURN_OID);
+
+ STR_APPEND(ldap_desc);
+ STR_APPEND("=");
+
+ /* DirectoryString by definition in RFC 5280 cannot be empty.
+ * If asn_node.value_len = 0 the parser correctly rejects such DirectoryString.
+ * However, if asn_node.value contains ASN.1 TLV triplet with length = 0,
+ * such DirectoryString is not rejected by the parser as the node itself is not empty.
+ * Explicitly reject DirectoryString in such case.
+ */
+ const char *asn_desc = _gnutls_oid_get_asn_desc(oid);
+ if (asn_desc && !strcmp(asn_desc, "PKIX1.DirectoryString") && tvd.data[1] == 0) {
+ gnutls_assert();
+ result = GNUTLS_E_ASN1_VALUE_NOT_VALID;
+ _gnutls_debug_log("Empty DirectoryString\n");
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_dn_to_string(oid, tvd.data,
+ tvd.size, &td);
+ if (result < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Cannot parse OID: '%s' with value '%s'\n",
+ oid, _gnutls_bin2hex(tvd.data,
+ tvd.size,
+ tmpbuffer3,
+ sizeof
+ (tmpbuffer3),
+ NULL));
+ goto cleanup;
+ }
+
+ DATA_APPEND(td.data, td.size);
+ _gnutls_free_datum(&td);
+ _gnutls_free_datum(&tvd);
+
+ /* Where there is a multi-valued RDN, the outputs from adjoining
+ * AttributeTypeAndValues are separated by a plus ('+' ASCII 43)
+ * character.
+ */
+ if (k2 < max_k2) {
+ STR_APPEND("+");
+ } else if (!last) {
+ STR_APPEND(",");
+ }
+ }
+ while (1);
+
+ result = 0;
+
+ cleanup:
+ _gnutls_free_datum(&td);
+ _gnutls_free_datum(&tvd);
+ return result;
+}
+
+int
+_gnutls_x509_get_dn(asn1_node asn1_struct,
+ const char *asn1_rdn_name, gnutls_datum_t * dn,
+ unsigned flags)
+{
+ gnutls_buffer_st out_str;
+ int i, k1, result;
+
+ _gnutls_buffer_init(&out_str);
+
+ result = asn1_number_of_elements(asn1_struct, asn1_rdn_name, &k1);
+ if (result != ASN1_SUCCESS) {
+ if (result == ASN1_ELEMENT_NOT_FOUND || result == ASN1_VALUE_NOT_FOUND) {
+ result = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ } else {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ }
+ goto cleanup;
+ }
+
+ if (k1 == 0) {
+ gnutls_assert();
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ if (flags & GNUTLS_X509_DN_FLAG_COMPAT) {
+ for (i=0;i<k1;i++) {
+ result = append_elements(asn1_struct, asn1_rdn_name, &out_str, i+1, (i==(k1-1))?1:0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ } else {
+ while (k1 > 0) {
+ result = append_elements(asn1_struct, asn1_rdn_name, &out_str, k1, k1==1?1:0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ k1--;
+ }
+ }
+
+ return _gnutls_buffer_to_datum(&out_str, dn, 1);
+
+ cleanup:
+ _gnutls_buffer_clear(&out_str);
+ return result;
+
+}
+
+
+/* Parses an X509 DN in the asn1_struct, and puts the output into
+ * the string buf. The output is an LDAP encoded DN.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence".
+ * That is to point in the rndSequence.
+ */
+int
+_gnutls_x509_parse_dn(asn1_node asn1_struct,
+ const char *asn1_rdn_name, char *buf,
+ size_t * buf_size, unsigned flags)
+{
+ int ret;
+ gnutls_datum_t dn = {NULL, 0};
+
+ if (buf_size == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (*buf_size > 0 && buf)
+ buf[0] = 0;
+ else
+ *buf_size = 0;
+
+ ret = _gnutls_x509_get_dn(asn1_struct, asn1_rdn_name, &dn, flags);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (dn.size >= (unsigned int) *buf_size) {
+ gnutls_assert();
+ *buf_size = dn.size + 1;
+ ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ goto cleanup;
+ }
+
+ assert(dn.data != NULL);
+
+ if (buf) {
+ memcpy(buf, dn.data, dn.size);
+ buf[dn.size] = 0;
+ *buf_size = dn.size;
+ } else
+ *buf_size = dn.size + 1;
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&dn);
+ return ret;
+}
+
+/* Parses an X509 DN in the asn1_struct, and searches for the
+ * given OID in the DN.
+ *
+ * If raw_flag == 0, the output will be encoded in the LDAP way. (#hex for non printable)
+ * Otherwise the raw DER data are returned.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence".
+ * That is to point in the rndSequence.
+ *
+ * indx specifies which OID to return. Ie 0 means return the first specified
+ * OID found, 1 the second etc.
+ */
+int
+_gnutls_x509_parse_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name,
+ const char *given_oid, int indx,
+ unsigned int raw_flag, gnutls_datum_t * out)
+{
+ int k2, k1, result;
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer2[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ gnutls_datum_t td;
+ uint8_t value[256];
+ char oid[MAX_OID_SIZE];
+ int len;
+ int i = 0;
+
+ k1 = 0;
+ do {
+
+ k1++;
+ /* create a string like "tbsCertList.issuer.rdnSequence.?1"
+ */
+ if (asn1_rdn_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ asn1_rdn_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ }
+
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ k2 = 0;
+
+ do { /* Move to the attribute type and values
+ */
+ k2++;
+
+ if (tmpbuffer1[0] != 0)
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "%s.?%d", tmpbuffer1, k2);
+ else
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "?%d", k2);
+
+ /* Try to read the RelativeDistinguishedName attributes.
+ */
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer2, value,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (strcmp(oid, given_oid) == 0 && indx == i++) { /* Found the OID */
+
+ /* Read the Value
+ */
+ _gnutls_str_cpy(tmpbuffer3,
+ sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3,
+ sizeof(tmpbuffer3),
+ ".value");
+
+ result =
+ _gnutls_x509_read_value(asn1_struct,
+ tmpbuffer3,
+ &td);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (raw_flag != 0) {
+ out->data = td.data;
+ out->size = td.size;
+ return 0;
+
+ } else { /* parse data. raw_flag == 0 */
+ result =
+ _gnutls_x509_dn_to_string(oid,
+ td.
+ data,
+ td.
+ size,
+ out);
+
+ _gnutls_free_datum(&td);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ } /* raw_flag == 0 */
+ }
+ }
+ while (1);
+
+ }
+ while (1);
+
+ gnutls_assert();
+
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ cleanup:
+ return result;
+}
+
+
+/* Parses an X509 DN in the asn1_struct, and returns the requested
+ * DN OID.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer.rdnSequence".
+ * That is to point in the rndSequence.
+ *
+ * indx specifies which OID to return. Ie 0 means return the first specified
+ * OID found, 1 the second etc.
+ */
+int
+_gnutls_x509_get_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name,
+ int indx, void *_oid, size_t * sizeof_oid)
+{
+ int k2, k1, result;
+ char tmpbuffer1[MAX_NAME_SIZE];
+ char tmpbuffer2[MAX_NAME_SIZE];
+ char tmpbuffer3[MAX_NAME_SIZE];
+ char value[256];
+ char oid[MAX_OID_SIZE];
+ int len;
+ int i = 0;
+
+ k1 = 0;
+ do {
+
+ k1++;
+ /* create a string like "tbsCertList.issuer.rdnSequence.?1"
+ */
+ if (asn1_rdn_name[0] != 0)
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "%s.?%d",
+ asn1_rdn_name, k1);
+ else
+ snprintf(tmpbuffer1, sizeof(tmpbuffer1), "?%d",
+ k1);
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer1, value, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ }
+
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ k2 = 0;
+
+ do { /* Move to the attribute type and values
+ */
+ k2++;
+
+ if (tmpbuffer1[0] != 0)
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "%s.?%d", tmpbuffer1, k2);
+ else
+ snprintf(tmpbuffer2, sizeof(tmpbuffer2),
+ "?%d", k2);
+
+ /* Try to read the RelativeDistinguishedName attributes.
+ */
+
+ len = sizeof(value) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer2, value,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+ if (result != ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer3, sizeof(tmpbuffer3),
+ tmpbuffer2);
+ _gnutls_str_cat(tmpbuffer3, sizeof(tmpbuffer3),
+ ".type");
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(asn1_struct, tmpbuffer3, oid,
+ &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ break;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (indx == i++) { /* Found the OID */
+
+ len = strlen(oid) + 1;
+
+ if (*sizeof_oid < (unsigned) len) {
+ *sizeof_oid = len;
+ gnutls_assert();
+ return
+ GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ memcpy(_oid, oid, len);
+ *sizeof_oid = len - 1;
+
+ return 0;
+ }
+ }
+ while (1);
+
+ }
+ while (1);
+
+ gnutls_assert();
+
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ cleanup:
+ return result;
+}
+
+/* This will write the AttributeTypeAndValue field. The data must be already DER encoded.
+ * 'multi' must be (0) if writing an AttributeTypeAndValue, and 1 if Attribute.
+ * In all cases only one value is written.
+ */
+static int
+_gnutls_x509_write_attribute(const char *given_oid,
+ asn1_node asn1_struct, const char *where,
+ const void *_data, int sizeof_data)
+{
+ char tmp[128];
+ int result;
+
+ /* write the data (value)
+ */
+
+ _gnutls_str_cpy(tmp, sizeof(tmp), where);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".value");
+
+ result = asn1_write_value(asn1_struct, tmp, _data, sizeof_data);
+ if (result < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* write the type
+ */
+ _gnutls_str_cpy(tmp, sizeof(tmp), where);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".type");
+
+ result = asn1_write_value(asn1_struct, tmp, given_oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+
+/* Decodes an X.509 Attribute (if multi==1) or an AttributeTypeAndValue
+ * otherwise.
+ *
+ * octet_string should be non-zero if we are to decode octet strings after
+ * decoding.
+ *
+ * The output is allocated and stored in value.
+ */
+int
+_gnutls_x509_decode_and_read_attribute(asn1_node asn1_struct,
+ const char *where, char *oid,
+ int oid_size,
+ gnutls_datum_t * value, int multi,
+ int octet_string)
+{
+ char tmpbuffer[128];
+ int len, result;
+
+ /* Read the OID
+ */
+ _gnutls_str_cpy(tmpbuffer, sizeof(tmpbuffer), where);
+ _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), ".type");
+
+ len = oid_size - 1;
+ result = asn1_read_value(asn1_struct, tmpbuffer, oid, &len);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ return result;
+ }
+
+ /* Read the Value
+ */
+
+ _gnutls_str_cpy(tmpbuffer, sizeof(tmpbuffer), where);
+ _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), ".value");
+
+ if (multi)
+ _gnutls_str_cat(tmpbuffer, sizeof(tmpbuffer), "s.?1"); /* .values.?1 */
+
+ if (octet_string)
+ result =
+ _gnutls_x509_read_string(asn1_struct, tmpbuffer, value,
+ ASN1_ETYPE_OCTET_STRING, 0);
+ else
+ result =
+ _gnutls_x509_read_value(asn1_struct, tmpbuffer, value);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+
+}
+
+/* Sets an X509 DN in the asn1_struct, and puts the given OID in the DN.
+ * The input is assumed to be raw data.
+ *
+ * asn1_rdn_name must be a string in the form "tbsCertificate.issuer".
+ * That is to point before the rndSequence.
+ *
+ */
+int
+_gnutls_x509_set_dn_oid(asn1_node asn1_struct,
+ const char *asn1_name, const char *given_oid,
+ int raw_flag, const char *name, int sizeof_name)
+{
+ int result;
+ char tmp[MAX_NAME_SIZE], asn1_rdn_name[MAX_NAME_SIZE];
+
+ if (sizeof_name == 0 || name == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* create the rdnSequence
+ */
+ result =
+ asn1_write_value(asn1_struct, asn1_name, "rdnSequence", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (asn1_name[0] != 0) {
+ _gnutls_str_cpy(asn1_rdn_name, sizeof(asn1_rdn_name), asn1_name);
+ _gnutls_str_cat(asn1_rdn_name, sizeof(asn1_rdn_name),
+ ".rdnSequence");
+ } else {
+ _gnutls_str_cpy(asn1_rdn_name, sizeof(asn1_rdn_name), "rdnSequence");
+ }
+
+ /* create a new element
+ */
+ result = asn1_write_value(asn1_struct, asn1_rdn_name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ _gnutls_str_cpy(tmp, sizeof(tmp), asn1_rdn_name);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST");
+
+ /* create the set with only one element
+ */
+ result = asn1_write_value(asn1_struct, tmp, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+
+ /* Encode and write the data
+ */
+ _gnutls_str_cpy(tmp, sizeof(tmp), asn1_rdn_name);
+ _gnutls_str_cat(tmp, sizeof(tmp), ".?LAST.?LAST");
+
+ if (!raw_flag) {
+ result =
+ _gnutls_x509_encode_and_write_attribute(given_oid,
+ asn1_struct,
+ tmp, name,
+ sizeof_name,
+ 0);
+ } else {
+ result =
+ _gnutls_x509_write_attribute(given_oid, asn1_struct,
+ tmp, name, sizeof_name);
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_x509_rdn_get:
+ * @idn: should contain a DER encoded RDN sequence
+ * @buf: a pointer to a structure to hold the peer's name
+ * @buf_size: holds the size of @buf
+ *
+ * This function will return the name of the given RDN sequence. The
+ * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in
+ * RFC4514.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_rdn_get2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_rdn_get(const gnutls_datum_t * idn,
+ char *buf, size_t * buf_size)
+{
+ int ret;
+ gnutls_datum_t out;
+
+ ret = gnutls_x509_rdn_get2(idn, &out, GNUTLS_X509_DN_FLAG_COMPAT);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_copy_string(&out, (void*)buf, buf_size);
+ gnutls_free(out.data);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_rdn_get2:
+ * @idn: should contain a DER encoded RDN sequence
+ * @str: a datum that will hold the name
+ * @flags: zero of %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will return the name of the given RDN sequence. The
+ * name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in
+ * RFC4514.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_rdn_get2(const gnutls_datum_t * idn,
+ gnutls_datum_t *str, unsigned flags)
+{
+ int ret;
+ gnutls_x509_dn_t dn;
+
+ ret = gnutls_x509_dn_init(&dn);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_x509_dn_import(dn, idn);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_dn_get_str2(dn, str, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_x509_dn_deinit(dn);
+ return ret;
+}
+
+/**
+ * gnutls_x509_rdn_get_by_oid:
+ * @idn: should contain a DER encoded RDN sequence
+ * @oid: an Object Identifier
+ * @indx: In case multiple same OIDs exist in the RDN indicates which
+ * to send. Use 0 for the first one.
+ * @raw_flag: If non-zero then the raw DER data are returned.
+ * @buf: a pointer to a structure to hold the peer's name
+ * @buf_size: holds the size of @buf
+ *
+ * This function will return the name of the given Object identifier,
+ * of the RDN sequence. The name will be encoded using the rules
+ * from RFC4514.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_rdn_get_by_oid(const gnutls_datum_t * idn, const char *oid,
+ unsigned indx, unsigned int raw_flag,
+ void *buf, size_t * buf_size)
+{
+ int result;
+ asn1_node dn = NULL;
+ gnutls_datum_t td;
+
+ if (buf_size == 0) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Name", &dn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&dn, idn->data, idn->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ /* couldn't decode DER */
+ gnutls_assert();
+ asn1_delete_structure(&dn);
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _gnutls_x509_parse_dn_oid(dn, "rdnSequence", oid, indx,
+ raw_flag, &td);
+
+ asn1_delete_structure(&dn);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ return _gnutls_strdatum_to_buf(&td, buf, buf_size);
+}
+
+/**
+ * gnutls_x509_rdn_get_oid:
+ * @idn: should contain a DER encoded RDN sequence
+ * @indx: Indicates which OID to return. Use 0 for the first one.
+ * @buf: a pointer to a structure to hold the peer's name OID
+ * @buf_size: holds the size of @buf
+ *
+ * This function will return the specified Object identifier, of the
+ * RDN sequence.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned and *@buf_size is
+ * updated if the provided buffer is not long enough, otherwise a
+ * negative error value.
+ *
+ * Since: 2.4.0
+ **/
+int
+gnutls_x509_rdn_get_oid(const gnutls_datum_t * idn,
+ unsigned indx, void *buf, size_t * buf_size)
+{
+ int result;
+ asn1_node dn = NULL;
+
+ if (buf_size == 0) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Name", &dn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&dn, idn->data, idn->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ /* couldn't decode DER */
+ gnutls_assert();
+ asn1_delete_structure(&dn);
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _gnutls_x509_get_dn_oid(dn, "rdnSequence", indx, buf,
+ buf_size);
+
+ asn1_delete_structure(&dn);
+ return result;
+}
+
+/*
+ * Compares the DER encoded part of a DN.
+ *
+ * Returns 1 if the DN's match and (0) if they don't match. Otherwise
+ * a negative error code is returned to indicate error.
+ */
+int
+_gnutls_x509_compare_raw_dn(const gnutls_datum_t * dn1,
+ const gnutls_datum_t * dn2)
+{
+ int ret;
+ gnutls_datum_t str1, str2;
+
+ /* Simple case of completely identical? */
+
+ if (dn1->size == dn2->size) {
+ if (memcmp(dn1->data, dn2->data, dn2->size) == 0) {
+ return 1;
+ }
+ }
+
+ /* RFC5280 (https://tools.ietf.org/html/rfc5280#section-7.1)
+ * requires that the LDAP StringPrep profile and caseIgnoreMatch
+ * must be used for this comparison. We do not use that but
+ * instead we do a simpler comparison that ignores the tags used
+ * such as `UTF8String` and `PrintableString`. */
+
+ if ((dn1->size == 0) || (dn2->size == 0)) {
+ gnutls_assert();
+ return 0;
+ }
+
+ ret = gnutls_x509_rdn_get2(dn1, &str1, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return 0;
+ }
+
+ ret = gnutls_x509_rdn_get2(dn2, &str2, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_free_datum(&str1);
+ return 0;
+ }
+
+ if (str1.size != str2.size) {
+ ret = 0;
+ goto cleanup;
+ }
+ if (memcmp(str1.data, str2.data, str2.size) != 0) {
+ gnutls_assert();
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = 1; /* they match */
+
+cleanup:
+ _gnutls_free_datum(&str1);
+ _gnutls_free_datum(&str2);
+
+ return ret;
+}
diff --git a/lib/x509/email-verify.c b/lib/x509/email-verify.c
new file mode 100644
index 0000000..053e512
--- /dev/null
+++ b/lib/x509/email-verify.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2002 Andrew McDonald
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <str.h>
+#include <x509_int.h>
+#include <common.h>
+#include "errors.h"
+#include <system.h>
+
+/**
+ * gnutls_x509_crt_check_email:
+ * @cert: should contain an gnutls_x509_crt_t type
+ * @email: A null terminated string that contains an email address (RFC822)
+ * @flags: should be zero
+ *
+ * This function will check if the given certificate's subject matches
+ * the given email address.
+ *
+ * Returns: non-zero for a successful match, and zero on failure.
+ **/
+unsigned
+gnutls_x509_crt_check_email(gnutls_x509_crt_t cert,
+ const char *email, unsigned int flags)
+{
+ char rfc822name[MAX_CN];
+ size_t rfc822namesize;
+ int found_rfc822name = 0;
+ int ret = 0;
+ int i = 0;
+ char *a_email;
+ gnutls_datum_t out;
+
+ /* convert the provided email to ACE-Labels domain. */
+ ret = _gnutls_idna_email_map(email, strlen(email), &out);
+ if (ret < 0) {
+ _gnutls_debug_log("unable to convert email %s to IDNA format\n", email);
+ a_email = (char*)email;
+ } else {
+ a_email = (char*)out.data;
+ }
+
+ /* try matching against:
+ * 1) an address as an alternative name (subjectAltName) extension
+ * in the certificate
+ * 2) the EMAIL field in the certificate
+ *
+ * only try (2) if there is no subjectAltName extension of
+ * type RFC822Name, and there is a single EMAIL.
+ */
+
+ /* Check through all included subjectAltName extensions, comparing
+ * against all those of type RFC822Name.
+ */
+ for (i = 0; !(ret < 0); i++) {
+
+ rfc822namesize = sizeof(rfc822name);
+ ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
+ rfc822name,
+ &rfc822namesize,
+ NULL);
+
+ if (ret == GNUTLS_SAN_RFC822NAME) {
+ found_rfc822name = 1;
+
+ if (_gnutls_has_embedded_null(rfc822name, rfc822namesize)) {
+ _gnutls_debug_log("certificate has %s with embedded null in rfc822name\n", rfc822name);
+ continue;
+ }
+
+ if (!_gnutls_str_is_print(rfc822name, rfc822namesize)) {
+ _gnutls_debug_log("invalid (non-ASCII) email in certificate %.*s\n", (int)rfc822namesize, rfc822name);
+ continue;
+ }
+
+ ret = _gnutls_hostname_compare(rfc822name, rfc822namesize, a_email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS);
+ if (ret != 0) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+ }
+
+ if (!found_rfc822name) {
+ /* did not get the necessary extension, use CN instead
+ */
+
+ /* enforce the RFC6125 (§1.8) requirement that only
+ * a single CN must be present */
+ rfc822namesize = sizeof(rfc822name);
+ ret = gnutls_x509_crt_get_dn_by_oid
+ (cert, GNUTLS_OID_PKCS9_EMAIL, 1, 0, rfc822name,
+ &rfc822namesize);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ rfc822namesize = sizeof(rfc822name);
+ ret = gnutls_x509_crt_get_dn_by_oid
+ (cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, rfc822name,
+ &rfc822namesize);
+ if (ret < 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (_gnutls_has_embedded_null(rfc822name, rfc822namesize)) {
+ _gnutls_debug_log("certificate has EMAIL %s with embedded null in name\n", rfc822name);
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!_gnutls_str_is_print(rfc822name, rfc822namesize)) {
+ _gnutls_debug_log("invalid (non-ASCII) email in certificate DN %.*s\n", (int)rfc822namesize, rfc822name);
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = _gnutls_hostname_compare(rfc822name, rfc822namesize, a_email, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS);
+ if (ret != 0) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ /* not found a matching name
+ */
+ ret = 0;
+ cleanup:
+ if (a_email != email) {
+ gnutls_free(a_email);
+ }
+ return ret;
+}
diff --git a/lib/x509/extensions.c b/lib/x509/extensions.c
new file mode 100644
index 0000000..dc333f4
--- /dev/null
+++ b/lib/x509/extensions.c
@@ -0,0 +1,911 @@
+/*
+ * Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate to the X.509 extension parsing.
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <global.h>
+#include <libtasn1.h>
+#include <common.h>
+#include <gnutls/x509-ext.h>
+#include <gnutls/x509.h>
+#include <x509_int.h>
+#include <datum.h>
+
+int
+_gnutls_get_extension(asn1_node asn, const char *root,
+ const char *extension_id, int indx,
+ gnutls_datum_t * ret, unsigned int *_critical)
+{
+ int k, result, len;
+ char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
+ char str_critical[10];
+ int critical = 0;
+ char extnID[MAX_OID_SIZE];
+ gnutls_datum_t value;
+ int indx_counter = 0;
+
+ ret->data = NULL;
+ ret->size = 0;
+
+ k = 0;
+ do {
+ k++;
+
+ snprintf(name, sizeof(name), "%s.?%d", root, k);
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".extnID");
+
+ len = sizeof(extnID) - 1;
+ result = asn1_read_value(asn, name2, extnID, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Handle Extension
+ */
+ if (strcmp(extnID, extension_id) == 0
+ && indx == indx_counter++) {
+ /* extension was found
+ */
+
+ /* read the critical status.
+ */
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".critical");
+
+ len = sizeof(str_critical);
+ result =
+ asn1_read_value(asn, name2,
+ str_critical, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (str_critical[0] == 'T')
+ critical = 1;
+ else
+ critical = 0;
+
+ /* read the value.
+ */
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2),
+ ".extnValue");
+
+ result =
+ _gnutls_x509_read_value(asn, name2, &value);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ ret->data = value.data;
+ ret->size = value.size;
+
+ if (_critical)
+ *_critical = critical;
+
+ return 0;
+ }
+ }
+ while (1);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ } else {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+}
+
+static int
+get_indx_extension(asn1_node asn, const char *root,
+ int indx, gnutls_datum_t * out)
+{
+ char name[MAX_NAME_SIZE];
+ int ret;
+
+ out->data = NULL;
+ out->size = 0;
+
+ snprintf(name, sizeof(name), "%s.?%d.extnValue", root, indx+1);
+
+ ret = _gnutls_x509_read_value(asn, name, out);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int
+_gnutls_x509_crt_get_extension(gnutls_x509_crt_t cert,
+ const char *extension_id, int indx,
+ gnutls_datum_t * data, unsigned int *critical)
+{
+ return _gnutls_get_extension(cert->cert, "tbsCertificate.extensions",
+ extension_id, indx, data, critical);
+}
+
+/**
+ * gnutls_x509_crt_get_extension_data2:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: Specifies which extension OID to read. Use (0) to get the first one.
+ * @data: will contain the extension DER-encoded data
+ *
+ * This function will return the requested by the index extension data in the
+ * certificate. The extension data will be allocated using
+ * gnutls_malloc().
+ *
+ * Use gnutls_x509_crt_get_extension_info() to extract the OID.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If you have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ **/
+int
+gnutls_x509_crt_get_extension_data2(gnutls_x509_crt_t cert,
+ unsigned indx,
+ gnutls_datum_t * data)
+{
+ return get_indx_extension(cert->cert, "tbsCertificate.extensions",
+ indx, data);
+}
+
+int
+_gnutls_x509_crl_get_extension(gnutls_x509_crl_t crl,
+ const char *extension_id, int indx,
+ gnutls_datum_t * data,
+ unsigned int *critical)
+{
+ return _gnutls_get_extension(crl->crl, "tbsCertList.crlExtensions",
+ extension_id, indx, data, critical);
+}
+
+/**
+ * gnutls_x509_crl_get_extension_data2:
+ * @crl: should contain a #gnutls_x509_crl_t type
+ * @indx: Specifies which extension OID to read. Use (0) to get the first one.
+ * @data: will contain the extension DER-encoded data
+ *
+ * This function will return the requested by the index extension data in the
+ * certificate revocation list. The extension data will be allocated using
+ * gnutls_malloc().
+ *
+ * Use gnutls_x509_crt_get_extension_info() to extract the OID.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If you have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ **/
+int
+gnutls_x509_crl_get_extension_data2(gnutls_x509_crl_t crl,
+ unsigned indx,
+ gnutls_datum_t * data)
+{
+ return get_indx_extension(crl->crl, "tbsCertList.crlExtensions",
+ indx, data);
+}
+
+/* This function will attempt to return the requested extension OID found in
+ * the given X509v3 certificate.
+ *
+ * If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ * be returned.
+ */
+static int get_extension_oid(asn1_node asn, const char *root,
+ unsigned indx, void *oid, size_t * sizeof_oid)
+{
+ int k, result, len;
+ char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
+ char extnID[MAX_OID_SIZE];
+ unsigned indx_counter = 0;
+
+ k = 0;
+ do {
+ k++;
+
+ snprintf(name, sizeof(name), "%s.?%d", root, k);
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".extnID");
+
+ len = sizeof(extnID) - 1;
+ result = asn1_read_value(asn, name2, extnID, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Handle Extension
+ */
+ if (indx == indx_counter++) {
+ len = strlen(extnID) + 1;
+
+ if (*sizeof_oid < (unsigned) len) {
+ *sizeof_oid = len;
+ gnutls_assert();
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ memcpy(oid, extnID, len);
+ *sizeof_oid = len - 1;
+
+ return 0;
+ }
+
+
+ }
+ while (1);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ } else {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+}
+
+/* This function will attempt to return the requested extension OID found in
+ * the given X509v3 certificate.
+ *
+ * If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ * be returned.
+ */
+int
+_gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert,
+ int indx, void *oid,
+ size_t * sizeof_oid)
+{
+ return get_extension_oid(cert->cert, "tbsCertificate.extensions",
+ indx, oid, sizeof_oid);
+}
+
+int
+_gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl,
+ int indx, void *oid,
+ size_t * sizeof_oid)
+{
+ return get_extension_oid(crl->crl, "tbsCertList.crlExtensions",
+ indx, oid, sizeof_oid);
+}
+
+/* This function will attempt to set the requested extension in
+ * the given X509v3 certificate.
+ *
+ * Critical will be either 0 or 1.
+ */
+static int
+add_extension(asn1_node asn, const char *root, const char *extension_id,
+ const gnutls_datum_t * ext_data, unsigned int critical)
+{
+ int result;
+ const char *str;
+ char name[MAX_NAME_SIZE];
+
+ snprintf(name, sizeof(name), "%s", root);
+
+ /* Add a new extension in the list.
+ */
+ result = asn1_write_value(asn, name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (root[0] != 0)
+ snprintf(name, sizeof(name), "%s.?LAST.extnID", root);
+ else
+ snprintf(name, sizeof(name), "?LAST.extnID");
+
+ result = asn1_write_value(asn, name, extension_id, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (critical == 0)
+ str = "FALSE";
+ else
+ str = "TRUE";
+
+ if (root[0] != 0)
+ snprintf(name, sizeof(name), "%s.?LAST.critical", root);
+ else
+ snprintf(name, sizeof(name), "?LAST.critical");
+
+ result = asn1_write_value(asn, name, str, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (root[0] != 0)
+ snprintf(name, sizeof(name), "%s.?LAST.extnValue", root);
+ else
+ snprintf(name, sizeof(name), "?LAST.extnValue");
+
+ result = _gnutls_x509_write_value(asn, name, ext_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/* Overwrite the given extension (using the index)
+ * index here starts from one.
+ */
+static int
+overwrite_extension(asn1_node asn, const char *root, unsigned int indx,
+ const gnutls_datum_t * ext_data, unsigned int critical)
+{
+ char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
+ const char *str;
+ int result;
+
+ if (root[0] != 0)
+ snprintf(name, sizeof(name), "%s.?%u", root, indx);
+ else
+ snprintf(name, sizeof(name), "?%u", indx);
+
+ if (critical == 0)
+ str = "FALSE";
+ else
+ str = "TRUE";
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".critical");
+
+ result = asn1_write_value(asn, name2, str, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".extnValue");
+
+ result = _gnutls_x509_write_value(asn, name2, ext_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+int
+_gnutls_set_extension(asn1_node asn, const char *root,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data, unsigned int critical)
+{
+ int result = 0;
+ int k, len;
+ char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE];
+ char extnID[MAX_OID_SIZE];
+
+ /* Find the index of the given extension.
+ */
+ k = 0;
+ do {
+ k++;
+
+ if (root[0] != 0)
+ snprintf(name, sizeof(name), "%s.?%d", root, k);
+ else
+ snprintf(name, sizeof(name), "?%d", k);
+
+ len = sizeof(extnID) - 1;
+ result = asn1_read_value(asn, name, extnID, &len);
+
+ /* move to next
+ */
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+
+ do {
+
+ _gnutls_str_cpy(name2, sizeof(name2), name);
+ _gnutls_str_cat(name2, sizeof(name2), ".extnID");
+
+ len = sizeof(extnID) - 1;
+ result = asn1_read_value(asn, name2, extnID, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ break;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Handle Extension
+ */
+ if (strcmp(extnID, ext_id) == 0) {
+ /* extension was found
+ */
+ return overwrite_extension(asn, root, k,
+ ext_data,
+ critical);
+ }
+
+
+ }
+ while (0);
+ }
+ while (1);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return add_extension(asn, root, ext_id, ext_data,
+ critical);
+ } else {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+
+ return 0;
+}
+
+/* This function will attempt to overwrite the requested extension with
+ * the given one.
+ *
+ * Critical will be either 0 or 1.
+ */
+int
+_gnutls_x509_crt_set_extension(gnutls_x509_crt_t cert,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data,
+ unsigned int critical)
+{
+ MODIFIED(cert);
+ cert->use_extensions = 1;
+
+ return _gnutls_set_extension(cert->cert, "tbsCertificate.extensions",
+ ext_id, ext_data, critical);
+}
+
+int
+_gnutls_x509_crl_set_extension(gnutls_x509_crl_t crl,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data,
+ unsigned int critical)
+{
+ return _gnutls_set_extension(crl->crl, "tbsCertList.crlExtensions", ext_id,
+ ext_data, critical);
+}
+
+int
+_gnutls_x509_crq_set_extension(gnutls_x509_crq_t crq,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data,
+ unsigned int critical)
+{
+ unsigned char *extensions = NULL;
+ size_t extensions_size = 0;
+ gnutls_datum_t der;
+ asn1_node c2;
+ int result;
+
+ result =
+ gnutls_x509_crq_get_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ 0, NULL,
+ &extensions_size);
+ if (result == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ extensions = gnutls_malloc(extensions_size);
+ if (extensions == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = gnutls_x509_crq_get_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ 0,
+ extensions,
+ &extensions_size);
+ }
+ if (result < 0) {
+ if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ extensions_size = 0;
+ } else {
+ gnutls_assert();
+ gnutls_free(extensions);
+ return result;
+ }
+ }
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extensions",
+ &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(extensions);
+ return _gnutls_asn2err(result);
+ }
+
+ if (extensions_size > 0) {
+ result =
+ _asn1_strict_der_decode(&c2, extensions, extensions_size,
+ NULL);
+ gnutls_free(extensions);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+ }
+
+ result = _gnutls_set_extension(c2, "", ext_id, ext_data, critical);
+ if (result < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return result;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", &der, 0);
+
+ asn1_delete_structure(&c2);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ gnutls_x509_crq_set_attribute_by_oid(crq,
+ "1.2.840.113549.1.9.14",
+ der.data, der.size);
+ gnutls_free(der.data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+
+ return 0;
+}
+
+/* extract an INTEGER from the DER encoded extension
+ */
+int
+_gnutls_x509_ext_extract_number(uint8_t * number,
+ size_t * _nr_size,
+ uint8_t * extnValue, int extnValueLen)
+{
+ asn1_node ext = NULL;
+ int result;
+ int nr_size = *_nr_size;
+
+ /* here it doesn't matter so much that we use CertificateSerialNumber. It is equal
+ * to using INTEGER.
+ */
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.CertificateSerialNumber",
+ &ext)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&ext, extnValue, extnValueLen, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&ext);
+ return _gnutls_asn2err(result);
+ }
+
+ /* the default value of cA is false.
+ */
+ result = asn1_read_value(ext, "", number, &nr_size);
+ if (result != ASN1_SUCCESS)
+ result = _gnutls_asn2err(result);
+ else
+ result = 0;
+
+ *_nr_size = nr_size;
+
+ asn1_delete_structure(&ext);
+
+ return result;
+}
+
+/* generate an INTEGER in a DER encoded extension
+ */
+int
+_gnutls_x509_ext_gen_number(const uint8_t * number, size_t nr_size,
+ gnutls_datum_t * der_ext)
+{
+ asn1_node ext = NULL;
+ int result;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.CertificateSerialNumber", &ext);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value(ext, "", number, nr_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&ext);
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_der_encode(ext, "", der_ext, 0);
+
+ asn1_delete_structure(&ext);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+int
+_gnutls_write_general_name(asn1_node ext, const char *ext_name,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data, unsigned int data_size)
+{
+ const char *str;
+ int result;
+ char name[128];
+
+ if (data == NULL) {
+ if (data_size == 0)
+ data = (void*)"";
+ else
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ switch (type) {
+ case GNUTLS_SAN_DNSNAME:
+ str = "dNSName";
+ break;
+ case GNUTLS_SAN_RFC822NAME:
+ str = "rfc822Name";
+ break;
+ case GNUTLS_SAN_URI:
+ str = "uniformResourceIdentifier";
+ break;
+ case GNUTLS_SAN_IPADDRESS:
+ str = "iPAddress";
+ break;
+ case GNUTLS_SAN_REGISTERED_ID:
+ str = "registeredID";
+ break;
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ result = asn1_write_value(ext, ext_name, str, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.%s", ext_name, str);
+
+ result = asn1_write_value(ext, name, data, data_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&ext);
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+int
+_gnutls_write_new_general_name(asn1_node ext, const char *ext_name,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data, unsigned int data_size)
+{
+ int result;
+ char name[128];
+
+ result = asn1_write_value(ext, ext_name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (ext_name[0] == 0) { /* no dot */
+ _gnutls_str_cpy(name, sizeof(name), "?LAST");
+ } else {
+ _gnutls_str_cpy(name, sizeof(name), ext_name);
+ _gnutls_str_cat(name, sizeof(name), ".?LAST");
+ }
+
+ result = _gnutls_write_general_name(ext, name, type,
+ data, data_size);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+int
+_gnutls_write_new_othername(asn1_node ext, const char *ext_name,
+ const char *oid,
+ const void *data, unsigned int data_size)
+{
+ int result;
+ char name[128];
+ char name2[128];
+
+ result = asn1_write_value(ext, ext_name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (ext_name[0] == 0) { /* no dot */
+ _gnutls_str_cpy(name, sizeof(name), "?LAST");
+ } else {
+ _gnutls_str_cpy(name, sizeof(name), ext_name);
+ _gnutls_str_cat(name, sizeof(name), ".?LAST");
+ }
+
+ result = asn1_write_value(ext, name, "otherName", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name2, sizeof(name2), "%s.otherName.type-id", name);
+
+ result = asn1_write_value(ext, name2, oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&ext);
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name2, sizeof(name2), "%s.otherName.value", name);
+
+ result = asn1_write_value(ext, name2, data, data_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&ext);
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/* Convert the given name to GeneralNames in a DER encoded extension.
+ * This is the same as subject alternative name.
+ */
+int
+_gnutls_x509_ext_gen_subject_alt_name(gnutls_x509_subject_alt_name_t
+ type,
+ const char *othername_oid,
+ const void *data,
+ unsigned int data_size,
+ const gnutls_datum_t * prev_der_ext,
+ gnutls_datum_t * der_ext)
+{
+ int ret;
+ gnutls_subject_alt_names_t sans = NULL;
+ gnutls_datum_t name;
+
+ ret = gnutls_subject_alt_names_init(&sans);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (prev_der_ext && prev_der_ext->data != NULL &&
+ prev_der_ext->size != 0) {
+
+ ret = gnutls_x509_ext_import_subject_alt_names(prev_der_ext, sans, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ name.data = (void*)data;
+ name.size = data_size;
+ ret = gnutls_subject_alt_names_set(sans, type, &name, othername_oid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_export_subject_alt_names(sans, der_ext);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ if (sans != NULL)
+ gnutls_subject_alt_names_deinit(sans);
+
+ return ret;
+}
+
+/* generate the AuthorityKeyID in a DER encoded extension
+ */
+int
+_gnutls_x509_ext_gen_auth_key_id(const void *id, size_t id_size,
+ gnutls_datum_t * der_ext)
+{
+ gnutls_x509_aki_t aki;
+ int ret;
+ gnutls_datum_t l_id;
+
+ ret = gnutls_x509_aki_init(&aki);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ l_id.data = (void*)id;
+ l_id.size = id_size;
+ ret = gnutls_x509_aki_set_id(aki, &l_id);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_export_authority_key_id(aki, der_ext);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_x509_aki_deinit(aki);
+ return ret;
+}
diff --git a/lib/x509/hostname-verify.c b/lib/x509/hostname-verify.c
new file mode 100644
index 0000000..6ef8ba0
--- /dev/null
+++ b/lib/x509/hostname-verify.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ * Copyright (C) 2002 Andrew McDonald
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <str.h>
+#include <x509_int.h>
+#include <common.h>
+#include "errors.h"
+#include <system.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/**
+ * gnutls_x509_crt_check_hostname:
+ * @cert: should contain an gnutls_x509_crt_t type
+ * @hostname: A null terminated string that contains a DNS name
+ *
+ * This function will check if the given certificate's subject matches
+ * the given hostname. This is a basic implementation of the matching
+ * described in RFC6125, and takes into account wildcards,
+ * and the DNSName/IPAddress subject alternative name PKIX extension.
+ *
+ * For details see also gnutls_x509_crt_check_hostname2().
+ *
+ * Returns: non-zero for a successful match, and zero on failure.
+ **/
+unsigned
+gnutls_x509_crt_check_hostname(gnutls_x509_crt_t cert,
+ const char *hostname)
+{
+ return gnutls_x509_crt_check_hostname2(cert, hostname, 0);
+}
+
+static int
+check_ip(gnutls_x509_crt_t cert, const void *ip, unsigned ip_size)
+{
+ char temp[16];
+ size_t temp_size;
+ unsigned i;
+ int ret = 0;
+
+ /* try matching against:
+ * 1) a IPaddress alternative name (subjectAltName) extension
+ * in the certificate
+ */
+
+ /* Check through all included subjectAltName extensions, comparing
+ * against all those of type IPAddress.
+ */
+ for (i = 0; !(ret < 0); i++) {
+ temp_size = sizeof(temp);
+ ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
+ temp,
+ &temp_size,
+ NULL);
+
+ if (ret == GNUTLS_SAN_IPADDRESS) {
+ if (temp_size == ip_size && memcmp(temp, ip, ip_size) == 0)
+ return 1;
+ } else if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ ret = 0;
+ }
+ }
+
+ /* not found a matching IP
+ */
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_check_ip:
+ * @cert: should contain an gnutls_x509_crt_t type
+ * @ip: A pointer to the raw IP address
+ * @ip_size: the number of bytes in ip (4 or 16)
+ * @flags: should be zero
+ *
+ * This function will check if the IP allowed IP addresses in
+ * the certificate's subject alternative name match the provided
+ * IP address.
+ *
+ * Returns: non-zero for a successful match, and zero on failure.
+ **/
+unsigned
+gnutls_x509_crt_check_ip(gnutls_x509_crt_t cert,
+ const unsigned char *ip, unsigned int ip_size,
+ unsigned int flags)
+{
+ return check_ip(cert, ip, ip_size);
+}
+
+/* whether gnutls_x509_crt_check_hostname2() will consider these
+ * alternative name types. This is to satisfy RFC6125 requirement
+ * that we do not fallback to CN-ID if we encounter a supported name
+ * type.
+ */
+#define IS_SAN_SUPPORTED(san) (san==GNUTLS_SAN_DNSNAME||san==GNUTLS_SAN_IPADDRESS)
+
+/**
+ * gnutls_x509_crt_check_hostname2:
+ * @cert: should contain an gnutls_x509_crt_t type
+ * @hostname: A null terminated string that contains a DNS name
+ * @flags: gnutls_certificate_verify_flags
+ *
+ * This function will check if the given certificate's subject matches
+ * the given hostname. This is a basic implementation of the matching
+ * described in RFC6125, and takes into account wildcards,
+ * and the DNSName/IPAddress subject alternative name PKIX extension.
+ *
+ * IPv4 addresses are accepted by this function in the dotted-decimal
+ * format (e.g, ddd.ddd.ddd.ddd), and IPv6 addresses in the hexadecimal
+ * x:x:x:x:x:x:x:x format. For them the IPAddress subject alternative
+ * name extension is consulted. Previous versions to 3.6.0 of GnuTLS
+ * in case of a non-match would consult (in a non-standard extension)
+ * the DNSname and CN fields. This is no longer the case.
+ *
+ * When the flag %GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS is specified no
+ * wildcards are considered. Otherwise they are only considered if the
+ * domain name consists of three components or more, and the wildcard
+ * starts at the leftmost position.
+
+ * When the flag %GNUTLS_VERIFY_DO_NOT_ALLOW_IP_MATCHES is specified,
+ * the input will be treated as a DNS name, and matching of textual IP addresses
+ * against the IPAddress part of the alternative name will not be allowed.
+ *
+ * The function gnutls_x509_crt_check_ip() is available for matching
+ * IP addresses.
+ *
+ * Returns: non-zero for a successful match, and zero on failure.
+ *
+ * Since: 3.3.0
+ **/
+unsigned
+gnutls_x509_crt_check_hostname2(gnutls_x509_crt_t cert,
+ const char *hostname, unsigned int flags)
+{
+ char dnsname[MAX_CN];
+ size_t dnsnamesize;
+ int found_dnsname = 0;
+ int ret = 0;
+ int i = 0;
+ struct in_addr ipv4;
+ char *p = NULL;
+ char *a_hostname;
+ unsigned have_other_addresses = 0;
+ gnutls_datum_t out;
+
+ /* check whether @hostname is an ip address */
+ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_IP_MATCHES) &&
+ ((p=strchr(hostname, ':')) != NULL || inet_pton(AF_INET, hostname, &ipv4) != 0)) {
+
+ if (p != NULL) {
+ struct in6_addr ipv6;
+
+ ret = inet_pton(AF_INET6, hostname, &ipv6);
+ if (ret == 0) {
+ gnutls_assert();
+ goto hostname_fallback;
+ }
+ ret = check_ip(cert, &ipv6, 16);
+ } else {
+ ret = check_ip(cert, &ipv4, 4);
+ }
+
+ /* Prior to 3.6.0 we were accepting misconfigured servers, that place their IP
+ * in the DNS field of subjectAlternativeName. That is no longer the case. */
+ return ret;
+ }
+
+ hostname_fallback:
+ /* convert the provided hostname to ACE-Labels domain. */
+ ret = gnutls_idna_map (hostname, strlen(hostname), &out, 0);
+ if (ret < 0) {
+ _gnutls_debug_log("unable to convert hostname %s to IDNA format\n", hostname);
+ a_hostname = (char*)hostname;
+ } else {
+ a_hostname = (char*)out.data;
+ }
+
+ /* try matching against:
+ * 1) a DNS name as an alternative name (subjectAltName) extension
+ * in the certificate
+ * 2) the common name (CN) in the certificate, if the certificate is acceptable for TLS_WWW_SERVER purpose
+ *
+ * either of these may be of the form: *.domain.tld
+ *
+ * only try (2) if there is no subjectAltName extension of
+ * type dNSName, and there is a single CN.
+ */
+
+ /* Check through all included subjectAltName extensions, comparing
+ * against all those of type dNSName.
+ */
+ for (i = 0; !(ret < 0); i++) {
+
+ dnsnamesize = sizeof(dnsname);
+ ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
+ dnsname,
+ &dnsnamesize,
+ NULL);
+
+ if (ret == GNUTLS_SAN_DNSNAME) {
+ found_dnsname = 1;
+
+ if (_gnutls_has_embedded_null(dnsname, dnsnamesize)) {
+ _gnutls_debug_log("certificate has %s with embedded null in name\n", dnsname);
+ continue;
+ }
+
+ if (!_gnutls_str_is_print(dnsname, dnsnamesize)) {
+ _gnutls_debug_log("invalid (non-ASCII) name in certificate %.*s\n", (int)dnsnamesize, dnsname);
+ continue;
+ }
+
+ ret = _gnutls_hostname_compare(dnsname, dnsnamesize, a_hostname, flags);
+ if (ret != 0) {
+ ret = 1;
+ goto cleanup;
+ }
+ } else {
+ if (IS_SAN_SUPPORTED(ret))
+ have_other_addresses = 1;
+ }
+ }
+
+ if (!have_other_addresses && !found_dnsname && _gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0) {
+ /* did not get the necessary extension, use CN instead, if the
+ * certificate would have been acceptable for a TLS WWW server purpose.
+ * That is because only for that purpose the CN is a valid field to
+ * store the hostname.
+ */
+
+ /* enforce the RFC6125 (§1.8) requirement that only
+ * a single CN must be present */
+ dnsnamesize = sizeof(dnsname);
+ ret = gnutls_x509_crt_get_dn_by_oid
+ (cert, OID_X520_COMMON_NAME, 1, 0, dnsname,
+ &dnsnamesize);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ dnsnamesize = sizeof(dnsname);
+ ret = gnutls_x509_crt_get_dn_by_oid
+ (cert, OID_X520_COMMON_NAME, 0, 0, dnsname,
+ &dnsnamesize);
+ if (ret < 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (_gnutls_has_embedded_null(dnsname, dnsnamesize)) {
+ _gnutls_debug_log("certificate has CN %s with embedded null in name\n", dnsname);
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!_gnutls_str_is_print(dnsname, dnsnamesize)) {
+ _gnutls_debug_log("invalid (non-ASCII) name in certificate CN %.*s\n", (int)dnsnamesize, dnsname);
+ ret = 0;
+ goto cleanup;
+ }
+
+ ret = _gnutls_hostname_compare(dnsname, dnsnamesize, a_hostname, flags);
+ if (ret != 0) {
+ ret = 1;
+ goto cleanup;
+ }
+ }
+
+ /* not found a matching name
+ */
+ ret = 0;
+ cleanup:
+ if (a_hostname != hostname) {
+ gnutls_free(a_hostname);
+ }
+ return ret;
+}
diff --git a/lib/x509/ip-in-cidr.h b/lib/x509/ip-in-cidr.h
new file mode 100644
index 0000000..d5e26eb
--- /dev/null
+++ b/lib/x509/ip-in-cidr.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors: Nikos Mavrogiannopoulos, Daiki Ueno, Martin Ukrop
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_IP_IN_CIDR_H
+#define GNUTLS_LIB_X509_IP_IN_CIDR_H
+
+/*-
+ * ip_in_cidr:
+ * @ip: IP datum (IPv4 or IPv6)
+ * @cidr: CIDR datum (IPv4 or IPv6)
+ *
+ * Check if @ip lies in the given @cidr range.
+ * The @ip version must match the @cidr version (v4/v6),
+ * (this is not checked).
+ *
+ * Returns: 1 if @ip lies within @cidr, 0 otherwise
+ -*/
+static unsigned ip_in_cidr(const gnutls_datum_t *ip, const gnutls_datum_t *cidr)
+{
+ unsigned byte;
+#ifndef BUILD_IN_TESTS
+ char str_ip[48];
+ char str_cidr[97];
+
+ _gnutls_hard_log("matching %.*s with CIDR constraint %.*s\n",
+ (int) sizeof(str_ip),
+ _gnutls_ip_to_string(ip->data, ip->size, str_ip, sizeof(str_ip)),
+ (int) sizeof(str_cidr),
+ _gnutls_cidr_to_string(cidr->data, cidr->size, str_cidr, sizeof(str_cidr)));
+#endif
+ for (byte = 0; byte < ip->size; byte++)
+ if (((ip->data[byte] ^ cidr->data[byte]) & cidr->data[ip->size+byte]) != 0)
+ return 0;
+
+ return 1; /* match */
+}
+
+#endif /* GNUTLS_LIB_X509_IP_IN_CIDR_H */
diff --git a/lib/x509/ip.c b/lib/x509/ip.c
new file mode 100644
index 0000000..c4cb008
--- /dev/null
+++ b/lib/x509/ip.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2007-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "ip.h"
+#include <gnutls/x509.h>
+#include <arpa/inet.h>
+
+/*-
+ * _gnutls_mask_to_prefix:
+ * @mask: CIDR mask
+ * @mask_size: number of bytes in @mask
+ *
+ * Check for mask validity (form of 1*0*) and return its prefix numerically.
+ *
+ * Returns: Length of 1-prefix (0 -- mask_size*8), -1 in case of invalid mask
+ -*/
+int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size)
+{
+ unsigned i, prefix_length = 0;
+ for (i=0; i<mask_size; i++) {
+ if (mask[i] == 0xFF) {
+ prefix_length += 8;
+ } else {
+ switch(mask[i]) {
+ case 0xFE: prefix_length += 7; break;
+ case 0xFC: prefix_length += 6; break;
+ case 0xF8: prefix_length += 5; break;
+ case 0xF0: prefix_length += 4; break;
+ case 0xE0: prefix_length += 3; break;
+ case 0xC0: prefix_length += 2; break;
+ case 0x80: prefix_length += 1; break;
+ case 0x00: break;
+ default:
+ return -1;
+ }
+ break;
+ }
+ }
+ i++;
+ // mask is invalid, if there follows something else than 0x00
+ for ( ; i<mask_size; i++) {
+ if (mask[i] != 0)
+ return -1;
+ }
+ return prefix_length;
+}
+
+/*-
+ * _gnutls_ip_to_string:
+ * @_ip: IP address (RFC5280 format)
+ * @ip_size: Size of @_ip (4 or 16)
+ * @out: Resulting string
+ * @out_size: Size of @out
+ *
+ * Transform IP address into human-readable string.
+ * @string must be already allocated and
+ * at least 16/48 bytes for IPv4/v6 address respectively.
+ *
+ * Returns: Address of result string.
+ -*/
+const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size)
+{
+
+ if (ip_size != 4 && ip_size != 16) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ if (ip_size == 4 && out_size < 16) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ if (ip_size == 16 && out_size < 48) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ if (ip_size == 4)
+ return inet_ntop(AF_INET, _ip, out, out_size);
+ else
+ return inet_ntop(AF_INET6, _ip, out, out_size);
+}
+
+/*-
+ * _gnutls_cidr_to_string:
+ * @_ip: CIDR range (RFC5280 format)
+ * @ip_size: Size of @_ip (8 or 32)
+ * @out: Resulting string
+ * @out_size: Size of @out
+ *
+ * Transform CIDR IP address range into human-readable string.
+ * The input @_ip must be in RFC5280 format (IP address in network byte
+ * order, followed by its network mask which is 4 bytes in IPv4 and
+ * 16 bytes in IPv6). @string must be already allocated and
+ * at least 33/97 bytes for IPv4/v6 address respectively.
+ *
+ * Returns: Address of result string.
+ -*/
+const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size)
+{
+ const unsigned char *ip = _ip;
+ char tmp[64];
+ const char *p;
+
+ if (ip_size != 8 && ip_size != 32) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ if (ip_size == 8) {
+ p = inet_ntop(AF_INET, ip, tmp, sizeof(tmp));
+
+ if (p)
+ snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+4, 4));
+ } else {
+ p = inet_ntop(AF_INET6, ip, tmp, sizeof(tmp));
+
+ if (p)
+ snprintf(out, out_size, "%s/%d", tmp, _gnutls_mask_to_prefix(ip+16, 16));
+ }
+
+ if (p == NULL)
+ return NULL;
+
+ return out;
+}
+
+static void prefix_to_mask(unsigned prefix, unsigned char *mask, size_t mask_size)
+{
+ int i;
+ unsigned j;
+ memset(mask, 0, mask_size);
+
+ for (i = prefix, j = 0; i > 0 && j < mask_size; i -= 8, j++) {
+ if (i >= 8) {
+ mask[j] = 0xff;
+ } else {
+ mask[j] = (unsigned long)(0xffU << (8 - i));
+ }
+ }
+}
+
+/*-
+ * _gnutls_mask_ip:
+ * @ip: IP of @ipsize bytes
+ * @mask: netmask of @ipsize bytes
+ * @ipsize: IP length (4 or 16)
+ *
+ * Mask given IP in place according to the given mask.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ -*/
+int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize)
+{
+ unsigned i;
+
+ if (ipsize != 4 && ipsize != 16)
+ return GNUTLS_E_MALFORMED_CIDR;
+ for (i = 0; i < ipsize; i++)
+ ip[i] &= mask[i];
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_x509_cidr_to_rfc5280:
+ * @cidr: CIDR in RFC4632 format (IP/prefix), null-terminated
+ * @cidr_rfc5280: CIDR range converted to RFC5280 format
+ *
+ * This function will convert text CIDR range with prefix (such as '10.0.0.0/8')
+ * to RFC5280 (IP address in network byte order followed by its network mask).
+ * Works for both IPv4 and IPv6.
+ *
+ * The resulting object is directly usable for IP name constraints usage,
+ * for example in functions %gnutls_x509_name_constraints_add_permitted
+ * or %gnutls_x509_name_constraints_add_excluded.
+ *
+ * The data in datum needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.5.4
+ */
+int gnutls_x509_cidr_to_rfc5280(const char *cidr, gnutls_datum_t *cidr_rfc5280)
+{
+ unsigned iplength, prefix;
+ int ret;
+ char *p;
+ char *p_end = NULL;
+ char *cidr_tmp;
+
+ p = strchr(cidr, '/');
+ if (p != NULL) {
+ prefix = strtol(p+1, &p_end, 10);
+ if (prefix == 0 && p_end == p+1) {
+ _gnutls_debug_log("Cannot parse prefix given in CIDR %s\n", cidr);
+ gnutls_assert();
+ return GNUTLS_E_MALFORMED_CIDR;
+ }
+ unsigned length = p-cidr+1;
+ cidr_tmp = gnutls_malloc(length);
+ if (cidr_tmp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ memcpy(cidr_tmp, cidr, length);
+ cidr_tmp[length-1] = 0;
+ } else {
+ _gnutls_debug_log("No prefix given in CIDR %s\n", cidr);
+ gnutls_assert();
+ return GNUTLS_E_MALFORMED_CIDR;
+ }
+
+ if (strchr(cidr, ':') != 0) { /* IPv6 */
+ iplength = 16;
+ } else { /* IPv4 */
+ iplength = 4;
+ }
+ cidr_rfc5280->size = 2*iplength;
+
+ if (prefix > iplength*8) {
+ _gnutls_debug_log("Invalid prefix given in CIDR %s (%d)\n", cidr, prefix);
+ ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
+ goto cleanup;
+ }
+
+ cidr_rfc5280->data = gnutls_malloc(cidr_rfc5280->size);
+ if (cidr_rfc5280->data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ ret = inet_pton(iplength == 4 ? AF_INET : AF_INET6, cidr_tmp, cidr_rfc5280->data);
+ if (ret == 0) {
+ _gnutls_debug_log("Cannot parse IP from CIDR %s\n", cidr_tmp);
+ ret = gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
+ goto cleanup;
+ }
+
+ prefix_to_mask(prefix, &cidr_rfc5280->data[iplength], iplength);
+ _gnutls_mask_ip(cidr_rfc5280->data, &cidr_rfc5280->data[iplength], iplength);
+
+ ret = GNUTLS_E_SUCCESS;
+
+cleanup:
+ gnutls_free(cidr_tmp);
+ return ret;
+}
diff --git a/lib/x509/ip.h b/lib/x509/ip.h
new file mode 100644
index 0000000..2c8569b
--- /dev/null
+++ b/lib/x509/ip.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Martin Ukrop
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_IP_H
+#define GNUTLS_LIB_X509_IP_H
+
+// for documentation, see the definition
+int _gnutls_mask_to_prefix(const unsigned char *mask, unsigned mask_size);
+
+// for documentation, see the definition
+const char *_gnutls_ip_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size);
+
+// for documentation, see the definition
+const char *_gnutls_cidr_to_string(const void *_ip, unsigned int ip_size, char *out, unsigned int out_size);
+
+// for documentation, see the definition
+int _gnutls_mask_ip(unsigned char *ip, const unsigned char *mask, unsigned ipsize);
+
+#endif /* GNUTLS_LIB_X509_IP_H */
diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c
new file mode 100644
index 0000000..44e4297
--- /dev/null
+++ b/lib/x509/key_decode.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2013-2017 Red Hat
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <global.h>
+#include <libtasn1.h>
+#include <datum.h>
+#include "common.h"
+#include "x509_int.h"
+#include "pk.h"
+#include <num.h>
+#include <ecc.h>
+
+static int _gnutls_x509_read_rsa_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+static int _gnutls_x509_read_dsa_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+static int _gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+static int _gnutls_x509_read_eddsa_pubkey(gnutls_ecc_curve_t curve,
+ uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+static int _gnutls_x509_read_ecdh_pubkey(gnutls_ecc_curve_t curve,
+ uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+static int _gnutls_x509_read_gost_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+
+static int
+_gnutls_x509_read_dsa_params(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+
+/*
+ * some x509 certificate parsing functions that relate to MPI parameter
+ * extraction. This reads the BIT STRING subjectPublicKey.
+ * Returns 2 parameters (m,e). It does not set params_nr.
+ */
+int
+_gnutls_x509_read_rsa_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPublicKey", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_der_decoding(&spk, der, dersize, NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return _gnutls_asn2err(result);
+ }
+
+
+ if (_gnutls_x509_read_int(spk, "modulus",
+ &params->params[0]) < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return GNUTLS_E_ASN1_GENERIC_ERROR;
+ }
+
+ if (_gnutls_x509_read_int(spk, "publicExponent",
+ &params->params[1]) < 0) {
+ gnutls_assert();
+ _gnutls_mpi_release(&params->params[0]);
+ asn1_delete_structure(&spk);
+ return GNUTLS_E_ASN1_GENERIC_ERROR;
+ }
+
+ asn1_delete_structure(&spk);
+
+ return 0;
+
+}
+
+/*
+ * some x509 certificate parsing functions that relate to MPI parameter
+ * extraction. This reads the BIT STRING subjectPublicKey.
+ * Returns 2 parameters (m,e). It does not set params_nr.
+ */
+int
+_gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ /* RFC5480 defines the public key to be an ECPoint (i.e. OCTET STRING),
+ * Then it says that the OCTET STRING _value_ is converted to BIT STRING.
+ * That means that the value we place there is the raw X9.62 one. */
+ return _gnutls_ecc_ansi_x962_import(der, dersize,
+ &params->params[ECC_X],
+ &params->params[ECC_Y]);
+}
+
+int _gnutls_x509_read_eddsa_pubkey(gnutls_ecc_curve_t curve,
+ uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ int size = gnutls_ecc_curve_get_size(curve);
+ if (dersize != size)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ return _gnutls_set_datum(&params->raw_pub, der, dersize);
+}
+
+int _gnutls_x509_read_ecdh_pubkey(gnutls_ecc_curve_t curve,
+ uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ int size = gnutls_ecc_curve_get_size(curve);
+ if (dersize != size)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ return _gnutls_set_datum(&params->raw_pub, der, dersize);
+}
+
+/* Pubkey is a concatenation of X (in little endian) and Y (also LE)
+ * encoded into OCTET STRING. */
+static int
+_gnutls_x509_read_gost_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ int ret;
+ int len;
+ bigint_t *x = &params->params[GOST_X];
+ bigint_t *y = &params->params[GOST_Y];
+
+ /* Quick and dirty parsing of OCTET STRING of 0x40 or 0x80 bytes */
+ if (dersize < 1 || der[0] != ASN1_TAG_OCTET_STRING) {
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ }
+
+ der++;
+ dersize--;
+
+ ret = asn1_get_length_der(der, dersize, &len);
+ if (ret <= 0 || ret % 2 != 0 || dersize != len + ret) {
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ }
+
+ der += len;
+ dersize -= len;
+
+ /* read data */
+ ret = _gnutls_mpi_init_scan_le(x, der, dersize / 2);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret = _gnutls_mpi_init_scan_le(y, der + dersize / 2, dersize / 2);
+ if (ret < 0) {
+ _gnutls_mpi_release(y);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ return 0;
+}
+
+/* reads p,q and g
+ * from the certificate (subjectPublicKey BIT STRING).
+ * params[0-2]. It does NOT set params_nr.
+ */
+static int
+_gnutls_x509_read_dsa_params(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.Dss-Parms",
+ &spk)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_der_decoding(&spk, der, dersize, NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return _gnutls_asn2err(result);
+ }
+
+ /* If the parameters are not included in the certificate
+ * then the issuer's parameters should be used. This is not
+ * implemented, and is not used in practice (along with DSA).
+ */
+
+ /* Read p */
+
+ if (_gnutls_x509_read_int(spk, "p", &params->params[0]) < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return GNUTLS_E_ASN1_GENERIC_ERROR;
+ }
+
+ /* Read q */
+
+ if (_gnutls_x509_read_int(spk, "q", &params->params[1]) < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ _gnutls_mpi_release(&params->params[0]);
+ return GNUTLS_E_ASN1_GENERIC_ERROR;
+ }
+
+ /* Read g */
+
+ if (_gnutls_x509_read_int(spk, "g", &params->params[2]) < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ _gnutls_mpi_release(&params->params[0]);
+ _gnutls_mpi_release(&params->params[1]);
+ return GNUTLS_E_ASN1_GENERIC_ERROR;
+ }
+
+ asn1_delete_structure(&spk);
+
+ params->params_nr = 3; /* public key is missing */
+ params->algo = GNUTLS_PK_DSA;
+
+ return 0;
+
+}
+
+/* reads the curve from the certificate.
+ * params[0-4]. It does NOT set params_nr.
+ */
+int
+_gnutls_x509_read_ecc_params(uint8_t * der, int dersize,
+ unsigned int * curve)
+{
+ int ret;
+ asn1_node spk = NULL;
+ char oid[MAX_OID_SIZE];
+ int oid_size;
+
+ if ((ret = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.ECParameters",
+ &spk)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = asn1_der_decoding(&spk, der, dersize, NULL);
+
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ /* read the curve */
+ oid_size = sizeof(oid);
+ ret = asn1_read_value(spk, "namedCurve", oid, &oid_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ *curve = gnutls_oid_to_ecc_curve(oid);
+ if (*curve == GNUTLS_ECC_CURVE_INVALID) {
+ _gnutls_debug_log("Curve %s is not supported\n", oid);
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+
+ asn1_delete_structure(&spk);
+
+ return ret;
+
+}
+
+/* Reads RSA-PSS parameters.
+ */
+int
+_gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize,
+ gnutls_x509_spki_st * params)
+{
+ int result;
+ asn1_node spk = NULL;
+ asn1_node c2 = NULL;
+ gnutls_digest_algorithm_t digest;
+ char oid[MAX_OID_SIZE] = "";
+ int size;
+ unsigned int trailer;
+ gnutls_datum_t value = { NULL, 0 };
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPSSParameters", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _asn1_strict_der_decode(&spk, der, dersize, NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ size = sizeof(oid);
+ result = asn1_read_value(spk, "hashAlgorithm.algorithm", oid, &size);
+ if (result == ASN1_SUCCESS)
+ digest = gnutls_oid_to_digest(oid);
+ else if (result == ASN1_ELEMENT_NOT_FOUND)
+ /* The default hash algorithm is SHA-1 */
+ digest = GNUTLS_DIG_SHA1;
+ else {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (digest == GNUTLS_DIG_UNKNOWN) {
+ gnutls_assert();
+ _gnutls_debug_log("Unknown RSA-PSS hash: %s\n", oid);
+ result = GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+ goto cleanup;
+ }
+
+ size = sizeof(oid);
+ result = asn1_read_value(spk, "maskGenAlgorithm.algorithm", oid, &size);
+ if (result == ASN1_SUCCESS) {
+ gnutls_digest_algorithm_t digest2;
+
+ /* Error out if algorithm other than mgf1 is specified */
+ if (strcmp(oid, PKIX1_RSA_PSS_MGF1_OID) != 0) {
+ gnutls_assert();
+ _gnutls_debug_log("Unknown mask algorithm: %s\n", oid);
+ result = GNUTLS_E_UNKNOWN_ALGORITHM;
+ goto cleanup;
+ }
+
+ /* Check if maskGenAlgorithm.parameters does exist and
+ * is identical to hashAlgorithm */
+ result = _gnutls_x509_read_value(spk, "maskGenAlgorithm.parameters", &value);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.AlgorithmIdentifier", &c2))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _asn1_strict_der_decode(&c2, value.data, value.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ size = sizeof(oid);
+ result = asn1_read_value(c2, "algorithm", oid, &size);
+ if (result == ASN1_SUCCESS)
+ digest2 = gnutls_oid_to_digest(oid);
+ else if (result == ASN1_ELEMENT_NOT_FOUND)
+ /* The default hash algorithm for mgf1 is SHA-1 */
+ digest2 = GNUTLS_DIG_SHA1;
+ else {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (digest != digest2) {
+ gnutls_assert();
+ result = GNUTLS_E_CONSTRAINT_ERROR;
+ goto cleanup;
+ }
+ } else if (result != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ memset(params, 0, sizeof(gnutls_x509_spki_st));
+ params->pk = GNUTLS_PK_RSA_PSS;
+ params->rsa_pss_dig = digest;
+
+ result = _gnutls_x509_read_uint(spk, "saltLength", &params->salt_size);
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND ||
+ result == GNUTLS_E_ASN1_VALUE_NOT_FOUND)
+ params->salt_size = 20;
+ else if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_read_uint(spk, "trailerField", &trailer);
+ if (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND ||
+ result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ trailer = 1;
+ else if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ if (trailer != 1) {
+ gnutls_assert();
+ result = GNUTLS_E_CERTIFICATE_ERROR;
+ goto cleanup;
+ }
+
+ result = 0;
+ cleanup:
+ _gnutls_free_datum(&value);
+ asn1_delete_structure(&c2);
+ asn1_delete_structure(&spk);
+ return result;
+}
+
+/* reads the curve from the certificate.
+ * It does NOT set params_nr.
+ */
+int
+_gnutls_x509_read_gost_params(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params,
+ gnutls_pk_algorithm_t algo)
+{
+ int ret;
+ asn1_node spk = NULL;
+ char oid[MAX_OID_SIZE];
+ int oid_size;
+ gnutls_ecc_curve_t curve;
+ gnutls_gost_paramset_t param;
+
+ if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(),
+ algo == GNUTLS_PK_GOST_01 ?
+ "GNUTLS.GOSTParametersOld" :
+ "GNUTLS.GOSTParameters",
+ &spk)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _asn1_strict_der_decode(&spk, der, dersize, NULL);
+
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ /* read the curve */
+ oid_size = sizeof(oid);
+ ret = asn1_read_value(spk, "publicKeyParamSet", oid, &oid_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ curve = gnutls_oid_to_ecc_curve(oid);
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
+ _gnutls_debug_log("Curve %s is not supported\n", oid);
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ goto cleanup;
+ }
+
+ /* Read the digest */
+ oid_size = sizeof(oid);
+ ret = asn1_read_value(spk, "digestParamSet", oid, &oid_size);
+ if (ret != ASN1_SUCCESS &&
+ ret != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+ /* For now ignore the OID: we use pk OID instead */
+
+ oid_size = sizeof(oid);
+ ret = asn1_read_value(spk, "encryptionParamSet", oid, &oid_size);
+ if (ret != ASN1_SUCCESS &&
+ ret != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ if (ret != ASN1_ELEMENT_NOT_FOUND)
+ param = gnutls_oid_to_gost_paramset(oid);
+ else
+ param = _gnutls_gost_paramset_default(algo);
+
+ if (param == GNUTLS_GOST_PARAMSET_UNKNOWN) {
+ gnutls_assert();
+ ret = param;
+ goto cleanup;
+ }
+
+ params->curve = curve;
+ params->gost_params = param;
+ ret = 0;
+
+ cleanup:
+
+ asn1_delete_structure(&spk);
+
+ return ret;
+
+}
+
+/* This function must be called after _gnutls_x509_read_params()
+ */
+int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der,
+ int dersize, gnutls_pk_params_st * params)
+{
+ int ret;
+
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ ret = _gnutls_x509_read_rsa_pubkey(der, dersize, params);
+ if (ret >= 0) {
+ params->algo = algo;
+ params->params_nr = RSA_PUBLIC_PARAMS;
+ }
+ break;
+ case GNUTLS_PK_DSA:
+ if (params->params_nr != 3) /* _gnutls_x509_read_pubkey_params must have been called */
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gnutls_x509_read_dsa_pubkey(der, dersize, params);
+ if (ret >= 0) {
+ params->algo = GNUTLS_PK_DSA;
+ params->params_nr = DSA_PUBLIC_PARAMS;
+ }
+ break;
+ case GNUTLS_PK_ECDSA:
+ ret = _gnutls_x509_read_ecc_pubkey(der, dersize, params);
+ if (ret >= 0) {
+ params->algo = GNUTLS_PK_ECDSA;
+ params->params_nr = ECC_PUBLIC_PARAMS;
+ }
+ break;
+ case GNUTLS_PK_EDDSA_ED25519:
+ ret = _gnutls_x509_read_eddsa_pubkey(GNUTLS_ECC_CURVE_ED25519, der, dersize, params);
+ break;
+ case GNUTLS_PK_EDDSA_ED448:
+ ret = _gnutls_x509_read_eddsa_pubkey(GNUTLS_ECC_CURVE_ED448, der, dersize, params);
+ break;
+ case GNUTLS_PK_ECDH_X25519:
+ ret = _gnutls_x509_read_ecdh_pubkey(GNUTLS_ECC_CURVE_X25519, der, dersize, params);
+ break;
+ case GNUTLS_PK_ECDH_X448:
+ ret = _gnutls_x509_read_ecdh_pubkey(GNUTLS_ECC_CURVE_X448, der, dersize, params);
+ break;
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ ret = _gnutls_x509_read_gost_pubkey(der, dersize, params);
+ if (ret >= 0) {
+ params->algo = algo;
+ params->params_nr = GOST_PUBLIC_PARAMS;
+ }
+ break;
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ break;
+ }
+ return ret;
+}
+
+/* This function must be called prior to _gnutls_x509_read_pubkey()
+ */
+int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo,
+ uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ return 0;
+ case GNUTLS_PK_RSA_PSS:
+ return _gnutls_x509_read_rsa_pss_params(der, dersize, &params->spki);
+ case GNUTLS_PK_DSA:
+ return _gnutls_x509_read_dsa_params(der, dersize, params);
+ case GNUTLS_PK_EC:
+ return _gnutls_x509_read_ecc_params(der, dersize, &params->curve);
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ return _gnutls_x509_read_gost_params(der, dersize, params, algo);
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ }
+}
+
+/* This function must be called after _gnutls_x509_read_pubkey()
+ */
+int _gnutls_x509_check_pubkey_params(gnutls_pk_params_st * params)
+{
+ switch (params->algo) {
+ case GNUTLS_PK_RSA_PSS: {
+ unsigned bits;
+ const mac_entry_st *me;
+ size_t hash_size;
+
+ if (params->spki.pk == GNUTLS_PK_UNKNOWN) /* no params present */
+ return 0;
+
+ bits = pubkey_to_bits(params);
+
+ me = hash_to_entry(params->spki.rsa_pss_dig);
+ if (unlikely(me == NULL))
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PUBKEY_PARAMS);
+
+ hash_size = _gnutls_hash_get_algo_len(me);
+ if (hash_size + params->spki.salt_size + 2 > (bits + 7) / 8)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PUBKEY_PARAMS);
+ return 0;
+ }
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_DSA:
+ case GNUTLS_PK_ECDSA:
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ }
+}
+
+/* reads DSA's Y
+ * from the certificate
+ * only sets params[3]
+ */
+int
+_gnutls_x509_read_dsa_pubkey(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params)
+{
+ return _gnutls_x509_read_der_int(der, dersize, &params->params[3]);
+}
diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c
new file mode 100644
index 0000000..8428cd1
--- /dev/null
+++ b/lib/x509/key_encode.c
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2013-2017 Red Hat
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <global.h>
+#include <libtasn1.h>
+#include <datum.h>
+#include "common.h"
+#include "x509_int.h"
+#include <num.h>
+#include <pk.h>
+#include <mpi.h>
+#include <ecc.h>
+
+static int _gnutls_x509_write_rsa_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+static int _gnutls_x509_write_dsa_params(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+static int _gnutls_x509_write_dsa_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+static int _gnutls_x509_write_gost_params(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+static int _gnutls_x509_write_gost_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+
+/*
+ * some x509 certificate functions that relate to MPI parameter
+ * setting. This writes the BIT STRING subjectPublicKey.
+ * Needs 2 parameters (m,e).
+ *
+ * Allocates the space used to store the DER data.
+ */
+static int
+_gnutls_x509_write_rsa_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ der->data = NULL;
+ der->size = 0;
+
+ if (params->params_nr < RSA_PUBLIC_PARAMS) {
+ gnutls_assert();
+ result = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPublicKey", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _gnutls_x509_write_int(spk, "modulus", params->params[0], 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_write_int(spk, "publicExponent",
+ params->params[1], 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(spk, "", der, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&spk);
+
+ return result;
+}
+
+/*
+ * some x509 certificate functions that relate to MPI parameter
+ * setting. This writes an ECPoint.
+ *
+ * Allocates the space used to store the DER data.
+ */
+int
+_gnutls_x509_write_ecc_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ int result;
+
+ der->data = NULL;
+ der->size = 0;
+
+ if (params->params_nr < ECC_PUBLIC_PARAMS)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ result =
+ _gnutls_ecc_ansi_x962_export(params->curve,
+ params->params[ECC_X],
+ params->params[ECC_Y], /*&out */
+ der);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ return 0;
+}
+
+/*
+ * some x509 certificate functions that relate to MPI parameter
+ * setting. This writes a raw public key.
+ *
+ * Allocates the space used to store the data.
+ */
+int
+_gnutls_x509_write_eddsa_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * raw)
+{
+ int ret;
+
+ raw->data = NULL;
+ raw->size = 0;
+
+ if (params->raw_pub.size == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (params->curve != GNUTLS_ECC_CURVE_ED25519 &&
+ params->curve != GNUTLS_ECC_CURVE_ED448)
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ ret = _gnutls_set_datum(raw, params->raw_pub.data, params->raw_pub.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+/*
+ * some x509 certificate functions that relate to MPI parameter
+ * setting. This writes a raw public key.
+ *
+ * Allocates the space used to store the data.
+ */
+static int
+_gnutls_x509_write_modern_ecdh_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * raw)
+{
+ int ret;
+
+ raw->data = NULL;
+ raw->size = 0;
+
+ if (params->raw_pub.size == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (params->curve != GNUTLS_ECC_CURVE_X25519 &&
+ params->curve != GNUTLS_ECC_CURVE_X448)
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ ret = _gnutls_set_datum(raw, params->raw_pub.data, params->raw_pub.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int
+_gnutls_x509_write_gost_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ bigint_t x, y;
+ int numlen;
+ int byte_size, ret;
+ size_t size;
+ int pos;
+
+ der->data = NULL;
+ der->size = 0;
+
+ if (params->params_nr < GOST_PUBLIC_PARAMS)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ x = params->params[GOST_X];
+ y = params->params[GOST_Y];
+ numlen = gnutls_ecc_curve_get_size(params->curve);
+
+ if (numlen == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ der->size = 1 + ASN1_MAX_LENGTH_SIZE + 2 * numlen;
+
+ der->data = gnutls_malloc(der->size);
+ if (der->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memset(der->data, 0, der->size);
+
+ der->data[0] = ASN1_TAG_OCTET_STRING;
+ asn1_length_der(2 * numlen, &der->data[1], &pos);
+ pos += 1;
+
+ /* pad and store x */
+ byte_size = (_gnutls_mpi_get_nbits(x) + 7) / 8;
+ if (numlen < byte_size) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ size = numlen;
+ ret = _gnutls_mpi_print_le(x, &der->data[pos], &size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* pad and store y */
+ byte_size = (_gnutls_mpi_get_nbits(y) + 7) / 8;
+ if (numlen < byte_size) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ size = numlen;
+ ret = _gnutls_mpi_print_le(y, &der->data[pos + numlen], &size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ der->size = pos + 2 * numlen;
+
+ return 0;
+
+ cleanup:
+ _gnutls_free_datum(der);
+ return ret;
+}
+
+int
+_gnutls_x509_write_pubkey_params(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ switch (params->algo) {
+ case GNUTLS_PK_DSA:
+ return _gnutls_x509_write_dsa_params(params, der);
+ case GNUTLS_PK_RSA:
+ der->data = gnutls_malloc(ASN1_NULL_SIZE);
+ if (der->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(der->data, ASN1_NULL, ASN1_NULL_SIZE);
+ der->size = ASN1_NULL_SIZE;
+ return 0;
+ case GNUTLS_PK_RSA_PSS:
+ return _gnutls_x509_write_rsa_pss_params(&params->spki, der);
+ case GNUTLS_PK_ECDSA:
+ return _gnutls_x509_write_ecc_params(params->curve, der);
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ der->data = NULL;
+ der->size = 0;
+
+ return 0;
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ return _gnutls_x509_write_gost_params(params, der);
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ }
+}
+
+int
+_gnutls_x509_write_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ switch (params->algo) {
+ case GNUTLS_PK_DSA:
+ return _gnutls_x509_write_dsa_pubkey(params, der);
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ return _gnutls_x509_write_rsa_pubkey(params, der);
+ case GNUTLS_PK_ECDSA:
+ return _gnutls_x509_write_ecc_pubkey(params, der);
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ return _gnutls_x509_write_eddsa_pubkey(params, der);
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ return _gnutls_x509_write_modern_ecdh_pubkey(params, der);
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ return _gnutls_x509_write_gost_pubkey(params, der);
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ }
+}
+
+/*
+ * This function writes the parameters for DSS keys.
+ * Needs 3 parameters (p,q,g).
+ *
+ * Allocates the space used to store the DER data.
+ */
+static int
+_gnutls_x509_write_dsa_params(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ der->data = NULL;
+ der->size = 0;
+
+ if (params->params_nr < DSA_PUBLIC_PARAMS - 1) {
+ gnutls_assert();
+ result = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.DSAParameters", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_write_int(spk, "p", params->params[0], 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_write_int(spk, "q", params->params[1], 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_write_int(spk, "g", params->params[2], 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(spk, "", der, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&spk);
+ return result;
+}
+
+/*
+ * This function writes the parameters for ECC keys.
+ * That is the ECParameters struct.
+ *
+ * Allocates the space used to store the DER data.
+ */
+int
+_gnutls_x509_write_ecc_params(const gnutls_ecc_curve_t curve,
+ gnutls_datum_t * der)
+{
+ int result;
+ asn1_node spk = NULL;
+ const char *oid;
+
+ der->data = NULL;
+ der->size = 0;
+
+ oid = gnutls_ecc_curve_get_oid(curve);
+ if (oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.ECParameters", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if ((result =
+ asn1_write_value(spk, "", "namedCurve", 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result =
+ asn1_write_value(spk, "namedCurve", oid,
+ 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(spk, "", der, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&spk);
+ return result;
+}
+
+int
+_gnutls_x509_write_rsa_pss_params(const gnutls_x509_spki_st *params,
+ gnutls_datum_t *der)
+{
+ int result;
+ asn1_node spk = NULL;
+ asn1_node c2 = NULL;
+ const char *oid;
+ gnutls_datum_t tmp = { NULL, 0 };
+
+ der->data = NULL;
+ der->size = 0;
+
+ if (params->pk != GNUTLS_PK_RSA_PSS)
+ return 0;
+
+ /* refuse to write parameters we cannot read */
+ if (gnutls_pk_to_sign(GNUTLS_PK_RSA_PSS, params->rsa_pss_dig) == GNUTLS_SIGN_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPSSParameters", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ oid = gnutls_digest_get_oid(params->rsa_pss_dig);
+
+ if ((result = asn1_write_value(spk, "hashAlgorithm.algorithm", oid, 1))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result = asn1_write_value(spk, "hashAlgorithm.parameters", NULL, 0))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result =
+ asn1_write_value(spk, "maskGenAlgorithm.algorithm",
+ PKIX1_RSA_PSS_MGF1_OID, 1))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.AlgorithmIdentifier", &c2))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result = asn1_write_value(c2, "algorithm", oid, 1))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result = asn1_write_value(c2, "parameters", NULL, 0))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", &tmp, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if ((result =
+ asn1_write_value(spk, "maskGenAlgorithm.parameters",
+ tmp.data, tmp.size))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_write_uint32(spk, "saltLength",
+ params->salt_size);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_write_uint32(spk, "trailerField", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(spk, "", der, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ asn1_delete_structure(&c2);
+ asn1_delete_structure(&spk);
+ return result;
+}
+
+static int
+_gnutls_x509_write_gost_params(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ int result;
+ asn1_node spk = NULL;
+ const char *oid;
+
+ der->data = NULL;
+ der->size = 0;
+
+ oid = gnutls_ecc_curve_get_oid(params->curve);
+ if (oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(),
+ params->algo == GNUTLS_PK_GOST_01 ?
+ "GNUTLS.GOSTParametersOld" :
+ "GNUTLS.GOSTParameters", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if ((result =
+ asn1_write_value(spk, "publicKeyParamSet", oid,
+ 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* For compatibility per R 1323565.1.023—2018 provide digest OID only
+ * for GOST-2001 keys or GOST-2012 keys with CryptoPro curves. Do not
+ * set this optional parameter for TC26 curves */
+ if (params->algo == GNUTLS_PK_GOST_01)
+ oid = HASH_OID_GOST_R_3411_94_CRYPTOPRO_PARAMS;
+ else if (params->algo == GNUTLS_PK_GOST_12_256 &&
+ (params->curve == GNUTLS_ECC_CURVE_GOST256CPA ||
+ params->curve == GNUTLS_ECC_CURVE_GOST256CPB ||
+ params->curve == GNUTLS_ECC_CURVE_GOST256CPC ||
+ params->curve == GNUTLS_ECC_CURVE_GOST256CPXA ||
+ params->curve == GNUTLS_ECC_CURVE_GOST256CPXB))
+ oid = HASH_OID_STREEBOG_256;
+ else if (params->algo == GNUTLS_PK_GOST_12_512 &&
+ (params->curve == GNUTLS_ECC_CURVE_GOST512A ||
+ params->curve == GNUTLS_ECC_CURVE_GOST512B))
+ oid = HASH_OID_STREEBOG_512;
+ else
+ oid = NULL;
+
+ if ((result = asn1_write_value(spk, "digestParamSet", oid, oid ? 1 : 0)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ oid = gnutls_gost_paramset_get_oid(params->gost_params);
+ if (oid == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ if (params->algo == GNUTLS_PK_GOST_01) {
+ if (params->gost_params == _gnutls_gost_paramset_default(params->algo))
+ oid = NULL;
+
+ if ((result =
+ asn1_write_value(spk, "encryptionParamSet", oid,
+ oid ? 1 : 0)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ result = _gnutls_x509_der_encode(spk, "", der, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&spk);
+ return result;
+}
+
+/*
+ * This function writes the public parameters for DSS keys.
+ * Needs 1 parameter (y).
+ *
+ * Allocates the space used to store the DER data.
+ */
+static int
+_gnutls_x509_write_dsa_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ der->data = NULL;
+ der->size = 0;
+
+ if (params->params_nr < DSA_PUBLIC_PARAMS) {
+ gnutls_assert();
+ result = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_write_int(spk, "", params->params[3], 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(spk, "", der, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&spk);
+ return result;
+}
+
+/* Encodes the RSA parameters into an ASN.1 RSA private key structure.
+ */
+static int
+_gnutls_asn1_encode_rsa(asn1_node * c2, gnutls_pk_params_st * params)
+{
+ int result, ret;
+ uint8_t null = '\0';
+ gnutls_pk_params_st pk_params;
+
+ /* we do copy the parameters into a new structure to run _gnutls_pk_fixup,
+ * i.e., regenerate some parameters in case they were broken */
+ gnutls_pk_params_init(&pk_params);
+
+ ret = _gnutls_pk_params_copy(&pk_params, params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_pk_fixup(GNUTLS_PK_RSA, GNUTLS_EXPORT, &pk_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Ok. Now we have the data. Create the asn1 structures
+ */
+
+ /* first make sure that no previously allocated data are leaked */
+ if (*c2 != NULL) {
+ asn1_delete_structure(c2);
+ *c2 = NULL;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPrivateKey", c2))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Write PRIME
+ */
+ ret =
+ _gnutls_x509_write_int(*c2, "modulus",
+ params->params[RSA_MODULUS], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_int(*c2, "publicExponent",
+ params->params[RSA_PUB], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "privateExponent",
+ params->params[RSA_PRIV], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "prime1",
+ params->params[RSA_PRIME1], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "prime2",
+ params->params[RSA_PRIME2], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "coefficient",
+ params->params[RSA_COEF], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "exponent1",
+ params->params[RSA_E1], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "exponent2",
+ params->params[RSA_E2], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if ((result = asn1_write_value(*c2, "otherPrimeInfos",
+ NULL, 0)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((result =
+ asn1_write_value(*c2, "version", &null, 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0)
+ asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE);
+
+ gnutls_pk_params_clear(&pk_params);
+ gnutls_pk_params_release(&pk_params);
+ return ret;
+}
+
+/* Encodes the ECC parameters into an ASN.1 ECPrivateKey structure.
+ */
+static int
+_gnutls_asn1_encode_ecc(asn1_node * c2, gnutls_pk_params_st * params)
+{
+ int ret;
+ uint8_t one = '\x01';
+ gnutls_datum_t pubkey = { NULL, 0 };
+ const char *oid;
+
+ oid = gnutls_ecc_curve_get_oid(params->curve);
+ if (oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* first make sure that no previously allocated data are leaked */
+ if (*c2 != NULL) {
+ asn1_delete_structure(c2);
+ *c2 = NULL;
+ }
+
+ if ((ret = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.ECPrivateKey", c2))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ if ((ret =
+ asn1_write_value(*c2, "Version", &one, 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ if (curve_is_eddsa(params->curve) ||
+ curve_is_modern_ecdh(params->curve)) {
+ if (params->raw_pub.size == 0 || params->raw_priv.size == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ ret =
+ asn1_write_value(*c2, "privateKey", params->raw_priv.data, params->raw_priv.size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ asn1_write_value(*c2, "publicKey", params->raw_pub.data, params->raw_pub.size*8);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+ } else {
+ if (params->params_nr != ECC_PRIVATE_PARAMS)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret =
+ _gnutls_ecc_ansi_x962_export(params->curve,
+ params->params[ECC_X],
+ params->params[ECC_Y], &pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "privateKey",
+ params->params[ECC_K], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if ((ret =
+ asn1_write_value(*c2, "publicKey", pubkey.data,
+ pubkey.size * 8)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+ }
+
+ /* write our choice */
+ if ((ret =
+ asn1_write_value(*c2, "parameters", "namedCurve",
+ 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ if ((ret =
+ asn1_write_value(*c2, "parameters.namedCurve", oid,
+ 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&pubkey);
+ return 0;
+
+cleanup:
+ asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE);
+ _gnutls_free_datum(&pubkey);
+
+ return ret;
+}
+
+static int
+_gnutls_asn1_encode_gost(asn1_node * c2, gnutls_pk_params_st * params)
+{
+ int ret;
+ const char *oid;
+
+ oid = gnutls_pk_get_oid(params->algo);
+
+ if (params->params_nr != GOST_PRIVATE_PARAMS || oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* first make sure that no previously allocated data are leaked */
+ if (*c2 != NULL) {
+ asn1_delete_structure(c2);
+ *c2 = NULL;
+ }
+
+ if ((ret = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.GOSTPrivateKey", c2))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int_le(*c2, "", params->params[GOST_K]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+
+ return 0;
+
+cleanup:
+ asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE);
+
+ return ret;
+}
+
+/* Encodes the DSA parameters into an ASN.1 DSAPrivateKey structure.
+ */
+static int
+_gnutls_asn1_encode_dsa(asn1_node * c2, gnutls_pk_params_st * params)
+{
+ int result, ret;
+ const uint8_t null = '\0';
+
+ /* first make sure that no previously allocated data are leaked */
+ if (*c2 != NULL) {
+ asn1_delete_structure(c2);
+ *c2 = NULL;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPrivateKey", c2))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Write PRIME
+ */
+ ret =
+ _gnutls_x509_write_int(*c2, "p",
+ params->params[DSA_P], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_int(*c2, "q",
+ params->params[DSA_Q], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_int(*c2, "g",
+ params->params[DSA_G], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_int(*c2, "Y",
+ params->params[DSA_Y], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_key_int(*c2, "priv",
+ params->params[DSA_X], 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if ((result =
+ asn1_write_value(*c2, "version", &null, 1)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ return 0;
+
+cleanup:
+ asn1_delete_structure2(c2, ASN1_DELETE_FLAG_ZEROIZE);
+
+ return ret;
+}
+
+int _gnutls_asn1_encode_privkey(asn1_node * c2,
+ gnutls_pk_params_st * params)
+{
+ switch (params->algo) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ return _gnutls_asn1_encode_rsa(c2, params);
+ case GNUTLS_PK_DSA:
+ return _gnutls_asn1_encode_dsa(c2, params);
+ case GNUTLS_PK_ECDSA:
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ return _gnutls_asn1_encode_ecc(c2, params);
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ return _gnutls_asn1_encode_gost(c2, params);
+ default:
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ }
+}
diff --git a/lib/x509/krb5.c b/lib/x509/krb5.c
new file mode 100644
index 0000000..b26d07d
--- /dev/null
+++ b/lib/x509/krb5.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include <config.h>
+#include <gnutls/gnutls.h>
+#include <libtasn1.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errors.h>
+#include "krb5.h"
+#include "common.h"
+
+#define _gnutls_asn2err(x) GNUTLS_E_ASN1_DER_ERROR
+
+#define MAX_COMPONENTS 6
+
+typedef struct krb5_principal_data {
+ char *realm;
+ char *data[MAX_COMPONENTS];
+ uint32_t length;
+ int8_t type;
+} krb5_principal_data;
+
+extern const asn1_static_node krb5_asn1_tab[];
+
+static void cleanup_principal(krb5_principal_data * princ)
+{
+ unsigned i;
+ if (princ) {
+ gnutls_free(princ->realm);
+ for (i = 0; i < princ->length; i++)
+ gnutls_free(princ->data[i]);
+ memset(princ, 0, sizeof(*princ));
+ gnutls_free(princ);
+ }
+}
+
+static krb5_principal_data *name_to_principal(const char *_name)
+{
+ krb5_principal_data *princ;
+ char *p, *p2, *sp;
+ unsigned pos = 0;
+ char *name = NULL;
+
+ princ = gnutls_calloc(1, sizeof(struct krb5_principal_data));
+ if (princ == NULL)
+ return NULL;
+
+ name = gnutls_strdup(_name);
+ if (name == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ p = strrchr(name, '@');
+ p2 = strchr(name, '@');
+ if (p == NULL) {
+ /* unknown name type */
+ gnutls_assert();
+ goto fail;
+ }
+
+ princ->realm = gnutls_strdup(p + 1);
+ if (princ->realm == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+ *p = 0;
+
+ if (p == p2) {
+ p = strtok_r(name, "/", &sp);
+ while (p) {
+ if (pos == MAX_COMPONENTS) {
+ _gnutls_debug_log
+ ("%s: Cannot parse names with more than %d components\n",
+ __func__, MAX_COMPONENTS);
+ goto fail;
+ }
+
+ princ->data[pos] = gnutls_strdup(p);
+ if (princ->data[pos] == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ princ->length++;
+ pos++;
+
+ p = strtok_r(NULL, "/", &sp);
+ }
+
+ if ((princ->length == 2)
+ && (strcmp(princ->data[0], "krbtgt") == 0)) {
+ princ->type = 2; /* KRB_NT_SRV_INST */
+ } else {
+ princ->type = 1; /* KRB_NT_PRINCIPAL */
+ }
+ } else { /* enterprise */
+ princ->data[0] = gnutls_strdup(name);
+ if (princ->data[0] == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ princ->length++;
+ princ->type = 10; /* KRB_NT_ENTERPRISE */
+ }
+
+ goto cleanup;
+ fail:
+ cleanup_principal(princ);
+ princ = NULL;
+
+ cleanup:
+ gnutls_free(name);
+ return princ;
+}
+
+int _gnutls_krb5_principal_to_der(const char *name, gnutls_datum_t * der)
+{
+ int ret, result;
+ asn1_node c2 = NULL;
+ krb5_principal_data *princ;
+ unsigned i;
+
+ princ = name_to_principal(name);
+ if (princ == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+
+ result =
+ asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.KRB5PrincipalName", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2, "realm", princ->realm, strlen(princ->realm));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2, "principalName.name-type", &princ->type, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ for (i = 0; i < princ->length; i++) {
+ result =
+ asn1_write_value(c2, "principalName.name-string", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2,
+ "principalName.name-string.?LAST",
+ princ->data[i], strlen(princ->data[i]));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", der, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ cleanup_principal(princ);
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+static int principal_to_str(asn1_node c2, gnutls_buffer_st * str)
+{
+ gnutls_datum_t realm = { NULL, 0 };
+ gnutls_datum_t component = { NULL, 0 };
+ unsigned char name_type[2];
+ int ret, result, len;
+ unsigned i;
+ char val[128];
+
+ ret = _gnutls_x509_read_value(c2, "realm", &realm);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ len = sizeof(name_type);
+ result =
+ asn1_read_value(c2, "principalName.name-type", name_type, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (len != 1
+ || (name_type[0] != 1 && name_type[0] != 2 && name_type[0] != 10)) {
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ for (i = 0;; i++) {
+ snprintf(val, sizeof(val), "principalName.name-string.?%u",
+ i + 1);
+ ret = _gnutls_x509_read_value(c2, val, &component);
+ if (ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND
+ || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ break;
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (i > 0) {
+ ret = _gnutls_buffer_append_data(str, "/", 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret =
+ _gnutls_buffer_append_data(str, component.data,
+ component.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&component);
+ }
+
+ ret = _gnutls_buffer_append_data(str, "@", 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data(str, realm.data, realm.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&component);
+ gnutls_free(realm.data);
+ return ret;
+}
+
+int _gnutls_krb5_der_to_principal(const gnutls_datum_t * der,
+ gnutls_datum_t * name)
+{
+ int ret, result;
+ asn1_node c2 = NULL;
+ gnutls_buffer_st str;
+
+ _gnutls_buffer_init(&str);
+
+ result =
+ asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.KRB5PrincipalName", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_der_decoding(&c2, der->data, der->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = principal_to_str(c2, &str);
+ if (ret < 0) {
+ /* for some reason we cannot convert to a human readable string
+ * the principal. Then we use the #HEX format.
+ */
+ _gnutls_buffer_reset(&str);
+ ret = _gnutls_buffer_append_data(&str, "#", 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_buffer_hexprint(&str, der->data, der->size);
+ }
+
+ asn1_delete_structure(&c2);
+ return _gnutls_buffer_to_datum(&str, name, 1);
+
+ cleanup:
+ _gnutls_buffer_clear(&str);
+ asn1_delete_structure(&c2);
+ return ret;
+}
diff --git a/lib/x509/krb5.h b/lib/x509/krb5.h
new file mode 100644
index 0000000..815bb28
--- /dev/null
+++ b/lib/x509/krb5.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_KRB5_H
+#define GNUTLS_LIB_X509_KRB5_H
+
+int _gnutls_krb5_principal_to_der(const char *name, gnutls_datum_t * der);
+int _gnutls_krb5_der_to_principal(const gnutls_datum_t * der, gnutls_datum_t *name);
+
+#endif /* GNUTLS_LIB_X509_KRB5_H */
diff --git a/lib/x509/mpi.c b/lib/x509/mpi.c
new file mode 100644
index 0000000..c0751df
--- /dev/null
+++ b/lib/x509/mpi.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <global.h>
+#include <libtasn1.h>
+#include <datum.h>
+#include "common.h"
+#include "x509_int.h"
+#include <num.h>
+#include <limits.h>
+
+/* Reads an Integer from the DER encoded data
+ */
+
+int _gnutls_x509_read_der_int(uint8_t * der, int dersize, bigint_t * out)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ /* == INTEGER */
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey",
+ &spk)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&spk, der, dersize, NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return _gnutls_asn2err(result);
+ }
+
+ /* Read Y */
+
+ if ((result = _gnutls_x509_read_int(spk, "", out)) < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return _gnutls_asn2err(result);
+ }
+
+ asn1_delete_structure(&spk);
+
+ return 0;
+
+}
+
+int _gnutls_x509_read_der_uint(uint8_t * der, int dersize, unsigned int *out)
+{
+ int result;
+ asn1_node spk = NULL;
+
+ /* == INTEGER */
+ if ((result = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey",
+ &spk)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&spk, der, dersize, NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return _gnutls_asn2err(result);
+ }
+
+ /* Read Y */
+
+ if ((result = _gnutls_x509_read_uint(spk, "", out)) < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&spk);
+ return _gnutls_asn2err(result);
+ }
+
+ asn1_delete_structure(&spk);
+
+ return 0;
+
+}
+
+
+/* Extracts DSA and RSA parameters from a certificate.
+ */
+int
+_gnutls_get_asn_mpis(asn1_node asn, const char *root,
+ gnutls_pk_params_st * params)
+{
+ int result;
+ char name[256];
+ gnutls_datum_t tmp = { NULL, 0 };
+ gnutls_pk_algorithm_t pk_algorithm;
+ gnutls_ecc_curve_t curve;
+
+ gnutls_pk_params_init(params);
+
+ result = _gnutls_x509_get_pk_algorithm(asn, root, &curve, NULL);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ pk_algorithm = result;
+ params->curve = curve;
+ params->algo = pk_algorithm;
+
+ /* Read the algorithm's parameters
+ */
+ _asnstr_append_name(name, sizeof(name), root,
+ ".algorithm.parameters");
+
+ if (pk_algorithm != GNUTLS_PK_RSA &&
+ pk_algorithm != GNUTLS_PK_EDDSA_ED25519 && pk_algorithm != GNUTLS_PK_ECDH_X25519 &&
+ pk_algorithm != GNUTLS_PK_EDDSA_ED448 && pk_algorithm != GNUTLS_PK_ECDH_X448) {
+ /* RSA and EdDSA do not use parameters */
+ result = _gnutls_x509_read_value(asn, name, &tmp);
+ if (pk_algorithm == GNUTLS_PK_RSA_PSS &&
+ (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)) {
+ goto skip_params;
+ }
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result =
+ _gnutls_x509_read_pubkey_params(pk_algorithm,
+ tmp.data, tmp.size,
+ params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ _gnutls_free_datum(&tmp);
+ }
+
+ skip_params:
+ /* Now read the public key */
+ _asnstr_append_name(name, sizeof(name), root, ".subjectPublicKey");
+
+ result = _gnutls_x509_read_value(asn, name, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if ((result =
+ _gnutls_x509_read_pubkey(pk_algorithm, tmp.data, tmp.size,
+ params)) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = _gnutls_x509_check_pubkey_params(params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ if (result < 0)
+ gnutls_pk_params_release(params);
+ _gnutls_free_datum(&tmp);
+ return result;
+}
+
+/* Extracts DSA and RSA parameters from a certificate.
+ */
+int
+_gnutls_x509_crt_get_mpis(gnutls_x509_crt_t cert,
+ gnutls_pk_params_st * params)
+{
+ /* Read the algorithm's OID
+ */
+ return _gnutls_get_asn_mpis(cert->cert,
+ "tbsCertificate.subjectPublicKeyInfo",
+ params);
+}
+
+/* Extracts DSA and RSA parameters from a certificate.
+ */
+int
+_gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert,
+ gnutls_pk_params_st * params)
+{
+ /* Read the algorithm's OID
+ */
+ return _gnutls_get_asn_mpis(cert->crq,
+ "certificationRequestInfo.subjectPKInfo",
+ params);
+}
+
+/*
+ * This function reads and decodes the parameters for DSS or RSA keys.
+ * This is the "signatureAlgorithm" fields.
+ */
+int
+_gnutls_x509_read_pkalgo_params(asn1_node src, const char *src_name,
+ gnutls_x509_spki_st *spki, unsigned is_sig)
+{
+ int result;
+ char name[128];
+ char oid[MAX_OID_SIZE];
+ int oid_size;
+
+ memset(spki, 0, sizeof(*spki));
+
+ _gnutls_str_cpy(name, sizeof(name), src_name);
+ _gnutls_str_cat(name, sizeof(name), ".algorithm");
+
+ oid_size = sizeof(oid);
+ result = asn1_read_value(src, name, oid, &oid_size);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (strcmp (oid, PK_PKIX1_RSA_PSS_OID) == 0) {
+ gnutls_datum_t tmp = { NULL, 0 };
+
+ _gnutls_str_cpy(name, sizeof(name), src_name);
+ _gnutls_str_cat(name, sizeof(name), ".parameters");
+
+ result = _gnutls_x509_read_value(src, name, &tmp);
+ if (result < 0) {
+ if (!is_sig) {
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND ||
+ result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) {
+ /* it is ok to not have parameters in SPKI, but
+ * not in signatures */
+ return 0;
+ }
+ }
+
+ return gnutls_assert_val(result);
+ }
+
+ result = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size,
+ spki);
+ _gnutls_free_datum(&tmp);
+
+ if (result < 0)
+ gnutls_assert();
+
+ return result;
+ }
+
+ return 0;
+}
+
+static int write_oid_and_params(asn1_node dst, const char *dst_name, const char *oid, gnutls_x509_spki_st *params)
+{
+ int result;
+ char name[128];
+
+ if (params == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ _gnutls_str_cpy(name, sizeof(name), dst_name);
+ _gnutls_str_cat(name, sizeof(name), ".algorithm");
+
+ /* write the OID.
+ */
+ result = asn1_write_value(dst, name, oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ _gnutls_str_cpy(name, sizeof(name), dst_name);
+ _gnutls_str_cat(name, sizeof(name), ".parameters");
+
+ if (params->pk == GNUTLS_PK_RSA)
+ result =
+ asn1_write_value(dst, name, ASN1_NULL, ASN1_NULL_SIZE);
+ else if (params->pk == GNUTLS_PK_RSA_PSS) {
+ gnutls_datum_t tmp = { NULL, 0 };
+
+ result = _gnutls_x509_write_rsa_pss_params(params, &tmp);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ result = asn1_write_value(dst, name, tmp.data, tmp.size);
+ _gnutls_free_datum(&tmp);
+ } else
+ result = asn1_write_value(dst, name, NULL, 0);
+
+ if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) {
+ /* Here we ignore the element not found error, since this
+ * may have been disabled before.
+ */
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+int
+_gnutls_x509_write_spki_params(asn1_node dst, const char *dst_name,
+ gnutls_x509_spki_st *params)
+{
+ const char *oid;
+
+ if (params->legacy && params->pk == GNUTLS_PK_RSA)
+ oid = PK_PKIX1_RSA_OID;
+ else if (params->pk == GNUTLS_PK_RSA_PSS)
+ oid = PK_PKIX1_RSA_PSS_OID;
+ else
+ oid = gnutls_pk_get_oid(params->pk);
+
+ if (oid == NULL) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Cannot find OID for public key algorithm %s\n",
+ gnutls_pk_get_name(params->pk));
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return write_oid_and_params(dst, dst_name, oid, params);
+}
+
+int
+_gnutls_x509_write_sign_params(asn1_node dst, const char *dst_name,
+ const gnutls_sign_entry_st *se, gnutls_x509_spki_st *params)
+{
+ const char *oid;
+
+ if (params->legacy && params->pk == GNUTLS_PK_RSA)
+ oid = PK_PKIX1_RSA_OID;
+ else if (params->pk == GNUTLS_PK_RSA_PSS)
+ oid = PK_PKIX1_RSA_PSS_OID;
+ else
+ oid = se->oid;
+
+ if (oid == NULL) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Cannot find OID for sign algorithm %s\n",
+ se->name);
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return write_oid_and_params(dst, dst_name, oid, params);
+}
+
+/* this function reads a (small) unsigned integer
+ * from asn1 structs. Combines the read and the conversion
+ * steps.
+ */
+int
+_gnutls_x509_read_uint(asn1_node node, const char *value,
+ unsigned int *ret)
+{
+ int len, result;
+ uint8_t *tmpstr;
+
+ len = 0;
+ result = asn1_read_value(node, value, NULL, &len);
+ if (result != ASN1_MEM_ERROR) {
+ return _gnutls_asn2err(result);
+ }
+
+ tmpstr = gnutls_malloc(len);
+ if (tmpstr == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = asn1_read_value(node, value, tmpstr, &len);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(tmpstr);
+ return _gnutls_asn2err(result);
+ }
+
+ if (len == 1)
+ *ret = tmpstr[0];
+ else if (len == 2)
+ *ret = _gnutls_read_uint16(tmpstr);
+ else if (len == 3)
+ *ret = _gnutls_read_uint24(tmpstr);
+ else if (len == 4)
+ *ret = _gnutls_read_uint32(tmpstr);
+ else {
+ gnutls_assert();
+ gnutls_free(tmpstr);
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ gnutls_free(tmpstr);
+
+ return 0;
+}
+
+/* Writes the specified integer into the specified node.
+ */
+int
+_gnutls_x509_write_uint32(asn1_node node, const char *value, uint32_t num)
+{
+ uint8_t tmpstr[5];
+ int result;
+
+ tmpstr[0] = 0;
+ _gnutls_write_uint32(num, tmpstr+1);
+
+ if (tmpstr[1] > SCHAR_MAX) {
+ result = asn1_write_value(node, value, tmpstr, 5);
+ } else {
+ result = asn1_write_value(node, value, tmpstr+1, 4);
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c
new file mode 100644
index 0000000..ba37674
--- /dev/null
+++ b/lib/x509/name_constraints.c
@@ -0,0 +1,1404 @@
+/*
+ * Copyright (C) 2014-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors: Nikos Mavrogiannopoulos, Daiki Ueno, Martin Ukrop
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions on X.509 Certificate parsing
+ */
+
+#include "gnutls_int.h"
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <gnutls/x509-ext.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <libtasn1.h>
+
+#include "ip.h"
+#include "ip-in-cidr.h"
+
+// for documentation see the implementation
+static int name_constraints_intersect_nodes(name_constraints_node_st * nc1,
+ name_constraints_node_st * nc2,
+ name_constraints_node_st ** intersection);
+
+/*-
+ * is_nc_empty:
+ * @nc: name constraints structure
+ * @type: type (gnutls_x509_subject_alt_name_t)
+ *
+ * Test whether given name constraints structure has any constraints (permitted
+ * or excluded) of a given type. @nc must be allocated (not NULL) before the call.
+ *
+ * Returns: 0 if @nc contains constraints of type @type, 1 otherwise
+ -*/
+static unsigned is_nc_empty(struct gnutls_name_constraints_st* nc, unsigned type)
+{
+ name_constraints_node_st *t;
+
+ if (nc->permitted == NULL && nc->excluded == NULL)
+ return 1;
+
+ t = nc->permitted;
+ while (t != NULL) {
+ if (t->type == type)
+ return 0;
+ t = t->next;
+ }
+
+ t = nc->excluded;
+ while (t != NULL) {
+ if (t->type == type)
+ return 0;
+ t = t->next;
+ }
+
+ /* no constraint for that type exists */
+ return 1;
+}
+
+/*-
+ * validate_name_constraints_node:
+ * @type: type of name constraints
+ * @name: datum of name constraint
+ *
+ * Check the validity of given name constraints node (@type and @name).
+ * The supported types are GNUTLS_SAN_DNSNAME, GNUTLS_SAN_RFC822NAME,
+ * GNUTLS_SAN_DN, GNUTLS_SAN_URI and GNUTLS_SAN_IPADDRESS.
+ *
+ * CIDR ranges are checked for correct length (IPv4/IPv6) and correct mask format.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ -*/
+static int validate_name_constraints_node(gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t* name)
+{
+ if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME &&
+ type != GNUTLS_SAN_DN && type != GNUTLS_SAN_URI &&
+ type != GNUTLS_SAN_IPADDRESS &&
+ type != GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL) {
+ return gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN);
+ }
+
+ if (type == GNUTLS_SAN_IPADDRESS) {
+ if (name->size != 8 && name->size != 32)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ int prefix = _gnutls_mask_to_prefix(name->data + name->size/2, name->size/2);
+ if (prefix < 0)
+ return gnutls_assert_val(GNUTLS_E_MALFORMED_CIDR);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr,
+ name_constraints_node_st ** _nc)
+{
+ int ret;
+ char tmpstr[128];
+ unsigned indx;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned int type;
+ struct name_constraints_node_st *nc, *prev;
+
+ prev = *_nc;
+ if (prev != NULL) {
+ while(prev->next != NULL)
+ prev = prev->next;
+ }
+
+ for (indx=1;;indx++) {
+ snprintf(tmpstr, sizeof(tmpstr), "%s.?%u.base", vstr, indx);
+
+ ret =
+ _gnutls_parse_general_name2(c2, tmpstr, -1, &tmp, &type, 0);
+
+ if (ret < 0) {
+ gnutls_assert();
+ break;
+ }
+
+ if (type == GNUTLS_SAN_OTHERNAME) {
+ gnutls_datum_t oid = { NULL, 0 };
+ gnutls_datum_t parsed_othername = { NULL, 0 };
+ ret = _gnutls_parse_general_name2(c2, tmpstr, -1, &oid, &type, 1);
+ if(ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_othername_to_virtual((char*)oid.data, &tmp, &type,
+ &parsed_othername);
+ if(ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ gnutls_free(oid.data);
+ gnutls_free(tmp.data);
+
+ memcpy(&tmp, &parsed_othername, sizeof(gnutls_datum_t));
+ }
+
+ ret = validate_name_constraints_node(type, &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ nc = gnutls_malloc(sizeof(struct name_constraints_node_st));
+ if (nc == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ memcpy(&nc->name, &tmp, sizeof(gnutls_datum_t));
+ nc->type = type;
+ nc->next = NULL;
+
+ if (prev == NULL) {
+ *_nc = prev = nc;
+ } else {
+ prev->next = nc;
+ prev = nc;
+ }
+
+ tmp.data = NULL;
+ }
+
+ assert(ret < 0);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(tmp.data);
+ return ret;
+}
+
+/*-
+ * _gnutls_name_constraints_node_free:
+ * @node: name constraints node
+ *
+ * Deallocate a list of name constraints nodes starting at the given node.
+ -*/
+void _gnutls_name_constraints_node_free(name_constraints_node_st *node)
+{
+ name_constraints_node_st *next, *t;
+
+ t = node;
+ while (t != NULL) {
+ next = t->next;
+ gnutls_free(t->name.data);
+ gnutls_free(t);
+ t = next;
+ }
+}
+
+/*-
+ * name_constraints_node_new:
+ * @type: name constraints type to set (gnutls_x509_subject_alt_name_t)
+ * @data: name.data to set or NULL
+ * @size: name.size to set
+ *
+ * Allocate a new name constraints node and set its type, name size and name data.
+ * If @data is set to NULL, name data will be an array of \x00 (the length of @size).
+ * The .next pointer is set to NULL.
+ *
+ * Returns: Pointer to newly allocated node or NULL in case of memory error.
+ -*/
+static name_constraints_node_st* name_constraints_node_new(unsigned type,
+ unsigned char *data,
+ unsigned int size)
+{
+ name_constraints_node_st *tmp = gnutls_malloc(sizeof(struct name_constraints_node_st));
+ if (tmp == NULL)
+ return NULL;
+ tmp->type = type;
+ tmp->next = NULL;
+ tmp->name.size = size;
+ tmp->name.data = NULL;
+ if (tmp->name.size > 0) {
+
+ tmp->name.data = gnutls_malloc(tmp->name.size);
+ if (tmp->name.data == NULL) {
+ gnutls_free(tmp);
+ return NULL;
+ }
+ if (data != NULL) {
+ memcpy(tmp->name.data, data, size);
+ } else {
+ memset(tmp->name.data, 0, size);
+ }
+ }
+ return tmp;
+}
+
+/*-
+ * @brief _gnutls_name_constraints_intersect:
+ * @_nc: first name constraints list (permitted)
+ * @_nc2: name constraints list to merge with (permitted)
+ * @_nc_excluded: Corresponding excluded name constraints list
+ *
+ * This function finds the intersection of @_nc and @_nc2. The result is placed in @_nc,
+ * the original @_nc is deallocated. @_nc2 is not changed. If necessary, a universal
+ * excluded name constraint node of the right type is added to the list provided
+ * in @_nc_excluded.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ -*/
+static
+int _gnutls_name_constraints_intersect(name_constraints_node_st ** _nc,
+ name_constraints_node_st * _nc2,
+ name_constraints_node_st ** _nc_excluded)
+{
+ name_constraints_node_st *nc, *nc2, *t, *tmp, *dest = NULL, *prev = NULL;
+ int ret, type, used;
+
+ /* temporary array to see, if we need to add universal excluded constraints
+ * (see phase 3 for details)
+ * indexed directly by (gnutls_x509_subject_alt_name_t enum - 1) */
+ unsigned char types_with_empty_intersection[GNUTLS_SAN_MAX];
+ memset(types_with_empty_intersection, 0, sizeof(types_with_empty_intersection));
+
+ if (*_nc == NULL || _nc2 == NULL)
+ return 0;
+
+ /* Phase 1
+ * For each name in _NC, if a _NC2 does not contain a name
+ * with the same type, preserve the original name.
+ * Do this also for node of unknown type (not DNS, email, IP */
+ t = nc = *_nc;
+ while (t != NULL) {
+ name_constraints_node_st *next = t->next;
+ nc2 = _nc2;
+ while (nc2 != NULL) {
+ if (t->type == nc2->type) {
+ // check bounds (we will use 't->type' as index)
+ if (t->type > GNUTLS_SAN_MAX || t->type == 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ // note the possibility of empty intersection for this type
+ // if we add something to the intersection in phase 2,
+ // we will reset this flag back to 0 then
+ types_with_empty_intersection[t->type - 1] = 1;
+ break;
+ }
+ nc2 = nc2->next;
+ }
+ if (nc2 == NULL ||
+ (t->type != GNUTLS_SAN_DNSNAME &&
+ t->type != GNUTLS_SAN_RFC822NAME &&
+ t->type != GNUTLS_SAN_IPADDRESS)
+ ) {
+ /* move node from NC to DEST */
+ if (prev != NULL)
+ prev->next = next;
+ else
+ prev = nc = next;
+ t->next = dest;
+ dest = t;
+ } else {
+ prev = t;
+ }
+ t = next;
+ }
+
+ /* Phase 2
+ * iterate through all combinations from nc2 and nc1
+ * and create intersections of nodes with same type */
+ nc2 = _nc2;
+ while (nc2 != NULL) {
+ // current nc2 node has not yet been used for any intersection
+ // (and is not in DEST either)
+ used = 0;
+ t = nc;
+ while (t != NULL) {
+ // save intersection of name constraints into tmp
+ ret = name_constraints_intersect_nodes(t, nc2, &tmp);
+ if (ret < 0) return gnutls_assert_val(ret);
+ used = 1;
+ // if intersection is not empty
+ if (tmp != NULL) { // intersection for this type is not empty
+ // check bounds
+ if (tmp->type > GNUTLS_SAN_MAX || tmp->type == 0) {
+ gnutls_free(tmp);
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ // we will not add universal excluded constraint for this type
+ types_with_empty_intersection[tmp->type - 1] = 0;
+ // add intersection node to DEST
+ tmp->next = dest;
+ dest = tmp;
+ }
+ t = t->next;
+ }
+ // if the node from nc2 was not used for intersection, copy it to DEST
+ // Beware: also copies nodes other than DNS, email, IP,
+ // since their counterpart may have been moved in phase 1.
+ if (!used) {
+ tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size);
+ if (tmp == NULL) {
+ _gnutls_name_constraints_node_free(dest);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ tmp->next = dest;
+ dest = tmp;
+ }
+ nc2 = nc2->next;
+ }
+
+ /* replace the original with the new */
+ _gnutls_name_constraints_node_free(nc);
+ *_nc = dest;
+
+ /* Phase 3
+ * For each type: If we have empty permitted name constraints now
+ * and we didn't have at the beginning, we have to add a new
+ * excluded constraint with universal wildcard
+ * (since the intersection of permitted is now empty). */
+ for (type = 1; type <= GNUTLS_SAN_MAX; type++) {
+ if (types_with_empty_intersection[type-1] == 0)
+ continue;
+ _gnutls_hard_log("Adding universal excluded name constraint for type %d.\n", type);
+ switch (type) {
+ case GNUTLS_SAN_IPADDRESS:
+ // add universal restricted range for IPv4
+ tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 8);
+ if (tmp == NULL) {
+ _gnutls_name_constraints_node_free(dest);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ tmp->next = *_nc_excluded;
+ *_nc_excluded = tmp;
+ // add universal restricted range for IPv6
+ tmp = name_constraints_node_new(GNUTLS_SAN_IPADDRESS, NULL, 32);
+ if (tmp == NULL) {
+ _gnutls_name_constraints_node_free(dest);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ tmp->next = *_nc_excluded;
+ *_nc_excluded = tmp;
+ break;
+ case GNUTLS_SAN_DNSNAME:
+ case GNUTLS_SAN_RFC822NAME:
+ tmp = name_constraints_node_new(type, NULL, 0);
+ if (tmp == NULL) {
+ _gnutls_name_constraints_node_free(dest);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ tmp->next = *_nc_excluded;
+ *_nc_excluded = tmp;
+ break;
+ default: // do nothing, at least one node was already moved in phase 1
+ break;
+ }
+ }
+ return GNUTLS_E_SUCCESS;
+}
+
+static int _gnutls_name_constraints_append(name_constraints_node_st **_nc,
+ name_constraints_node_st *_nc2)
+{
+ name_constraints_node_st *nc, *nc2;
+ struct name_constraints_node_st *tmp;
+
+ if (_nc2 == NULL)
+ return 0;
+
+ nc2 = _nc2;
+ while (nc2) {
+ nc = *_nc;
+
+ tmp = name_constraints_node_new(nc2->type, nc2->name.data, nc2->name.size);
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ tmp->next = nc;
+ *_nc = tmp;
+
+ nc2 = nc2->next;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_name_constraints:
+ * @crt: should contain a #gnutls_x509_crt_t type
+ * @nc: The nameconstraints intermediate type
+ * @flags: zero or %GNUTLS_EXT_FLAG_APPEND
+ * @critical: the extension status
+ *
+ * This function will return an intermediate type containing
+ * the name constraints of the provided CA certificate. That
+ * structure can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND,
+ * then if the @nc structure is empty this function will behave
+ * identically as if the flag was not set.
+ * Otherwise if there are elements in the @nc structure then the
+ * constraints will be merged with the existing constraints following
+ * RFC5280 p6.1.4 (excluded constraints will be appended, permitted
+ * will be intersected).
+ *
+ * Note that @nc must be initialized prior to calling this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_crt_get_name_constraints(gnutls_x509_crt_t crt,
+ gnutls_x509_name_constraints_t nc,
+ unsigned int flags,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t der = { NULL, 0 };
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.30", 0, &der,
+ critical);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (der.size == 0 || der.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ ret = gnutls_x509_ext_import_name_constraints(&der, nc, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ _gnutls_free_datum(&der);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_name_constraints_deinit:
+ * @nc: The nameconstraints
+ *
+ * This function will deinitialize a name constraints type.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc)
+{
+ _gnutls_name_constraints_node_free(nc->permitted);
+ _gnutls_name_constraints_node_free(nc->excluded);
+
+ gnutls_free(nc);
+}
+
+/**
+ * gnutls_x509_name_constraints_init:
+ * @nc: The nameconstraints
+ *
+ * This function will initialize a name constraints type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_init(gnutls_x509_name_constraints_t *nc)
+{
+ *nc = gnutls_calloc(1, sizeof(struct gnutls_name_constraints_st));
+ if (*nc == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+static
+int name_constraints_add(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name,
+ unsigned permitted)
+{
+ struct name_constraints_node_st * tmp, *prev = NULL;
+ int ret;
+
+ ret = validate_name_constraints_node(type, name);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (permitted != 0)
+ prev = tmp = nc->permitted;
+ else
+ prev = tmp = nc->excluded;
+
+ while(tmp != NULL) {
+ tmp = tmp->next;
+ if (tmp != NULL)
+ prev = tmp;
+ }
+
+ tmp = name_constraints_node_new(type, name->data, name->size);
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ tmp->next = NULL;
+
+ if (prev == NULL) {
+ if (permitted != 0)
+ nc->permitted = tmp;
+ else
+ nc->excluded = tmp;
+ } else
+ prev->next = tmp;
+
+ return 0;
+}
+
+/*-
+ * _gnutls_x509_name_constraints_merge:
+ * @nc: The nameconstraints
+ * @nc2: The name constraints to be merged with
+ *
+ * This function will merge the provided name constraints structures
+ * as per RFC5280 p6.1.4. That is, the excluded constraints will be appended,
+ * and permitted will be intersected. The intersection assumes that @nc
+ * is the root CA constraints.
+ *
+ * The merged constraints will be placed in @nc.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.5.0
+ -*/
+int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_name_constraints_t nc2)
+{
+ int ret;
+
+ ret =
+ _gnutls_name_constraints_intersect(&nc->permitted,
+ nc2->permitted, &nc->excluded);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_name_constraints_append(&nc->excluded,
+ nc2->excluded);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_name_constraints_add_permitted:
+ * @nc: The nameconstraints
+ * @type: The type of the constraints
+ * @name: The data of the constraints
+ *
+ * This function will add a name constraint to the list of permitted
+ * constraints. The constraints @type can be any of the following types:
+ * %GNUTLS_SAN_DNSNAME, %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DN,
+ * %GNUTLS_SAN_URI, %GNUTLS_SAN_IPADDRESS. For the latter, an IP address
+ * in network byte order is expected, followed by its network mask.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_add_permitted(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name)
+{
+ return name_constraints_add(nc, type, name, 1);
+}
+
+/**
+ * gnutls_x509_name_constraints_add_excluded:
+ * @nc: The nameconstraints
+ * @type: The type of the constraints
+ * @name: The data of the constraints
+ *
+ * This function will add a name constraint to the list of excluded
+ * constraints. The constraints @type can be any of the following types:
+ * %GNUTLS_SAN_DNSNAME, %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DN,
+ * %GNUTLS_SAN_URI, %GNUTLS_SAN_IPADDRESS. For the latter, an IP address
+ * in network byte order is expected, followed by its network mask (which is
+ * 4 bytes in IPv4 or 16-bytes in IPv6).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_add_excluded(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name)
+{
+ return name_constraints_add(nc, type, name, 0);
+}
+
+/**
+ * gnutls_x509_crt_set_name_constraints:
+ * @crt: The certificate
+ * @nc: The nameconstraints structure
+ * @critical: whether this extension will be critical
+ *
+ * This function will set the provided name constraints to
+ * the certificate extension list. This extension is always
+ * marked as critical.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_crt_set_name_constraints(gnutls_x509_crt_t crt,
+ gnutls_x509_name_constraints_t nc,
+ unsigned int critical)
+{
+int ret;
+gnutls_datum_t der;
+
+ ret = gnutls_x509_ext_export_name_constraints(nc, &der);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.30", &der, critical);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ crt->use_extensions = 1;
+
+cleanup:
+ _gnutls_free_datum(&der);
+ return ret;
+}
+
+static
+unsigned ends_with(const gnutls_datum_t * str, const gnutls_datum_t * suffix)
+{
+ unsigned char *tree;
+ unsigned int treelen;
+
+ if (suffix->size >= str->size)
+ return 0;
+
+ tree = suffix->data;
+ treelen = suffix->size;
+ if((treelen > 0) && (tree[0] == '.')) {
+ tree++;
+ treelen--;
+ }
+
+ if (memcmp(str->data + str->size - treelen, tree, treelen) == 0 &&
+ str->data[str->size - treelen -1] == '.')
+ return 1; /* match */
+
+ return 0;
+}
+
+static
+unsigned email_ends_with(const gnutls_datum_t * str, const gnutls_datum_t * suffix)
+{
+ if (suffix->size >= str->size)
+ return 0;
+
+ if (suffix->size > 1 && suffix->data[0] == '.') {
+ /* .domain.com */
+ if (memcmp(str->data + str->size - suffix->size, suffix->data, suffix->size) == 0)
+ return 1; /* match */
+ } else {
+ if (memcmp(str->data + str->size - suffix->size, suffix->data, suffix->size) == 0 &&
+ str->data[str->size - suffix->size -1] == '@')
+ return 1; /* match */
+ }
+
+ return 0;
+}
+
+static unsigned dnsname_matches(const gnutls_datum_t *name, const gnutls_datum_t *suffix)
+{
+ _gnutls_hard_log("matching %.*s with DNS constraint %.*s\n", name->size, name->data,
+ suffix->size, suffix->data);
+
+ if (suffix->size == name->size && memcmp(suffix->data, name->data, suffix->size) == 0)
+ return 1; /* match */
+
+ return ends_with(name, suffix);
+}
+
+static unsigned email_matches(const gnutls_datum_t *name, const gnutls_datum_t *suffix)
+{
+ _gnutls_hard_log("matching %.*s with e-mail constraint %.*s\n", name->size, name->data,
+ suffix->size, suffix->data);
+
+ if (suffix->size == name->size && memcmp(suffix->data, name->data, suffix->size) == 0)
+ return 1; /* match */
+
+ return email_ends_with(name, suffix);
+}
+
+/*-
+ * name_constraints_intersect_nodes:
+ * @nc1: name constraints node 1
+ * @nc2: name constraints node 2
+ * @_intersection: newly allocated node with intersected constraints,
+ * NULL if the intersection is empty
+ *
+ * Inspect 2 name constraints nodes (of possibly different types) and allocate
+ * a new node with intersection of given constraints.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ -*/
+static int
+name_constraints_intersect_nodes(name_constraints_node_st * nc1,
+ name_constraints_node_st * nc2,
+ name_constraints_node_st ** _intersection)
+{
+ // presume empty intersection
+ name_constraints_node_st *intersection = NULL;
+ name_constraints_node_st *to_copy = NULL;
+ unsigned iplength = 0;
+ unsigned byte;
+
+ *_intersection = NULL;
+
+ if (nc1->type != nc2->type) {
+ return GNUTLS_E_SUCCESS;
+ }
+ switch (nc1->type) {
+ case GNUTLS_SAN_DNSNAME:
+ if (!dnsname_matches(&nc2->name, &nc1->name))
+ return GNUTLS_E_SUCCESS;
+ to_copy = nc2;
+ break;
+ case GNUTLS_SAN_RFC822NAME:
+ if (!email_matches(&nc2->name, &nc1->name))
+ return GNUTLS_E_SUCCESS;
+ to_copy = nc2;
+ break;
+ case GNUTLS_SAN_IPADDRESS:
+ if (nc1->name.size != nc2->name.size)
+ return GNUTLS_E_SUCCESS;
+ iplength = nc1->name.size/2;
+ for (byte = 0; byte < iplength; byte++) {
+ if (((nc1->name.data[byte]^nc2->name.data[byte]) // XOR of addresses
+ & nc1->name.data[byte+iplength] // AND mask from nc1
+ & nc2->name.data[byte+iplength]) // AND mask from nc2
+ != 0) {
+ // CIDRS do not intersect
+ return GNUTLS_E_SUCCESS;
+ }
+ }
+ to_copy = nc2;
+ break;
+ default:
+ // for other types, we don't know how to do the intersection, assume empty
+ return GNUTLS_E_SUCCESS;
+ }
+
+ // copy existing node if applicable
+ if (to_copy != NULL) {
+ *_intersection = name_constraints_node_new(to_copy->type, to_copy->name.data, to_copy->name.size);
+ if (*_intersection == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ intersection = *_intersection;
+
+ assert(intersection->name.data != NULL);
+
+ if (intersection->type == GNUTLS_SAN_IPADDRESS) {
+ // make sure both IP addresses are correctly masked
+ _gnutls_mask_ip(intersection->name.data, intersection->name.data+iplength, iplength);
+ _gnutls_mask_ip(nc1->name.data, nc1->name.data+iplength, iplength);
+ // update intersection, if necessary (we already know one is subset of other)
+ for (byte = 0; byte < 2 * iplength; byte++) {
+ intersection->name.data[byte] |= nc1->name.data[byte];
+ }
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/*
+ * Returns: true if the certification is acceptable, and false otherwise.
+ */
+static
+unsigned check_unsupported_constraint(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type)
+{
+unsigned i;
+int ret;
+unsigned rtype;
+gnutls_datum_t rname;
+
+ /* check if there is a restrictions with that type, if
+ * yes, then reject the name.
+ */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != type)
+ continue;
+ else
+ return gnutls_assert_val(0);
+ }
+
+ } while(ret == 0);
+
+ return 1;
+}
+
+static
+unsigned check_dns_constraints(gnutls_x509_name_constraints_t nc,
+ const gnutls_datum_t * name)
+{
+unsigned i;
+int ret;
+unsigned rtype;
+unsigned allowed_found = 0;
+gnutls_datum_t rname;
+
+ /* check restrictions */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != GNUTLS_SAN_DNSNAME)
+ continue;
+
+ /* a name of value 0 means that the CA shouldn't have issued
+ * a certificate with a DNSNAME. */
+ if (rname.size == 0)
+ return gnutls_assert_val(0);
+
+ if (dnsname_matches(name, &rname) != 0)
+ return gnutls_assert_val(0); /* rejected */
+ }
+ } while(ret == 0);
+
+ /* check allowed */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != GNUTLS_SAN_DNSNAME)
+ continue;
+
+ if (rname.size == 0)
+ continue;
+
+ allowed_found = 1;
+
+ if (dnsname_matches(name, &rname) != 0)
+ return 1; /* accepted */
+ }
+ } while(ret == 0);
+
+ if (allowed_found != 0) /* there are allowed directives but this host wasn't found */
+ return gnutls_assert_val(0);
+
+ return 1;
+}
+
+static
+unsigned check_email_constraints(gnutls_x509_name_constraints_t nc,
+ const gnutls_datum_t * name)
+{
+unsigned i;
+int ret;
+unsigned rtype;
+unsigned allowed_found = 0;
+gnutls_datum_t rname;
+
+ /* check restrictions */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != GNUTLS_SAN_RFC822NAME)
+ continue;
+
+ /* a name of value 0 means that the CA shouldn't have issued
+ * a certificate with an e-mail. */
+ if (rname.size == 0)
+ return gnutls_assert_val(0);
+
+ if (email_matches(name, &rname) != 0)
+ return gnutls_assert_val(0); /* rejected */
+ }
+ } while(ret == 0);
+
+ /* check allowed */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != GNUTLS_SAN_RFC822NAME)
+ continue;
+
+ if (rname.size == 0)
+ continue;
+
+ allowed_found = 1;
+
+ if (email_matches(name, &rname) != 0)
+ return 1; /* accepted */
+ }
+ } while(ret == 0);
+
+ if (allowed_found != 0) /* there are allowed directives but this host wasn't found */
+ return gnutls_assert_val(0);
+
+ return 1;
+}
+
+static
+unsigned check_ip_constraints(gnutls_x509_name_constraints_t nc,
+ const gnutls_datum_t * name)
+{
+ unsigned i;
+ int ret;
+ unsigned rtype;
+ unsigned allowed_found = 0;
+ gnutls_datum_t rname;
+
+ /* check restrictions */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != GNUTLS_SAN_IPADDRESS)
+ continue;
+
+ /* do not check IPv4 against IPv6 constraints and vice versa */
+ if (name->size != rname.size / 2)
+ continue;
+
+ if (ip_in_cidr(name, &rname) != 0)
+ return gnutls_assert_val(0); /* rejected */
+ }
+ } while(ret == 0);
+
+ /* check allowed */
+ i = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_permitted(nc, i++, &rtype, &rname);
+ if (ret >= 0) {
+ if (rtype != GNUTLS_SAN_IPADDRESS)
+ continue;
+
+ /* do not check IPv4 against IPv6 constraints and vice versa */
+ if (name->size != rname.size / 2)
+ continue;
+
+ allowed_found = 1;
+
+ if (ip_in_cidr(name, &rname) != 0)
+ return 1; /* accepted */
+ }
+ } while(ret == 0);
+
+ if (allowed_found != 0) /* there are allowed directives but this host wasn't found */
+ return gnutls_assert_val(0);
+
+ return 1;
+}
+
+/**
+ * gnutls_x509_name_constraints_check:
+ * @nc: the extracted name constraints
+ * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t)
+ * @name: the name to be checked
+ *
+ * This function will check the provided name against the constraints in
+ * @nc using the RFC5280 rules. Currently this function is limited to DNS
+ * names, emails and IP addresses (of type %GNUTLS_SAN_DNSNAME,
+ * %GNUTLS_SAN_RFC822NAME and %GNUTLS_SAN_IPADDRESS).
+ *
+ * Returns: zero if the provided name is not acceptable, and non-zero otherwise.
+ *
+ * Since: 3.3.0
+ **/
+unsigned gnutls_x509_name_constraints_check(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * name)
+{
+ if (type == GNUTLS_SAN_DNSNAME)
+ return check_dns_constraints(nc, name);
+
+ if (type == GNUTLS_SAN_RFC822NAME)
+ return check_email_constraints(nc, name);
+
+ if (type == GNUTLS_SAN_IPADDRESS)
+ return check_ip_constraints(nc, name);
+
+ return check_unsupported_constraint(nc, type);
+}
+
+/* This function checks for unsupported constraints, that we also
+ * know their structure. That is it will fail only if the constraint
+ * is present in the CA, _and_ the name in the end certificate contains
+ * the constrained element.
+ *
+ * Returns: true if the certification is acceptable, and false otherwise
+ */
+static unsigned check_unsupported_constraint2(gnutls_x509_crt_t cert,
+ gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type)
+{
+ unsigned idx, found_one;
+ char name[MAX_CN];
+ size_t name_size;
+ unsigned san_type;
+ int ret;
+
+ found_one = 0;
+
+ for (idx=0;;idx++) {
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_subject_alt_name2(cert,
+ idx, name, &name_size, &san_type, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret < 0)
+ return gnutls_assert_val(0);
+
+ if (san_type != GNUTLS_SAN_URI)
+ continue;
+
+ found_one = 1;
+ break;
+ }
+
+ if (found_one != 0)
+ return check_unsupported_constraint(nc, type);
+
+ /* no name was found in the certificate, so accept */
+ return 1;
+}
+
+/**
+ * gnutls_x509_name_constraints_check_crt:
+ * @nc: the extracted name constraints
+ * @type: the type of the constraint to check (of type gnutls_x509_subject_alt_name_t)
+ * @cert: the certificate to be checked
+ *
+ * This function will check the provided certificate names against the constraints in
+ * @nc using the RFC5280 rules. It will traverse all the certificate's names and
+ * alternative names.
+ *
+ * Currently this function is limited to DNS
+ * names and emails (of type %GNUTLS_SAN_DNSNAME and %GNUTLS_SAN_RFC822NAME).
+ *
+ * Returns: zero if the provided name is not acceptable, and non-zero otherwise.
+ *
+ * Since: 3.3.0
+ **/
+unsigned gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_subject_alt_name_t type,
+ gnutls_x509_crt_t cert)
+{
+char name[MAX_CN];
+size_t name_size;
+int ret;
+unsigned idx, t, san_type;
+gnutls_datum_t n;
+unsigned found_one;
+
+ if (is_nc_empty(nc, type) != 0)
+ return 1; /* shortcut; no constraints to check */
+
+ if (type == GNUTLS_SAN_RFC822NAME) {
+ found_one = 0;
+ for (idx=0;;idx++) {
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_subject_alt_name2(cert,
+ idx, name, &name_size, &san_type, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret < 0)
+ return gnutls_assert_val(0);
+
+ if (san_type != GNUTLS_SAN_RFC822NAME)
+ continue;
+
+ found_one = 1;
+ n.data = (void*)name;
+ n.size = name_size;
+ t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME,
+ &n);
+ if (t == 0)
+ return gnutls_assert_val(t);
+ }
+
+ /* there is at least a single e-mail. That means that the EMAIL field will
+ * not be used for verifying the identity of the holder. */
+ if (found_one != 0)
+ return 1;
+
+ do {
+ /* ensure there is only a single EMAIL, similarly to CN handling (rfc6125) */
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL,
+ 1, 0, name, &name_size);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ return gnutls_assert_val(0);
+
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL,
+ 0, 0, name, &name_size);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret < 0)
+ return gnutls_assert_val(0);
+
+ found_one = 1;
+ n.data = (void*)name;
+ n.size = name_size;
+ t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_RFC822NAME, &n);
+ if (t == 0)
+ return gnutls_assert_val(t);
+ } while(0);
+
+ /* passed */
+ if (found_one != 0)
+ return 1;
+ else {
+ /* no name was found. According to RFC5280:
+ * If no name of the type is in the certificate, the certificate is acceptable.
+ */
+ return gnutls_assert_val(1);
+ }
+ } else if (type == GNUTLS_SAN_DNSNAME) {
+ found_one = 0;
+ for (idx=0;;idx++) {
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_subject_alt_name2(cert,
+ idx, name, &name_size, &san_type, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret < 0)
+ return gnutls_assert_val(0);
+
+ if (san_type != GNUTLS_SAN_DNSNAME)
+ continue;
+
+ found_one = 1;
+ n.data = (void*)name;
+ n.size = name_size;
+ t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME,
+ &n);
+ if (t == 0)
+ return gnutls_assert_val(t);
+ }
+
+ /* there is at least a single DNS name. That means that the CN will
+ * not be used for verifying the identity of the holder. */
+ if (found_one != 0)
+ return 1;
+
+ /* verify the name constraints against the CN, if the certificate is
+ * not a CA. We do this check only on certificates marked as WWW server,
+ * because that's where the CN check is only performed. */
+ if (_gnutls_check_key_purpose(cert, GNUTLS_KP_TLS_WWW_SERVER, 0) != 0)
+ do {
+ /* ensure there is only a single CN, according to rfc6125 */
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME,
+ 1, 0, name, &name_size);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ return gnutls_assert_val(0);
+
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME,
+ 0, 0, name, &name_size);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret < 0)
+ return gnutls_assert_val(0);
+
+ found_one = 1;
+ n.data = (void*)name;
+ n.size = name_size;
+ t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_DNSNAME,
+ &n);
+ if (t == 0)
+ return gnutls_assert_val(t);
+ } while(0);
+
+ /* passed */
+ if (found_one != 0)
+ return 1;
+ else {
+ /* no name was found. According to RFC5280:
+ * If no name of the type is in the certificate, the certificate is acceptable.
+ */
+ return gnutls_assert_val(1);
+ }
+ } else if (type == GNUTLS_SAN_IPADDRESS) {
+ found_one = 0;
+ for (idx=0;;idx++) {
+ name_size = sizeof(name);
+ ret = gnutls_x509_crt_get_subject_alt_name2(cert,
+ idx, name, &name_size, &san_type, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret < 0)
+ return gnutls_assert_val(0);
+
+ if (san_type != GNUTLS_SAN_IPADDRESS)
+ continue;
+
+ found_one = 1;
+ n.data = (void*)name;
+ n.size = name_size;
+ t = gnutls_x509_name_constraints_check(nc, GNUTLS_SAN_IPADDRESS, &n);
+ if (t == 0)
+ return gnutls_assert_val(t);
+ }
+
+ /* there is at least a single IP address. */
+
+ if (found_one != 0) {
+ return 1;
+ } else {
+ /* no name was found. According to RFC5280:
+ * If no name of the type is in the certificate, the certificate is acceptable.
+ */
+ return gnutls_assert_val(1);
+ }
+ } else if (type == GNUTLS_SAN_URI) {
+ return check_unsupported_constraint2(cert, nc, type);
+ } else
+ return check_unsupported_constraint(nc, type);
+}
+
+/**
+ * gnutls_x509_name_constraints_get_permitted:
+ * @nc: the extracted name constraints
+ * @idx: the index of the constraint
+ * @type: the type of the constraint (of type gnutls_x509_subject_alt_name_t)
+ * @name: the name in the constraint (of the specific type)
+ *
+ * This function will return an intermediate type containing
+ * the name constraints of the provided CA certificate. That
+ * structure can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * The name should be treated as constant and valid for the lifetime of @nc.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_get_permitted(gnutls_x509_name_constraints_t nc,
+ unsigned idx,
+ unsigned *type, gnutls_datum_t * name)
+{
+ unsigned int i;
+ struct name_constraints_node_st * tmp = nc->permitted;
+
+ for (i = 0; i < idx; i++) {
+ if (tmp == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ tmp = tmp->next;
+ }
+
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ *type = tmp->type;
+ *name = tmp->name;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_name_constraints_get_excluded:
+ * @nc: the extracted name constraints
+ * @idx: the index of the constraint
+ * @type: the type of the constraint (of type gnutls_x509_subject_alt_name_t)
+ * @name: the name in the constraint (of the specific type)
+ *
+ * This function will return an intermediate type containing
+ * the name constraints of the provided CA certificate. That
+ * structure can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * The name should be treated as constant and valid for the lifetime of @nc.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_name_constraints_get_excluded(gnutls_x509_name_constraints_t nc,
+ unsigned idx,
+ unsigned *type, gnutls_datum_t * name)
+{
+ unsigned int i;
+ struct name_constraints_node_st * tmp = nc->excluded;
+
+ for (i = 0; i < idx; i++) {
+ if (tmp == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ tmp = tmp->next;
+ }
+
+ if (tmp == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ *type = tmp->type;
+ *name = tmp->name;
+
+ return 0;
+}
diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c
new file mode 100644
index 0000000..81f3d7e
--- /dev/null
+++ b/lib/x509/ocsp.c
@@ -0,0 +1,2642 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+
+#include "gnutls_int.h"
+#include <global.h>
+#include "errors.h"
+#include <libtasn1.h>
+#include <pk.h>
+#include "common.h"
+#include "verify-high.h"
+#include "x509.h"
+#include "ocsp.h"
+
+#include <gnutls/ocsp.h>
+#include <auth/cert.h>
+
+#include <assert.h>
+#include "intprops.h"
+
+typedef struct gnutls_ocsp_req_int {
+ asn1_node req;
+ unsigned init;
+} gnutls_ocsp_req_int;
+
+typedef struct gnutls_ocsp_resp_int {
+ asn1_node resp;
+ gnutls_datum_t response_type_oid;
+ asn1_node basicresp;
+ gnutls_datum_t der;
+ unsigned init;
+} gnutls_ocsp_resp_int;
+
+#define MAX_TIME 64
+
+/**
+ * gnutls_ocsp_req_init:
+ * @req: A pointer to the type to be initialized
+ *
+ * This function will initialize an OCSP request structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_ocsp_req_init(gnutls_ocsp_req_t * req)
+{
+ gnutls_ocsp_req_t tmp =
+ gnutls_calloc(1, sizeof(gnutls_ocsp_req_int));
+ int ret;
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.OCSPRequest",
+ &tmp->req);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return _gnutls_asn2err(ret);
+ }
+
+ *req = tmp;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_deinit:
+ * @req: The data to be deinitialized
+ *
+ * This function will deinitialize a OCSP request structure.
+ **/
+void gnutls_ocsp_req_deinit(gnutls_ocsp_req_t req)
+{
+ if (!req)
+ return;
+
+ if (req->req)
+ asn1_delete_structure(&req->req);
+
+ req->req = NULL;
+ gnutls_free(req);
+}
+
+/**
+ * gnutls_ocsp_resp_init:
+ * @resp: A pointer to the type to be initialized
+ *
+ * This function will initialize an OCSP response structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_ocsp_resp_init(gnutls_ocsp_resp_t * resp)
+{
+ gnutls_ocsp_resp_t tmp =
+ gnutls_calloc(1, sizeof(gnutls_ocsp_resp_int));
+ int ret;
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.OCSPResponse", &tmp->resp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.BasicOCSPResponse",
+ &tmp->basicresp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&tmp->resp);
+ gnutls_free(tmp);
+ return _gnutls_asn2err(ret);
+ }
+
+ *resp = tmp;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_deinit:
+ * @resp: The data to be deinitialized
+ *
+ * This function will deinitialize a OCSP response structure.
+ **/
+void gnutls_ocsp_resp_deinit(gnutls_ocsp_resp_t resp)
+{
+ if (!resp)
+ return;
+
+ if (resp->resp)
+ asn1_delete_structure(&resp->resp);
+ gnutls_free(resp->response_type_oid.data);
+ if (resp->basicresp)
+ asn1_delete_structure(&resp->basicresp);
+
+ resp->resp = NULL;
+ resp->basicresp = NULL;
+
+ gnutls_free(resp->der.data);
+ gnutls_free(resp);
+}
+
+/**
+ * gnutls_ocsp_req_import:
+ * @req: The data to store the parsed request.
+ * @data: DER encoded OCSP request.
+ *
+ * This function will convert the given DER encoded OCSP request to
+ * the native #gnutls_ocsp_req_t format. The output will be stored in
+ * @req.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_req_import(gnutls_ocsp_req_t req, const gnutls_datum_t * data)
+{
+ int ret = 0;
+
+ if (req == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (req->init) {
+ /* Any earlier _asn1_strict_der_decode will modify the ASN.1
+ structure, so we need to replace it with a fresh
+ structure. */
+ asn1_delete_structure(&req->req);
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.OCSPRequest", &req->req);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+ }
+ req->init = 1;
+
+ ret = _asn1_strict_der_decode(&req->req, data->data, data->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_import:
+ * @resp: The data to store the parsed response.
+ * @data: DER encoded OCSP response.
+ *
+ * This function will convert the given DER encoded OCSP response to
+ * the native #gnutls_ocsp_resp_t format. It also decodes the Basic
+ * OCSP Response part, if any. The output will be stored in @resp.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_import(gnutls_ocsp_resp_t resp,
+ const gnutls_datum_t * data)
+{
+ return gnutls_ocsp_resp_import2(resp, data, GNUTLS_X509_FMT_DER);
+}
+
+/**
+ * gnutls_ocsp_resp_import2:
+ * @resp: The data to store the parsed response.
+ * @data: DER or PEM encoded OCSP response.
+ * @fmt: DER or PEM
+ *
+ * This function will convert the given OCSP response to
+ * the native #gnutls_ocsp_resp_t format. It also decodes the Basic
+ * OCSP Response part, if any. The output will be stored in @resp.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_ocsp_resp_import2(gnutls_ocsp_resp_t resp,
+ const gnutls_datum_t *data,
+ gnutls_x509_crt_fmt_t fmt)
+{
+ int ret = 0;
+ gnutls_datum_t der;
+
+ if (resp == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ der.data = data->data;
+ der.size = data->size;
+
+ if (fmt == GNUTLS_X509_FMT_PEM) {
+ ret = gnutls_pem_base64_decode2(BARE_PEM_OCSP_RESPONSE, data, &der);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ if (resp->init != 0) {
+ /* Any earlier _asn1_strict_der_decode will modify the ASN.1
+ structure, so we need to replace it with a fresh
+ structure. */
+ asn1_delete_structure(&resp->resp);
+ if (resp->basicresp)
+ asn1_delete_structure(&resp->basicresp);
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.OCSPResponse",
+ &resp->resp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.BasicOCSPResponse",
+ &resp->basicresp);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ gnutls_free(resp->der.data);
+ }
+
+ resp->init = 1;
+ ret = _asn1_strict_der_decode(&resp->resp, der.data, der.size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ if (gnutls_ocsp_resp_get_status(resp) !=
+ GNUTLS_OCSP_RESP_SUCCESSFUL) {
+ ret = GNUTLS_E_SUCCESS;
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.responseType",
+ &resp->response_type_oid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"
+
+ if (resp->response_type_oid.size == sizeof(OCSP_BASIC) - 1
+ && memcmp(resp->response_type_oid.data, OCSP_BASIC,
+ resp->response_type_oid.size) == 0) {
+
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.response", &resp->der);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _asn1_strict_der_decode(&resp->basicresp, resp->der.data, resp->der.size,
+ NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+ } else {
+ asn1_delete_structure(&resp->basicresp);
+ resp->basicresp = NULL;
+ }
+
+ ret = GNUTLS_E_SUCCESS;
+cleanup:
+ if (der.data != data->data)
+ gnutls_free(der.data);
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_req_export:
+ * @req: Holds the OCSP request
+ * @data: newly allocate buffer holding DER encoded OCSP request
+ *
+ * This function will export the OCSP request to DER format.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int gnutls_ocsp_req_export(gnutls_ocsp_req_const_t req, gnutls_datum_t * data)
+{
+ int ret;
+
+ if (req == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* XXX remove when we support these fields */
+ (void)asn1_write_value(req->req, "tbsRequest.requestorName", NULL, 0);
+ (void)asn1_write_value(req->req, "optionalSignature", NULL, 0);
+
+ /* prune extension field if we don't have any extension */
+ ret = gnutls_ocsp_req_get_extension(req, 0, NULL, NULL, NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ (void)asn1_write_value(req->req, "tbsRequest.requestExtensions",
+ NULL, 0);
+
+ return _gnutls_x509_get_raw_field(req->req, "", data);
+}
+
+/**
+ * gnutls_ocsp_resp_export:
+ * @resp: Holds the OCSP response
+ * @data: newly allocate buffer holding DER encoded OCSP response
+ *
+ * This function will export the OCSP response to DER format.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int gnutls_ocsp_resp_export(gnutls_ocsp_resp_const_t resp, gnutls_datum_t * data)
+{
+ return gnutls_ocsp_resp_export2(resp, data, GNUTLS_X509_FMT_DER);
+}
+
+/**
+ * gnutls_ocsp_resp_export2:
+ * @resp: Holds the OCSP response
+ * @data: newly allocate buffer holding DER or PEM encoded OCSP response
+ * @fmt: DER or PEM
+ *
+ * This function will export the OCSP response to DER or PEM format.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since: 3.6.3
+ **/
+int gnutls_ocsp_resp_export2(gnutls_ocsp_resp_const_t resp, gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t fmt)
+{
+ int ret;
+ gnutls_datum_t der;
+
+ if (resp == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_get_raw_field(resp->resp, "", &der);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (fmt == GNUTLS_X509_FMT_DER) {
+ data->data = der.data;
+ data->size = der.size;
+ return ret;
+ } else {
+ ret = gnutls_pem_base64_encode2("OCSP RESPONSE", &der, data);
+ gnutls_free(der.data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ }
+}
+
+/**
+ * gnutls_ocsp_req_get_version:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ *
+ * This function will return the version of the OCSP request.
+ * Typically this is always 1 indicating version 1.
+ *
+ * Returns: version of OCSP request, or a negative error code on error.
+ **/
+int gnutls_ocsp_req_get_version(gnutls_ocsp_req_const_t req)
+{
+ if (req == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_version(req->req, "tbsRequest.version");
+}
+
+/**
+ * gnutls_ocsp_req_get_cert_id:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm
+ * @issuer_name_hash: output buffer with hash of issuer's DN
+ * @issuer_key_hash: output buffer with hash of issuer's public key
+ * @serial_number: output buffer with serial number of certificate to check
+ *
+ * This function will return the certificate information of the
+ * @indx'ed request in the OCSP request. The information returned
+ * corresponds to the CertID structure:
+ *
+ * <informalexample><programlisting>
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ * serialNumber CertificateSerialNumber }
+ * </programlisting></informalexample>
+ *
+ * Each of the pointers to output variables may be NULL to indicate
+ * that the caller is not interested in that value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ **/
+int
+gnutls_ocsp_req_get_cert_id(gnutls_ocsp_req_const_t req,
+ unsigned indx,
+ gnutls_digest_algorithm_t * digest,
+ gnutls_datum_t * issuer_name_hash,
+ gnutls_datum_t * issuer_key_hash,
+ gnutls_datum_t * serial_number)
+{
+ gnutls_datum_t sa;
+ char name[MAX_NAME_SIZE];
+ int ret;
+
+ if (req == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.hashAlgorithm.algorithm",
+ indx + 1);
+ ret = _gnutls_x509_read_value(req->req, name, &sa);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_oid_to_digest((char *) sa.data);
+ _gnutls_free_datum(&sa);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (digest)
+ *digest = ret;
+
+ if (issuer_name_hash) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.issuerNameHash",
+ indx + 1);
+ ret =
+ _gnutls_x509_read_value(req->req, name,
+ issuer_name_hash);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (issuer_key_hash) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.issuerKeyHash",
+ indx + 1);
+ ret =
+ _gnutls_x509_read_value(req->req, name,
+ issuer_key_hash);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (issuer_name_hash)
+ gnutls_free(issuer_name_hash->data);
+ return ret;
+ }
+ }
+
+ if (serial_number) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestList.?%u.reqCert.serialNumber",
+ indx + 1);
+ ret =
+ _gnutls_x509_read_value(req->req, name, serial_number);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (issuer_name_hash)
+ gnutls_free(issuer_name_hash->data);
+ if (issuer_key_hash)
+ gnutls_free(issuer_key_hash->data);
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_add_cert_id:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @digest: hash algorithm, a #gnutls_digest_algorithm_t value
+ * @issuer_name_hash: hash of issuer's DN
+ * @issuer_key_hash: hash of issuer's public key
+ * @serial_number: serial number of certificate to check
+ *
+ * This function will add another request to the OCSP request for a
+ * particular certificate having the issuer name hash of
+ * @issuer_name_hash and issuer key hash of @issuer_key_hash (both
+ * hashed using @digest) and serial number @serial_number.
+ *
+ * The information needed corresponds to the CertID structure:
+ *
+ * <informalexample><programlisting>
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ * serialNumber CertificateSerialNumber }
+ * </programlisting></informalexample>
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_add_cert_id(gnutls_ocsp_req_t req,
+ gnutls_digest_algorithm_t digest,
+ const gnutls_datum_t * issuer_name_hash,
+ const gnutls_datum_t * issuer_key_hash,
+ const gnutls_datum_t * serial_number)
+{
+ int result;
+ const char *oid;
+
+ if (req == NULL || issuer_name_hash == NULL
+ || issuer_key_hash == NULL || serial_number == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ oid = _gnutls_x509_digest_to_oid(hash_to_entry(digest));
+ if (oid == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ asn1_write_value(req->req, "tbsRequest.requestList", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.algorithm",
+ oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* XXX we don't support any algorithm with parameters */
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.hashAlgorithm.parameters",
+ ASN1_NULL, ASN1_NULL_SIZE);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.issuerNameHash",
+ issuer_name_hash->data, issuer_name_hash->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.reqCert.issuerKeyHash",
+ issuer_key_hash->data, issuer_key_hash->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value
+ (req->req, "tbsRequest.requestList.?LAST.reqCert.serialNumber",
+ serial_number->data, serial_number->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* XXX add separate function that can add extensions too */
+ result = asn1_write_value
+ (req->req,
+ "tbsRequest.requestList.?LAST.singleRequestExtensions", NULL,
+ 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_add_cert:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @digest: hash algorithm, a #gnutls_digest_algorithm_t value
+ * @issuer: issuer of @subject certificate
+ * @cert: certificate to request status for
+ *
+ * This function will add another request to the OCSP request for a
+ * particular certificate. The issuer name hash, issuer key hash, and
+ * serial number fields is populated as follows. The issuer name and
+ * the serial number is taken from @cert. The issuer key is taken
+ * from @issuer. The hashed values will be hashed using the @digest
+ * algorithm, normally %GNUTLS_DIG_SHA1.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_add_cert(gnutls_ocsp_req_t req,
+ gnutls_digest_algorithm_t digest,
+ gnutls_x509_crt_t issuer, gnutls_x509_crt_t cert)
+{
+ int ret;
+ gnutls_datum_t sn, tmp, inh, ikh;
+ uint8_t inh_buf[MAX_HASH_SIZE];
+ uint8_t ikh_buf[MAX_HASH_SIZE];
+ size_t inhlen = MAX_HASH_SIZE;
+ size_t ikhlen = MAX_HASH_SIZE;
+
+ if (req == NULL || issuer == NULL || cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_der_encode(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ &tmp, 0);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_fingerprint(digest, &tmp, inh_buf, &inhlen);
+ gnutls_free(tmp.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ inh.size = inhlen;
+ inh.data = inh_buf;
+
+ ret = _gnutls_x509_read_value
+ (issuer->cert,
+ "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey", &tmp);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_fingerprint(digest, &tmp, ikh_buf, &ikhlen);
+ gnutls_free(tmp.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ ikh.size = ikhlen;
+ ikh.data = ikh_buf;
+
+ ret =
+ _gnutls_x509_read_value(cert->cert,
+ "tbsCertificate.serialNumber", &sn);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_ocsp_req_add_cert_id(req, digest, &inh, &ikh, &sn);
+ gnutls_free(sn.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_get_extension:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @oid: will hold newly allocated buffer with OID of extension, may be NULL
+ * @critical: output variable with critical flag, may be NULL.
+ * @data: will hold newly allocated buffer with extension data, may be NULL
+ *
+ * This function will return all information about the requested
+ * extension in the OCSP request. The information returned is the
+ * OID, the critical flag, and the data itself. The extension OID
+ * will be stored as a string. Any of @oid, @critical, and @data may
+ * be NULL which means that the caller is not interested in getting
+ * that information back.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @oid->data and @data->data.
+ *
+ * Since 3.7.0 @oid->size does not account for the terminating null byte.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ * be returned.
+ **/
+int
+gnutls_ocsp_req_get_extension(gnutls_ocsp_req_const_t req,
+ unsigned indx,
+ gnutls_datum_t * oid,
+ unsigned int *critical,
+ gnutls_datum_t * data)
+{
+ int ret;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!req) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestExtensions.?%u.critical", indx + 1);
+ len = sizeof(str_critical);
+ ret = asn1_read_value(req->req, name, str_critical, &len);
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ if (critical) {
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ if (oid) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestExtensions.?%u.extnID",
+ indx + 1);
+ ret = _gnutls_x509_read_value(req->req, name, oid);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (data) {
+ snprintf(name, sizeof(name),
+ "tbsRequest.requestExtensions.?%u.extnValue",
+ indx + 1);
+ ret = _gnutls_x509_read_value(req->req, name, data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (oid)
+ gnutls_free(oid->data);
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_set_extension:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @oid: buffer with OID of extension as a string.
+ * @critical: critical flag, normally false.
+ * @data: the extension data
+ *
+ * This function will add an extension to the OCSP request. Calling
+ * this function multiple times for the same OID will overwrite values
+ * from earlier calls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_set_extension(gnutls_ocsp_req_t req,
+ const char *oid,
+ unsigned int critical,
+ const gnutls_datum_t * data)
+{
+ if (req == NULL || oid == NULL || data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_set_extension(req->req, "tbsRequest.requestExtensions", oid,
+ data, critical);
+}
+
+/**
+ * gnutls_ocsp_req_get_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @critical: whether nonce extension is marked critical, or NULL
+ * @nonce: will hold newly allocated buffer with nonce data
+ *
+ * This function will return the OCSP request nonce extension data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @nonce->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_get_nonce(gnutls_ocsp_req_const_t req,
+ unsigned int *critical, gnutls_datum_t * nonce)
+{
+ int ret;
+ gnutls_datum_t tmp;
+
+ if (req == NULL || nonce == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_get_extension(req->req, "tbsRequest.requestExtensions",
+ GNUTLS_OCSP_NONCE, 0, &tmp, critical);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, tmp.data,
+ (size_t) tmp.size, nonce, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(tmp.data);
+ return ret;
+ }
+
+ gnutls_free(tmp.data);
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_req_set_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ * @critical: critical flag, normally false.
+ * @nonce: the nonce data
+ *
+ * This function will add an nonce extension to the OCSP request.
+ * Calling this function multiple times will overwrite values from
+ * earlier calls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_req_set_nonce(gnutls_ocsp_req_t req,
+ unsigned int critical,
+ const gnutls_datum_t * nonce)
+{
+ int ret;
+ gnutls_datum_t dernonce;
+ unsigned char temp[SIZEOF_UNSIGNED_LONG_INT + 1];
+ int len;
+
+ if (req == NULL || nonce == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ asn1_length_der(nonce->size, temp, &len);
+
+ dernonce.size = 1 + len + nonce->size;
+ dernonce.data = gnutls_malloc(dernonce.size);
+ if (dernonce.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ dernonce.data[0] = '\x04';
+ memcpy(dernonce.data + 1, temp, len);
+ memcpy(dernonce.data + 1 + len, nonce->data, nonce->size);
+
+ ret = _gnutls_set_extension(req->req, "tbsRequest.requestExtensions",
+ GNUTLS_OCSP_NONCE, &dernonce, critical);
+ gnutls_free(dernonce.data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_req_randomize_nonce:
+ * @req: should contain a #gnutls_ocsp_req_t type
+ *
+ * This function will add or update an nonce extension to the OCSP
+ * request with a newly generated random value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int gnutls_ocsp_req_randomize_nonce(gnutls_ocsp_req_t req)
+{
+ int ret;
+ uint8_t rndbuf[23];
+ gnutls_datum_t nonce = { rndbuf, sizeof(rndbuf) };
+
+ if (req == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, rndbuf, sizeof(rndbuf));
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_ocsp_req_set_nonce(req, 0, &nonce);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_status:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return the status of a OCSP response, an
+ * #gnutls_ocsp_resp_status_t enumeration.
+ *
+ * Returns: status of OCSP request as a #gnutls_ocsp_resp_status_t, or
+ * a negative error code on error.
+ **/
+int gnutls_ocsp_resp_get_status(gnutls_ocsp_resp_const_t resp)
+{
+ uint8_t str[1];
+ int len, ret;
+
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ len = sizeof(str);
+ ret = asn1_read_value(resp->resp, "responseStatus", str, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ if (len != 1)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+ switch (str[0]) {
+ case GNUTLS_OCSP_RESP_SUCCESSFUL:
+ case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
+ case GNUTLS_OCSP_RESP_INTERNALERROR:
+ case GNUTLS_OCSP_RESP_TRYLATER:
+ case GNUTLS_OCSP_RESP_SIGREQUIRED:
+ case GNUTLS_OCSP_RESP_UNAUTHORIZED:
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+ }
+
+ return (int) str[0];
+}
+
+/**
+ * gnutls_ocsp_resp_get_response:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @response_type_oid: newly allocated output buffer with response type OID
+ * @response: newly allocated output buffer with DER encoded response
+ *
+ * This function will extract the response type OID in and the
+ * response data from an OCSP response. Normally the
+ * @response_type_oid is always "1.3.6.1.5.5.7.48.1.1" which means the
+ * @response should be decoded as a Basic OCSP Response, but
+ * technically other response types could be used.
+ *
+ * This function is typically only useful when you want to extract the
+ * response type OID of an response for diagnostic purposes.
+ * Otherwise gnutls_ocsp_resp_import() will decode the basic OCSP
+ * response part and the caller need not worry about that aspect.
+ *
+ * Since 3.7.0 @response_type_oid->size does not account for the terminating
+ * null byte.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_response(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * response_type_oid,
+ gnutls_datum_t * response)
+{
+ int ret;
+
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (response_type_oid != NULL) {
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.responseType",
+ response_type_oid);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (response != NULL) {
+ ret =
+ _gnutls_x509_read_value(resp->resp,
+ "responseBytes.response",
+ response);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_version:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return the version of the Basic OCSP Response.
+ * Typically this is always 1 indicating version 1.
+ *
+ * Returns: version of Basic OCSP response, or a negative error code
+ * on error.
+ **/
+int gnutls_ocsp_resp_get_version(gnutls_ocsp_resp_const_t resp)
+{
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_version(resp->resp, "tbsResponseData.version");
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @dn: newly allocated buffer with name
+ *
+ * This function will extract the name of the Basic OCSP Response in
+ * the provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string
+ * will be ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * If the responder ID is not a name but a hash, this function
+ * will return zero and the @dn elements will be set to %NULL.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_ocsp_resp_get_responder2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. When no data exist it will
+ * return success and set @dn elements to zero.
+ **/
+int
+gnutls_ocsp_resp_get_responder(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * dn)
+{
+ int ret;
+
+ ret = gnutls_ocsp_resp_get_responder2(resp, dn, GNUTLS_X509_DN_FLAG_COMPAT);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ dn->data = NULL;
+ dn->size = 0;
+ return 0; /* for backwards compatibility */
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder2:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @dn: newly allocated buffer with name
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will extract the name of the Basic OCSP Response in
+ * the provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. The output string
+ * will be ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * If the responder ID is not a name but a hash, this function
+ * will return zero and the @dn elements will be set to %NULL.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. When no data exist it will return
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ **/
+int
+gnutls_ocsp_resp_get_responder2(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * dn, unsigned flags)
+{
+ if (resp == NULL || dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ dn->data = NULL;
+ dn->size = 0;
+
+ return _gnutls_x509_get_dn(resp->basicresp,
+ "tbsResponseData.responderID.byName",
+ dn, flags);
+}
+
+/**
+ * gnutls_ocsp_resp_get_responder_by_key:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @type: should be %GNUTLS_OCSP_RESP_ID_KEY or %GNUTLS_OCSP_RESP_ID_DN
+ * @raw: newly allocated buffer with the raw ID
+ *
+ * This function will extract the raw key (or DN) ID of the Basic OCSP Response in
+ * the provided buffer. If the responder ID is not a key ID then
+ * this function will return %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @dn->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_resp_get_responder_raw_id(gnutls_ocsp_resp_const_t resp,
+ unsigned type,
+ gnutls_datum_t * raw)
+{
+ int ret;
+
+ if (resp == NULL || raw == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (type == GNUTLS_OCSP_RESP_ID_KEY)
+ ret = _gnutls_x509_read_value(resp->basicresp, "tbsResponseData.responderID.byKey", raw);
+ else {
+ gnutls_datum_t tmp;
+
+ /* simply reading a CHOICE of CHOICE value doesn't work in libtasn1 */
+ ret = _gnutls_x509_get_raw_field2(resp->basicresp, &resp->der,
+ "tbsResponseData.responderID.byName",
+ &tmp);
+ if (ret >= 0) {
+ int real;
+ /* skip the tag */
+ if (tmp.size < 2) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_GENERIC_ERROR;
+ goto fail;
+ }
+
+ tmp.data++;
+ tmp.size--;
+
+ ret = asn1_get_length_der(tmp.data, tmp.size, &real);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_GENERIC_ERROR;
+ goto fail;
+ }
+
+ if (tmp.size < (unsigned)real) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_GENERIC_ERROR;
+ goto fail;
+ }
+
+ tmp.data+=real;
+ tmp.size-=real;
+
+ ret = _gnutls_set_datum(raw, tmp.data, tmp.size);
+ }
+ }
+
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ fail:
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_produced:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return the time when the OCSP response was
+ * signed.
+ *
+ * Returns: signing time, or (time_t)-1 on error.
+ **/
+time_t gnutls_ocsp_resp_get_produced(gnutls_ocsp_resp_const_t resp)
+{
+ char ttime[MAX_TIME];
+ int len, ret;
+ time_t c_time;
+
+ if (resp == NULL || resp->basicresp == NULL) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ len = sizeof(ttime) - 1;
+ ret =
+ asn1_read_value(resp->basicresp, "tbsResponseData.producedAt",
+ ttime, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ c_time = _gnutls_x509_generalTime2gtime(ttime);
+
+ return c_time;
+}
+
+/**
+ * gnutls_ocsp_resp_check_crt:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @indx: Specifies response number to get. Use (0) to get the first one.
+ * @crt: The certificate to check
+ *
+ * This function will check whether the OCSP response
+ * is about the provided certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_ocsp_resp_check_crt(gnutls_ocsp_resp_const_t resp,
+ unsigned int indx, gnutls_x509_crt_t crt)
+{
+ int ret;
+ gnutls_digest_algorithm_t digest;
+ gnutls_datum_t rdn_hash = { NULL, 0 }, rserial = {
+ NULL, 0};
+ gnutls_datum_t cserial = { NULL, 0 };
+ gnutls_datum_t dn = { NULL, 0 };
+ uint8_t cdn_hash[MAX_HASH_SIZE];
+ size_t t, hash_len;
+
+ if (resp == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret =
+ gnutls_ocsp_resp_get_single(resp, indx, &digest, &rdn_hash,
+ NULL, &rserial, NULL, NULL, NULL,
+ NULL, NULL);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (rserial.size == 0 || digest == GNUTLS_DIG_UNKNOWN) {
+ ret = gnutls_assert_val(GNUTLS_E_OCSP_RESPONSE_ERROR);
+ goto cleanup;
+ }
+
+ hash_len = _gnutls_hash_get_algo_len(hash_to_entry(digest));
+ if (hash_len != rdn_hash.size) {
+ ret = gnutls_assert_val(GNUTLS_E_OCSP_RESPONSE_ERROR);
+ goto cleanup;
+ }
+
+ cserial.size = rserial.size;
+ cserial.data = gnutls_malloc(cserial.size);
+ if (cserial.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ t = cserial.size;
+ ret = gnutls_x509_crt_get_serial(crt, cserial.data, &t);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ cserial.size = t;
+
+ if (rserial.size != cserial.size
+ || memcmp(cserial.data, rserial.data, rserial.size) != 0) {
+ ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_get_raw_issuer_dn(crt, &dn);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_hash_fast(digest, dn.data, dn.size, cdn_hash);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (memcmp(cdn_hash, rdn_hash.data, hash_len) != 0) {
+ ret = GNUTLS_E_OCSP_RESPONSE_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(rdn_hash.data);
+ gnutls_free(rserial.data);
+ gnutls_free(cserial.data);
+ gnutls_free(dn.data);
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_single:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @indx: Specifies response number to get. Use (0) to get the first one.
+ * @digest: output variable with #gnutls_digest_algorithm_t hash algorithm
+ * @issuer_name_hash: output buffer with hash of issuer's DN
+ * @issuer_key_hash: output buffer with hash of issuer's public key
+ * @serial_number: output buffer with serial number of certificate to check
+ * @cert_status: a certificate status, a #gnutls_ocsp_cert_status_t enum.
+ * @this_update: time at which the status is known to be correct.
+ * @next_update: when newer information will be available, or (time_t)-1 if unspecified
+ * @revocation_time: when @cert_status is %GNUTLS_OCSP_CERT_REVOKED, holds time of revocation.
+ * @revocation_reason: revocation reason, a #gnutls_x509_crl_reason_t enum.
+ *
+ * This function will return the certificate information of the
+ * @indx'ed response in the Basic OCSP Response @resp. The
+ * information returned corresponds to the OCSP SingleResponse structure
+ * except the final singleExtensions.
+ *
+ * Each of the pointers to output variables may be NULL to indicate
+ * that the caller is not interested in that value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * CertID available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ **/
+int
+gnutls_ocsp_resp_get_single(gnutls_ocsp_resp_const_t resp,
+ unsigned indx,
+ gnutls_digest_algorithm_t * digest,
+ gnutls_datum_t * issuer_name_hash,
+ gnutls_datum_t * issuer_key_hash,
+ gnutls_datum_t * serial_number,
+ unsigned int *cert_status,
+ time_t * this_update,
+ time_t * next_update,
+ time_t * revocation_time,
+ unsigned int *revocation_reason)
+{
+ char name[MAX_NAME_SIZE];
+ int ret, result;
+ char oidtmp[MAX_OID_SIZE];
+ int len;
+ char ttime[MAX_TIME];
+
+ /* initialize any allocated values to NULL, to allow deallocation
+ * on error. */
+ if (issuer_name_hash)
+ issuer_name_hash->data = NULL;
+ if (issuer_key_hash)
+ issuer_key_hash->data = NULL;
+ if (serial_number)
+ serial_number->data = NULL;
+
+ if (digest) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.hashAlgorithm.algorithm",
+ indx + 1);
+ len = sizeof(oidtmp);
+ result = asn1_read_value(resp->basicresp, name, oidtmp, &len);
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ ret = gnutls_oid_to_digest(oidtmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ *digest = ret;
+ }
+
+ if (issuer_name_hash) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.issuerNameHash",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name,
+ issuer_name_hash);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (issuer_key_hash) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.issuerKeyHash",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name,
+ issuer_key_hash);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ if (serial_number) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certID.serialNumber",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name,
+ serial_number);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ if (cert_status) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certStatus",
+ indx + 1);
+
+ len = sizeof(oidtmp);
+ result = asn1_read_value(resp->basicresp, name, oidtmp, &len);
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto fail;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto fail;
+ }
+
+ if (len == 5 && memcmp(oidtmp, "good", len) == 0)
+ *cert_status = GNUTLS_OCSP_CERT_GOOD;
+ else if (len == 8
+ && memcmp(oidtmp, "revoked", len) == 0)
+ *cert_status = GNUTLS_OCSP_CERT_REVOKED;
+ else if (len == 8
+ && memcmp(oidtmp, "unknown", len) == 0)
+ *cert_status = GNUTLS_OCSP_CERT_UNKNOWN;
+ else {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_DER_ERROR;
+ goto fail;
+ }
+ }
+
+ if (this_update) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.thisUpdate",
+ indx + 1);
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(resp->basicresp, name, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = GNUTLS_E_ASN1_DER_ERROR;
+ goto fail;
+ } else {
+ *this_update =
+ _gnutls_x509_generalTime2gtime(ttime);
+ }
+ }
+
+ if (next_update) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.nextUpdate",
+ indx + 1);
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(resp->basicresp, name, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ *next_update = (time_t) (-1);
+ } else
+ *next_update =
+ _gnutls_x509_generalTime2gtime(ttime);
+ }
+
+ if (revocation_time) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certStatus."
+ "revoked.revocationTime", indx + 1);
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(resp->basicresp, name, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ *revocation_time = (time_t) (-1);
+ } else
+ *revocation_time =
+ _gnutls_x509_generalTime2gtime(ttime);
+ }
+
+ /* revocation_reason */
+ if (revocation_reason) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responses.?%u.certStatus."
+ "revoked.revocationReason", indx + 1);
+
+ ret = _gnutls_x509_read_uint(resp->basicresp, name,
+ revocation_reason);
+ if (ret < 0)
+ *revocation_reason =
+ GNUTLS_X509_CRLREASON_UNSPECIFIED;
+ }
+
+ return GNUTLS_E_SUCCESS;
+ fail:
+ if (issuer_name_hash)
+ gnutls_free(issuer_name_hash->data);
+ if (issuer_key_hash)
+ gnutls_free(issuer_key_hash->data);
+ if (serial_number)
+ gnutls_free(serial_number->data);
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_extension:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @indx: Specifies which extension OID to get. Use (0) to get the first one.
+ * @oid: will hold newly allocated buffer with OID of extension, may be NULL
+ * @critical: output variable with critical flag, may be NULL.
+ * @data: will hold newly allocated buffer with extension data, may be NULL
+ *
+ * This function will return all information about the requested
+ * extension in the OCSP response. The information returned is the
+ * OID, the critical flag, and the data itself. The extension OID
+ * will be stored as a string. Any of @oid, @critical, and @data may
+ * be NULL which means that the caller is not interested in getting
+ * that information back.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @oid->data and @data->data.
+ *
+ * Since 3.7.0 @oid->size does not account for the terminating null byte.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned. If you have reached the last
+ * extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will
+ * be returned.
+ **/
+int
+gnutls_ocsp_resp_get_extension(gnutls_ocsp_resp_const_t resp,
+ unsigned indx,
+ gnutls_datum_t * oid,
+ unsigned int *critical,
+ gnutls_datum_t * data)
+{
+ int ret;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!resp) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responseExtensions.?%u.critical",
+ indx + 1);
+ len = sizeof(str_critical);
+ ret = asn1_read_value(resp->basicresp, name, str_critical, &len);
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ if (critical) {
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ if (oid) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responseExtensions.?%u.extnID",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name, oid);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ if (data) {
+ snprintf(name, sizeof(name),
+ "tbsResponseData.responseExtensions.?%u.extnValue",
+ indx + 1);
+ ret = _gnutls_x509_read_value(resp->basicresp, name, data);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ if (oid)
+ gnutls_free(oid->data);
+ return ret;
+ }
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_nonce:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @critical: whether nonce extension is marked critical
+ * @nonce: will hold newly allocated buffer with nonce data
+ *
+ * This function will return the Basic OCSP Response nonce extension
+ * data.
+ *
+ * The caller needs to deallocate memory by calling gnutls_free() on
+ * @nonce->data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error code is returned.
+ **/
+int
+gnutls_ocsp_resp_get_nonce(gnutls_ocsp_resp_const_t resp,
+ unsigned int *critical, gnutls_datum_t * nonce)
+{
+ int ret;
+ gnutls_datum_t tmp;
+
+ ret =
+ _gnutls_get_extension(resp->basicresp,
+ "tbsResponseData.responseExtensions",
+ GNUTLS_OCSP_NONCE, 0, &tmp, critical);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, tmp.data,
+ (size_t) tmp.size, nonce, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_free(tmp.data);
+ return ret;
+ }
+
+ gnutls_free(tmp.data);
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_signature_algorithm:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm that has been used to
+ * sign the OCSP response.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value, or a negative error code
+ * on error.
+ **/
+int gnutls_ocsp_resp_get_signature_algorithm(gnutls_ocsp_resp_const_t resp)
+{
+ int ret;
+ gnutls_datum_t sa;
+
+ ret = _gnutls_x509_read_value(resp->basicresp,
+ "signatureAlgorithm.algorithm", &sa);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_oid_to_sign((char *) sa.data);
+
+ _gnutls_free_datum(&sa);
+
+ return ret;
+}
+
+/**
+ * gnutls_ocsp_resp_get_signature:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @sig: newly allocated output buffer with signature data
+ *
+ * This function will extract the signature field of a OCSP response.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_signature(gnutls_ocsp_resp_const_t resp,
+ gnutls_datum_t * sig)
+{
+ int ret;
+
+ if (resp == NULL || sig == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_read_value(resp->basicresp, "signature", sig);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_ocsp_resp_get_certs:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @certs: newly allocated array with #gnutls_x509_crt_t certificates
+ * @ncerts: output variable with number of allocated certs.
+ *
+ * This function will extract the X.509 certificates found in the
+ * Basic OCSP Response. The @certs output variable will hold a newly
+ * allocated zero-terminated array with X.509 certificates.
+ *
+ * Every certificate in the array needs to be de-allocated with
+ * gnutls_x509_crt_deinit() and the array itself must be freed using
+ * gnutls_free().
+ *
+ * Both the @certs and @ncerts variables may be NULL. Then the
+ * function will work as normal but will not return the NULL:d
+ * information. This can be used to get the number of certificates
+ * only, or to just get the certificate array without its size.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_get_certs(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_crt_t ** certs, size_t * ncerts)
+{
+ int ret;
+ size_t ctr = 0, i;
+ gnutls_x509_crt_t *tmpcerts = NULL, *tmpcerts2;
+ gnutls_datum_t c = { NULL, 0 };
+
+ if (resp == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ tmpcerts = gnutls_malloc(sizeof(*tmpcerts));
+ if (tmpcerts == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ for (;;) {
+ char name[MAX_NAME_SIZE];
+
+ snprintf(name, sizeof(name), "certs.?%u",
+ (unsigned int) (ctr + 1));
+ ret =
+ _gnutls_x509_der_encode(resp->basicresp, name, &c, 0);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ break;
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(ctr, 2))) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto error;
+ }
+
+ tmpcerts2 = _gnutls_reallocarray_fast(tmpcerts, ctr + 2,
+ sizeof(*tmpcerts));
+ if (tmpcerts2 == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto error;
+ }
+ tmpcerts = tmpcerts2;
+
+ ret = gnutls_x509_crt_init(&tmpcerts[ctr]);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+ ctr++;
+
+ ret = gnutls_x509_crt_import(tmpcerts[ctr - 1], &c,
+ GNUTLS_X509_FMT_DER);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ gnutls_free(c.data);
+ }
+
+ tmpcerts[ctr] = NULL;
+
+ if (ncerts)
+ *ncerts = ctr;
+ if (certs)
+ *certs = tmpcerts;
+ else {
+ /* clean up memory */
+ ret = GNUTLS_E_SUCCESS;
+ goto error;
+ }
+
+ return GNUTLS_E_SUCCESS;
+
+ error:
+ gnutls_free(c.data);
+ for (i = 0; i < ctr; i++)
+ gnutls_x509_crt_deinit(tmpcerts[i]);
+ gnutls_free(tmpcerts);
+ return ret;
+}
+
+/* Search the OCSP response for a certificate matching the responderId
+ mentioned in the OCSP response. */
+static gnutls_x509_crt_t find_signercert(gnutls_ocsp_resp_const_t resp)
+{
+ int rc;
+ gnutls_x509_crt_t *certs = NULL;
+ size_t ncerts = 0, i;
+ gnutls_datum_t riddn = {NULL, 0};
+ gnutls_datum_t keyid = {NULL, 0};
+ gnutls_x509_crt_t signercert = NULL;
+
+ rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_DN, &riddn);
+ if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &keyid);
+ }
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ rc = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ signercert = NULL;
+ goto quit;
+ }
+
+ for (i = 0; i < ncerts; i++) {
+ assert(certs[i] != NULL);
+ _gnutls_cert_log("checking whether signed against", certs[i]);
+ if (keyid.data != NULL) {
+ uint8_t digest[64]; /* to support longer key IDs */
+ gnutls_datum_t spki;
+ size_t digest_size = sizeof(digest);
+ int len;
+
+ _gnutls_debug_log("checking key ID against SPK identifier\n");
+
+ /* check subject key identifier as well, some certificates
+ * match that, but not the hash */
+ rc = gnutls_x509_crt_get_subject_key_id(certs[i], digest, &digest_size, NULL);
+ if (rc >= 0 && digest_size == keyid.size &&
+ memcmp(keyid.data, digest, digest_size) == 0) {
+ signercert = certs[i];
+ goto quit;
+ }
+
+ _gnutls_debug_log("checking key ID against SPKI hash\n");
+
+ /* continue with checking the hash */
+ rc = _gnutls_x509_get_raw_field2(certs[i]->cert, &certs[i]->der,
+ "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey",
+ &spki);
+ if (rc < 0 || spki.size < 6) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ /* For some reason the protocol requires we skip the
+ * tag, length and number of unused bits.
+ */
+ if (spki.data[0] != 0x03) { /* bit string */
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ rc = asn1_get_length_der(spki.data+1, spki.size-1, &len);
+ if (rc <= 0) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+ len += 1+1; /* skip unused bits as well */
+ if (len >= (int)spki.size) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ rc = gnutls_hash_fast(GNUTLS_DIG_SHA1, spki.data+len, spki.size-len, digest);
+ if (rc < 0) {
+ gnutls_assert();
+ signercert = NULL;
+ continue;
+ }
+
+ if ((20 == keyid.size) &&
+ memcmp(keyid.data, digest, 20) == 0) {
+ signercert = certs[i];
+ goto quit;
+ }
+ gnutls_assert();
+ } else {
+ _gnutls_debug_log("checking issuer DN\n");
+
+ assert(riddn.data != NULL);
+ if ((certs[i]->raw_dn.size == riddn.size)
+ && memcmp(riddn.data, certs[i]->raw_dn.data, riddn.size) == 0) {
+ signercert = certs[i];
+ goto quit;
+ }
+ gnutls_assert();
+ }
+ }
+
+ gnutls_assert();
+ signercert = NULL;
+
+ quit:
+ gnutls_free(riddn.data);
+ gnutls_free(keyid.data);
+ for (i = 0; i < ncerts; i++)
+ if (certs[i] != signercert)
+ gnutls_x509_crt_deinit(certs[i]);
+ gnutls_free(certs);
+ return signercert;
+}
+
+static int
+_ocsp_resp_verify_direct(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_crt_t signercert,
+ unsigned int *verify, unsigned int flags)
+{
+ gnutls_datum_t sig = { NULL };
+ gnutls_datum_t data = { NULL };
+ gnutls_pubkey_t pubkey = NULL;
+ int sigalg;
+ int rc;
+
+ if (resp == NULL || signercert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ rc = gnutls_ocsp_resp_get_signature_algorithm(resp);
+ if (rc < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ sigalg = rc;
+
+ rc = _gnutls_x509_get_raw_field2(resp->basicresp, &resp->der, "tbsResponseData", &data);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ rc = gnutls_pubkey_init(&pubkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ _gnutls_cert_log("ocsp signer", signercert);
+
+ rc = gnutls_pubkey_import_x509(pubkey, signercert, 0);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ rc = gnutls_ocsp_resp_get_signature(resp, &sig);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ rc = gnutls_pubkey_verify_data2(pubkey, sigalg, flags, &data, &sig);
+ if (rc == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE;
+ } else if (rc < 0) {
+ gnutls_assert();
+ goto done;
+ } else
+ *verify = 0;
+
+ rc = GNUTLS_E_SUCCESS;
+
+ done:
+ gnutls_free(sig.data);
+ gnutls_pubkey_deinit(pubkey);
+
+ return rc;
+}
+
+static inline unsigned int vstatus_to_ocsp_status(unsigned int status)
+{
+ unsigned int ostatus;
+
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ ostatus = GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM;
+ else if (status & GNUTLS_CERT_NOT_ACTIVATED)
+ ostatus = GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED;
+ else if (status & GNUTLS_CERT_EXPIRED)
+ ostatus = GNUTLS_OCSP_VERIFY_CERT_EXPIRED;
+ else
+ ostatus = GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER;
+
+ return ostatus;
+}
+
+static int check_ocsp_purpose(gnutls_x509_crt_t signercert)
+{
+ char oidtmp[MAX_OID_SIZE];
+ size_t oidsize;
+ int indx, rc;
+
+ for (indx = 0;; indx++) {
+ oidsize = sizeof(oidtmp);
+ rc = gnutls_x509_crt_get_key_purpose_oid(signercert, indx,
+ oidtmp, &oidsize,
+ NULL);
+
+ if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return rc;
+ } else if (rc == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ gnutls_assert();
+ continue;
+ } else if (rc != GNUTLS_E_SUCCESS) {
+ return gnutls_assert_val(rc);
+ }
+
+ if (memcmp(oidtmp, GNUTLS_KP_OCSP_SIGNING, oidsize) != 0) {
+ gnutls_assert();
+ continue;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_ocsp_resp_verify_direct:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @issuer: certificate believed to have signed the response
+ * @verify: output variable with verification status, an #gnutls_ocsp_verify_reason_t
+ * @flags: verification flags from #gnutls_certificate_verify_flags
+ *
+ * Verify signature of the Basic OCSP Response against the public key
+ * in the @issuer certificate.
+ *
+ * The output @verify variable will hold verification status codes
+ * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND,
+ * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the
+ * function returned %GNUTLS_E_SUCCESS.
+ *
+ * Note that the function returns %GNUTLS_E_SUCCESS even when
+ * verification failed. The caller must always inspect the @verify
+ * variable to find out the verification status.
+ *
+ * The @flags variable should be 0 for now.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_verify_direct(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_crt_t issuer,
+ unsigned int *verify, unsigned int flags)
+{
+ gnutls_x509_crt_t signercert;
+ int rc;
+
+ if (resp == NULL || issuer == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ signercert = find_signercert(resp);
+ if (!signercert) {
+ signercert = issuer;
+ } else if (!gnutls_x509_crt_equals(signercert, issuer)) {
+
+ /* response contains a signer. Verify him */
+
+ unsigned int vtmp;
+
+ rc = gnutls_x509_crt_verify(signercert, &issuer, 1, flags,
+ &vtmp);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (vtmp != 0) {
+ _gnutls_reason_log("cert verification", vtmp);
+ *verify = vstatus_to_ocsp_status(vtmp);
+ gnutls_assert();
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+
+ rc = check_ocsp_purpose(signercert);
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+ }
+
+ rc = _ocsp_resp_verify_direct(resp, signercert, verify, flags);
+
+ done:
+ if (signercert != issuer)
+ gnutls_x509_crt_deinit(signercert);
+
+ return rc;
+}
+
+/**
+ * gnutls_ocsp_resp_verify:
+ * @resp: should contain a #gnutls_ocsp_resp_t type
+ * @trustlist: trust anchors as a #gnutls_x509_trust_list_t type
+ * @verify: output variable with verification status, an #gnutls_ocsp_verify_reason_t
+ * @flags: verification flags from #gnutls_certificate_verify_flags
+ *
+ * Verify signature of the Basic OCSP Response against the public key
+ * in the certificate of a trusted signer. The @trustlist should be
+ * populated with trust anchors. The function will extract the signer
+ * certificate from the Basic OCSP Response and will verify it against
+ * the @trustlist. A trusted signer is a certificate that is either
+ * in @trustlist, or it is signed directly by a certificate in
+ * @trustlist and has the id-ad-ocspSigning Extended Key Usage bit
+ * set.
+ *
+ * The output @verify variable will hold verification status codes
+ * (e.g., %GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND,
+ * %GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) which are only valid if the
+ * function returned %GNUTLS_E_SUCCESS.
+ *
+ * Note that the function returns %GNUTLS_E_SUCCESS even when
+ * verification failed. The caller must always inspect the @verify
+ * variable to find out the verification status.
+ *
+ * The @flags variable should be 0 for now.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_verify(gnutls_ocsp_resp_const_t resp,
+ gnutls_x509_trust_list_t trustlist,
+ unsigned int *verify, unsigned int flags)
+{
+ gnutls_x509_crt_t signercert = NULL;
+ int rc;
+
+ /* Algorithm:
+ 1. Find signer cert.
+ 1a. Search in OCSP response Certificate field for responderID.
+ 1b. Verify that signer cert is trusted.
+ 2a. It is in trustlist?
+ 2b. It has OCSP key usage and directly signed by a CA in trustlist?
+ 3. Verify signature of Basic Response using public key from signer cert.
+ */
+
+ signercert = find_signercert(resp);
+ if (!signercert) {
+ gnutls_datum_t dn;
+
+ rc = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_DN, &dn);
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+
+ rc = gnutls_x509_trust_list_get_issuer_by_dn(trustlist, &dn, &signercert, 0);
+ gnutls_free(dn.data);
+
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+ } else {
+ /* Either the signer is directly trusted (i.e., in trustlist) or it
+ is directly signed by something in trustlist and has proper OCSP
+ extkeyusage. */
+ rc = _gnutls_trustlist_inlist(trustlist, signercert);
+ if (rc == 0) {
+ /* not in trustlist, need to verify signature and bits */
+ unsigned vtmp;
+ gnutls_typed_vdata_st vdata;
+
+ vdata.type = GNUTLS_DT_KEY_PURPOSE_OID;
+ vdata.data = (void*)GNUTLS_KP_OCSP_SIGNING;
+ vdata.size = 0;
+
+ gnutls_assert();
+
+ rc = gnutls_x509_trust_list_verify_crt2(trustlist,
+ &signercert, 1,
+ &vdata, 1,
+ flags, &vtmp, NULL);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (vtmp != 0) {
+ *verify = vstatus_to_ocsp_status(vtmp);
+ gnutls_assert();
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+
+ rc = check_ocsp_purpose(signercert);
+ if (rc < 0) {
+ gnutls_assert();
+ *verify = GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR;
+ rc = GNUTLS_E_SUCCESS;
+ goto done;
+ }
+ }
+ }
+
+ rc = _ocsp_resp_verify_direct(resp, signercert, verify, flags);
+
+ done:
+ gnutls_x509_crt_deinit(signercert);
+
+ return rc;
+}
+
+/**
+ * gnutls_x509_ocsp_resp_list_import2:
+ * @ocsps: Will hold the parsed OCSP response list.
+ * @size: It will contain the size of the list.
+ * @resp_data: The PEM encoded OCSP list.
+ * @format: One of %GNUTLS_X509_FMT_PEM or %GNUTLS_X509_FMT_DER
+ * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
+ *
+ * This function will convert the given PEM encoded OCSP response list
+ * to the native gnutls_ocsp_resp_t format. The output will be stored
+ * in @ocsps which will be allocated and initialized.
+ *
+ * The OCSP responses should have a header of "OCSP RESPONSE".
+ *
+ * To deinitialize responses, you need to deinitialize each %gnutls_ocsp_resp_t
+ * structure independently, and use gnutls_free() at @ocsps.
+ *
+ * In PEM files, when no OCSP responses are detected
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Returns: the number of responses read or a negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_ocsp_resp_list_import2(gnutls_ocsp_resp_t **ocsps,
+ unsigned int *size,
+ const gnutls_datum_t *resp_data,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int flags)
+{
+ gnutls_ocsp_resp_t resp = NULL;
+ gnutls_ocsp_resp_t *new_ocsps;
+ int ret;
+ unsigned i;
+
+
+ if (format == GNUTLS_X509_FMT_PEM) {
+ /* load multiple responses */
+ gnutls_datum_t p = {resp_data->data, resp_data->size};
+
+ *size = 0;
+ *ocsps = NULL;
+
+ p.data = memmem(p.data, p.size, PEM_OCSP_RESPONSE,
+ sizeof(PEM_OCSP_RESPONSE)-1);
+ if (p.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ p.size -= p.data - resp_data->data;
+ if (p.size <= 0) {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ goto cleanup;
+ }
+
+ do {
+ ret = gnutls_ocsp_resp_init(&resp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_ocsp_resp_import2(resp, &p, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(*size, 1))) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto fail;
+ }
+
+ new_ocsps = _gnutls_reallocarray(*ocsps,
+ *size + 1,
+ sizeof(gnutls_ocsp_resp_t));
+ if (new_ocsps == NULL) {
+ resp = NULL;
+ gnutls_assert();
+ goto fail;
+ }
+
+ new_ocsps[*size] = resp;
+ resp = NULL;
+ (*size)++;
+ *ocsps = new_ocsps;
+
+ p.data++;
+ p.size--;
+
+ p.data = memmem(p.data, p.size, PEM_OCSP_RESPONSE,
+ sizeof(PEM_OCSP_RESPONSE)-1);
+ if (p.data == NULL)
+ break;
+ p.size = resp_data->size - (p.data - resp_data->data);
+ } while(p.size > 0);
+ } else {
+ /* DER: load a single response */
+ ret = gnutls_ocsp_resp_init(&resp);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_ocsp_resp_import2(resp, resp_data, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ *ocsps = gnutls_malloc(sizeof(gnutls_ocsp_resp_t));
+ if (*ocsps == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ (*ocsps)[0] = resp;
+ resp = NULL;
+ *size = 1;
+ }
+
+ ret = 0;
+ goto cleanup;
+
+ fail:
+ for (i=0;i<*size;i++) {
+ gnutls_ocsp_resp_deinit((*ocsps)[i]);
+ }
+ gnutls_free(*ocsps);
+
+ cleanup:
+ if (resp)
+ gnutls_ocsp_resp_deinit(resp);
+ return ret;
+}
+
+/* This returns -1 if the OCSP response is invalid (revoked) or its
+ * data are too old. It returns -2 if it cannot determine the expiration
+ * time, and would otherwise treat it as too old.
+ * Otherwise it returns the time after which that data is invalid.
+ */
+time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_const_t resp)
+{
+ unsigned int cert_status;
+ time_t rtime, vtime, ntime, now;
+ int ret;
+
+ ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL,
+ &cert_status, &vtime, &ntime,
+ &rtime, NULL);
+ if (ret < 0) {
+ _gnutls_debug_log("There was an error parsing the OCSP response: %s\n",
+ gnutls_strerror(ret));
+ return gnutls_assert_val(-1);
+ }
+
+ if (cert_status != GNUTLS_OCSP_CERT_GOOD &&
+ cert_status != GNUTLS_OCSP_CERT_UNKNOWN) {
+ _gnutls_debug_log("The OCSP response status (%d) is invalid\n",
+ cert_status);
+ return gnutls_assert_val(-1);
+ }
+
+ now = gnutls_time(0);
+
+ if (ntime == -1) {
+ /* This is a problematic case, and there is no consensus on how
+ * to treat these responses. It doesn't contain the time after which
+ * the response is invalid, thus it is an OCSP response effectively
+ * valid forever defeating the purpose of OCSP. We set here the same
+ * limit we apply when verifying responses. */
+ if (now - vtime > MAX_OCSP_VALIDITY_SECS) {
+ _gnutls_debug_log("The OCSP response is old\n");
+ return gnutls_assert_val(-2);
+ }
+
+ return now + MAX_OCSP_VALIDITY_SECS;
+ } else {
+ /* there is a newer OCSP answer, don't trust this one */
+ if (ntime < now) {
+ _gnutls_debug_log("There is a newer OCSP response\n");
+ return gnutls_assert_val(-1);
+ }
+
+ return ntime;
+ }
+}
+
+const char *_gnutls_ocsp_verify_status_to_str(gnutls_ocsp_verify_reason_t r, char out[MAX_OCSP_MSG_SIZE])
+{
+ gnutls_buffer_st str;
+ gnutls_datum_t buf;
+ int ret;
+
+ _gnutls_buffer_init(&str);
+
+ if (r == 0)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response is trusted. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer could not be found. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("Error in the signer's key usageflags. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer is not trusted. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response depends on insecure algorithms. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signature cannot be validated. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer's certificate is not activated. "));
+
+ if (r & GNUTLS_OCSP_VERIFY_CERT_EXPIRED)
+ _gnutls_buffer_append_str(&str,
+ _
+ ("The OCSP response's signer's certificate is expired. "));
+
+ ret = _gnutls_buffer_to_datum(&str, &buf, 1);
+ if (ret < 0)
+ return _("Memory error");
+
+ snprintf(out, MAX_OCSP_MSG_SIZE, "%s", buf.data);
+ gnutls_free(buf.data);
+
+ return out;
+}
diff --git a/lib/x509/ocsp.h b/lib/x509/ocsp.h
new file mode 100644
index 0000000..b9c4757
--- /dev/null
+++ b/lib/x509/ocsp.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_OCSP_H
+#define GNUTLS_LIB_X509_OCSP_H
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+#include <gnutls/ocsp.h>
+
+/* fifteen days */
+#define MAX_OCSP_VALIDITY_SECS (15*60*60*24)
+
+time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_const_t resp);
+#define MAX_OCSP_MSG_SIZE 128
+const char *_gnutls_ocsp_verify_status_to_str(gnutls_ocsp_verify_reason_t r, char out[MAX_OCSP_MSG_SIZE]);
+
+#endif /* GNUTLS_LIB_X509_OCSP_H */
diff --git a/lib/x509/ocsp_output.c b/lib/x509/ocsp_output.c
new file mode 100644
index 0000000..8ead90c
--- /dev/null
+++ b/lib/x509/ocsp_output.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Online Certificate Status Protocol - RFC 2560
+ */
+
+#include "gnutls_int.h"
+#include <global.h>
+#include "errors.h"
+#include <libtasn1.h>
+#include <pk.h>
+#include <str.h>
+#include "algorithms.h"
+
+#include <gnutls/ocsp.h>
+
+#define addf _gnutls_buffer_append_printf
+#define adds _gnutls_buffer_append_str
+
+static void print_req(gnutls_buffer_st * str, gnutls_ocsp_req_const_t req)
+{
+ int ret;
+ unsigned indx;
+
+ /* Version. */
+ {
+ int version = gnutls_ocsp_req_get_version(req);
+ if (version < 0)
+ addf(str, "error: get_version: %s\n",
+ gnutls_strerror(version));
+ else
+ addf(str, _("\tVersion: %d\n"), version);
+ }
+
+ /* XXX requestorName */
+
+ /* requestList */
+ addf(str, "\tRequest List:\n");
+ for (indx = 0;; indx++) {
+ gnutls_digest_algorithm_t digest;
+ gnutls_datum_t in, ik, sn;
+
+ ret =
+ gnutls_ocsp_req_get_cert_id(req, indx, &digest, &in,
+ &ik, &sn);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ addf(str, "\t\tCertificate ID:\n");
+ if (ret != GNUTLS_E_SUCCESS) {
+ addf(str, "error: get_cert_id: %s\n",
+ gnutls_strerror(ret));
+ continue;
+ }
+ addf(str, "\t\t\tHash Algorithm: %s\n",
+ _gnutls_digest_get_name(hash_to_entry(digest)));
+
+ adds(str, "\t\t\tIssuer Name Hash: ");
+ _gnutls_buffer_hexprint(str, in.data, in.size);
+ adds(str, "\n");
+
+ adds(str, "\t\t\tIssuer Key Hash: ");
+ _gnutls_buffer_hexprint(str, ik.data, ik.size);
+ adds(str, "\n");
+
+ adds(str, "\t\t\tSerial Number: ");
+ _gnutls_buffer_hexprint(str, sn.data, sn.size);
+ adds(str, "\n");
+
+ gnutls_free(in.data);
+ gnutls_free(ik.data);
+ gnutls_free(sn.data);
+
+ /* XXX singleRequestExtensions */
+ }
+
+ for (indx = 0;; indx++) {
+ gnutls_datum_t oid;
+ unsigned int critical;
+ gnutls_datum_t data;
+
+ ret =
+ gnutls_ocsp_req_get_extension(req, indx, &oid,
+ &critical, &data);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret != GNUTLS_E_SUCCESS) {
+ addf(str, "error: get_extension: %s\n",
+ gnutls_strerror(ret));
+ continue;
+ }
+ if (indx == 0)
+ adds(str, "\tExtensions:\n");
+
+ if (oid.size == sizeof(GNUTLS_OCSP_NONCE) - 1 &&
+ memcmp(oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) {
+ gnutls_datum_t nonce;
+ unsigned int ncrit;
+
+ ret =
+ gnutls_ocsp_req_get_nonce(req, &ncrit,
+ &nonce);
+ if (ret != GNUTLS_E_SUCCESS) {
+ addf(str, "error: get_nonce: %s\n",
+ gnutls_strerror(ret));
+ } else {
+ addf(str, "\t\tNonce%s: ",
+ ncrit ? " (critical)" : "");
+ _gnutls_buffer_hexprint(str, nonce.data,
+ nonce.size);
+ adds(str, "\n");
+ gnutls_free(nonce.data);
+ }
+ } else {
+ addf(str, "\t\tUnknown extension %s (%s):\n",
+ oid.data,
+ critical ? "critical" : "not critical");
+
+ adds(str, _("\t\t\tASCII: "));
+ _gnutls_buffer_asciiprint(str, (char *) data.data,
+ data.size);
+ addf(str, "\n");
+
+ adds(str, _("\t\t\tHexdump: "));
+ _gnutls_buffer_hexprint(str, (char *) data.data,
+ data.size);
+ adds(str, "\n");
+ }
+
+ gnutls_free(oid.data);
+ gnutls_free(data.data);
+ }
+
+ /* XXX Signature */
+}
+
+/**
+ * gnutls_ocsp_req_print:
+ * @req: The data to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with (0) terminated string.
+ *
+ * This function will pretty print a OCSP request, suitable for
+ * display to a human.
+ *
+ * If the format is %GNUTLS_OCSP_PRINT_FULL then all fields of the
+ * request will be output, on multiple lines.
+ *
+ * The output @out->data needs to be deallocate using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_req_print(gnutls_ocsp_req_const_t req,
+ gnutls_ocsp_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+ int rc;
+
+ if (format != GNUTLS_OCSP_PRINT_FULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ _gnutls_buffer_init(&str);
+
+ _gnutls_buffer_append_str(&str, _("OCSP Request Information:\n"));
+
+ print_req(&str, req);
+
+ rc = _gnutls_buffer_to_datum(&str, out, 1);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return rc;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+static void
+print_resp(gnutls_buffer_st * str, gnutls_ocsp_resp_const_t resp,
+ gnutls_ocsp_print_formats_t format)
+{
+ int ret;
+ unsigned indx;
+
+ ret = gnutls_ocsp_resp_get_status(resp);
+ if (ret < 0) {
+ addf(str, "error: ocsp_resp_get_status: %s\n",
+ gnutls_strerror(ret));
+ return;
+ }
+
+ adds(str, "\tResponse Status: ");
+ switch (ret) {
+ case GNUTLS_OCSP_RESP_SUCCESSFUL:
+ adds(str, "Successful\n");
+ break;
+
+ case GNUTLS_OCSP_RESP_MALFORMEDREQUEST:
+ adds(str, "malformedRequest\n");
+ return;
+
+ case GNUTLS_OCSP_RESP_INTERNALERROR:
+ adds(str, "internalError\n");
+ return;
+
+ case GNUTLS_OCSP_RESP_TRYLATER:
+ adds(str, "tryLater\n");
+ return;
+
+ case GNUTLS_OCSP_RESP_SIGREQUIRED:
+ adds(str, "sigRequired\n");
+ return;
+
+ case GNUTLS_OCSP_RESP_UNAUTHORIZED:
+ adds(str, "unauthorized\n");
+ return;
+
+ default:
+ adds(str, "unknown\n");
+ return;
+ }
+
+ {
+ gnutls_datum_t oid;
+
+ ret = gnutls_ocsp_resp_get_response(resp, &oid, NULL);
+ if (ret < 0) {
+ addf(str, "error: get_response: %s\n",
+ gnutls_strerror(ret));
+ return;
+ }
+
+ adds(str, "\tResponse Type: ");
+#define OCSP_BASIC "1.3.6.1.5.5.7.48.1.1"
+
+ if (oid.size == sizeof(OCSP_BASIC) - 1
+ && memcmp(oid.data, OCSP_BASIC, oid.size) == 0) {
+ adds(str, "Basic OCSP Response\n");
+ gnutls_free(oid.data);
+ } else {
+ addf(str, "Unknown response type (%.*s)\n",
+ oid.size, oid.data);
+ gnutls_free(oid.data);
+ return;
+ }
+ }
+
+ /* Version. */
+ {
+ int version = gnutls_ocsp_resp_get_version(resp);
+ if (version < 0)
+ addf(str, "error: get_version: %s\n",
+ gnutls_strerror(version));
+ else
+ addf(str, _("\tVersion: %d\n"), version);
+ }
+
+ /* responderID */
+ {
+ gnutls_datum_t dn = {NULL, 0};
+
+ ret = gnutls_ocsp_resp_get_responder2(resp, &dn, 0);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ ret = gnutls_ocsp_resp_get_responder_raw_id(resp, GNUTLS_OCSP_RESP_ID_KEY, &dn);
+
+ if (ret >= 0) {
+ addf(str, _("\tResponder Key ID: "));
+ _gnutls_buffer_hexprint(str, dn.data, dn.size);
+ adds(str, "\n");
+ }
+ gnutls_free(dn.data);
+ } else {
+ addf(str, "error: get_responder2: %s\n",
+ gnutls_strerror(ret));
+ }
+ } else {
+ addf(str, _("\tResponder ID: %s\n"), dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+ time_t tim = gnutls_ocsp_resp_get_produced(resp);
+
+ if (tim == (time_t) - 1)
+ addf(str, "error: ocsp_resp_get_produced\n");
+ else if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) tim);
+ else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t)
+ == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) tim);
+ else
+ addf(str, _("\tProduced At: %s\n"), s);
+ }
+
+ addf(str, "\tResponses:\n");
+ for (indx = 0;; indx++) {
+ gnutls_digest_algorithm_t digest;
+ gnutls_datum_t in, ik, sn;
+ unsigned int cert_status;
+ time_t this_update;
+ time_t next_update;
+ time_t revocation_time;
+ unsigned int revocation_reason;
+
+ ret = gnutls_ocsp_resp_get_single(resp,
+ indx,
+ &digest, &in, &ik, &sn,
+ &cert_status,
+ &this_update,
+ &next_update,
+ &revocation_time,
+ &revocation_reason);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ addf(str, "\t\tCertificate ID:\n");
+ if (ret != GNUTLS_E_SUCCESS) {
+ addf(str, "error: get_singleresponse: %s\n",
+ gnutls_strerror(ret));
+ continue;
+ }
+ addf(str, "\t\t\tHash Algorithm: %s\n",
+ _gnutls_digest_get_name(hash_to_entry(digest)));
+
+ adds(str, "\t\t\tIssuer Name Hash: ");
+ _gnutls_buffer_hexprint(str, in.data, in.size);
+ adds(str, "\n");
+
+ adds(str, "\t\t\tIssuer Key Hash: ");
+ _gnutls_buffer_hexprint(str, ik.data, ik.size);
+ adds(str, "\n");
+
+ adds(str, "\t\t\tSerial Number: ");
+ _gnutls_buffer_hexprint(str, sn.data, sn.size);
+ adds(str, "\n");
+
+ gnutls_free(in.data);
+ gnutls_free(ik.data);
+ gnutls_free(sn.data);
+
+ {
+ const char *p = NULL;
+
+ switch (cert_status) {
+ case GNUTLS_OCSP_CERT_GOOD:
+ p = "good";
+ break;
+
+ case GNUTLS_OCSP_CERT_REVOKED:
+ p = "revoked";
+ break;
+
+ case GNUTLS_OCSP_CERT_UNKNOWN:
+ p = "unknown";
+ break;
+
+ default:
+ addf(str,
+ "\t\tCertificate Status: unexpected value %d\n",
+ cert_status);
+ break;
+ }
+
+ if (p)
+ addf(str, "\t\tCertificate Status: %s\n",
+ p);
+ }
+
+ /* XXX revocation reason */
+
+ if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (revocation_time == (time_t) - 1)
+ addf(str, "error: revocation_time\n");
+ else if (gmtime_r(&revocation_time, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) revocation_time);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) revocation_time);
+ else
+ addf(str, _("\t\tRevocation time: %s\n"),
+ s);
+ }
+
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (this_update == (time_t) - 1)
+ addf(str, "error: this_update\n");
+ else if (gmtime_r(&this_update, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) this_update);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) this_update);
+ else
+ addf(str, _("\t\tThis Update: %s\n"), s);
+ }
+
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (next_update != (time_t) - 1) {
+ if (gmtime_r(&next_update, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) next_update);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) next_update);
+ else
+ addf(str, _("\t\tNext Update: %s\n"), s);
+ }
+ }
+
+ /* XXX singleRequestExtensions */
+ }
+
+ adds(str, "\tExtensions:\n");
+ for (indx = 0;; indx++) {
+ gnutls_datum_t oid;
+ unsigned int critical;
+ gnutls_datum_t data;
+
+ ret =
+ gnutls_ocsp_resp_get_extension(resp, indx, &oid,
+ &critical, &data);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (ret != GNUTLS_E_SUCCESS) {
+ addf(str, "error: get_extension: %s\n",
+ gnutls_strerror(ret));
+ continue;
+ }
+
+ if (oid.size == sizeof(GNUTLS_OCSP_NONCE) - 1 &&
+ memcmp(oid.data, GNUTLS_OCSP_NONCE, oid.size) == 0) {
+ gnutls_datum_t nonce;
+ unsigned int ncrit;
+
+ ret =
+ gnutls_ocsp_resp_get_nonce(resp, &ncrit,
+ &nonce);
+ if (ret != GNUTLS_E_SUCCESS) {
+ addf(str, "error: get_nonce: %s\n",
+ gnutls_strerror(ret));
+ } else {
+ addf(str, "\t\tNonce%s: ",
+ ncrit ? " (critical)" : "");
+ _gnutls_buffer_hexprint(str, nonce.data,
+ nonce.size);
+ adds(str, "\n");
+ gnutls_free(nonce.data);
+ }
+ } else {
+ addf(str, "\t\tUnknown extension %s (%s):\n",
+ oid.data,
+ critical ? "critical" : "not critical");
+
+ adds(str, _("\t\t\tASCII: "));
+ _gnutls_buffer_asciiprint(str, (char *) data.data,
+ data.size);
+ addf(str, "\n");
+
+ adds(str, _("\t\t\tHexdump: "));
+ _gnutls_buffer_hexprint(str, (char *) data.data,
+ data.size);
+ adds(str, "\n");
+ }
+
+ gnutls_free(oid.data);
+ gnutls_free(data.data);
+
+ }
+
+ ret = gnutls_ocsp_resp_get_signature_algorithm(resp);
+ if (ret < 0)
+ addf(str, "error: get_signature_algorithm: %s\n",
+ gnutls_strerror(ret));
+ else {
+ const char *name =
+ gnutls_sign_algorithm_get_name(ret);
+ if (name == NULL)
+ name = _("unknown");
+ addf(str, _("\tSignature Algorithm: %s\n"), name);
+ }
+ if (ret != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure(ret) == 0) {
+ adds(str,
+ _("warning: signed using a broken signature "
+ "algorithm that can be forged.\n"));
+ }
+
+ if (format == GNUTLS_OCSP_PRINT_FULL) {
+ gnutls_datum_t sig;
+ gnutls_x509_crt_t *certs;
+ size_t ncerts, i;
+ gnutls_datum_t out;
+
+ /* Signature. */
+ ret = gnutls_ocsp_resp_get_signature(resp, &sig);
+ if (ret < 0)
+ addf(str, "error: get_signature: %s\n",
+ gnutls_strerror(ret));
+ else {
+ adds(str, _("\tSignature:\n"));
+ _gnutls_buffer_hexdump(str, sig.data, sig.size,
+ "\t\t");
+
+ gnutls_free(sig.data);
+ }
+
+ /* certs */
+ ret = gnutls_ocsp_resp_get_certs(resp, &certs, &ncerts);
+ if (ret < 0)
+ addf(str, "error: get_certs: %s\n",
+ gnutls_strerror(ret));
+ else {
+ if (ncerts > 0)
+ addf(str, "\tAdditional certificates:\n");
+
+ for (i = 0; i < ncerts; i++) {
+ size_t s = 0;
+
+ ret =
+ gnutls_x509_crt_print(certs[i],
+ GNUTLS_CRT_PRINT_FULL,
+ &out);
+ if (ret < 0)
+ addf(str, "error: crt_print: %s\n",
+ gnutls_strerror(ret));
+ else {
+ addf(str, "%.*s", out.size,
+ out.data);
+ gnutls_free(out.data);
+ }
+
+ ret =
+ gnutls_x509_crt_export(certs[i],
+ GNUTLS_X509_FMT_PEM,
+ NULL, &s);
+ if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ addf(str,
+ "error: crt_export: %s\n",
+ gnutls_strerror(ret));
+ else {
+ out.data = gnutls_malloc(s);
+ if (out.data == NULL)
+ addf(str,
+ "error: malloc: %s\n",
+ gnutls_strerror
+ (GNUTLS_E_MEMORY_ERROR));
+ else {
+ ret =
+ gnutls_x509_crt_export
+ (certs[i],
+ GNUTLS_X509_FMT_PEM,
+ out.data, &s);
+ if (ret < 0)
+ addf(str,
+ "error: crt_export: %s\n",
+ gnutls_strerror
+ (ret));
+ else {
+ out.size = s;
+ addf(str, "%.*s",
+ out.size,
+ out.data);
+ }
+ gnutls_free(out.data);
+ }
+ }
+
+ gnutls_x509_crt_deinit(certs[i]);
+ }
+ gnutls_free(certs);
+ }
+ }
+}
+
+/**
+ * gnutls_ocsp_resp_print:
+ * @resp: The data to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with (0) terminated string.
+ *
+ * This function will pretty print a OCSP response, suitable for
+ * display to a human.
+ *
+ * If the format is %GNUTLS_OCSP_PRINT_FULL then all fields of the
+ * response will be output, on multiple lines.
+ *
+ * The output @out->data needs to be deallocate using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_ocsp_resp_print(gnutls_ocsp_resp_const_t resp,
+ gnutls_ocsp_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+ int rc;
+
+ _gnutls_buffer_init(&str);
+
+ _gnutls_buffer_append_str(&str, _("OCSP Response Information:\n"));
+
+ print_resp(&str, resp, format);
+
+ rc = _gnutls_buffer_to_datum(&str, out, 1);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return rc;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
diff --git a/lib/x509/output.c b/lib/x509/output.c
new file mode 100644
index 0000000..2ec288c
--- /dev/null
+++ b/lib/x509/output.c
@@ -0,0 +1,3083 @@
+/*
+ * Copyright (C) 2007-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2017 Red Hat, Inc.
+ *
+ * Author: Simon Josefsson, Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions for printing X.509 Certificate structures
+ */
+
+#include "gnutls_int.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_int.h>
+#include <num.h>
+#include "errors.h"
+#include "hello_ext.h"
+#include "ip.h"
+
+#define addf _gnutls_buffer_append_printf
+#define adds _gnutls_buffer_append_str
+
+#define NON_NULL(x) (((x)!=NULL)?((char*)(x)):"")
+#define ERROR_STR (char*) "(error)"
+
+static void print_idn_name(gnutls_buffer_st *str, const char *prefix, const char *type, gnutls_datum_t *name)
+{
+ unsigned printable = 1;
+ unsigned is_printed = 0;
+ gnutls_datum_t out = {NULL, 0};
+ int ret;
+
+ if (!_gnutls_str_is_print((char*)name->data, name->size))
+ printable = 0;
+
+ is_printed = 0;
+ if (!printable) {
+ addf(str, _("%s%s: %.*s (contains illegal chars)\n"), prefix, type, name->size, NON_NULL(name->data));
+ is_printed = 1;
+ } else if (name->data != NULL) {
+ if (strstr((char*)name->data, "xn--") != NULL) {
+ ret = gnutls_idna_reverse_map((char*)name->data, name->size, &out, 0);
+ if (ret >= 0) {
+ addf(str, _("%s%s: %.*s (%s)\n"), prefix, type, name->size, NON_NULL(name->data), out.data);
+ is_printed = 1;
+ gnutls_free(out.data);
+ }
+ }
+ }
+
+ if (is_printed == 0) {
+ addf(str, _("%s%s: %.*s\n"), prefix, type, name->size, NON_NULL(name->data));
+ }
+}
+
+static void print_idn_email(gnutls_buffer_st *str, const char *prefix, const char *type, gnutls_datum_t *name)
+{
+ unsigned printable = 1;
+ unsigned is_printed = 0;
+ gnutls_datum_t out = {NULL, 0};
+ int ret;
+
+ if (!_gnutls_str_is_print((char*)name->data, name->size))
+ printable = 0;
+
+ is_printed = 0;
+ if (!printable) {
+ addf(str, _("%s%s: %.*s (contains illegal chars)\n"), prefix, type, name->size, NON_NULL(name->data));
+ is_printed = 1;
+ } else if (name->data != NULL) {
+ if (strstr((char*)name->data, "xn--") != NULL) {
+ ret = _gnutls_idna_email_reverse_map((char*)name->data, name->size, &out);
+ if (ret >= 0) {
+ addf(str, _("%s%s: %.*s (%s)\n"), prefix, type, name->size, NON_NULL(name->data), out.data);
+ is_printed = 1;
+ gnutls_free(out.data);
+ }
+ }
+ }
+
+ if (is_printed == 0) {
+ addf(str, _("%s%s: %.*s\n"), prefix, type, name->size, NON_NULL(name->data));
+ }
+}
+
+static void
+print_name(gnutls_buffer_st *str, const char *prefix, unsigned type, gnutls_datum_t *name, unsigned ip_is_cidr)
+{
+ char *sname = (char*)name->data;
+ char str_ip[64];
+ const char *p;
+
+ if ((type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_OTHERNAME_XMPP
+ || type == GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL
+ || type == GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL
+ || type == GNUTLS_SAN_RFC822NAME
+ || type == GNUTLS_SAN_URI) && sname != NULL && strlen(sname) != name->size) {
+ adds(str,
+ _("warning: SAN contains an embedded NUL, "
+ "replacing with '!'\n"));
+ while (strlen(sname) < name->size)
+ name->data[strlen(sname)] = '!';
+ }
+
+ switch (type) {
+ case GNUTLS_SAN_DNSNAME:
+ print_idn_name(str, prefix, "DNSname", name);
+ break;
+
+ case GNUTLS_SAN_RFC822NAME:
+ print_idn_email(str, prefix, "RFC822Name", name);
+ break;
+
+ case GNUTLS_SAN_URI:
+ addf(str, _("%sURI: %.*s\n"), prefix, name->size, NON_NULL(name->data));
+ break;
+
+ case GNUTLS_SAN_IPADDRESS:
+ if (!ip_is_cidr)
+ p = _gnutls_ip_to_string(name->data, name->size, str_ip, sizeof(str_ip));
+ else
+ p = _gnutls_cidr_to_string(name->data, name->size, str_ip, sizeof(str_ip));
+ if (p == NULL)
+ p = ERROR_STR;
+ addf(str, "%sIPAddress: %s\n", prefix, p);
+ break;
+
+ case GNUTLS_SAN_DN:
+ addf(str, _("%sdirectoryName: %.*s\n"), prefix, name->size, NON_NULL(name->data));
+ break;
+
+ case GNUTLS_SAN_REGISTERED_ID:
+ addf(str, _("%sRegistered ID: %.*s\n"), prefix, name->size, NON_NULL(name->data));
+ break;
+
+ case GNUTLS_SAN_OTHERNAME_XMPP:
+ addf(str, _("%sXMPP Address: %.*s\n"), prefix, name->size, NON_NULL(name->data));
+ break;
+
+ case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL:
+ addf(str, _("%sKRB5Principal: %.*s\n"), prefix, name->size, NON_NULL(name->data));
+ break;
+
+ case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL:
+ addf(str, _("%sUser Principal Name: %.*s\n"), prefix, name->size, NON_NULL(name->data));
+ break;
+
+ default:
+ addf(str, _("%sUnknown name: "), prefix);
+ _gnutls_buffer_hexprint(str, name->data, name->size);
+ adds(str, "\n");
+ break;
+ }
+}
+
+static char *get_pk_name(gnutls_x509_crt_t cert, unsigned *bits)
+{
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ oid_size = sizeof(oid);
+ int ret;
+
+ ret = gnutls_x509_crt_get_pk_algorithm(cert, bits);
+ if (ret > 0) {
+ const char *name = gnutls_pk_algorithm_get_name(ret);
+
+ if (name != NULL)
+ return gnutls_strdup(name);
+ }
+
+ ret = gnutls_x509_crt_get_pk_oid(cert, oid, &oid_size);
+ if (ret < 0)
+ return NULL;
+
+ return gnutls_strdup(oid);
+}
+
+static char *crq_get_pk_name(gnutls_x509_crq_t crq)
+{
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ oid_size = sizeof(oid);
+ int ret;
+
+ ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL);
+ if (ret > 0) {
+ const char *name = gnutls_pk_algorithm_get_name(ret);
+
+ if (name != NULL)
+ return gnutls_strdup(name);
+ }
+
+ ret = gnutls_x509_crq_get_pk_oid(crq, oid, &oid_size);
+ if (ret < 0)
+ return NULL;
+
+ return gnutls_strdup(oid);
+}
+
+static char *get_sign_name(gnutls_x509_crt_t cert, int *algo)
+{
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ oid_size = sizeof(oid);
+ int ret;
+
+ *algo = 0;
+
+ ret = gnutls_x509_crt_get_signature_algorithm(cert);
+ if (ret > 0) {
+ const char *name = gnutls_sign_get_name(ret);
+
+ *algo = ret;
+
+ if (name != NULL)
+ return gnutls_strdup(name);
+ }
+
+ ret = gnutls_x509_crt_get_signature_oid(cert, oid, &oid_size);
+ if (ret < 0)
+ return NULL;
+
+ return gnutls_strdup(oid);
+}
+
+static char *crq_get_sign_name(gnutls_x509_crq_t crq)
+{
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ oid_size = sizeof(oid);
+ int ret;
+
+ ret = gnutls_x509_crq_get_signature_algorithm(crq);
+ if (ret > 0) {
+ const char *name = gnutls_sign_get_name(ret);
+
+ if (name != NULL)
+ return gnutls_strdup(name);
+ }
+
+ ret = gnutls_x509_crq_get_signature_oid(crq, oid, &oid_size);
+ if (ret < 0)
+ return NULL;
+
+ return gnutls_strdup(oid);
+}
+
+static char *crl_get_sign_name(gnutls_x509_crl_t crl, int *algo)
+{
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ oid_size = sizeof(oid);
+ int ret;
+
+ *algo = 0;
+
+ ret = gnutls_x509_crl_get_signature_algorithm(crl);
+ if (ret > 0) {
+ const char *name = gnutls_sign_get_name(ret);
+
+ *algo = ret;
+
+ if (name != NULL)
+ return gnutls_strdup(name);
+ }
+
+ ret = gnutls_x509_crl_get_signature_oid(crl, oid, &oid_size);
+ if (ret < 0)
+ return NULL;
+
+ return gnutls_strdup(oid);
+}
+
+
+static void print_proxy(gnutls_buffer_st * str, gnutls_datum_t *der)
+{
+ int pathlen;
+ char *policyLanguage;
+ char *policy;
+ size_t npolicy;
+ int err;
+
+ err = gnutls_x509_ext_import_proxy(der, &pathlen, &policyLanguage,
+ &policy, &npolicy);
+ if (err < 0) {
+ addf(str, "error: get_proxy: %s\n", gnutls_strerror(err));
+ return;
+ }
+
+ if (pathlen >= 0)
+ addf(str, _("\t\t\tPath Length Constraint: %d\n"),
+ pathlen);
+ addf(str, _("\t\t\tPolicy Language: %s"), policyLanguage);
+ if (strcmp(policyLanguage, "1.3.6.1.5.5.7.21.1") == 0)
+ adds(str, " (id-ppl-inheritALL)\n");
+ else if (strcmp(policyLanguage, "1.3.6.1.5.5.7.21.2") == 0)
+ adds(str, " (id-ppl-independent)\n");
+ else
+ adds(str, "\n");
+ if (npolicy) {
+ adds(str, _("\t\t\tPolicy:\n\t\t\t\tASCII: "));
+ _gnutls_buffer_asciiprint(str, policy, npolicy);
+ adds(str, _("\n\t\t\t\tHexdump: "));
+ _gnutls_buffer_hexprint(str, policy, npolicy);
+ adds(str, "\n");
+ }
+ gnutls_free(policy);
+ gnutls_free(policyLanguage);
+}
+
+
+static void print_nc(gnutls_buffer_st * str, const char* prefix, gnutls_datum_t *der)
+{
+ gnutls_x509_name_constraints_t nc;
+ int ret;
+ unsigned idx = 0;
+ gnutls_datum_t name;
+ unsigned type;
+ char new_prefix[16];
+
+ ret = gnutls_x509_name_constraints_init(&nc);
+ if (ret < 0)
+ return;
+
+ ret = gnutls_x509_ext_import_name_constraints(der, nc, 0);
+ if (ret < 0)
+ goto cleanup;
+
+ snprintf(new_prefix, sizeof(new_prefix), "%s\t\t\t\t", prefix);
+
+ do {
+ ret = gnutls_x509_name_constraints_get_permitted(nc, idx++, &type, &name);
+
+ if (ret >= 0) {
+ if (idx == 1)
+ addf(str, _("%s\t\t\tPermitted:\n"), prefix);
+
+ print_name(str, new_prefix, type, &name, 1);
+ }
+ } while (ret == 0);
+
+ idx = 0;
+ do {
+ ret = gnutls_x509_name_constraints_get_excluded(nc, idx++, &type, &name);
+
+ if (ret >= 0) {
+ if (idx == 1)
+ addf(str, _("%s\t\t\tExcluded:\n"), prefix);
+
+ print_name(str, new_prefix, type, &name, 1);
+ }
+ } while (ret == 0);
+
+cleanup:
+ gnutls_x509_name_constraints_deinit(nc);
+}
+
+static void print_aia(gnutls_buffer_st * str, const gnutls_datum_t *der)
+{
+ int err;
+ int seq;
+ gnutls_datum_t san = { NULL, 0 }, oid = {NULL, 0};
+ gnutls_x509_aia_t aia;
+ unsigned int san_type;
+
+ err = gnutls_x509_aia_init(&aia);
+ if (err < 0)
+ return;
+
+ err = gnutls_x509_ext_import_aia(der, aia, 0);
+ if (err < 0) {
+ addf(str, "error: get_aia: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ for (seq=0;;seq++) {
+ err = gnutls_x509_aia_get(aia, seq, &oid, &san_type, &san);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ goto cleanup;
+ if (err < 0) {
+ addf(str, "error: aia_get: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ if (strcmp((char*)oid.data, GNUTLS_OID_AD_OCSP) == 0)
+ addf(str, _("\t\t\tAccess Method: %s (%s)\n"), GNUTLS_OID_AD_OCSP, "id-ad-ocsp");
+ else if (strcmp((char*)oid.data, GNUTLS_OID_AD_CAISSUERS) == 0)
+ addf(str, _("\t\t\tAccess Method: %s (%s)\n"), GNUTLS_OID_AD_CAISSUERS, "id-ad-caIssuers");
+ else {
+ addf(str, _("\t\t\tAccess Method: %s (%s)\n"), (char*)oid.data, "UNKNOWN");
+ }
+
+ adds(str, "\t\t\tAccess Location ");
+ print_name(str, "", san_type, &san, 0);
+ }
+
+cleanup:
+ gnutls_x509_aia_deinit(aia);
+}
+
+static void print_ski(gnutls_buffer_st * str, gnutls_datum_t *der)
+{
+ gnutls_datum_t id = {NULL, 0};
+ int err;
+
+ err = gnutls_x509_ext_import_subject_key_id(der, &id);
+ if (err < 0) {
+ addf(str, "error: get_subject_key_id: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ adds(str, "\t\t\t");
+ _gnutls_buffer_hexprint(str, id.data, id.size);
+ adds(str, "\n");
+
+ gnutls_free(id.data);
+}
+
+static void print_time(gnutls_buffer_st *str, time_t timestamp)
+{
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (gmtime_r(&timestamp, &t) == NULL) {
+ addf(str, "error: gmtime_r (%lu)\n", timestamp);
+ return;
+ }
+
+ if (strftime(s, max, "%a, %b %d %H:%M:%S UTC %Y", &t) == 0)
+ addf(str, "error: strftime (%lu)\n", timestamp);
+ else
+ addf(str, "%s\n", s);
+}
+
+static void print_scts(gnutls_buffer_st * str, const gnutls_datum_t *der,
+ const char *prefix)
+{
+ int retval;
+ unsigned int version;
+ time_t timestamp;
+ gnutls_datum_t logid = { NULL, 0 }, sig = { NULL, 0 };
+ gnutls_sign_algorithm_t sigalg;
+ gnutls_x509_ct_scts_t scts;
+
+ retval = gnutls_x509_ext_ct_scts_init(&scts);
+ if (retval < 0) {
+ addf(str, "error: gnutls_x509_ext_ct_scts_init(): %s\n",
+ gnutls_strerror(retval));
+ return;
+ }
+
+ retval = gnutls_x509_ext_ct_import_scts(der, scts, 0);
+ if (retval < 0) {
+ addf(str, "error: gnutls_x509_ext_ct_import_scts(): %s\n",
+ gnutls_strerror(retval));
+ goto cleanup;
+ }
+
+ for (int i = 0;; i++) {
+ retval = gnutls_x509_ct_sct_get_version(scts, i, &version);
+ if (retval == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+
+ addf(str, _("%s\t\t\tSigned Certificate Timestamp %d:\n"),
+ prefix, (i+1));
+
+ if (version != 1) {
+ addf(str, _("%s\t\t\t\tVersion: %d (unknown SCT version)\n"),
+ prefix, version);
+ continue;
+ }
+
+ retval = gnutls_x509_ct_sct_get(scts, i,
+ &timestamp,
+ &logid,
+ &sigalg, &sig);
+ if (retval < 0) {
+ addf(str, "error: could not get SCT info: %s\n",
+ gnutls_strerror(retval));
+ break;
+ }
+
+ addf(str, _("%s\t\t\t\tVersion: %d\n"),
+ prefix, version);
+ addf(str, _("%s\t\t\t\tLog ID: "), prefix);
+ _gnutls_buffer_hexprint(str, logid.data, logid.size);
+ addf(str, "\n");
+ addf(str, _("%s\t\t\t\tTime: "), prefix);
+ print_time(str, timestamp);
+ addf(str, _("%s\t\t\t\tExtensions: none\n"), /* there are no extensions defined for v1 */
+ prefix);
+ addf(str, _("%s\t\t\t\tSignature algorithm: %s\n"),
+ prefix, gnutls_sign_get_name(sigalg));
+ addf(str, _("%s\t\t\t\tSignature: "), prefix);
+ _gnutls_buffer_hexprint(str, sig.data, sig.size);
+ addf(str, "\n");
+
+ _gnutls_free_datum(&sig);
+ _gnutls_free_datum(&logid);
+ sig.data = NULL;
+ logid.data = NULL;
+ }
+
+cleanup:
+ _gnutls_free_datum(&sig);
+ _gnutls_free_datum(&logid);
+ gnutls_x509_ext_ct_scts_deinit(scts);
+}
+
+
+#define TYPE_CRT 2
+#define TYPE_CRQ 3
+
+typedef union {
+ gnutls_x509_crt_t crt;
+ gnutls_x509_crq_t crq;
+} cert_type_t;
+
+static void
+print_aki_gn_serial(gnutls_buffer_st * str, gnutls_x509_aki_t aki)
+{
+ gnutls_datum_t san, other_oid, serial;
+ unsigned int alt_type;
+ int err;
+
+ err =
+ gnutls_x509_aki_get_cert_issuer(aki,
+ 0, &alt_type, &san, &other_oid, &serial);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ return;
+ } else if (err < 0) {
+ addf(str, "error: gnutls_x509_aki_get_cert_issuer: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ print_name(str, "\t\t\t", alt_type, &san, 0);
+
+ adds(str, "\t\t\tserial: ");
+ _gnutls_buffer_hexprint(str, serial.data, serial.size);
+ adds(str, "\n");
+}
+
+static void print_aki(gnutls_buffer_st * str, gnutls_datum_t *der)
+{
+ int err;
+ gnutls_x509_aki_t aki;
+ gnutls_datum_t id;
+
+ err = gnutls_x509_aki_init(&aki);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_aki_init: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ err = gnutls_x509_ext_import_authority_key_id(der, aki, 0);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_ext_import_authority_key_id: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ /* Check if an alternative name is there */
+ print_aki_gn_serial(str, aki);
+
+ err = gnutls_x509_aki_get_id(aki, &id);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ goto cleanup;
+ } else if (err < 0) {
+ addf(str, "error: gnutls_x509_aki_get_id: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ adds(str, "\t\t\t");
+ _gnutls_buffer_hexprint(str, id.data, id.size);
+ adds(str, "\n");
+
+ cleanup:
+ gnutls_x509_aki_deinit(aki);
+}
+
+static void
+print_key_usage2(gnutls_buffer_st * str, const char *prefix, unsigned int key_usage)
+{
+ if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE)
+ addf(str, _("%sDigital signature.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_NON_REPUDIATION)
+ addf(str, _("%sNon repudiation.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_KEY_ENCIPHERMENT)
+ addf(str, _("%sKey encipherment.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_DATA_ENCIPHERMENT)
+ addf(str, _("%sData encipherment.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_KEY_AGREEMENT)
+ addf(str, _("%sKey agreement.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_KEY_CERT_SIGN)
+ addf(str, _("%sCertificate signing.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_CRL_SIGN)
+ addf(str, _("%sCRL signing.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_ENCIPHER_ONLY)
+ addf(str, _("%sKey encipher only.\n"), prefix);
+ if (key_usage & GNUTLS_KEY_DECIPHER_ONLY)
+ addf(str, _("%sKey decipher only.\n"), prefix);
+}
+
+static void
+print_key_usage(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der)
+{
+ unsigned int key_usage;
+ int err;
+
+ err = gnutls_x509_ext_import_key_usage(der, &key_usage);
+ if (err < 0) {
+ addf(str, "error: get_key_usage: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ print_key_usage2(str, prefix, key_usage);
+}
+
+static void
+print_private_key_usage_period(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der)
+{
+ time_t activation, expiration;
+ int err;
+ char s[42];
+ struct tm t;
+ size_t max;
+
+ err = gnutls_x509_ext_import_private_key_usage_period(der, &activation, &expiration);
+ if (err < 0) {
+ addf(str, "error: get_private_key_usage_period: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ max = sizeof(s);
+
+ if (gmtime_r(&activation, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) activation);
+ else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) activation);
+ else
+ addf(str, _("\t\t\tNot Before: %s\n"), s);
+
+ if (gmtime_r(&expiration, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) expiration);
+ else if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) expiration);
+ else
+ addf(str, _("\t\t\tNot After: %s\n"), s);
+
+}
+
+static void print_crldist(gnutls_buffer_st * str, gnutls_datum_t *der)
+{
+ int err;
+ int indx;
+ gnutls_x509_crl_dist_points_t dp;
+ unsigned int flags, type;
+ gnutls_datum_t dist;
+
+ err = gnutls_x509_crl_dist_points_init(&dp);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_crl_dist_points_init: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ err = gnutls_x509_ext_import_crl_dist_points(der, dp, 0);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_ext_import_crl_dist_points: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ for (indx = 0;; indx++) {
+ err =
+ gnutls_x509_crl_dist_points_get(dp, indx, &type, &dist, &flags);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ goto cleanup;
+ else if (err < 0) {
+ addf(str, "error: get_crl_dist_points: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ print_name(str, "\t\t\t", type, &dist, 0);
+ }
+ cleanup:
+ gnutls_x509_crl_dist_points_deinit(dp);
+}
+
+static void
+print_key_purpose(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der)
+{
+ int indx;
+ gnutls_datum_t oid;
+ char *p;
+ int err;
+ gnutls_x509_key_purposes_t purposes;
+
+ err = gnutls_x509_key_purpose_init(&purposes);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_key_purpose_init: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ err = gnutls_x509_ext_import_key_purposes(der, purposes, 0);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_ext_import_key_purposes: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ for (indx = 0;; indx++) {
+ err = gnutls_x509_key_purpose_get(purposes, indx, &oid);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ goto cleanup;
+ else if (err < 0) {
+ addf(str, "error: gnutls_x509_key_purpose_get: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ p = (void*)oid.data;
+ if (strcmp(p, GNUTLS_KP_TLS_WWW_SERVER) == 0)
+ addf(str, _("%s\t\t\tTLS WWW Server.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_TLS_WWW_CLIENT) == 0)
+ addf(str, _("%s\t\t\tTLS WWW Client.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_CODE_SIGNING) == 0)
+ addf(str, _("%s\t\t\tCode signing.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_EMAIL_PROTECTION) == 0)
+ addf(str, _("%s\t\t\tEmail protection.\n"),
+ prefix);
+ else if (strcmp(p, GNUTLS_KP_TIME_STAMPING) == 0)
+ addf(str, _("%s\t\t\tTime stamping.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_OCSP_SIGNING) == 0)
+ addf(str, _("%s\t\t\tOCSP signing.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_IPSEC_IKE) == 0)
+ addf(str, _("%s\t\t\tIpsec IKE.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_MS_SMART_CARD_LOGON) == 0)
+ addf(str, _("%s\t\t\tSmart Card Logon.\n"), prefix);
+ else if (strcmp(p, GNUTLS_KP_ANY) == 0)
+ addf(str, _("%s\t\t\tAny purpose.\n"), prefix);
+ else
+ addf(str, "%s\t\t\t%s\n", prefix, p);
+ }
+ cleanup:
+ gnutls_x509_key_purpose_deinit(purposes);
+}
+
+static void
+print_basic(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der)
+{
+ int pathlen;
+ unsigned ca;
+ int err;
+
+ err = gnutls_x509_ext_import_basic_constraints(der, &ca, &pathlen);
+ if (err < 0) {
+ addf(str, "error: get_basic_constraints: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ if (ca == 0)
+ addf(str, _("%s\t\t\tCertificate Authority (CA): FALSE\n"),
+ prefix);
+ else
+ addf(str, _("%s\t\t\tCertificate Authority (CA): TRUE\n"),
+ prefix);
+
+ if (pathlen >= 0)
+ addf(str, _("%s\t\t\tPath Length Constraint: %d\n"),
+ prefix, pathlen);
+}
+
+
+static void
+print_altname(gnutls_buffer_st * str, const char *prefix, gnutls_datum_t *der)
+{
+ unsigned int altname_idx;
+ gnutls_subject_alt_names_t names;
+ unsigned int type;
+ gnutls_datum_t san;
+ gnutls_datum_t othername;
+ char pfx[16];
+ int err;
+
+ err = gnutls_subject_alt_names_init(&names);
+ if (err < 0) {
+ addf(str, "error: gnutls_subject_alt_names_init: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ err = gnutls_x509_ext_import_subject_alt_names(der, names, 0);
+ if (err < 0) {
+ addf(str, "error: gnutls_x509_ext_import_subject_alt_names: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ for (altname_idx = 0;; altname_idx++) {
+ err = gnutls_subject_alt_names_get(names, altname_idx,
+ &type, &san, &othername);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ else if (err < 0) {
+ addf(str,
+ "error: gnutls_subject_alt_names_get: %s\n",
+ gnutls_strerror(err));
+ break;
+ }
+
+
+ if (type == GNUTLS_SAN_OTHERNAME) {
+ unsigned vtype;
+ gnutls_datum_t virt;
+
+ err = gnutls_x509_othername_to_virtual((char*)othername.data, &san, &vtype, &virt);
+ if (err >= 0) {
+ snprintf(pfx, sizeof(pfx), "%s\t\t\t", prefix);
+ print_name(str, pfx, vtype, &virt, 0);
+ gnutls_free(virt.data);
+ continue;
+ }
+
+ addf(str,
+ _("%s\t\t\totherName OID: %.*s\n"),
+ prefix, (int)othername.size, (char*)othername.data);
+ addf(str, _("%s\t\t\totherName DER: "),
+ prefix);
+ _gnutls_buffer_hexprint(str, san.data, san.size);
+ addf(str, _("\n%s\t\t\totherName ASCII: "),
+ prefix);
+ _gnutls_buffer_asciiprint(str, (char*)san.data, san.size);
+ addf(str, "\n");
+ } else {
+
+ snprintf(pfx, sizeof(pfx), "%s\t\t\t", prefix);
+ print_name(str, pfx, type, &san, 0);
+ }
+ }
+
+ cleanup:
+ gnutls_subject_alt_names_deinit(names);
+}
+
+static void
+guiddump(gnutls_buffer_st * str, const char *data, size_t len,
+ const char *spc)
+{
+ size_t j;
+
+ if (spc)
+ adds(str, spc);
+ addf(str, "{");
+ addf(str, "%.2X", (unsigned char) data[3]);
+ addf(str, "%.2X", (unsigned char) data[2]);
+ addf(str, "%.2X", (unsigned char) data[1]);
+ addf(str, "%.2X", (unsigned char) data[0]);
+ addf(str, "-");
+ addf(str, "%.2X", (unsigned char) data[5]);
+ addf(str, "%.2X", (unsigned char) data[4]);
+ addf(str, "-");
+ addf(str, "%.2X", (unsigned char) data[7]);
+ addf(str, "%.2X", (unsigned char) data[6]);
+ addf(str, "-");
+ addf(str, "%.2X", (unsigned char) data[8]);
+ addf(str, "%.2X", (unsigned char) data[9]);
+ addf(str, "-");
+ for (j = 10; j < 16; j++) {
+ addf(str, "%.2X", (unsigned char) data[j]);
+ }
+ addf(str, "}\n");
+}
+
+static void
+print_unique_ids(gnutls_buffer_st * str, const gnutls_x509_crt_t cert)
+{
+ int result;
+ char buf[256]; /* if its longer, we won't bother to print it */
+ size_t buf_size = 256;
+
+ result =
+ gnutls_x509_crt_get_issuer_unique_id(cert, buf, &buf_size);
+ if (result >= 0) {
+ addf(str, ("\tIssuer Unique ID:\n"));
+ _gnutls_buffer_hexdump(str, buf, buf_size, "\t\t\t");
+ if (buf_size == 16) { /* this could be a GUID */
+ guiddump(str, buf, buf_size, "\t\t\t");
+ }
+ }
+
+ buf_size = 256;
+ result =
+ gnutls_x509_crt_get_subject_unique_id(cert, buf, &buf_size);
+ if (result >= 0) {
+ addf(str, ("\tSubject Unique ID:\n"));
+ _gnutls_buffer_hexdump(str, buf, buf_size, "\t\t\t");
+ if (buf_size == 16) { /* this could be a GUID */
+ guiddump(str, buf, buf_size, "\t\t\t");
+ }
+ }
+}
+
+static void print_tlsfeatures(gnutls_buffer_st * str, const char *prefix, const gnutls_datum_t *der)
+{
+ int err;
+ int seq;
+ gnutls_x509_tlsfeatures_t features;
+ const char *name;
+ unsigned int feature;
+
+ err = gnutls_x509_tlsfeatures_init(&features);
+ if (err < 0)
+ return;
+
+ err = gnutls_x509_ext_import_tlsfeatures(der, features, 0);
+ if (err < 0) {
+ addf(str, "error: get_tlsfeatures: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ for (seq=0;;seq++) {
+ err = gnutls_x509_tlsfeatures_get(features, seq, &feature);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ goto cleanup;
+ if (err < 0) {
+ addf(str, "error: get_tlsfeatures: %s\n",
+ gnutls_strerror(err));
+ goto cleanup;
+ }
+
+ name = gnutls_ext_get_name(feature);
+ if (name == NULL)
+ addf(str, "%s\t\t\t%u\n", prefix, feature);
+ else
+ addf(str, "%s\t\t\t%s(%u)\n", prefix, name, feature);
+ }
+
+cleanup:
+ gnutls_x509_tlsfeatures_deinit(features);
+}
+
+static void print_subject_sign_tool(gnutls_buffer_st * str, const char *prefix, const gnutls_datum_t *der)
+{
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ ret = _gnutls_x509_decode_string(ASN1_ETYPE_UTF8_STRING, der->data, der->size, &tmp, 0);
+ if (ret < 0) {
+ addf(str, _("%s\t\t\tASCII: "), prefix);
+ _gnutls_buffer_asciiprint(str, (char*)der->data, der->size);
+
+ addf(str, "\n");
+ addf(str, _("%s\t\t\tHexdump: "), prefix);
+ _gnutls_buffer_hexprint(str, (char*)der->data, der->size);
+ adds(str, "\n");
+
+ return;
+ }
+
+ addf(str, _("%s\t\t\t%.*s\n"), prefix, tmp.size, NON_NULL(tmp.data));
+ _gnutls_free_datum(&tmp);
+}
+
+static void print_issuer_sign_tool(gnutls_buffer_st * str, const char *prefix, const gnutls_datum_t *der)
+{
+ int ret;
+ asn1_node tmpasn = NULL;
+ char asn1_err[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = "";
+ gnutls_datum_t tmp;
+
+ if (asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.IssuerSignTool",
+ &tmpasn) != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto hexdump;
+ }
+
+ if (_asn1_strict_der_decode(&tmpasn, der->data, der->size, asn1_err) != ASN1_SUCCESS) {
+ gnutls_assert();
+ _gnutls_debug_log("_asn1_strict_der_decode: %s\n", asn1_err);
+ goto hexdump;
+ }
+
+ ret = _gnutls_x509_read_value(tmpasn, "signTool", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto hexdump;
+ }
+ addf(str, _("%s\t\t\tSignTool: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data));
+ _gnutls_free_datum(&tmp);
+
+ ret = _gnutls_x509_read_value(tmpasn, "cATool", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto hexdump;
+ }
+ addf(str, _("%s\t\t\tCATool: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data));
+ _gnutls_free_datum(&tmp);
+
+ ret = _gnutls_x509_read_value(tmpasn, "signToolCert", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto hexdump;
+ }
+ addf(str, _("%s\t\t\tSignToolCert: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data));
+ _gnutls_free_datum(&tmp);
+
+ ret = _gnutls_x509_read_value(tmpasn, "cAToolCert", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto hexdump;
+ }
+ addf(str, _("%s\t\t\tCAToolCert: %.*s\n"), prefix, tmp.size, NON_NULL(tmp.data));
+ _gnutls_free_datum(&tmp);
+
+ asn1_delete_structure(&tmpasn);
+
+ return;
+
+hexdump:
+ asn1_delete_structure(&tmpasn);
+
+ addf(str, _("%s\t\t\tASCII: "), prefix);
+ _gnutls_buffer_asciiprint(str, (char*)der->data, der->size);
+
+ addf(str, "\n");
+ addf(str, _("%s\t\t\tHexdump: "), prefix);
+ _gnutls_buffer_hexprint(str, (char*)der->data, der->size);
+ adds(str, "\n");
+}
+
+#define ENTRY(oid, name) {oid, sizeof(oid)-1, name, sizeof(name)-1, NULL, 0}
+
+static const struct oid_to_string cp_oid2str[] = {
+ ENTRY("2.5.29.32.0", "anyPolicy"),
+
+ ENTRY("2.23.140.1.2.1", "CA/B Domain Validated"),
+ ENTRY("2.23.140.1.2.2", "CA/B Organization Validated"),
+ ENTRY("2.23.140.1.2.3", "CA/B Individual Validated"),
+ ENTRY("2.23.140.1.1", "CA/B Extended Validation"),
+
+ /* draft-deremin-rfc4491-bis */
+ ENTRY("1.2.643.100.113.1", "Russian security class KC1"),
+ ENTRY("1.2.643.100.113.2", "Russian security class KC2"),
+ ENTRY("1.2.643.100.113.3", "Russian security class KC3"),
+ ENTRY("1.2.643.100.113.4", "Russian security class KB1"),
+ ENTRY("1.2.643.100.113.5", "Russian security class KB2"),
+ ENTRY("1.2.643.100.113.6", "Russian security class KA1"),
+
+ {NULL, 0, NULL, 0},
+};
+
+struct ext_indexes_st {
+ int san;
+ int ian;
+ int proxy;
+ int basic;
+ int keyusage;
+ int keypurpose;
+ int ski;
+ int aki, nc;
+ int crldist, pkey_usage_period;
+ int tlsfeatures;
+};
+
+static void print_extension(gnutls_buffer_st * str, const char *prefix,
+ struct ext_indexes_st *idx, const char *oid,
+ unsigned critical, gnutls_datum_t *der)
+{
+ int err;
+ unsigned j;
+ char pfx[16];
+
+ if (strcmp(oid, "2.5.29.19") == 0) {
+ if (idx->basic) {
+ addf(str,
+ "warning: more than one basic constraint\n");
+ }
+
+ addf(str, _("%s\t\tBasic Constraints (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_basic(str, prefix, der);
+ idx->basic++;
+
+ } else if (strcmp(oid, "2.5.29.14") == 0) {
+ if (idx->ski) {
+ addf(str,
+ "warning: more than one SKI extension\n");
+ }
+
+ addf(str,
+ _("%s\t\tSubject Key Identifier (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_ski(str, der);
+
+ idx->ski++;
+ } else if (strcmp(oid, "2.5.29.32") == 0) {
+ struct gnutls_x509_policy_st policy;
+ gnutls_x509_policies_t policies;
+ const char *name;
+ const struct oid_to_string *entry;
+ int x;
+
+ err = gnutls_x509_policies_init(&policies);
+ if (err < 0) {
+ addf(str,
+ "error: certificate policies: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ err = gnutls_x509_ext_import_policies(der, policies, 0);
+ if (err < 0) {
+ addf(str,
+ "error: certificate policies import: %s\n",
+ gnutls_strerror(err));
+ gnutls_x509_policies_deinit(policies);
+ return;
+ }
+
+ for (x = 0;; x++) {
+ err = gnutls_x509_policies_get(policies, x, &policy);
+ if (err ==
+ GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+
+ if (err < 0) {
+ addf(str,
+ "error: certificate policy: %s\n",
+ gnutls_strerror(err));
+ break;
+ }
+
+ if (x == 0)
+ addf(str,
+ "%s\t\tCertificate Policies (%s):\n",
+ prefix,
+ critical ? _("critical") :
+ _("not critical"));
+
+ entry = _gnutls_oid_get_entry(cp_oid2str, policy.oid);
+ if (entry != NULL && entry->name_desc != NULL)
+ addf(str, "%s\t\t\t%s (%s)\n", prefix, policy.oid, entry->name_desc);
+ else
+ addf(str, "%s\t\t\t%s\n", prefix, policy.oid);
+ for (j = 0; j < policy.qualifiers; j++) {
+ if (policy.qualifier[j].type ==
+ GNUTLS_X509_QUALIFIER_URI)
+ name = "URI";
+ else if (policy.qualifier[j].
+ type ==
+ GNUTLS_X509_QUALIFIER_NOTICE)
+ name = "Note";
+ else
+ name = "Unknown qualifier";
+ addf(str, "%s\t\t\t\t%s: %s\n",
+ prefix, name,
+ policy.qualifier[j].data);
+ }
+ }
+ gnutls_x509_policies_deinit(policies);
+ } else if (strcmp(oid, "2.5.29.54") == 0) {
+ unsigned int skipcerts;
+
+ err = gnutls_x509_ext_import_inhibit_anypolicy(der, &skipcerts);
+ if (err < 0) {
+ addf(str,
+ "error: certificate inhibit any policy import: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ addf(str,
+ "%s\t\tInhibit anyPolicy skip certs: %u (%s)\n",
+ prefix, skipcerts,
+ critical ? _("critical") :
+ _("not critical"));
+
+ } else if (strcmp(oid, "2.5.29.35") == 0) {
+
+ if (idx->aki) {
+ addf(str,
+ "warning: more than one AKI extension\n");
+ }
+
+ addf(str,
+ _("%s\t\tAuthority Key Identifier (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_aki(str, der);
+
+ idx->aki++;
+ } else if (strcmp(oid, "2.5.29.15") == 0) {
+ if (idx->keyusage) {
+ addf(str,
+ "warning: more than one key usage extension\n");
+ }
+
+ addf(str, _("%s\t\tKey Usage (%s):\n"), prefix,
+ critical ? _("critical") : _("not critical"));
+
+ snprintf(pfx, sizeof(pfx), "%s\t\t\t", prefix);
+ print_key_usage(str, pfx, der);
+
+ idx->keyusage++;
+ } else if (strcmp(oid, "2.5.29.16") == 0) {
+ if (idx->pkey_usage_period) {
+ addf(str,
+ "warning: more than one private key usage period extension\n");
+ }
+
+ addf(str,
+ _("%s\t\tPrivate Key Usage Period (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_private_key_usage_period(str, prefix, der);
+
+ idx->pkey_usage_period++;
+ } else if (strcmp(oid, "2.5.29.37") == 0) {
+ if (idx->keypurpose) {
+ addf(str,
+ "warning: more than one key purpose extension\n");
+ }
+
+ addf(str, _("%s\t\tKey Purpose (%s):\n"), prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_key_purpose(str, prefix, der);
+ idx->keypurpose++;
+ } else if (strcmp(oid, "2.5.29.17") == 0) {
+ if (idx->san) {
+ addf(str,
+ "warning: more than one SKI extension\n");
+ }
+
+ addf(str,
+ _("%s\t\tSubject Alternative Name (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+ print_altname(str, prefix, der);
+ idx->san++;
+ } else if (strcmp(oid, "2.5.29.18") == 0) {
+ if (idx->ian) {
+ addf(str,
+ "warning: more than one Issuer AltName extension\n");
+ }
+
+ addf(str,
+ _("%s\t\tIssuer Alternative Name (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_altname(str, prefix, der);
+
+ idx->ian++;
+ } else if (strcmp(oid, "2.5.29.31") == 0) {
+ if (idx->crldist) {
+ addf(str,
+ "warning: more than one CRL distribution point\n");
+ }
+
+ addf(str,
+ _("%s\t\tCRL Distribution points (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_crldist(str, der);
+ idx->crldist++;
+ } else if (strcmp(oid, "1.3.6.1.5.5.7.1.14") == 0) {
+ if (idx->proxy) {
+ addf(str,
+ "warning: more than one proxy extension\n");
+ }
+
+ addf(str,
+ _
+ ("%s\t\tProxy Certificate Information (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_proxy(str, der);
+
+ idx->proxy++;
+ } else if (strcmp(oid, "1.3.6.1.5.5.7.1.1") == 0) {
+ addf(str, _("%s\t\tAuthority Information "
+ "Access (%s):\n"), prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_aia(str, der);
+ } else if (strcmp(oid, GNUTLS_X509EXT_OID_CT_SCT_V1) == 0) {
+ addf(str, _("%s\t\tCT Precertificate SCTs (%s):\n"),
+ prefix, critical ? _("critical") : _("not critical"));
+
+ print_scts(str, der, prefix);
+ } else if (strcmp(oid, "2.5.29.30") == 0) {
+ if (idx->nc) {
+ addf(str,
+ "warning: more than one name constraints extension\n");
+ }
+ idx->nc++;
+
+ addf(str, _("%s\t\tName Constraints (%s):\n"), prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_nc(str, prefix, der);
+ } else if (strcmp(oid, GNUTLS_X509EXT_OID_TLSFEATURES) == 0) {
+ if (idx->tlsfeatures) {
+ addf(str,
+ "warning: more than one tlsfeatures extension\n");
+ }
+
+ addf(str, _("%s\t\tTLS Features (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_tlsfeatures(str, prefix, der);
+
+ idx->tlsfeatures++;
+ } else if (strcmp(oid, "1.2.643.100.111") == 0) {
+ addf(str, _("%s\t\tSubject Signing Tool(%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_subject_sign_tool(str, prefix, der);
+ } else if (strcmp(oid, "1.2.643.100.112") == 0) {
+ addf(str, _("%s\t\tIssuer Signing Tool(%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ print_issuer_sign_tool(str, prefix, der);
+ } else if (strcmp(oid, "2.5.4.3") == 0) {
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ addf(str, _("%s\t\tCommon Name (%s):\n"),
+ prefix,
+ critical ? _("critical") : _("not critical"));
+
+ ret = _gnutls_x509_decode_string(ASN1_ETYPE_PRINTABLE_STRING, der->data, der->size, &tmp, 0);
+ if (ret < 0) {
+ addf(str, "error: x509_decode_string: %s\n",
+ gnutls_strerror(ret));
+ } else {
+ addf(str, "%s\t\t\t%s\n", prefix, tmp.data);
+ gnutls_free(tmp.data);
+ }
+ } else {
+ addf(str, _("%s\t\tUnknown extension %s (%s):\n"),
+ prefix, oid,
+ critical ? _("critical") : _("not critical"));
+
+ addf(str, _("%s\t\t\tASCII: "), prefix);
+ _gnutls_buffer_asciiprint(str, (char*)der->data, der->size);
+
+ addf(str, "\n");
+ addf(str, _("%s\t\t\tHexdump: "), prefix);
+ _gnutls_buffer_hexprint(str, (char*)der->data, der->size);
+ adds(str, "\n");
+ }
+}
+
+static void
+print_extensions(gnutls_buffer_st * str, const char *prefix, int type,
+ cert_type_t cert)
+{
+ unsigned i;
+ int err;
+ gnutls_datum_t der = {NULL, 0};
+ struct ext_indexes_st idx;
+
+ memset(&idx, 0, sizeof(idx));
+
+ for (i = 0;; i++) {
+ char oid[MAX_OID_SIZE] = "";
+ size_t sizeof_oid = sizeof(oid);
+ unsigned int critical;
+
+ if (type == TYPE_CRT)
+ err =
+ gnutls_x509_crt_get_extension_info(cert.crt, i,
+ oid,
+ &sizeof_oid,
+ &critical);
+
+ else if (type == TYPE_CRQ)
+ err =
+ gnutls_x509_crq_get_extension_info(cert.crq, i,
+ oid,
+ &sizeof_oid,
+ &critical);
+ else {
+ gnutls_assert();
+ return;
+ }
+
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (err < 0) {
+ addf(str, "error: get_extension_info: %s\n",
+ gnutls_strerror(err));
+ break;
+ }
+
+ if (i == 0)
+ addf(str, _("%s\tExtensions:\n"), prefix);
+
+ if (type == TYPE_CRT)
+ err = gnutls_x509_crt_get_extension_data2(cert.crt, i, &der);
+ else
+ err = gnutls_x509_crq_get_extension_data2(cert.crq, i, &der);
+
+ if (err < 0) {
+ der.data = NULL;
+ der.size = 0;
+ }
+
+ print_extension(str, prefix, &idx, oid, critical, &der);
+ gnutls_free(der.data);
+ }
+}
+
+static void reverse_datum(gnutls_datum_t *d)
+{
+ unsigned int i;
+ unsigned char c;
+
+ for (i = 0; i < d->size / 2; i++) {
+ c = d->data[i];
+ d->data[i] = d->data[d->size - i - 1];
+ d->data[d->size - i - 1] = c;
+ }
+}
+
+static void
+print_pubkey(gnutls_buffer_st * str, const char *key_name,
+ gnutls_pubkey_t pubkey, gnutls_x509_spki_st *spki,
+ gnutls_certificate_print_formats_t format)
+{
+ int err;
+ const char *name;
+ unsigned bits;
+ unsigned pk;
+
+ err = gnutls_pubkey_get_pk_algorithm(pubkey, &bits);
+ if (err < 0) {
+ addf(str, "error: get_pk_algorithm: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ pk = err;
+
+ name = gnutls_pk_algorithm_get_name(pk);
+ if (name == NULL)
+ name = _("unknown");
+
+ addf(str, _("\t%sPublic Key Algorithm: %s\n"), key_name, name);
+
+ addf(str, _("\tAlgorithm Security Level: %s (%d bits)\n"),
+ gnutls_sec_param_get_name(gnutls_pk_bits_to_sec_param
+ (err, bits)), bits);
+
+ if (spki && pk == GNUTLS_PK_RSA_PSS && spki->pk == pk) {
+ addf(str, _("\t\tParameters:\n"));
+ addf(str, "\t\t\tHash Algorithm: %s\n",
+ gnutls_digest_get_name(spki->rsa_pss_dig));
+ addf(str, "\t\t\tSalt Length: %d\n", spki->salt_size);
+ }
+
+ switch (pk) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ {
+ gnutls_datum_t m, e;
+
+ err = gnutls_pubkey_get_pk_rsa_raw(pubkey, &m, &e);
+ if (err < 0)
+ addf(str, "error: get_pk_rsa_raw: %s\n",
+ gnutls_strerror(err));
+ else {
+ if (format ==
+ GNUTLS_CRT_PRINT_FULL_NUMBERS) {
+ addf(str,
+ _("\t\tModulus (bits %d): "),
+ bits);
+ _gnutls_buffer_hexprint(str,
+ m.data,
+ m.size);
+ adds(str, "\n");
+ addf(str,
+ _("\t\tExponent (bits %d): "),
+ e.size * 8);
+ _gnutls_buffer_hexprint(str,
+ e.data,
+ e.size);
+ adds(str, "\n");
+ } else {
+ addf(str,
+ _("\t\tModulus (bits %d):\n"),
+ bits);
+ _gnutls_buffer_hexdump(str, m.data,
+ m.size,
+ "\t\t\t");
+ addf(str,
+ _
+ ("\t\tExponent (bits %d):\n"),
+ e.size * 8);
+ _gnutls_buffer_hexdump(str, e.data,
+ e.size,
+ "\t\t\t");
+ }
+
+ gnutls_free(m.data);
+ gnutls_free(e.data);
+ }
+
+ }
+ break;
+
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ case GNUTLS_PK_ECDSA:
+ {
+ gnutls_datum_t x, y;
+ gnutls_ecc_curve_t curve;
+
+ err =
+ gnutls_pubkey_get_pk_ecc_raw(pubkey, &curve,
+ &x, &y);
+ if (err < 0) {
+ addf(str, "error: get_pk_ecc_raw: %s\n",
+ gnutls_strerror(err));
+ } else {
+ addf(str, _("\t\tCurve:\t%s\n"),
+ gnutls_ecc_curve_get_name(curve));
+ if (format ==
+ GNUTLS_CRT_PRINT_FULL_NUMBERS) {
+ adds(str, _("\t\tX: "));
+ _gnutls_buffer_hexprint(str,
+ x.data,
+ x.size);
+ adds(str, "\n");
+ if (y.size > 0) {
+ adds(str, _("\t\tY: "));
+ _gnutls_buffer_hexprint(str,
+ y.data,
+ y.size);
+ adds(str, "\n");
+ }
+ } else {
+ adds(str, _("\t\tX:\n"));
+ _gnutls_buffer_hexdump(str, x.data,
+ x.size,
+ "\t\t\t");
+ if (y.size > 0) {
+ adds(str, _("\t\tY:\n"));
+ _gnutls_buffer_hexdump(str, y.data,
+ y.size,
+ "\t\t\t");
+ }
+ }
+
+ gnutls_free(x.data);
+ gnutls_free(y.data);
+
+ }
+ }
+ break;
+ case GNUTLS_PK_DSA:
+ {
+ gnutls_datum_t p, q, g, y;
+
+ err =
+ gnutls_pubkey_get_pk_dsa_raw(pubkey, &p, &q,
+ &g, &y);
+ if (err < 0)
+ addf(str, "error: get_pk_dsa_raw: %s\n",
+ gnutls_strerror(err));
+ else {
+ if (format ==
+ GNUTLS_CRT_PRINT_FULL_NUMBERS) {
+ addf(str,
+ _
+ ("\t\tPublic key (bits %d): "),
+ bits);
+ _gnutls_buffer_hexprint(str,
+ y.data,
+ y.size);
+ adds(str, "\n");
+ adds(str, _("\t\tP: "));
+ _gnutls_buffer_hexprint(str,
+ p.data,
+ p.size);
+ adds(str, "\n");
+ adds(str, _("\t\tQ: "));
+ _gnutls_buffer_hexprint(str,
+ q.data,
+ q.size);
+ adds(str, "\n");
+ adds(str, _("\t\tG: "));
+ _gnutls_buffer_hexprint(str,
+ g.data,
+ g.size);
+ adds(str, "\n");
+ } else {
+ addf(str,
+ _
+ ("\t\tPublic key (bits %d):\n"),
+ bits);
+ _gnutls_buffer_hexdump(str, y.data,
+ y.size,
+ "\t\t\t");
+ adds(str, _("\t\tP:\n"));
+ _gnutls_buffer_hexdump(str, p.data,
+ p.size,
+ "\t\t\t");
+ adds(str, _("\t\tQ:\n"));
+ _gnutls_buffer_hexdump(str, q.data,
+ q.size,
+ "\t\t\t");
+ adds(str, _("\t\tG:\n"));
+ _gnutls_buffer_hexdump(str, g.data,
+ g.size,
+ "\t\t\t");
+ }
+
+ gnutls_free(p.data);
+ gnutls_free(q.data);
+ gnutls_free(g.data);
+ gnutls_free(y.data);
+
+ }
+ }
+ break;
+
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ gnutls_datum_t x, y;
+ gnutls_ecc_curve_t curve;
+ gnutls_digest_algorithm_t digest;
+ gnutls_gost_paramset_t param;
+
+ err =
+ gnutls_pubkey_export_gost_raw2(pubkey, &curve,
+ &digest,
+ &param,
+ &x, &y, 0);
+ if (err < 0)
+ addf(str, "error: get_pk_gost_raw: %s\n",
+ gnutls_strerror(err));
+ else {
+ addf(str, _("\t\tCurve:\t%s\n"),
+ gnutls_ecc_curve_get_name(curve));
+ addf(str, _("\t\tDigest:\t%s\n"),
+ gnutls_digest_get_name(digest));
+ addf(str, _("\t\tParamSet: %s\n"),
+ gnutls_gost_paramset_get_name(param));
+ reverse_datum(&x);
+ reverse_datum(&y);
+ if (format ==
+ GNUTLS_CRT_PRINT_FULL_NUMBERS) {
+ adds(str, _("\t\tX: "));
+ _gnutls_buffer_hexprint(str,
+ x.data,
+ x.size);
+ adds(str, "\n");
+ adds(str, _("\t\tY: "));
+ _gnutls_buffer_hexprint(str,
+ y.data,
+ y.size);
+ adds(str, "\n");
+ } else {
+ adds(str, _("\t\tX:\n"));
+ _gnutls_buffer_hexdump(str, x.data,
+ x.size,
+ "\t\t\t");
+ adds(str, _("\t\tY:\n"));
+ _gnutls_buffer_hexdump(str, y.data,
+ y.size,
+ "\t\t\t");
+ }
+
+ gnutls_free(x.data);
+ gnutls_free(y.data);
+
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int
+print_crt_sig_params(gnutls_buffer_st * str, gnutls_x509_crt_t crt,
+ gnutls_certificate_print_formats_t format)
+{
+ int ret;
+ gnutls_pk_algorithm_t pk;
+ gnutls_x509_spki_st params;
+ gnutls_sign_algorithm_t sign;
+
+ sign = gnutls_x509_crt_get_signature_algorithm(crt);
+ pk = gnutls_sign_get_pk_algorithm(sign);
+ if (pk == GNUTLS_PK_RSA_PSS) {
+ ret = _gnutls_x509_read_sign_params(crt->cert,
+ "signatureAlgorithm",
+ &params);
+ if (ret < 0) {
+ addf(str, "error: read_pss_params: %s\n",
+ gnutls_strerror(ret));
+ } else
+ addf(str, "\t\tSalt Length: %d\n", params.salt_size);
+ }
+
+ return 0;
+}
+
+static void print_pk_name(gnutls_buffer_st * str, gnutls_x509_crt_t crt)
+{
+ const char *p;
+ char *name = get_pk_name(crt, NULL);
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+
+ addf(str, "\tSubject Public Key Algorithm: %s\n", p);
+ gnutls_free(name);
+}
+
+static int
+print_crt_pubkey(gnutls_buffer_st * str, gnutls_x509_crt_t crt,
+ gnutls_certificate_print_formats_t format)
+{
+ gnutls_pubkey_t pubkey = NULL;
+ gnutls_x509_spki_st params;
+ int ret, pk;
+
+ ret = _gnutls_x509_crt_read_spki_params(crt, &params);
+ if (ret < 0)
+ return ret;
+
+ pk = gnutls_x509_crt_get_pk_algorithm(crt, NULL);
+ if (pk < 0) {
+ gnutls_assert();
+ pk = GNUTLS_PK_UNKNOWN;
+ }
+
+ if (pk == GNUTLS_PK_UNKNOWN) {
+ print_pk_name(str, crt); /* print basic info only */
+ return 0;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return ret;
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ if (ret != GNUTLS_E_UNIMPLEMENTED_FEATURE)
+ addf(str, "error importing public key: %s\n", gnutls_strerror(ret));
+ print_pk_name(str, crt); /* print basic info only */
+ ret = 0;
+ goto cleanup;
+ }
+
+ print_pubkey(str, _("Subject "), pubkey, &params, format);
+ ret = 0;
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+
+ return ret;
+}
+
+static void
+print_cert(gnutls_buffer_st * str, gnutls_x509_crt_t cert,
+ gnutls_certificate_print_formats_t format)
+{
+ /* Version. */
+ {
+ int version = gnutls_x509_crt_get_version(cert);
+ if (version < 0)
+ addf(str, "error: get_version: %s\n",
+ gnutls_strerror(version));
+ else
+ addf(str, _("\tVersion: %d\n"), version);
+ }
+
+ /* Serial. */
+ {
+ char serial[128];
+ size_t serial_size = sizeof(serial);
+ int err;
+
+ err =
+ gnutls_x509_crt_get_serial(cert, serial, &serial_size);
+ if (err < 0)
+ addf(str, "error: get_serial: %s\n",
+ gnutls_strerror(err));
+ else {
+ adds(str, _("\tSerial Number (hex): "));
+ _gnutls_buffer_hexprint(str, serial, serial_size);
+ adds(str, "\n");
+ }
+ }
+
+ /* Issuer. */
+ if (format != GNUTLS_CRT_PRINT_UNSIGNED_FULL) {
+ gnutls_datum_t dn;
+ int err;
+
+ err = gnutls_x509_crt_get_issuer_dn3(cert, &dn, 0);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ addf(str, _("\tIssuer:\n"));
+ } else if (err < 0) {
+ addf(str, "error: get_issuer_dn: %s\n",
+ gnutls_strerror(err));
+ } else {
+ addf(str, _("\tIssuer: %s\n"), dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ /* Validity. */
+ {
+ time_t tim;
+
+ adds(str, _("\tValidity:\n"));
+
+ tim = gnutls_x509_crt_get_activation_time(cert);
+ if (tim != -1) {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) tim);
+ else
+ addf(str, _("\t\tNot Before: %s\n"), s);
+ } else {
+ addf(str, _("\t\tNot Before: %s\n"), _("unknown"));
+ }
+
+ tim = gnutls_x509_crt_get_expiration_time(cert);
+ if (tim != -1) {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) tim);
+ else
+ addf(str, _("\t\tNot After: %s\n"), s);
+ } else {
+ addf(str, _("\t\tNot After: %s\n"), _("unknown"));
+ }
+ }
+
+ /* Subject. */
+ {
+ gnutls_datum_t dn;
+ int err;
+
+ err = gnutls_x509_crt_get_dn3(cert, &dn, 0);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ addf(str, _("\tSubject:\n"));
+ } else if (err < 0) {
+ addf(str, "error: get_dn: %s\n",
+ gnutls_strerror(err));
+ } else {
+ addf(str, _("\tSubject: %s\n"), dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ /* SubjectPublicKeyInfo. */
+ print_crt_pubkey(str, cert, format);
+
+ print_unique_ids(str, cert);
+
+ /* Extensions. */
+ if (gnutls_x509_crt_get_version(cert) >= 3) {
+ cert_type_t ccert;
+
+ ccert.crt = cert;
+ print_extensions(str, "", TYPE_CRT, ccert);
+ }
+
+ /* Signature. */
+ if (format != GNUTLS_CRT_PRINT_UNSIGNED_FULL) {
+ int err;
+ size_t size = 0;
+ char *buffer = NULL;
+ char *name;
+ const char *p;
+
+ name = get_sign_name(cert, &err);
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+
+ addf(str, _("\tSignature Algorithm: %s\n"), p);
+ gnutls_free(name);
+
+ print_crt_sig_params(str, cert, format);
+
+ if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure2(err, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0) {
+ adds(str,
+ _("warning: signed using a broken signature "
+ "algorithm that can be forged.\n"));
+ }
+
+ err = gnutls_x509_crt_get_signature(cert, buffer, &size);
+ if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ addf(str, "error: get_signature: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ buffer = gnutls_malloc(size);
+ if (!buffer) {
+ addf(str, "error: malloc: %s\n",
+ gnutls_strerror(GNUTLS_E_MEMORY_ERROR));
+ return;
+ }
+
+ err = gnutls_x509_crt_get_signature(cert, buffer, &size);
+ if (err < 0) {
+ gnutls_free(buffer);
+ addf(str, "error: get_signature2: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ adds(str, _("\tSignature:\n"));
+ _gnutls_buffer_hexdump(str, buffer, size, "\t\t");
+
+ gnutls_free(buffer);
+ }
+}
+
+static void
+print_fingerprint(gnutls_buffer_st * str, gnutls_x509_crt_t cert)
+{
+ int err;
+ char buffer[MAX_HASH_SIZE];
+ size_t size = sizeof(buffer);
+
+ adds(str, _("\tFingerprint:\n"));
+
+ err = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, buffer, &size);
+ if (err < 0) {
+ addf(str, "error: get_fingerprint: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ adds(str, _("\t\tsha1:"));
+ _gnutls_buffer_hexprint(str, buffer, size);
+ adds(str, "\n");
+
+ size = sizeof(buffer);
+ err = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, buffer, &size);
+ if (err < 0) {
+ addf(str, "error: get_fingerprint: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+ adds(str, _("\t\tsha256:"));
+ _gnutls_buffer_hexprint(str, buffer, size);
+ adds(str, "\n");
+}
+
+typedef int get_id_func(void *obj, unsigned, unsigned char*, size_t*);
+
+static void print_obj_id(gnutls_buffer_st *str, const char *prefix, void *obj, get_id_func *get_id)
+{
+ unsigned char sha1_buffer[MAX_HASH_SIZE];
+ unsigned char sha2_buffer[MAX_HASH_SIZE];
+ int err;
+ size_t sha1_size, sha2_size;
+
+ sha1_size = sizeof(sha1_buffer);
+ err = get_id(obj, GNUTLS_KEYID_USE_SHA1, sha1_buffer, &sha1_size);
+ if (err == GNUTLS_E_UNIMPLEMENTED_FEATURE) /* unsupported algo */
+ return;
+
+ if (err < 0) {
+ addf(str, "error: get_key_id(sha1): %s\n", gnutls_strerror(err));
+ return;
+ }
+
+ sha2_size = sizeof(sha2_buffer);
+ err = get_id(obj, GNUTLS_KEYID_USE_SHA256, sha2_buffer, &sha2_size);
+ if (err == GNUTLS_E_UNIMPLEMENTED_FEATURE) /* unsupported algo */
+ return;
+
+ if (err < 0) {
+ addf(str, "error: get_key_id(sha256): %s\n", gnutls_strerror(err));
+ return;
+ }
+
+ addf(str, _("%sPublic Key ID:\n%s\tsha1:"), prefix, prefix);
+ _gnutls_buffer_hexprint(str, sha1_buffer, sha1_size);
+ addf(str, "\n%s\tsha256:", prefix);
+ _gnutls_buffer_hexprint(str, sha2_buffer, sha2_size);
+ adds(str, "\n");
+
+ addf(str, _("%sPublic Key PIN:\n%s\tpin-sha256:"), prefix, prefix);
+ _gnutls_buffer_base64print(str, sha2_buffer, sha2_size);
+ adds(str, "\n");
+
+ return;
+}
+
+static void print_keyid(gnutls_buffer_st * str, gnutls_x509_crt_t cert)
+{
+ int err;
+ const char *name;
+ unsigned int bits;
+ unsigned char sha1_buffer[MAX_HASH_SIZE];
+ size_t sha1_size;
+
+ err = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
+ if (err < 0)
+ return;
+
+ print_obj_id(str, "\t", cert, (get_id_func*)gnutls_x509_crt_get_key_id);
+
+ if (IS_EC(err)) {
+ gnutls_ecc_curve_t curve;
+
+ err = gnutls_x509_crt_get_pk_ecc_raw(cert, &curve, NULL, NULL);
+ if (err < 0)
+ return;
+
+ name = gnutls_ecc_curve_get_name(curve);
+ bits = 0;
+ } else if (IS_GOSTEC(err)) {
+ gnutls_ecc_curve_t curve;
+
+ err = gnutls_x509_crt_get_pk_gost_raw(cert, &curve, NULL, NULL, NULL, NULL);
+ if (err < 0)
+ return;
+
+ name = gnutls_ecc_curve_get_name(curve);
+ bits = 0;
+ } else {
+ name = gnutls_pk_get_name(err);
+ }
+
+ if (name == NULL)
+ return;
+
+ sha1_size = sizeof(sha1_buffer);
+ err = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA1, sha1_buffer, &sha1_size);
+ if (err == GNUTLS_E_UNIMPLEMENTED_FEATURE) /* unsupported algo */
+ return;
+}
+
+static void
+print_other(gnutls_buffer_st * str, gnutls_x509_crt_t cert,
+ gnutls_certificate_print_formats_t format)
+{
+ if (format != GNUTLS_CRT_PRINT_UNSIGNED_FULL) {
+ print_fingerprint(str, cert);
+ }
+ print_keyid(str, cert);
+}
+
+static void print_oneline(gnutls_buffer_st * str, gnutls_x509_crt_t cert)
+{
+ int err;
+
+ /* Subject. */
+ {
+ gnutls_datum_t dn;
+
+ err = gnutls_x509_crt_get_dn3(cert, &dn, 0);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ addf(str, _("no subject,"));
+ } else if (err < 0) {
+ addf(str, "unknown subject (%s), ",
+ gnutls_strerror(err));
+ } else {
+ addf(str, "subject `%s', ", dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ /* Issuer. */
+ {
+ gnutls_datum_t dn;
+
+ err = gnutls_x509_crt_get_issuer_dn3(cert, &dn, 0);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ addf(str, _("no issuer,"));
+ } else if (err < 0) {
+ addf(str, "unknown issuer (%s), ",
+ gnutls_strerror(err));
+ } else {
+ addf(str, "issuer `%s', ", dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ {
+ char serial[128];
+ size_t serial_size = sizeof(serial);
+
+ err =
+ gnutls_x509_crt_get_serial(cert, serial, &serial_size);
+ if (err >= 0) {
+ adds(str, "serial 0x");
+ _gnutls_buffer_hexprint(str, serial, serial_size);
+ adds(str, ", ");
+ }
+ }
+
+ /* Key algorithm and size. */
+ {
+ unsigned int bits;
+ const char *p;
+ char *name = get_pk_name(cert, &bits);
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+ addf(str, "%s key %d bits, ", p, bits);
+ gnutls_free(name);
+ }
+
+ /* Signature Algorithm. */
+ {
+ char *name = get_sign_name(cert, &err);
+ const char *p;
+
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+
+ if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure2(err, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0)
+ addf(str, _("signed using %s (broken!), "), p);
+ else
+ addf(str, _("signed using %s, "), p);
+ gnutls_free(name);
+ }
+
+ /* Validity. */
+ {
+ time_t tim;
+
+ tim = gnutls_x509_crt_get_activation_time(cert);
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "unknown activation (%ld), ",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max, "%Y-%m-%d %H:%M:%S UTC",
+ &t) == 0)
+ addf(str, "failed activation (%ld), ",
+ (unsigned long) tim);
+ else
+ addf(str, "activated `%s', ", s);
+ }
+
+ tim = gnutls_x509_crt_get_expiration_time(cert);
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "unknown expiry (%ld), ",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max, "%Y-%m-%d %H:%M:%S UTC",
+ &t) == 0)
+ addf(str, "failed expiry (%ld), ",
+ (unsigned long) tim);
+ else
+ addf(str, "expires `%s', ", s);
+ }
+ }
+
+ {
+ int pathlen;
+ char *policyLanguage;
+
+ err = gnutls_x509_crt_get_proxy(cert, NULL,
+ &pathlen, &policyLanguage,
+ NULL, NULL);
+ if (err == 0) {
+ addf(str, "proxy certificate (policy=");
+ if (strcmp(policyLanguage, "1.3.6.1.5.5.7.21.1") ==
+ 0)
+ addf(str, "id-ppl-inheritALL");
+ else if (strcmp
+ (policyLanguage,
+ "1.3.6.1.5.5.7.21.2") == 0)
+ addf(str, "id-ppl-independent");
+ else
+ addf(str, "%s", policyLanguage);
+ if (pathlen >= 0)
+ addf(str, ", pathlen=%d), ", pathlen);
+ else
+ addf(str, "), ");
+ gnutls_free(policyLanguage);
+ }
+ }
+
+ {
+ unsigned char buffer[MAX_HASH_SIZE];
+ size_t size = sizeof(buffer);
+
+ err = gnutls_x509_crt_get_key_id(cert, GNUTLS_KEYID_USE_SHA256,
+ buffer, &size);
+ if (err >= 0) {
+ addf(str, "pin-sha256=\"");
+ _gnutls_buffer_base64print(str, buffer, size);
+ adds(str, "\"");
+ }
+ }
+
+}
+
+/**
+ * gnutls_x509_crt_print:
+ * @cert: The data to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print a X.509 certificate, suitable for
+ * display to a human.
+ *
+ * If the format is %GNUTLS_CRT_PRINT_FULL then all fields of the
+ * certificate will be output, on multiple lines. The
+ * %GNUTLS_CRT_PRINT_ONELINE format will generate one line with some
+ * selected fields, which is useful for logging purposes.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_print(gnutls_x509_crt_t cert,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+ int ret;
+
+ if (format == GNUTLS_CRT_PRINT_COMPACT) {
+ _gnutls_buffer_init(&str);
+
+ print_oneline(&str, cert);
+
+ ret = _gnutls_buffer_append_data(&str, "\n", 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ print_keyid(&str, cert);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+ } else if (format == GNUTLS_CRT_PRINT_ONELINE) {
+ _gnutls_buffer_init(&str);
+
+ print_oneline(&str, cert);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+ } else {
+ _gnutls_buffer_init(&str);
+
+ _gnutls_buffer_append_str(&str,
+ _
+ ("X.509 Certificate Information:\n"));
+
+ print_cert(&str, cert, format);
+
+ _gnutls_buffer_append_str(&str, _("Other Information:\n"));
+
+ print_other(&str, cert, format);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+ }
+}
+
+static void
+print_crl(gnutls_buffer_st * str, gnutls_x509_crl_t crl, int notsigned)
+{
+ /* Version. */
+ {
+ int version = gnutls_x509_crl_get_version(crl);
+ if (version < 0)
+ addf(str, "error: get_version: %s\n",
+ gnutls_strerror(version));
+ else
+ addf(str, _("\tVersion: %d\n"), version);
+ }
+
+ /* Issuer. */
+ if (!notsigned) {
+ gnutls_datum_t dn;
+ int err;
+
+ err = gnutls_x509_crl_get_issuer_dn3(crl, &dn, 0);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ addf(str, _("\tIssuer:\n"));
+ } else if (err < 0) {
+ addf(str, "error: get_issuer_dn: %s\n",
+ gnutls_strerror(err));
+ } else {
+ addf(str, _("\tIssuer: %s\n"), dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ /* Validity. */
+ {
+ time_t tim;
+
+ adds(str, _("\tUpdate dates:\n"));
+
+ tim = gnutls_x509_crl_get_this_update(crl);
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) tim);
+ else
+ addf(str, _("\t\tIssued: %s\n"), s);
+ }
+
+ tim = gnutls_x509_crl_get_next_update(crl);
+ {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ if (tim == -1)
+ addf(str, "\t\tNo next update time.\n");
+ else if (gmtime_r(&tim, &t) == NULL)
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max, "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long) tim);
+ else
+ addf(str, _("\t\tNext at: %s\n"), s);
+ }
+ }
+
+ /* Extensions. */
+ if (gnutls_x509_crl_get_version(crl) >= 2) {
+ size_t i;
+ int err = 0;
+ int aki_idx = 0;
+ int crl_nr = 0;
+
+ for (i = 0;; i++) {
+ char oid[MAX_OID_SIZE] = "";
+ size_t sizeof_oid = sizeof(oid);
+ unsigned int critical;
+
+ err = gnutls_x509_crl_get_extension_info(crl, i,
+ oid,
+ &sizeof_oid,
+ &critical);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (err < 0) {
+ addf(str,
+ "error: get_extension_info: %s\n",
+ gnutls_strerror(err));
+ break;
+ }
+
+ if (i == 0)
+ adds(str, _("\tExtensions:\n"));
+
+ if (strcmp(oid, "2.5.29.20") == 0) {
+ char nr[128];
+ size_t nr_size = sizeof(nr);
+
+ if (crl_nr) {
+ addf(str,
+ "warning: more than one CRL number\n");
+ }
+
+ err =
+ gnutls_x509_crl_get_number(crl, nr,
+ &nr_size,
+ &critical);
+
+ addf(str, _("\t\tCRL Number (%s): "),
+ critical ? _("critical") :
+ _("not critical"));
+
+ if (err < 0)
+ addf(str,
+ "error: get_number: %s\n",
+ gnutls_strerror(err));
+ else {
+ _gnutls_buffer_hexprint(str, nr,
+ nr_size);
+ addf(str, "\n");
+ }
+
+ crl_nr++;
+ } else if (strcmp(oid, "2.5.29.35") == 0) {
+ gnutls_datum_t der;
+
+ if (aki_idx) {
+ addf(str,
+ "warning: more than one AKI extension\n");
+ }
+
+ addf(str,
+ _
+ ("\t\tAuthority Key Identifier (%s):\n"),
+ critical ? _("critical") :
+ _("not critical"));
+
+ err = gnutls_x509_crl_get_extension_data2(crl, i, &der);
+ if (err < 0) {
+ addf(str,
+ "error: get_extension_data2: %s\n",
+ gnutls_strerror(err));
+ continue;
+ }
+ print_aki(str, &der);
+ gnutls_free(der.data);
+
+ aki_idx++;
+ } else {
+ gnutls_datum_t der;
+
+ addf(str,
+ _("\t\tUnknown extension %s (%s):\n"),
+ oid,
+ critical ? _("critical") :
+ _("not critical"));
+
+ err =
+ gnutls_x509_crl_get_extension_data2(crl,
+ i,
+ &der);
+ if (err < 0) {
+ addf(str,
+ "error: get_extension_data2: %s\n",
+ gnutls_strerror(err));
+ continue;
+ }
+
+ adds(str, _("\t\t\tASCII: "));
+ _gnutls_buffer_asciiprint(str, (char*)der.data, der.size);
+ adds(str, "\n");
+
+ adds(str, _("\t\t\tHexdump: "));
+ _gnutls_buffer_hexprint(str, der.data, der.size);
+ adds(str, "\n");
+
+ gnutls_free(der.data);
+ }
+ }
+ }
+
+
+ /* Revoked certificates. */
+ {
+ int num = gnutls_x509_crl_get_crt_count(crl);
+ gnutls_x509_crl_iter_t iter = NULL;
+ int j;
+
+ if (num)
+ addf(str, _("\tRevoked certificates (%d):\n"),
+ num);
+ else
+ adds(str, _("\tNo revoked certificates.\n"));
+
+ for (j = 0; j < num; j++) {
+ unsigned char serial[128];
+ size_t serial_size = sizeof(serial);
+ int err;
+ time_t tim;
+
+ err =
+ gnutls_x509_crl_iter_crt_serial(crl, &iter, serial,
+ &serial_size,
+ &tim);
+ if (err < 0) {
+ addf(str, "error: iter_crt_serial: %s\n",
+ gnutls_strerror(err));
+ break;
+ } else {
+ char s[42];
+ size_t max = sizeof(s);
+ struct tm t;
+
+ adds(str, _("\t\tSerial Number (hex): "));
+ _gnutls_buffer_hexprint(str, serial,
+ serial_size);
+ adds(str, "\n");
+
+ if (gmtime_r(&tim, &t) == NULL)
+ addf(str,
+ "error: gmtime_r (%ld)\n",
+ (unsigned long) tim);
+ else if (strftime
+ (s, max,
+ "%a %b %d %H:%M:%S UTC %Y",
+ &t) == 0)
+ addf(str,
+ "error: strftime (%ld)\n",
+ (unsigned long) tim);
+ else
+ addf(str,
+ _("\t\tRevoked at: %s\n"), s);
+ }
+ }
+ gnutls_x509_crl_iter_deinit(iter);
+ }
+
+ /* Signature. */
+ if (!notsigned) {
+ int err;
+ size_t size = 0;
+ char *buffer = NULL;
+ char *name;
+ const char *p;
+
+ name = crl_get_sign_name(crl, &err);
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+
+ addf(str, _("\tSignature Algorithm: %s\n"), p);
+ gnutls_free(name);
+
+ if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure2(err, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0) {
+ adds(str,
+ _("warning: signed using a broken signature "
+ "algorithm that can be forged.\n"));
+ }
+
+ err = gnutls_x509_crl_get_signature(crl, buffer, &size);
+ if (err != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ addf(str, "error: get_signature: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ buffer = gnutls_malloc(size);
+ if (!buffer) {
+ addf(str, "error: malloc: %s\n",
+ gnutls_strerror(GNUTLS_E_MEMORY_ERROR));
+ return;
+ }
+
+ err = gnutls_x509_crl_get_signature(crl, buffer, &size);
+ if (err < 0) {
+ gnutls_free(buffer);
+ addf(str, "error: get_signature2: %s\n",
+ gnutls_strerror(err));
+ return;
+ }
+
+ adds(str, _("\tSignature:\n"));
+ _gnutls_buffer_hexdump(str, buffer, size, "\t\t");
+
+ gnutls_free(buffer);
+ }
+}
+
+/**
+ * gnutls_x509_crl_print:
+ * @crl: The data to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print a X.509 certificate revocation
+ * list, suitable for display to a human.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_print(gnutls_x509_crl_t crl,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+
+ _gnutls_buffer_init(&str);
+
+ _gnutls_buffer_append_str
+ (&str, _("X.509 Certificate Revocation List Information:\n"));
+
+ print_crl(&str, crl, format == GNUTLS_CRT_PRINT_UNSIGNED_FULL);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+}
+
+static int
+print_crq_sig_params(gnutls_buffer_st * str, gnutls_x509_crq_t crt,
+ gnutls_certificate_print_formats_t format)
+{
+ int ret;
+ gnutls_pk_algorithm_t pk;
+ gnutls_x509_spki_st params;
+ gnutls_sign_algorithm_t sign;
+
+ sign = gnutls_x509_crq_get_signature_algorithm(crt);
+ pk = gnutls_sign_get_pk_algorithm(sign);
+ if (pk == GNUTLS_PK_RSA_PSS) {
+ ret = _gnutls_x509_read_sign_params(crt->crq,
+ "signatureAlgorithm",
+ &params);
+ if (ret < 0) {
+ addf(str, "error: read_pss_params: %s\n",
+ gnutls_strerror(ret));
+ } else
+ addf(str, "\t\tSalt Length: %d\n", params.salt_size);
+ }
+
+ return 0;
+}
+
+static int
+print_crq_pubkey(gnutls_buffer_st * str, gnutls_x509_crq_t crq,
+ gnutls_certificate_print_formats_t format)
+{
+ gnutls_pubkey_t pubkey;
+ gnutls_x509_spki_st params;
+ int ret;
+
+ ret = _gnutls_x509_crq_read_spki_params(crq, &params);
+ if (ret < 0)
+ return ret;
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return ret;
+
+ ret = gnutls_pubkey_import_x509_crq(pubkey, crq, 0);
+ if (ret < 0)
+ goto cleanup;
+
+ print_pubkey(str, _("Subject "), pubkey, &params, format);
+ ret = 0;
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+
+ if (ret < 0) { /* print only name */
+ const char *p;
+ char *name = crq_get_pk_name(crq);
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+
+ addf(str, "\tSubject Public Key Algorithm: %s\n", p);
+ gnutls_free(name);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void
+print_crq(gnutls_buffer_st * str, gnutls_x509_crq_t cert,
+ gnutls_certificate_print_formats_t format)
+{
+ /* Version. */
+ {
+ int version = gnutls_x509_crq_get_version(cert);
+ if (version < 0)
+ addf(str, "error: get_version: %s\n",
+ gnutls_strerror(version));
+ else
+ addf(str, _("\tVersion: %d\n"), version);
+ }
+
+ /* Subject */
+ {
+ gnutls_datum_t dn;
+ int err;
+
+ err = gnutls_x509_crq_get_dn3(cert, &dn, 0);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ addf(str, _("\tSubject:\n"));
+ } else if (err < 0) {
+ addf(str, "error: get_dn: %s\n",
+ gnutls_strerror(err));
+ } else {
+ addf(str, _("\tSubject: %s\n"), dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ {
+ char *name;
+ const char *p;
+
+ print_crq_pubkey(str, cert, format);
+
+ name = crq_get_sign_name(cert);
+ if (name == NULL)
+ p = _("unknown");
+ else
+ p = name;
+
+ addf(str, _("\tSignature Algorithm: %s\n"), p);
+
+ gnutls_free(name);
+
+ print_crq_sig_params(str, cert, format);
+ }
+
+ /* parse attributes */
+ {
+ size_t i;
+ int err = 0;
+ int extensions = 0;
+ int challenge = 0;
+
+ for (i = 0;; i++) {
+ char oid[MAX_OID_SIZE] = "";
+ size_t sizeof_oid = sizeof(oid);
+
+ err =
+ gnutls_x509_crq_get_attribute_info(cert, i,
+ oid,
+ &sizeof_oid);
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (err < 0) {
+ addf(str,
+ "error: get_extension_info: %s\n",
+ gnutls_strerror(err));
+ break;
+ }
+
+ if (i == 0)
+ adds(str, _("\tAttributes:\n"));
+
+ if (strcmp(oid, "1.2.840.113549.1.9.14") == 0) {
+ cert_type_t ccert;
+
+ if (extensions) {
+ addf(str,
+ "warning: more than one extensionsRequest\n");
+ }
+
+ ccert.crq = cert;
+ print_extensions(str, "\t", TYPE_CRQ,
+ ccert);
+
+ extensions++;
+ } else if (strcmp(oid, "1.2.840.113549.1.9.7") ==
+ 0) {
+ char *pass;
+ size_t size;
+
+ if (challenge) {
+ adds(str,
+ "warning: more than one Challenge password attribute\n");
+ }
+
+ err =
+ gnutls_x509_crq_get_challenge_password
+ (cert, NULL, &size);
+ if (err < 0
+ && err !=
+ GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ addf(str,
+ "error: get_challenge_password: %s\n",
+ gnutls_strerror(err));
+ continue;
+ }
+
+ size++;
+
+ pass = gnutls_malloc(size);
+ if (!pass) {
+ addf(str, "error: malloc: %s\n",
+ gnutls_strerror
+ (GNUTLS_E_MEMORY_ERROR));
+ continue;
+ }
+
+ err =
+ gnutls_x509_crq_get_challenge_password
+ (cert, pass, &size);
+ if (err < 0)
+ addf(str,
+ "error: get_challenge_password: %s\n",
+ gnutls_strerror(err));
+ else
+ addf(str,
+ _
+ ("\t\tChallenge password: %s\n"),
+ pass);
+
+ gnutls_free(pass);
+
+ challenge++;
+ } else {
+ char *buffer;
+ size_t extlen = 0;
+
+ addf(str, _("\t\tUnknown attribute %s:\n"),
+ oid);
+
+ err =
+ gnutls_x509_crq_get_attribute_data
+ (cert, i, NULL, &extlen);
+ if (err < 0) {
+ addf(str,
+ "error: get_attribute_data: %s\n",
+ gnutls_strerror(err));
+ continue;
+ }
+
+ buffer = gnutls_malloc(extlen);
+ if (!buffer) {
+ addf(str, "error: malloc: %s\n",
+ gnutls_strerror
+ (GNUTLS_E_MEMORY_ERROR));
+ continue;
+ }
+
+ err =
+ gnutls_x509_crq_get_attribute_data
+ (cert, i, buffer, &extlen);
+ if (err < 0) {
+ gnutls_free(buffer);
+ addf(str,
+ "error: get_attribute_data2: %s\n",
+ gnutls_strerror(err));
+ continue;
+ }
+
+ adds(str, _("\t\t\tASCII: "));
+ _gnutls_buffer_asciiprint(str, buffer,
+ extlen);
+ adds(str, "\n");
+
+ adds(str, _("\t\t\tHexdump: "));
+ _gnutls_buffer_hexprint(str, buffer,
+ extlen);
+ adds(str, "\n");
+
+ gnutls_free(buffer);
+ }
+ }
+ }
+}
+
+static void print_crq_other(gnutls_buffer_st * str, gnutls_x509_crq_t crq)
+{
+ int ret;
+
+ /* on unknown public key algorithms don't print the key ID */
+ ret = gnutls_x509_crq_get_pk_algorithm(crq, NULL);
+ if (ret < 0)
+ return;
+
+ print_obj_id(str, "\t", crq, (get_id_func*)gnutls_x509_crq_get_key_id);
+}
+
+/**
+ * gnutls_x509_crq_print:
+ * @crq: The data to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print a certificate request, suitable for
+ * display to a human.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crq_print(gnutls_x509_crq_t crq,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+
+ _gnutls_buffer_init(&str);
+
+ _gnutls_buffer_append_str
+ (&str, _("PKCS #10 Certificate Request Information:\n"));
+
+ print_crq(&str, crq, format);
+
+ _gnutls_buffer_append_str(&str, _("Other Information:\n"));
+
+ print_crq_other(&str, crq);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+}
+
+static void
+print_pubkey_other(gnutls_buffer_st * str, gnutls_pubkey_t pubkey,
+ gnutls_certificate_print_formats_t format)
+{
+ int ret;
+ unsigned int usage;
+
+ ret = gnutls_pubkey_get_key_usage(pubkey, &usage);
+ if (ret < 0) {
+ addf(str, "error: get_key_usage: %s\n",
+ gnutls_strerror(ret));
+ return;
+ }
+
+ adds(str, "\n");
+ if (pubkey->key_usage) {
+ adds(str, _("Public Key Usage:\n"));
+ print_key_usage2(str, "\t", pubkey->key_usage);
+ }
+
+ /* on unknown public key algorithms don't print the key ID */
+ ret = gnutls_pubkey_get_pk_algorithm(pubkey, NULL);
+ if (ret < 0)
+ return;
+
+ print_obj_id(str, "", pubkey, (get_id_func*)gnutls_pubkey_get_key_id);
+}
+
+/**
+ * gnutls_pubkey_print:
+ * @pubkey: The data to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print public key information, suitable for
+ * display to a human.
+ *
+ * Only %GNUTLS_CRT_PRINT_FULL and %GNUTLS_CRT_PRINT_FULL_NUMBERS
+ * are implemented.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.5
+ **/
+int
+gnutls_pubkey_print(gnutls_pubkey_t pubkey,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+
+ _gnutls_buffer_init(&str);
+
+ _gnutls_buffer_append_str(&str, _("Public Key Information:\n"));
+
+ print_pubkey(&str, "", pubkey, NULL, format);
+ print_pubkey_other(&str, pubkey, format);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+}
+
+/**
+ * gnutls_x509_ext_print:
+ * @exts: The data to be printed
+ * @exts_size: the number of available structures
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print X.509 certificate extensions,
+ * suitable for display to a human.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_ext_print(gnutls_x509_ext_st *exts, unsigned int exts_size,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+ struct ext_indexes_st idx;
+ unsigned i;
+
+ memset(&idx, 0, sizeof(idx));
+ _gnutls_buffer_init(&str);
+
+ for (i=0;i<exts_size;i++)
+ print_extension(&str, "", &idx, (char*)exts[i].oid, exts[i].critical, &exts[i].data);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+}
diff --git a/lib/x509/pkcs12.c b/lib/x509/pkcs12.c
new file mode 100644
index 0000000..11b9da3
--- /dev/null
+++ b/lib/x509/pkcs12.c
@@ -0,0 +1,2063 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2012 Nikos Mavrogiannopoulos
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate on PKCS12 packet parsing.
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <num.h>
+#include <common.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include "pkcs7_int.h"
+#include <random.h>
+#include "intprops.h"
+
+
+/* Decodes the PKCS #12 auth_safe, and returns the allocated raw data,
+ * which holds them. Returns an asn1_node of authenticatedSafe.
+ */
+static int
+_decode_pkcs12_auth_safe(asn1_node pkcs12, asn1_node * authen_safe,
+ gnutls_datum_t * raw)
+{
+ char oid[MAX_OID_SIZE];
+ asn1_node c2 = NULL;
+ gnutls_datum_t auth_safe = { NULL, 0 };
+ int len, result;
+ char error_str[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+ len = sizeof(oid) - 1;
+ result =
+ asn1_read_value(pkcs12, "authSafe.contentType", oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (strcmp(oid, DATA_OID) != 0) {
+ gnutls_assert();
+ _gnutls_debug_log("Unknown PKCS12 Content OID '%s'\n",
+ oid);
+ return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE;
+ }
+
+ /* Step 1. Read the content data
+ */
+
+ result =
+ _gnutls_x509_read_string(pkcs12, "authSafe.content",
+ &auth_safe, ASN1_ETYPE_OCTET_STRING, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Step 2. Extract the authenticatedSafe.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-AuthenticatedSafe",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_der_decoding(&c2, auth_safe.data, auth_safe.size,
+ error_str);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ _gnutls_debug_log("DER error: %s\n", error_str);
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (raw == NULL) {
+ _gnutls_free_datum(&auth_safe);
+ } else {
+ raw->data = auth_safe.data;
+ raw->size = auth_safe.size;
+ }
+
+ if (authen_safe)
+ *authen_safe = c2;
+ else
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ _gnutls_free_datum(&auth_safe);
+ return result;
+}
+
+static int pkcs12_reinit(gnutls_pkcs12_t pkcs12)
+{
+int result;
+
+ if (pkcs12->pkcs12)
+ asn1_delete_structure(&pkcs12->pkcs12);
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-PFX",
+ &pkcs12->pkcs12);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_init:
+ * @pkcs12: A pointer to the type to be initialized
+ *
+ * This function will initialize a PKCS12 type. PKCS12 structures
+ * usually contain lists of X.509 Certificates and X.509 Certificate
+ * revocation lists.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_init(gnutls_pkcs12_t * pkcs12)
+{
+ *pkcs12 = gnutls_calloc(1, sizeof(gnutls_pkcs12_int));
+
+ if (*pkcs12) {
+ int result = pkcs12_reinit(*pkcs12);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(*pkcs12);
+ return result;
+ }
+ return 0; /* success */
+ }
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+/**
+ * gnutls_pkcs12_deinit:
+ * @pkcs12: The type to be initialized
+ *
+ * This function will deinitialize a PKCS12 type.
+ **/
+void gnutls_pkcs12_deinit(gnutls_pkcs12_t pkcs12)
+{
+ if (!pkcs12)
+ return;
+
+ if (pkcs12->pkcs12)
+ asn1_delete_structure(&pkcs12->pkcs12);
+
+ gnutls_free(pkcs12);
+}
+
+/**
+ * gnutls_pkcs12_import:
+ * @pkcs12: The data to store the parsed PKCS12.
+ * @data: The DER or PEM encoded PKCS12.
+ * @format: One of DER or PEM
+ * @flags: an ORed sequence of gnutls_privkey_pkcs8_flags
+ *
+ * This function will convert the given DER or PEM encoded PKCS12
+ * to the native gnutls_pkcs12_t format. The output will be stored in 'pkcs12'.
+ *
+ * If the PKCS12 is PEM encoded it should have a header of "PKCS12".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs12_import(gnutls_pkcs12_t pkcs12,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format, unsigned int flags)
+{
+ int result = 0, need_free = 0;
+ gnutls_datum_t _data;
+ char error_str[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* If the PKCS12 is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ result =
+ _gnutls_fbase64_decode(PEM_PKCS12, data->data,
+ data->size, &_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ need_free = 1;
+ }
+
+ if (pkcs12->expanded) {
+ result = pkcs12_reinit(pkcs12);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ pkcs12->expanded = 1;
+
+ result =
+ asn1_der_decoding(&pkcs12->pkcs12, _data.data, _data.size,
+ error_str);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ _gnutls_debug_log("DER error: %s\n", error_str);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (need_free)
+ _gnutls_free_datum(&_data);
+
+ return 0;
+
+ cleanup:
+ if (need_free)
+ _gnutls_free_datum(&_data);
+ return result;
+}
+
+
+/**
+ * gnutls_pkcs12_export:
+ * @pkcs12: A pkcs12 type
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a structure PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the pkcs12 structure to DER or PEM format.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *output_data_size will be updated and GNUTLS_E_SHORT_MEMORY_BUFFER
+ * will be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN PKCS12".
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int
+gnutls_pkcs12_export(gnutls_pkcs12_t pkcs12,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ int ret;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_export_int(pkcs12->pkcs12, format, PEM_PKCS12,
+ output_data, output_data_size);
+
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ /* PKCS#12 export is always non-approved, because the MAC
+ * calculation involves non-approved KDF (PKCS#12 KDF) and
+ * without MAC the protection is insufficient.
+ */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_pkcs12_export2:
+ * @pkcs12: A pkcs12 type
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a structure PEM or DER encoded
+ *
+ * This function will export the pkcs12 structure to DER or PEM format.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN PKCS12".
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_pkcs12_export2(gnutls_pkcs12_t pkcs12,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ int ret;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_export_int2(pkcs12->pkcs12, format, PEM_PKCS12,
+ out);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ /* PKCS#12 export is always non-approved, because the MAC
+ * calculation involves non-approved KDF (PKCS#12 KDF) and
+ * without MAC the protection is insufficient.
+ */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+ return ret;
+}
+
+static int oid2bag(const char *oid)
+{
+ if (strcmp(oid, BAG_PKCS8_KEY) == 0)
+ return GNUTLS_BAG_PKCS8_KEY;
+ if (strcmp(oid, BAG_PKCS8_ENCRYPTED_KEY) == 0)
+ return GNUTLS_BAG_PKCS8_ENCRYPTED_KEY;
+ if (strcmp(oid, BAG_CERTIFICATE) == 0)
+ return GNUTLS_BAG_CERTIFICATE;
+ if (strcmp(oid, BAG_CRL) == 0)
+ return GNUTLS_BAG_CRL;
+ if (strcmp(oid, BAG_SECRET) == 0)
+ return GNUTLS_BAG_SECRET;
+
+ return GNUTLS_BAG_UNKNOWN;
+}
+
+static const char *bag_to_oid(int bag)
+{
+ switch (bag) {
+ case GNUTLS_BAG_PKCS8_KEY:
+ return BAG_PKCS8_KEY;
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ return BAG_PKCS8_ENCRYPTED_KEY;
+ case GNUTLS_BAG_CERTIFICATE:
+ return BAG_CERTIFICATE;
+ case GNUTLS_BAG_CRL:
+ return BAG_CRL;
+ case GNUTLS_BAG_SECRET:
+ return BAG_SECRET;
+ }
+ return NULL;
+}
+
+/* Decodes the SafeContents, and puts the output in
+ * the given bag.
+ */
+int
+_pkcs12_decode_safe_contents(const gnutls_datum_t * content,
+ gnutls_pkcs12_bag_t bag)
+{
+ char oid[MAX_OID_SIZE], root[MAX_NAME_SIZE];
+ asn1_node c2 = NULL;
+ int len, result;
+ int bag_type;
+ gnutls_datum_t attr_val;
+ gnutls_datum_t t;
+ int count = 0, attributes, j;
+ unsigned i;
+
+ /* Step 1. Extract the SEQUENCE.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-SafeContents",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_der_decoding(&c2, content->data, content->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Count the number of bags
+ */
+ result = asn1_number_of_elements(c2, "", &count);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ bag->bag_elements = MIN(MAX_BAG_ELEMENTS, count);
+
+ for (i = 0; i < bag->bag_elements; i++) {
+
+ snprintf(root, sizeof(root), "?%u.bagId", i + 1);
+
+ len = sizeof(oid);
+ result = asn1_read_value(c2, root, oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Read the Bag type
+ */
+ bag_type = oid2bag(oid);
+
+ if (bag_type < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Read the Bag Value
+ */
+
+ snprintf(root, sizeof(root), "?%u.bagValue", i + 1);
+
+ result =
+ _gnutls_x509_read_value(c2, root,
+ &bag->element[i].data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (bag_type == GNUTLS_BAG_CERTIFICATE
+ || bag_type == GNUTLS_BAG_CRL
+ || bag_type == GNUTLS_BAG_SECRET) {
+ gnutls_datum_t tmp = bag->element[i].data;
+ bag->element[i].data.data = NULL;
+ bag->element[i].data.size = 0;
+
+ result =
+ _pkcs12_decode_crt_bag(bag_type, &tmp,
+ &bag->element[i].data);
+ _gnutls_free_datum(&tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* read the bag attributes
+ */
+ snprintf(root, sizeof(root), "?%u.bagAttributes", i + 1);
+
+ result = asn1_number_of_elements(c2, root, &attributes);
+ if (result != ASN1_SUCCESS
+ && result != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (attributes < 0)
+ attributes = 1;
+
+ if (result != ASN1_ELEMENT_NOT_FOUND)
+ for (j = 0; j < attributes; j++) {
+
+ snprintf(root, sizeof(root),
+ "?%u.bagAttributes.?%d", i + 1,
+ j + 1);
+
+ result =
+ _gnutls_x509_decode_and_read_attribute
+ (c2, root, oid, sizeof(oid), &attr_val,
+ 1, 0);
+
+ if (result < 0) {
+ gnutls_assert();
+ continue; /* continue in case we find some known attributes */
+ }
+
+ if (strcmp(oid, KEY_ID_OID) == 0) {
+ result =
+ _gnutls_x509_decode_string
+ (ASN1_ETYPE_OCTET_STRING,
+ attr_val.data, attr_val.size,
+ &t, 1);
+
+ _gnutls_free_datum(&attr_val);
+ if (result < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Error decoding PKCS12 Bag Attribute OID '%s'\n",
+ oid);
+ continue;
+ }
+
+ _gnutls_free_datum(&bag->element[i].local_key_id);
+ bag->element[i].local_key_id.data = t.data;
+ bag->element[i].local_key_id.size = t.size;
+ } else if (strcmp(oid, FRIENDLY_NAME_OID) == 0 && bag->element[i].friendly_name == NULL) {
+ result =
+ _gnutls_x509_decode_string
+ (ASN1_ETYPE_BMP_STRING,
+ attr_val.data, attr_val.size,
+ &t, 1);
+
+ _gnutls_free_datum(&attr_val);
+ if (result < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Error decoding PKCS12 Bag Attribute OID '%s'\n",
+ oid);
+ continue;
+ }
+
+ gnutls_free(bag->element[i].friendly_name);
+ bag->element[i].friendly_name = (char *) t.data;
+ } else {
+ _gnutls_free_datum(&attr_val);
+ _gnutls_debug_log
+ ("Unknown PKCS12 Bag Attribute OID '%s'\n",
+ oid);
+ }
+ }
+
+
+ bag->element[i].type = bag_type;
+
+ }
+
+ result = 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+
+static int
+_parse_safe_contents(asn1_node sc, const char *sc_name,
+ gnutls_pkcs12_bag_t bag)
+{
+ gnutls_datum_t content = { NULL, 0 };
+ int result;
+
+ /* Step 1. Extract the content.
+ */
+
+ result =
+ _gnutls_x509_read_string(sc, sc_name, &content,
+ ASN1_ETYPE_OCTET_STRING, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _pkcs12_decode_safe_contents(&content, bag);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&content);
+
+ return 0;
+
+ cleanup:
+ _gnutls_free_datum(&content);
+ return result;
+}
+
+
+/**
+ * gnutls_pkcs12_get_bag:
+ * @pkcs12: A pkcs12 type
+ * @indx: contains the index of the bag to extract
+ * @bag: An initialized bag, where the contents of the bag will be copied
+ *
+ * This function will return a Bag from the PKCS12 structure.
+ *
+ * After the last Bag has been read
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs12_get_bag(gnutls_pkcs12_t pkcs12,
+ int indx, gnutls_pkcs12_bag_t bag)
+{
+ asn1_node c2 = NULL;
+ int result, len;
+ char root2[MAX_NAME_SIZE];
+ char oid[MAX_OID_SIZE];
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Step 1. decode the data.
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, &c2, NULL);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 2. Parse the AuthenticatedSafe
+ */
+
+ snprintf(root2, sizeof(root2), "?%d.contentType", indx + 1);
+
+ len = sizeof(oid) - 1;
+ result = asn1_read_value(c2, root2, oid, &len);
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Not encrypted Bag
+ */
+
+ snprintf(root2, sizeof(root2), "?%d.content", indx + 1);
+
+ if (strcmp(oid, DATA_OID) == 0) {
+ result = _parse_safe_contents(c2, root2, bag);
+ goto cleanup;
+ }
+
+ /* ENC_DATA_OID needs decryption */
+
+ result = _gnutls_x509_read_value(c2, root2, &bag->element[0].data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bag->element[0].type = GNUTLS_BAG_ENCRYPTED;
+ bag->bag_elements = 1;
+
+ result = 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+}
+
+/* Creates an empty PFX structure for the PKCS12 structure.
+ */
+static int create_empty_pfx(asn1_node pkcs12)
+{
+ uint8_t three = 3;
+ int result;
+ asn1_node c2 = NULL;
+
+ /* Use version 3
+ */
+ result = asn1_write_value(pkcs12, "version", &three, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Write the content type of the data
+ */
+ result =
+ asn1_write_value(pkcs12, "authSafe.contentType", DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Check if the authenticatedSafe content is empty, and encode a
+ * null one in that case.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-AuthenticatedSafe",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_der_encode_and_copy(c2, "", pkcs12,
+ "authSafe.content", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/**
+ * gnutls_pkcs12_set_bag:
+ * @pkcs12: should contain a gnutls_pkcs12_t type
+ * @bag: An initialized bag
+ *
+ * This function will insert a Bag into the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_set_bag(gnutls_pkcs12_t pkcs12, gnutls_pkcs12_bag_t bag)
+{
+ asn1_node c2 = NULL;
+ asn1_node safe_cont = NULL;
+ int result;
+ int enc = 0, dum = 1;
+ char null;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Step 1. Check if the pkcs12 structure is empty. In that
+ * case generate an empty PFX.
+ */
+ result =
+ asn1_read_value(pkcs12->pkcs12, "authSafe.content", &null,
+ &dum);
+ if (result == ASN1_VALUE_NOT_FOUND) {
+ result = create_empty_pfx(pkcs12->pkcs12);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* Step 2. decode the authenticatedSafe.
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, &c2, NULL);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 3. Encode the bag elements into a SafeContents
+ * structure.
+ */
+ result = _pkcs12_encode_safe_contents(bag, &safe_cont, &enc);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 4. Insert the encoded SafeContents into the AuthenticatedSafe
+ * structure.
+ */
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (enc)
+ result =
+ asn1_write_value(c2, "?LAST.contentType", ENC_DATA_OID,
+ 1);
+ else
+ result =
+ asn1_write_value(c2, "?LAST.contentType", DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (enc) {
+ /* Encrypted packets are written directly.
+ */
+ result =
+ asn1_write_value(c2, "?LAST.content",
+ bag->element[0].data.data,
+ bag->element[0].data.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ } else {
+ result =
+ _gnutls_x509_der_encode_and_copy(safe_cont, "", c2,
+ "?LAST.content", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ asn1_delete_structure(&safe_cont);
+
+
+ /* Step 5. Re-encode and copy the AuthenticatedSafe into the pkcs12
+ * structure.
+ */
+ result =
+ _gnutls_x509_der_encode_and_copy(c2, "", pkcs12->pkcs12,
+ "authSafe.content", 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ asn1_delete_structure(&safe_cont);
+ return result;
+}
+
+#if ENABLE_GOST
+/*
+ * Russian differs from PKCS#12 here. It described proprietary way
+ * to obtain MAC key instead of using standard mechanism.
+ *
+ * See https://wwwold.tc26.ru/standard/rs/%D0%A0%2050.1.112-2016.pdf
+ * section 5.
+ */
+static int
+_gnutls_pkcs12_gost_string_to_key(gnutls_mac_algorithm_t algo,
+ const uint8_t * salt,
+ unsigned int salt_size, unsigned int iter,
+ const char *pass, unsigned int req_keylen,
+ uint8_t * keybuf)
+{
+ uint8_t temp[96];
+ size_t temp_len = sizeof(temp);
+ gnutls_datum_t key;
+ gnutls_datum_t _salt;
+ int ret;
+
+ if (iter == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ key.data = (void *)pass;
+ key.size = pass ? strlen(pass) : 0;
+
+ _salt.data = (void *)salt;
+ _salt.size = salt_size;
+
+ ret = gnutls_pbkdf2(algo, &key, &_salt, iter, temp, temp_len);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ memcpy(keybuf, temp + temp_len - req_keylen, req_keylen);
+
+ return 0;
+}
+#endif
+
+/**
+ * gnutls_pkcs12_generate_mac2:
+ * @pkcs12: A pkcs12 type
+ * @mac: the MAC algorithm to use
+ * @pass: The password for the MAC
+ *
+ * This function will generate a MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_generate_mac2(gnutls_pkcs12_t pkcs12, gnutls_mac_algorithm_t mac, const char *pass)
+{
+ uint8_t salt[8], key[MAX_HASH_SIZE];
+ int result;
+ const int iter = PKCS12_ITER_COUNT;
+ mac_hd_st td1;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned mac_size, key_len;
+ uint8_t mac_out[MAX_HASH_SIZE];
+ const mac_entry_st *me = mac_to_entry(mac);
+
+ if (pkcs12 == NULL || me == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (me->oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ mac_size = _gnutls_mac_get_algo_len(me);
+ key_len = mac_size;
+
+ /* Generate the salt.
+ */
+ result = gnutls_rnd(GNUTLS_RND_NONCE, salt, sizeof(salt));
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Write the salt into the structure.
+ */
+ result =
+ asn1_write_value(pkcs12->pkcs12, "macData.macSalt", salt,
+ sizeof(salt));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* write the iterations
+ */
+
+ if (iter > 1) {
+ result =
+ _gnutls_x509_write_uint32(pkcs12->pkcs12,
+ "macData.iterations", iter);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* Generate the key.
+ */
+#if ENABLE_GOST
+ if (me->id == GNUTLS_MAC_GOSTR_94 ||
+ me->id == GNUTLS_MAC_STREEBOG_256 ||
+ me->id == GNUTLS_MAC_STREEBOG_512) {
+ key_len = 32;
+ result = _gnutls_pkcs12_gost_string_to_key(me->id,
+ salt,
+ sizeof(salt),
+ iter,
+ pass,
+ key_len,
+ key);
+ } else
+#endif
+ result = _gnutls_pkcs12_string_to_key(me, 3 /*MAC*/,
+ salt, sizeof(salt),
+ iter, pass,
+ mac_size, key);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Get the data to be MACed
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* MAC the data
+ */
+ result = _gnutls_mac_init(&td1, me,
+ key, key_len);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_mac(&td1, tmp.data, tmp.size);
+ _gnutls_free_datum(&tmp);
+
+ _gnutls_mac_deinit(&td1, mac_out);
+
+
+ result =
+ asn1_write_value(pkcs12->pkcs12, "macData.mac.digest", mac_out,
+ mac_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs12->pkcs12,
+ "macData.mac.digestAlgorithm.parameters",
+ NULL, 0);
+ if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs12->pkcs12,
+ "macData.mac.digestAlgorithm.algorithm",
+ me->oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ return 0;
+
+ cleanup:
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ _gnutls_free_datum(&tmp);
+ return result;
+}
+
+/**
+ * gnutls_pkcs12_generate_mac:
+ * @pkcs12: A pkcs12 type
+ * @pass: The password for the MAC
+ *
+ * This function will generate a MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_generate_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+{
+ return gnutls_pkcs12_generate_mac2(pkcs12, GNUTLS_MAC_SHA256, pass);
+}
+
+/**
+ * gnutls_pkcs12_verify_mac:
+ * @pkcs12: should contain a gnutls_pkcs12_t type
+ * @pass: The password for the MAC
+ *
+ * This function will verify the MAC for the PKCS12 structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_verify_mac(gnutls_pkcs12_t pkcs12, const char *pass)
+{
+ uint8_t key[MAX_HASH_SIZE];
+ char oid[MAX_OID_SIZE];
+ int result;
+ unsigned int iter;
+ int len;
+ mac_hd_st td1;
+ gnutls_datum_t tmp = { NULL, 0 }, salt = {
+ NULL, 0};
+ uint8_t mac_output[MAX_HASH_SIZE];
+ uint8_t mac_output_orig[MAX_HASH_SIZE];
+ gnutls_mac_algorithm_t algo;
+ unsigned mac_len, key_len;
+ const mac_entry_st *entry;
+#if ENABLE_GOST
+ int gost_retry = 0;
+#endif
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* read the iterations
+ */
+ result =
+ _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations",
+ &iter);
+ if (result < 0) {
+ iter = 1; /* the default */
+ }
+
+ len = sizeof(oid);
+ result =
+ asn1_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm",
+ oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ algo = DIG_TO_MAC(gnutls_oid_to_digest(oid));
+ if (algo == GNUTLS_MAC_UNKNOWN) {
+ unknown_mac:
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+ }
+
+ entry = mac_to_entry(algo);
+ if (entry == NULL)
+ goto unknown_mac;
+
+ mac_len = _gnutls_mac_get_algo_len(entry);
+ key_len = mac_len;
+
+ /* Read the salt from the structure.
+ */
+ result =
+ _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt",
+ &salt);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Generate the key.
+ */
+ result = _gnutls_pkcs12_string_to_key(entry, 3 /*MAC*/,
+ salt.data, salt.size,
+ iter, pass,
+ key_len, key);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Get the data to be MACed
+ */
+ result = _decode_pkcs12_auth_safe(pkcs12->pkcs12, NULL, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+#if ENABLE_GOST
+ /* GOST PKCS#12 files use either PKCS#12 scheme or proprietary
+ * HMAC-based scheme to generate MAC key. */
+pkcs12_try_gost:
+#endif
+
+ /* MAC the data
+ */
+ result = _gnutls_mac_init(&td1, entry, key, key_len);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_mac(&td1, tmp.data, tmp.size);
+
+ _gnutls_mac_deinit(&td1, mac_output);
+
+ len = sizeof(mac_output_orig);
+ result =
+ asn1_read_value(pkcs12->pkcs12, "macData.mac.digest",
+ mac_output_orig, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((unsigned)len != mac_len ||
+ memcmp(mac_output_orig, mac_output, len) != 0) {
+
+#if ENABLE_GOST
+ /* It is possible that GOST files use proprietary
+ * key generation scheme */
+ if (!gost_retry &&
+ (algo == GNUTLS_MAC_GOSTR_94 ||
+ algo == GNUTLS_MAC_STREEBOG_256 ||
+ algo == GNUTLS_MAC_STREEBOG_512)) {
+ gost_retry = 1;
+ key_len = 32;
+ result = _gnutls_pkcs12_gost_string_to_key(algo,
+ salt.data,
+ salt.size,
+ iter,
+ pass,
+ key_len,
+ key);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ goto pkcs12_try_gost;
+ }
+#endif
+
+ gnutls_assert();
+ result = GNUTLS_E_MAC_VERIFY_FAILED;
+ goto cleanup;
+ }
+
+ /* _gnutls_pkcs12_string_to_key is not a FIPS approved operation */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ result = 0;
+ cleanup:
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ _gnutls_free_datum(&tmp);
+ _gnutls_free_datum(&salt);
+ return result;
+}
+
+
+static int
+write_attributes(gnutls_pkcs12_bag_t bag, int elem,
+ asn1_node c2, const char *where)
+{
+ int result;
+ char root[128];
+
+ /* If the bag attributes are empty, then write
+ * nothing to the attribute field.
+ */
+ if (bag->element[elem].friendly_name == NULL &&
+ bag->element[elem].local_key_id.data == NULL) {
+ /* no attributes
+ */
+ result = asn1_write_value(c2, where, NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+ }
+
+ if (bag->element[elem].local_key_id.data != NULL) {
+
+ /* Add a new Attribute
+ */
+ result = asn1_write_value(c2, where, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ _gnutls_str_cpy(root, sizeof(root), where);
+ _gnutls_str_cat(root, sizeof(root), ".?LAST");
+
+ result =
+ _gnutls_x509_encode_and_write_attribute(KEY_ID_OID, c2,
+ root,
+ bag->element
+ [elem].
+ local_key_id.data,
+ bag->element
+ [elem].
+ local_key_id.size,
+ 1);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ if (bag->element[elem].friendly_name != NULL) {
+ uint8_t *name;
+ int size, i;
+ const char *p;
+
+ /* Add a new Attribute
+ */
+ result = asn1_write_value(c2, where, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* convert name to BMPString
+ */
+ size = strlen(bag->element[elem].friendly_name) * 2;
+ name = gnutls_malloc(size);
+
+ if (name == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ p = bag->element[elem].friendly_name;
+ for (i = 0; i < size; i += 2) {
+ name[i] = 0;
+ name[i + 1] = *p;
+ p++;
+ }
+
+ _gnutls_str_cpy(root, sizeof(root), where);
+ _gnutls_str_cat(root, sizeof(root), ".?LAST");
+
+ result =
+ _gnutls_x509_encode_and_write_attribute
+ (FRIENDLY_NAME_OID, c2, root, name, size, 1);
+
+ gnutls_free(name);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Encodes the bag into a SafeContents structure, and puts the output in
+ * the given datum. Enc is set to non-zero if the data are encrypted;
+ */
+int
+_pkcs12_encode_safe_contents(gnutls_pkcs12_bag_t bag, asn1_node * contents,
+ int *enc)
+{
+ asn1_node c2 = NULL;
+ int result;
+ unsigned i;
+ const char *oid;
+
+ if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED && enc) {
+ *enc = 1;
+ return 0; /* ENCRYPTED BAG, do nothing. */
+ } else if (enc)
+ *enc = 0;
+
+ /* Step 1. Create the SEQUENCE.
+ */
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-12-SafeContents",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ for (i = 0; i < bag->bag_elements; i++) {
+
+ oid = bag_to_oid(bag->element[i].type);
+ if (oid == NULL) {
+ gnutls_assert();
+ continue;
+ }
+
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Copy the bag type.
+ */
+ result = asn1_write_value(c2, "?LAST.bagId", oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Set empty attributes
+ */
+ result =
+ write_attributes(bag, i, c2, "?LAST.bagAttributes");
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+
+ /* Copy the Bag Value
+ */
+
+ if (bag->element[i].type == GNUTLS_BAG_CERTIFICATE ||
+ bag->element[i].type == GNUTLS_BAG_SECRET ||
+ bag->element[i].type == GNUTLS_BAG_CRL) {
+ gnutls_datum_t tmp;
+
+ /* in that case encode it to a CertBag or
+ * a CrlBag.
+ */
+
+ result =
+ _pkcs12_encode_crt_bag(bag->element[i].type,
+ &bag->element[i].data,
+ &tmp);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_write_value(c2, "?LAST.bagValue",
+ &tmp);
+
+ _gnutls_free_datum(&tmp);
+
+ } else {
+
+ result =
+ _gnutls_x509_write_value(c2, "?LAST.bagValue",
+ &bag->element[i].
+ data);
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ }
+
+ /* Encode the data and copy them into the datum
+ */
+ *contents = c2;
+
+ return 0;
+
+ cleanup:
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/* Checks if the extra_certs contain certificates that may form a chain
+ * with the first certificate in chain (it is expected that chain_len==1)
+ * and appends those in the chain.
+ */
+static int make_chain(gnutls_x509_crt_t ** chain, unsigned int *chain_len,
+ gnutls_x509_crt_t ** extra_certs,
+ unsigned int *extra_certs_len, unsigned int flags)
+{
+ unsigned int i;
+
+ if (*chain_len != 1)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ i = 0;
+ while (i < *extra_certs_len) {
+ /* if it is an issuer but not a self-signed one */
+ if (gnutls_x509_crt_check_issuer
+ ((*chain)[*chain_len - 1], (*extra_certs)[i]) != 0) {
+ if (!(flags & GNUTLS_PKCS12_SP_INCLUDE_SELF_SIGNED)
+ &&
+ gnutls_x509_crt_check_issuer((*extra_certs)[i],
+ (*extra_certs)[i])
+ != 0)
+ goto skip;
+
+ if (unlikely(INT_ADD_OVERFLOW(*chain_len, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ *chain = _gnutls_reallocarray_fast(*chain,
+ ++(*chain_len),
+ sizeof((*chain)[0]));
+ if (*chain == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ (*chain)[*chain_len - 1] = (*extra_certs)[i];
+
+ (*extra_certs)[i] =
+ (*extra_certs)[*extra_certs_len - 1];
+ (*extra_certs_len)--;
+
+ i = 0;
+ continue;
+ }
+
+ skip:
+ i++;
+ }
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_simple_parse:
+ * @p12: A pkcs12 type
+ * @password: optional password used to decrypt the structure, bags and keys.
+ * @key: a structure to store the parsed private key.
+ * @chain: the corresponding to key certificate chain (may be %NULL)
+ * @chain_len: will be updated with the number of additional (may be %NULL)
+ * @extra_certs: optional pointer to receive an array of additional
+ * certificates found in the PKCS12 structure (may be %NULL).
+ * @extra_certs_len: will be updated with the number of additional
+ * certs (may be %NULL).
+ * @crl: an optional structure to store the parsed CRL (may be %NULL).
+ * @flags: should be zero or one of GNUTLS_PKCS12_SP_*
+ *
+ * This function parses a PKCS12 structure in @pkcs12 and extracts the
+ * private key, the corresponding certificate chain, any additional
+ * certificates and a CRL. The structures in @key, @chain @crl, and @extra_certs
+ * must not be initialized.
+ *
+ * The @extra_certs and @extra_certs_len parameters are optional
+ * and both may be set to %NULL. If either is non-%NULL, then both must
+ * be set. The value for @extra_certs is allocated
+ * using gnutls_malloc().
+ *
+ * Encrypted PKCS12 bags and PKCS8 private keys are supported, but
+ * only with password based security and the same password for all
+ * operations.
+ *
+ * Note that a PKCS12 structure may contain many keys and/or certificates,
+ * and there is no way to identify which key/certificate pair you want.
+ * For this reason this function is useful for PKCS12 files that contain
+ * only one key/certificate pair and/or one CRL.
+ *
+ * If the provided structure has encrypted fields but no password
+ * is provided then this function returns %GNUTLS_E_DECRYPTION_FAILED.
+ *
+ * Note that normally the chain constructed does not include self signed
+ * certificates, to comply with TLS' requirements. If, however, the flag
+ * %GNUTLS_PKCS12_SP_INCLUDE_SELF_SIGNED is specified then
+ * self signed certificates will be included in the chain.
+ *
+ * Prior to using this function the PKCS #12 structure integrity must
+ * be verified using gnutls_pkcs12_verify_mac().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.0
+ **/
+int
+gnutls_pkcs12_simple_parse(gnutls_pkcs12_t p12,
+ const char *password,
+ gnutls_x509_privkey_t * key,
+ gnutls_x509_crt_t ** chain,
+ unsigned int *chain_len,
+ gnutls_x509_crt_t ** extra_certs,
+ unsigned int *extra_certs_len,
+ gnutls_x509_crl_t * crl, unsigned int flags)
+{
+ gnutls_pkcs12_bag_t bag = NULL;
+ gnutls_x509_crt_t *_extra_certs = NULL;
+ unsigned int _extra_certs_len = 0;
+ gnutls_x509_crt_t *_chain = NULL;
+ unsigned int _chain_len = 0;
+ int idx = 0;
+ int ret;
+ size_t cert_id_size = 0;
+ size_t key_id_size = 0;
+ uint8_t cert_id[20];
+ uint8_t key_id[20];
+ int privkey_ok = 0;
+ unsigned int i;
+ int elements_in_bag;
+
+ *key = NULL;
+
+ if (crl)
+ *crl = NULL;
+
+ /* find the first private key */
+ for (;;) {
+
+ ret = gnutls_pkcs12_bag_init(&bag);
+ if (ret < 0) {
+ bag = NULL;
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag(p12, idx, bag);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ break;
+ }
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_type(bag, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (ret == GNUTLS_BAG_ENCRYPTED) {
+ if (password == NULL) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_DECRYPTION_FAILED);
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_decrypt(bag, password);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ }
+
+ elements_in_bag = gnutls_pkcs12_bag_get_count(bag);
+ if (elements_in_bag < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ for (i = 0; i < (unsigned)elements_in_bag; i++) {
+ int type;
+ gnutls_datum_t data;
+
+ type = gnutls_pkcs12_bag_get_type(bag, i);
+ if (type < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_data(bag, i, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ switch (type) {
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ if (password == NULL) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_DECRYPTION_FAILED);
+ goto done;
+ }
+
+ FALLTHROUGH;
+ case GNUTLS_BAG_PKCS8_KEY:
+ if (*key != NULL) { /* too simple to continue */
+ gnutls_assert();
+ break;
+ }
+
+ ret = gnutls_x509_privkey_init(key);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_x509_privkey_import_pkcs8
+ (*key, &data, GNUTLS_X509_FMT_DER,
+ password,
+ type ==
+ GNUTLS_BAG_PKCS8_KEY ?
+ GNUTLS_PKCS_PLAIN : 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ key_id_size = sizeof(key_id);
+ ret =
+ gnutls_x509_privkey_get_key_id(*key, 0,
+ key_id,
+ &key_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ privkey_ok = 1; /* break */
+ break;
+ default:
+ break;
+ }
+ }
+
+ idx++;
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+
+ if (privkey_ok != 0) /* private key was found */
+ break;
+ }
+
+ if (privkey_ok == 0) { /* no private key */
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* now find the corresponding certificate
+ */
+ idx = 0;
+ bag = NULL;
+ for (;;) {
+ ret = gnutls_pkcs12_bag_init(&bag);
+ if (ret < 0) {
+ bag = NULL;
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_get_bag(p12, idx, bag);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ break;
+ }
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_type(bag, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ if (ret == GNUTLS_BAG_ENCRYPTED) {
+ ret = gnutls_pkcs12_bag_decrypt(bag, password);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ }
+
+ elements_in_bag = gnutls_pkcs12_bag_get_count(bag);
+ if (elements_in_bag < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ for (i = 0; i < (unsigned)elements_in_bag; i++) {
+ int type;
+ gnutls_datum_t data;
+ gnutls_x509_crt_t this_cert;
+
+ type = gnutls_pkcs12_bag_get_type(bag, i);
+ if (type < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret = gnutls_pkcs12_bag_get_data(bag, i, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ switch (type) {
+ case GNUTLS_BAG_CERTIFICATE:
+ ret = gnutls_x509_crt_init(&this_cert);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret =
+ gnutls_x509_crt_import(this_cert,
+ &data,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crt_deinit(this_cert);
+ this_cert = NULL;
+ goto done;
+ }
+
+ /* check if the key id match */
+ cert_id_size = sizeof(cert_id);
+ ret =
+ gnutls_x509_crt_get_key_id(this_cert,
+ 0, cert_id,
+ &cert_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crt_deinit(this_cert);
+ this_cert = NULL;
+ goto done;
+ }
+
+ if (memcmp(cert_id, key_id, cert_id_size) != 0) { /* they don't match - skip the certificate */
+ if (unlikely(INT_ADD_OVERFLOW(_extra_certs_len, 1))) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto done;
+ }
+
+ _extra_certs =
+ _gnutls_reallocarray_fast(_extra_certs,
+ ++_extra_certs_len,
+ sizeof(_extra_certs[0]));
+ if (!_extra_certs) {
+ gnutls_assert();
+ ret =
+ GNUTLS_E_MEMORY_ERROR;
+ goto done;
+ }
+ _extra_certs
+ [_extra_certs_len -
+ 1] = this_cert;
+ this_cert = NULL;
+ } else {
+ if (chain && _chain_len == 0) {
+ _chain =
+ gnutls_malloc(sizeof
+ (_chain
+ [0]) *
+ (++_chain_len));
+ if (!_chain) {
+ gnutls_assert();
+ ret =
+ GNUTLS_E_MEMORY_ERROR;
+ goto done;
+ }
+ _chain[_chain_len - 1] =
+ this_cert;
+ this_cert = NULL;
+ } else {
+ gnutls_x509_crt_deinit
+ (this_cert);
+ this_cert = NULL;
+ }
+ }
+ break;
+
+ case GNUTLS_BAG_CRL:
+ if (crl == NULL || *crl != NULL) {
+ gnutls_assert();
+ break;
+ }
+
+ ret = gnutls_x509_crl_init(crl);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+
+ ret =
+ gnutls_x509_crl_import(*crl, &data,
+ GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_x509_crl_deinit(*crl);
+ *crl = NULL;
+ goto done;
+ }
+ break;
+
+ case GNUTLS_BAG_ENCRYPTED:
+ /* XXX Bother to recurse one level down? Unlikely to
+ use the same password anyway. */
+ case GNUTLS_BAG_EMPTY:
+ default:
+ break;
+ }
+ }
+
+ idx++;
+ gnutls_pkcs12_bag_deinit(bag);
+ bag = NULL;
+ }
+
+ if (chain != NULL) {
+ if (_chain_len != 1) {
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto done;
+ }
+
+ ret =
+ make_chain(&_chain, &_chain_len, &_extra_certs,
+ &_extra_certs_len, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto done;
+ }
+ }
+
+ ret = 0;
+
+ done:
+ if (bag)
+ gnutls_pkcs12_bag_deinit(bag);
+
+ if (ret < 0) {
+ if (*key) {
+ gnutls_x509_privkey_deinit(*key);
+ *key = NULL;
+ }
+ if (crl != NULL && *crl != NULL) {
+ gnutls_x509_crl_deinit(*crl);
+ *crl = NULL;
+ }
+ if (_extra_certs_len && _extra_certs != NULL) {
+ for (i = 0; i < _extra_certs_len; i++)
+ gnutls_x509_crt_deinit(_extra_certs[i]);
+ gnutls_free(_extra_certs);
+ }
+ if (_chain_len && _chain != NULL) {
+ for (i = 0; i < _chain_len; i++)
+ gnutls_x509_crt_deinit(_chain[i]);
+ gnutls_free(_chain);
+ }
+
+ return ret;
+ }
+
+ if (extra_certs && _extra_certs_len > 0) {
+ *extra_certs = _extra_certs;
+ *extra_certs_len = _extra_certs_len;
+ } else {
+ if (extra_certs) {
+ *extra_certs = NULL;
+ *extra_certs_len = 0;
+ }
+ for (i = 0; i < _extra_certs_len; i++)
+ gnutls_x509_crt_deinit(_extra_certs[i]);
+ gnutls_free(_extra_certs);
+ }
+
+ if (chain != NULL) {
+ *chain = _chain;
+ *chain_len = _chain_len;
+ }
+
+ return ret;
+}
+
+
+/**
+ * gnutls_pkcs12_mac_info:
+ * @pkcs12: A pkcs12 type
+ * @mac: the MAC algorithm used as %gnutls_mac_algorithm_t
+ * @salt: the salt used for string to key (if non-NULL then @salt_size initially holds its size)
+ * @salt_size: string to key salt size
+ * @iter_count: string to key iteration count
+ * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID
+ *
+ * This function will provide information on the MAC algorithm used
+ * in a PKCS #12 structure. If the structure algorithms
+ * are unknown the code %GNUTLS_E_UNKNOWN_HASH_ALGORITHM will be returned,
+ * and only @oid, will be set. That is, @oid will be set on structures
+ * with a MAC whether supported or not. It must be deinitialized using gnutls_free().
+ * The other variables are only set on supported structures.
+ *
+ * Returns: %GNUTLS_E_INVALID_REQUEST if the provided structure doesn't contain a MAC,
+ * %GNUTLS_E_UNKNOWN_HASH_ALGORITHM if the structure's MAC isn't supported, or
+ * another negative error code in case of a failure. Zero on success.
+ **/
+int
+gnutls_pkcs12_mac_info(gnutls_pkcs12_t pkcs12, unsigned int *mac,
+ void *salt, unsigned int *salt_size, unsigned int *iter_count, char **oid)
+{
+ int ret;
+ gnutls_datum_t tmp = { NULL, 0 }, dsalt = {
+ NULL, 0};
+ gnutls_mac_algorithm_t algo;
+
+ if (oid)
+ *oid = NULL;
+
+ if (pkcs12 == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_x509_read_value(pkcs12->pkcs12, "macData.mac.digestAlgorithm.algorithm",
+ &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (oid) {
+ *oid = (char*)tmp.data;
+ }
+
+ algo = DIG_TO_MAC(gnutls_oid_to_digest((char*)tmp.data));
+ if (algo == GNUTLS_MAC_UNKNOWN || mac_to_entry(algo) == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+ }
+
+ if (oid) {
+ tmp.data = NULL;
+ }
+
+ if (mac) {
+ *mac = algo;
+ }
+
+ if (iter_count) {
+ ret =
+ _gnutls_x509_read_uint(pkcs12->pkcs12, "macData.iterations",
+ iter_count);
+ if (ret < 0) {
+ *iter_count = 1; /* the default */
+ }
+ }
+
+ if (salt) {
+ /* Read the salt from the structure.
+ */
+ ret =
+ _gnutls_x509_read_null_value(pkcs12->pkcs12, "macData.macSalt",
+ &dsalt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (*salt_size >= (unsigned)dsalt.size) {
+ *salt_size = dsalt.size;
+ if (dsalt.size > 0)
+ memcpy(salt, dsalt.data, dsalt.size);
+ } else {
+ *salt_size = dsalt.size;
+ ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ _gnutls_free_datum(&dsalt);
+ return ret;
+
+}
+
diff --git a/lib/x509/pkcs12_bag.c b/lib/x509/pkcs12_bag.c
new file mode 100644
index 0000000..51a506e
--- /dev/null
+++ b/lib/x509/pkcs12_bag.c
@@ -0,0 +1,906 @@
+/*
+ * Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Red Hat
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate on PKCS12 Bag packet parsing.
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include "x509_int.h"
+#include "pkcs7_int.h"
+
+/**
+ * gnutls_pkcs12_bag_init:
+ * @bag: A pointer to the type to be initialized
+ *
+ * This function will initialize a PKCS12 bag structure. PKCS12 Bags
+ * usually contain private keys, lists of X.509 Certificates and X.509
+ * Certificate revocation lists.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs12_bag_init(gnutls_pkcs12_bag_t * bag)
+{
+ *bag = gnutls_calloc(1, sizeof(gnutls_pkcs12_bag_int));
+
+ if (*bag) {
+ return 0; /* success */
+ }
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+static inline void _pkcs12_bag_free_data(gnutls_pkcs12_bag_t bag)
+{
+ unsigned i;
+
+ for (i = 0; i < bag->bag_elements; i++) {
+ _gnutls_free_datum(&bag->element[i].data);
+ _gnutls_free_datum(&bag->element[i].local_key_id);
+ gnutls_free(bag->element[i].friendly_name);
+ bag->element[i].type = 0;
+ }
+
+}
+
+
+/**
+ * gnutls_pkcs12_bag_deinit:
+ * @bag: A pointer to the type to be initialized
+ *
+ * This function will deinitialize a PKCS12 Bag structure.
+ **/
+void gnutls_pkcs12_bag_deinit(gnutls_pkcs12_bag_t bag)
+{
+ if (!bag)
+ return;
+
+ _pkcs12_bag_free_data(bag);
+
+ gnutls_free(bag);
+}
+
+/**
+ * gnutls_pkcs12_bag_get_type:
+ * @bag: The bag
+ * @indx: The element of the bag to get the type
+ *
+ * This function will return the bag's type.
+ *
+ * Returns: On error a negative error value or one of the #gnutls_pkcs12_bag_type_t enumerations.
+ **/
+int
+gnutls_pkcs12_bag_get_type(gnutls_pkcs12_bag_t bag, unsigned indx)
+{
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (indx >= bag->bag_elements)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ return bag->element[indx].type;
+}
+
+/**
+ * gnutls_pkcs12_bag_get_count:
+ * @bag: The bag
+ *
+ * This function will return the number of the elements within the bag.
+ *
+ * Returns: Number of elements in bag, or an negative error code on
+ * error.
+ **/
+int gnutls_pkcs12_bag_get_count(gnutls_pkcs12_bag_t bag)
+{
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return bag->bag_elements;
+}
+
+/**
+ * gnutls_pkcs12_bag_get_data:
+ * @bag: The bag
+ * @indx: The element of the bag to get the data from
+ * @data: where the bag's data will be. Should be treated as constant.
+ *
+ * This function will return the bag's data. The data is a constant
+ * that is stored into the bag. Should not be accessed after the bag
+ * is deleted.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs12_bag_get_data(gnutls_pkcs12_bag_t bag, unsigned indx,
+ gnutls_datum_t * data)
+{
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (indx >= bag->bag_elements)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ data->data = bag->element[indx].data.data;
+ data->size = bag->element[indx].data.size;
+
+ return 0;
+}
+
+#define X509_CERT_OID "1.2.840.113549.1.9.22.1"
+#define X509_CRL_OID "1.2.840.113549.1.9.23.1"
+#define RANDOM_NONCE_OID "1.2.840.113549.1.9.25.3"
+
+int
+_pkcs12_decode_crt_bag(gnutls_pkcs12_bag_type_t type,
+ const gnutls_datum_t * in, gnutls_datum_t * out)
+{
+ int ret;
+ asn1_node c2 = NULL;
+
+ switch (type) {
+ case GNUTLS_BAG_CERTIFICATE:
+ if ((ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-CertBag",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_der_decoding(&c2, in->data, in->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_read_string(c2, "certValue", out,
+ ASN1_ETYPE_OCTET_STRING, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+
+ case GNUTLS_BAG_CRL:
+ if ((ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-CRLBag",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_der_decoding(&c2, in->data, in->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_read_string(c2, "crlValue", out,
+ ASN1_ETYPE_OCTET_STRING, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+
+ case GNUTLS_BAG_SECRET:
+ if ((ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-SecretBag",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_der_decoding(&c2, in->data, in->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_read_string(c2, "secretValue", out,
+ ASN1_ETYPE_OCTET_STRING, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+
+ default:
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ }
+
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+
+ cleanup:
+
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+
+int
+_pkcs12_encode_crt_bag(gnutls_pkcs12_bag_type_t type,
+ const gnutls_datum_t * raw, gnutls_datum_t * out)
+{
+ int ret;
+ asn1_node c2 = NULL;
+
+ switch (type) {
+ case GNUTLS_BAG_CERTIFICATE:
+ if ((ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-CertBag",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_write_value(c2, "certId", X509_CERT_OID, 1);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_string(c2, "certValue", raw,
+ ASN1_ETYPE_OCTET_STRING);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+
+ case GNUTLS_BAG_CRL:
+ if ((ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-CRLBag",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = asn1_write_value(c2, "crlId", X509_CRL_OID, 1);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_string(c2, "crlValue", raw,
+ ASN1_ETYPE_OCTET_STRING);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+
+ case GNUTLS_BAG_SECRET:
+ if ((ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-SecretBag",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ asn1_write_value(c2, "secretTypeId", RANDOM_NONCE_OID,
+ 1);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_write_string(c2, "secretValue", raw,
+ ASN1_ETYPE_OCTET_STRING);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+
+ default:
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", out, 0);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ asn1_delete_structure(&c2);
+
+ return 0;
+
+
+ cleanup:
+
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+
+/**
+ * gnutls_pkcs12_bag_set_data:
+ * @bag: The bag
+ * @type: The data's type
+ * @data: the data to be copied.
+ *
+ * This function will insert the given data of the given type into
+ * the bag.
+ *
+ * Returns: the index of the added bag on success, or a negative
+ * value on error.
+ **/
+int
+gnutls_pkcs12_bag_set_data(gnutls_pkcs12_bag_t bag,
+ gnutls_pkcs12_bag_type_t type,
+ const gnutls_datum_t * data)
+{
+ int ret;
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (bag->bag_elements == MAX_BAG_ELEMENTS - 1) {
+ gnutls_assert();
+ /* bag is full */
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ if (bag->bag_elements == 1) {
+ /* A bag with a key or an encrypted bag, must have
+ * only one element.
+ */
+
+ if (bag->element[0].type == GNUTLS_BAG_PKCS8_KEY ||
+ bag->element[0].type == GNUTLS_BAG_PKCS8_ENCRYPTED_KEY
+ || bag->element[0].type == GNUTLS_BAG_ENCRYPTED) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+ }
+
+ ret =
+ _gnutls_set_datum(&bag->element[bag->bag_elements].data,
+ data->data, data->size);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ bag->element[bag->bag_elements].type = type;
+
+ bag->bag_elements++;
+
+ return bag->bag_elements - 1;
+}
+
+/**
+ * gnutls_pkcs12_bag_set_crt:
+ * @bag: The bag
+ * @crt: the certificate to be copied.
+ *
+ * This function will insert the given certificate into the
+ * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data().
+ *
+ * Returns: the index of the added bag on success, or a negative
+ * value on failure.
+ **/
+int
+gnutls_pkcs12_bag_set_crt(gnutls_pkcs12_bag_t bag, gnutls_x509_crt_t crt)
+{
+ int ret;
+ gnutls_datum_t data;
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_der_encode(crt->cert, "", &data, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_CERTIFICATE, &data);
+
+ _gnutls_free_datum(&data);
+
+ return ret;
+}
+
+/**
+ * gnutls_pkcs12_bag_set_crl:
+ * @bag: The bag
+ * @crl: the CRL to be copied.
+ *
+ * This function will insert the given CRL into the
+ * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data().
+ *
+ * Returns: the index of the added bag on success, or a negative error code
+ * on failure.
+ **/
+int
+gnutls_pkcs12_bag_set_crl(gnutls_pkcs12_bag_t bag, gnutls_x509_crl_t crl)
+{
+ int ret;
+ gnutls_datum_t data;
+
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_der_encode(crl->crl, "", &data, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_CRL, &data);
+
+ _gnutls_free_datum(&data);
+
+ return ret;
+}
+
+/**
+ * gnutls_pkcs12_bag_set_key_id:
+ * @bag: The bag
+ * @indx: The bag's element to add the id
+ * @id: the ID
+ *
+ * This function will add the given key ID, to the specified, by the
+ * index, bag element. The key ID will be encoded as a 'Local key
+ * identifier' bag attribute, which is usually used to distinguish
+ * the local private key and the certificate pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. or a negative error code on error.
+ **/
+int
+gnutls_pkcs12_bag_set_key_id(gnutls_pkcs12_bag_t bag, unsigned indx,
+ const gnutls_datum_t * id)
+{
+ int ret;
+
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (indx > bag->bag_elements - 1) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_set_datum(&bag->element[indx].local_key_id,
+ id->data, id->size);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_bag_get_key_id:
+ * @bag: The bag
+ * @indx: The bag's element to add the id
+ * @id: where the ID will be copied (to be treated as const)
+ *
+ * This function will return the key ID, of the specified bag element.
+ * The key ID is usually used to distinguish the local private key and
+ * the certificate pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. or a negative error code on error.
+ **/
+int
+gnutls_pkcs12_bag_get_key_id(gnutls_pkcs12_bag_t bag, unsigned indx,
+ gnutls_datum_t * id)
+{
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (indx > bag->bag_elements - 1) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ id->data = bag->element[indx].local_key_id.data;
+ id->size = bag->element[indx].local_key_id.size;
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_bag_get_friendly_name:
+ * @bag: The bag
+ * @indx: The bag's element to add the id
+ * @name: will hold a pointer to the name (to be treated as const)
+ *
+ * This function will return the friendly name, of the specified bag
+ * element. The key ID is usually used to distinguish the local
+ * private key and the certificate pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. or a negative error code on error.
+ **/
+int
+gnutls_pkcs12_bag_get_friendly_name(gnutls_pkcs12_bag_t bag, unsigned indx,
+ char **name)
+{
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (indx > bag->bag_elements - 1) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ *name = bag->element[indx].friendly_name;
+
+ return 0;
+}
+
+
+/**
+ * gnutls_pkcs12_bag_set_friendly_name:
+ * @bag: The bag
+ * @indx: The bag's element to add the id
+ * @name: the name
+ *
+ * This function will add the given key friendly name, to the
+ * specified, by the index, bag element. The name will be encoded as
+ * a 'Friendly name' bag attribute, which is usually used to set a
+ * user name to the local private key and the certificate pair.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. or a negative error code on error.
+ **/
+int
+gnutls_pkcs12_bag_set_friendly_name(gnutls_pkcs12_bag_t bag, unsigned indx,
+ const char *name)
+{
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (indx > bag->bag_elements - 1) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ bag->element[indx].friendly_name = gnutls_strdup(name);
+
+ if (name == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_pkcs12_bag_decrypt:
+ * @bag: The bag
+ * @pass: The password used for encryption, must be ASCII.
+ *
+ * This function will decrypt the given encrypted bag and return 0 on
+ * success.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned.
+ **/
+int gnutls_pkcs12_bag_decrypt(gnutls_pkcs12_bag_t bag, const char *pass)
+{
+ int ret;
+ gnutls_datum_t dec;
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (bag->element[0].type != GNUTLS_BAG_ENCRYPTED) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_pkcs7_decrypt_data(&bag->element[0].data, pass, &dec);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* decryption succeeded. Now decode the SafeContents
+ * stuff, and parse it.
+ */
+
+ _gnutls_free_datum(&bag->element[0].data);
+
+ ret = _pkcs12_decode_safe_contents(&dec, bag);
+
+ _gnutls_free_datum(&dec);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_bag_encrypt:
+ * @bag: The bag
+ * @pass: The password used for encryption, must be ASCII
+ * @flags: should be one of #gnutls_pkcs_encrypt_flags_t elements bitwise or'd
+ *
+ * This function will encrypt the given bag.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned.
+ **/
+int
+gnutls_pkcs12_bag_encrypt(gnutls_pkcs12_bag_t bag, const char *pass,
+ unsigned int flags)
+{
+ int ret;
+ asn1_node safe_cont = NULL;
+ gnutls_datum_t der = { NULL, 0 };
+ gnutls_datum_t enc = { NULL, 0 };
+ schema_id id;
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (bag->element[0].type == GNUTLS_BAG_ENCRYPTED) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Encode the whole bag to a safe contents
+ * structure.
+ */
+ ret = _pkcs12_encode_safe_contents(bag, &safe_cont, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* DER encode the SafeContents.
+ */
+ ret = _gnutls_x509_der_encode(safe_cont, "", &der, 0);
+
+ asn1_delete_structure(&safe_cont);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (flags & GNUTLS_PKCS_PLAIN) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ id = _gnutls_pkcs_flags_to_schema(flags);
+
+ /* Now encrypt them.
+ */
+ ret = _gnutls_pkcs7_encrypt_data(id, &der, pass, &enc);
+
+ _gnutls_free_datum(&der);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* encryption succeeded.
+ */
+
+ _pkcs12_bag_free_data(bag);
+
+ bag->element[0].type = GNUTLS_BAG_ENCRYPTED;
+ bag->element[0].data = enc;
+
+ bag->bag_elements = 1;
+
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_bag_enc_info:
+ * @bag: The bag
+ * @schema: indicate the schema as one of %gnutls_pkcs_encrypt_flags_t
+ * @cipher: the cipher used as %gnutls_cipher_algorithm_t
+ * @salt: PBKDF2 salt (if non-NULL then @salt_size initially holds its size)
+ * @salt_size: PBKDF2 salt size
+ * @iter_count: PBKDF2 iteration count
+ * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID
+ *
+ * This function will provide information on the encryption algorithms used
+ * in an encrypted bag. If the structure algorithms
+ * are unknown the code %GNUTLS_E_UNKNOWN_CIPHER_TYPE will be returned,
+ * and only @oid, will be set. That is, @oid will be set on encrypted bags
+ * whether supported or not. It must be deinitialized using gnutls_free().
+ * The other variables are only set on supported structures.
+ *
+ * Returns: %GNUTLS_E_INVALID_REQUEST if the provided bag isn't encrypted,
+ * %GNUTLS_E_UNKNOWN_CIPHER_TYPE if the structure's encryption isn't supported, or
+ * another negative error code in case of a failure. Zero on success.
+ **/
+int
+gnutls_pkcs12_bag_enc_info(gnutls_pkcs12_bag_t bag, unsigned int *schema, unsigned int *cipher,
+ void *salt, unsigned int *salt_size, unsigned int *iter_count, char **oid)
+{
+ int ret;
+ struct pbkdf2_params kdf;
+ const struct pkcs_cipher_schema_st *p;
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (bag->element[0].type != GNUTLS_BAG_ENCRYPTED) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_pkcs7_data_enc_info(&bag->element[0].data, &p, &kdf, oid);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (schema)
+ *schema = p->flag;
+
+ if (cipher)
+ *cipher = p->cipher;
+
+ if (iter_count)
+ *iter_count = kdf.iter_count;
+
+ if (salt) {
+ if (*salt_size >= (unsigned)kdf.salt_size) {
+ memcpy(salt, kdf.salt, kdf.salt_size);
+ } else {
+ *salt_size = kdf.salt_size;
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ }
+ }
+
+ if (salt_size)
+ *salt_size = kdf.salt_size;
+
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs12_bag_set_privkey:
+ * @bag: The bag
+ * @privkey: the private key to be copied.
+ * @password: the password to protect the key with (may be %NULL)
+ * @flags: should be one of #gnutls_pkcs_encrypt_flags_t elements bitwise or'd
+ *
+ * This function will insert the given private key into the
+ * bag. This is just a wrapper over gnutls_pkcs12_bag_set_data().
+ *
+ * Returns: the index of the added bag on success, or a negative
+ * value on failure.
+ **/
+int
+gnutls_pkcs12_bag_set_privkey(gnutls_pkcs12_bag_t bag, gnutls_x509_privkey_t privkey,
+ const char *password, unsigned flags)
+{
+ int ret;
+ gnutls_datum_t data = {NULL, 0};
+
+ if (bag == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_privkey_export2_pkcs8(privkey, GNUTLS_X509_FMT_DER,
+ password, flags, &data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (password == NULL) {
+ ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_PKCS8_KEY, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ ret = gnutls_pkcs12_bag_set_data(bag, GNUTLS_BAG_PKCS8_ENCRYPTED_KEY, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ cleanup:
+ _gnutls_free_datum(&data);
+
+ return ret;
+}
diff --git a/lib/x509/pkcs12_encr.c b/lib/x509/pkcs12_encr.c
new file mode 100644
index 0000000..55c46e4
--- /dev/null
+++ b/lib/x509/pkcs12_encr.c
@@ -0,0 +1,227 @@
+/* minip12.c - A mini pkcs-12 implementation (modified for gnutls)
+ *
+ * Copyright (C) 2002-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+
+#include <mpi.h>
+#include "errors.h"
+#include <x509_int.h>
+#include <algorithms.h>
+
+#define MAX_PASS_LEN 8192
+#define MAX_V_SIZE 128
+/* ID should be:
+ * 3 for MAC
+ * 2 for IV
+ * 1 for encryption key
+ *
+ * Note that this function produces different key for the
+ * NULL password, and for the password with zero length.
+ */
+int
+_gnutls_pkcs12_string_to_key(const mac_entry_st * me,
+ unsigned int id, const uint8_t * salt,
+ unsigned int salt_size, unsigned int iter,
+ const char *pw, unsigned int req_keylen,
+ uint8_t * keybuf)
+{
+ int rc;
+ unsigned int i, j;
+ digest_hd_st md;
+ bigint_t num_b1 = NULL, num_ij = NULL;
+ bigint_t v_mpi = NULL;
+ unsigned int pwlen;
+ uint8_t hash[MAX_HASH_SIZE], buf_b[MAX_V_SIZE], buf_i[MAX_PASS_LEN + MAX_V_SIZE], *p;
+ uint8_t d[MAX_V_SIZE];
+ size_t cur_keylen;
+ size_t n, m, plen, i_size;
+ size_t slen;
+ gnutls_datum_t ucs2 = {NULL, 0};
+ unsigned mac_len;
+ uint8_t v_val[MAX_V_SIZE+1];
+ unsigned v_size = 0;
+
+ switch (me->id) {
+ case GNUTLS_DIG_GOSTR_94:
+ v_size = 32;
+ break;
+ case GNUTLS_DIG_SHA1:
+ case GNUTLS_DIG_SHA224:
+ case GNUTLS_DIG_SHA256:
+ case GNUTLS_DIG_STREEBOG_256:
+ case GNUTLS_DIG_STREEBOG_512:
+ v_size = 64;
+ break;
+ case GNUTLS_DIG_SHA384:
+ case GNUTLS_DIG_SHA512:
+ v_size = 128;
+ break;
+ default:
+ break;
+ }
+
+ if (v_size == 0 || v_size > MAX_V_SIZE)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ memset(v_val, 0, sizeof(v_val));
+ v_val[0] = 0x01; /* make it be 2^64 or 2^128 */
+
+ cur_keylen = 0;
+
+ if (pw) {
+ pwlen = strlen(pw);
+
+ if (pwlen == 0) {
+ ucs2.data = gnutls_calloc(1, 2);
+ if (ucs2.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ ucs2.size = 2;
+ } else {
+ rc = _gnutls_utf8_to_ucs2(pw, pwlen, &ucs2, 1);
+ if (rc < 0)
+ return gnutls_assert_val(rc);
+
+ /* include terminating zero */
+ ucs2.size+=2;
+ }
+ pwlen = ucs2.size;
+ pw = (char*)ucs2.data;
+ } else {
+ pwlen = 0;
+ }
+
+ if (pwlen > MAX_PASS_LEN) {
+ rc = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ rc = _gnutls_mpi_init_scan(&v_mpi, v_val, v_size+1);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Store salt and password in BUF_I */
+ slen = ((salt_size+v_size-1)/v_size) * v_size;
+ plen = ((pwlen+v_size-1)/v_size) * v_size;
+ i_size = slen + plen;
+
+ if (i_size > sizeof(buf_i)) {
+ rc = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ p = buf_i;
+ for (i = 0; i < slen; i++)
+ *p++ = salt[i % salt_size];
+
+ if (pw) {
+ for (i = j = 0; i < plen; i += 2) {
+ *p++ = pw[j];
+ *p++ = pw[j+1];
+ j+=2;
+ if (j >= pwlen)
+ j = 0;
+ }
+ } else {
+ memset(p, 0, plen);
+ }
+
+ mac_len = _gnutls_mac_get_algo_len(me);
+ assert(mac_len != 0);
+
+ for (;;) {
+ rc = _gnutls_hash_init(&md, me);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ memset(d, id & 0xff, v_size);
+ _gnutls_hash(&md, d, v_size);
+ _gnutls_hash(&md, buf_i, i_size);
+ _gnutls_hash_deinit(&md, hash);
+ for (i = 1; i < iter; i++) {
+ rc = _gnutls_hash_fast((gnutls_digest_algorithm_t)me->id,
+ hash, mac_len, hash);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ for (i = 0; i < mac_len && cur_keylen < req_keylen; i++)
+ keybuf[cur_keylen++] = hash[i];
+ if (cur_keylen == req_keylen) {
+ rc = 0; /* ready */
+ goto cleanup;
+ }
+
+ /* need more bytes. */
+ for (i = 0; i < v_size; i++)
+ buf_b[i] = hash[i % mac_len];
+ n = v_size;
+ rc = _gnutls_mpi_init_scan(&num_b1, buf_b, n);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ rc = _gnutls_mpi_add_ui(num_b1, num_b1, 1);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ for (i = 0; i < i_size; i += v_size) {
+ n = v_size;
+ rc = _gnutls_mpi_init_scan(&num_ij, buf_i + i, n);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ rc = _gnutls_mpi_addm(num_ij, num_ij, num_b1, v_mpi);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ n = v_size;
+ m = (_gnutls_mpi_get_nbits(num_ij) + 7) / 8;
+
+ memset(buf_i + i, 0, n - m);
+ rc = _gnutls_mpi_print(num_ij, buf_i + i + n - m,
+ &n);
+ if (rc < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ _gnutls_mpi_release(&num_ij);
+ }
+ }
+ cleanup:
+ _gnutls_mpi_release(&num_ij);
+ _gnutls_mpi_release(&num_b1);
+ _gnutls_mpi_release(&v_mpi);
+ gnutls_free(ucs2.data);
+
+ return rc;
+}
diff --git a/lib/x509/pkcs7-attrs.c b/lib/x509/pkcs7-attrs.c
new file mode 100644
index 0000000..25f9472
--- /dev/null
+++ b/lib/x509/pkcs7-attrs.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate on PKCS7 attribute setting.
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509_b64.h>
+#include <gnutls/abstract.h>
+#include <gnutls/pkcs7.h>
+
+/**
+ * gnutls_pkcs7_add_attr:
+ * @list: A list of existing attributes or pointer to %NULL for the first one
+ * @oid: the OID of the attribute to be set
+ * @data: the raw (DER-encoded) data of the attribute to be set
+ * @flags: zero or %GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING
+ *
+ * This function will set a PKCS #7 attribute in the provided list.
+ * If this function fails, the previous list would be deallocated.
+ *
+ * Note that any attributes set with this function must either be
+ * DER or BER encoded, unless a special flag is present.
+ *
+ * Returns: On success, the new list head, otherwise %NULL.
+ *
+ * Since: 3.4.2
+ **/
+int
+gnutls_pkcs7_add_attr(gnutls_pkcs7_attrs_t * list, const char *oid,
+ gnutls_datum_t * data, unsigned flags)
+{
+ int ret;
+ gnutls_pkcs7_attrs_st *r;
+
+ r = gnutls_calloc(1, sizeof(gnutls_pkcs7_attrs_st));
+ if (r == NULL)
+ goto fail;
+
+ if (flags & GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING) {
+ ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING,
+ data->data, data->size,
+ &r->data);
+ } else {
+ ret = _gnutls_set_datum(&r->data, data->data, data->size);
+ }
+ if (ret < 0)
+ goto fail;
+
+ r->oid = gnutls_strdup(oid);
+ if (r->oid == NULL)
+ goto fail;
+
+ r->next = *list;
+ *list = r;
+
+ return 0;
+ fail:
+ if (r) {
+ gnutls_free(r->data.data);
+ gnutls_free(r);
+ }
+ gnutls_pkcs7_attrs_deinit(*list);
+ return GNUTLS_E_MEMORY_ERROR;
+
+}
+
+/**
+ * gnutls_pkcs7_get_attr:
+ * @list: A list of existing attributes or %NULL for the first one
+ * @idx: the index of the attribute to get
+ * @oid: the OID of the attribute (read-only)
+ * @data: the raw data of the attribute
+ * @flags: zero or %GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING
+ *
+ * This function will get a PKCS #7 attribute from the provided list.
+ * The OID is a constant string, but data will be allocated and must be
+ * deinitialized by the caller.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned
+ * if there are no data in the current index.
+ *
+ * Since: 3.4.2
+ **/
+int
+gnutls_pkcs7_get_attr(gnutls_pkcs7_attrs_t list, unsigned idx, char **oid,
+ gnutls_datum_t * data, unsigned flags)
+{
+ unsigned i;
+ gnutls_pkcs7_attrs_st *p = list;
+ int ret;
+
+ for (i = 0; i < idx; i++) {
+ p = p->next;
+ if (p == NULL)
+ break;
+ }
+
+ if (p == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ *oid = p->oid;
+
+ if (flags & GNUTLS_PKCS7_ATTR_ENCODE_OCTET_STRING) {
+ ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING,
+ p->data.data, p->data.size,
+ data, 1);
+ } else {
+ ret = _gnutls_set_datum(data, p->data.data, p->data.size);
+ }
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs7_attrs_deinit:
+ * @list: A list of existing attributes
+ *
+ * This function will clear a PKCS #7 attribute list.
+ *
+ * Since: 3.4.2
+ **/
+void gnutls_pkcs7_attrs_deinit(gnutls_pkcs7_attrs_t list)
+{
+ gnutls_pkcs7_attrs_st *r = list, *next;
+
+ while (r) {
+ next = r->next;
+
+ gnutls_free(r->data.data);
+ gnutls_free(r->oid);
+ gnutls_free(r);
+ r = next;
+ }
+}
diff --git a/lib/x509/pkcs7-crypt.c b/lib/x509/pkcs7-crypt.c
new file mode 100644
index 0000000..59eddcd
--- /dev/null
+++ b/lib/x509/pkcs7-crypt.c
@@ -0,0 +1,1793 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2014-2016 Red Hat
+ * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include "pkcs7_int.h"
+#include <algorithms.h>
+#include <num.h>
+#include <random.h>
+#include <pk.h>
+
+#define PBES1_DES_MD5_OID "1.2.840.113549.1.5.3"
+
+#define PBES2_OID "1.2.840.113549.1.5.13"
+#define PBKDF2_OID "1.2.840.113549.1.5.12"
+#define DES_EDE3_CBC_OID "1.2.840.113549.3.7"
+#define AES_128_CBC_OID "2.16.840.1.101.3.4.1.2"
+#define AES_192_CBC_OID "2.16.840.1.101.3.4.1.22"
+#define AES_256_CBC_OID "2.16.840.1.101.3.4.1.42"
+#define DES_CBC_OID "1.3.14.3.2.7"
+
+/* oid_pbeWithSHAAnd3_KeyTripleDES_CBC */
+#define PKCS12_PBE_3DES_SHA1_OID "1.2.840.113549.1.12.1.3"
+#define PKCS12_PBE_ARCFOUR_SHA1_OID "1.2.840.113549.1.12.1.1"
+#define PKCS12_PBE_RC2_40_SHA1_OID "1.2.840.113549.1.12.1.6"
+
+static const struct pkcs_cipher_schema_st avail_pkcs_cipher_schemas[] = {
+ {
+ .schema = PBES1_DES_MD5,
+ .name = "PBES1-DES-CBC-MD5",
+ .flag = GNUTLS_PKCS_PBES1_DES_MD5,
+ .cipher = GNUTLS_CIPHER_DES_CBC,
+ .pbes2 = 0,
+ .cipher_oid = PBES1_DES_MD5_OID,
+ .write_oid = PBES1_DES_MD5_OID,
+ .desc = NULL,
+ .iv_name = NULL,
+ .decrypt_only = 1},
+ {
+ .schema = PBES2_3DES,
+ .name = "PBES2-3DES-CBC",
+ .flag = GNUTLS_PKCS_PBES2_3DES,
+ .cipher = GNUTLS_CIPHER_3DES_CBC,
+ .pbes2 = 1,
+ .cipher_oid = DES_EDE3_CBC_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.pkcs-5-des-EDE3-CBC-params",
+ .iv_name = "",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_DES,
+ .name = "PBES2-DES-CBC",
+ .flag = GNUTLS_PKCS_PBES2_DES,
+ .cipher = GNUTLS_CIPHER_DES_CBC,
+ .pbes2 = 1,
+ .cipher_oid = DES_CBC_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.pkcs-5-des-CBC-params",
+ .iv_name = "",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_AES_128,
+ .name = "PBES2-AES128-CBC",
+ .flag = GNUTLS_PKCS_PBES2_AES_128,
+ .cipher = GNUTLS_CIPHER_AES_128_CBC,
+ .pbes2 = 1,
+ .cipher_oid = AES_128_CBC_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.pkcs-5-aes128-CBC-params",
+ .iv_name = "",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_AES_192,
+ .name = "PBES2-AES192-CBC",
+ .flag = GNUTLS_PKCS_PBES2_AES_192,
+ .cipher = GNUTLS_CIPHER_AES_192_CBC,
+ .pbes2 = 1,
+ .cipher_oid = AES_192_CBC_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.pkcs-5-aes192-CBC-params",
+ .iv_name = "",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_AES_256,
+ .name = "PBES2-AES256-CBC",
+ .flag = GNUTLS_PKCS_PBES2_AES_256,
+ .cipher = GNUTLS_CIPHER_AES_256_CBC,
+ .pbes2 = 1,
+ .cipher_oid = AES_256_CBC_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.pkcs-5-aes256-CBC-params",
+ .iv_name = "",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_GOST28147_89_TC26Z,
+ .name = "PBES2-GOST28147-89-TC26Z",
+ .flag = GNUTLS_PKCS_PBES2_GOST_TC26Z,
+ .cipher = GNUTLS_CIPHER_GOST28147_TC26Z_CFB,
+ .pbes2 = 1,
+ .cipher_oid = GOST28147_89_TC26Z_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.Gost28147-89-Parameters",
+ .iv_name = "iv",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_GOST28147_89_CPA,
+ .name = "PBES2-GOST28147-89-CPA",
+ .flag = GNUTLS_PKCS_PBES2_GOST_CPA,
+ .cipher = GNUTLS_CIPHER_GOST28147_CPA_CFB,
+ .pbes2 = 1,
+ .cipher_oid = GOST28147_89_CPA_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.Gost28147-89-Parameters",
+ .iv_name = "iv",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_GOST28147_89_CPB,
+ .name = "PBES2-GOST28147-89-CPB",
+ .flag = GNUTLS_PKCS_PBES2_GOST_CPB,
+ .cipher = GNUTLS_CIPHER_GOST28147_CPB_CFB,
+ .pbes2 = 1,
+ .cipher_oid = GOST28147_89_CPB_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.Gost28147-89-Parameters",
+ .iv_name = "iv",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_GOST28147_89_CPC,
+ .name = "PBES2-GOST28147-89-CPC",
+ .flag = GNUTLS_PKCS_PBES2_GOST_CPC,
+ .cipher = GNUTLS_CIPHER_GOST28147_CPC_CFB,
+ .pbes2 = 1,
+ .cipher_oid = GOST28147_89_CPC_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.Gost28147-89-Parameters",
+ .iv_name = "iv",
+ .decrypt_only = 0},
+ {
+ .schema = PBES2_GOST28147_89_CPD,
+ .name = "PBES2-GOST28147-89-CPD",
+ .flag = GNUTLS_PKCS_PBES2_GOST_CPD,
+ .cipher = GNUTLS_CIPHER_GOST28147_CPD_CFB,
+ .pbes2 = 1,
+ .cipher_oid = GOST28147_89_CPD_OID,
+ .write_oid = PBES2_OID,
+ .desc = "PKIX1.Gost28147-89-Parameters",
+ .iv_name = "iv",
+ .decrypt_only = 0},
+ {
+ .schema = PKCS12_ARCFOUR_SHA1,
+ .name = "PKCS12-ARCFOUR-SHA1",
+ .flag = GNUTLS_PKCS_PKCS12_ARCFOUR,
+ .cipher = GNUTLS_CIPHER_ARCFOUR,
+ .pbes2 = 0,
+ .cipher_oid = PKCS12_PBE_ARCFOUR_SHA1_OID,
+ .write_oid = PKCS12_PBE_ARCFOUR_SHA1_OID,
+ .desc = NULL,
+ .iv_name = NULL,
+ .decrypt_only = 0},
+ {
+ .schema = PKCS12_RC2_40_SHA1,
+ .name = "PKCS12-RC2-40-SHA1",
+ .flag = GNUTLS_PKCS_PKCS12_RC2_40,
+ .cipher = GNUTLS_CIPHER_RC2_40_CBC,
+ .pbes2 = 0,
+ .cipher_oid = PKCS12_PBE_RC2_40_SHA1_OID,
+ .write_oid = PKCS12_PBE_RC2_40_SHA1_OID,
+ .desc = NULL,
+ .iv_name = NULL,
+ .decrypt_only = 0},
+ {
+ .schema = PKCS12_3DES_SHA1,
+ .name = "PKCS12-3DES-SHA1",
+ .flag = GNUTLS_PKCS_PKCS12_3DES,
+ .cipher = GNUTLS_CIPHER_3DES_CBC,
+ .pbes2 = 0,
+ .cipher_oid = PKCS12_PBE_3DES_SHA1_OID,
+ .write_oid = PKCS12_PBE_3DES_SHA1_OID,
+ .desc = NULL,
+ .iv_name = NULL,
+ .decrypt_only = 0},
+ {0, 0, 0, 0, 0}
+};
+
+#define PBES2_SCHEMA_LOOP(b) { \
+ const struct pkcs_cipher_schema_st * _p; \
+ for (_p=avail_pkcs_cipher_schemas;_p->schema != 0;_p++) { b; } \
+ }
+
+#define PBES2_SCHEMA_FIND_FROM_FLAGS(fl, what) \
+ PBES2_SCHEMA_LOOP( if (_p->flag == GNUTLS_PKCS_CIPHER_MASK(fl)) { what; } )
+
+int _gnutls_pkcs_flags_to_schema(unsigned int flags)
+{
+ PBES2_SCHEMA_FIND_FROM_FLAGS(flags, return _p->schema;
+ );
+
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Selecting default encryption PBES2_AES_256 (flags: %u).\n",
+ flags);
+ return PBES2_AES_256;
+}
+
+/**
+ * gnutls_pkcs_schema_get_name:
+ * @schema: Holds the PKCS #12 or PBES2 schema (%gnutls_pkcs_encrypt_flags_t)
+ *
+ * This function will return a human readable description of the
+ * PKCS12 or PBES2 schema.
+ *
+ * Returns: a constraint string or %NULL on error.
+ *
+ * Since: 3.4.0
+ */
+const char *gnutls_pkcs_schema_get_name(unsigned int schema)
+{
+ PBES2_SCHEMA_FIND_FROM_FLAGS(schema, return _p->name;
+ );
+ return NULL;
+}
+
+/**
+ * gnutls_pkcs_schema_get_oid:
+ * @schema: Holds the PKCS #12 or PBES2 schema (%gnutls_pkcs_encrypt_flags_t)
+ *
+ * This function will return the object identifier of the
+ * PKCS12 or PBES2 schema.
+ *
+ * Returns: a constraint string or %NULL on error.
+ *
+ * Since: 3.4.0
+ */
+const char *gnutls_pkcs_schema_get_oid(unsigned int schema)
+{
+ PBES2_SCHEMA_FIND_FROM_FLAGS(schema, return _p->cipher_oid;
+ );
+ return NULL;
+}
+
+static const struct pkcs_cipher_schema_st *algo_to_pbes2_cipher_schema(unsigned
+ cipher)
+{
+ PBES2_SCHEMA_LOOP(if (_p->cipher == cipher && _p->pbes2 != 0) {
+ return _p;}
+ ) ;
+
+ gnutls_assert();
+ return NULL;
+}
+
+/* Converts a PKCS#7 encryption schema OID to an internal
+ * schema_id or returns a negative value */
+int _gnutls_check_pkcs_cipher_schema(const char *oid)
+{
+ if (strcmp(oid, PBES2_OID) == 0)
+ return PBES2_GENERIC; /* PBES2 ciphers are under an umbrella OID */
+
+ PBES2_SCHEMA_LOOP(if (_p->pbes2 == 0 && strcmp(oid, _p->write_oid) == 0) {
+ return _p->schema;}
+ ) ;
+ _gnutls_debug_log
+ ("PKCS #12 encryption schema OID '%s' is unsupported.\n", oid);
+
+ return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+const struct pkcs_cipher_schema_st *_gnutls_pkcs_schema_get(schema_id schema)
+{
+ PBES2_SCHEMA_LOOP(if (schema == _p->schema) return _p;) ;
+
+ gnutls_assert();
+ return NULL;
+}
+
+/* Converts an OID to a gnutls cipher type.
+ */
+static int
+pbes2_cipher_oid_to_algo(const char *oid, gnutls_cipher_algorithm_t * algo)
+{
+
+ *algo = 0;
+ PBES2_SCHEMA_LOOP(if
+ (_p->pbes2 != 0 && strcmp(_p->cipher_oid, oid) == 0) {
+ *algo = _p->cipher; return 0;}
+ ) ;
+
+ _gnutls_debug_log("PKCS #8 encryption OID '%s' is unsupported.\n", oid);
+ return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+/* Decrypts a PKCS #7 encryptedData. The output is allocated
+ * and stored in dec.
+ */
+int
+_gnutls_pkcs7_decrypt_data(const gnutls_datum_t * data,
+ const char *password, gnutls_datum_t * dec)
+{
+ int result, len;
+ char enc_oid[MAX_OID_SIZE];
+ gnutls_datum_t tmp;
+ asn1_node pasn = NULL, pkcs7_asn = NULL;
+ int params_start, params_end, params_len;
+ struct pbkdf2_params kdf_params;
+ struct pbe_enc_params enc_params;
+ schema_id schema;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-7-EncryptedData",
+ &pkcs7_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Check the encryption schema OID
+ */
+ len = sizeof(enc_oid);
+ result =
+ asn1_read_value(pkcs7_asn,
+ "encryptedContentInfo.contentEncryptionAlgorithm.algorithm",
+ enc_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ schema = result;
+
+ /* Get the DER encoding of the parameters.
+ */
+ result =
+ asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size,
+ "encryptedContentInfo.contentEncryptionAlgorithm.parameters",
+ &params_start, &params_end);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ params_len = params_end - params_start + 1;
+
+ result =
+ _gnutls_read_pkcs_schema_params(&schema, password,
+ &data->data[params_start],
+ params_len, &kdf_params,
+ &enc_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* Parameters have been decoded. Now
+ * decrypt the EncryptedData.
+ */
+
+ result =
+ _gnutls_pkcs_raw_decrypt_data(schema, pkcs7_asn,
+ "encryptedContentInfo.encryptedContent",
+ password, &kdf_params, &enc_params,
+ &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE);
+
+ *dec = tmp;
+
+ return 0;
+
+ error:
+ asn1_delete_structure(&pasn);
+ asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+}
+
+int
+_gnutls_pkcs7_data_enc_info(const gnutls_datum_t * data,
+ const struct pkcs_cipher_schema_st **p,
+ struct pbkdf2_params *kdf_params, char **oid)
+{
+ int result, len;
+ char enc_oid[MAX_OID_SIZE];
+ asn1_node pasn = NULL, pkcs7_asn = NULL;
+ int params_start, params_end, params_len;
+ struct pbe_enc_params enc_params;
+ schema_id schema;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-7-EncryptedData",
+ &pkcs7_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Check the encryption schema OID
+ */
+ len = sizeof(enc_oid);
+ result =
+ asn1_read_value(pkcs7_asn,
+ "encryptedContentInfo.contentEncryptionAlgorithm.algorithm",
+ enc_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ if (oid) {
+ *oid = gnutls_strdup(enc_oid);
+ }
+
+ if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ schema = result;
+
+ /* Get the DER encoding of the parameters.
+ */
+ result =
+ asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size,
+ "encryptedContentInfo.contentEncryptionAlgorithm.parameters",
+ &params_start, &params_end);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ params_len = params_end - params_start + 1;
+
+ result =
+ _gnutls_read_pkcs_schema_params(&schema, NULL,
+ &data->data[params_start],
+ params_len, kdf_params,
+ &enc_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ *p = _gnutls_pkcs_schema_get(schema);
+ if (*p == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+ goto error;
+ }
+
+ asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE);
+
+ return 0;
+
+ error:
+ asn1_delete_structure(&pasn);
+ asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+}
+
+/* Encrypts to a PKCS #7 encryptedData. The output is allocated
+ * and stored in enc.
+ */
+int
+_gnutls_pkcs7_encrypt_data(schema_id schema,
+ const gnutls_datum_t * data,
+ const char *password, gnutls_datum_t * enc)
+{
+ int result;
+ gnutls_datum_t key = { NULL, 0 };
+ gnutls_datum_t tmp = { NULL, 0 };
+ asn1_node pkcs7_asn = NULL;
+ struct pbkdf2_params kdf_params;
+ struct pbe_enc_params enc_params;
+ const struct pkcs_cipher_schema_st *s;
+
+ s = _gnutls_pkcs_schema_get(schema);
+ if (s == NULL || s->decrypt_only) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-7-EncryptedData",
+ &pkcs7_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result =
+ asn1_write_value(pkcs7_asn,
+ "encryptedContentInfo.contentEncryptionAlgorithm.algorithm",
+ s->write_oid, 1);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Generate a symmetric key.
+ */
+
+ result =
+ _gnutls_pkcs_generate_key(schema, password, &kdf_params,
+ &enc_params, &key);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = _gnutls_pkcs_write_schema_params(schema, pkcs7_asn,
+ "encryptedContentInfo.contentEncryptionAlgorithm.parameters",
+ &kdf_params, &enc_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* Parameters have been encoded. Now
+ * encrypt the Data.
+ */
+ result = _gnutls_pkcs_raw_encrypt_data(data, &enc_params, &key, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* write the encrypted data.
+ */
+ result =
+ asn1_write_value(pkcs7_asn,
+ "encryptedContentInfo.encryptedContent",
+ tmp.data, tmp.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ _gnutls_free_datum(&tmp);
+ _gnutls_free_key_datum(&key);
+
+ /* Now write the rest of the pkcs-7 stuff.
+ */
+
+ result = _gnutls_x509_write_uint32(pkcs7_asn, "version", 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result =
+ asn1_write_value(pkcs7_asn, "encryptedContentInfo.contentType",
+ DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = asn1_write_value(pkcs7_asn, "unprotectedAttrs", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Now encode and copy the DER stuff.
+ */
+ result = _gnutls_x509_der_encode(pkcs7_asn, "", enc, 0);
+
+ asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ error:
+ _gnutls_free_key_datum(&key);
+ _gnutls_free_datum(&tmp);
+ asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+}
+
+/* Reads the PBKDF2 parameters.
+ */
+static int
+read_pbkdf2_params(asn1_node pasn,
+ const gnutls_datum_t * der, struct pbkdf2_params *params)
+{
+ int params_start, params_end;
+ int params_len, len, result;
+ asn1_node pbkdf2_asn = NULL;
+ char oid[MAX_OID_SIZE];
+
+ memset(params, 0, sizeof(*params));
+
+ params->mac = GNUTLS_MAC_SHA1;
+
+ /* Check the key derivation algorithm
+ */
+ len = sizeof(oid);
+ result =
+ asn1_read_value(pasn, "keyDerivationFunc.algorithm", oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ _gnutls_hard_log("keyDerivationFunc.algorithm: %s\n", oid);
+
+ if (strcmp(oid, PBKDF2_OID) != 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("PKCS #8 key derivation OID '%s' is unsupported.\n", oid);
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ asn1_der_decoding_startEnd(pasn, der->data, der->size,
+ "keyDerivationFunc.parameters",
+ &params_start, &params_end);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ params_len = params_end - params_start + 1;
+
+ /* Now check the key derivation and the encryption
+ * functions.
+ */
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-5-PBKDF2-params",
+ &pbkdf2_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _asn1_strict_der_decode(&pbkdf2_asn, &der->data[params_start],
+ params_len, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* read the salt */
+ params->salt_size = sizeof(params->salt);
+ result =
+ asn1_read_value(pbkdf2_asn, "salt.specified", params->salt,
+ &params->salt_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ _gnutls_hard_log("salt.specified.size: %d\n", params->salt_size);
+
+ if (params->salt_size < 0) {
+ result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto error;
+ }
+
+ /* read the iteration count
+ */
+ result =
+ _gnutls_x509_read_uint(pbkdf2_asn, "iterationCount",
+ &params->iter_count);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (params->iter_count >= MAX_ITER_COUNT || params->iter_count == 0) {
+ result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto error;
+ }
+
+ _gnutls_hard_log("iterationCount: %d\n", params->iter_count);
+
+ /* read the keylength, if it is set.
+ */
+ result =
+ _gnutls_x509_read_uint(pbkdf2_asn, "keyLength", &params->key_size);
+ if (result < 0) {
+ params->key_size = 0;
+ }
+
+ if (params->key_size > MAX_CIPHER_KEY_SIZE) {
+ result = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto error;
+ }
+
+ _gnutls_hard_log("keyLength: %d\n", params->key_size);
+
+ len = sizeof(oid);
+ result = asn1_read_value(pbkdf2_asn, "prf.algorithm", oid, &len);
+ if (result != ASN1_SUCCESS) {
+ /* use the default MAC */
+ result = 0;
+ goto error;
+ }
+
+ params->mac = gnutls_oid_to_mac(oid);
+ if (params->mac == GNUTLS_MAC_UNKNOWN) {
+ gnutls_assert();
+ _gnutls_debug_log("Unsupported hash algorithm: %s\n", oid);
+ result = GNUTLS_E_UNKNOWN_HASH_ALGORITHM;
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&pbkdf2_asn);
+ return result;
+
+}
+
+/* Reads the PBE parameters from PKCS-12 schemas (*&#%*&#% RSA).
+ */
+static int read_pkcs12_kdf_params(asn1_node pasn, struct pbkdf2_params *params)
+{
+ int result;
+
+ memset(params, 0, sizeof(*params));
+
+ /* read the salt */
+ params->salt_size = sizeof(params->salt);
+ result =
+ asn1_read_value(pasn, "salt", params->salt, &params->salt_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (params->salt_size < 0)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ _gnutls_hard_log("salt.size: %d\n", params->salt_size);
+
+ /* read the iteration count
+ */
+ result =
+ _gnutls_x509_read_uint(pasn, "iterations", &params->iter_count);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ if (params->iter_count >= MAX_ITER_COUNT || params->iter_count == 0)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ _gnutls_hard_log("iterationCount: %d\n", params->iter_count);
+
+ params->key_size = 0;
+
+ return 0;
+}
+
+/* Writes the PBE parameters for PKCS-12 schemas.
+ */
+static int
+write_pkcs12_kdf_params(asn1_node pasn, const struct pbkdf2_params *kdf_params)
+{
+ int result;
+
+ /* write the salt
+ */
+ result =
+ asn1_write_value(pasn, "salt",
+ kdf_params->salt, kdf_params->salt_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ _gnutls_hard_log("salt.size: %d\n", kdf_params->salt_size);
+
+ /* write the iteration count
+ */
+ result =
+ _gnutls_x509_write_uint32(pasn, "iterations",
+ kdf_params->iter_count);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count);
+
+ return 0;
+
+ error:
+ return result;
+
+}
+
+static int
+read_pbes2_gost_oid(uint8_t *der, size_t len, char *oid, int oid_size)
+{
+ int result;
+ asn1_node pbe_asn = NULL;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Gost28147-89-Parameters", &pbe_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _asn1_strict_der_decode(&pbe_asn, der, len, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = asn1_read_value(pbe_asn, "encryptionParamSet",
+ oid, &oid_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&pbe_asn);
+ return result;
+}
+
+static int
+read_pbes2_enc_params(asn1_node pasn,
+ const gnutls_datum_t * der, struct pbe_enc_params *params)
+{
+ int params_start, params_end;
+ int params_len, len, result;
+ asn1_node pbe_asn = NULL;
+ const struct pkcs_cipher_schema_st *p;
+
+ memset(params, 0, sizeof(*params));
+
+ /* Check the encryption algorithm
+ */
+ len = sizeof(params->pbes2_oid);
+ result = asn1_read_value(pasn, "encryptionScheme.algorithm", params->pbes2_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ _gnutls_hard_log("encryptionScheme.algorithm: %s\n", params->pbes2_oid);
+
+ result =
+ asn1_der_decoding_startEnd(pasn, der->data, der->size,
+ "encryptionScheme.parameters",
+ &params_start, &params_end);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ params_len = params_end - params_start + 1;
+
+ /* For GOST we have to read params to determine actual cipher */
+ if (!strcmp(params->pbes2_oid, GOST28147_89_OID)) {
+ len = sizeof(params->pbes2_oid);
+ result = read_pbes2_gost_oid(&der->data[params_start],
+ params_len, params->pbes2_oid, len);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ if ((result = pbes2_cipher_oid_to_algo(params->pbes2_oid, &params->cipher)) < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Now check the encryption parameters.
+ */
+ p = algo_to_pbes2_cipher_schema(params->cipher);
+ if (p == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ p->desc, &pbe_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ _asn1_strict_der_decode(&pbe_asn, &der->data[params_start],
+ params_len, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* read the IV */
+ params->iv_size = sizeof(params->iv);
+ result = asn1_read_value(pbe_asn,
+ p->iv_name,
+ params->iv, &params->iv_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ _gnutls_hard_log("IV.size: %d\n", params->iv_size);
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&pbe_asn);
+ return result;
+}
+
+/* Read the parameters cipher, IV, salt etc using the given
+ * schema ID. Initially the schema ID should have PBES2_GENERIC, for
+ * PBES2 schemas, and will be updated by this function for details.
+ */
+int
+_gnutls_read_pkcs_schema_params(schema_id * schema, const char *password,
+ const uint8_t * data, int data_size,
+ struct pbkdf2_params *kdf_params,
+ struct pbe_enc_params *enc_params)
+{
+ asn1_node pasn = NULL;
+ int result;
+ gnutls_datum_t tmp;
+ const struct pkcs_cipher_schema_st *p;
+
+ if (*schema == PBES2_GENERIC) {
+ /* Now check the key derivation and the encryption
+ * functions.
+ */
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-5-PBES2-params",
+ &pasn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Decode the parameters.
+ */
+ result = _asn1_strict_der_decode(&pasn, data, data_size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ tmp.data = (uint8_t *) data;
+ tmp.size = data_size;
+
+ result = read_pbkdf2_params(pasn, &tmp, kdf_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = read_pbes2_enc_params(pasn, &tmp, enc_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ asn1_delete_structure2(&pasn, ASN1_DELETE_FLAG_ZEROIZE);
+
+ p = algo_to_pbes2_cipher_schema(enc_params->cipher);
+ if (p == NULL) {
+ result = GNUTLS_E_INVALID_REQUEST;
+ gnutls_assert();
+ goto error;
+ }
+
+ *schema = p->schema;
+ return 0;
+ } else if (*schema == PBES1_DES_MD5) {
+ return _gnutls_read_pbkdf1_params(data, data_size, kdf_params,
+ enc_params);
+ } else { /* PKCS #12 schema */
+ memset(enc_params, 0, sizeof(*enc_params));
+
+ p = _gnutls_pkcs_schema_get(*schema);
+ if (p == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+ goto error;
+ }
+ enc_params->cipher = p->cipher;
+ enc_params->iv_size = gnutls_cipher_get_iv_size(p->cipher);
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-PbeParams",
+ &pasn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Decode the parameters.
+ */
+ result = _asn1_strict_der_decode(&pasn, data, data_size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = read_pkcs12_kdf_params(pasn, kdf_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (enc_params->iv_size) {
+ result =
+ _gnutls_pkcs12_string_to_key(mac_to_entry
+ (GNUTLS_MAC_SHA1),
+ 2 /*IV*/,
+ kdf_params->salt,
+ kdf_params->salt_size,
+ kdf_params->iter_count,
+ password,
+ enc_params->iv_size,
+ enc_params->iv);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ }
+
+ asn1_delete_structure(&pasn);
+
+ return 0;
+ } /* switch */
+
+ error:
+ asn1_delete_structure(&pasn);
+ return result;
+}
+
+static int
+_gnutls_pbes2_string_to_key(unsigned int pass_len, const char *password,
+ const struct pbkdf2_params *kdf_params,
+ int key_size, uint8_t *key)
+{
+ gnutls_datum_t _key;
+ gnutls_datum_t salt;
+
+ _key.data = (void *)password;
+ _key.size = pass_len;
+ salt.data = (void *)kdf_params->salt;
+ salt.size = kdf_params->salt_size;
+
+ return gnutls_pbkdf2(kdf_params->mac, &_key, &salt,
+ kdf_params->iter_count, key, key_size);
+}
+
+int
+_gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn,
+ const char *root, const char *_password,
+ const struct pbkdf2_params *kdf_params,
+ const struct pbe_enc_params *enc_params,
+ gnutls_datum_t * decrypted_data)
+{
+ gnutls_datum_t enc = { NULL, 0 };
+ uint8_t *key = NULL;
+ gnutls_datum_t dkey, d_iv;
+ gnutls_cipher_hd_t ch = NULL;
+ int key_size, ret;
+ unsigned int pass_len = 0;
+ const struct pkcs_cipher_schema_st *p;
+ unsigned block_size;
+ const cipher_entry_st *ce;
+ char *password;
+
+ if (_password) {
+ gnutls_datum_t pout;
+ ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ password = (char*)pout.data;
+ pass_len = pout.size;
+ } else {
+ password = NULL;
+ pass_len = 0;
+ }
+
+ ret = _gnutls_x509_read_value(pkcs8_asn, root, &enc);
+ if (ret < 0) {
+ gnutls_assert();
+ enc.data = NULL;
+ goto cleanup;
+ }
+
+ if (schema == PBES1_DES_MD5) {
+ ret = _gnutls_decrypt_pbes1_des_md5_data(password, pass_len,
+ kdf_params,
+ enc_params, &enc,
+ decrypted_data);
+ if (ret < 0)
+ goto error;
+ goto cleanup;
+ }
+
+ if (kdf_params->key_size == 0) {
+ key_size = gnutls_cipher_get_key_size(enc_params->cipher);
+ } else
+ key_size = kdf_params->key_size;
+
+ key = gnutls_malloc(key_size);
+ if (key == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto error;
+ }
+
+ /* generate the key
+ */
+ p = _gnutls_pkcs_schema_get(schema);
+ if (p != NULL && p->pbes2 != 0) { /* PBES2 */
+ ret = _gnutls_pbes2_string_to_key(pass_len, password,
+ kdf_params,
+ key_size, key);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ } else if (p != NULL) { /* PKCS 12 schema */
+ ret =
+ _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1),
+ 1 /*KEY*/,
+ kdf_params->salt,
+ kdf_params->salt_size,
+ kdf_params->iter_count,
+ password, key_size, key);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ } else {
+ gnutls_assert();
+ ret = GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+ goto error;
+ }
+
+ ce = cipher_to_entry(enc_params->cipher);
+ block_size = _gnutls_cipher_get_block_size(ce);
+
+ if (ce->type == CIPHER_BLOCK) {
+ if (enc.size % block_size != 0 || (unsigned)enc_params->iv_size != block_size) {
+ gnutls_assert();
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto error;
+ }
+ } else {
+ unsigned iv_size = _gnutls_cipher_get_iv_size(ce);
+ if (iv_size > (unsigned)enc_params->iv_size) {
+ gnutls_assert();
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto error;
+ }
+ }
+
+ /* do the decryption.
+ */
+ dkey.data = key;
+ dkey.size = key_size;
+
+ d_iv.data = (uint8_t *) enc_params->iv;
+ d_iv.size = enc_params->iv_size;
+
+ ret = gnutls_cipher_init(&ch, ce->id, &dkey, &d_iv);
+
+ zeroize_temp_key(key, key_size);
+ gnutls_free(key);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = gnutls_cipher_decrypt(ch, enc.data, enc.size);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto error;
+ }
+
+ decrypted_data->data = enc.data;
+
+ if (ce->type == CIPHER_BLOCK && block_size != 1) {
+ unsigned pslen = (uint8_t)enc.data[enc.size - 1];
+ unsigned i;
+
+ if (pslen > block_size || pslen >= enc.size || pslen == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto error;
+ }
+
+ /* verify padding according to rfc2898 */
+ decrypted_data->size = enc.size - pslen;
+ for (i=0;i<pslen;i++) {
+ if (enc.data[enc.size-1-i] != pslen) {
+ gnutls_assert();
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto error;
+ }
+ }
+ } else {
+ decrypted_data->size = enc.size;
+ }
+
+ gnutls_cipher_deinit(ch);
+
+ ret = 0;
+
+ cleanup:
+ if (password) {
+ zeroize_temp_key(password, pass_len);
+ gnutls_free(password);
+ }
+
+ return ret;
+
+ error:
+ if (password) {
+ zeroize_temp_key(password, pass_len);
+ gnutls_free(password);
+ }
+ if (enc.data) {
+ zeroize_temp_key(enc.data, enc.size);
+ gnutls_free(enc.data);
+ }
+ if (key) {
+ zeroize_temp_key(key, key_size);
+ gnutls_free(key);
+ }
+ if (ch) {
+ gnutls_cipher_deinit(ch);
+ }
+ return ret;
+}
+
+/* Writes the PBKDF2 parameters.
+ */
+static int
+write_pbkdf2_params(asn1_node pasn, const struct pbkdf2_params *kdf_params)
+{
+ int result;
+ asn1_node pbkdf2_asn = NULL;
+ uint8_t tmp[MAX_OID_SIZE];
+ const mac_entry_st *me;
+
+ /* Write the key derivation algorithm
+ */
+ result =
+ asn1_write_value(pasn, "keyDerivationFunc.algorithm",
+ PBKDF2_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Now write the key derivation and the encryption
+ * functions.
+ */
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-5-PBKDF2-params",
+ &pbkdf2_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value(pbkdf2_asn, "salt", "specified", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* write the salt
+ */
+ result =
+ asn1_write_value(pbkdf2_asn, "salt.specified",
+ kdf_params->salt, kdf_params->salt_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ _gnutls_hard_log("salt.specified.size: %d\n", kdf_params->salt_size);
+
+ /* write the iteration count
+ */
+ _gnutls_write_uint32(kdf_params->iter_count, tmp);
+
+ result = asn1_write_value(pbkdf2_asn, "iterationCount", tmp, 4);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count);
+
+ /* write the keylength, if it is set.
+ */
+ result = asn1_write_value(pbkdf2_asn, "keyLength", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ me = _gnutls_mac_to_entry(kdf_params->mac);
+ if (!me || !me->mac_oid) {
+ gnutls_assert();
+ result = GNUTLS_E_INTERNAL_ERROR;
+ goto error;
+ }
+
+ result = asn1_write_value(pbkdf2_asn, "prf.algorithm",
+ me->mac_oid, strlen(me->mac_oid));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = asn1_write_value(pbkdf2_asn, "prf.parameters", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* now encode them an put the DER output
+ * in the keyDerivationFunc.parameters
+ */
+ result = _gnutls_x509_der_encode_and_copy(pbkdf2_asn, "",
+ pasn,
+ "keyDerivationFunc.parameters",
+ 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&pbkdf2_asn);
+ return result;
+
+}
+
+static int
+write_pbes2_enc_params(asn1_node pasn, const struct pbe_enc_params *params)
+{
+ int result;
+ asn1_node pbe_asn = NULL;
+ const struct pkcs_cipher_schema_st *p;
+ const char *cipher_oid;
+
+ /* Write the encryption algorithm
+ */
+ p = algo_to_pbes2_cipher_schema(params->cipher);
+ if (p == NULL || p->pbes2 == 0) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Now check the encryption parameters.
+ */
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ p->desc, &pbe_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (p->schema == PBES2_GOST28147_89_TC26Z ||
+ p->schema == PBES2_GOST28147_89_CPA ||
+ p->schema == PBES2_GOST28147_89_CPB ||
+ p->schema == PBES2_GOST28147_89_CPC ||
+ p->schema == PBES2_GOST28147_89_CPD) {
+ cipher_oid = GOST28147_89_OID;
+ result = asn1_write_value(pbe_asn, "encryptionParamSet",
+ p->cipher_oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ } else {
+ cipher_oid = p->cipher_oid;
+ }
+
+ result =
+ asn1_write_value(pasn, "encryptionScheme.algorithm",
+ cipher_oid,
+ 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+ _gnutls_hard_log("encryptionScheme.algorithm: %s\n", cipher_oid);
+
+ /* read the salt */
+ result = asn1_write_value(pbe_asn, p->iv_name,
+ params->iv, params->iv_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ _gnutls_hard_log("IV.size: %d\n", params->iv_size);
+
+ /* now encode them an put the DER output
+ * in the encryptionScheme.parameters
+ */
+ result = _gnutls_x509_der_encode_and_copy(pbe_asn, "",
+ pasn,
+ "encryptionScheme.parameters",
+ 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&pbe_asn);
+ return result;
+
+}
+
+/* Generates a key and also stores the key parameters.
+ */
+int
+_gnutls_pkcs_generate_key(schema_id schema,
+ const char *_password,
+ struct pbkdf2_params *kdf_params,
+ struct pbe_enc_params *enc_params,
+ gnutls_datum_t * key)
+{
+ unsigned char rnd[2];
+ unsigned int pass_len = 0;
+ int ret;
+ const struct pkcs_cipher_schema_st *p;
+ char *password = NULL;
+
+ if (_password) {
+ gnutls_datum_t pout;
+ ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ password = (char*)pout.data;
+ pass_len = pout.size;
+ } else {
+ password = NULL;
+ pass_len = 0;
+ }
+
+ ret = gnutls_rnd(GNUTLS_RND_RANDOM, rnd, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* generate salt */
+ kdf_params->salt_size =
+ MIN(sizeof(kdf_params->salt), (unsigned)(12 + (rnd[1] % 10)));
+
+ p = _gnutls_pkcs_schema_get(schema);
+ if (p != NULL && p->pbes2 != 0) { /* PBES2 */
+ enc_params->cipher = p->cipher;
+ } else if (p != NULL) {
+ /* non PBES2 algorithms */
+ enc_params->cipher = p->cipher;
+ kdf_params->salt_size = 8;
+ } else {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ ret = gnutls_rnd(GNUTLS_RND_RANDOM, kdf_params->salt,
+ kdf_params->salt_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ kdf_params->iter_count = PKCS12_ITER_COUNT;
+ key->size = kdf_params->key_size =
+ gnutls_cipher_get_key_size(enc_params->cipher);
+
+ enc_params->iv_size = gnutls_cipher_get_iv_size(enc_params->cipher);
+ key->data = gnutls_malloc(key->size);
+ if (key->data == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ /* now generate the key.
+ */
+
+ if (p->pbes2 != 0) {
+ if (p->schema == PBES2_GOST28147_89_TC26Z)
+ kdf_params->mac = GNUTLS_MAC_STREEBOG_512;
+ else if (p->schema == PBES2_GOST28147_89_CPA ||
+ p->schema == PBES2_GOST28147_89_CPB ||
+ p->schema == PBES2_GOST28147_89_CPC ||
+ p->schema == PBES2_GOST28147_89_CPD)
+ kdf_params->mac = GNUTLS_MAC_GOSTR_94;
+ else
+ kdf_params->mac = GNUTLS_MAC_SHA256;
+ ret = _gnutls_pbes2_string_to_key(pass_len, password,
+ kdf_params,
+ kdf_params->key_size,
+ key->data);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (enc_params->iv_size) {
+ ret = gnutls_rnd(GNUTLS_RND_NONCE,
+ enc_params->iv, enc_params->iv_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ } else { /* PKCS 12 schema */
+ ret =
+ _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1),
+ 1 /*KEY*/,
+ kdf_params->salt,
+ kdf_params->salt_size,
+ kdf_params->iter_count,
+ password,
+ kdf_params->key_size,
+ key->data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Now generate the IV
+ */
+ if (enc_params->iv_size) {
+ ret =
+ _gnutls_pkcs12_string_to_key(mac_to_entry
+ (GNUTLS_MAC_SHA1),
+ 2 /*IV*/,
+ kdf_params->salt,
+ kdf_params->salt_size,
+ kdf_params->iter_count,
+ password,
+ enc_params->iv_size,
+ enc_params->iv);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(password);
+ return ret;
+}
+
+/* Encodes the parameters to be written in the encryptionAlgorithm.parameters
+ * part.
+ */
+int
+_gnutls_pkcs_write_schema_params(schema_id schema, asn1_node pkcs8_asn,
+ const char *where,
+ const struct pbkdf2_params *kdf_params,
+ const struct pbe_enc_params *enc_params)
+{
+ int result;
+ asn1_node pasn = NULL;
+ const struct pkcs_cipher_schema_st *p;
+
+ p = _gnutls_pkcs_schema_get(schema);
+
+ if (p != NULL && p->pbes2 != 0) { /* PBES2 */
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-5-PBES2-params",
+ &pasn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = write_pbkdf2_params(pasn, kdf_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = write_pbes2_enc_params(pasn, enc_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = _gnutls_x509_der_encode_and_copy(pasn, "",
+ pkcs8_asn, where, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ asn1_delete_structure(&pasn);
+
+ } else if (p != NULL) { /* PKCS #12 */
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-12-PbeParams",
+ &pasn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = write_pkcs12_kdf_params(pasn, kdf_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = _gnutls_x509_der_encode_and_copy(pasn, "",
+ pkcs8_asn, where, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ asn1_delete_structure(&pasn);
+ }
+
+ return 0;
+
+ error:
+ asn1_delete_structure(&pasn);
+ return result;
+
+}
+
+int
+_gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain,
+ const struct pbe_enc_params *enc_params,
+ const gnutls_datum_t * key, gnutls_datum_t * encrypted)
+{
+ int result;
+ int data_size;
+ uint8_t *data = NULL;
+ gnutls_datum_t d_iv;
+ gnutls_cipher_hd_t ch = NULL;
+ uint8_t pad, pad_size;
+ const cipher_entry_st *ce;
+
+ ce = cipher_to_entry(enc_params->cipher);
+ pad_size = _gnutls_cipher_get_block_size(ce);
+
+ if (pad_size == 1 || ce->type == CIPHER_STREAM) /* stream */
+ pad_size = 0;
+
+ data = gnutls_malloc(plain->size + pad_size);
+ if (data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ memcpy(data, plain->data, plain->size);
+
+ if (pad_size > 0) {
+ pad = pad_size - (plain->size % pad_size);
+ if (pad == 0)
+ pad = pad_size;
+ memset(&data[plain->size], pad, pad);
+ } else
+ pad = 0;
+
+ data_size = plain->size + pad;
+
+ d_iv.data = (uint8_t *) enc_params->iv;
+ d_iv.size = enc_params->iv_size;
+ result = gnutls_cipher_init(&ch, enc_params->cipher, key, &d_iv);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = gnutls_cipher_encrypt(ch, data, data_size);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ encrypted->data = data;
+ encrypted->size = data_size;
+
+ gnutls_cipher_deinit(ch);
+
+ return 0;
+
+ error:
+ gnutls_free(data);
+ if (ch) {
+ gnutls_cipher_deinit(ch);
+ }
+ return result;
+}
diff --git a/lib/x509/pkcs7-output.c b/lib/x509/pkcs7-output.c
new file mode 100644
index 0000000..3d686df
--- /dev/null
+++ b/lib/x509/pkcs7-output.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_int.h>
+#include <num.h>
+#include "errors.h"
+#include <extras/randomart.h>
+#include <pkcs7_int.h>
+
+#define addf _gnutls_buffer_append_printf
+#define adds _gnutls_buffer_append_str
+
+static void print_dn(gnutls_buffer_st * str, const char *prefix,
+ const gnutls_datum_t * raw)
+{
+ gnutls_x509_dn_t dn = NULL;
+ gnutls_datum_t output = { NULL, 0 };
+ int ret;
+
+ ret = gnutls_x509_dn_init(&dn);
+ if (ret < 0) {
+ addf(str, "%s: [error]\n", prefix);
+ return;
+ }
+
+ ret = gnutls_x509_dn_import(dn, raw);
+ if (ret < 0) {
+ addf(str, "%s: [error]\n", prefix);
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_dn_get_str2(dn, &output, 0);
+ if (ret < 0) {
+ addf(str, "%s: [error]\n", prefix);
+ goto cleanup;
+ }
+
+ addf(str, "%s: %s\n", prefix, output.data);
+
+ cleanup:
+ gnutls_x509_dn_deinit(dn);
+ gnutls_free(output.data);
+}
+
+/* Do not encode ASN1 and type for now */
+#define ENTRY(oid, name, type) {oid, sizeof(oid)-1, name, sizeof(name)-1, NULL, type}
+#define ENTRY2(oid, name) {oid, sizeof(oid)-1, name, sizeof(name)-1, NULL, ASN1_ETYPE_INVALID}
+
+static const struct oid_to_string pkcs7_attrs[] = {
+ ENTRY ("1.2.840.113549.1.9.3", "contentType", ASN1_ETYPE_OBJECT_ID),
+ ENTRY ("1.2.840.113549.1.9.4", "messageDigest", ASN1_ETYPE_OCTET_STRING),
+ ENTRY ("1.2.840.113549.1.9.5", "signingTime", ASN1_ETYPE_INVALID),
+ ENTRY2("1.2.840.113549.1.9.6", "countersignature"),
+ ENTRY2("1.2.840.113549.1.9.15", "smimeCapabilities"),
+
+ ENTRY2("1.2.840.113549.1.9.16.2.1", "aa-receiptRequest"),
+ ENTRY2("1.2.840.113549.1.9.16.2.2", "aa-securityLabel"),
+ ENTRY2("1.2.840.113549.1.9.16.2.3", "aa-mlExpandHistory"),
+ ENTRY2("1.2.840.113549.1.9.16.2.4", "aa-contentHint"),
+ ENTRY2("1.2.840.113549.1.9.16.2.9", "aa-equivalentLabels"),
+ ENTRY2("1.2.840.113549.1.9.16.2.10", "aa-contentReference"),
+ ENTRY2("1.2.840.113549.1.9.16.2.11", "aa-encrypKeyPref"),
+ ENTRY2("1.2.840.113549.1.9.16.2.12", "aa-signingCertificate"),
+ ENTRY2("1.2.840.113549.1.9.16.2.19", "aa-ets-otherSigCert"),
+ ENTRY2("1.2.840.113549.1.9.16.2.47", "aa-signingCertificateV2"),
+
+ {NULL, 0, NULL, 0, NULL, 0}
+};
+
+static void print_raw(gnutls_buffer_st * str, const char *prefix,
+ const gnutls_datum_t * raw)
+{
+ gnutls_datum_t result;
+ int ret;
+
+ if (raw->data == NULL || raw->size == 0)
+ return;
+
+ ret = gnutls_hex_encode2(raw, &result);
+ if (ret < 0) {
+ addf(str, "%s: [error]\n", prefix);
+ return;
+ }
+
+ addf(str, "%s: %s\n", prefix, result.data);
+ gnutls_free(result.data);
+}
+
+static void print_pkcs7_info(gnutls_pkcs7_signature_info_st * info,
+ gnutls_buffer_st * str,
+ gnutls_certificate_print_formats_t format)
+{
+ unsigned i;
+ char *oid;
+ gnutls_datum_t data;
+ char prefix[128];
+ char s[42];
+ size_t max;
+ int ret;
+ const struct oid_to_string * entry;
+
+ if (info->issuer_dn.size > 0)
+ print_dn(str, "\tSigner's issuer DN", &info->issuer_dn);
+ print_raw(str, "\tSigner's serial", &info->signer_serial);
+ print_raw(str, "\tSigner's issuer key ID", &info->issuer_keyid);
+ if (info->signing_time != -1) {
+ struct tm t;
+ if (gmtime_r(&info->signing_time, &t) == NULL) {
+ addf(str, "error: gmtime_r (%ld)\n",
+ (unsigned long)info->signing_time);
+ } else {
+ max = sizeof(s);
+ if (strftime(s, max, "%a %b %d %H:%M:%S UTC %Y", &t) ==
+ 0) {
+ addf(str, "error: strftime (%ld)\n",
+ (unsigned long)info->signing_time);
+ } else {
+ addf(str, "\tSigning time: %s\n", s);
+ }
+ }
+ }
+
+ addf(str, "\tSignature Algorithm: %s\n",
+ gnutls_sign_get_name(info->algo));
+
+ if (format == GNUTLS_CRT_PRINT_FULL) {
+ if (info->signed_attrs) {
+ for (i = 0;; i++) {
+ ret =
+ gnutls_pkcs7_get_attr(info->signed_attrs, i,
+ &oid, &data, 0);
+ if (ret < 0)
+ break;
+ if (i == 0)
+ addf(str, "\tSigned Attributes:\n");
+
+ entry = _gnutls_oid_get_entry(pkcs7_attrs, oid);
+ snprintf(prefix, sizeof(prefix), "\t\t%s",
+ (entry && entry->name_desc) ? entry->name_desc : oid);
+ print_raw(str, prefix, &data);
+ gnutls_free(data.data);
+ }
+ }
+ if (info->unsigned_attrs) {
+ for (i = 0;; i++) {
+ ret =
+ gnutls_pkcs7_get_attr(info->unsigned_attrs,
+ i, &oid, &data, 0);
+ if (ret < 0)
+ break;
+ if (i == 0)
+ addf(str, "\tUnsigned Attributes:\n");
+
+ entry = _gnutls_oid_get_entry(pkcs7_attrs, oid);
+ snprintf(prefix, sizeof(prefix), "\t\t%s",
+ (entry && entry->name_desc) ? entry->name_desc : oid);
+ print_raw(str, prefix, &data);
+ gnutls_free(data.data);
+ }
+ }
+ }
+ adds(str, "\n");
+}
+
+/**
+ * gnutls_pkcs7_print_signature_info:
+ * @info: The PKCS7 signature info struct to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print a PKCS #7 signature info structure, suitable
+ * for display to a human.
+ *
+ * Currently the supported formats are %GNUTLS_CRT_PRINT_FULL and
+ * %GNUTLS_CRT_PRINT_COMPACT.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.14
+ **/
+int gnutls_pkcs7_print_signature_info(gnutls_pkcs7_signature_info_st * info,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ gnutls_buffer_st str;
+
+ _gnutls_buffer_init(&str);
+ print_pkcs7_info(info, &str, format);
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+}
+
+/**
+ * gnutls_pkcs7_crt_print:
+ * @pkcs7: The PKCS7 struct to be printed
+ * @format: Indicate the format to use
+ * @out: Newly allocated datum with null terminated string.
+ *
+ * This function will pretty print a signed PKCS #7 structure, suitable for
+ * display to a human.
+ *
+ * Currently the supported formats are %GNUTLS_CRT_PRINT_FULL and
+ * %GNUTLS_CRT_PRINT_COMPACT.
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_print(gnutls_pkcs7_t pkcs7,
+ gnutls_certificate_print_formats_t format,
+ gnutls_datum_t * out)
+{
+ int count, ret, i;
+ gnutls_pkcs7_signature_info_st info;
+ gnutls_buffer_st str;
+ const char *oid;
+
+ _gnutls_buffer_init(&str);
+
+ /* For backwards compatibility with structures using the default OID,
+ * we don't print the eContent Type explicitly */
+ oid = gnutls_pkcs7_get_embedded_data_oid(pkcs7);
+ if (oid) {
+ if (strcmp(oid, DATA_OID) != 0
+ && strcmp(oid, DIGESTED_DATA_OID) != 0) {
+ addf(&str, "eContent Type: %s\n", oid);
+ }
+ }
+
+ for (i = 0;; i++) {
+ if (i == 0)
+ addf(&str, "Signers:\n");
+
+ ret = gnutls_pkcs7_get_signature_info(pkcs7, i, &info);
+ if (ret < 0)
+ break;
+
+ print_pkcs7_info(&info, &str, format);
+ gnutls_pkcs7_signature_info_deinit(&info);
+ }
+
+ if (format == GNUTLS_CRT_PRINT_FULL) {
+ gnutls_datum_t data, b64;
+
+ count = gnutls_pkcs7_get_crt_count(pkcs7);
+
+ if (count > 0) {
+ addf(&str, "Number of certificates: %u\n\n",
+ count);
+
+ for (i = 0; i < count; i++) {
+ ret =
+ gnutls_pkcs7_get_crt_raw2(pkcs7, i, &data);
+ if (ret < 0) {
+ addf(&str,
+ "Error: cannot print certificate %d\n",
+ i);
+ continue;
+ }
+
+ ret =
+ gnutls_pem_base64_encode_alloc
+ ("CERTIFICATE", &data, &b64);
+ if (ret < 0) {
+ gnutls_free(data.data);
+ continue;
+ }
+
+ adds(&str, (char*)b64.data);
+ adds(&str, "\n");
+ gnutls_free(b64.data);
+ gnutls_free(data.data);
+ }
+ }
+
+ count = gnutls_pkcs7_get_crl_count(pkcs7);
+ if (count > 0) {
+ addf(&str, "Number of CRLs: %u\n\n", count);
+
+ for (i = 0; i < count; i++) {
+ ret =
+ gnutls_pkcs7_get_crl_raw2(pkcs7, i, &data);
+ if (ret < 0) {
+ addf(&str,
+ "Error: cannot print certificate %d\n",
+ i);
+ continue;
+ }
+
+ ret =
+ gnutls_pem_base64_encode_alloc("X509 CRL",
+ &data, &b64);
+ if (ret < 0) {
+ gnutls_free(data.data);
+ continue;
+ }
+
+ adds(&str, (char*)b64.data);
+ adds(&str, "\n");
+ gnutls_free(b64.data);
+ gnutls_free(data.data);
+ }
+ }
+ }
+
+ return _gnutls_buffer_to_datum(&str, out, 1);
+}
diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c
new file mode 100644
index 0000000..ff8cab0
--- /dev/null
+++ b/lib/x509/pkcs7.c
@@ -0,0 +1,2564 @@
+/*
+ * Copyright (C) 2003-2015 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate on PKCS7 certificate lists parsing.
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509_b64.h>
+#include <pkcs7_int.h>
+#include <gnutls/abstract.h>
+#include <gnutls/pkcs7.h>
+
+#define ATTR_MESSAGE_DIGEST "1.2.840.113549.1.9.4"
+#define ATTR_SIGNING_TIME "1.2.840.113549.1.9.5"
+#define ATTR_CONTENT_TYPE "1.2.840.113549.1.9.3"
+
+static const uint8_t one = 1;
+
+/* Decodes the PKCS #7 signed data, and returns an asn1_node,
+ * which holds them. If raw is non null then the raw decoded
+ * data are copied (they are locally allocated) there.
+ */
+static int _decode_pkcs7_signed_data(gnutls_pkcs7_t pkcs7)
+{
+ asn1_node c2;
+ int len, result;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ len = MAX_OID_SIZE - 1;
+ result = asn1_read_value(pkcs7->pkcs7, "contentType", pkcs7->encap_data_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (strcmp(pkcs7->encap_data_oid, SIGNED_DATA_OID) != 0) {
+ gnutls_assert();
+ _gnutls_debug_log("Unknown PKCS7 Content OID '%s'\n", pkcs7->encap_data_oid);
+ return GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE;
+ }
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-7-SignedData",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* the Signed-data has been created, so
+ * decode them.
+ */
+ result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Step 1. In case of a signed structure extract certificate set.
+ */
+
+ result = asn1_der_decoding(&c2, tmp.data, tmp.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* read the encapsulated content */
+ len = MAX_OID_SIZE - 1;
+ result =
+ asn1_read_value(c2, "encapContentInfo.eContentType", pkcs7->encap_data_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (strcmp(pkcs7->encap_data_oid, DATA_OID) != 0
+ && strcmp(pkcs7->encap_data_oid, DIGESTED_DATA_OID) != 0) {
+ _gnutls_debug_log
+ ("Unknown PKCS#7 Encapsulated Content OID '%s'; treating as raw data\n",
+ pkcs7->encap_data_oid);
+
+ }
+
+ /* Try reading as octet string according to rfc5652. If that fails, attempt
+ * a raw read according to rfc2315 */
+ result = _gnutls_x509_read_string(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data, ASN1_ETYPE_OCTET_STRING, 1);
+ if (result < 0) {
+ result = _gnutls_x509_read_value(c2, "encapContentInfo.eContent", &pkcs7->der_signed_data);
+ if (result < 0) {
+ pkcs7->der_signed_data.data = NULL;
+ pkcs7->der_signed_data.size = 0;
+ } else {
+ int tag_len, len_len;
+ unsigned char cls;
+ unsigned long tag;
+
+ /* we skip the embedded element's tag and length - uncharted territorry - used by MICROSOFT_CERT_TRUST_LIST */
+ result = asn1_get_tag_der(pkcs7->der_signed_data.data, pkcs7->der_signed_data.size, &cls, &tag_len, &tag);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_get_length_ber(pkcs7->der_signed_data.data+tag_len, pkcs7->der_signed_data.size-tag_len, &len_len);
+ if (result < 0) {
+ gnutls_assert();
+ result = GNUTLS_E_ASN1_DER_ERROR;
+ goto cleanup;
+ }
+
+ tag_len += len_len;
+ memmove(pkcs7->der_signed_data.data, &pkcs7->der_signed_data.data[tag_len], pkcs7->der_signed_data.size-tag_len);
+ pkcs7->der_signed_data.size-=tag_len;
+ }
+ }
+
+ if (pkcs7->signed_data)
+ asn1_delete_structure(&pkcs7->signed_data);
+ pkcs7->signed_data = c2;
+ gnutls_free(tmp.data);
+
+ return 0;
+
+ cleanup:
+ gnutls_free(tmp.data);
+ if (c2)
+ asn1_delete_structure(&c2);
+ return result;
+}
+
+static int pkcs7_reinit(gnutls_pkcs7_t pkcs7)
+{
+ int result;
+
+ asn1_delete_structure(&pkcs7->pkcs7);
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-7-ContentInfo", &pkcs7->pkcs7);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs7_init:
+ * @pkcs7: A pointer to the type to be initialized
+ *
+ * This function will initialize a PKCS7 structure. PKCS7 structures
+ * usually contain lists of X.509 Certificates and X.509 Certificate
+ * revocation lists.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_init(gnutls_pkcs7_t * pkcs7)
+{
+ *pkcs7 = gnutls_calloc(1, sizeof(gnutls_pkcs7_int));
+
+ if (*pkcs7) {
+ int result = pkcs7_reinit(*pkcs7);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(*pkcs7);
+ return result;
+ }
+ return 0; /* success */
+ }
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+/**
+ * gnutls_pkcs7_deinit:
+ * @pkcs7: the type to be deinitialized
+ *
+ * This function will deinitialize a PKCS7 type.
+ **/
+void gnutls_pkcs7_deinit(gnutls_pkcs7_t pkcs7)
+{
+ if (!pkcs7)
+ return;
+
+ if (pkcs7->pkcs7)
+ asn1_delete_structure(&pkcs7->pkcs7);
+
+ if (pkcs7->signed_data)
+ asn1_delete_structure(&pkcs7->signed_data);
+
+ _gnutls_free_datum(&pkcs7->der_signed_data);
+
+ gnutls_free(pkcs7);
+}
+
+/**
+ * gnutls_pkcs7_import:
+ * @pkcs7: The data to store the parsed PKCS7.
+ * @data: The DER or PEM encoded PKCS7.
+ * @format: One of DER or PEM
+ *
+ * This function will convert the given DER or PEM encoded PKCS7 to
+ * the native #gnutls_pkcs7_t format. The output will be stored in
+ * @pkcs7. Any signed data that may be present inside the @pkcs7
+ * structure, like certificates set by gnutls_pkcs7_set_crt(), will
+ * be freed and overwritten by this function.
+ *
+ * If the PKCS7 is PEM encoded it should have a header of "PKCS7".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs7_import(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format)
+{
+ int result = 0, need_free = 0;
+ gnutls_datum_t _data;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ /* If the PKCS7 is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ result =
+ _gnutls_fbase64_decode(PEM_PKCS7, data->data,
+ data->size, &_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ need_free = 1;
+ }
+
+ if (pkcs7->expanded) {
+ result = pkcs7_reinit(pkcs7);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ pkcs7->expanded = 1;
+
+ result = asn1_der_decoding(&pkcs7->pkcs7, _data.data, _data.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Decode the signed data.
+ */
+ result = _decode_pkcs7_signed_data(pkcs7);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ if (need_free)
+ _gnutls_free_datum(&_data);
+ return result;
+}
+
+/**
+ * gnutls_pkcs7_get_crt_raw2:
+ * @pkcs7: should contain a gnutls_pkcs7_t type
+ * @indx: contains the index of the certificate to extract
+ * @cert: will hold the contents of the certificate; must be deallocated with gnutls_free()
+ *
+ * This function will return a certificate of the PKCS7 or RFC2630
+ * certificate set.
+ *
+ * After the last certificate has been read
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. If the provided buffer is not long enough,
+ * then @certificate_size is updated and
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned.
+ *
+ * Since: 3.4.2
+ **/
+int
+gnutls_pkcs7_get_crt_raw2(gnutls_pkcs7_t pkcs7,
+ unsigned indx, gnutls_datum_t * cert)
+{
+ int result, len;
+ char root2[MAX_NAME_SIZE];
+ char oid[MAX_OID_SIZE];
+ gnutls_datum_t tmp = { NULL, 0 };
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* Step 2. Parse the CertificateSet
+ */
+ snprintf(root2, sizeof(root2), "certificates.?%u", indx + 1);
+
+ len = sizeof(oid) - 1;
+
+ result = asn1_read_value(pkcs7->signed_data, root2, oid, &len);
+
+ if (result == ASN1_VALUE_NOT_FOUND) {
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* if 'Certificate' is the choice found:
+ */
+ if (strcmp(oid, "certificate") == 0) {
+ int start, end;
+
+ result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ asn1_der_decoding_startEnd(pkcs7->signed_data, tmp.data,
+ tmp.size, root2, &start, &end);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ end = end - start + 1;
+
+ result = _gnutls_set_datum(cert, &tmp.data[start], end);
+ } else {
+ result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+ }
+
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ return result;
+}
+
+/**
+ * gnutls_pkcs7_get_crt_raw:
+ * @pkcs7: should contain a gnutls_pkcs7_t type
+ * @indx: contains the index of the certificate to extract
+ * @certificate: the contents of the certificate will be copied
+ * there (may be null)
+ * @certificate_size: should hold the size of the certificate
+ *
+ * This function will return a certificate of the PKCS7 or RFC2630
+ * certificate set.
+ *
+ * After the last certificate has been read
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. If the provided buffer is not long enough,
+ * then @certificate_size is updated and
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned.
+ **/
+int
+gnutls_pkcs7_get_crt_raw(gnutls_pkcs7_t pkcs7,
+ unsigned indx, void *certificate,
+ size_t * certificate_size)
+{
+ int ret;
+ gnutls_datum_t tmp = { NULL, 0 };
+
+ ret = gnutls_pkcs7_get_crt_raw2(pkcs7, indx, &tmp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if ((unsigned)tmp.size > *certificate_size) {
+ *certificate_size = tmp.size;
+ ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ goto cleanup;
+ }
+
+ *certificate_size = tmp.size;
+ if (certificate)
+ memcpy(certificate, tmp.data, tmp.size);
+
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ return ret;
+}
+
+/**
+ * gnutls_pkcs7_get_crt_count:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ *
+ * This function will return the number of certificates in the PKCS7
+ * or RFC2630 certificate set.
+ *
+ * Returns: On success, a positive number is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_get_crt_count(gnutls_pkcs7_t pkcs7)
+{
+ int result, count;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* Step 2. Count the CertificateSet */
+
+ result =
+ asn1_number_of_elements(pkcs7->signed_data, "certificates", &count);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return 0; /* no certificates */
+ }
+
+ return count;
+}
+
+/**
+ * gnutls_pkcs7_signature_info_deinit:
+ * @info: should point to a #gnutls_pkcs7_signature_info_st structure
+ *
+ * This function will deinitialize any allocated value in the
+ * provided #gnutls_pkcs7_signature_info_st.
+ *
+ * Since: 3.4.2
+ **/
+void gnutls_pkcs7_signature_info_deinit(gnutls_pkcs7_signature_info_st * info)
+{
+ gnutls_free(info->sig.data);
+ gnutls_free(info->issuer_dn.data);
+ gnutls_free(info->signer_serial.data);
+ gnutls_free(info->issuer_keyid.data);
+ gnutls_pkcs7_attrs_deinit(info->signed_attrs);
+ gnutls_pkcs7_attrs_deinit(info->unsigned_attrs);
+ memset(info, 0, sizeof(*info));
+}
+
+static time_t parse_time(gnutls_pkcs7_t pkcs7, const char *root)
+{
+ char tval[128];
+ asn1_node c2 = NULL;
+ time_t ret;
+ int result, len;
+
+ result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Time", &c2);
+ if (result != ASN1_SUCCESS) {
+ ret = -1;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ len = sizeof(tval);
+ result = asn1_read_value(pkcs7->signed_data, root, tval, &len);
+ if (result != ASN1_SUCCESS) {
+ ret = -1;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _asn1_strict_der_decode(&c2, tval, len, NULL);
+ if (result != ASN1_SUCCESS) {
+ ret = -1;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_get_time(c2, "", 0);
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_pkcs7_get_signature_count:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ *
+ * This function will return the number of signatures in the PKCS7
+ * structure.
+ *
+ * Returns: On success, a positive number is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.3
+ **/
+int gnutls_pkcs7_get_signature_count(gnutls_pkcs7_t pkcs7)
+{
+ int ret, count;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret =
+ asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return 0;
+ }
+
+ return count;
+}
+
+/**
+ * gnutls_pkcs7_get_signature_info:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @idx: the index of the signature info to check
+ * @info: will contain the output signature
+ *
+ * This function will return information about the signature identified
+ * by idx in the provided PKCS #7 structure. The information should be
+ * deinitialized using gnutls_pkcs7_signature_info_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.2
+ **/
+int gnutls_pkcs7_get_signature_info(gnutls_pkcs7_t pkcs7, unsigned idx,
+ gnutls_pkcs7_signature_info_st * info)
+{
+ int ret, count, len;
+ char root[256];
+ char oid[MAX_OID_SIZE];
+ gnutls_pk_algorithm_t pk;
+ gnutls_sign_algorithm_t sig;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned i;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ memset(info, 0, sizeof(*info));
+ info->signing_time = -1;
+
+ ret =
+ asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count);
+ if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.signatureAlgorithm.algorithm", idx + 1);
+
+ len = sizeof(oid) - 1;
+ ret = asn1_read_value(pkcs7->signed_data, root, oid, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto unsupp_algo;
+ }
+
+ sig = gnutls_oid_to_sign(oid);
+ if (sig == GNUTLS_SIGN_UNKNOWN) {
+ /* great PKCS #7 allows to only specify a public key algo */
+ pk = gnutls_oid_to_pk(oid);
+ if (pk == GNUTLS_PK_UNKNOWN) {
+ gnutls_assert();
+ goto unsupp_algo;
+ }
+
+ /* use the digests algorithm */
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.digestAlgorithm.algorithm", idx + 1);
+
+ len = sizeof(oid) - 1;
+ ret = asn1_read_value(pkcs7->signed_data, root, oid, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto unsupp_algo;
+ }
+
+ ret = gnutls_oid_to_digest(oid);
+ if (ret == GNUTLS_DIG_UNKNOWN) {
+ gnutls_assert();
+ goto unsupp_algo;
+ }
+
+ sig = gnutls_pk_to_sign(pk, ret);
+ if (sig == GNUTLS_SIGN_UNKNOWN) {
+ gnutls_assert();
+ goto unsupp_algo;
+ }
+ }
+
+ info->algo = sig;
+
+ snprintf(root, sizeof(root), "signerInfos.?%u.signature", idx + 1);
+ /* read the signature */
+ ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &info->sig);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ /* read the issuer info */
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.sid.issuerAndSerialNumber.issuer.rdnSequence",
+ idx + 1);
+ /* read the signature */
+ ret =
+ _gnutls_x509_get_raw_field(pkcs7->signed_data, root,
+ &info->issuer_dn);
+ if (ret >= 0) {
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.sid.issuerAndSerialNumber.serialNumber",
+ idx + 1);
+ /* read the signature */
+ ret =
+ _gnutls_x509_read_value(pkcs7->signed_data, root,
+ &info->signer_serial);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ } else { /* keyid */
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.sid.subjectKeyIdentifier", idx + 1);
+ /* read the signature */
+ ret =
+ _gnutls_x509_read_value(pkcs7->signed_data, root,
+ &info->issuer_keyid);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+ }
+
+ if (info->issuer_keyid.data == NULL && info->issuer_dn.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ goto fail;
+ }
+
+ /* read the signing time */
+ for (i = 0;; i++) {
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.signedAttrs.?%u.type", idx + 1,
+ i + 1);
+ len = sizeof(oid) - 1;
+ ret = asn1_read_value(pkcs7->signed_data, root, oid, &len);
+ if (ret != ASN1_SUCCESS) {
+ break;
+ }
+
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.signedAttrs.?%u.values.?1", idx + 1,
+ i + 1);
+ ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &tmp);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ tmp.data = NULL;
+ tmp.size = 0;
+ } else if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_pkcs7_add_attr(&info->signed_attrs, oid, &tmp, 0);
+ gnutls_free(tmp.data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (strcmp(oid, ATTR_SIGNING_TIME) == 0) {
+ info->signing_time = parse_time(pkcs7, root);
+ }
+ }
+
+ /* read the unsigned attrs */
+ for (i = 0;; i++) {
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.unsignedAttrs.?%u.type", idx + 1,
+ i + 1);
+ len = sizeof(oid) - 1;
+ ret = asn1_read_value(pkcs7->signed_data, root, oid, &len);
+ if (ret != ASN1_SUCCESS) {
+ break;
+ }
+
+ snprintf(root, sizeof(root),
+ "signerInfos.?%u.unsignedAttrs.?%u.values.?1", idx + 1,
+ i + 1);
+ ret = _gnutls_x509_read_value(pkcs7->signed_data, root, &tmp);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ tmp.data = NULL;
+ tmp.size = 0;
+ } else if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ gnutls_pkcs7_add_attr(&info->unsigned_attrs, oid, &tmp, 0);
+ gnutls_free(tmp.data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ return 0;
+ fail:
+ gnutls_free(tmp.data);
+ gnutls_pkcs7_signature_info_deinit(info);
+ return ret;
+ unsupp_algo:
+ return GNUTLS_E_UNKNOWN_ALGORITHM;
+}
+
+/* Verifies that the hash attribute ATTR_MESSAGE_DIGEST is present
+ * and matches our calculated hash */
+static int verify_hash_attr(gnutls_pkcs7_t pkcs7, const char *root,
+ gnutls_sign_algorithm_t algo,
+ const gnutls_datum_t *data)
+{
+ unsigned hash;
+ gnutls_datum_t tmp = { NULL, 0 };
+ gnutls_datum_t tmp2 = { NULL, 0 };
+ uint8_t hash_output[MAX_HASH_SIZE];
+ unsigned hash_size, i;
+ char oid[MAX_OID_SIZE];
+ char name[256];
+ unsigned msg_digest_ok = 0;
+ unsigned num_cont_types = 0;
+ int ret;
+
+ hash = gnutls_sign_get_hash_algorithm(algo);
+
+ /* hash the data */
+ if (hash == GNUTLS_DIG_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ hash_size = gnutls_hash_get_len(hash);
+
+ if (data == NULL || data->data == NULL) {
+ data = &pkcs7->der_signed_data;
+ }
+
+ if (data->size == 0) {
+ return gnutls_assert_val(GNUTLS_E_NO_EMBEDDED_DATA);
+ }
+
+ ret = gnutls_hash_fast(hash, data->data, data->size, hash_output);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* now verify that hash matches */
+ for (i = 0;; i++) {
+ snprintf(name, sizeof(name), "%s.signedAttrs.?%u", root, i + 1);
+
+ ret = _gnutls_x509_decode_and_read_attribute(pkcs7->signed_data,
+ name, oid,
+ sizeof(oid), &tmp,
+ 1, 0);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ break;
+ return gnutls_assert_val(ret);
+ }
+
+ if (strcmp(oid, ATTR_MESSAGE_DIGEST) == 0) {
+ ret =
+ _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING,
+ tmp.data, tmp.size,
+ &tmp2, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (tmp2.size == hash_size
+ && memcmp(hash_output, tmp2.data, tmp2.size) == 0) {
+ msg_digest_ok = 1;
+ } else {
+ gnutls_assert();
+ }
+ } else if (strcmp(oid, ATTR_CONTENT_TYPE) == 0) {
+ if (num_cont_types > 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+
+ num_cont_types++;
+
+ /* check if it matches */
+ ret =
+ _gnutls_x509_get_raw_field(pkcs7->signed_data,
+ "encapContentInfo.eContentType",
+ &tmp2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (tmp2.size != tmp.size
+ || memcmp(tmp.data, tmp2.data, tmp2.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+ }
+
+ gnutls_free(tmp.data);
+ gnutls_free(tmp2.data);
+ }
+
+ if (msg_digest_ok)
+ ret = 0;
+ else
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+
+ cleanup:
+ gnutls_free(tmp.data);
+ gnutls_free(tmp2.data);
+ return ret;
+}
+
+/* Returns the data to be used for signature verification. PKCS #7
+ * decided that this should not be an easy task.
+ */
+static int figure_pkcs7_sigdata(gnutls_pkcs7_t pkcs7, const char *root,
+ const gnutls_datum_t * data,
+ gnutls_sign_algorithm_t algo,
+ gnutls_datum_t * sigdata)
+{
+ int ret;
+ char name[256];
+
+ snprintf(name, sizeof(name), "%s.signedAttrs", root);
+ /* read the signature */
+ ret = _gnutls_x509_get_raw_field(pkcs7->signed_data, name, sigdata);
+ if (ret == 0) {
+ /* verify that hash matches */
+ ret = verify_hash_attr(pkcs7, root, algo, data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (sigdata->size > 0)
+ sigdata->data[0] = 0x31;
+
+ return 0;
+ }
+
+ /* We have no signedAttrs. Use the provided data, or the encapsulated */
+ if (data == NULL || data->data == NULL) {
+ return _gnutls_set_datum(sigdata, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size);
+ }
+
+ return _gnutls_set_datum(sigdata, data->data, data->size);
+}
+
+/**
+ * gnutls_pkcs7_get_embedded_data:
+ * @pkcs7: should contain a gnutls_pkcs7_t type
+ * @flags: must be zero or %GNUTLS_PKCS7_EDATA_GET_RAW
+ * @data: will hold the embedded data in the provided structure
+ *
+ * This function will return the data embedded in the signature of
+ * the PKCS7 structure. If no data are available then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * The returned data must be de-allocated using gnutls_free().
+ *
+ * Note, that this function returns the exact same data that are
+ * authenticated. If the %GNUTLS_PKCS7_EDATA_GET_RAW flag is provided,
+ * the returned data will be including the wrapping tag/value as
+ * they are encoded in the structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.8
+ **/
+int
+gnutls_pkcs7_get_embedded_data(gnutls_pkcs7_t pkcs7, unsigned flags,
+ gnutls_datum_t *data)
+{
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if (pkcs7->der_signed_data.size == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (flags & GNUTLS_PKCS7_EDATA_GET_RAW) {
+ if (pkcs7->signed_data == NULL)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ return _gnutls_x509_read_value(pkcs7->signed_data, "encapContentInfo.eContent", data);
+ } else {
+ return _gnutls_set_datum(data, pkcs7->der_signed_data.data, pkcs7->der_signed_data.size);
+ }
+}
+
+/**
+ * gnutls_pkcs7_get_embedded_data_oid:
+ * @pkcs7: should contain a gnutls_pkcs7_t type
+ *
+ * This function will return the OID of the data embedded in the signature of
+ * the PKCS7 structure. If no data are available then %NULL will be
+ * returned. The returned value will be valid during the lifetime
+ * of the @pkcs7 structure.
+ *
+ * Returns: On success, a pointer to an OID string, %NULL on error.
+ *
+ * Since: 3.5.5
+ **/
+const char *
+gnutls_pkcs7_get_embedded_data_oid(gnutls_pkcs7_t pkcs7)
+{
+ if (pkcs7 == NULL || pkcs7->encap_data_oid[0] == 0)
+ return NULL;
+
+ return pkcs7->encap_data_oid;
+}
+
+/**
+ * gnutls_pkcs7_verify_direct:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @signer: the certificate believed to have signed the structure
+ * @idx: the index of the signature info to check
+ * @data: The data to be verified or %NULL
+ * @flags: Zero or an OR list of #gnutls_certificate_verify_flags
+ *
+ * This function will verify the provided data against the signature
+ * present in the SignedData of the PKCS #7 structure. If the data
+ * provided are NULL then the data in the encapsulatedContent field
+ * will be used instead.
+ *
+ * Note that, unlike gnutls_pkcs7_verify() this function does not
+ * verify the key purpose of the signer. It is expected for the caller
+ * to verify the intended purpose of the %signer -e.g., via gnutls_x509_crt_get_key_purpose_oid(),
+ * or gnutls_x509_crt_check_key_purpose().
+ *
+ * Note also, that since GnuTLS 3.5.6 this function introduces checks in the
+ * end certificate (@signer), including time checks and key usage checks.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. A verification error results to a
+ * %GNUTLS_E_PK_SIG_VERIFY_FAILED and the lack of encapsulated data
+ * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ *
+ * Since: 3.4.2
+ **/
+int gnutls_pkcs7_verify_direct(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_crt_t signer,
+ unsigned idx,
+ const gnutls_datum_t *data, unsigned flags)
+{
+ int count, ret;
+ gnutls_datum_t tmpdata = { NULL, 0 };
+ gnutls_pkcs7_signature_info_st info;
+ gnutls_datum_t sigdata = { NULL, 0 };
+ char root[128];
+
+ memset(&info, 0, sizeof(info));
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret =
+ asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count);
+ if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1);
+ ret = figure_pkcs7_sigdata(pkcs7, root, data, info.algo, &sigdata);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ gnutls_x509_crt_verify_data2(signer, info.algo, flags, &sigdata,
+ &info.sig);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ cleanup:
+ gnutls_free(tmpdata.data);
+ gnutls_free(sigdata.data);
+ gnutls_pkcs7_signature_info_deinit(&info);
+
+ return ret;
+}
+
+/* Finds the issuer of the given certificate (@cert) in the
+ * included in PKCS#7 list of certificates */
+static gnutls_x509_crt_t find_verified_issuer_of(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_crt_t cert,
+ const char *purpose,
+ unsigned vflags)
+{
+ gnutls_x509_crt_t issuer = NULL;
+ int ret, count;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned i, vtmp;
+
+ count = gnutls_pkcs7_get_crt_count(pkcs7);
+ if (count < 0) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ for (i = 0; i < (unsigned)count; i++) {
+ /* Try to find the signer in the appended list. */
+ ret = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_x509_crt_init(&issuer);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_x509_crt_import(issuer, &tmp, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (!gnutls_x509_crt_check_issuer(cert, issuer)) {
+ gnutls_assert();
+ goto skip;
+ }
+
+ ret = gnutls_x509_crt_verify(cert, &issuer, 1, vflags|GNUTLS_VERIFY_DO_NOT_ALLOW_SAME, &vtmp);
+ if (ret < 0 || vtmp != 0 ||
+ (purpose != NULL && !_gnutls_check_key_purpose(issuer, purpose, 0))) {
+ gnutls_assert(); /* maybe next one is trusted */
+ _gnutls_cert_log("failed verification with", issuer);
+ skip:
+ gnutls_x509_crt_deinit(issuer);
+ issuer = NULL;
+ gnutls_free(tmp.data);
+ continue;
+ }
+
+ _gnutls_cert_log("issued by", issuer);
+
+ /* we found a signer we trust. let's return it */
+ break;
+ }
+
+ if (issuer == NULL) {
+ gnutls_assert();
+ return NULL;
+ }
+ goto cleanup;
+
+ fail:
+ if (issuer) {
+ gnutls_x509_crt_deinit(issuer);
+ issuer = NULL;
+ }
+
+ cleanup:
+ gnutls_free(tmp.data);
+
+ return issuer;
+}
+
+/* Finds a certificate that is issued by @issuer -if given-, and matches
+ * either the serial number or the key ID (both in @info) .
+ */
+static gnutls_x509_crt_t find_child_of_with_serial(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_crt_t issuer,
+ const char *purpose,
+ gnutls_pkcs7_signature_info_st *info)
+{
+ gnutls_x509_crt_t crt = NULL;
+ int ret, count;
+ uint8_t tmp[128];
+ size_t tmp_size;
+ gnutls_datum_t tmpdata = { NULL, 0 };
+ unsigned i;
+
+ count = gnutls_pkcs7_get_crt_count(pkcs7);
+ if (count < 0) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ for (i = 0; i < (unsigned)count; i++) {
+ /* Try to find the crt in the appended list. */
+ ret = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &tmpdata);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_x509_crt_import(crt, &tmpdata, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (issuer != NULL) {
+ if (!gnutls_x509_crt_check_issuer(crt, issuer)) {
+ gnutls_assert();
+ goto skip;
+ }
+ }
+
+ if (purpose) {
+ ret =
+ _gnutls_check_key_purpose(crt, purpose, 0);
+ if (ret == 0) {
+ _gnutls_cert_log("key purpose unacceptable", crt);
+ goto skip;
+ }
+ }
+
+ if (info->signer_serial.size > 0) {
+ tmp_size = sizeof(tmp);
+ ret = gnutls_x509_crt_get_serial(crt, tmp, &tmp_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto skip;
+ }
+
+ if (tmp_size != info->signer_serial.size
+ || memcmp(info->signer_serial.data, tmp,
+ tmp_size) != 0) {
+ _gnutls_cert_log("doesn't match serial", crt);
+ gnutls_assert();
+ goto skip;
+ }
+ } else if (info->issuer_keyid.size > 0) {
+ tmp_size = sizeof(tmp);
+ ret = gnutls_x509_crt_get_subject_key_id(crt, tmp, &tmp_size, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto skip;
+ }
+
+ if (tmp_size != info->issuer_keyid.size
+ || memcmp(info->issuer_keyid.data, tmp,
+ tmp_size) != 0) {
+ _gnutls_cert_log("doesn't match key ID", crt);
+ gnutls_assert();
+ skip:
+ gnutls_x509_crt_deinit(crt);
+ crt = NULL;
+ gnutls_free(tmpdata.data);
+ continue;
+ }
+ } else {
+ gnutls_assert();
+ crt = NULL;
+ goto fail;
+ }
+
+ _gnutls_cert_log("signer is", crt);
+
+ /* we found the child with the given serial or key ID */
+ break;
+ }
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ goto cleanup;
+ fail:
+ if (crt) {
+ gnutls_x509_crt_deinit(crt);
+ crt = NULL;
+ }
+
+ cleanup:
+ gnutls_free(tmpdata.data);
+
+ return crt;
+}
+
+static
+gnutls_x509_crt_t find_signer(gnutls_pkcs7_t pkcs7, gnutls_x509_trust_list_t tl,
+ gnutls_typed_vdata_st * vdata,
+ unsigned vdata_size,
+ unsigned vflags,
+ gnutls_pkcs7_signature_info_st * info)
+{
+ gnutls_x509_crt_t issuer = NULL;
+ gnutls_x509_crt_t signer = NULL;
+ int ret;
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned i, vtmp;
+ const char *purpose = NULL;
+
+ if (info->issuer_keyid.data) {
+ ret =
+ gnutls_x509_trust_list_get_issuer_by_subject_key_id(tl,
+ NULL,
+ &info->
+ issuer_keyid,
+ &signer,
+ 0);
+ if (ret < 0) {
+ gnutls_assert();
+ signer = NULL;
+ }
+ }
+
+ /* get key purpose */
+ for (i = 0; i < vdata_size; i++) {
+ if (vdata[i].type == GNUTLS_DT_KEY_PURPOSE_OID) {
+ purpose = (char *)vdata[i].data;
+ break;
+ }
+ }
+
+ /* this will give us the issuer of the signer (wtf) */
+ if (info->issuer_dn.data && signer == NULL) {
+ ret =
+ gnutls_x509_trust_list_get_issuer_by_dn(tl,
+ &info->issuer_dn,
+ &issuer, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ signer = NULL;
+ }
+
+ if (issuer) {
+ /* try to find the actual signer in the list of
+ * certificates */
+ signer = find_child_of_with_serial(pkcs7, issuer, purpose, info);
+ if (signer == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ gnutls_x509_crt_deinit(issuer);
+ issuer = NULL;
+ }
+ }
+
+ if (signer == NULL) {
+ /* get the signer from the pkcs7 list; the one that matches serial
+ * or key ID */
+ signer = find_child_of_with_serial(pkcs7, NULL, purpose, info);
+ if (signer == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ /* if the signer cannot be verified from our trust list, make a chain of certificates
+ * starting from the identified signer, to a root we know. */
+ ret = gnutls_x509_trust_list_verify_crt2(tl, &signer, 1, vdata, vdata_size, vflags, &vtmp, NULL);
+ if (ret < 0 || vtmp != 0) {
+ gnutls_x509_crt_t prev = NULL;
+
+ issuer = signer;
+ /* construct a chain */
+ do {
+ if (prev && prev != signer) {
+ gnutls_x509_crt_deinit(prev);
+ }
+ prev = issuer;
+
+ issuer = find_verified_issuer_of(pkcs7, issuer, purpose, vflags);
+
+ if (issuer != NULL && gnutls_x509_crt_check_issuer(issuer, issuer)) {
+ if (prev && prev != signer)
+ gnutls_x509_crt_deinit(prev);
+ prev = issuer;
+ break;
+ }
+ } while(issuer != NULL);
+
+ issuer = prev; /* the last we have seen */
+
+ if (issuer == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_x509_trust_list_verify_crt2(tl, &issuer, 1, vdata, vdata_size, vflags, &vtmp, NULL);
+ if (ret < 0 || vtmp != 0) {
+ /* could not construct a valid chain */
+ _gnutls_reason_log("signer's chain failed trust list verification", vtmp);
+ gnutls_assert();
+ goto fail;
+ }
+ }
+ } else {
+ /* verify that the signer we got is trusted */
+ ret = gnutls_x509_trust_list_verify_crt2(tl, &signer, 1, vdata, vdata_size, vflags, &vtmp, NULL);
+ if (ret < 0 || vtmp != 0) {
+ /* could not construct a valid chain */
+ _gnutls_reason_log("signer failed trust list verification", vtmp);
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ if (signer == NULL) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ goto cleanup;
+
+ fail:
+ if (signer != NULL) {
+ if (issuer == signer)
+ issuer = NULL;
+ gnutls_x509_crt_deinit(signer);
+ signer = NULL;
+ }
+
+ cleanup:
+ if (issuer != NULL) {
+ gnutls_x509_crt_deinit(issuer);
+ issuer = NULL;
+ }
+ gnutls_free(tmp.data);
+
+ return signer;
+}
+
+/**
+ * gnutls_pkcs7_verify:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @tl: A list of trusted certificates
+ * @vdata: an array of typed data
+ * @vdata_size: the number of data elements
+ * @idx: the index of the signature info to check
+ * @data: The data to be verified or %NULL
+ * @flags: Zero or an OR list of #gnutls_certificate_verify_flags
+ *
+ * This function will verify the provided data against the signature
+ * present in the SignedData of the PKCS #7 structure. If the data
+ * provided are NULL then the data in the encapsulatedContent field
+ * will be used instead.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. A verification error results to a
+ * %GNUTLS_E_PK_SIG_VERIFY_FAILED and the lack of encapsulated data
+ * to verify to a %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE.
+ *
+ * Since: 3.4.2
+ **/
+int gnutls_pkcs7_verify(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_trust_list_t tl,
+ gnutls_typed_vdata_st *vdata,
+ unsigned int vdata_size,
+ unsigned idx,
+ const gnutls_datum_t *data, unsigned flags)
+{
+ int count, ret;
+ gnutls_datum_t tmpdata = { NULL, 0 };
+ gnutls_pkcs7_signature_info_st info;
+ gnutls_x509_crt_t signer;
+ gnutls_datum_t sigdata = { NULL, 0 };
+ char root[128];
+
+ memset(&info, 0, sizeof(info));
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret =
+ asn1_number_of_elements(pkcs7->signed_data, "signerInfos", &count);
+ if (ret != ASN1_SUCCESS || idx + 1 > (unsigned)count) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* read data */
+ ret = gnutls_pkcs7_get_signature_info(pkcs7, idx, &info);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ snprintf(root, sizeof(root), "signerInfos.?%u", idx + 1);
+ ret = figure_pkcs7_sigdata(pkcs7, root, data, info.algo, &sigdata);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ signer = find_signer(pkcs7, tl, vdata, vdata_size, flags, &info);
+ if (signer) {
+ ret =
+ gnutls_x509_crt_verify_data3(signer, info.algo, vdata, vdata_size,
+ &sigdata, &info.sig, flags);
+ if (ret < 0) {
+ _gnutls_cert_log("failed struct verification with", signer);
+ gnutls_assert();
+ }
+ gnutls_x509_crt_deinit(signer);
+ } else {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIG_VERIFY_FAILED;
+ }
+
+ cleanup:
+ gnutls_free(tmpdata.data);
+ gnutls_free(sigdata.data);
+ gnutls_pkcs7_signature_info_deinit(&info);
+
+ return ret;
+}
+
+static void disable_opt_fields(gnutls_pkcs7_t pkcs7)
+{
+ int result;
+ int count;
+
+ /* disable the optional fields */
+ result = asn1_number_of_elements(pkcs7->signed_data, "crls", &count);
+ if (result != ASN1_SUCCESS || count == 0) {
+ (void)asn1_write_value(pkcs7->signed_data, "crls", NULL, 0);
+ }
+
+ result =
+ asn1_number_of_elements(pkcs7->signed_data, "certificates", &count);
+ if (result != ASN1_SUCCESS || count == 0) {
+ (void)asn1_write_value(pkcs7->signed_data, "certificates", NULL, 0);
+ }
+
+ return;
+}
+
+static int reencode(gnutls_pkcs7_t pkcs7)
+{
+ int result;
+
+ if (pkcs7->signed_data != NULL) {
+ disable_opt_fields(pkcs7);
+
+ /* Replace the old content with the new
+ */
+ result =
+ _gnutls_x509_der_encode_and_copy(pkcs7->signed_data, "",
+ pkcs7->pkcs7, "content",
+ 0);
+ if (result < 0) {
+ return gnutls_assert_val(result);
+ }
+
+ /* Write the content type of the signed data
+ */
+ result =
+ asn1_write_value(pkcs7->pkcs7, "contentType",
+ SIGNED_DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ }
+ return 0;
+}
+
+/**
+ * gnutls_pkcs7_export:
+ * @pkcs7: The pkcs7 type
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a structure PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the pkcs7 structure to DER or PEM format.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER
+ * will be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN PKCS7".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_pkcs7_export(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ int ret;
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if ((ret = reencode(pkcs7)) < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_x509_export_int(pkcs7->pkcs7, format, PEM_PKCS7,
+ output_data, output_data_size);
+}
+
+/**
+ * gnutls_pkcs7_export2:
+ * @pkcs7: The pkcs7 type
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a structure PEM or DER encoded
+ *
+ * This function will export the pkcs7 structure to DER or PEM format.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN PKCS7".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_pkcs7_export2(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ int ret;
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if ((ret = reencode(pkcs7)) < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_x509_export_int2(pkcs7->pkcs7, format, PEM_PKCS7, out);
+}
+
+/* Creates an empty signed data structure in the pkcs7
+ * structure and returns a handle to the signed data.
+ */
+static int create_empty_signed_data(asn1_node pkcs7, asn1_node * sdata)
+{
+ int result;
+
+ *sdata = NULL;
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.pkcs-7-SignedData",
+ sdata)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Use version 1
+ */
+ result = asn1_write_value(*sdata, "version", &one, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Use no digest algorithms
+ */
+
+ /* id-data */
+ result =
+ asn1_write_value(*sdata, "encapContentInfo.eContentType",
+ DIGESTED_DATA_OID, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(*sdata, "encapContentInfo.eContent", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Add no certificates.
+ */
+
+ /* Add no crls.
+ */
+
+ /* Add no signerInfos.
+ */
+
+ return 0;
+
+ cleanup:
+ asn1_delete_structure(sdata);
+ return result;
+
+}
+
+/**
+ * gnutls_pkcs7_set_crt_raw:
+ * @pkcs7: The pkcs7 type
+ * @crt: the DER encoded certificate to be added
+ *
+ * This function will add a certificate to the PKCS7 or RFC2630
+ * certificate set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_set_crt_raw(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crt)
+{
+ int result;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* If the signed data are uninitialized
+ * then create them.
+ */
+ if (pkcs7->signed_data == NULL) {
+ /* The pkcs7 structure is new, so create the
+ * signedData.
+ */
+ result =
+ create_empty_signed_data(pkcs7->pkcs7, &pkcs7->signed_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* Step 2. Append the new certificate.
+ */
+
+ result = asn1_write_value(pkcs7->signed_data, "certificates", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data, "certificates.?LAST",
+ "certificate", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data,
+ "certificates.?LAST.certificate", crt->data,
+ crt->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ return result;
+}
+
+/**
+ * gnutls_pkcs7_set_crt:
+ * @pkcs7: The pkcs7 type
+ * @crt: the certificate to be copied.
+ *
+ * This function will add a parsed certificate to the PKCS7 or
+ * RFC2630 certificate set. This is a wrapper function over
+ * gnutls_pkcs7_set_crt_raw() .
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_set_crt(gnutls_pkcs7_t pkcs7, gnutls_x509_crt_t crt)
+{
+ int ret;
+ gnutls_datum_t data;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret = _gnutls_x509_der_encode(crt->cert, "", &data, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_pkcs7_set_crt_raw(pkcs7, &data);
+
+ _gnutls_free_datum(&data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs7_delete_crt:
+ * @pkcs7: The pkcs7 type
+ * @indx: the index of the certificate to delete
+ *
+ * This function will delete a certificate from a PKCS7 or RFC2630
+ * certificate set. Index starts from 0. Returns 0 on success.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_delete_crt(gnutls_pkcs7_t pkcs7, int indx)
+{
+ int result;
+ char root2[MAX_NAME_SIZE];
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* Step 2. Delete the certificate.
+ */
+
+ snprintf(root2, sizeof(root2), "certificates.?%d", indx + 1);
+
+ result = asn1_write_value(pkcs7->signed_data, root2, NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ return result;
+}
+
+/* Read and write CRLs
+ */
+
+/**
+ * gnutls_pkcs7_get_crl_raw2:
+ * @pkcs7: The pkcs7 type
+ * @indx: contains the index of the crl to extract
+ * @crl: will contain the contents of the CRL in an allocated buffer
+ *
+ * This function will return a DER encoded CRL of the PKCS7 or RFC2630 crl set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. After the last crl has been read
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Since: 3.4.2
+ **/
+int
+gnutls_pkcs7_get_crl_raw2(gnutls_pkcs7_t pkcs7,
+ unsigned indx, gnutls_datum_t * crl)
+{
+ int result;
+ char root2[MAX_NAME_SIZE];
+ gnutls_datum_t tmp = { NULL, 0 };
+ int start, end;
+
+ if (pkcs7 == NULL || crl == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ result = _gnutls_x509_read_value(pkcs7->pkcs7, "content", &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Step 2. Parse the CertificateSet
+ */
+
+ snprintf(root2, sizeof(root2), "crls.?%u", indx + 1);
+
+ /* Get the raw CRL
+ */
+ result =
+ asn1_der_decoding_startEnd(pkcs7->signed_data, tmp.data, tmp.size,
+ root2, &start, &end);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ end = end - start + 1;
+
+ result = _gnutls_set_datum(crl, &tmp.data[start], end);
+
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ return result;
+}
+
+/**
+ * gnutls_pkcs7_get_crl_raw:
+ * @pkcs7: The pkcs7 type
+ * @indx: contains the index of the crl to extract
+ * @crl: the contents of the crl will be copied there (may be null)
+ * @crl_size: should hold the size of the crl
+ *
+ * This function will return a crl of the PKCS7 or RFC2630 crl set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. If the provided buffer is not long enough,
+ * then @crl_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER is
+ * returned. After the last crl has been read
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ **/
+int
+gnutls_pkcs7_get_crl_raw(gnutls_pkcs7_t pkcs7,
+ unsigned indx, void *crl, size_t * crl_size)
+{
+ int ret;
+ gnutls_datum_t tmp = { NULL, 0 };
+
+ ret = gnutls_pkcs7_get_crl_raw2(pkcs7, indx, &tmp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if ((unsigned)tmp.size > *crl_size) {
+ *crl_size = tmp.size;
+ ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ goto cleanup;
+ }
+
+ assert(tmp.data != NULL);
+
+ *crl_size = tmp.size;
+ if (crl)
+ memcpy(crl, tmp.data, tmp.size);
+
+ cleanup:
+ _gnutls_free_datum(&tmp);
+ return ret;
+}
+
+/**
+ * gnutls_pkcs7_get_crl_count:
+ * @pkcs7: The pkcs7 type
+ *
+ * This function will return the number of certificates in the PKCS7
+ * or RFC2630 crl set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_get_crl_count(gnutls_pkcs7_t pkcs7)
+{
+ int result, count;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* Step 2. Count the CertificateSet */
+
+ result = asn1_number_of_elements(pkcs7->signed_data, "crls", &count);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return 0; /* no crls */
+ }
+
+ return count;
+
+}
+
+/**
+ * gnutls_pkcs7_set_crl_raw:
+ * @pkcs7: The pkcs7 type
+ * @crl: the DER encoded crl to be added
+ *
+ * This function will add a crl to the PKCS7 or RFC2630 crl set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_set_crl_raw(gnutls_pkcs7_t pkcs7, const gnutls_datum_t * crl)
+{
+ int result;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* If the signed data are uninitialized
+ * then create them.
+ */
+ if (pkcs7->signed_data == NULL) {
+ /* The pkcs7 structure is new, so create the
+ * signedData.
+ */
+ result =
+ create_empty_signed_data(pkcs7->pkcs7, &pkcs7->signed_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* Step 2. Append the new crl.
+ */
+
+ result = asn1_write_value(pkcs7->signed_data, "crls", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data, "crls.?LAST", crl->data,
+ crl->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ return result;
+}
+
+/**
+ * gnutls_pkcs7_set_crl:
+ * @pkcs7: The pkcs7 type
+ * @crl: the DER encoded crl to be added
+ *
+ * This function will add a parsed CRL to the PKCS7 or RFC2630 crl
+ * set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_set_crl(gnutls_pkcs7_t pkcs7, gnutls_x509_crl_t crl)
+{
+ int ret;
+ gnutls_datum_t data;
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret = _gnutls_x509_der_encode(crl->crl, "", &data, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_pkcs7_set_crl_raw(pkcs7, &data);
+
+ _gnutls_free_datum(&data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs7_delete_crl:
+ * @pkcs7: The pkcs7 type
+ * @indx: the index of the crl to delete
+ *
+ * This function will delete a crl from a PKCS7 or RFC2630 crl set.
+ * Index starts from 0. Returns 0 on success.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_pkcs7_delete_crl(gnutls_pkcs7_t pkcs7, int indx)
+{
+ int result;
+ char root2[MAX_NAME_SIZE];
+
+ if (pkcs7 == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ /* Delete the crl.
+ */
+
+ snprintf(root2, sizeof(root2), "crls.?%d", indx + 1);
+
+ result = asn1_write_value(pkcs7->signed_data, root2, NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ return result;
+}
+
+static int write_signer_id(asn1_node c2, const char *root,
+ gnutls_x509_crt_t signer, unsigned flags)
+{
+ int result;
+ size_t serial_size;
+ uint8_t serial[128];
+ char name[256];
+
+ if (flags & GNUTLS_PKCS7_WRITE_SPKI) {
+ const uint8_t ver = 3;
+
+ snprintf(name, sizeof(name), "%s.version", root);
+ result = asn1_write_value(c2, name, &ver, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.sid", root);
+ result = asn1_write_value(c2, name, "subjectKeyIdentifier", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ serial_size = sizeof(serial);
+ result =
+ gnutls_x509_crt_get_subject_key_id(signer, serial,
+ &serial_size, NULL);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ snprintf(name, sizeof(name), "%s.subjectKeyIdentifier", root);
+ result = asn1_write_value(c2, name, serial, serial_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ } else {
+ serial_size = sizeof(serial);
+ result =
+ gnutls_x509_crt_get_serial(signer, serial, &serial_size);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ snprintf(name, sizeof(name), "%s.sid", root);
+ result = asn1_write_value(c2, name, "issuerAndSerialNumber", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name),
+ "%s.sid.issuerAndSerialNumber.serialNumber", root);
+ result = asn1_write_value(c2, name, serial, serial_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name),
+ "%s.sid.issuerAndSerialNumber.issuer", root);
+ result =
+ asn1_copy_node(c2, name, signer->cert,
+ "tbsCertificate.issuer");
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ }
+
+ return 0;
+}
+
+static int add_attrs(asn1_node c2, const char *root, gnutls_pkcs7_attrs_t attrs,
+ unsigned already_set)
+{
+ char name[256];
+ gnutls_pkcs7_attrs_st *p = attrs;
+ int result;
+
+ if (attrs == NULL) {
+ /* if there are no other attributes delete that field */
+ if (already_set == 0)
+ (void)asn1_write_value(c2, root, NULL, 0);
+ } else {
+ while (p != NULL) {
+ result = asn1_write_value(c2, root, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.type", root);
+ result = asn1_write_value(c2, name, p->oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values", root);
+ result = asn1_write_value(c2, name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values.?1",
+ root);
+ result =
+ asn1_write_value(c2, name, p->data.data,
+ p->data.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ p = p->next;
+ }
+ }
+
+ return 0;
+}
+
+static int write_attributes(asn1_node c2, const char *root,
+ const gnutls_datum_t * data,
+ const mac_entry_st * me,
+ gnutls_pkcs7_attrs_t other_attrs, unsigned flags)
+{
+ char name[256];
+ int result, ret;
+ uint8_t digest[MAX_HASH_SIZE];
+ gnutls_datum_t tmp = { NULL, 0 };
+ unsigned digest_size;
+ unsigned already_set = 0;
+
+ if (flags & GNUTLS_PKCS7_INCLUDE_TIME) {
+ if (data == NULL || data->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Add time */
+ result = asn1_write_value(c2, root, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.type", root);
+ result = asn1_write_value(c2, name, ATTR_SIGNING_TIME, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values", root);
+ result = asn1_write_value(c2, name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values.?1", root);
+ ret = _gnutls_x509_set_raw_time(c2, name, gnutls_time(0));
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ already_set = 1;
+ }
+
+ ret = add_attrs(c2, root, other_attrs, already_set);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (already_set != 0 || other_attrs != NULL) {
+ /* Add content type */
+ result = asn1_write_value(c2, root, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.type", root);
+ result = asn1_write_value(c2, name, ATTR_CONTENT_TYPE, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values", root);
+ result = asn1_write_value(c2, name, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_get_raw_field(c2,
+ "encapContentInfo.eContentType",
+ &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST.values.?1", root);
+ result = asn1_write_value(c2, name, tmp.data, tmp.size);
+ gnutls_free(tmp.data);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ /* If we add any attribute we should add them all */
+ /* Add hash */
+ digest_size = _gnutls_hash_get_algo_len(me);
+ ret = gnutls_hash_fast(MAC_TO_DIG(me->id), data->data, data->size, digest);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ result = asn1_write_value(c2, root, "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "%s.?LAST", root);
+ ret =
+ _gnutls_x509_encode_and_write_attribute(ATTR_MESSAGE_DIGEST,
+ c2, name, digest,
+ digest_size, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pkcs7_sign:
+ * @pkcs7: should contain a #gnutls_pkcs7_t type
+ * @signer: the certificate to sign the structure
+ * @signer_key: the key to sign the structure
+ * @data: The data to be signed or %NULL if the data are already embedded
+ * @signed_attrs: Any additional attributes to be included in the signed ones (or %NULL)
+ * @unsigned_attrs: Any additional attributes to be included in the unsigned ones (or %NULL)
+ * @dig: The digest algorithm to use for signing
+ * @flags: Should be zero or one of %GNUTLS_PKCS7 flags
+ *
+ * This function will add a signature in the provided PKCS #7 structure
+ * for the provided data. Multiple signatures can be made with different
+ * signers.
+ *
+ * The available flags are:
+ * %GNUTLS_PKCS7_EMBED_DATA, %GNUTLS_PKCS7_INCLUDE_TIME, %GNUTLS_PKCS7_INCLUDE_CERT,
+ * and %GNUTLS_PKCS7_WRITE_SPKI. They are explained in the #gnutls_pkcs7_sign_flags
+ * definition.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.2
+ **/
+int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7,
+ gnutls_x509_crt_t signer,
+ gnutls_privkey_t signer_key,
+ const gnutls_datum_t *data,
+ gnutls_pkcs7_attrs_t signed_attrs,
+ gnutls_pkcs7_attrs_t unsigned_attrs,
+ gnutls_digest_algorithm_t dig, unsigned flags)
+{
+ int ret, result;
+ gnutls_datum_t sigdata = { NULL, 0 };
+ gnutls_datum_t signature = { NULL, 0 };
+ const mac_entry_st *me = hash_to_entry(dig);
+ unsigned pk, sigalgo;
+ gnutls_x509_spki_st key_params, params;
+ const gnutls_sign_entry_st *se;
+
+ if (pkcs7 == NULL || me == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if (pkcs7->signed_data == NULL) {
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-7-SignedData",
+ &pkcs7->signed_data);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (!(flags & GNUTLS_PKCS7_EMBED_DATA)) {
+ (void)asn1_write_value(pkcs7->signed_data,
+ "encapContentInfo.eContent", NULL, 0);
+ }
+ }
+
+ result = asn1_write_value(pkcs7->signed_data, "version", &one, 1);
+ if (result != ASN1_SUCCESS) {
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data,
+ "encapContentInfo.eContentType", DATA_OID,
+ 0);
+ if (result != ASN1_SUCCESS) {
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if ((flags & GNUTLS_PKCS7_EMBED_DATA) && data->data) { /* embed data */
+ ret =
+ _gnutls_x509_write_string(pkcs7->signed_data,
+ "encapContentInfo.eContent", data,
+ ASN1_ETYPE_OCTET_STRING);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ }
+
+ if (flags & GNUTLS_PKCS7_INCLUDE_CERT) {
+ ret = gnutls_pkcs7_set_crt(pkcs7, signer);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* append digest info algorithm */
+ result =
+ asn1_write_value(pkcs7->signed_data, "digestAlgorithms", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data,
+ "digestAlgorithms.?LAST.algorithm",
+ _gnutls_x509_digest_to_oid(me), 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ (void)asn1_write_value(pkcs7->signed_data,
+ "digestAlgorithms.?LAST.parameters", NULL, 0);
+
+ /* append signer's info */
+ result = asn1_write_value(pkcs7->signed_data, "signerInfos", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.version",
+ &one, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data,
+ "signerInfos.?LAST.digestAlgorithm.algorithm",
+ _gnutls_x509_digest_to_oid(me), 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ (void)asn1_write_value(pkcs7->signed_data,
+ "signerInfos.?LAST.digestAlgorithm.parameters", NULL,
+ 0);
+
+ ret =
+ write_signer_id(pkcs7->signed_data, "signerInfos.?LAST", signer,
+ flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ add_attrs(pkcs7->signed_data, "signerInfos.?LAST.unsignedAttrs",
+ unsigned_attrs, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ write_attributes(pkcs7->signed_data,
+ "signerInfos.?LAST.signedAttrs", data, me,
+ signed_attrs, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ disable_opt_fields(pkcs7);
+
+ /* write the signature algorithm */
+ pk = gnutls_x509_crt_get_pk_algorithm(signer, NULL);
+
+ ret = _gnutls_privkey_get_spki_params(signer_key, &key_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_crt_get_spki_params(signer, &key_params, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_privkey_update_spki_params(signer_key, pk, dig, 0,
+ &params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ se = _gnutls_pk_to_sign_entry(params.pk, dig);
+ if (se == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+ goto cleanup;
+ }
+
+ /* RFC5652 is silent on what the values would be and initially I assumed that
+ * typical signature algorithms should be set. However RFC2315 (PKCS#7) mentions
+ * that a generic RSA OID should be used. We switch to this "unexpected" value
+ * because some implementations cannot cope with the "expected" signature values.
+ */
+ params.legacy = 1;
+ ret =
+ _gnutls_x509_write_sign_params(pkcs7->signed_data,
+ "signerInfos.?LAST.signatureAlgorithm",
+ se, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ sigalgo = se->id;
+
+ /* sign the data */
+ ret =
+ figure_pkcs7_sigdata(pkcs7, "signerInfos.?LAST", data, sigalgo,
+ &sigdata);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ FIX_SIGN_PARAMS(params, flags, dig);
+
+ ret = privkey_sign_and_hash_data(signer_key, se,
+ &sigdata, &signature, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(pkcs7->signed_data, "signerInfos.?LAST.signature",
+ signature.data, signature.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(sigdata.data);
+ gnutls_free(signature.data);
+ return ret;
+}
diff --git a/lib/x509/pkcs7_int.h b/lib/x509/pkcs7_int.h
new file mode 100644
index 0000000..3d57124
--- /dev/null
+++ b/lib/x509/pkcs7_int.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_PKCS7_INT_H
+#define GNUTLS_LIB_X509_PKCS7_INT_H
+
+#include <gnutls/x509.h>
+
+/* PKCS #7
+ */
+#define DATA_OID "1.2.840.113549.1.7.1"
+#define ENC_DATA_OID "1.2.840.113549.1.7.6"
+
+#define SIGNED_DATA_OID "1.2.840.113549.1.7.2"
+#define DIGESTED_DATA_OID "1.2.840.113549.1.7.5"
+
+
+typedef enum schema_id {
+ PBES2_GENERIC=1, /* when the algorithm is unknown, temporal use when reading only */
+ PBES2_DES, /* the stuff in PKCS #5 */
+ PBES2_3DES,
+ PBES2_AES_128,
+ PBES2_AES_192,
+ PBES2_AES_256,
+ PBES2_GOST28147_89_TC26Z,
+ PBES2_GOST28147_89_CPA,
+ PBES2_GOST28147_89_CPB,
+ PBES2_GOST28147_89_CPC,
+ PBES2_GOST28147_89_CPD,
+ PKCS12_3DES_SHA1, /* the stuff in PKCS #12 */
+ PKCS12_ARCFOUR_SHA1,
+ PKCS12_RC2_40_SHA1,
+ PBES1_DES_MD5 /* openssl before 1.1.0 uses that by default */
+} schema_id;
+
+struct pkcs_cipher_schema_st {
+ unsigned int schema;
+ const char *name;
+ unsigned int flag;
+ unsigned int cipher;
+ unsigned pbes2;
+ const char *cipher_oid;
+ const char *write_oid;
+ const char *desc;
+ const char *iv_name;
+ unsigned decrypt_only;
+};
+
+const struct pkcs_cipher_schema_st *_gnutls_pkcs_schema_get(schema_id schema);
+
+struct pbe_enc_params {
+ gnutls_cipher_algorithm_t cipher;
+ uint8_t iv[MAX_CIPHER_BLOCK_SIZE];
+ int iv_size;
+ char pbes2_oid[MAX_OID_SIZE]; /* when reading params, the OID is stored for info purposes */
+};
+
+int
+_gnutls_decrypt_pbes1_des_md5_data(const char *password,
+ unsigned password_len,
+ const struct pbkdf2_params *kdf_params,
+ const struct pbe_enc_params *enc_params,
+ const gnutls_datum_t *encrypted_data,
+ gnutls_datum_t *decrypted_data);
+
+int _gnutls_check_pkcs_cipher_schema(const char *oid);
+
+int
+_gnutls_pkcs_raw_decrypt_data(schema_id schema, asn1_node pkcs8_asn,
+ const char *root, const char *password,
+ const struct pbkdf2_params *kdf_params,
+ const struct pbe_enc_params *enc_params,
+ gnutls_datum_t *decrypted_data);
+
+int
+_gnutls_pkcs_raw_encrypt_data(const gnutls_datum_t * plain,
+ const struct pbe_enc_params *enc_params,
+ const gnutls_datum_t * key, gnutls_datum_t * encrypted);
+
+int _gnutls_pkcs7_decrypt_data(const gnutls_datum_t * data,
+ const char *password, gnutls_datum_t * dec);
+
+int _gnutls_read_pbkdf1_params(const uint8_t * data, int data_size,
+ struct pbkdf2_params *kdf_params,
+ struct pbe_enc_params *enc_params);
+
+int
+_gnutls_read_pkcs_schema_params(schema_id * schema, const char *password,
+ const uint8_t * data, int data_size,
+ struct pbkdf2_params *kdf_params,
+ struct pbe_enc_params *enc_params);
+
+int
+_gnutls_pkcs_write_schema_params(schema_id schema, asn1_node pkcs8_asn,
+ const char *where,
+ const struct pbkdf2_params *kdf_params,
+ const struct pbe_enc_params *enc_params);
+
+int
+_gnutls_pkcs_generate_key(schema_id schema,
+ const char *password,
+ struct pbkdf2_params *kdf_params,
+ struct pbe_enc_params *enc_params, gnutls_datum_t * key);
+
+int _gnutls_pkcs_flags_to_schema(unsigned int flags);
+int _gnutls_pkcs7_encrypt_data(schema_id schema,
+ const gnutls_datum_t * data,
+ const char *password, gnutls_datum_t * enc);
+
+int
+_gnutls_pkcs7_data_enc_info(const gnutls_datum_t * data, const struct pkcs_cipher_schema_st **p,
+ struct pbkdf2_params *kdf_params, char **oid);
+
+#endif /* GNUTLS_LIB_X509_PKCS7_INT_H */
diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c
new file mode 100644
index 0000000..792a413
--- /dev/null
+++ b/lib/x509/privkey.c
@@ -0,0 +1,2388 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2012-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2015-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <tls-sig.h>
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <pk.h>
+#include <mpi.h>
+#include <ecc.h>
+#include <pin.h>
+
+/**
+ * gnutls_x509_privkey_init:
+ * @key: A pointer to the type to be initialized
+ *
+ * This function will initialize a private key type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_privkey_init(gnutls_x509_privkey_t * key)
+{
+ FAIL_IF_LIB_ERROR;
+
+ *key = gnutls_calloc(1, sizeof(gnutls_x509_privkey_int));
+
+ if (*key) {
+ (*key)->key = NULL;
+ return 0; /* success */
+ }
+
+ return GNUTLS_E_MEMORY_ERROR;
+}
+
+void _gnutls_x509_privkey_reinit(gnutls_x509_privkey_t key)
+{
+ gnutls_pk_params_clear(&key->params);
+ gnutls_pk_params_release(&key->params);
+ /* avoid re-use of fields which may have had some sensible value */
+ zeroize_key(&key->params, sizeof(key->params));
+
+ if (key->key)
+ asn1_delete_structure2(&key->key, ASN1_DELETE_FLAG_ZEROIZE);
+ key->key = NULL;
+}
+
+/**
+ * gnutls_x509_privkey_deinit:
+ * @key: The key to be deinitialized
+ *
+ * This function will deinitialize a private key structure.
+ **/
+void gnutls_x509_privkey_deinit(gnutls_x509_privkey_t key)
+{
+ if (!key)
+ return;
+
+ _gnutls_x509_privkey_reinit(key);
+ gnutls_free(key);
+}
+
+/**
+ * gnutls_x509_privkey_cpy:
+ * @dst: The destination key, which should be initialized.
+ * @src: The source key
+ *
+ * This function will copy a private key from source to destination
+ * key. Destination has to be initialized.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_cpy(gnutls_x509_privkey_t dst,
+ gnutls_x509_privkey_t src)
+{
+ int ret;
+
+ if (!src || !dst)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret = _gnutls_pk_params_copy(&dst->params, &src->params);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret =
+ _gnutls_asn1_encode_privkey(&dst->key,
+ &dst->params);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_pk_params_release(&dst->params);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Converts an RSA PKCS#1 key to
+ * an internal structure (gnutls_private_key)
+ */
+asn1_node
+_gnutls_privkey_decode_pkcs1_rsa_key(const gnutls_datum_t * raw_key,
+ gnutls_x509_privkey_t pkey)
+{
+ int result;
+ asn1_node pkey_asn;
+
+ gnutls_pk_params_init(&pkey->params);
+
+ if (asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.RSAPrivateKey",
+ &pkey_asn) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ result =
+ _asn1_strict_der_decode(&pkey_asn, raw_key->data, raw_key->size,
+ NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (_gnutls_x509_read_int(pkey_asn, "modulus",
+ &pkey->params.params[0]) < 0)
+ {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_int(pkey_asn, "publicExponent",
+ &pkey->params.params[1]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(pkey_asn, "privateExponent",
+ &pkey->params.params[2]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(pkey_asn, "prime1",
+ &pkey->params.params[3]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(pkey_asn, "prime2",
+ &pkey->params.params[4]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(pkey_asn, "coefficient",
+ &pkey->params.params[5]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(pkey_asn, "exponent1",
+ &pkey->params.params[6]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(pkey_asn, "exponent2",
+ &pkey->params.params[7]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ pkey->params.params_nr = RSA_PRIVATE_PARAMS;
+ pkey->params.algo = GNUTLS_PK_RSA;
+
+ return pkey_asn;
+
+ error:
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ gnutls_pk_params_clear(&pkey->params);
+ gnutls_pk_params_release(&pkey->params);
+ return NULL;
+}
+
+/* Converts an ECC key to
+ * an internal structure (gnutls_private_key)
+ */
+int
+_gnutls_privkey_decode_ecc_key(asn1_node* pkey_asn, const gnutls_datum_t * raw_key,
+ gnutls_x509_privkey_t pkey, gnutls_ecc_curve_t curve)
+{
+ int ret;
+ unsigned int version;
+ char oid[MAX_OID_SIZE];
+ int oid_size;
+ gnutls_datum_t out;
+
+ if (curve_is_eddsa(curve)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ gnutls_pk_params_init(&pkey->params);
+
+ if ((ret =
+ asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.ECPrivateKey",
+ pkey_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret =
+ _asn1_strict_der_decode(pkey_asn, raw_key->data, raw_key->size,
+ NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto error;
+ }
+
+ ret = _gnutls_x509_read_uint(*pkey_asn, "Version", &version);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (version != 1) {
+ _gnutls_debug_log
+ ("ECC private key version %u is not supported\n",
+ version);
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ goto error;
+ }
+
+ /* read the curve */
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
+ oid_size = sizeof(oid);
+ ret =
+ asn1_read_value(*pkey_asn, "parameters.namedCurve", oid,
+ &oid_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto error;
+ }
+
+ pkey->params.curve = gnutls_oid_to_ecc_curve(oid);
+
+ if (pkey->params.curve == GNUTLS_ECC_CURVE_INVALID) {
+ _gnutls_debug_log("Curve %s is not supported\n", oid);
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ goto error;
+ }
+ } else {
+ pkey->params.curve = curve;
+ }
+
+
+ /* read the public key */
+ ret = _gnutls_x509_read_value(*pkey_asn, "publicKey", &out);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret =
+ _gnutls_ecc_ansi_x962_import(out.data, out.size,
+ &pkey->params.params[ECC_X],
+ &pkey->params.params[ECC_Y]);
+
+ _gnutls_free_datum(&out);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr += 2;
+
+ /* read the private key */
+ ret =
+ _gnutls_x509_read_key_int(*pkey_asn, "privateKey",
+ &pkey->params.params[ECC_K]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+ pkey->params.algo = GNUTLS_PK_EC;
+
+ return 0;
+
+ error:
+ asn1_delete_structure2(pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ gnutls_pk_params_clear(&pkey->params);
+ gnutls_pk_params_release(&pkey->params);
+ return ret;
+
+}
+
+
+static asn1_node
+decode_dsa_key(const gnutls_datum_t * raw_key, gnutls_x509_privkey_t pkey)
+{
+ int result;
+ asn1_node dsa_asn;
+ gnutls_datum_t seed = {NULL,0};
+ char oid[MAX_OID_SIZE];
+ int oid_size;
+
+ if (asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.DSAPrivateKey",
+ &dsa_asn) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ gnutls_pk_params_init(&pkey->params);
+
+
+ result =
+ _asn1_strict_der_decode(&dsa_asn, raw_key->data, raw_key->size,
+ NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (_gnutls_x509_read_int(dsa_asn, "p",
+ &pkey->params.params[0]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_int(dsa_asn, "q",
+ &pkey->params.params[1]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_int(dsa_asn, "g",
+ &pkey->params.params[2]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_int(dsa_asn, "Y",
+ &pkey->params.params[3]) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ if (_gnutls_x509_read_key_int(dsa_asn, "priv",
+ &pkey->params.params[4]) < 0)
+ {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+ pkey->params.algo = GNUTLS_PK_DSA;
+
+ oid_size = sizeof(oid);
+ result = asn1_read_value(dsa_asn, "seed.algorithm", oid, &oid_size);
+ if (result == ASN1_SUCCESS) {
+ pkey->params.palgo = gnutls_oid_to_digest(oid);
+
+ result = _gnutls_x509_read_value(dsa_asn, "seed.seed", &seed);
+ if (result == ASN1_SUCCESS) {
+ if (seed.size <= sizeof(pkey->params.seed)) {
+ memcpy(pkey->params.seed, seed.data, seed.size);
+ pkey->params.seed_size = seed.size;
+ }
+ gnutls_free(seed.data);
+ }
+ }
+
+ return dsa_asn;
+
+ error:
+ asn1_delete_structure2(&dsa_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ gnutls_pk_params_clear(&pkey->params);
+ gnutls_pk_params_release(&pkey->params);
+ return NULL;
+
+}
+
+
+#define PEM_KEY_DSA "DSA PRIVATE KEY"
+#define PEM_KEY_RSA "RSA PRIVATE KEY"
+#define PEM_KEY_ECC "EC PRIVATE KEY"
+#define PEM_KEY_PKCS8 "PRIVATE KEY"
+
+#define MAX_PEM_HEADER_SIZE 25
+
+#define IF_CHECK_FOR(pemstr, _algo, cptr, bptr, size, key) \
+ if (left > sizeof(pemstr) && memcmp(cptr, pemstr, sizeof(pemstr)-1) == 0) { \
+ result = _gnutls_fbase64_decode(pemstr, bptr, size, &_data); \
+ if (result >= 0) \
+ key->params.algo = _algo; \
+ }
+
+/**
+ * gnutls_x509_privkey_import:
+ * @key: The data to store the parsed key
+ * @data: The DER or PEM encoded certificate.
+ * @format: One of DER or PEM
+ *
+ * This function will convert the given DER or PEM encoded key to the
+ * native #gnutls_x509_privkey_t format. The output will be stored in
+ * @key .
+ *
+ * If the key is PEM encoded it should have a header that contains "PRIVATE
+ * KEY". Note that this function falls back to PKCS #8 decoding without
+ * password, if the default format fails to import.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format)
+{
+ int result = 0, need_free = 0;
+ gnutls_datum_t _data;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ key->params.algo = GNUTLS_PK_UNKNOWN;
+
+ /* If the Certificate is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ unsigned left;
+ char *ptr;
+ uint8_t *begin_ptr;
+
+ ptr = memmem(data->data, data->size, "PRIVATE KEY-----", sizeof("PRIVATE KEY-----")-1);
+
+ result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ if (ptr != NULL) {
+ left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data);
+
+ if (data->size - left > MAX_PEM_HEADER_SIZE) {
+ ptr -= MAX_PEM_HEADER_SIZE;
+ left += MAX_PEM_HEADER_SIZE;
+ } else {
+ ptr = (char*)data->data;
+ left = data->size;
+ }
+
+ ptr = memmem(ptr, left, "-----BEGIN ", sizeof("-----BEGIN ")-1);
+ if (ptr != NULL) {
+ begin_ptr = (uint8_t*)ptr;
+ left = data->size - ((ptrdiff_t)begin_ptr - (ptrdiff_t)data->data);
+
+ ptr += sizeof("-----BEGIN ")-1;
+
+ IF_CHECK_FOR(PEM_KEY_RSA, GNUTLS_PK_RSA, ptr, begin_ptr, left, key)
+ else IF_CHECK_FOR(PEM_KEY_ECC, GNUTLS_PK_EC, ptr, begin_ptr, left, key)
+ else IF_CHECK_FOR(PEM_KEY_DSA, GNUTLS_PK_DSA, ptr, begin_ptr, left, key)
+
+ if (key->params.algo == GNUTLS_PK_UNKNOWN && left >= sizeof(PEM_KEY_PKCS8)) {
+ if (memcmp(ptr, PEM_KEY_PKCS8, sizeof(PEM_KEY_PKCS8)-1) == 0) {
+ result =
+ _gnutls_fbase64_decode(PEM_KEY_PKCS8,
+ begin_ptr, left, &_data);
+ if (result >= 0) {
+ /* signal for PKCS #8 keys */
+ key->params.algo = -1;
+ }
+ }
+ }
+ }
+
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ need_free = 1;
+ }
+
+ if (key->expanded) {
+ _gnutls_x509_privkey_reinit(key);
+ }
+ key->expanded = 1;
+
+ if (key->params.algo == (gnutls_pk_algorithm_t)-1) {
+ result =
+ gnutls_x509_privkey_import_pkcs8(key, data, format,
+ NULL,
+ GNUTLS_PKCS_PLAIN);
+ if (result < 0) {
+ gnutls_assert();
+ key->key = NULL;
+ goto cleanup;
+ } else {
+ /* some keys under PKCS#8 don't set key->key */
+ goto finish;
+ }
+ } else if (key->params.algo == GNUTLS_PK_RSA) {
+ key->key =
+ _gnutls_privkey_decode_pkcs1_rsa_key(&_data, key);
+ if (key->key == NULL)
+ gnutls_assert();
+ } else if (key->params.algo == GNUTLS_PK_DSA) {
+ key->key = decode_dsa_key(&_data, key);
+ if (key->key == NULL)
+ gnutls_assert();
+ } else if (key->params.algo == GNUTLS_PK_EC) {
+ result = _gnutls_privkey_decode_ecc_key(&key->key, &_data, key, 0);
+ if (result < 0) {
+ gnutls_assert();
+ key->key = NULL;
+ }
+ } else {
+ /* Try decoding each of the keys, and accept the one that
+ * succeeds.
+ */
+ key->params.algo = GNUTLS_PK_RSA;
+ key->key =
+ _gnutls_privkey_decode_pkcs1_rsa_key(&_data, key);
+
+ if (key->key == NULL) {
+ key->params.algo = GNUTLS_PK_DSA;
+ key->key = decode_dsa_key(&_data, key);
+ if (key->key == NULL) {
+ key->params.algo = GNUTLS_PK_EC;
+ result =
+ _gnutls_privkey_decode_ecc_key(&key->key, &_data, key, 0);
+ if (result < 0) {
+ result =
+ gnutls_x509_privkey_import_pkcs8(key, data, format,
+ NULL,
+ GNUTLS_PKCS_PLAIN);
+ if (result >= 0) {
+ /* there are keys (ed25519) which leave key->key NULL */
+ goto finish;
+ }
+
+ /* result < 0 */
+ gnutls_assert();
+ key->key = NULL;
+
+ if (result == GNUTLS_E_PK_INVALID_PRIVKEY)
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ if (key->key == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_ASN1_DER_ERROR;
+ goto cleanup;
+ }
+
+ finish:
+ result =
+ _gnutls_pk_fixup(key->params.algo, GNUTLS_IMPORT, &key->params);
+ if (result < 0) {
+ gnutls_assert();
+ }
+
+ cleanup:
+ if (need_free) {
+ zeroize_temp_key(_data.data, _data.size);
+ _gnutls_free_datum(&_data);
+ }
+
+ /* The key has now been decoded.
+ */
+
+ return result;
+}
+
+static int import_pkcs12_privkey(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ const char *password, unsigned int flags)
+{
+ int ret;
+ gnutls_pkcs12_t p12;
+ gnutls_x509_privkey_t newkey;
+
+ ret = gnutls_pkcs12_init(&p12);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pkcs12_import(p12, data, format, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ gnutls_pkcs12_simple_parse(p12, password, &newkey, NULL, NULL,
+ NULL, NULL, NULL, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = gnutls_x509_privkey_cpy(key, newkey);
+ gnutls_x509_privkey_deinit(newkey);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = 0;
+ fail:
+
+ gnutls_pkcs12_deinit(p12);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_import2:
+ * @key: The data to store the parsed key
+ * @data: The DER or PEM encoded key.
+ * @format: One of DER or PEM
+ * @password: A password (optional)
+ * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t
+ *
+ * This function will import the given DER or PEM encoded key, to
+ * the native #gnutls_x509_privkey_t format, irrespective of the
+ * input format. The input format is auto-detected.
+ *
+ * The supported formats are basic unencrypted key, PKCS8, PKCS12,
+ * and the openssl format.
+ *
+ * If the provided key is encrypted but no password was given, then
+ * %GNUTLS_E_DECRYPTION_FAILED is returned. Since GnuTLS 3.4.0 this
+ * function will utilize the PIN callbacks if any.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import2(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ const char *password, unsigned int flags)
+{
+ int ret = 0;
+ int saved_ret = GNUTLS_E_PARSING_ERROR;
+ char pin[GNUTLS_PKCS11_MAX_PIN_LEN];
+ unsigned head_enc = 1;
+
+ if (format == GNUTLS_X509_FMT_PEM) {
+ size_t left;
+ char *ptr;
+
+ ptr = memmem(data->data, data->size, "PRIVATE KEY-----", sizeof("PRIVATE KEY-----")-1);
+
+ if (ptr != NULL) {
+ left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data);
+
+ if (data->size - left > 15) {
+ ptr -= 15;
+ left += 15;
+ } else {
+ ptr = (char*)data->data;
+ left = data->size;
+ }
+
+ ptr = memmem(ptr, left, "-----BEGIN ", sizeof("-----BEGIN ")-1);
+ if (ptr != NULL) {
+ ptr += sizeof("-----BEGIN ")-1;
+ left = data->size - ((ptrdiff_t)ptr - (ptrdiff_t)data->data);
+ }
+
+ if (ptr != NULL && left > sizeof(PEM_KEY_RSA)) {
+ if (memcmp(ptr, PEM_KEY_RSA, sizeof(PEM_KEY_RSA)-1) == 0 ||
+ memcmp(ptr, PEM_KEY_ECC, sizeof(PEM_KEY_ECC)-1) == 0 ||
+ memcmp(ptr, PEM_KEY_DSA, sizeof(PEM_KEY_DSA)-1) == 0) {
+ head_enc = 0;
+ }
+ }
+ }
+ }
+
+ if (head_enc == 0 || (password == NULL && !(flags & GNUTLS_PKCS_NULL_PASSWORD))) {
+ ret = gnutls_x509_privkey_import(key, data, format);
+ if (ret >= 0)
+ return ret;
+
+ gnutls_assert();
+ saved_ret = ret;
+ /* fall through to PKCS #8 decoding */
+ }
+
+ if ((password != NULL || (flags & GNUTLS_PKCS_NULL_PASSWORD))
+ || ret < 0) {
+
+ ret =
+ gnutls_x509_privkey_import_pkcs8(key, data, format,
+ password, flags);
+
+ if (ret == GNUTLS_E_DECRYPTION_FAILED &&
+ password == NULL && (!(flags & GNUTLS_PKCS_PLAIN))) {
+ /* use the callback if any */
+ ret = _gnutls_retrieve_pin(&key->pin, "key:", "", 0, pin, sizeof(pin));
+ if (ret == 0) {
+ password = pin;
+ }
+
+ ret =
+ gnutls_x509_privkey_import_pkcs8(key, data, format,
+ password, flags);
+ }
+
+ if (saved_ret == GNUTLS_E_PARSING_ERROR)
+ saved_ret = ret;
+
+ if (ret < 0) {
+ if (ret == GNUTLS_E_DECRYPTION_FAILED)
+ goto cleanup;
+ ret =
+ import_pkcs12_privkey(key, data, format,
+ password, flags);
+ if (ret < 0 && format == GNUTLS_X509_FMT_PEM) {
+ if (ret == GNUTLS_E_DECRYPTION_FAILED)
+ goto cleanup;
+
+ ret =
+ gnutls_x509_privkey_import_openssl(key,
+ data,
+ password);
+
+ if (ret == GNUTLS_E_DECRYPTION_FAILED && password == NULL &&
+ (key->pin.cb || _gnutls_pin_func)) {
+ /* use the callback if any */
+ memset(pin, 0, GNUTLS_PKCS11_MAX_PIN_LEN);
+ ret = _gnutls_retrieve_pin(&key->pin, "key:", "", 0, pin, sizeof(pin));
+ if (ret == 0) {
+ ret = gnutls_x509_privkey_import_openssl(key, data, pin);
+ }
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret == GNUTLS_E_PARSING_ERROR)
+ ret = saved_ret;
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_privkey_import_rsa_raw:
+ * @key: The data to store the parsed key
+ * @m: holds the modulus
+ * @e: holds the public exponent
+ * @d: holds the private exponent
+ * @p: holds the first prime (p)
+ * @q: holds the second prime (q)
+ * @u: holds the coefficient
+ *
+ * This function will convert the given RSA raw parameters to the
+ * native #gnutls_x509_privkey_t format. The output will be stored in
+ * @key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import_rsa_raw(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * m,
+ const gnutls_datum_t * e,
+ const gnutls_datum_t * d,
+ const gnutls_datum_t * p,
+ const gnutls_datum_t * q,
+ const gnutls_datum_t * u)
+{
+ return gnutls_x509_privkey_import_rsa_raw2(key, m, e, d, p, q, u,
+ NULL, NULL);
+}
+
+/**
+ * gnutls_x509_privkey_import_rsa_raw2:
+ * @key: The data to store the parsed key
+ * @m: holds the modulus
+ * @e: holds the public exponent
+ * @d: holds the private exponent
+ * @p: holds the first prime (p)
+ * @q: holds the second prime (q)
+ * @u: holds the coefficient (optional)
+ * @e1: holds e1 = d mod (p-1) (optional)
+ * @e2: holds e2 = d mod (q-1) (optional)
+ *
+ * This function will convert the given RSA raw parameters to the
+ * native #gnutls_x509_privkey_t format. The output will be stored in
+ * @key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import_rsa_raw2(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * m,
+ const gnutls_datum_t * e,
+ const gnutls_datum_t * d,
+ const gnutls_datum_t * p,
+ const gnutls_datum_t * q,
+ const gnutls_datum_t * u,
+ const gnutls_datum_t * e1,
+ const gnutls_datum_t * e2)
+{
+ int ret;
+ size_t siz = 0;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ gnutls_pk_params_init(&key->params);
+
+ siz = m->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_MODULUS], m->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ siz = e->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PUB], e->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ if (d) {
+ siz = d->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PRIV], d->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+ }
+
+ siz = p->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PRIME1], p->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ siz = q->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_PRIME2], q->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ if (u) {
+ siz = u->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[RSA_COEF], u->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+ }
+
+ if (e1 && e2) {
+ siz = e1->size;
+ if (_gnutls_mpi_init_scan_nz
+ (&key->params.params[RSA_E1], e1->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ siz = e2->size;
+ if (_gnutls_mpi_init_scan_nz
+ (&key->params.params[RSA_E2], e2->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+ }
+
+ key->params.algo = GNUTLS_PK_RSA;
+
+ ret = _gnutls_pk_fixup(GNUTLS_PK_RSA, GNUTLS_IMPORT, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ key->params.params_nr = RSA_PRIVATE_PARAMS;
+ key->params.algo = GNUTLS_PK_RSA;
+
+ ret =
+ _gnutls_asn1_encode_privkey(&key->key,
+ &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ gnutls_pk_params_clear(&key->params);
+ gnutls_pk_params_release(&key->params);
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_privkey_import_dsa_raw:
+ * @key: The data to store the parsed key
+ * @p: holds the p
+ * @q: holds the q
+ * @g: holds the g
+ * @y: holds the y (optional)
+ * @x: holds the x
+ *
+ * This function will convert the given DSA raw parameters to the
+ * native #gnutls_x509_privkey_t format. The output will be stored
+ * in @key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import_dsa_raw(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * p,
+ const gnutls_datum_t * q,
+ const gnutls_datum_t * g,
+ const gnutls_datum_t * y,
+ const gnutls_datum_t * x)
+{
+ int ret;
+ size_t siz = 0;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ gnutls_pk_params_init(&key->params);
+
+ siz = p->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[0], p->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+
+ siz = q->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[1], q->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+
+ siz = g->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[2], g->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+
+ if (y) {
+ siz = y->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[3], y->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ }
+
+ siz = x->size;
+ if (_gnutls_mpi_init_scan_nz(&key->params.params[4], x->data, siz)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_pk_fixup(GNUTLS_PK_DSA, GNUTLS_IMPORT, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ key->params.algo = GNUTLS_PK_DSA;
+ key->params.params_nr = DSA_PRIVATE_PARAMS;
+
+ ret =
+ _gnutls_asn1_encode_privkey(&key->key,
+ &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ gnutls_pk_params_clear(&key->params);
+ gnutls_pk_params_release(&key->params);
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_privkey_import_ecc_raw:
+ * @key: The data to store the parsed key
+ * @curve: holds the curve
+ * @x: holds the x-coordinate
+ * @y: holds the y-coordinate
+ * @k: holds the k
+ *
+ * This function will convert the given elliptic curve parameters to the
+ * native #gnutls_x509_privkey_t format. The output will be stored
+ * in @key. For EdDSA keys, the @x and @k values must be in the
+ * native to curve format.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key,
+ gnutls_ecc_curve_t curve,
+ const gnutls_datum_t * x,
+ const gnutls_datum_t * y,
+ const gnutls_datum_t * k)
+{
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ gnutls_pk_params_init(&key->params);
+
+ key->params.curve = curve;
+
+ if (curve_is_eddsa(curve)) {
+ unsigned size;
+ switch (curve) {
+ case GNUTLS_ECC_CURVE_ED25519:
+ key->params.algo = GNUTLS_PK_EDDSA_ED25519;
+ break;
+ case GNUTLS_ECC_CURVE_ED448:
+ key->params.algo = GNUTLS_PK_EDDSA_ED448;
+ break;
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
+
+ size = gnutls_ecc_curve_get_size(curve);
+ if (x->size != size || k->size != size) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ ret = _gnutls_set_datum(&key->params.raw_pub, x->data, x->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_set_datum(&key->params.raw_priv, k->data, k->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+ }
+
+ if (_gnutls_mpi_init_scan_nz
+ (&key->params.params[ECC_X], x->data, x->size)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ if (_gnutls_mpi_init_scan_nz
+ (&key->params.params[ECC_Y], y->data, y->size)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ if (_gnutls_mpi_init_scan_nz
+ (&key->params.params[ECC_K], k->data, k->size)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ key->params.algo = GNUTLS_PK_EC;
+
+ ret =
+ _gnutls_pk_fixup(GNUTLS_PK_EC, GNUTLS_IMPORT, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_asn1_encode_privkey(&key->key,
+ &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ gnutls_pk_params_clear(&key->params);
+ gnutls_pk_params_release(&key->params);
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_privkey_import_gost_raw:
+ * @key: The data to store the parsed key
+ * @curve: holds the curve
+ * @digest: will hold the digest
+ * @paramset: will hold the GOST parameter set ID
+ * @x: holds the x-coordinate
+ * @y: holds the y-coordinate
+ * @k: holds the k (private key)
+ *
+ * This function will convert the given GOST private key's parameters to the
+ * native #gnutls_x509_privkey_t format. The output will be stored
+ * in @key. @digest should be one of GNUTLS_DIG_GOSR_94,
+ * GNUTLS_DIG_STREEBOG_256 or GNUTLS_DIG_STREEBOG_512. If @paramset is set to
+ * GNUTLS_GOST_PARAMSET_UNKNOWN default one will be selected depending on
+ * @digest.
+ *
+ * Note: parameters should be stored with least significant byte first. On
+ * version 3.6.3 big-endian format was used incorrectly.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_x509_privkey_import_gost_raw(gnutls_x509_privkey_t key,
+ gnutls_ecc_curve_t curve,
+ gnutls_digest_algorithm_t digest,
+ gnutls_gost_paramset_t paramset,
+ const gnutls_datum_t * x,
+ const gnutls_datum_t * y,
+ const gnutls_datum_t * k)
+{
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ key->params.curve = curve;
+ key->params.algo = _gnutls_digest_gost(digest);
+
+ if (paramset == GNUTLS_GOST_PARAMSET_UNKNOWN)
+ paramset = _gnutls_gost_paramset_default(key->params.algo);
+
+ key->params.gost_params = paramset;
+
+ if (_gnutls_mpi_init_scan_le
+ (&key->params.params[GOST_X], x->data, x->size)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ if (_gnutls_mpi_init_scan_le
+ (&key->params.params[GOST_Y], y->data, y->size)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ if (_gnutls_mpi_init_scan_le
+ (&key->params.params[GOST_K], k->data, k->size)) {
+ gnutls_assert();
+ ret = GNUTLS_E_MPI_SCAN_FAILED;
+ goto cleanup;
+ }
+ key->params.params_nr++;
+
+ ret =
+ _gnutls_pk_fixup(key->params.algo, GNUTLS_IMPORT, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ gnutls_pk_params_clear(&key->params);
+ gnutls_pk_params_release(&key->params);
+ return ret;
+
+}
+
+
+/**
+ * gnutls_x509_privkey_get_pk_algorithm:
+ * @key: should contain a #gnutls_x509_privkey_t type
+ *
+ * This function will return the public key algorithm of a private
+ * key.
+ *
+ * Returns: a member of the #gnutls_pk_algorithm_t enumeration on
+ * success, or a negative error code on error.
+ **/
+int gnutls_x509_privkey_get_pk_algorithm(gnutls_x509_privkey_t key)
+{
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return key->params.algo;
+}
+
+/**
+ * gnutls_x509_privkey_get_pk_algorithm2:
+ * @key: should contain a #gnutls_x509_privkey_t type
+ * @bits: The number of bits in the public key algorithm
+ *
+ * This function will return the public key algorithm of a private
+ * key.
+ *
+ * Returns: a member of the #gnutls_pk_algorithm_t enumeration on
+ * success, or a negative error code on error.
+ **/
+int
+gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t key,
+ unsigned int *bits)
+{
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (bits) {
+ ret = pubkey_to_bits(&key->params);
+ if (ret < 0)
+ ret = 0;
+ *bits = ret;
+ }
+
+ return key->params.algo;
+}
+
+void
+_gnutls_x509_privkey_get_spki_params(gnutls_x509_privkey_t key,
+ gnutls_x509_spki_st *params)
+{
+ memcpy(params, &key->params.spki, sizeof(gnutls_x509_spki_st));
+}
+
+/**
+ * gnutls_x509_privkey_get_spki:
+ * @key: should contain a #gnutls_x509_privkey_t type
+ * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t
+ * @flags: must be zero
+ *
+ * This function will return the public key information of a private
+ * key. The provided @spki must be initialized.
+ *
+ * Returns: Zero on success, or a negative error code on error.
+ **/
+int
+gnutls_x509_privkey_get_spki(gnutls_x509_privkey_t key, gnutls_x509_spki_t spki, unsigned int flags)
+{
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (key->params.spki.pk == GNUTLS_PK_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ _gnutls_x509_privkey_get_spki_params(key, spki);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_privkey_set_spki:
+ * @key: should contain a #gnutls_x509_privkey_t type
+ * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t
+ * @flags: must be zero
+ *
+ * This function will return the public key information of a private
+ * key. The provided @spki must be initialized.
+ *
+ * Returns: Zero on success, or a negative error code on error.
+ **/
+int
+gnutls_x509_privkey_set_spki(gnutls_x509_privkey_t key, const gnutls_x509_spki_t spki, unsigned int flags)
+{
+ gnutls_pk_params_st tparams;
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (!_gnutls_pk_are_compat(key->params.algo, spki->pk))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ memcpy(&tparams, &key->params, sizeof(gnutls_pk_params_st));
+ memcpy(&tparams.spki, spki, sizeof (gnutls_x509_spki_st));
+ ret = _gnutls_x509_check_pubkey_params(&tparams);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ memcpy(&key->params.spki, spki, sizeof (gnutls_x509_spki_st));
+
+ key->params.algo = spki->pk;
+
+ return 0;
+}
+
+static const char *set_msg(gnutls_x509_privkey_t key)
+{
+ if (GNUTLS_PK_IS_RSA(key->params.algo)) {
+ return PEM_KEY_RSA;
+ } else if (key->params.algo == GNUTLS_PK_DSA) {
+ return PEM_KEY_DSA;
+ } else if (key->params.algo == GNUTLS_PK_EC)
+ return PEM_KEY_ECC;
+ else
+ return "UNKNOWN";
+}
+
+/**
+ * gnutls_x509_privkey_export:
+ * @key: Holds the key
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a private key PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the private key to a PKCS#1 structure for
+ * RSA or RSA-PSS keys, and integer sequence for DSA keys. Other keys types
+ * will be exported in PKCS#8 form.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN RSA PRIVATE KEY".
+ *
+ * It is recommended to use gnutls_x509_privkey_export_pkcs8() instead
+ * of this function, when a consistent output format is required.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_export(gnutls_x509_privkey_t key,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ gnutls_datum_t out;
+ int ret;
+
+ ret = gnutls_x509_privkey_export2(key, format, &out);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (format == GNUTLS_X509_FMT_PEM)
+ ret = _gnutls_copy_string(&out, output_data, output_data_size);
+ else
+ ret = _gnutls_copy_data(&out, output_data, output_data_size);
+ gnutls_free(out.data);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_export2:
+ * @key: Holds the key
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a private key PEM or DER encoded
+ *
+ * This function will export the private key to a PKCS#1 structure for
+ * RSA or RSA-PSS keys, and integer sequence for DSA keys. Other keys types
+ * will be exported in PKCS#8 form.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * It is recommended to use gnutls_x509_privkey_export2_pkcs8() instead
+ * of this function, when a consistent output format is required.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since 3.1.3
+ **/
+int
+gnutls_x509_privkey_export2(gnutls_x509_privkey_t key,
+ gnutls_x509_crt_fmt_t format,
+ gnutls_datum_t * out)
+{
+ const char *msg;
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (key->key == NULL) { /* can only export in PKCS#8 form */
+ return gnutls_x509_privkey_export2_pkcs8(key, format, NULL, 0, out);
+ }
+
+ msg = set_msg(key);
+
+ if (key->flags & GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT) {
+ ret = gnutls_x509_privkey_fix(key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ return _gnutls_x509_export_int2(key->key, format, msg, out);
+}
+
+/**
+ * gnutls_x509_privkey_sec_param:
+ * @key: a key
+ *
+ * This function will return the security parameter appropriate with
+ * this private key.
+ *
+ * Returns: On success, a valid security parameter is returned otherwise
+ * %GNUTLS_SEC_PARAM_UNKNOWN is returned.
+ *
+ * Since: 2.12.0
+ **/
+gnutls_sec_param_t gnutls_x509_privkey_sec_param(gnutls_x509_privkey_t key)
+{
+ int bits;
+
+ bits = pubkey_to_bits(&key->params);
+ if (bits <= 0)
+ return GNUTLS_SEC_PARAM_UNKNOWN;
+
+ return gnutls_pk_bits_to_sec_param(key->params.algo, bits);
+}
+
+/**
+ * gnutls_x509_privkey_export_ecc_raw:
+ * @key: a key
+ * @curve: will hold the curve
+ * @x: will hold the x-coordinate
+ * @y: will hold the y-coordinate
+ * @k: will hold the private key
+ *
+ * This function will export the ECC private key's parameters found
+ * in the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * In EdDSA curves the @y parameter will be %NULL and the other parameters
+ * will be in the native format for the curve.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0
+ **/
+int gnutls_x509_privkey_export_ecc_raw(gnutls_x509_privkey_t key,
+ gnutls_ecc_curve_t *curve,
+ gnutls_datum_t *x,
+ gnutls_datum_t *y,
+ gnutls_datum_t *k)
+{
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_params_get_ecc_raw(&key->params, curve, x, y, k, 0);
+}
+
+/**
+ * gnutls_x509_privkey_export_gost_raw:
+ * @key: a key
+ * @curve: will hold the curve
+ * @digest: will hold the digest
+ * @paramset: will hold the GOST parameter set ID
+ * @x: will hold the x-coordinate
+ * @y: will hold the y-coordinate
+ * @k: will hold the private key
+ *
+ * This function will export the GOST private key's parameters found
+ * in the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Note: parameters will be stored with least significant byte first. On
+ * version 3.6.3 this was incorrectly returned in big-endian format.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int gnutls_x509_privkey_export_gost_raw(gnutls_x509_privkey_t key,
+ gnutls_ecc_curve_t * curve,
+ gnutls_digest_algorithm_t * digest,
+ gnutls_gost_paramset_t * paramset,
+ gnutls_datum_t * x,
+ gnutls_datum_t * y,
+ gnutls_datum_t * k)
+{
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_params_get_gost_raw(&key->params, curve, digest, paramset,
+ x, y, k, 0);
+}
+
+/**
+ * gnutls_x509_privkey_export_rsa_raw:
+ * @key: a key
+ * @m: will hold the modulus
+ * @e: will hold the public exponent
+ * @d: will hold the private exponent
+ * @p: will hold the first prime (p)
+ * @q: will hold the second prime (q)
+ * @u: will hold the coefficient
+ *
+ * This function will export the RSA private key's parameters found
+ * in the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_export_rsa_raw(gnutls_x509_privkey_t key,
+ gnutls_datum_t * m, gnutls_datum_t * e,
+ gnutls_datum_t * d, gnutls_datum_t * p,
+ gnutls_datum_t * q, gnutls_datum_t * u)
+{
+ return _gnutls_params_get_rsa_raw(&key->params, m, e, d, p, q, u, NULL, NULL, 0);
+}
+
+/**
+ * gnutls_x509_privkey_export_rsa_raw2:
+ * @key: a key
+ * @m: will hold the modulus
+ * @e: will hold the public exponent
+ * @d: will hold the private exponent
+ * @p: will hold the first prime (p)
+ * @q: will hold the second prime (q)
+ * @u: will hold the coefficient
+ * @e1: will hold e1 = d mod (p-1)
+ * @e2: will hold e2 = d mod (q-1)
+ *
+ * This function will export the RSA private key's parameters found
+ * in the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_x509_privkey_export_rsa_raw2(gnutls_x509_privkey_t key,
+ gnutls_datum_t * m, gnutls_datum_t * e,
+ gnutls_datum_t * d, gnutls_datum_t * p,
+ gnutls_datum_t * q, gnutls_datum_t * u,
+ gnutls_datum_t * e1,
+ gnutls_datum_t * e2)
+{
+ return _gnutls_params_get_rsa_raw(&key->params, m, e, d, p, q, u, e1, e2, 0);
+}
+
+/**
+ * gnutls_x509_privkey_export_dsa_raw:
+ * @key: a key
+ * @p: will hold the p
+ * @q: will hold the q
+ * @g: will hold the g
+ * @y: will hold the y
+ * @x: will hold the x
+ *
+ * This function will export the DSA private key's parameters found
+ * in the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_export_dsa_raw(gnutls_x509_privkey_t key,
+ gnutls_datum_t * p, gnutls_datum_t * q,
+ gnutls_datum_t * g, gnutls_datum_t * y,
+ gnutls_datum_t * x)
+{
+ return _gnutls_params_get_dsa_raw(&key->params, p, q, g, y, x, 0);
+}
+
+/**
+ * gnutls_x509_privkey_generate:
+ * @key: an initialized key
+ * @algo: is one of the algorithms in #gnutls_pk_algorithm_t.
+ * @bits: the size of the parameters to generate
+ * @flags: Must be zero or flags from #gnutls_privkey_flags_t.
+ *
+ * This function will generate a random private key. Note that this
+ * function must be called on an initialized private key.
+ *
+ * The flag %GNUTLS_PRIVKEY_FLAG_PROVABLE
+ * instructs the key generation process to use algorithms like Shawe-Taylor
+ * (from FIPS PUB186-4) which generate provable parameters out of a seed
+ * for RSA and DSA keys. See gnutls_x509_privkey_generate2() for more
+ * information.
+ *
+ * Note that when generating an elliptic curve key, the curve
+ * can be substituted in the place of the bits parameter using the
+ * GNUTLS_CURVE_TO_BITS() macro. The input to the macro is any curve from
+ * %gnutls_ecc_curve_t.
+ *
+ * For DSA keys, if the subgroup size needs to be specified check
+ * the GNUTLS_SUBGROUP_TO_BITS() macro.
+ *
+ * It is recommended to do not set the number of @bits directly, use gnutls_sec_param_to_pk_bits() instead .
+ *
+ * See also gnutls_privkey_generate(), gnutls_x509_privkey_generate2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_generate(gnutls_x509_privkey_t key,
+ gnutls_pk_algorithm_t algo, unsigned int bits,
+ unsigned int flags)
+{
+ return gnutls_x509_privkey_generate2(key, algo, bits, flags, NULL, 0);
+}
+
+/**
+ * gnutls_x509_privkey_generate2:
+ * @key: a key
+ * @algo: is one of the algorithms in #gnutls_pk_algorithm_t.
+ * @bits: the size of the modulus
+ * @flags: Must be zero or flags from #gnutls_privkey_flags_t.
+ * @data: Allow specifying %gnutls_keygen_data_st types such as the seed to be used.
+ * @data_size: The number of @data available.
+ *
+ * This function will generate a random private key. Note that this
+ * function must be called on an initialized private key.
+ *
+ * The flag %GNUTLS_PRIVKEY_FLAG_PROVABLE
+ * instructs the key generation process to use algorithms like Shawe-Taylor
+ * (from FIPS PUB186-4) which generate provable parameters out of a seed
+ * for RSA and DSA keys. On DSA keys the PQG parameters are generated using the
+ * seed, while on RSA the two primes. To specify an explicit seed
+ * (by default a random seed is used), use the @data with a %GNUTLS_KEYGEN_SEED
+ * type.
+ *
+ * Note that when generating an elliptic curve key, the curve
+ * can be substituted in the place of the bits parameter using the
+ * GNUTLS_CURVE_TO_BITS() macro.
+ *
+ * To export the generated keys in memory or in files it is recommended to use the
+ * PKCS#8 form as it can handle all key types, and can store additional parameters
+ * such as the seed, in case of provable RSA or DSA keys.
+ * Generated keys can be exported in memory using gnutls_privkey_export_x509(),
+ * and then with gnutls_x509_privkey_export2_pkcs8().
+ *
+ * If key generation is part of your application, avoid setting the number
+ * of bits directly, and instead use gnutls_sec_param_to_pk_bits().
+ * That way the generated keys will adapt to the security levels
+ * of the underlying GnuTLS library.
+ *
+ * See also gnutls_privkey_generate2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_generate2(gnutls_x509_privkey_t key,
+ gnutls_pk_algorithm_t algo, unsigned int bits,
+ unsigned int flags, const gnutls_keygen_data_st *data, unsigned data_size)
+{
+ int ret;
+ unsigned i;
+ gnutls_x509_spki_t tpki = NULL;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ gnutls_pk_params_init(&key->params);
+
+ for (i=0;i<data_size;i++) {
+ if (data[i].type == GNUTLS_KEYGEN_SEED && data[i].size < sizeof(key->params.seed)) {
+ key->params.seed_size = data[i].size;
+ memcpy(key->params.seed, data[i].data, data[i].size);
+ } else if (data[i].type == GNUTLS_KEYGEN_DIGEST) {
+ key->params.palgo = data[i].size;
+ } else if (data[i].type == GNUTLS_KEYGEN_SPKI) {
+ tpki = (void*)data[i].data;
+ }
+ }
+
+ if (IS_EC(algo)) {
+ if (GNUTLS_BITS_ARE_CURVE(bits))
+ bits = GNUTLS_BITS_TO_CURVE(bits);
+ else
+ bits = _gnutls_ecc_bits_to_curve(algo, bits);
+
+ if (gnutls_ecc_curve_get_pk(bits) != algo) {
+ _gnutls_debug_log("curve is incompatible with public key algorithm\n");
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ }
+
+ if (IS_GOSTEC(algo)) {
+ int size;
+
+ if (GNUTLS_BITS_ARE_CURVE(bits))
+ bits = GNUTLS_BITS_TO_CURVE(bits);
+ else
+ bits = _gnutls_ecc_bits_to_curve(algo, bits);
+
+ size = gnutls_ecc_curve_get_size(bits);
+
+ if ((algo == GNUTLS_PK_GOST_01 && size != 32) ||
+ (algo == GNUTLS_PK_GOST_12_256 && size != 32) ||
+ (algo == GNUTLS_PK_GOST_12_512 && size != 64)) {
+ _gnutls_debug_log("curve is incompatible with public key algorithm\n");
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ key->params.gost_params = _gnutls_gost_paramset_default(algo);
+ }
+
+ if (flags & GNUTLS_PRIVKEY_FLAG_PROVABLE) {
+ key->params.pkflags |= GNUTLS_PK_FLAG_PROVABLE;
+ }
+
+ key->params.algo = algo;
+
+ ret = _gnutls_pk_generate_params(algo, bits, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (algo == GNUTLS_PK_RSA_PSS && (flags & GNUTLS_PRIVKEY_FLAG_CA) &&
+ !key->params.spki.pk) {
+ const mac_entry_st *me;
+ key->params.spki.pk = GNUTLS_PK_RSA_PSS;
+
+ key->params.spki.rsa_pss_dig = _gnutls_pk_bits_to_sha_hash(bits);
+
+ me = hash_to_entry(key->params.spki.rsa_pss_dig);
+ if (unlikely(me == NULL)) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ ret = _gnutls_find_rsa_pss_salt_size(bits, me, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ key->params.spki.salt_size = ret;
+ }
+
+ ret = _gnutls_pk_generate_keys(algo, bits, &key->params, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_pk_verify_priv_params(algo, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (tpki) {
+ ret = gnutls_x509_privkey_set_spki(key, tpki, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = _gnutls_asn1_encode_privkey(&key->key, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ key->params.algo = GNUTLS_PK_UNKNOWN;
+ gnutls_pk_params_clear(&key->params);
+ gnutls_pk_params_release(&key->params);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_get_seed:
+ * @key: should contain a #gnutls_x509_privkey_t type
+ * @digest: if non-NULL it will contain the digest algorithm used for key generation (if applicable)
+ * @seed: where seed will be copied to
+ * @seed_size: originally holds the size of @seed, will be updated with actual size
+ *
+ * This function will return the seed that was used to generate the
+ * given private key. That function will succeed only if the key was generated
+ * as a provable key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_privkey_get_seed(gnutls_x509_privkey_t key, gnutls_digest_algorithm_t *digest, void *seed, size_t *seed_size)
+{
+ if (key->params.seed_size == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (seed_size == NULL || seed == NULL) {
+ if (key->params.seed_size)
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ else
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if (*seed_size < key->params.seed_size) {
+ *seed_size = key->params.seed_size;
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ }
+
+ if (digest)
+ *digest = key->params.palgo;
+
+ memcpy(seed, key->params.seed, key->params.seed_size);
+ *seed_size = key->params.seed_size;
+ return 0;
+}
+
+static
+int cmp_rsa_key(gnutls_x509_privkey_t key1, gnutls_x509_privkey_t key2)
+{
+ gnutls_datum_t m1 = {NULL, 0}, e1 = {NULL, 0}, d1 = {NULL, 0}, p1 = {NULL, 0}, q1 = {NULL, 0};
+ gnutls_datum_t m2 = {NULL, 0}, e2 = {NULL, 0}, d2 = {NULL, 0}, p2 = {NULL, 0}, q2 = {NULL, 0};
+ int ret;
+
+ ret = gnutls_x509_privkey_export_rsa_raw(key1, &m1, &e1, &d1, &p1, &q1, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_x509_privkey_export_rsa_raw(key2, &m2, &e2, &d2, &p2, &q2, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (m1.size != m2.size || memcmp(m1.data, m2.data, m1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ if (d1.size != d2.size || memcmp(d1.data, d2.data, d1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ if (e1.size != e2.size || memcmp(e1.data, e2.data, e1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ if (p1.size != p2.size || memcmp(p1.data, p2.data, p1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ if (q1.size != q2.size || memcmp(q1.data, q2.data, q1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(m1.data);
+ gnutls_free(e1.data);
+ gnutls_free(d1.data);
+ gnutls_free(p1.data);
+ gnutls_free(q1.data);
+ gnutls_free(m2.data);
+ gnutls_free(e2.data);
+ gnutls_free(d2.data);
+ gnutls_free(p2.data);
+ gnutls_free(q2.data);
+ return ret;
+}
+
+static
+int cmp_dsa_key(gnutls_x509_privkey_t key1, gnutls_x509_privkey_t key2)
+{
+ gnutls_datum_t p1 = {NULL, 0}, q1 = {NULL, 0}, g1 = {NULL, 0};
+ gnutls_datum_t p2 = {NULL, 0}, q2 = {NULL, 0}, g2 = {NULL, 0};
+ int ret;
+
+ ret = gnutls_x509_privkey_export_dsa_raw(key1, &p1, &q1, &g1, NULL, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_x509_privkey_export_dsa_raw(key2, &p2, &q2, &g2, NULL, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (g1.size != g2.size || memcmp(g1.data, g2.data, g1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ if (p1.size != p2.size || memcmp(p1.data, p2.data, p1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ if (q1.size != q2.size || memcmp(q1.data, q2.data, q1.size) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PRIVKEY_VERIFICATION_ERROR;
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(g1.data);
+ gnutls_free(p1.data);
+ gnutls_free(q1.data);
+ gnutls_free(g2.data);
+ gnutls_free(p2.data);
+ gnutls_free(q2.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_verify_seed:
+ * @key: should contain a #gnutls_x509_privkey_t type
+ * @digest: it contains the digest algorithm used for key generation (if applicable)
+ * @seed: the seed of the key to be checked with
+ * @seed_size: holds the size of @seed
+ *
+ * This function will verify that the given private key was generated from
+ * the provided seed. If @seed is %NULL then the seed stored in the @key's structure
+ * will be used for verification.
+ *
+ * Returns: In case of a verification failure %GNUTLS_E_PRIVKEY_VERIFICATION_ERROR
+ * is returned, and zero or positive code on success.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_privkey_verify_seed(gnutls_x509_privkey_t key, gnutls_digest_algorithm_t digest, const void *seed, size_t seed_size)
+{
+ int ret;
+ gnutls_x509_privkey_t okey;
+ unsigned bits;
+ gnutls_keygen_data_st data;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (key->params.algo != GNUTLS_PK_RSA && key->params.algo != GNUTLS_PK_DSA)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ ret = gnutls_x509_privkey_get_pk_algorithm2(key, &bits);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_x509_privkey_init(&okey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (seed == NULL) {
+ seed = key->params.seed;
+ seed_size = key->params.seed_size;
+ }
+
+ if (seed == NULL || seed_size == 0)
+ return gnutls_assert_val(GNUTLS_E_PK_NO_VALIDATION_PARAMS);
+
+ data.type = GNUTLS_KEYGEN_SEED;
+ data.data = (void*)seed;
+ data.size = seed_size;
+
+ ret = gnutls_x509_privkey_generate2(okey, key->params.algo, bits,
+ GNUTLS_PRIVKEY_FLAG_PROVABLE, &data, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (key->params.algo == GNUTLS_PK_RSA)
+ ret = cmp_rsa_key(key, okey);
+ else
+ ret = cmp_dsa_key(key, okey);
+
+ cleanup:
+ gnutls_x509_privkey_deinit(okey);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_verify_params:
+ * @key: a key
+ *
+ * This function will verify the private key parameters.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_privkey_verify_params(gnutls_x509_privkey_t key)
+{
+ int ret;
+
+ ret = _gnutls_pk_verify_priv_params(key->params.algo, &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_privkey_get_key_id:
+ * @key: a key
+ * @flags: should be one of the flags from %gnutls_keyid_flags_t
+ * @output_data: will contain the key ID
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will return a unique ID that depends on the public key
+ * parameters. This ID can be used in checking whether a certificate
+ * corresponds to the given key.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will
+ * be returned. The output will normally be a SHA-1 hash output,
+ * which is 20 bytes.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_get_key_id(gnutls_x509_privkey_t key,
+ unsigned int flags,
+ unsigned char *output_data,
+ size_t * output_data_size)
+{
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_get_key_id(&key->params,
+ output_data, output_data_size, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_privkey_sign_hash:
+ * @key: a key
+ * @hash: holds the data to be signed
+ * @signature: will contain newly allocated signature
+ *
+ * This function will sign the given hash using the private key. Do not
+ * use this function directly unless you know what it is. Typical signing
+ * requires the data to be hashed and stored in special formats
+ * (e.g. BER Digest-Info for RSA).
+ *
+ * This API is provided only for backwards compatibility, and thus
+ * restricted to RSA, DSA and ECDSA key types. For other key types please
+ * use gnutls_privkey_sign_hash() and gnutls_privkey_sign_data().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Deprecated in: 2.12.0
+ */
+int
+gnutls_x509_privkey_sign_hash(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * hash,
+ gnutls_datum_t * signature)
+{
+ int result;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (key->params.algo != GNUTLS_PK_RSA && key->params.algo != GNUTLS_PK_ECDSA &&
+ key->params.algo != GNUTLS_PK_DSA) {
+ /* too primitive API - use only with legacy types */
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ _gnutls_pk_sign(key->params.algo, signature, hash,
+ &key->params, &key->params.spki);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_privkey_sign_data:
+ * @key: a key
+ * @digest: should be a digest algorithm
+ * @flags: should be 0 for now
+ * @data: holds the data to be signed
+ * @signature: will contain the signature
+ * @signature_size: holds the size of signature (and will be replaced
+ * by the new size)
+ *
+ * This function will sign the given data using a signature algorithm
+ * supported by the private key. Signature algorithms are always used
+ * together with a hash functions. Different hash functions may be
+ * used for the RSA algorithm, but only SHA-1 for the DSA keys.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@signature_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will
+ * be returned.
+ *
+ * Use gnutls_x509_crt_get_preferred_hash_algorithm() to determine
+ * the hash algorithm.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ */
+int
+gnutls_x509_privkey_sign_data(gnutls_x509_privkey_t key,
+ gnutls_digest_algorithm_t digest,
+ unsigned int flags,
+ const gnutls_datum_t * data,
+ void *signature, size_t * signature_size)
+{
+ gnutls_privkey_t privkey;
+ gnutls_datum_t sig = {NULL, 0};
+ int ret;
+
+ ret = gnutls_privkey_init(&privkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_privkey_import_x509(privkey, key, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_privkey_sign_data(privkey, digest, flags, data, &sig);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (*signature_size < sig.size) {
+ *signature_size = sig.size;
+ ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ goto cleanup;
+ }
+
+ *signature_size = sig.size;
+ memcpy(signature, sig.data, sig.size);
+
+cleanup:
+ _gnutls_free_datum(&sig);
+ gnutls_privkey_deinit(privkey);
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_fix:
+ * @key: a key
+ *
+ * This function will recalculate the secondary parameters in a key.
+ * In RSA keys, this can be the coefficient and exponent1,2.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_privkey_fix(gnutls_x509_privkey_t key)
+{
+ int ret;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (key->key) {
+ asn1_delete_structure2(&key->key, ASN1_DELETE_FLAG_ZEROIZE);
+
+ ret =
+ _gnutls_asn1_encode_privkey(&key->key,
+ &key->params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_privkey_set_pin_function:
+ * @privkey: The certificate structure
+ * @fn: the callback
+ * @userdata: data associated with the callback
+ *
+ * This function will set a callback function to be used when
+ * it is required to access a protected object. This function overrides
+ * the global function set using gnutls_pkcs11_set_pin_function().
+ *
+ * Note that this callback is used when decrypting a key.
+ *
+ * Since: 3.4.0
+ *
+ **/
+void gnutls_x509_privkey_set_pin_function(gnutls_x509_privkey_t privkey,
+ gnutls_pin_callback_t fn,
+ void *userdata)
+{
+ privkey->pin.cb = fn;
+ privkey->pin.data = userdata;
+}
+
+/**
+ * gnutls_x509_privkey_set_flags:
+ * @key: A key of type #gnutls_x509_privkey_t
+ * @flags: flags from the %gnutls_privkey_flags
+ *
+ * This function will set flags for the specified private key, after
+ * it is generated. Currently this is useful for the %GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT
+ * to allow exporting a "provable" private key in backwards compatible way.
+ *
+ * Since: 3.5.0
+ *
+ **/
+void gnutls_x509_privkey_set_flags(gnutls_x509_privkey_t key,
+ unsigned int flags)
+{
+ key->flags |= flags;
+}
+
diff --git a/lib/x509/privkey_openssl.c b/lib/x509/privkey_openssl.c
new file mode 100644
index 0000000..9fc70e0
--- /dev/null
+++ b/lib/x509/privkey_openssl.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2012 Free Software Foundation, Inc.
+ *
+ * Author: David Woodhouse
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include <algorithms.h>
+#include <num.h>
+#include <random.h>
+
+static int
+openssl_hash_password(const char *_password, gnutls_datum_t * key,
+ gnutls_datum_t * salt)
+{
+ unsigned char md5[16];
+ digest_hd_st hd;
+ unsigned int count = 0;
+ int ret;
+ char *password = NULL;
+
+ if (_password != NULL) {
+ gnutls_datum_t pout;
+ ret = _gnutls_utf8_password_normalize(_password, strlen(_password), &pout, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ password = (char*)pout.data;
+ }
+
+ while (count < key->size) {
+ ret = _gnutls_hash_init(&hd, mac_to_entry(GNUTLS_MAC_MD5));
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (count) {
+ ret = _gnutls_hash(&hd, md5, sizeof(md5));
+ if (ret < 0) {
+ hash_err:
+ _gnutls_hash_deinit(&hd, NULL);
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ if (password) {
+ ret = _gnutls_hash(&hd, password, strlen(password));
+ if (ret < 0) {
+ gnutls_assert();
+ goto hash_err;
+ }
+ }
+ ret = _gnutls_hash(&hd, salt->data, 8);
+ if (ret < 0) {
+ gnutls_assert();
+ goto hash_err;
+ }
+
+ _gnutls_hash_deinit(&hd, md5);
+
+ if (key->size - count <= sizeof(md5)) {
+ memcpy(&key->data[count], md5, key->size - count);
+ break;
+ }
+
+ memcpy(&key->data[count], md5, sizeof(md5));
+ count += sizeof(md5);
+ }
+ ret = 0;
+
+ cleanup:
+ gnutls_free(password);
+ return ret;
+}
+
+struct pem_cipher {
+ const char *name;
+ gnutls_cipher_algorithm_t cipher;
+};
+
+static const struct pem_cipher pem_ciphers[] = {
+ {"DES-CBC", GNUTLS_CIPHER_DES_CBC},
+ {"DES-EDE3-CBC", GNUTLS_CIPHER_3DES_CBC},
+ {"AES-128-CBC", GNUTLS_CIPHER_AES_128_CBC},
+ {"AES-192-CBC", GNUTLS_CIPHER_AES_192_CBC},
+ {"AES-256-CBC", GNUTLS_CIPHER_AES_256_CBC},
+ {"CAMELLIA-128-CBC", GNUTLS_CIPHER_CAMELLIA_128_CBC},
+ {"CAMELLIA-192-CBC", GNUTLS_CIPHER_CAMELLIA_192_CBC},
+ {"CAMELLIA-256-CBC", GNUTLS_CIPHER_CAMELLIA_256_CBC},
+};
+
+/**
+ * gnutls_x509_privkey_import_openssl:
+ * @key: The data to store the parsed key
+ * @data: The DER or PEM encoded key.
+ * @password: the password to decrypt the key (if it is encrypted).
+ *
+ * This function will convert the given PEM encrypted to
+ * the native gnutls_x509_privkey_t format. The
+ * output will be stored in @key.
+ *
+ * The @password should be in ASCII. If the password is not provided
+ * or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned.
+ *
+ * If the Certificate is PEM encoded it should have a header of
+ * "PRIVATE KEY" and the "DEK-Info" header.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * data,
+ const char *password)
+{
+ gnutls_cipher_hd_t handle;
+ gnutls_cipher_algorithm_t cipher = GNUTLS_CIPHER_UNKNOWN;
+ gnutls_datum_t b64_data;
+ gnutls_datum_t salt, enc_key;
+ unsigned char *key_data;
+ size_t key_data_size;
+ const char *pem_header = (void *) data->data;
+ const char *pem_header_start = (void *) data->data;
+ ssize_t pem_header_size;
+ int ret;
+ unsigned int i, iv_size, l;
+
+ pem_header_size = data->size;
+
+ pem_header =
+ memmem(pem_header, pem_header_size, "PRIVATE KEY---", 14);
+ if (pem_header == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_PARSING_ERROR;
+ }
+
+ pem_header_size -= (ptrdiff_t) (pem_header - pem_header_start);
+
+ pem_header = memmem(pem_header, pem_header_size, "DEK-Info: ", 10);
+ if (pem_header == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_PARSING_ERROR;
+ }
+
+ pem_header_size =
+ data->size - (ptrdiff_t) (pem_header - pem_header_start) - 10;
+ pem_header += 10;
+
+ for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) {
+ l = strlen(pem_ciphers[i].name);
+ if (!strncmp(pem_header, pem_ciphers[i].name, l) &&
+ pem_header[l] == ',') {
+ pem_header += l + 1;
+ cipher = pem_ciphers[i].cipher;
+ break;
+ }
+ }
+
+ if (cipher == GNUTLS_CIPHER_UNKNOWN) {
+ _gnutls_debug_log
+ ("Unsupported PEM encryption type: %.10s\n",
+ pem_header);
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ iv_size = gnutls_cipher_get_iv_size(cipher);
+ salt.size = iv_size;
+ salt.data = gnutls_malloc(salt.size);
+ if (!salt.data)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ for (i = 0; i < salt.size * 2; i++) {
+ unsigned char x;
+ const char *c = &pem_header[i];
+
+ if (*c >= '0' && *c <= '9')
+ x = (*c) - '0';
+ else if (*c >= 'A' && *c <= 'F')
+ x = (*c) - 'A' + 10;
+ else {
+ gnutls_assert();
+ /* Invalid salt in encrypted PEM file */
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto out_salt;
+ }
+ if (i & 1)
+ salt.data[i / 2] |= x;
+ else
+ salt.data[i / 2] = x << 4;
+ }
+
+ pem_header += salt.size * 2;
+ if (*pem_header != '\r' && *pem_header != '\n') {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto out_salt;
+ }
+ while (*pem_header == '\n' || *pem_header == '\r')
+ pem_header++;
+
+ ret =
+ _gnutls_base64_decode((const void *) pem_header,
+ pem_header_size, &b64_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto out_salt;
+ }
+
+ if (b64_data.size < 16) {
+ /* Just to be sure our parsing is OK */
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto out_b64;
+ }
+
+ enc_key.size = gnutls_cipher_get_key_size(cipher);
+ enc_key.data = gnutls_malloc(enc_key.size);
+ if (!enc_key.data) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto out_b64;
+ }
+
+ key_data_size = b64_data.size;
+ key_data = gnutls_malloc(key_data_size);
+ if (!key_data) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto out_enc_key;
+ }
+
+ while (1) {
+ memcpy(key_data, b64_data.data, key_data_size);
+
+ ret = openssl_hash_password(password, &enc_key, &salt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto out;
+ }
+
+ ret = gnutls_cipher_init(&handle, cipher, &enc_key, &salt);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_cipher_deinit(handle);
+ goto out;
+ }
+
+ ret =
+ gnutls_cipher_decrypt(handle, key_data, key_data_size);
+ gnutls_cipher_deinit(handle);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto out;
+ }
+
+ /* We have to strip any padding to accept it.
+ So a bit more ASN.1 parsing for us. */
+ if (key_data[0] == 0x30) {
+ gnutls_datum_t key_datum;
+ unsigned int blocksize =
+ gnutls_cipher_get_block_size(cipher);
+ unsigned int keylen = key_data[1];
+ unsigned int ofs = 2;
+
+ if (keylen & 0x80) {
+ int lenlen = keylen & 0x7f;
+ keylen = 0;
+
+ if (lenlen > 3) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ while (lenlen) {
+ keylen <<= 8;
+ keylen |= key_data[ofs++];
+ lenlen--;
+ }
+ }
+ keylen += ofs;
+
+ /* If there appears to be more or less padding than required, fail */
+ if (key_data_size - keylen > blocksize || key_data_size < keylen+1) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ /* If the padding bytes aren't all equal to the amount of padding, fail */
+ ofs = keylen;
+ while (ofs < key_data_size) {
+ if (key_data[ofs] !=
+ key_data_size - keylen) {
+ gnutls_assert();
+ goto fail;
+ }
+ ofs++;
+ }
+
+ key_datum.data = key_data;
+ key_datum.size = keylen;
+ ret =
+ gnutls_x509_privkey_import(key, &key_datum,
+ GNUTLS_X509_FMT_DER);
+ if (ret == 0)
+ goto out;
+ }
+ fail:
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto out;
+ }
+ out:
+ zeroize_key(key_data, key_data_size);
+ gnutls_free(key_data);
+ out_enc_key:
+ _gnutls_free_key_datum(&enc_key);
+ out_b64:
+ gnutls_free(b64_data.data);
+ out_salt:
+ gnutls_free(salt.data);
+ return ret;
+}
diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c
new file mode 100644
index 0000000..c22ece9
--- /dev/null
+++ b/lib/x509/privkey_pkcs8.c
@@ -0,0 +1,1675 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2014-2017 Red Hat
+ * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include "pkcs7_int.h"
+#include <algorithms.h>
+#include <num.h>
+#include <random.h>
+#include <pk.h>
+#include "attributes.h"
+#include "prov-seed.h"
+
+static int _decode_pkcs8_ecc_key(asn1_node pkcs8_asn,
+ gnutls_x509_privkey_t pkey);
+static
+int pkcs8_key_info(const gnutls_datum_t * raw_key,
+ const struct pkcs_cipher_schema_st **p,
+ struct pbkdf2_params *kdf_params,
+ char **oid);
+
+static int decode_private_key_info(const gnutls_datum_t * der,
+ gnutls_x509_privkey_t pkey);
+
+#define PEM_PKCS8 "ENCRYPTED PRIVATE KEY"
+#define PEM_UNENCRYPTED_PKCS8 "PRIVATE KEY"
+
+/* Returns a negative error code if the encryption schema in
+ * the OID is not supported. The schema ID is returned.
+ */
+/* Encodes a private key to the raw format PKCS #8 needs.
+ * For RSA it is a PKCS #1 DER private key and for DSA it is
+ * an ASN.1 INTEGER of the x value.
+ */
+inline static int
+_encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw)
+{
+ int ret;
+ asn1_node spk = NULL;
+
+ switch (pkey->params.algo) {
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ /* we encode as octet string (which is going to be stored inside
+ * another octet string). No comments. */
+ ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING,
+ pkey->params.raw_priv.data, pkey->params.raw_priv.size,
+ raw);
+ if (ret < 0)
+ gnutls_assert();
+ return ret;
+
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ if ((ret = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.GOSTPrivateKey", &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto error;
+ }
+
+ ret = _gnutls_x509_write_key_int_le(spk, "", pkey->params.params[GOST_K]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = _gnutls_x509_der_encode(spk, "", raw, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE);
+ break;
+
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ case GNUTLS_PK_ECDSA:
+ ret =
+ _gnutls_x509_export_int2(pkey->key, GNUTLS_X509_FMT_DER,
+ "", raw);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ break;
+ case GNUTLS_PK_DSA:
+ /* DSAPublicKey == INTEGER */
+ if ((ret = asn1_create_element
+ (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey",
+ &spk))
+ != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret =
+ _gnutls_x509_write_int(spk, "", pkey->params.params[4],
+ 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ ret = _gnutls_x509_der_encode(spk, "", raw, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE);
+ break;
+
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return 0;
+
+ error:
+ asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE);
+ asn1_delete_structure(&spk);
+ return ret;
+
+}
+
+/*
+ * Encodes a PKCS #1 private key to a PKCS #8 private key
+ * info. The output will be allocated and stored into der. Also
+ * the asn1_node of private key info will be returned.
+ */
+static int
+encode_to_private_key_info(gnutls_x509_privkey_t pkey,
+ gnutls_datum_t * der, asn1_node * pkey_info)
+{
+ int result, len;
+ uint8_t null = 0;
+ const char *oid;
+ gnutls_datum_t algo_params = { NULL, 0 };
+ gnutls_datum_t algo_privkey = { NULL, 0 };
+
+ oid = gnutls_pk_get_oid(pkey->params.algo);
+ if (oid == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ }
+
+ result =
+ _gnutls_x509_write_pubkey_params(&pkey->params, &algo_params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-8-PrivateKeyInfo",
+ pkey_info)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Write the version.
+ */
+ result = asn1_write_value(*pkey_info, "version", &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* write the privateKeyAlgorithm
+ * fields. (OID+NULL data)
+ */
+ result =
+ asn1_write_value(*pkey_info, "privateKeyAlgorithm.algorithm",
+ oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result =
+ asn1_write_value(*pkey_info, "privateKeyAlgorithm.parameters",
+ algo_params.data, algo_params.size);
+ _gnutls_free_key_datum(&algo_params);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+
+ /* Write the raw private key
+ */
+ result = _encode_privkey(pkey, &algo_privkey);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result =
+ asn1_write_value(*pkey_info, "privateKey", algo_privkey.data,
+ algo_privkey.size);
+ _gnutls_free_key_datum(&algo_privkey);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ if ((pkey->params.pkflags & GNUTLS_PK_FLAG_PROVABLE) && pkey->params.seed_size > 0) {
+ gnutls_datum_t seed_info;
+ /* rfc8479 attribute encoding */
+
+ result = _x509_encode_provable_seed(pkey, &seed_info);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = _x509_set_attribute(*pkey_info, "attributes", OID_ATTR_PROV_SEED, &seed_info);
+ gnutls_free(seed_info.data);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ } else {
+ /* Append an empty Attributes field.
+ */
+ result = asn1_write_value(*pkey_info, "attributes", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ }
+
+ /* DER Encode the generated private key info.
+ */
+ len = 0;
+ result = asn1_der_coding(*pkey_info, "", NULL, &len, NULL);
+ if (result != ASN1_MEM_ERROR) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* allocate data for the der
+ */
+ der->size = len;
+ der->data = gnutls_malloc(len);
+ if (der->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result = asn1_der_coding(*pkey_info, "", der->data, &len, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ asn1_delete_structure2(pkey_info, ASN1_DELETE_FLAG_ZEROIZE);
+ _gnutls_free_datum(&algo_params);
+ _gnutls_free_key_datum(&algo_privkey);
+ return result;
+
+}
+
+/* Converts a PKCS #8 private key info to
+ * a PKCS #8 EncryptedPrivateKeyInfo.
+ */
+static int
+encode_to_pkcs8_key(schema_id schema, const gnutls_datum_t * der_key,
+ const char *password, asn1_node * out)
+{
+ int result;
+ gnutls_datum_t key = { NULL, 0 };
+ gnutls_datum_t tmp = { NULL, 0 };
+ asn1_node pkcs8_asn = NULL;
+ struct pbkdf2_params kdf_params;
+ struct pbe_enc_params enc_params;
+ const struct pkcs_cipher_schema_st *s;
+
+ s = _gnutls_pkcs_schema_get(schema);
+ if (s == NULL || s->decrypt_only) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-8-EncryptedPrivateKeyInfo",
+ &pkcs8_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Write the encryption schema OID
+ */
+ result =
+ asn1_write_value(pkcs8_asn, "encryptionAlgorithm.algorithm",
+ s->write_oid, 1);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Generate a symmetric key.
+ */
+
+ result =
+ _gnutls_pkcs_generate_key(schema, password, &kdf_params, &enc_params, &key);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result =
+ _gnutls_pkcs_write_schema_params(schema, pkcs8_asn,
+ "encryptionAlgorithm.parameters",
+ &kdf_params, &enc_params);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* Parameters have been encoded. Now
+ * encrypt the Data.
+ */
+ result = _gnutls_pkcs_raw_encrypt_data(der_key, &enc_params, &key, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* write the encrypted data.
+ */
+ result =
+ asn1_write_value(pkcs8_asn, "encryptedData", tmp.data,
+ tmp.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ _gnutls_free_datum(&tmp);
+ _gnutls_free_key_datum(&key);
+
+ *out = pkcs8_asn;
+
+ return 0;
+
+ error:
+ _gnutls_free_key_datum(&key);
+ _gnutls_free_datum(&tmp);
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+}
+
+
+/**
+ * gnutls_x509_privkey_export_pkcs8:
+ * @key: Holds the key
+ * @format: the format of output params. One of PEM or DER.
+ * @password: the password that will be used to encrypt the key.
+ * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t
+ * @output_data: will contain a private key PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the private key to a PKCS8 structure.
+ * Both RSA and DSA keys can be exported. For DSA keys we use
+ * PKCS #11 definitions. If the flags do not specify the encryption
+ * cipher, then the default 3DES (PBES2) will be used.
+ *
+ * The @password can be either ASCII or UTF-8 in the default PBES2
+ * encryption schemas, or ASCII for the PKCS12 schemas.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will
+ * be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if
+ * encryption is not used.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int
+gnutls_x509_privkey_export_pkcs8(gnutls_x509_privkey_t key,
+ gnutls_x509_crt_fmt_t format,
+ const char *password,
+ unsigned int flags,
+ void *output_data,
+ size_t * output_data_size)
+{
+ asn1_node pkcs8_asn = NULL, pkey_info;
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+ schema_id schema;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Get the private key info
+ * tmp holds the DER encoding.
+ */
+ ret = encode_to_private_key_info(key, &tmp, &pkey_info);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ schema = _gnutls_pkcs_flags_to_schema(flags);
+
+ if (((flags & GNUTLS_PKCS_PLAIN) || password == NULL)
+ && !(flags & GNUTLS_PKCS_NULL_PASSWORD)) {
+ _gnutls_free_datum(&tmp);
+
+ ret =
+ _gnutls_x509_export_int(pkey_info, format,
+ PEM_UNENCRYPTED_PKCS8,
+ output_data, output_data_size);
+
+ asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE);
+ } else {
+ asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); /* we don't need it */
+
+ ret =
+ encode_to_pkcs8_key(schema, &tmp, password,
+ &pkcs8_asn);
+ _gnutls_free_key_datum(&tmp);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_export_int(pkcs8_asn, format, PEM_PKCS8,
+ output_data, output_data_size);
+
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_pkcs8_info:
+ * @data: Holds the PKCS #8 data
+ * @format: the format of the PKCS #8 data
+ * @schema: indicate the schema as one of %gnutls_pkcs_encrypt_flags_t
+ * @cipher: the cipher used as %gnutls_cipher_algorithm_t
+ * @salt: PBKDF2 salt (if non-NULL then @salt_size initially holds its size)
+ * @salt_size: PBKDF2 salt size
+ * @iter_count: PBKDF2 iteration count
+ * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID
+ *
+ * This function will provide information on the algorithms used
+ * in a particular PKCS #8 structure. If the structure algorithms
+ * are unknown the code %GNUTLS_E_UNKNOWN_CIPHER_TYPE will be returned,
+ * and only @oid, will be set. That is, @oid will be set on encrypted PKCS #8
+ * structures whether supported or not. It must be deinitialized using gnutls_free().
+ * The other variables are only set on supported structures.
+ *
+ * Returns: %GNUTLS_E_INVALID_REQUEST if the provided structure isn't an encrypted key,
+ * %GNUTLS_E_UNKNOWN_CIPHER_TYPE if the structure's encryption isn't supported, or
+ * another negative error code in case of a failure. Zero on success.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_pkcs8_info(const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format,
+ unsigned int *schema, unsigned int *cipher,
+ void *salt, unsigned int *salt_size,
+ unsigned int *iter_count,
+ char **oid)
+{
+ int ret = 0, need_free = 0;
+ gnutls_datum_t _data;
+ const struct pkcs_cipher_schema_st *p = NULL;
+ struct pbkdf2_params kdf;
+
+ memset(&kdf, 0, sizeof(kdf));
+
+ if (oid)
+ *oid = NULL;
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ /* If the Certificate is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ /* Try the first header
+ */
+ ret =
+ _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8,
+ data->data, data->size, &_data);
+
+ if (ret < 0) { /* Try the encrypted header
+ */
+ ret =
+ _gnutls_fbase64_decode(PEM_PKCS8, data->data,
+ data->size, &_data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ }
+
+ need_free = 1;
+ }
+
+ ret = pkcs8_key_info(&_data, &p, &kdf, oid);
+ if (ret == GNUTLS_E_DECRYPTION_FAILED)
+ ret = GNUTLS_E_INVALID_REQUEST;
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ assert(p != NULL);
+
+ if (need_free)
+ _gnutls_free_datum(&_data);
+
+ if (schema)
+ *schema = p->flag;
+
+ if (cipher)
+ *cipher = p->cipher;
+
+ if (iter_count)
+ *iter_count = kdf.iter_count;
+
+ if (salt) {
+ if (*salt_size >= (unsigned)kdf.salt_size) {
+ memcpy(salt, kdf.salt, kdf.salt_size);
+ } else {
+ *salt_size = kdf.salt_size;
+ ret = gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ goto cleanup;
+ }
+ }
+
+ if (salt_size)
+ *salt_size = kdf.salt_size;
+
+ return 0;
+
+ cleanup:
+ if (ret != GNUTLS_E_UNKNOWN_CIPHER_TYPE && oid) {
+ gnutls_free(*oid);
+ }
+ if (need_free)
+ _gnutls_free_datum(&_data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_privkey_export2_pkcs8:
+ * @key: Holds the key
+ * @format: the format of output params. One of PEM or DER.
+ * @password: the password that will be used to encrypt the key.
+ * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t
+ * @out: will contain a private key PEM or DER encoded
+ *
+ * This function will export the private key to a PKCS8 structure.
+ * Both RSA and DSA keys can be exported. For DSA keys we use
+ * PKCS #11 definitions. If the flags do not specify the encryption
+ * cipher, then the default 3DES (PBES2) will be used.
+ *
+ * The @password can be either ASCII or UTF-8 in the default PBES2
+ * encryption schemas, or ASCII for the PKCS12 schemas.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if
+ * encryption is not used.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since 3.1.3
+ **/
+int
+gnutls_x509_privkey_export2_pkcs8(gnutls_x509_privkey_t key,
+ gnutls_x509_crt_fmt_t format,
+ const char *password,
+ unsigned int flags, gnutls_datum_t * out)
+{
+ asn1_node pkcs8_asn = NULL, pkey_info;
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+ schema_id schema;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Get the private key info
+ * tmp holds the DER encoding.
+ */
+ ret = encode_to_private_key_info(key, &tmp, &pkey_info);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ schema = _gnutls_pkcs_flags_to_schema(flags);
+
+ if (((flags & GNUTLS_PKCS_PLAIN) || password == NULL)
+ && !(flags & GNUTLS_PKCS_NULL_PASSWORD)) {
+ _gnutls_free_key_datum(&tmp);
+
+ ret =
+ _gnutls_x509_export_int2(pkey_info, format,
+ PEM_UNENCRYPTED_PKCS8, out);
+
+ asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE);
+ } else {
+ asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); /* we don't need it */
+
+ ret =
+ encode_to_pkcs8_key(schema, &tmp, password,
+ &pkcs8_asn);
+ _gnutls_free_key_datum(&tmp);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_export_int2(pkcs8_asn, format, PEM_PKCS8,
+ out);
+
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ }
+
+ return ret;
+}
+
+
+
+ /* We've gotten this far. In the real world it's almost certain
+ * that we're dealing with a good file, but wrong password.
+ * Sadly like 90% of random data is somehow valid DER for the
+ * a first small number of bytes, so no easy way to guarantee. */
+#define CHECK_ERR_FOR_ENCRYPTED(result) \
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || \
+ result == GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND || \
+ result == GNUTLS_E_ASN1_DER_ERROR || \
+ result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || \
+ result == GNUTLS_E_ASN1_GENERIC_ERROR || \
+ result == GNUTLS_E_ASN1_VALUE_NOT_VALID || \
+ result == GNUTLS_E_ASN1_TAG_ERROR || \
+ result == GNUTLS_E_ASN1_TAG_IMPLICIT || \
+ result == GNUTLS_E_ASN1_TYPE_ANY_ERROR || \
+ result == GNUTLS_E_ASN1_SYNTAX_ERROR || \
+ result == GNUTLS_E_ASN1_DER_OVERFLOW) { \
+ result = GNUTLS_E_DECRYPTION_FAILED; \
+ }
+
+static int pkcs8_key_decrypt(const gnutls_datum_t * raw_key,
+ asn1_node pkcs8_asn, const char *password,
+ gnutls_x509_privkey_t pkey)
+{
+ int result, len;
+ char enc_oid[MAX_OID_SIZE];
+ gnutls_datum_t tmp = {NULL, 0};
+ int params_start, params_end, params_len;
+ struct pbkdf2_params kdf_params;
+ struct pbe_enc_params enc_params;
+ schema_id schema;
+
+ /* Check the encryption schema OID
+ */
+ len = sizeof(enc_oid);
+ result =
+ asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm",
+ enc_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ schema = result;
+
+ /* Get the DER encoding of the parameters.
+ */
+ result =
+ asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data,
+ raw_key->size,
+ "encryptionAlgorithm.parameters",
+ &params_start, &params_end);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ params_len = params_end - params_start + 1;
+
+ result =
+ _gnutls_read_pkcs_schema_params(&schema, password,
+ &raw_key->data[params_start],
+ params_len, &kdf_params, &enc_params);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* Parameters have been decoded. Now
+ * decrypt the EncryptedData.
+ */
+ result =
+ _gnutls_pkcs_raw_decrypt_data(schema, pkcs8_asn, "encryptedData", password,
+ &kdf_params, &enc_params, &tmp);
+ if (result < 0) {
+ gnutls_assert();
+ result = GNUTLS_E_DECRYPTION_FAILED;
+ goto error;
+ }
+
+ result = decode_private_key_info(&tmp, pkey);
+ _gnutls_free_key_datum(&tmp);
+
+ CHECK_ERR_FOR_ENCRYPTED(result);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ return result;
+}
+
+static int check_for_decrypted(const gnutls_datum_t *der)
+{
+ int result;
+ asn1_node pkcs8_asn = NULL;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-8-PrivateKeyInfo",
+ &pkcs8_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&pkcs8_asn, der->data, der->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = 0;
+ error:
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+
+}
+
+static
+int pkcs8_key_info(const gnutls_datum_t * raw_key,
+ const struct pkcs_cipher_schema_st **p,
+ struct pbkdf2_params *kdf_params,
+ char **oid)
+{
+ int result, len;
+ char enc_oid[MAX_OID_SIZE*2];
+ int params_start, params_end, params_len;
+ struct pbe_enc_params enc_params;
+ schema_id schema;
+ asn1_node pkcs8_asn = NULL;
+
+ memset(&enc_params, 0, sizeof(enc_params));
+
+ result = check_for_decrypted(raw_key);
+ if (result == 0)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-8-EncryptedPrivateKeyInfo",
+ &pkcs8_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result =
+ _asn1_strict_der_decode(&pkcs8_asn, raw_key->data, raw_key->size,
+ NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Check the encryption schema OID
+ */
+ len = sizeof(enc_oid);
+ result =
+ asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm",
+ enc_oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (oid) {
+ *oid = gnutls_strdup(enc_oid);
+ }
+
+ if ((result = _gnutls_check_pkcs_cipher_schema(enc_oid)) < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ schema = result;
+
+ /* Get the DER encoding of the parameters.
+ */
+ result =
+ asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data,
+ raw_key->size,
+ "encryptionAlgorithm.parameters",
+ &params_start, &params_end);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+ params_len = params_end - params_start + 1;
+
+ result =
+ _gnutls_read_pkcs_schema_params(&schema, NULL,
+ &raw_key->data[params_start],
+ params_len, kdf_params, &enc_params);
+
+ if (result < 0) {
+ gnutls_assert();
+ if (oid && enc_params.pbes2_oid[0] != 0) {
+ snprintf(enc_oid, sizeof(enc_oid), "%s/%s", *oid, enc_params.pbes2_oid);
+ gnutls_free(*oid);
+ *oid = gnutls_strdup(enc_oid);
+ }
+ goto error;
+ }
+
+ *p = _gnutls_pkcs_schema_get(schema);
+ if (*p == NULL) {
+ gnutls_assert();
+ result = GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+}
+
+/* Converts a PKCS #8 key to
+ * an internal structure (gnutls_private_key)
+ * (normally a PKCS #1 encoded RSA key)
+ */
+static int
+pkcs8_key_decode(const gnutls_datum_t * raw_key,
+ const char *password, gnutls_x509_privkey_t pkey,
+ unsigned int decrypt)
+{
+ int result;
+ asn1_node pkcs8_asn = NULL;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-8-EncryptedPrivateKeyInfo",
+ &pkcs8_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result =
+ _asn1_strict_der_decode(&pkcs8_asn, raw_key->data, raw_key->size,
+ NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ if (decrypt)
+ result =
+ pkcs8_key_decrypt(raw_key, pkcs8_asn, password, pkey);
+ else
+ result = 0;
+
+ error:
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+
+}
+
+/* Decodes an RSA privateKey from a PKCS8 structure.
+ */
+static int
+_decode_pkcs8_rsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey)
+{
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ pkey->key = _gnutls_privkey_decode_pkcs1_rsa_key(&tmp, pkey);
+ _gnutls_free_key_datum(&tmp);
+
+ if (pkey->key == NULL) {
+ ret = GNUTLS_E_PK_INVALID_PRIVKEY;
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+/* Decodes an RSA-PSS privateKey from a PKCS8 structure.
+ */
+static int
+_decode_pkcs8_rsa_pss_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey)
+{
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+ gnutls_x509_spki_st params;
+
+ memset(&params, 0, sizeof(params));
+
+ ret = _gnutls_x509_read_value(pkcs8_asn,
+ "privateKeyAlgorithm.parameters", &tmp);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_ASN1_VALUE_NOT_FOUND || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ goto skip_params;
+
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size, &params);
+ _gnutls_free_key_datum(&tmp);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ skip_params:
+ ret = _decode_pkcs8_rsa_key(pkcs8_asn, pkey);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ pkey->params.algo = GNUTLS_PK_RSA_PSS;
+ memcpy(&pkey->params.spki, &params, sizeof(gnutls_x509_spki_st));
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+/* Decodes an ECC privateKey from a PKCS8 structure.
+ */
+static int
+_decode_pkcs8_ecc_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey)
+{
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+ unsigned char oid[MAX_OID_SIZE];
+ unsigned curve = GNUTLS_ECC_CURVE_INVALID;
+ int len, result;
+
+ /* openssl PKCS #8 files with ECC keys place the curve in
+ * privateKeyAlgorithm.parameters instead of the ECPrivateKey.parameters.
+ */
+ len = sizeof(oid);
+ result =
+ asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.parameters",
+ oid, &len);
+ if (result == ASN1_SUCCESS) {
+ ret = _gnutls_x509_read_ecc_params(oid, len, &curve);
+ if (ret < 0) {
+ _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid);
+ curve = GNUTLS_ECC_CURVE_INVALID;
+ }
+ }
+
+ ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = _gnutls_privkey_decode_ecc_key(&pkey->key, &tmp, pkey, curve);
+ _gnutls_free_key_datum(&tmp);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+static int
+_decode_pkcs8_eddsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey, const char *oid)
+{
+ int ret;
+ gnutls_datum_t tmp;
+ gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID;
+ const gnutls_ecc_curve_entry_st *ce;
+
+ gnutls_pk_params_init(&pkey->params);
+
+ curve = gnutls_oid_to_ecc_curve(oid);
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
+ _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid);
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ }
+
+ ce = _gnutls_ecc_curve_get_params(curve);
+ if (_curve_is_eddsa(ce)) {
+ ret = _gnutls_x509_read_string(pkcs8_asn, "privateKey", &tmp, ASN1_ETYPE_OCTET_STRING, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ return gnutls_assert_val(ret);
+ }
+
+ if (tmp.size != ce->size) {
+ gnutls_free(tmp.data);
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+ gnutls_free(pkey->params.raw_priv.data);
+ switch (curve) {
+ case GNUTLS_ECC_CURVE_ED25519:
+ pkey->params.algo = GNUTLS_PK_EDDSA_ED25519;
+ break;
+ case GNUTLS_ECC_CURVE_ED448:
+ pkey->params.algo = GNUTLS_PK_EDDSA_ED448;
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ pkey->params.raw_priv.data = tmp.data;
+ pkey->params.raw_priv.size = tmp.size;
+ pkey->params.curve = curve;
+
+ tmp.data = NULL;
+ return 0;
+ } else {
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ }
+}
+
+static int
+_decode_pkcs8_modern_ecdh_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey, const char *oid)
+{
+ int ret;
+ gnutls_datum_t tmp;
+ gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID;
+ const gnutls_ecc_curve_entry_st *ce;
+
+ gnutls_pk_params_init(&pkey->params);
+
+ curve = gnutls_oid_to_ecc_curve(oid);
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
+ _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid);
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ }
+
+ ce = _gnutls_ecc_curve_get_params(curve);
+ if (_curve_is_modern_ecdh(ce)) {
+ ret = _gnutls_x509_read_string(pkcs8_asn, "privateKey", &tmp, ASN1_ETYPE_OCTET_STRING, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ return gnutls_assert_val(ret);
+ }
+
+ if (tmp.size != ce->size) {
+ gnutls_free(tmp.data);
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+ gnutls_free(pkey->params.raw_priv.data);
+ switch (curve) {
+ case GNUTLS_ECC_CURVE_X25519:
+ pkey->params.algo = GNUTLS_PK_ECDH_X25519;
+ break;
+ case GNUTLS_ECC_CURVE_X448:
+ pkey->params.algo = GNUTLS_PK_ECDH_X448;
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ pkey->params.raw_priv.data = tmp.data;
+ pkey->params.raw_priv.size = tmp.size;
+ pkey->params.curve = curve;
+
+ tmp.data = NULL;
+ return 0;
+ } else {
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ }
+}
+
+/* Converts a GOST key to
+ * an internal structure (gnutls_private_key)
+ */
+static int
+_privkey_decode_gost_key(const gnutls_datum_t * raw_key,
+ gnutls_x509_privkey_t pkey)
+{
+ int ret;
+ int ecc_size = gnutls_ecc_curve_get_size(pkey->params.curve);
+
+ /* Just to be sure here */
+ if (ecc_size <= 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ goto error;
+ }
+
+ /* Private key form described in R 50.1.112-2016.
+ * Private key can come up as masked value concatenated with several masks.
+ * each part is of ecc_size bytes. Key will be unmasked in pk_fixup */
+ if (raw_key->size % ecc_size == 0) {
+ ret = _gnutls_mpi_init_scan_le(&pkey->params.params[GOST_K],
+ raw_key->data, raw_key->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ } else if (raw_key->data[0] == ASN1_TAG_INTEGER) {
+ asn1_node pkey_asn;
+
+ /* Very old format: INTEGER packed in OCTET STRING */
+ if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.GOSTPrivateKeyOld",
+ &pkey_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto error;
+ }
+
+ ret = _asn1_strict_der_decode(&pkey_asn,
+ raw_key->data, raw_key->size,
+ NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ goto error;
+ }
+
+ ret = _gnutls_x509_read_key_int(pkey_asn, "",
+ &pkey->params.params[GOST_K]);
+ if (ret < 0) {
+ gnutls_assert();
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ goto error;
+ }
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ } else if (raw_key->data[0] == ASN1_TAG_OCTET_STRING) {
+ asn1_node pkey_asn;
+
+ /* format: OCTET STRING packed in OCTET STRING */
+ if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.GOSTPrivateKey",
+ &pkey_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto error;
+ }
+
+ ret = _asn1_strict_der_decode(&pkey_asn,
+ raw_key->data, raw_key->size,
+ NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ goto error;
+ }
+
+ ret = _gnutls_x509_read_key_int_le(pkey_asn, "",
+ &pkey->params.params[GOST_K]);
+ if (ret < 0) {
+ gnutls_assert();
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ goto error;
+ }
+ asn1_delete_structure2(&pkey_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ } else {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto error;
+ }
+
+ pkey->params.params_nr++;
+
+ return 0;
+
+ error:
+ return ret;
+
+}
+
+/* Decodes a GOST privateKey from a PKCS8 structure.
+ */
+static int
+_decode_pkcs8_gost_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey,
+ gnutls_pk_algorithm_t algo)
+{
+ int ret;
+ gnutls_datum_t tmp;
+ unsigned char oid[3 * MAX_OID_SIZE]; /* GOST parameters can have 3 OIDs at most */
+ int len, result;
+
+ gnutls_pk_params_init(&pkey->params);
+
+ len = sizeof(oid);
+ result = asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.parameters",
+ oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto error;
+ } else {
+ ret = _gnutls_x509_read_gost_params(oid, len, &pkey->params, algo);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ /* Will be fixed later by pk_fixup */
+ ret = _gnutls_mpi_init(&pkey->params.params[GOST_X]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ ret = _gnutls_mpi_init(&pkey->params.params[GOST_Y]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ pkey->params.params_nr++;
+
+ _gnutls_mpi_set_ui(pkey->params.params[GOST_X], 0);
+ _gnutls_mpi_set_ui(pkey->params.params[GOST_Y], 0);
+
+ ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = _privkey_decode_gost_key(&tmp, pkey);
+ _gnutls_free_key_datum(&tmp);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ pkey->params.algo = algo;
+
+ return 0;
+
+error:
+ gnutls_pk_params_clear(&pkey->params);
+ gnutls_pk_params_release(&pkey->params);
+
+ return ret;
+}
+
+/* Decodes an DSA privateKey and params from a PKCS8 structure.
+ */
+static int
+_decode_pkcs8_dsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey)
+{
+ int ret;
+ gnutls_datum_t tmp = {NULL, 0};
+
+ gnutls_pk_params_init(&pkey->params);
+
+ ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret =
+ _gnutls_x509_read_der_int(tmp.data, tmp.size,
+ &pkey->params.params[4]);
+ _gnutls_free_key_datum(&tmp);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret =
+ _gnutls_x509_read_value(pkcs8_asn,
+ "privateKeyAlgorithm.parameters",
+ &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret =
+ _gnutls_x509_read_pubkey_params(GNUTLS_PK_DSA, tmp.data,
+ tmp.size, &pkey->params);
+ _gnutls_free_datum(&tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (_gnutls_mpi_cmp_ui(pkey->params.params[0], 0) == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_ILLEGAL_PARAMETER;
+ goto error;
+ }
+
+ /* the public key can be generated as g^x mod p */
+ ret = _gnutls_mpi_init(&pkey->params.params[3]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = _gnutls_mpi_powm(pkey->params.params[3], pkey->params.params[2],
+ pkey->params.params[4], pkey->params.params[0]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ pkey->params.algo = GNUTLS_PK_DSA;
+ pkey->params.params_nr = DSA_PRIVATE_PARAMS;
+
+ ret =
+ _gnutls_asn1_encode_privkey(&pkey->key,
+ &pkey->params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ if (pkey->params.params_nr != DSA_PRIVATE_PARAMS)
+ _gnutls_mpi_release(&pkey->params.params[4]);
+ return ret;
+}
+
+
+static int
+decode_private_key_info(const gnutls_datum_t * der,
+ gnutls_x509_privkey_t pkey)
+{
+ int result, len;
+ char oid[MAX_OID_SIZE];
+ asn1_node pkcs8_asn = NULL;
+ gnutls_datum_t sder;
+ int ret;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-8-PrivateKeyInfo",
+ &pkcs8_asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = _asn1_strict_der_decode(&pkcs8_asn, der->data, der->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* Check the private key algorithm OID
+ */
+ len = sizeof(oid);
+ result =
+ asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.algorithm",
+ oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ pkey->params.algo = gnutls_oid_to_pk(oid);
+ if (pkey->params.algo == GNUTLS_PK_UNKNOWN) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("PKCS #8 private key OID '%s' is unsupported.\n",
+ oid);
+ result = GNUTLS_E_UNKNOWN_PK_ALGORITHM;
+ goto error;
+ }
+
+ /* Get the DER encoding of the actual private key.
+ */
+
+ switch(pkey->params.algo) {
+ case GNUTLS_PK_RSA:
+ result = _decode_pkcs8_rsa_key(pkcs8_asn, pkey);
+ break;
+ case GNUTLS_PK_RSA_PSS:
+ result = _decode_pkcs8_rsa_pss_key(pkcs8_asn, pkey);
+ break;
+ case GNUTLS_PK_DSA:
+ result = _decode_pkcs8_dsa_key(pkcs8_asn, pkey);
+ break;
+ case GNUTLS_PK_ECDSA:
+ result = _decode_pkcs8_ecc_key(pkcs8_asn, pkey);
+ break;
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ result = _decode_pkcs8_eddsa_key(pkcs8_asn, pkey, oid);
+ break;
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ result = _decode_pkcs8_modern_ecdh_key(pkcs8_asn, pkey, oid);
+ break;
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ result = _decode_pkcs8_gost_key(pkcs8_asn,
+ pkey, pkey->params.algo);
+ break;
+ default:
+ result = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ goto error;
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* check for provable parameters attribute */
+ ret = _x509_parse_attribute(pkcs8_asn, "attributes", OID_ATTR_PROV_SEED, 0, 1, &sder);
+ if (ret >= 0) { /* ignore it when not being present */
+ ret = _x509_decode_provable_seed(pkey, &sder);
+ gnutls_free(sder.data);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+ }
+
+ result = 0;
+
+error:
+ asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE);
+ return result;
+
+}
+
+/**
+ * gnutls_x509_privkey_import_pkcs8:
+ * @key: The data to store the parsed key
+ * @data: The DER or PEM encoded key.
+ * @format: One of DER or PEM
+ * @password: the password to decrypt the key (if it is encrypted).
+ * @flags: 0 if encrypted or GNUTLS_PKCS_PLAIN if not encrypted.
+ *
+ * This function will convert the given DER or PEM encoded PKCS8 2.0
+ * encrypted key to the native gnutls_x509_privkey_t format. The
+ * output will be stored in @key. Both RSA and DSA keys can be
+ * imported, and flags can only be used to indicate an unencrypted
+ * key.
+ *
+ * The @password can be either ASCII or UTF-8 in the default PBES2
+ * encryption schemas, or ASCII for the PKCS12 schemas.
+ *
+ * If the Certificate is PEM encoded it should have a header of
+ * "ENCRYPTED PRIVATE KEY", or "PRIVATE KEY". You only need to
+ * specify the flags if the key is DER encoded, since in that case
+ * the encryption status cannot be auto-detected.
+ *
+ * If the %GNUTLS_PKCS_PLAIN flag is specified and the supplied data
+ * are encrypted then %GNUTLS_E_DECRYPTION_FAILED is returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey_t key,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ const char *password, unsigned int flags)
+{
+ int result = 0, need_free = 0;
+ gnutls_datum_t _data;
+
+ if (key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ _data.data = data->data;
+ _data.size = data->size;
+
+ key->params.algo = GNUTLS_PK_UNKNOWN;
+
+ /* If the Certificate is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ /* Try the first header
+ */
+ result =
+ _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8,
+ data->data, data->size, &_data);
+
+ if (result < 0) { /* Try the encrypted header
+ */
+ result =
+ _gnutls_fbase64_decode(PEM_PKCS8, data->data,
+ data->size, &_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ } else if (flags == 0)
+ flags |= GNUTLS_PKCS_PLAIN;
+
+ need_free = 1;
+ }
+
+ if (key->expanded) {
+ _gnutls_x509_privkey_reinit(key);
+ }
+ key->expanded = 1;
+
+ /* Here we don't check for password == NULL to maintain a backwards
+ * compatibility behavior, with old versions that were encrypting using
+ * a NULL password.
+ */
+ if (flags & GNUTLS_PKCS_PLAIN) {
+ result = decode_private_key_info(&_data, key);
+ if (result < 0) { /* check if it is encrypted */
+ if (pkcs8_key_decode(&_data, "", key, 0) == 0)
+ result = GNUTLS_E_DECRYPTION_FAILED;
+ }
+ } else { /* encrypted. */
+ result = pkcs8_key_decode(&_data, password, key, 1);
+ }
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* This part is necessary to get the public key on certain algorithms.
+ * In the import above we only get the private key. */
+ result =
+ _gnutls_pk_fixup(key->params.algo, GNUTLS_IMPORT, &key->params);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (need_free)
+ _gnutls_free_datum(&_data);
+
+ /* The key has now been decoded.
+ */
+ return 0;
+
+ cleanup:
+ asn1_delete_structure2(&key->key, ASN1_DELETE_FLAG_ZEROIZE);
+ key->params.algo = GNUTLS_PK_UNKNOWN;
+ if (need_free) {
+ zeroize_temp_key(_data.data, _data.size);
+ _gnutls_free_datum(&_data);
+ }
+ return result;
+}
+
diff --git a/lib/x509/privkey_pkcs8_pbes1.c b/lib/x509/privkey_pkcs8_pbes1.c
new file mode 100644
index 0000000..983530e
--- /dev/null
+++ b/lib/x509/privkey_pkcs8_pbes1.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 Red Hat
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include "pkcs7_int.h"
+#include <algorithms.h>
+#include <nettle/md5.h>
+
+/* This file includes support for PKCS#8 PBES1 with DES and MD5.
+ * We only support decryption for compatibility with other software.
+ */
+
+int _gnutls_read_pbkdf1_params(const uint8_t * data, int data_size,
+ struct pbkdf2_params *kdf_params,
+ struct pbe_enc_params *enc_params)
+{
+ asn1_node pasn = NULL;
+ int len;
+ int ret, result;
+
+ memset(kdf_params, 0, sizeof(*kdf_params));
+ memset(enc_params, 0, sizeof(*enc_params));
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.pkcs-5-PBE-params",
+ &pasn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Decode the parameters.
+ */
+ result =
+ _asn1_strict_der_decode(&pasn, data, data_size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ ret =
+ _gnutls_x509_read_uint(pasn, "iterationCount",
+ &kdf_params->iter_count);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (kdf_params->iter_count >= MAX_ITER_COUNT || kdf_params->iter_count == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto error;
+ }
+
+ len = sizeof(kdf_params->salt);
+ result =
+ asn1_read_value(pasn, "salt",
+ kdf_params->salt, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ if (len != 8) {
+ gnutls_assert();
+ ret = GNUTLS_E_ILLEGAL_PARAMETER;
+ goto error;
+ }
+
+ enc_params->cipher = GNUTLS_CIPHER_DES_CBC;
+
+ ret = 0;
+ error:
+ asn1_delete_structure2(&pasn, ASN1_DELETE_FLAG_ZEROIZE);
+ return ret;
+
+}
+
+static void pbkdf1_md5(const char *password, unsigned password_len,
+ const uint8_t salt[8], unsigned iter_count, unsigned key_size, uint8_t *key)
+{
+ struct md5_ctx ctx;
+ uint8_t tmp[16];
+ unsigned i;
+
+ if (key_size > sizeof(tmp))
+ abort();
+
+ for (i=0;i<iter_count;i++) {
+ md5_init(&ctx);
+ if (i==0) {
+ md5_update(&ctx, password_len, (uint8_t*)password);
+ md5_update(&ctx, 8, salt);
+ md5_digest(&ctx, 16, tmp);
+ } else {
+ md5_update(&ctx, 16, tmp);
+ md5_digest(&ctx, 16, tmp);
+ }
+ }
+
+ memcpy(key, tmp, key_size);
+ return;
+}
+
+int
+_gnutls_decrypt_pbes1_des_md5_data(const char *password,
+ unsigned password_len,
+ const struct pbkdf2_params *kdf_params,
+ const struct pbe_enc_params *enc_params,
+ const gnutls_datum_t *encrypted_data,
+ gnutls_datum_t *decrypted_data)
+{
+ int result;
+ gnutls_datum_t dkey, d_iv;
+ gnutls_cipher_hd_t ch;
+ uint8_t key[16];
+ const unsigned block_size = 8;
+
+ if (enc_params->cipher != GNUTLS_CIPHER_DES_CBC)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (encrypted_data->size % block_size != 0)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ /* generate the key
+ */
+ pbkdf1_md5(password, password_len, kdf_params->salt, kdf_params->iter_count, sizeof(key), key);
+
+ dkey.data = key;
+ dkey.size = 8;
+ d_iv.data = &key[8];
+ d_iv.size = 8;
+ result = gnutls_cipher_init(&ch, GNUTLS_CIPHER_DES_CBC, &dkey, &d_iv);
+ if (result < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(result);
+ }
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+
+ result = gnutls_cipher_decrypt(ch, encrypted_data->data, encrypted_data->size);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if ((int)encrypted_data->size - encrypted_data->data[encrypted_data->size - 1] < 0) {
+ gnutls_assert();
+ result = GNUTLS_E_ILLEGAL_PARAMETER;
+ goto error;
+ }
+
+ decrypted_data->data = encrypted_data->data;
+ decrypted_data->size = encrypted_data->size - encrypted_data->data[encrypted_data->size - 1];
+
+ result = 0;
+ error:
+ gnutls_cipher_deinit(ch);
+
+ return result;
+}
+
diff --git a/lib/x509/prov-seed.c b/lib/x509/prov-seed.c
new file mode 100644
index 0000000..d0119b4
--- /dev/null
+++ b/lib/x509/prov-seed.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_int.h>
+#include <mpi.h>
+#include "prov-seed.h"
+
+/* This function encodes a seed value and a hash algorithm OID to the format
+ * described in RFC8479. The output is the DER encoded form.
+ */
+int _x509_encode_provable_seed(gnutls_x509_privkey_t pkey, gnutls_datum_t *der)
+{
+
+ asn1_node c2;
+ int ret, result;
+ const char *oid;
+
+ oid = gnutls_digest_get_oid(pkey->params.palgo);
+ if (oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if ((result =
+ asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.ProvableSeed",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value(c2, "seed", pkey->params.seed, pkey->params.seed_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "algorithm", oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", der, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure2(&c2, ASN1_DELETE_FLAG_ZEROIZE);
+ return ret;
+}
+
+/* This function decodes a DER encoded form of seed and a hash algorithm, as in
+ * RFC8479.
+ */
+int _x509_decode_provable_seed(gnutls_x509_privkey_t pkey, const gnutls_datum_t *der)
+{
+
+ asn1_node c2;
+ int ret, result;
+ char oid[MAX_OID_SIZE];
+ int oid_size;
+ gnutls_datum_t seed = {NULL, 0};
+
+ if ((result =
+ asn1_create_element(_gnutls_get_gnutls_asn(),
+ "GNUTLS.ProvableSeed",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, der->data, der->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_read_value(c2, "seed", &seed);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (seed.size <= sizeof(pkey->params.seed)) {
+ memcpy(pkey->params.seed, seed.data, seed.size);
+ pkey->params.seed_size = seed.size;
+ } else {
+ ret = 0; /* ignore struct */
+ _gnutls_debug_log("%s: ignoring ProvableSeed due to very long params\n", __func__);
+ goto cleanup;
+ }
+
+ oid_size = sizeof(oid);
+ result = asn1_read_value(c2, "algorithm", oid, &oid_size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ pkey->params.palgo = gnutls_oid_to_digest(oid);
+ pkey->params.pkflags |= GNUTLS_PK_FLAG_PROVABLE;
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(seed.data);
+ asn1_delete_structure2(&c2, ASN1_DELETE_FLAG_ZEROIZE);
+ return ret;
+}
diff --git a/lib/x509/prov-seed.h b/lib/x509/prov-seed.h
new file mode 100644
index 0000000..a7753cc
--- /dev/null
+++ b/lib/x509/prov-seed.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_PROV_SEED_H
+#define GNUTLS_LIB_X509_PROV_SEED_H
+
+int _x509_encode_provable_seed(gnutls_x509_privkey_t pkey, gnutls_datum_t *der);
+int _x509_decode_provable_seed(gnutls_x509_privkey_t pkey, const gnutls_datum_t *der);
+
+#endif /* GNUTLS_LIB_X509_PROV_SEED_H */
diff --git a/lib/x509/sign.c b/lib/x509/sign.c
new file mode 100644
index 0000000..303e9f2
--- /dev/null
+++ b/lib/x509/sign.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* All functions which relate to X.509 certificate signing stuff are
+ * included here
+ */
+
+#include "gnutls_int.h"
+
+#include "errors.h"
+#include <libtasn1.h>
+#include <global.h>
+#include <num.h> /* MAX */
+#include <tls-sig.h>
+#include <str.h>
+#include <datum.h>
+#include <x509_int.h>
+#include <common.h>
+#include <gnutls/abstract.h>
+#include <pk.h>
+
+/* This is the same as the _gnutls_x509_sign, but this one will decode
+ * the asn1_node given, and sign the DER data. Actually used to get the DER
+ * of the TBS and sign it on the fly.
+ */
+int
+_gnutls_x509_get_tbs(asn1_node cert, const char *tbs_name,
+ gnutls_datum_t * tbs)
+{
+ return _gnutls_x509_der_encode(cert, tbs_name, tbs, 0);
+}
+
+int
+_gnutls_x509_crt_get_spki_params(gnutls_x509_crt_t crt,
+ const gnutls_x509_spki_st *key_params,
+ gnutls_x509_spki_st *params)
+{
+ int result;
+ gnutls_x509_spki_st crt_params;
+
+ result = _gnutls_x509_crt_read_spki_params(crt, &crt_params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if (crt_params.pk == GNUTLS_PK_RSA_PSS) {
+ if (key_params->pk == GNUTLS_PK_RSA_PSS) {
+ if (crt_params.rsa_pss_dig != key_params->rsa_pss_dig) {
+ gnutls_assert();
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ if (crt_params.salt_size < key_params->salt_size) {
+ gnutls_assert();
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+ } else if (key_params->pk != GNUTLS_PK_RSA && key_params->pk != GNUTLS_PK_UNKNOWN) {
+ gnutls_assert();
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+ memcpy(params, &crt_params, sizeof(gnutls_x509_spki_st));
+ } else {
+ memcpy(params, key_params, sizeof(gnutls_x509_spki_st));
+ }
+
+ return 0;
+}
+
+/*-
+ * _gnutls_x509_pkix_sign - This function will sign a CRL or a certificate with a key
+ * @src: should contain an asn1_node
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ *
+ * This function will sign a CRL or a certificate with the issuer's private key, and
+ * will copy the issuer's information into the CRL or certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ -*/
+int
+_gnutls_x509_pkix_sign(asn1_node src, const char *src_name,
+ gnutls_digest_algorithm_t dig,
+ unsigned int flags,
+ gnutls_x509_crt_t issuer,
+ gnutls_privkey_t issuer_key)
+{
+ int result;
+ gnutls_datum_t signature;
+ gnutls_datum_t tbs;
+ char name[128];
+ gnutls_pk_algorithm_t pk;
+ gnutls_x509_spki_st key_params, params;
+ const gnutls_sign_entry_st *se;
+
+ pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL);
+ if (pk == GNUTLS_PK_UNKNOWN)
+ pk = gnutls_privkey_get_pk_algorithm(issuer_key, NULL);
+
+ result = _gnutls_privkey_get_spki_params(issuer_key, &key_params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = _gnutls_x509_crt_get_spki_params(issuer, &key_params, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = _gnutls_privkey_update_spki_params(issuer_key, pk, dig, flags,
+ &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 1. Copy the issuer's name into the certificate.
+ */
+ _gnutls_str_cpy(name, sizeof(name), src_name);
+ _gnutls_str_cat(name, sizeof(name), ".issuer");
+
+ result =
+ asn1_copy_node(src, name, issuer->cert,
+ "tbsCertificate.subject");
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Step 1.5. Write the signature stuff in the tbsCertificate.
+ */
+ _gnutls_str_cpy(name, sizeof(name), src_name);
+ _gnutls_str_cat(name, sizeof(name), ".signature");
+
+ se = _gnutls_pk_to_sign_entry(params.pk, dig);
+ if (se == NULL)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+
+ _gnutls_debug_log("signing structure using %s\n", se->name);
+
+ result = _gnutls_x509_write_sign_params(src, name, se, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* Step 2. Sign the certificate.
+ */
+ result = _gnutls_x509_get_tbs(src, src_name, &tbs);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ FIX_SIGN_PARAMS(params, flags, dig);
+
+ if (_gnutls_pk_is_not_prehashed(params.pk)) {
+ result = privkey_sign_raw_data(issuer_key, se, &tbs, &signature, &params);
+ } else {
+ result = privkey_sign_and_hash_data(issuer_key, se,
+ &tbs, &signature, &params);
+ }
+ gnutls_free(tbs.data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ /* write the signature (bits)
+ */
+ result =
+ asn1_write_value(src, "signature", signature.data,
+ signature.size * 8);
+
+ _gnutls_free_datum(&signature);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* Step 3. Move up and write the AlgorithmIdentifier, which is also
+ * the same.
+ */
+
+ result = _gnutls_x509_write_sign_params(src, "signatureAlgorithm",
+ se, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
diff --git a/lib/x509/spki.c b/lib/x509/spki.c
new file mode 100644
index 0000000..c87ff1b
--- /dev/null
+++ b/lib/x509/spki.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Authors: Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_int.h>
+
+/**
+ * gnutls_x509_spki_init:
+ * @spki: A pointer to the type to be initialized
+ *
+ * This function will initialize a SubjectPublicKeyInfo structure used
+ * in PKIX. The structure is used to set additional parameters
+ * in the public key information field of a certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.0
+ *
+ **/
+int
+gnutls_x509_spki_init(gnutls_x509_spki_t *spki)
+{
+ gnutls_x509_spki_t tmp;
+
+ FAIL_IF_LIB_ERROR;
+
+ tmp =
+ gnutls_calloc(1, sizeof(gnutls_x509_spki_st));
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ *spki = tmp;
+
+ return 0; /* success */
+}
+
+/**
+ * gnutls_x509_spki_deinit:
+ * @spki: the SubjectPublicKeyInfo structure
+ *
+ * This function will deinitialize a SubjectPublicKeyInfo structure.
+ *
+ * Since: 3.6.0
+ *
+ **/
+void
+gnutls_x509_spki_deinit(gnutls_x509_spki_t spki)
+{
+ gnutls_free(spki);
+}
+
+/**
+ * gnutls_x509_spki_set_rsa_pss_params:
+ * @spki: the SubjectPublicKeyInfo structure
+ * @dig: a digest algorithm of type #gnutls_digest_algorithm_t
+ * @salt_size: the size of salt string
+ *
+ * This function will set the public key parameters for
+ * an RSA-PSS algorithm, in the SubjectPublicKeyInfo structure.
+ *
+ * Since: 3.6.0
+ *
+ **/
+void
+gnutls_x509_spki_set_rsa_pss_params(gnutls_x509_spki_t spki,
+ gnutls_digest_algorithm_t dig,
+ unsigned int salt_size)
+{
+ spki->pk = GNUTLS_PK_RSA_PSS;
+ spki->rsa_pss_dig = dig;
+ spki->salt_size = salt_size;
+}
+
+/**
+ * gnutls_x509_spki_get_rsa_pss_params:
+ * @spki: the SubjectPublicKeyInfo structure
+ * @dig: if non-NULL, it will hold the digest algorithm
+ * @salt_size: if non-NULL, it will hold the salt size
+ *
+ * This function will get the public key algorithm parameters
+ * of RSA-PSS type.
+ *
+ * Returns: zero if the parameters are present or a negative
+ * value on error.
+ *
+ * Since: 3.6.0
+ *
+ **/
+int
+gnutls_x509_spki_get_rsa_pss_params(gnutls_x509_spki_t spki,
+ gnutls_digest_algorithm_t *dig,
+ unsigned int *salt_size)
+{
+ if (spki->pk == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (spki->pk != GNUTLS_PK_RSA_PSS)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (dig)
+ *dig = spki->rsa_pss_dig;
+ if (salt_size)
+ *salt_size = spki->salt_size;
+
+ return 0;
+}
diff --git a/lib/x509/supported_exts.gperf b/lib/x509/supported_exts.gperf
new file mode 100644
index 0000000..aa5a6c1
--- /dev/null
+++ b/lib/x509/supported_exts.gperf
@@ -0,0 +1,40 @@
+%{
+%}
+%language=ANSI-C
+%readonly-tables
+%define lookup-function-name is_ext_oid_supported
+%define hash-function-name x509_ext_hash
+
+struct supported_exts_st { const char *name; };
+%%
+#GNUTLS_X509EXT_OID_SUBJECT_KEY_ID
+2.5.29.14
+#GNUTLS_X509EXT_OID_KEY_USAGE
+2.5.29.15
+#GNUTLS_X509EXT_OID_PRIVATE_KEY_USAGE_PERIOD - not supported
+#GNUTLS_X509EXT_OID_SAN
+2.5.29.17
+#GNUTLS_X509EXT_OID_IAN
+2.5.29.18
+#GNUTLS_X509EXT_OID_BASIC_CONSTRAINTS
+2.5.29.19
+#GNUTLS_X509EXT_OID_NAME_CONSTRAINTS
+2.5.29.30
+#GNUTLS_X509EXT_OID_CRL_DIST_POINTS
+2.5.29.31
+#GNUTLS_X509EXT_OID_AUTHORITY_KEY_ID
+2.5.29.35
+#GNUTLS_X509EXT_OID_EXTENDED_KEY_USAGE
+2.5.29.37
+#GNUTLS_X509EXT_OID_AUTHORITY_INFO_ACCESS
+1.3.6.1.5.5.7.1.1
+#GNUTLS_X509EXT_OID_PROXY_CRT_INFO
+1.3.6.1.5.5.7.1.14
+#GNUTLS_X509EXT_OID_TLSFEATURES
+1.3.6.1.5.5.7.1.24
+# We do not support verification with specific policies,
+# as such all the policies and restrictions are acceptable.
+#GNUTLS_X509EXT_OID_CRT_POLICY
+2.5.29.32
+#GNUTLS_X509EXT_OID_INHIBIT_ANYPOLICY
+2.5.29.54
diff --git a/lib/x509/supported_exts.h b/lib/x509/supported_exts.h
new file mode 100644
index 0000000..c7bb97b
--- /dev/null
+++ b/lib/x509/supported_exts.h
@@ -0,0 +1,161 @@
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf --global-table -t supported_exts.gperf */
+/* Computed positions: -k'8-9,17' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
+#endif
+
+#line 1 "supported_exts.gperf"
+
+#line 8 "supported_exts.gperf"
+struct supported_exts_st { const char *name; };
+
+#define TOTAL_KEYWORDS 14
+#define MIN_WORD_LENGTH 9
+#define MAX_WORD_LENGTH 18
+#define MIN_HASH_VALUE 13
+#define MAX_HASH_VALUE 34
+/* maximum key range = 22, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+x509_ext_hash (register const char *str, register size_t len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 0, 35, 3, 10,
+ 4, 5, 4, 0, 35, 15, 13, 14, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ };
+ register unsigned int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[16]];
+ /*FALLTHROUGH*/
+ case 16:
+ case 15:
+ case 14:
+ case 13:
+ case 12:
+ case 11:
+ case 10:
+ case 9:
+ hval += asso_values[(unsigned char)str[8]];
+ /*FALLTHROUGH*/
+ case 8:
+ hval += asso_values[(unsigned char)str[7]];
+ break;
+ }
+ return hval;
+}
+
+static const struct supported_exts_st wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""},
+#line 40 "supported_exts.gperf"
+ {"2.5.29.54"},
+#line 26 "supported_exts.gperf"
+ {"2.5.29.35"},
+ {""}, {""},
+#line 22 "supported_exts.gperf"
+ {"2.5.29.30"},
+#line 38 "supported_exts.gperf"
+ {"2.5.29.32"},
+#line 13 "supported_exts.gperf"
+ {"2.5.29.15"},
+ {""}, {""},
+#line 34 "supported_exts.gperf"
+ {"1.3.6.1.5.5.7.1.24"},
+#line 11 "supported_exts.gperf"
+ {"2.5.29.14"},
+#line 24 "supported_exts.gperf"
+ {"2.5.29.31"},
+ {""}, {""},
+#line 30 "supported_exts.gperf"
+ {"1.3.6.1.5.5.7.1.1"},
+#line 32 "supported_exts.gperf"
+ {"1.3.6.1.5.5.7.1.14"},
+#line 28 "supported_exts.gperf"
+ {"2.5.29.37"},
+ {""}, {""},
+#line 18 "supported_exts.gperf"
+ {"2.5.29.18"},
+#line 20 "supported_exts.gperf"
+ {"2.5.29.19"},
+#line 16 "supported_exts.gperf"
+ {"2.5.29.17"}
+ };
+
+static const struct supported_exts_st *
+is_ext_oid_supported (register const char *str, register size_t len)
+{
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register unsigned int key = x509_ext_hash (str, len);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register const char *s = wordlist[key].name;
+
+ if (*str == *s && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/lib/x509/time.c b/lib/x509/time.c
new file mode 100644
index 0000000..2726600
--- /dev/null
+++ b/lib/x509/time.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <libtasn1.h>
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <str.h>
+#include <x509.h>
+#include <num.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include "extras/hex.h"
+#include <common.h>
+#include <c-ctype.h>
+
+time_t _gnutls_utcTime2gtime(const char *ttime);
+
+/* TIME functions
+ * Conversions between generalized or UTC time to time_t
+ *
+ */
+
+/* This is an emulation of the struct tm.
+ * Since we do not use libc's functions, we don't need to
+ * depend on the libc structure.
+ */
+typedef struct fake_tm {
+ int tm_mon;
+ int tm_year; /* FULL year - ie 1971 */
+ int tm_mday;
+ int tm_hour;
+ int tm_min;
+ int tm_sec;
+} fake_tm;
+
+/* The mktime_utc function is due to Russ Allbery (rra@stanford.edu),
+ * who placed it under public domain:
+ */
+
+/* The number of days in each month.
+ */
+static const int MONTHDAYS[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+ /* Whether a given year is a leap year. */
+#define ISLEAP(year) \
+ (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0))
+
+/*
+ ** Given a struct tm representing a calendar time in UTC, convert it to
+ ** seconds since epoch. Returns (time_t) -1 if the time is not
+ ** convertible. Note that this function does not canonicalize the provided
+ ** struct tm, nor does it allow out of range values or years before 1970.
+ */
+static time_t mktime_utc(const struct fake_tm *tm)
+{
+ time_t result = 0;
+ int i;
+
+/* We do allow some ill-formed dates, but we don't do anything special
+ * with them and our callers really shouldn't pass them to us. Do
+ * explicitly disallow the ones that would cause invalid array accesses
+ * or other algorithm problems.
+ */
+ if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 1970)
+ return (time_t) - 1;
+
+ /* Check for "obvious" mistakes in dates */
+ if (tm->tm_sec > 60 || tm->tm_min > 59 || tm->tm_mday > 31 || tm->tm_mday < 1 || tm->tm_hour > 23)
+ return (time_t) - 1;
+
+/* Convert to a time_t.
+ */
+ for (i = 1970; i < tm->tm_year; i++)
+ result += 365 + ISLEAP(i);
+ for (i = 0; i < tm->tm_mon; i++)
+ result += MONTHDAYS[i];
+ if (tm->tm_mon > 1 && ISLEAP(tm->tm_year))
+ result++;
+ result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour;
+ result = 60 * result + tm->tm_min;
+ result = 60 * result + tm->tm_sec;
+ return result;
+}
+
+
+/* this one will parse dates of the form:
+ * month|day|hour|minute|sec* (2 chars each)
+ * and year is given. Returns a time_t date.
+ */
+static time_t time2gtime(const char *ttime, int year)
+{
+ char xx[4];
+ struct fake_tm etime;
+
+ if (strlen(ttime) < 8) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+ etime.tm_year = year;
+
+ /* In order to work with 32 bit
+ * time_t.
+ */
+ if (sizeof(time_t) <= 4 && etime.tm_year >= 2038)
+ return (time_t) 2145914603; /* 2037-12-31 23:23:23 */
+
+ if (etime.tm_year < 1970)
+ return (time_t) 0;
+
+ xx[2] = 0;
+
+/* get the month
+ */
+ memcpy(xx, ttime, 2); /* month */
+ etime.tm_mon = atoi(xx) - 1;
+ ttime += 2;
+
+/* get the day
+ */
+ memcpy(xx, ttime, 2); /* day */
+ etime.tm_mday = atoi(xx);
+ ttime += 2;
+
+/* get the hour
+ */
+ memcpy(xx, ttime, 2); /* hour */
+ etime.tm_hour = atoi(xx);
+ ttime += 2;
+
+/* get the minutes
+ */
+ memcpy(xx, ttime, 2); /* minutes */
+ etime.tm_min = atoi(xx);
+ ttime += 2;
+
+ if (strlen(ttime) >= 2) {
+ memcpy(xx, ttime, 2);
+ etime.tm_sec = atoi(xx);
+ } else
+ etime.tm_sec = 0;
+
+ return mktime_utc(&etime);
+}
+
+
+/* returns a time_t value that contains the given time.
+ * The given time is expressed as:
+ * YEAR(2)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)*
+ *
+ * (seconds are optional)
+ */
+time_t _gnutls_utcTime2gtime(const char *ttime)
+{
+ char xx[3];
+ int year, i;
+ int len = strlen(ttime);
+
+ if (len < 10) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+#ifdef STRICT_DER_TIME
+ /* Make sure everything else is digits. */
+ for (i = 0; i < len - 1; i++) {
+ if (c_isdigit(ttime[i]))
+ continue;
+ return gnutls_assert_val((time_t)-1);
+ }
+#endif
+ xx[2] = 0;
+
+/* get the year
+ */
+ memcpy(xx, ttime, 2); /* year */
+ year = atoi(xx);
+ ttime += 2;
+
+ if (year > 49)
+ year += 1900;
+ else
+ year += 2000;
+
+ return time2gtime(ttime, year);
+}
+
+/* returns a time_t value that contains the given time.
+ * The given time is expressed as:
+ * YEAR(4)|MONTH(2)|DAY(2)|HOUR(2)|MIN(2)|SEC(2)*
+ */
+time_t _gnutls_x509_generalTime2gtime(const char *ttime)
+{
+ char xx[5];
+ int year;
+
+ if (strlen(ttime) < 12) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+ if (strchr(ttime, 'Z') == 0) {
+ gnutls_assert();
+ /* required to be in GMT */
+ return (time_t) - 1;
+ }
+
+ if (strchr(ttime, '.') != 0) {
+ gnutls_assert();
+ /* no fractional seconds allowed */
+ return (time_t) - 1;
+ }
+ xx[4] = 0;
+
+/* get the year
+ */
+ memcpy(xx, ttime, 4); /* year */
+ year = atoi(xx);
+ ttime += 4;
+
+ return time2gtime(ttime, year);
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-y2k"
+/* tag will contain ASN1_TAG_UTCTime or ASN1_TAG_GENERALIZEDTime */
+static int
+gtime_to_suitable_time(time_t gtime, char *str_time, size_t str_time_size, unsigned *tag)
+{
+ size_t ret;
+ struct tm _tm;
+
+ if (gtime == (time_t)-1
+#if SIZEOF_LONG == 8
+ || gtime >= 253402210800
+#endif
+ ) {
+ if (tag)
+ *tag = ASN1_TAG_GENERALIZEDTime;
+ snprintf(str_time, str_time_size, "99991231235959Z");
+ return 0;
+ }
+
+ if (!gmtime_r(&gtime, &_tm)) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ if (_tm.tm_year >= 150) {
+ if (tag)
+ *tag = ASN1_TAG_GENERALIZEDTime;
+ ret = strftime(str_time, str_time_size, "%Y%m%d%H%M%SZ", &_tm);
+ } else {
+ if (tag)
+ *tag = ASN1_TAG_UTCTime;
+ ret = strftime(str_time, str_time_size, "%y%m%d%H%M%SZ", &_tm);
+ }
+
+ if (!ret) {
+ gnutls_assert();
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ return 0;
+}
+#pragma GCC diagnostic pop
+
+static int
+gtime_to_generalTime(time_t gtime, char *str_time, size_t str_time_size)
+{
+ size_t ret;
+ struct tm _tm;
+
+ if (gtime == (time_t)-1
+#if SIZEOF_LONG == 8
+ || gtime >= 253402210800
+#endif
+ ) {
+ snprintf(str_time, str_time_size, "99991231235959Z");
+ return 0;
+ }
+
+ if (!gmtime_r(&gtime, &_tm)) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ ret = strftime(str_time, str_time_size, "%Y%m%d%H%M%SZ", &_tm);
+ if (!ret) {
+ gnutls_assert();
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ return 0;
+}
+
+
+/* Extracts the time in time_t from the asn1_node given. When should
+ * be something like "tbsCertList.thisUpdate".
+ */
+#define MAX_TIME 64
+time_t _gnutls_x509_get_time(asn1_node c2, const char *where, int force_general)
+{
+ char ttime[MAX_TIME];
+ char name[128];
+ time_t c_time = (time_t) - 1;
+ int len, result;
+
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(c2, where, ttime, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ if (force_general != 0) {
+ c_time = _gnutls_x509_generalTime2gtime(ttime);
+ } else {
+ _gnutls_str_cpy(name, sizeof(name), where);
+
+ /* choice */
+ if (strcmp(ttime, "generalTime") == 0) {
+ if (name[0] == 0)
+ _gnutls_str_cpy(name, sizeof(name),
+ "generalTime");
+ else
+ _gnutls_str_cat(name, sizeof(name),
+ ".generalTime");
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(c2, name, ttime, &len);
+ if (result == ASN1_SUCCESS)
+ c_time =
+ _gnutls_x509_generalTime2gtime(ttime);
+ } else { /* UTCTIME */
+ if (name[0] == 0)
+ _gnutls_str_cpy(name, sizeof(name), "utcTime");
+ else
+ _gnutls_str_cat(name, sizeof(name), ".utcTime");
+ len = sizeof(ttime) - 1;
+ result = asn1_read_value(c2, name, ttime, &len);
+ if (result == ASN1_SUCCESS)
+ c_time = _gnutls_utcTime2gtime(ttime);
+ }
+
+ /* We cannot handle dates after 2031 in 32 bit machines.
+ * a time_t of 64bits has to be used.
+ */
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+ }
+
+ return c_time;
+}
+
+/* Sets the time in time_t in the asn1_node given. Where should
+ * be something like "tbsCertList.thisUpdate".
+ */
+int
+_gnutls_x509_set_time(asn1_node c2, const char *where, time_t tim,
+ int force_general)
+{
+ char str_time[MAX_TIME];
+ char name[128];
+ int result, len;
+ unsigned tag;
+
+ if (force_general != 0) {
+ result =
+ gtime_to_generalTime(tim, str_time, sizeof(str_time));
+ if (result < 0)
+ return gnutls_assert_val(result);
+ len = strlen(str_time);
+ result = asn1_write_value(c2, where, str_time, len);
+ if (result != ASN1_SUCCESS)
+ return gnutls_assert_val(_gnutls_asn2err(result));
+
+ return 0;
+ }
+
+ result = gtime_to_suitable_time(tim, str_time, sizeof(str_time), &tag);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ _gnutls_str_cpy(name, sizeof(name), where);
+ if (tag == ASN1_TAG_UTCTime) {
+ if ((result = asn1_write_value(c2, where, "utcTime", 1)) < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ _gnutls_str_cat(name, sizeof(name), ".utcTime");
+ } else {
+ if ((result = asn1_write_value(c2, where, "generalTime", 1)) < 0) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ _gnutls_str_cat(name, sizeof(name), ".generalTime");
+ }
+
+ len = strlen(str_time);
+ result = asn1_write_value(c2, name, str_time, len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/* This will set a DER encoded Time element. To be used in fields
+ * which are of the ANY.
+ */
+int
+_gnutls_x509_set_raw_time(asn1_node c2, const char *where, time_t tim)
+{
+ char str_time[MAX_TIME];
+ uint8_t buf[128];
+ int result, len, der_len;
+ unsigned tag;
+
+ result =
+ gtime_to_suitable_time(tim, str_time, sizeof(str_time), &tag);
+ if (result < 0)
+ return gnutls_assert_val(result);
+ len = strlen(str_time);
+
+ buf[0] = tag;
+ asn1_length_der(len, buf+1, &der_len);
+
+ if ((unsigned)len > sizeof(buf)-der_len-1) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ memcpy(buf+1+der_len, str_time, len);
+
+ result = asn1_write_value(c2, where, buf, len+1+der_len);
+ if (result != ASN1_SUCCESS)
+ return gnutls_assert_val(_gnutls_asn2err(result));
+ return 0;
+}
+
diff --git a/lib/x509/tls_features.c b/lib/x509/tls_features.c
new file mode 100644
index 0000000..bbb9896
--- /dev/null
+++ b/lib/x509/tls_features.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ *
+ * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <gnutls/x509-ext.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <libtasn1.h>
+#include <pk.h>
+#include <pkcs11_int.h>
+#include "urls.h"
+
+/**
+ * gnutls_x509_tlsfeatures_init:
+ * @f: The TLS features
+ *
+ * This function will initialize a X.509 TLS features extension structure
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_tlsfeatures_init(gnutls_x509_tlsfeatures_t *f)
+{
+ *f = gnutls_calloc(1, sizeof(struct gnutls_x509_tlsfeatures_st));
+ if (*f == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_tlsfeatures_deinit:
+ * @f: The TLS features
+ *
+ * This function will deinitialize a X.509 TLS features extension structure
+ *
+ * Since: 3.5.1
+ **/
+void gnutls_x509_tlsfeatures_deinit(gnutls_x509_tlsfeatures_t f)
+{
+ gnutls_free(f);
+}
+
+/**
+ * gnutls_x509_tlsfeatures_get:
+ * @f: The TLS features
+ * @idx: The index of the feature to get
+ * @feature: If the function succeeds, the feature will be stored in this variable
+ *
+ * This function will get a feature from the X.509 TLS features
+ * extension structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_tlsfeatures_get(gnutls_x509_tlsfeatures_t f, unsigned idx, unsigned int *feature)
+{
+ if (f == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (idx >= f->size) {
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ }
+
+ *feature = f->feature[idx];
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_tlsfeatures:
+ * @crt: A X.509 certificate
+ * @features: If the function succeeds, the
+ * features will be stored in this variable.
+ * @flags: zero or %GNUTLS_EXT_FLAG_APPEND
+ * @critical: the extension status
+ *
+ * This function will get the X.509 TLS features
+ * extension structure from the certificate. The
+ * returned structure needs to be freed using
+ * gnutls_x509_tlsfeatures_deinit().
+ *
+ * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND,
+ * then if the @features structure is empty this function will behave
+ * identically as if the flag was not set. Otherwise if there are elements
+ * in the @features structure then they will be merged with.
+ *
+ * Note that @features must be initialized prior to calling this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_crt_get_tlsfeatures(gnutls_x509_crt_t crt,
+ gnutls_x509_tlsfeatures_t features,
+ unsigned int flags,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t der;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, 0,
+ &der, critical)) < 0)
+ {
+ return ret;
+ }
+
+ if (der.size == 0 || der.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(der.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_set_tlsfeatures:
+ * @crt: A X.509 certificate
+ * @features: If the function succeeds, the
+ * features will be added to the certificate.
+ *
+ * This function will set the certificates
+ * X.509 TLS extension from the given structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_crt_set_tlsfeatures(gnutls_x509_crt_t crt,
+ gnutls_x509_tlsfeatures_t features)
+{
+ int ret;
+ gnutls_datum_t der;
+
+ if (crt == NULL || features == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_ext_export_tlsfeatures(features, &der);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0);
+
+ _gnutls_free_datum(&der);
+
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_tls_features_check_crt:
+ * @feat: a set of TLSFeatures
+ * @cert: the certificate to be checked
+ *
+ * This function will check the provided certificate against the TLSFeatures
+ * set in @feat using the RFC7633 p.4.2.2 rules. It will check whether the certificate
+ * contains the features in @feat or a superset.
+ *
+ * Returns: non-zero if the provided certificate complies, and zero otherwise.
+ *
+ * Since: 3.5.1
+ **/
+unsigned gnutls_x509_tlsfeatures_check_crt(gnutls_x509_tlsfeatures_t feat,
+ gnutls_x509_crt_t cert)
+{
+ int ret;
+ gnutls_x509_tlsfeatures_t cfeat;
+ unsigned i, j, uret, found;
+
+ if (feat->size == 0)
+ return 1; /* shortcut; no constraints to check */
+
+ ret = gnutls_x509_tlsfeatures_init(&cfeat);
+ if (ret < 0)
+ return gnutls_assert_val(0);
+
+ ret = gnutls_x509_crt_get_tlsfeatures(cert, cfeat, 0, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ uret = 0;
+ goto cleanup;
+ }
+
+ /* if cert's features cannot be a superset */
+ if (feat->size > cfeat->size) {
+ _gnutls_debug_log("certificate has %u, while issuer has %u tlsfeatures\n", cfeat->size, feat->size);
+ gnutls_assert();
+ uret = 0;
+ goto cleanup;
+ }
+
+ for (i=0;i<feat->size;i++) {
+ found = 0;
+ for (j=0;j<cfeat->size;j++) {
+ if (feat->feature[i] == cfeat->feature[j]) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ _gnutls_debug_log("feature %d was not found in cert\n", (int)feat->feature[i]);
+ uret = 0;
+ goto cleanup;
+ }
+ }
+
+ uret = 1;
+ cleanup:
+ gnutls_x509_tlsfeatures_deinit(cfeat);
+ return uret;
+}
diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c
new file mode 100644
index 0000000..5698d4f
--- /dev/null
+++ b/lib/x509/verify-high.c
@@ -0,0 +1,1834 @@
+/*
+ * Copyright (C) 2011-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <libtasn1.h>
+#include <global.h>
+#include <num.h> /* MAX */
+#include <tls-sig.h>
+#include <str.h>
+#include <datum.h>
+#include <hash-pjw-bare.h>
+#include "x509_int.h"
+#include <common.h>
+#include <gnutls/x509-ext.h>
+#include "verify-high.h"
+#include "intprops.h"
+
+struct named_cert_st {
+ gnutls_x509_crt_t cert;
+ uint8_t name[MAX_SERVER_NAME_SIZE];
+ unsigned int name_size;
+};
+
+struct node_st {
+ /* The trusted certificates */
+ gnutls_x509_crt_t *trusted_cas;
+ unsigned int trusted_ca_size;
+
+ struct named_cert_st *named_certs;
+ unsigned int named_cert_size;
+
+ /* The trusted CRLs */
+ gnutls_x509_crl_t *crls;
+ unsigned int crl_size;
+};
+
+struct gnutls_x509_trust_list_iter {
+ unsigned int node_index;
+ unsigned int ca_index;
+
+#ifdef ENABLE_PKCS11
+ gnutls_pkcs11_obj_t* pkcs11_list;
+ unsigned int pkcs11_index;
+ unsigned int pkcs11_size;
+#endif
+};
+
+#define DEFAULT_SIZE 127
+
+struct cert_set_node_st {
+ gnutls_x509_crt_t *certs;
+ unsigned int size;
+};
+
+struct cert_set_st {
+ struct cert_set_node_st *node;
+ unsigned int size;
+};
+
+static int
+cert_set_init(struct cert_set_st *set, unsigned int size)
+{
+ memset(set, 0, sizeof(*set));
+
+ set->size = size;
+ set->node = gnutls_calloc(size, sizeof(*set->node));
+ if (!set->node) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ return 0;
+}
+
+static void
+cert_set_deinit(struct cert_set_st *set)
+{
+ size_t i;
+
+ for (i = 0; i < set->size; i++) {
+ gnutls_free(set->node[i].certs);
+ }
+
+ gnutls_free(set->node);
+}
+
+static bool
+cert_set_contains(struct cert_set_st *set, const gnutls_x509_crt_t cert)
+{
+ size_t hash, i;
+
+ hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size);
+ hash %= set->size;
+
+ for (i = 0; i < set->node[hash].size; i++) {
+ if (unlikely(gnutls_x509_crt_equals(set->node[hash].certs[i], cert))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int
+cert_set_add(struct cert_set_st *set, const gnutls_x509_crt_t cert)
+{
+ size_t hash;
+
+ hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size);
+ hash %= set->size;
+
+ if (unlikely(INT_ADD_OVERFLOW(set->node[hash].size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ set->node[hash].certs =
+ _gnutls_reallocarray_fast(set->node[hash].certs,
+ set->node[hash].size + 1,
+ sizeof(*set->node[hash].certs));
+ if (!set->node[hash].certs) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ set->node[hash].certs[set->node[hash].size] = cert;
+ set->node[hash].size++;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_trust_list_init:
+ * @list: A pointer to the type to be initialized
+ * @size: The size of the internal hash table. Use (0) for default size.
+ *
+ * This function will initialize an X.509 trust list structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_x509_trust_list_init(gnutls_x509_trust_list_t * list,
+ unsigned int size)
+{
+ gnutls_x509_trust_list_t tmp;
+
+ FAIL_IF_LIB_ERROR;
+
+ tmp =
+ gnutls_calloc(1, sizeof(struct gnutls_x509_trust_list_st));
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ if (size == 0)
+ size = DEFAULT_SIZE;
+ tmp->size = size;
+
+ tmp->node = gnutls_calloc(1, tmp->size * sizeof(tmp->node[0]));
+ if (tmp->node == NULL) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ *list = tmp;
+
+ return 0; /* success */
+}
+
+/**
+ * gnutls_x509_trust_list_deinit:
+ * @list: The list to be deinitialized
+ * @all: if non-zero it will deinitialize all the certificates and CRLs contained in the structure.
+ *
+ * This function will deinitialize a trust list. Note that the
+ * @all flag should be typically non-zero unless you have specified
+ * your certificates using gnutls_x509_trust_list_add_cas() and you
+ * want to prevent them from being deinitialized by this function.
+ *
+ * Since: 3.0.0
+ **/
+void
+gnutls_x509_trust_list_deinit(gnutls_x509_trust_list_t list,
+ unsigned int all)
+{
+ unsigned int i, j;
+
+ if (!list)
+ return;
+
+ for (j = 0; j < list->blacklisted_size; j++) {
+ gnutls_x509_crt_deinit(list->blacklisted[j]);
+ }
+ gnutls_free(list->blacklisted);
+
+ for (j = 0; j < list->keep_certs_size; j++) {
+ gnutls_x509_crt_deinit(list->keep_certs[j]);
+ }
+ gnutls_free(list->keep_certs);
+
+ for (i = 0; i < list->size; i++) {
+ if (all) {
+ for (j = 0; j < list->node[i].trusted_ca_size; j++) {
+ gnutls_x509_crt_deinit(list->node[i].
+ trusted_cas[j]);
+ }
+ }
+ gnutls_free(list->node[i].trusted_cas);
+
+
+ if (all) {
+ for (j = 0; j < list->node[i].crl_size; j++) {
+ gnutls_x509_crl_deinit(list->node[i].
+ crls[j]);
+ }
+ }
+ gnutls_free(list->node[i].crls);
+
+ if (all) {
+ for (j = 0; j < list->node[i].named_cert_size; j++) {
+ gnutls_x509_crt_deinit(list->node[i].
+ named_certs[j].
+ cert);
+ }
+ }
+ gnutls_free(list->node[i].named_certs);
+ }
+
+ gnutls_free(list->x509_rdn_sequence.data);
+ gnutls_free(list->node);
+ gnutls_free(list->pkcs11_token);
+ gnutls_free(list);
+}
+
+static int
+add_new_ca_to_rdn_seq(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t ca)
+{
+ gnutls_datum_t tmp;
+ size_t newsize;
+ unsigned char *newdata, *p;
+
+ /* Add DN of the last added CAs to the RDN sequence
+ * This will be sent to clients when a certificate
+ * request message is sent.
+ */
+ tmp.data = ca->raw_dn.data;
+ tmp.size = ca->raw_dn.size;
+
+ newsize = list->x509_rdn_sequence.size + 2 + tmp.size;
+ if (newsize < list->x509_rdn_sequence.size) {
+ gnutls_assert();
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ newdata =
+ gnutls_realloc_fast(list->x509_rdn_sequence.data,
+ newsize);
+ if (newdata == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ p = newdata + list->x509_rdn_sequence.size;
+ _gnutls_write_uint16(tmp.size, p);
+ if (tmp.data != NULL)
+ memcpy(p + 2, tmp.data, tmp.size);
+
+ list->x509_rdn_sequence.size = newsize;
+ list->x509_rdn_sequence.data = newdata;
+
+ return 0;
+}
+
+#ifdef ENABLE_PKCS11
+/* Keeps the provided certificate in a structure that will be
+ * deallocated on deinit. This is to handle get_issuer() with
+ * pkcs11 trust modules when the GNUTLS_TL_GET_COPY flag isn't
+ * given. It is not thread safe. */
+static int
+trust_list_add_compat(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert)
+{
+ if (unlikely(INT_ADD_OVERFLOW(list->keep_certs_size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ list->keep_certs =
+ _gnutls_reallocarray_fast(list->keep_certs,
+ list->keep_certs_size + 1,
+ sizeof(list->keep_certs[0]));
+ if (list->keep_certs == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ list->keep_certs[list->keep_certs_size] = cert;
+ list->keep_certs_size++;
+
+ return 0;
+}
+#endif
+
+/**
+ * gnutls_x509_trust_list_add_cas:
+ * @list: The list
+ * @clist: A list of CAs
+ * @clist_size: The length of the CA list
+ * @flags: flags from %gnutls_trust_list_flags_t
+ *
+ * This function will add the given certificate authorities
+ * to the trusted list. The CAs in @clist must not be deinitialized
+ * during the lifetime of @list.
+ *
+ * If the flag %GNUTLS_TL_NO_DUPLICATES is specified, then
+ * this function will ensure that no duplicates will be
+ * present in the final trust list.
+ *
+ * If the flag %GNUTLS_TL_NO_DUPLICATE_KEY is specified, then
+ * this function will ensure that no certificates with the
+ * same key are present in the final trust list.
+ *
+ * If either %GNUTLS_TL_NO_DUPLICATE_KEY or %GNUTLS_TL_NO_DUPLICATES
+ * are given, gnutls_x509_trust_list_deinit() must be called with parameter
+ * @all being 1.
+ *
+ * Returns: The number of added elements is returned; that includes
+ * duplicate entries.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_x509_trust_list_add_cas(gnutls_x509_trust_list_t list,
+ const gnutls_x509_crt_t * clist,
+ unsigned clist_size, unsigned int flags)
+{
+ unsigned i, j;
+ size_t hash;
+ int ret;
+ unsigned exists;
+
+ for (i = 0; i < clist_size; i++) {
+ exists = 0;
+ hash =
+ hash_pjw_bare(clist[i]->raw_dn.data,
+ clist[i]->raw_dn.size);
+ hash %= list->size;
+
+ /* avoid duplicates */
+ if (flags & GNUTLS_TL_NO_DUPLICATES || flags & GNUTLS_TL_NO_DUPLICATE_KEY) {
+ for (j=0;j<list->node[hash].trusted_ca_size;j++) {
+ if (flags & GNUTLS_TL_NO_DUPLICATES)
+ ret = gnutls_x509_crt_equals(list->node[hash].trusted_cas[j], clist[i]);
+ else
+ ret = _gnutls_check_if_same_key(list->node[hash].trusted_cas[j], clist[i], 1);
+ if (ret != 0) {
+ exists = 1;
+ break;
+ }
+ }
+
+ if (exists != 0) {
+ gnutls_x509_crt_deinit(list->node[hash].trusted_cas[j]);
+ list->node[hash].trusted_cas[j] = clist[i];
+ continue;
+ }
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(list->node[hash].trusted_ca_size, 1))) {
+ gnutls_assert();
+ return i;
+ }
+
+ list->node[hash].trusted_cas =
+ _gnutls_reallocarray_fast(list->node[hash].trusted_cas,
+ list->node[hash].trusted_ca_size + 1,
+ sizeof(list->node[hash].trusted_cas[0]));
+ if (list->node[hash].trusted_cas == NULL) {
+ gnutls_assert();
+ return i;
+ }
+
+ if (gnutls_x509_crt_get_version(clist[i]) >= 3 &&
+ gnutls_x509_crt_get_ca_status(clist[i], NULL) <= 0) {
+ gnutls_datum_t dn;
+ gnutls_assert();
+ if (gnutls_x509_crt_get_dn2(clist[i], &dn) >= 0) {
+ _gnutls_audit_log(NULL,
+ "There was a non-CA certificate in the trusted list: %s.\n",
+ dn.data);
+ gnutls_free(dn.data);
+ }
+ }
+
+ list->node[hash].trusted_cas[list->node[hash].
+ trusted_ca_size] = clist[i];
+ list->node[hash].trusted_ca_size++;
+
+ if (flags & GNUTLS_TL_USE_IN_TLS) {
+ ret = add_new_ca_to_rdn_seq(list, clist[i]);
+ if (ret < 0) {
+ gnutls_assert();
+ return i+1;
+ }
+ }
+ }
+
+ return i;
+}
+
+static int
+advance_iter(gnutls_x509_trust_list_t list,
+ gnutls_x509_trust_list_iter_t iter)
+{
+ if (iter->node_index < list->size) {
+ ++iter->ca_index;
+
+ /* skip entries */
+ while (iter->node_index < list->size &&
+ iter->ca_index >= list->node[iter->node_index].trusted_ca_size) {
+ ++iter->node_index;
+ iter->ca_index = 0;
+ }
+
+ if (iter->node_index < list->size)
+ return 0;
+ }
+
+#ifdef ENABLE_PKCS11
+ if (list->pkcs11_token != NULL) {
+ if (iter->pkcs11_list == NULL) {
+ int ret = gnutls_pkcs11_obj_list_import_url2(&iter->pkcs11_list, &iter->pkcs11_size,
+ list->pkcs11_token, (GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED), 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (iter->pkcs11_size > 0)
+ return 0;
+ } else if (iter->pkcs11_index < iter->pkcs11_size) {
+ ++iter->pkcs11_index;
+ if (iter->pkcs11_index < iter->pkcs11_size)
+ return 0;
+ }
+ }
+#endif
+
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+}
+
+/**
+ * gnutls_x509_trust_list_iter_get_ca:
+ * @list: The list
+ * @iter: A pointer to an iterator (initially the iterator should be %NULL)
+ * @crt: where the certificate will be copied
+ *
+ * This function obtains a certificate in the trust list and advances the
+ * iterator to the next certificate. The certificate returned in @crt must be
+ * deallocated with gnutls_x509_crt_deinit().
+ *
+ * When past the last element is accessed %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * is returned and the iterator is reset.
+ *
+ * The iterator is deinitialized and reset to %NULL automatically by this
+ * function after iterating through all elements until
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned. If the iteration is
+ * aborted early, it must be manually deinitialized using
+ * gnutls_x509_trust_list_iter_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_x509_trust_list_iter_get_ca(gnutls_x509_trust_list_t list,
+ gnutls_x509_trust_list_iter_t *iter,
+ gnutls_x509_crt_t *crt)
+{
+ int ret;
+
+ /* initialize iterator */
+ if (*iter == NULL) {
+ *iter = gnutls_malloc(sizeof (struct gnutls_x509_trust_list_iter));
+ if (*iter == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ (*iter)->node_index = 0;
+ (*iter)->ca_index = 0;
+
+#ifdef ENABLE_PKCS11
+ (*iter)->pkcs11_list = NULL;
+ (*iter)->pkcs11_size = 0;
+ (*iter)->pkcs11_index = 0;
+#endif
+
+ /* Advance iterator to the first valid entry */
+ if (list->node[0].trusted_ca_size == 0) {
+ ret = advance_iter(list, *iter);
+ if (ret != 0) {
+ gnutls_x509_trust_list_iter_deinit(*iter);
+ *iter = NULL;
+
+ *crt = NULL;
+ return gnutls_assert_val(ret);
+ }
+ }
+ }
+
+ /* obtain the certificate at the current iterator position */
+ if ((*iter)->node_index < list->size) {
+ ret = gnutls_x509_crt_init(crt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_x509_crt_cpy(*crt, list->node[(*iter)->node_index].trusted_cas[(*iter)->ca_index]);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(*crt);
+ return gnutls_assert_val(ret);
+ }
+ }
+#ifdef ENABLE_PKCS11
+ else if ( (*iter)->pkcs11_index < (*iter)->pkcs11_size) {
+ ret = gnutls_x509_crt_init(crt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_x509_crt_import_pkcs11(*crt, (*iter)->pkcs11_list[(*iter)->pkcs11_index]);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(*crt);
+ return gnutls_assert_val(ret);
+ }
+ }
+#endif
+
+ else {
+ /* iterator is at end */
+ gnutls_x509_trust_list_iter_deinit(*iter);
+ *iter = NULL;
+
+ *crt = NULL;
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ }
+
+ /* Move iterator to the next position.
+ * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned if the iterator
+ * has been moved to the end position. That is okay, we return the
+ * certificate that we read and when this function is called again we
+ * report GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE to our caller. */
+ ret = advance_iter(list, *iter);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_x509_crt_deinit(*crt);
+ *crt = NULL;
+
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_trust_list_iter_deinit:
+ * @iter: The iterator structure to be deinitialized
+ *
+ * This function will deinitialize an iterator structure.
+ *
+ * Since: 3.4.0
+ **/
+void gnutls_x509_trust_list_iter_deinit(gnutls_x509_trust_list_iter_t iter)
+{
+ if (!iter)
+ return;
+
+#ifdef ENABLE_PKCS11
+ if (iter->pkcs11_size > 0) {
+ unsigned i;
+ for (i = 0; i < iter->pkcs11_size; ++i)
+ gnutls_pkcs11_obj_deinit(iter->pkcs11_list[i]);
+ gnutls_free(iter->pkcs11_list);
+ }
+#endif
+
+ gnutls_free(iter);
+}
+
+static gnutls_x509_crt_t crt_cpy(gnutls_x509_crt_t src)
+{
+gnutls_x509_crt_t dst;
+int ret;
+
+ ret = gnutls_x509_crt_init(&dst);
+ if (ret < 0) {
+ gnutls_assert();
+ return NULL;
+ }
+
+ ret = _gnutls_x509_crt_cpy(dst, src);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(dst);
+ gnutls_assert();
+ return NULL;
+ }
+
+ return dst;
+}
+
+/**
+ * gnutls_x509_trust_list_remove_cas:
+ * @list: The list
+ * @clist: A list of CAs
+ * @clist_size: The length of the CA list
+ *
+ * This function will remove the given certificate authorities
+ * from the trusted list.
+ *
+ * Note that this function can accept certificates and authorities
+ * not yet known. In that case they will be kept in a separate
+ * black list that will be used during certificate verification.
+ * Unlike gnutls_x509_trust_list_add_cas() there is no deinitialization
+ * restriction for certificate list provided in this function.
+ *
+ * Returns: The number of removed elements is returned.
+ *
+ * Since: 3.1.10
+ **/
+int
+gnutls_x509_trust_list_remove_cas(gnutls_x509_trust_list_t list,
+ const gnutls_x509_crt_t * clist,
+ unsigned clist_size)
+{
+ int r = 0;
+ unsigned j, i;
+ size_t hash;
+
+ for (i = 0; i < clist_size; i++) {
+ hash =
+ hash_pjw_bare(clist[i]->raw_dn.data,
+ clist[i]->raw_dn.size);
+ hash %= list->size;
+
+ for (j = 0; j < list->node[hash].trusted_ca_size; j++) {
+ if (gnutls_x509_crt_equals
+ (clist[i],
+ list->node[hash].trusted_cas[j]) != 0) {
+
+ gnutls_x509_crt_deinit(list->node[hash].
+ trusted_cas[j]);
+ list->node[hash].trusted_cas[j] =
+ list->node[hash].trusted_cas[list->
+ node
+ [hash].
+ trusted_ca_size
+ - 1];
+ list->node[hash].trusted_ca_size--;
+ r++;
+ break;
+ }
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(list->blacklisted_size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ /* Add the CA (or plain) certificate to the black list as well.
+ * This will prevent a subordinate CA from being valid, and
+ * ensure that a server certificate will also get rejected.
+ */
+ list->blacklisted =
+ _gnutls_reallocarray_fast(list->blacklisted,
+ list->blacklisted_size + 1,
+ sizeof(list->blacklisted[0]));
+ if (list->blacklisted == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ list->blacklisted[list->blacklisted_size] = crt_cpy(clist[i]);
+ if (list->blacklisted[list->blacklisted_size] != NULL)
+ list->blacklisted_size++;
+ }
+
+ return r;
+}
+
+/**
+ * gnutls_x509_trust_list_add_named_crt:
+ * @list: The list
+ * @cert: A certificate
+ * @name: An identifier for the certificate
+ * @name_size: The size of the identifier
+ * @flags: should be 0.
+ *
+ * This function will add the given certificate to the trusted
+ * list and associate it with a name. The certificate will not be
+ * be used for verification with gnutls_x509_trust_list_verify_crt()
+ * but with gnutls_x509_trust_list_verify_named_crt() or
+ * gnutls_x509_trust_list_verify_crt2() - the latter only since
+ * GnuTLS 3.4.0 and if a hostname is provided.
+ *
+ * In principle this function can be used to set individual "server"
+ * certificates that are trusted by the user for that specific server
+ * but for no other purposes.
+ *
+ * The certificate @cert must not be deinitialized during the lifetime
+ * of the @list.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_x509_trust_list_add_named_crt(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert,
+ const void *name, size_t name_size,
+ unsigned int flags)
+{
+ size_t hash;
+
+ if (name_size >= MAX_SERVER_NAME_SIZE)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ hash =
+ hash_pjw_bare(cert->raw_issuer_dn.data,
+ cert->raw_issuer_dn.size);
+ hash %= list->size;
+
+ if (unlikely(INT_ADD_OVERFLOW(list->node[hash].named_cert_size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ list->node[hash].named_certs =
+ _gnutls_reallocarray_fast(list->node[hash].named_certs,
+ list->node[hash].named_cert_size + 1,
+ sizeof(list->node[hash].named_certs[0]));
+ if (list->node[hash].named_certs == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ list->node[hash].named_certs[list->node[hash].named_cert_size].
+ cert = cert;
+ memcpy(list->node[hash].
+ named_certs[list->node[hash].named_cert_size].name, name,
+ name_size);
+ list->node[hash].named_certs[list->node[hash].
+ named_cert_size].name_size =
+ name_size;
+
+ list->node[hash].named_cert_size++;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_trust_list_add_crls:
+ * @list: The list
+ * @crl_list: A list of CRLs
+ * @crl_size: The length of the CRL list
+ * @flags: flags from %gnutls_trust_list_flags_t
+ * @verification_flags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
+ *
+ * This function will add the given certificate revocation lists
+ * to the trusted list. The CRLs in @crl_list must not be deinitialized
+ * during the lifetime of @list.
+ *
+ * This function must be called after gnutls_x509_trust_list_add_cas()
+ * to allow verifying the CRLs for validity. If the flag %GNUTLS_TL_NO_DUPLICATES
+ * is given, then the final CRL list will not contain duplicate entries.
+ *
+ * If the flag %GNUTLS_TL_NO_DUPLICATES is given, gnutls_x509_trust_list_deinit() must be
+ * called with parameter @all being 1.
+ *
+ * If flag %GNUTLS_TL_VERIFY_CRL is given the CRLs will be verified before being added,
+ * and if verification fails, they will be skipped.
+ *
+ * Returns: The number of added elements is returned; that includes
+ * duplicate entries.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_trust_list_add_crls(gnutls_x509_trust_list_t list,
+ const gnutls_x509_crl_t * crl_list,
+ unsigned crl_size, unsigned int flags,
+ unsigned int verification_flags)
+{
+ int ret;
+ unsigned x, i, j = 0;
+ unsigned int vret = 0;
+ size_t hash;
+ gnutls_x509_crl_t *tmp;
+
+ /* Probably we can optimize things such as removing duplicates
+ * etc.
+ */
+ if (crl_size == 0 || crl_list == NULL)
+ return 0;
+
+ for (i = 0; i < crl_size; i++) {
+ hash =
+ hash_pjw_bare(crl_list[i]->raw_issuer_dn.data,
+ crl_list[i]->raw_issuer_dn.size);
+ hash %= list->size;
+
+ if (flags & GNUTLS_TL_VERIFY_CRL) {
+
+ ret =
+ gnutls_x509_crl_verify(crl_list[i],
+ list->node[hash].
+ trusted_cas,
+ list->node[hash].
+ trusted_ca_size,
+ verification_flags,
+ &vret);
+ if (ret < 0 || vret != 0) {
+ _gnutls_debug_log("CRL verification failed, not adding it\n");
+ if (flags & GNUTLS_TL_NO_DUPLICATES)
+ gnutls_x509_crl_deinit(crl_list[i]);
+ if (flags & GNUTLS_TL_FAIL_ON_INVALID_CRL)
+ return gnutls_assert_val(GNUTLS_E_CRL_VERIFICATION_ERROR);
+ continue;
+ }
+ }
+
+ /* If the CRL added overrides a previous one, then overwrite
+ * the old one */
+ if (flags & GNUTLS_TL_NO_DUPLICATES) {
+ for (x=0;x<list->node[hash].crl_size;x++) {
+ if (crl_list[i]->raw_issuer_dn.size == list->node[hash].crls[x]->raw_issuer_dn.size &&
+ memcmp(crl_list[i]->raw_issuer_dn.data, list->node[hash].crls[x]->raw_issuer_dn.data, crl_list[i]->raw_issuer_dn.size) == 0) {
+ if (gnutls_x509_crl_get_this_update(crl_list[i]) >=
+ gnutls_x509_crl_get_this_update(list->node[hash].crls[x])) {
+
+ gnutls_x509_crl_deinit(list->node[hash].crls[x]);
+ list->node[hash].crls[x] = crl_list[i];
+ goto next;
+ } else {
+ /* The new is older, discard it */
+ gnutls_x509_crl_deinit(crl_list[i]);
+ goto next;
+ }
+ }
+ }
+ }
+
+ if (unlikely(INT_ADD_OVERFLOW(list->node[hash].crl_size, 1))) {
+ gnutls_assert();
+ goto error;
+ }
+
+ tmp = _gnutls_reallocarray(list->node[hash].crls,
+ list->node[hash].crl_size + 1,
+ sizeof(list->node[hash].crls[0]));
+ if (tmp == NULL) {
+ gnutls_assert();
+ goto error;
+ }
+ list->node[hash].crls = tmp;
+
+
+ list->node[hash].crls[list->node[hash].crl_size] =
+ crl_list[i];
+ list->node[hash].crl_size++;
+
+ next:
+ j++;
+ }
+
+ return j;
+
+ error:
+ ret = i;
+ if (flags & GNUTLS_TL_NO_DUPLICATES)
+ while (i < crl_size)
+ gnutls_x509_crl_deinit(crl_list[i++]);
+ return ret;
+}
+
+/* Takes a certificate list and shortens it if there are
+ * intermedia certificates already trusted by us.
+ *
+ * Returns the new size of the list or a negative number on error.
+ */
+static int shorten_clist(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t * certificate_list,
+ unsigned int clist_size)
+{
+ unsigned int j, i;
+ size_t hash;
+
+ if (clist_size > 1) {
+ /* Check if the last certificate in the path is self signed.
+ * In that case ignore it (a certificate is trusted only if it
+ * leads to a trusted party by us, not the server's).
+ *
+ * This prevents from verifying self signed certificates against
+ * themselves. This (although not bad) caused verification
+ * failures on some root self signed certificates that use the
+ * MD2 algorithm.
+ */
+ if (gnutls_x509_crt_check_issuer
+ (certificate_list[clist_size - 1],
+ certificate_list[clist_size - 1]) != 0) {
+ clist_size--;
+ }
+ }
+
+ /* We want to shorten the chain by removing the cert that matches
+ * one of the certs we trust and all the certs after that i.e. if
+ * cert chain is A signed-by B signed-by C signed-by D (signed-by
+ * self-signed E but already removed above), and we trust B, remove
+ * B, C and D. */
+ for (i = 1; i < clist_size; i++) {
+ hash =
+ hash_pjw_bare(certificate_list[i]->raw_issuer_dn.data,
+ certificate_list[i]->raw_issuer_dn.size);
+ hash %= list->size;
+
+ for (j = 0; j < list->node[hash].trusted_ca_size; j++) {
+ if (gnutls_x509_crt_equals
+ (certificate_list[i],
+ list->node[hash].trusted_cas[j]) != 0) {
+ /* cut the list at the point of first the trusted certificate */
+ clist_size = i + 1;
+ break;
+ }
+ }
+ /* clist_size may have been changed which gets out of loop */
+ }
+
+ return clist_size;
+}
+
+/* Takes a subject certificate, retrieves a chain from its issuers in
+ * @certificate_list, using the issuer callback set for @list.
+ *
+ * Returns the new size of the list or a negative number on error.
+ */
+static int
+retrieve_issuers(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t subject,
+ gnutls_x509_crt_t *certificate_list,
+ unsigned int clist_size_max)
+{
+ gnutls_x509_crt_t *issuers;
+ unsigned int issuers_size;
+ unsigned int i;
+ int ret;
+
+ if (!list->issuer_callback) {
+ return 0;
+ }
+
+ _gnutls_cert_log("calling issuer callback on", subject);
+
+ ret = list->issuer_callback(list, subject, &issuers, &issuers_size);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ /* Ignore empty list */
+ if (!issuers_size) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (issuers_size > clist_size_max) {
+ _gnutls_debug_log("too many issuers returned; skipping\n");
+ ret = 0;
+ goto cleanup;
+ }
+
+ for (i = 0; i < issuers_size; i++) {
+ if (!gnutls_x509_crt_check_issuer(subject, issuers[i])) {
+ _gnutls_cert_log("unrelated certificate; skipping",
+ issuers[i]);
+ break;
+ }
+ subject = issuers[i];
+ }
+
+ ret = i;
+
+ memcpy(certificate_list, issuers, ret * sizeof(gnutls_x509_crt_t));
+
+ cleanup:
+ for (i = ret; i < issuers_size; i++) {
+ gnutls_x509_crt_deinit(issuers[i]);
+ }
+ gnutls_free(issuers);
+
+ return ret;
+}
+
+int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t * issuer,
+ unsigned int flags)
+{
+ int ret;
+ unsigned int i;
+ size_t hash;
+
+ hash =
+ hash_pjw_bare(cert->raw_issuer_dn.data,
+ cert->raw_issuer_dn.size);
+ hash %= list->size;
+
+ for (i = 0; i < list->node[hash].trusted_ca_size; i++) {
+ ret =
+ gnutls_x509_crt_check_issuer(cert,
+ list->node[hash].
+ trusted_cas[i]);
+ if (ret != 0) {
+ if (flags & GNUTLS_TL_GET_COPY) {
+ *issuer = crt_cpy(list->node[hash].trusted_cas[i]);
+ } else {
+ *issuer = list->node[hash].trusted_cas[i];
+ }
+ return 0;
+ }
+ }
+
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+}
+
+static
+int trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list,
+ const gnutls_datum_t *dn,
+ const gnutls_datum_t *spki,
+ gnutls_x509_crt_t * issuer,
+ unsigned int flags)
+{
+ int ret;
+ unsigned int i, j;
+ size_t hash;
+ uint8_t tmp[256];
+ size_t tmp_size;
+
+ if (dn) {
+ hash =
+ hash_pjw_bare(dn->data,
+ dn->size);
+ hash %= list->size;
+
+ for (i = 0; i < list->node[hash].trusted_ca_size; i++) {
+ ret = _gnutls_x509_compare_raw_dn(dn, &list->node[hash].trusted_cas[i]->raw_dn);
+ if (ret != 0) {
+ if (spki && spki->size > 0) {
+ tmp_size = sizeof(tmp);
+
+ ret = gnutls_x509_crt_get_subject_key_id(list->node[hash].trusted_cas[i], tmp, &tmp_size, NULL);
+ if (ret < 0)
+ continue;
+ if (spki->size != tmp_size || memcmp(spki->data, tmp, spki->size) != 0)
+ continue;
+ }
+ *issuer = crt_cpy(list->node[hash].trusted_cas[i]);
+ return 0;
+ }
+ }
+ } else if (spki) {
+ /* search everything! */
+ for (i = 0; i < list->size; i++) {
+ for (j = 0; j < list->node[i].trusted_ca_size; j++) {
+ tmp_size = sizeof(tmp);
+
+ ret = gnutls_x509_crt_get_subject_key_id(list->node[i].trusted_cas[j], tmp, &tmp_size, NULL);
+ if (ret < 0)
+ continue;
+
+ if (spki->size != tmp_size || memcmp(spki->data, tmp, spki->size) != 0)
+ continue;
+
+ *issuer = crt_cpy(list->node[i].trusted_cas[j]);
+ return 0;
+ }
+ }
+ }
+
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+}
+
+/**
+ * gnutls_x509_trust_list_get_issuer:
+ * @list: The list
+ * @cert: is the certificate to find issuer for
+ * @issuer: Will hold the issuer if any. Should be treated as constant
+ * unless %GNUTLS_TL_GET_COPY is set in @flags.
+ * @flags: flags from %gnutls_trust_list_flags_t (%GNUTLS_TL_GET_COPY is applicable)
+ *
+ * This function will find the issuer of the given certificate.
+ * If the flag %GNUTLS_TL_GET_COPY is specified a copy of the issuer
+ * will be returned which must be freed using gnutls_x509_crt_deinit().
+ * In that case the provided @issuer must not be initialized.
+ *
+ * Note that the flag %GNUTLS_TL_GET_COPY is required for this function
+ * to work with PKCS#11 trust lists in a thread-safe way.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0
+ **/
+int gnutls_x509_trust_list_get_issuer(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t * issuer,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = _gnutls_trust_list_get_issuer(list, cert, issuer, flags);
+ if (ret == 0) {
+ return 0;
+ }
+
+#ifdef ENABLE_PKCS11
+ if (ret < 0 && list->pkcs11_token) {
+ gnutls_x509_crt_t crt;
+ gnutls_datum_t der = {NULL, 0};
+ /* use the token for verification */
+ ret = gnutls_pkcs11_get_raw_issuer(list->pkcs11_token, cert, &der,
+ GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ gnutls_free(der.data);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER);
+ gnutls_free(der.data);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(crt);
+ return gnutls_assert_val(ret);
+ }
+
+ if (flags & GNUTLS_TL_GET_COPY) {
+ *issuer = crt;
+ return 0;
+ } else {
+ /* we add this CA to the keep_cert list in order to make it
+ * persistent. It will be deallocated when the trust list is.
+ */
+ ret = trust_list_add_compat(list, crt);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(crt);
+ return gnutls_assert_val(ret);
+ }
+ *issuer = crt;
+ return ret;
+ }
+ }
+#endif
+ return ret;
+}
+
+/**
+ * gnutls_x509_trust_list_get_issuer_by_dn:
+ * @list: The list
+ * @dn: is the issuer's DN
+ * @issuer: Will hold the issuer if any. Should be deallocated after use.
+ * @flags: Use zero
+ *
+ * This function will find the issuer with the given name, and
+ * return a copy of the issuer, which must be freed using gnutls_x509_crt_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ **/
+int gnutls_x509_trust_list_get_issuer_by_dn(gnutls_x509_trust_list_t list,
+ const gnutls_datum_t *dn,
+ gnutls_x509_crt_t *issuer,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = trust_list_get_issuer_by_dn(list, dn, NULL, issuer, flags);
+ if (ret == 0) {
+ return 0;
+ }
+
+#ifdef ENABLE_PKCS11
+ if (ret < 0 && list->pkcs11_token) {
+ gnutls_x509_crt_t crt;
+ gnutls_datum_t der = {NULL, 0};
+ /* use the token for verification */
+ ret = gnutls_pkcs11_get_raw_issuer_by_dn(list->pkcs11_token, dn, &der,
+ GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ gnutls_free(der.data);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER);
+ gnutls_free(der.data);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(crt);
+ return gnutls_assert_val(ret);
+ }
+
+ *issuer = crt;
+ return 0;
+ }
+#endif
+ return ret;
+}
+
+/**
+ * gnutls_x509_trust_list_get_issuer_by_subject_key_id:
+ * @list: The list
+ * @dn: is the issuer's DN (may be %NULL)
+ * @spki: is the subject key ID
+ * @issuer: Will hold the issuer if any. Should be deallocated after use.
+ * @flags: Use zero
+ *
+ * This function will find the issuer with the given name and subject key ID, and
+ * return a copy of the issuer, which must be freed using gnutls_x509_crt_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.2
+ **/
+int gnutls_x509_trust_list_get_issuer_by_subject_key_id(gnutls_x509_trust_list_t list,
+ const gnutls_datum_t *dn,
+ const gnutls_datum_t *spki,
+ gnutls_x509_crt_t *issuer,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = trust_list_get_issuer_by_dn(list, dn, spki, issuer, flags);
+ if (ret == 0) {
+ return 0;
+ }
+
+#ifdef ENABLE_PKCS11
+ if (ret < 0 && list->pkcs11_token) {
+ gnutls_x509_crt_t crt;
+ gnutls_datum_t der = {NULL, 0};
+ /* use the token for verification */
+ ret = gnutls_pkcs11_get_raw_issuer_by_subject_key_id(list->pkcs11_token, dn, spki, &der,
+ GNUTLS_X509_FMT_DER, GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ gnutls_free(der.data);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = gnutls_x509_crt_import(crt, &der, GNUTLS_X509_FMT_DER);
+ gnutls_free(der.data);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(crt);
+ return gnutls_assert_val(ret);
+ }
+
+ *issuer = crt;
+ return 0;
+ }
+#endif
+ return ret;
+}
+
+static
+int check_if_in_blacklist(gnutls_x509_crt_t * cert_list, unsigned int cert_list_size,
+ gnutls_x509_crt_t * blacklist, unsigned int blacklist_size)
+{
+unsigned i, j;
+
+ if (blacklist_size == 0)
+ return 0;
+
+ for (i=0;i<cert_list_size;i++) {
+ for (j=0;j<blacklist_size;j++) {
+ if (gnutls_x509_crt_equals(cert_list[i], blacklist[j]) != 0) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_trust_list_verify_crt:
+ * @list: The list
+ * @cert_list: is the certificate list to be verified
+ * @cert_list_size: is the certificate list size
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ * @voutput: will hold the certificate verification output.
+ * @func: If non-null will be called on each chain element verification with the output.
+ *
+ * This function will try to verify the given certificate and return
+ * its status. The @voutput parameter will hold an OR'ed sequence of
+ * %gnutls_certificate_status_t flags.
+ *
+ * The details of the verification are the same as in gnutls_x509_trust_list_verify_crt2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_trust_list_verify_crt(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t * cert_list,
+ unsigned int cert_list_size,
+ unsigned int flags,
+ unsigned int *voutput,
+ gnutls_verify_output_function func)
+{
+ return gnutls_x509_trust_list_verify_crt2(list, cert_list, cert_list_size,
+ NULL, 0, flags, voutput, func);
+}
+
+#define LAST_DN cert_list[cert_list_size-1]->raw_dn
+#define LAST_IDN cert_list[cert_list_size-1]->raw_issuer_dn
+/* This macro is introduced to detect a verification output which
+ * indicates an unknown signer, a signer which uses an insecure
+ * algorithm (e.g., sha1), a signer has expired, or something that
+ * indicates a superseded signer */
+#define SIGNER_OLD_OR_UNKNOWN(output) ((output & GNUTLS_CERT_SIGNER_NOT_FOUND) || \
+ (output & GNUTLS_CERT_EXPIRED) || \
+ (output & GNUTLS_CERT_INSECURE_ALGORITHM))
+#define SIGNER_WAS_KNOWN(output) (!(output & GNUTLS_CERT_SIGNER_NOT_FOUND))
+
+/**
+ * gnutls_x509_trust_list_verify_crt2:
+ * @list: The list
+ * @cert_list: is the certificate list to be verified
+ * @cert_list_size: is the certificate list size
+ * @data: an array of typed data
+ * @elements: the number of data elements
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ * @voutput: will hold the certificate verification output.
+ * @func: If non-null will be called on each chain element verification with the output.
+ *
+ * This function will attempt to verify the given certificate chain and return
+ * its status. The @voutput parameter will hold an OR'ed sequence of
+ * %gnutls_certificate_status_t flags.
+ *
+ * When a certificate chain of @cert_list_size with more than one certificates is
+ * provided, the verification status will apply to the first certificate in the chain
+ * that failed verification. The verification process starts from the end of the chain
+ * (from CA to end certificate). The first certificate in the chain must be the end-certificate
+ * while the rest of the members may be sorted or not.
+ *
+ * Additionally a certificate verification profile can be specified
+ * from the ones in %gnutls_certificate_verification_profiles_t by
+ * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification
+ * flags.
+ *
+ * Additional verification parameters are possible via the @data types; the
+ * acceptable types are %GNUTLS_DT_DNS_HOSTNAME, %GNUTLS_DT_IP_ADDRESS and %GNUTLS_DT_KEY_PURPOSE_OID.
+ * The former accepts as data a null-terminated hostname, and the latter a null-terminated
+ * object identifier (e.g., %GNUTLS_KP_TLS_WWW_SERVER).
+ * If a DNS hostname is provided then this function will compare
+ * the hostname in the end certificate against the given. If names do not match the
+ * %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. In addition it
+ * will consider certificates provided with gnutls_x509_trust_list_add_named_crt().
+ *
+ * If a key purpose OID is provided and the end-certificate contains the extended key
+ * usage PKIX extension, it will be required to match the provided OID
+ * or be marked for any purpose, otherwise verification will fail with
+ * %GNUTLS_CERT_PURPOSE_MISMATCH status.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. Note that verification failure will not result to an
+ * error code, only @voutput will be updated.
+ *
+ * Since: 3.3.8
+ **/
+int
+gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t * cert_list,
+ unsigned int cert_list_size,
+ gnutls_typed_vdata_st *data,
+ unsigned int elements,
+ unsigned int flags,
+ unsigned int *voutput,
+ gnutls_verify_output_function func)
+{
+ int ret = 0;
+ unsigned int i;
+ size_t hash;
+ gnutls_x509_crt_t sorted[DEFAULT_MAX_VERIFY_DEPTH];
+ gnutls_x509_crt_t retrieved[DEFAULT_MAX_VERIFY_DEPTH];
+ unsigned int retrieved_size = 0;
+ const char *hostname = NULL, *purpose = NULL, *email = NULL;
+ unsigned hostname_size = 0;
+ unsigned have_set_name = 0;
+ unsigned saved_output;
+ gnutls_datum_t ip = {NULL, 0};
+ struct cert_set_st cert_set = { NULL, 0 };
+
+ if (cert_list == NULL || cert_list_size < 1)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ for (i=0;i<elements;i++) {
+ if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) {
+ hostname = (void*)data[i].data;
+ if (data[i].size > 0) {
+ hostname_size = data[i].size;
+ }
+
+ if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ have_set_name = 1;
+ } else if (data[i].type == GNUTLS_DT_IP_ADDRESS) {
+ if (data[i].size > 0) {
+ ip.data = data[i].data;
+ ip.size = data[i].size;
+ }
+
+ if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ have_set_name = 1;
+ } else if (data[i].type == GNUTLS_DT_RFC822NAME) {
+ email = (void*)data[i].data;
+
+ if (have_set_name != 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ have_set_name = 1;
+ } else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) {
+ purpose = (void*)data[i].data;
+ }
+ }
+
+ if (hostname) { /* shortcut using the named certs - if any */
+ unsigned vtmp = 0;
+ if (hostname_size == 0)
+ hostname_size = strlen(hostname);
+
+ ret = gnutls_x509_trust_list_verify_named_crt(list,
+ cert_list[0], hostname, hostname_size,
+ flags, &vtmp, func);
+ if (ret == 0 && vtmp == 0) {
+ *voutput = vtmp;
+ return 0;
+ }
+ }
+
+ memcpy(sorted, cert_list, cert_list_size * sizeof(gnutls_x509_crt_t));
+ cert_list = sorted;
+
+ ret = cert_set_init(&cert_set, DEFAULT_MAX_VERIFY_DEPTH);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < cert_list_size &&
+ cert_list_size <= DEFAULT_MAX_VERIFY_DEPTH; ) {
+ unsigned int sorted_size = 1;
+ unsigned int j;
+ gnutls_x509_crt_t issuer;
+
+ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_UNSORTED_CHAIN)) {
+ sorted_size = _gnutls_sort_clist(&cert_list[i],
+ cert_list_size - i);
+ }
+
+ /* Remove duplicates. Start with index 1, as the first element
+ * may be re-checked after issuer retrieval. */
+ for (j = 1; j < sorted_size; j++) {
+ if (cert_set_contains(&cert_set, cert_list[i + j])) {
+ if (i + j < cert_list_size - 1) {
+ memmove(&cert_list[i + j],
+ &cert_list[i + j + 1],
+ sizeof(cert_list[i]));
+ }
+ cert_list_size--;
+ break;
+ }
+ }
+ /* Found a duplicate, try again with the same index. */
+ if (j < sorted_size) {
+ continue;
+ }
+
+ /* Record the certificates seen. */
+ for (j = 0; j < sorted_size; j++, i++) {
+ ret = cert_set_add(&cert_set, cert_list[i]);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ }
+
+ /* If the issuer of the certificate is known, no need
+ * for further processing. */
+ if (gnutls_x509_trust_list_get_issuer(list,
+ cert_list[i - 1],
+ &issuer,
+ GNUTLS_TL_GET_COPY) == 0) {
+ gnutls_x509_crt_deinit(issuer);
+ cert_list_size = i;
+ break;
+ }
+
+ /* If there is no gap between this and the next certificate,
+ * proceed with the next certificate. */
+ if (i < cert_list_size &&
+ gnutls_x509_crt_check_issuer(cert_list[i - 1],
+ cert_list[i])) {
+ continue;
+ }
+
+ ret = retrieve_issuers(list,
+ cert_list[i - 1],
+ &retrieved[retrieved_size],
+ DEFAULT_MAX_VERIFY_DEPTH -
+ MAX(retrieved_size,
+ cert_list_size));
+ if (ret < 0) {
+ break;
+ } else if (ret > 0) {
+ assert((unsigned int)ret <=
+ DEFAULT_MAX_VERIFY_DEPTH - cert_list_size);
+ memmove(&cert_list[i + ret],
+ &cert_list[i],
+ (cert_list_size - i) *
+ sizeof(gnutls_x509_crt_t));
+ memcpy(&cert_list[i],
+ &retrieved[retrieved_size],
+ ret * sizeof(gnutls_x509_crt_t));
+ retrieved_size += ret;
+ cert_list_size += ret;
+
+ /* Start again from the end of the previous segment. */
+ i--;
+ }
+ }
+
+ cert_list_size = shorten_clist(list, cert_list, cert_list_size);
+ if (cert_list_size <= 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ hash =
+ hash_pjw_bare(cert_list[cert_list_size - 1]->raw_issuer_dn.
+ data,
+ cert_list[cert_list_size -
+ 1]->raw_issuer_dn.size);
+ hash %= list->size;
+
+ ret = check_if_in_blacklist(cert_list, cert_list_size,
+ list->blacklisted, list->blacklisted_size);
+ if (ret != 0) {
+ *voutput = 0;
+ *voutput |= GNUTLS_CERT_REVOKED;
+ *voutput |= GNUTLS_CERT_INVALID;
+ ret = 0;
+ goto cleanup;
+ }
+
+ *voutput =
+ _gnutls_verify_crt_status(list, cert_list, cert_list_size,
+ list->node[hash].trusted_cas,
+ list->node[hash].trusted_ca_size,
+ flags, purpose, func);
+ saved_output = *voutput;
+
+ if (SIGNER_OLD_OR_UNKNOWN(*voutput) &&
+ (LAST_DN.size != LAST_IDN.size ||
+ memcmp(LAST_DN.data, LAST_IDN.data, LAST_IDN.size) != 0)) {
+
+ /* if we couldn't find the issuer, try to see if the last
+ * certificate is in the trusted list and try to verify against
+ * (if it is not self signed) */
+ hash =
+ hash_pjw_bare(cert_list[cert_list_size - 1]->raw_dn.
+ data, cert_list[cert_list_size - 1]->raw_dn.size);
+ hash %= list->size;
+
+ _gnutls_debug_log("issuer in verification was not found or insecure; trying against trust list\n");
+
+ *voutput =
+ _gnutls_verify_crt_status(list, cert_list, cert_list_size,
+ list->node[hash].trusted_cas,
+ list->node[hash].trusted_ca_size,
+ flags, purpose, func);
+ if (*voutput != 0) {
+ if (SIGNER_WAS_KNOWN(saved_output))
+ *voutput = saved_output;
+ gnutls_assert();
+ }
+ }
+
+ saved_output = *voutput;
+
+#ifdef ENABLE_PKCS11
+ if (SIGNER_OLD_OR_UNKNOWN(*voutput) && list->pkcs11_token) {
+ /* use the token for verification */
+
+ *voutput = _gnutls_pkcs11_verify_crt_status(list, list->pkcs11_token,
+ cert_list, cert_list_size,
+ purpose,
+ flags, func);
+ if (*voutput != 0) {
+ if (SIGNER_WAS_KNOWN(saved_output))
+ *voutput = saved_output;
+ gnutls_assert();
+ }
+ }
+#endif
+
+ /* End-certificate, key purpose and hostname checks. */
+ if (purpose) {
+ ret = _gnutls_check_key_purpose(cert_list[0], purpose, 0);
+ if (ret != 1) {
+ gnutls_assert();
+ *voutput |= GNUTLS_CERT_PURPOSE_MISMATCH|GNUTLS_CERT_INVALID;
+ }
+ }
+
+ if (hostname) {
+ ret =
+ gnutls_x509_crt_check_hostname2(cert_list[0], hostname, flags);
+ if (ret == 0) {
+ gnutls_assert();
+ *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID;
+ }
+ }
+
+ if (ip.data) {
+ ret =
+ gnutls_x509_crt_check_ip(cert_list[0], ip.data, ip.size, flags);
+ if (ret == 0) {
+ gnutls_assert();
+ *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID;
+ }
+ }
+
+ if (email) {
+ ret =
+ gnutls_x509_crt_check_email(cert_list[0], email, 0);
+ if (ret == 0) {
+ gnutls_assert();
+ *voutput |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID;
+ }
+ }
+
+ /* CRL checks follow */
+
+ if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ /* Check revocation of individual certificates.
+ * start with the last one that we already have its hash
+ */
+ ret =
+ _gnutls_x509_crt_check_revocation(cert_list
+ [cert_list_size - 1],
+ list->node[hash].crls,
+ list->node[hash].crl_size,
+ func);
+ if (ret == 1) { /* revoked */
+ *voutput |= GNUTLS_CERT_REVOKED;
+ *voutput |= GNUTLS_CERT_INVALID;
+ ret = 0;
+ goto cleanup;
+ }
+
+ for (i = 0; i < cert_list_size - 1; i++) {
+ hash =
+ hash_pjw_bare(cert_list[i]->raw_issuer_dn.data,
+ cert_list[i]->raw_issuer_dn.size);
+ hash %= list->size;
+
+ ret = _gnutls_x509_crt_check_revocation(cert_list[i],
+ list->node[hash].
+ crls,
+ list->node[hash].
+ crl_size, func);
+ if (ret < 0) {
+ gnutls_assert();
+ } else if (ret == 1) { /* revoked */
+ *voutput |= GNUTLS_CERT_REVOKED;
+ *voutput |= GNUTLS_CERT_INVALID;
+ ret = 0;
+ goto cleanup;
+ }
+ }
+
+ cleanup:
+ for (i = 0; i < retrieved_size; i++) {
+ gnutls_x509_crt_deinit(retrieved[i]);
+ }
+ cert_set_deinit(&cert_set);
+ return ret;
+}
+
+/**
+ * gnutls_x509_trust_list_verify_named_crt:
+ * @list: The list
+ * @cert: is the certificate to be verified
+ * @name: is the certificate's name
+ * @name_size: is the certificate's name size
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ * @voutput: will hold the certificate verification output.
+ * @func: If non-null will be called on each chain element verification with the output.
+ *
+ * This function will try to find a certificate that is associated with the provided
+ * name --see gnutls_x509_trust_list_add_named_crt(). If a match is found the
+ * certificate is considered valid. In addition to that this function will also
+ * check CRLs. The @voutput parameter will hold an OR'ed sequence of
+ * %gnutls_certificate_status_t flags.
+ *
+ * Additionally a certificate verification profile can be specified
+ * from the ones in %gnutls_certificate_verification_profiles_t by
+ * ORing the result of GNUTLS_PROFILE_TO_VFLAGS() to the verification
+ * flags.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_x509_trust_list_verify_named_crt(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert,
+ const void *name,
+ size_t name_size,
+ unsigned int flags,
+ unsigned int *voutput,
+ gnutls_verify_output_function func)
+{
+ int ret;
+ unsigned int i;
+ size_t hash;
+
+
+ hash =
+ hash_pjw_bare(cert->raw_issuer_dn.data,
+ cert->raw_issuer_dn.size);
+ hash %= list->size;
+
+ ret = check_if_in_blacklist(&cert, 1,
+ list->blacklisted, list->blacklisted_size);
+ if (ret != 0) {
+ *voutput = 0;
+ *voutput |= GNUTLS_CERT_REVOKED;
+ *voutput |= GNUTLS_CERT_INVALID;
+ return 0;
+ }
+
+ *voutput = GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND;
+
+ for (i = 0; i < list->node[hash].named_cert_size; i++) {
+ if (gnutls_x509_crt_equals(cert, list->node[hash].named_certs[i].cert) != 0) { /* check if name matches */
+ if (list->node[hash].named_certs[i].name_size ==
+ name_size
+ && memcmp(list->node[hash].named_certs[i].name,
+ name, name_size) == 0) {
+ *voutput = 0;
+ break;
+ }
+ }
+ }
+
+ if (*voutput != 0 || (flags & GNUTLS_VERIFY_DISABLE_CRL_CHECKS))
+ return 0;
+
+ /* Check revocation of individual certificates.
+ * start with the last one that we already have its hash
+ */
+ ret = _gnutls_x509_crt_check_revocation(cert,
+ list->node[hash].crls,
+ list->node[hash].crl_size,
+ func);
+ if (ret == 1) { /* revoked */
+ *voutput |= GNUTLS_CERT_REVOKED;
+ *voutput |= GNUTLS_CERT_INVALID;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* return 1 if @cert is in @list, 0 if not */
+int
+_gnutls_trustlist_inlist(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert)
+{
+ int ret;
+ unsigned int i;
+ size_t hash;
+
+ hash = hash_pjw_bare(cert->raw_dn.data, cert->raw_dn.size);
+ hash %= list->size;
+
+ for (i = 0; i < list->node[hash].trusted_ca_size; i++) {
+ ret =
+ gnutls_x509_crt_equals(cert,
+ list->node[hash].
+ trusted_cas[i]);
+ if (ret != 0)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/lib/x509/verify-high.h b/lib/x509/verify-high.h
new file mode 100644
index 0000000..4cbb29a
--- /dev/null
+++ b/lib/x509/verify-high.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_VERIFY_HIGH_H
+#define GNUTLS_LIB_X509_VERIFY_HIGH_H
+
+struct gnutls_x509_trust_list_st {
+ unsigned int size;
+ struct node_st *node;
+
+ /* holds a sequence of the RDNs of the CAs above.
+ * This is used when using the trust list in TLS.
+ */
+ gnutls_datum_t x509_rdn_sequence;
+
+ gnutls_x509_crt_t *blacklisted;
+ unsigned int blacklisted_size;
+
+ /* certificates that will be deallocated when this struct
+ * will be deinitialized */
+ gnutls_x509_crt_t *keep_certs;
+ unsigned int keep_certs_size;
+
+ char* pkcs11_token;
+
+ /* set this callback if the issuer in the certificate
+ * chain is missing. */
+ gnutls_x509_trust_list_getissuer_function *issuer_callback;
+ /* set user pointer. */
+ void *usr_ptr;
+};
+
+int _gnutls_trustlist_inlist(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert);
+
+#endif /* GNUTLS_LIB_X509_VERIFY_HIGH_H */
diff --git a/lib/x509/verify-high2.c b/lib/x509/verify-high2.c
new file mode 100644
index 0000000..16d757c
--- /dev/null
+++ b/lib/x509/verify-high2.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <libtasn1.h>
+#include <global.h>
+#include <num.h>
+#include <tls-sig.h>
+#include <str.h>
+#include <c-strcase.h>
+#include <datum.h>
+#include "x509_int.h"
+#include <common.h>
+#include "verify-high.h"
+#include "read-file.h"
+#include <pkcs11_int.h>
+#include "urls.h"
+
+#include <dirent.h>
+
+#if !defined(_DIRENT_HAVE_D_TYPE) && !defined(__native_client__)
+# ifdef DT_UNKNOWN
+# define _DIRENT_HAVE_D_TYPE
+# endif
+#endif
+
+#ifdef _WIN32
+# include <tchar.h>
+#endif
+
+/* Convenience functions for verify-high functionality
+ */
+
+/**
+ * gnutls_x509_trust_list_add_trust_mem:
+ * @list: The list
+ * @cas: A buffer containing a list of CAs (optional)
+ * @crls: A buffer containing a list of CRLs (optional)
+ * @type: The format of the certificates
+ * @tl_flags: flags from %gnutls_trust_list_flags_t
+ * @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
+ *
+ * This function will add the given certificate authorities
+ * to the trusted list.
+ *
+ * If this function is used gnutls_x509_trust_list_deinit() must be called
+ * with parameter @all being 1.
+ *
+ * Returns: The number of added elements is returned.
+ *
+ * Since: 3.1
+ **/
+int
+gnutls_x509_trust_list_add_trust_mem(gnutls_x509_trust_list_t list,
+ const gnutls_datum_t * cas,
+ const gnutls_datum_t * crls,
+ gnutls_x509_crt_fmt_t type,
+ unsigned int tl_flags,
+ unsigned int tl_vflags)
+{
+ int ret;
+ gnutls_x509_crt_t *x509_ca_list = NULL;
+ gnutls_x509_crl_t *x509_crl_list = NULL;
+ unsigned int x509_ncas, x509_ncrls;
+ unsigned int r = 0;
+
+ /* When adding CAs or CRLs, we use the GNUTLS_TL_NO_DUPLICATES flag to ensure
+ * that unaccounted certificates/CRLs are deinitialized. */
+
+ if (cas != NULL && cas->data != NULL) {
+ ret =
+ gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas,
+ cas, type, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ gnutls_x509_trust_list_add_cas(list, x509_ca_list,
+ x509_ncas, tl_flags|GNUTLS_TL_NO_DUPLICATES);
+ gnutls_free(x509_ca_list);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ else
+ r += ret;
+ }
+
+ if (crls != NULL && crls->data != NULL) {
+ ret =
+ gnutls_x509_crl_list_import2(&x509_crl_list,
+ &x509_ncrls, crls, type,
+ 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ gnutls_x509_trust_list_add_crls(list, x509_crl_list,
+ x509_ncrls, tl_flags|GNUTLS_TL_NO_DUPLICATES,
+ tl_vflags);
+ gnutls_free(x509_crl_list);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ else
+ r += ret;
+ }
+
+ return r;
+}
+
+/**
+ * gnutls_x509_trust_list_remove_trust_mem:
+ * @list: The list
+ * @cas: A buffer containing a list of CAs (optional)
+ * @type: The format of the certificates
+ *
+ * This function will remove the provided certificate authorities
+ * from the trusted list, and add them into a black list when needed.
+ *
+ * See also gnutls_x509_trust_list_remove_cas().
+ *
+ * Returns: The number of removed elements is returned.
+ *
+ * Since: 3.1.10
+ **/
+int
+gnutls_x509_trust_list_remove_trust_mem(gnutls_x509_trust_list_t list,
+ const gnutls_datum_t * cas,
+ gnutls_x509_crt_fmt_t type)
+{
+ int ret;
+ gnutls_x509_crt_t *x509_ca_list = NULL;
+ unsigned int x509_ncas;
+ unsigned int r = 0, i;
+
+ if (cas != NULL && cas->data != NULL) {
+ ret =
+ gnutls_x509_crt_list_import2(&x509_ca_list, &x509_ncas,
+ cas, type, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret =
+ gnutls_x509_trust_list_remove_cas(list, x509_ca_list,
+ x509_ncas);
+
+ for (i = 0; i < x509_ncas; i++)
+ gnutls_x509_crt_deinit(x509_ca_list[i]);
+ gnutls_free(x509_ca_list);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ else
+ r += ret;
+ }
+
+ return r;
+}
+
+#ifdef ENABLE_PKCS11
+static
+int remove_pkcs11_url(gnutls_x509_trust_list_t list, const char *ca_file)
+{
+ if (strcmp(ca_file, list->pkcs11_token) == 0) {
+ gnutls_free(list->pkcs11_token);
+ }
+ return 0;
+}
+
+/* This function does add a PKCS #11 object URL into trust list. The
+ * CA certificates are imported directly, rather than using it as a
+ * trusted PKCS#11 token.
+ */
+static
+int add_trust_list_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url, unsigned flags)
+{
+ gnutls_x509_crt_t *xcrt_list = NULL;
+ gnutls_pkcs11_obj_t *pcrt_list = NULL;
+ unsigned int pcrt_list_size = 0, i;
+ int ret;
+
+ /* here we don't use the flag GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE,
+ * as we want to explicitly load from any module available in the system.
+ */
+ ret =
+ gnutls_pkcs11_obj_list_import_url2(&pcrt_list, &pcrt_list_size,
+ url,
+ GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED,
+ 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (pcrt_list_size == 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ xcrt_list = _gnutls_reallocarray(NULL, pcrt_list_size,
+ sizeof(gnutls_x509_crt_t));
+ if (xcrt_list == NULL) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ ret =
+ gnutls_x509_crt_list_import_pkcs11(xcrt_list, pcrt_list_size,
+ pcrt_list, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ gnutls_x509_trust_list_add_cas(list, xcrt_list, pcrt_list_size,
+ flags);
+
+ cleanup:
+ for (i = 0; i < pcrt_list_size; i++)
+ gnutls_pkcs11_obj_deinit(pcrt_list[i]);
+ gnutls_free(pcrt_list);
+ gnutls_free(xcrt_list);
+
+ return ret;
+}
+
+static
+int remove_pkcs11_object_url(gnutls_x509_trust_list_t list, const char *url)
+{
+ gnutls_x509_crt_t *xcrt_list = NULL;
+ gnutls_pkcs11_obj_t *pcrt_list = NULL;
+ unsigned int pcrt_list_size = 0, i;
+ int ret;
+
+ ret =
+ gnutls_pkcs11_obj_list_import_url2(&pcrt_list, &pcrt_list_size,
+ url,
+ GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED,
+ 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (pcrt_list_size == 0) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ xcrt_list = _gnutls_reallocarray(NULL, pcrt_list_size,
+ sizeof(gnutls_x509_crt_t));
+ if (xcrt_list == NULL) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ ret =
+ gnutls_x509_crt_list_import_pkcs11(xcrt_list, pcrt_list_size,
+ pcrt_list, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ gnutls_x509_trust_list_remove_cas(list, xcrt_list, pcrt_list_size);
+
+ cleanup:
+ for (i = 0; i < pcrt_list_size; i++) {
+ gnutls_pkcs11_obj_deinit(pcrt_list[i]);
+ if (xcrt_list)
+ gnutls_x509_crt_deinit(xcrt_list[i]);
+ }
+ gnutls_free(pcrt_list);
+ gnutls_free(xcrt_list);
+
+ return ret;
+}
+#endif
+
+
+/**
+ * gnutls_x509_trust_list_add_trust_file:
+ * @list: The list
+ * @ca_file: A file containing a list of CAs (optional)
+ * @crl_file: A file containing a list of CRLs (optional)
+ * @type: The format of the certificates
+ * @tl_flags: flags from %gnutls_trust_list_flags_t
+ * @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
+ *
+ * This function will add the given certificate authorities
+ * to the trusted list. PKCS #11 URLs are also accepted, instead
+ * of files, by this function. A PKCS #11 URL implies a trust
+ * database (a specially marked module in p11-kit); the URL "pkcs11:"
+ * implies all trust databases in the system. Only a single URL specifying
+ * trust databases can be set; they cannot be stacked with multiple calls.
+ *
+ * Returns: The number of added elements is returned.
+ *
+ * Since: 3.1
+ **/
+int
+gnutls_x509_trust_list_add_trust_file(gnutls_x509_trust_list_t list,
+ const char *ca_file,
+ const char *crl_file,
+ gnutls_x509_crt_fmt_t type,
+ unsigned int tl_flags,
+ unsigned int tl_vflags)
+{
+ gnutls_datum_t cas = { NULL, 0 };
+ gnutls_datum_t crls = { NULL, 0 };
+ size_t size;
+ int ret;
+
+ if (ca_file != NULL) {
+#ifdef ENABLE_PKCS11
+ if (c_strncasecmp(ca_file, PKCS11_URL, PKCS11_URL_SIZE) == 0) {
+ unsigned pcrt_list_size = 0;
+
+ /* in case of a token URL import it as a PKCS #11 token,
+ * otherwise import the individual certificates.
+ */
+ if (is_pkcs11_url_object(ca_file) != 0) {
+ return add_trust_list_pkcs11_object_url(list, ca_file, tl_flags);
+ } else { /* trusted token */
+ if (list->pkcs11_token != NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ list->pkcs11_token = gnutls_strdup(ca_file);
+
+ /* enumerate the certificates */
+ ret = gnutls_pkcs11_obj_list_import_url(NULL, &pcrt_list_size,
+ ca_file,
+ (GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|GNUTLS_PKCS11_OBJ_FLAG_CRT|GNUTLS_PKCS11_OBJ_FLAG_MARK_CA|GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED),
+ 0);
+ if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ return gnutls_assert_val(ret);
+
+ return pcrt_list_size;
+ }
+ } else
+#endif
+ {
+ cas.data = (void *) read_file(ca_file, RF_BINARY, &size);
+ if (cas.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_FILE_ERROR;
+ }
+ cas.size = size;
+ }
+ }
+
+ if (crl_file) {
+ crls.data = (void *) read_file(crl_file, RF_BINARY, &size);
+ if (crls.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_FILE_ERROR;
+ }
+ crls.size = size;
+ }
+
+ ret =
+ gnutls_x509_trust_list_add_trust_mem(list, &cas, &crls, type,
+ tl_flags, tl_vflags);
+ free(crls.data);
+ free(cas.data);
+
+ return ret;
+}
+
+static
+int load_dir_certs(const char *dirname,
+ gnutls_x509_trust_list_t list,
+ unsigned int tl_flags, unsigned int tl_vflags,
+ unsigned type, unsigned crl)
+{
+ int ret;
+ int r = 0;
+ char path[GNUTLS_PATH_MAX];
+
+#if !defined(_WIN32) || !defined(_UNICODE)
+ DIR *dirp;
+ struct dirent *d;
+
+ dirp = opendir(dirname);
+ if (dirp != NULL) {
+ while ((d = readdir(dirp)) != NULL) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
+#endif
+ {
+ snprintf(path, sizeof(path), "%s/%s",
+ dirname, d->d_name);
+
+ if (crl != 0) {
+ ret =
+ gnutls_x509_trust_list_add_trust_file
+ (list, NULL, path, type, tl_flags,
+ tl_vflags);
+ } else {
+ ret =
+ gnutls_x509_trust_list_add_trust_file
+ (list, path, NULL, type, tl_flags,
+ tl_vflags);
+ }
+ if (ret >= 0)
+ r += ret;
+ }
+ }
+ closedir(dirp);
+ }
+#else /* _WIN32 */
+
+ _TDIR *dirp;
+ struct _tdirent *d;
+ gnutls_datum_t utf16 = {NULL, 0};
+
+#ifdef WORDS_BIGENDIAN
+ r = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, 1);
+#else
+ r = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, 0);
+#endif
+ if (r < 0)
+ return gnutls_assert_val(r);
+ dirp = _topendir((_TCHAR*)utf16.data);
+ gnutls_free(utf16.data);
+ if (dirp != NULL) {
+ while ((d = _treaddir(dirp)) != NULL) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
+#endif
+ {
+ snprintf(path, sizeof(path), "%s/%ls",
+ dirname, d->d_name);
+
+ if (crl != 0) {
+ ret =
+ gnutls_x509_trust_list_add_trust_file
+ (list, NULL, path, type, tl_flags,
+ tl_vflags);
+ } else {
+ ret =
+ gnutls_x509_trust_list_add_trust_file
+ (list, path, NULL, type, tl_flags,
+ tl_vflags);
+ }
+ if (ret >= 0)
+ r += ret;
+ }
+ }
+ _tclosedir(dirp);
+ }
+#endif /* _WIN32 */
+ return r;
+}
+
+/**
+ * gnutls_x509_trust_list_add_trust_dir:
+ * @list: The list
+ * @ca_dir: A directory containing the CAs (optional)
+ * @crl_dir: A directory containing a list of CRLs (optional)
+ * @type: The format of the certificates
+ * @tl_flags: flags from %gnutls_trust_list_flags_t
+ * @tl_vflags: gnutls_certificate_verify_flags if flags specifies GNUTLS_TL_VERIFY_CRL
+ *
+ * This function will add the given certificate authorities
+ * to the trusted list. Only directories are accepted by
+ * this function.
+ *
+ * Returns: The number of added elements is returned.
+ *
+ * Since: 3.3.6
+ **/
+int
+gnutls_x509_trust_list_add_trust_dir(gnutls_x509_trust_list_t list,
+ const char *ca_dir,
+ const char *crl_dir,
+ gnutls_x509_crt_fmt_t type,
+ unsigned int tl_flags,
+ unsigned int tl_vflags)
+{
+ int ret = 0;
+
+ if (ca_dir != NULL) {
+ int r = 0;
+ r = load_dir_certs(ca_dir, list, tl_flags, tl_vflags, type, 0);
+
+ if (r >= 0)
+ ret += r;
+ }
+
+ if (crl_dir) {
+ int r = 0;
+ r = load_dir_certs(crl_dir, list, tl_flags, tl_vflags, type, 1);
+
+ if (r >= 0)
+ ret += r;
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_trust_list_remove_trust_file:
+ * @list: The list
+ * @ca_file: A file containing a list of CAs
+ * @type: The format of the certificates
+ *
+ * This function will remove the given certificate authorities
+ * from the trusted list, and add them into a black list when needed.
+ * PKCS 11 URLs are also accepted, instead
+ * of files, by this function.
+ *
+ * See also gnutls_x509_trust_list_remove_cas().
+ *
+ * Returns: The number of added elements is returned.
+ *
+ * Since: 3.1.10
+ **/
+int
+gnutls_x509_trust_list_remove_trust_file(gnutls_x509_trust_list_t list,
+ const char *ca_file,
+ gnutls_x509_crt_fmt_t type)
+{
+ gnutls_datum_t cas = { NULL, 0 };
+ size_t size;
+ int ret;
+
+#ifdef ENABLE_PKCS11
+ if (c_strncasecmp(ca_file, PKCS11_URL, PKCS11_URL_SIZE) == 0) {
+ if (is_pkcs11_url_object(ca_file) != 0) {
+ return remove_pkcs11_object_url(list, ca_file);
+ } else { /* token */
+ return remove_pkcs11_url(list, ca_file);
+ }
+ } else
+#endif
+ {
+ cas.data = (void *) read_file(ca_file, RF_BINARY, &size);
+ if (cas.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_FILE_ERROR;
+ }
+ cas.size = size;
+ }
+
+ ret = gnutls_x509_trust_list_remove_trust_mem(list, &cas, type);
+ free(cas.data);
+
+ return ret;
+}
diff --git a/lib/x509/verify.c b/lib/x509/verify.c
new file mode 100644
index 0000000..c7e35f7
--- /dev/null
+++ b/lib/x509/verify.c
@@ -0,0 +1,1752 @@
+/*
+ * Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Nikos Mavrogiannopoulos
+ * Copyright (C) 2014 Red Hat
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* All functions which relate to X.509 certificate verification stuff are
+ * included here
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <libtasn1.h>
+#include <global.h>
+#include <num.h> /* MAX */
+#include <tls-sig.h>
+#include <str.h>
+#include <datum.h>
+#include <pkcs11_int.h>
+#include <x509_int.h>
+#include <common.h>
+#include <pk.h>
+#include <x509/verify-high.h>
+#include "supported_exts.h"
+#include "profiles.h"
+
+/* Checks if two certs have the same name and the same key. Return 1 on match.
+ * If @is_ca is zero then this function is identical to gnutls_x509_crt_equals()
+ */
+unsigned
+_gnutls_check_if_same_key(gnutls_x509_crt_t cert1,
+ gnutls_x509_crt_t cert2,
+ unsigned is_ca)
+{
+ int ret;
+ unsigned result;
+
+ if (is_ca == 0)
+ return gnutls_x509_crt_equals(cert1, cert2);
+
+ ret = _gnutls_is_same_dn(cert1, cert2);
+ if (ret == 0)
+ return 0;
+
+ if (cert1->raw_spki.size > 0 && (cert1->raw_spki.size == cert2->raw_spki.size) &&
+ (memcmp(cert1->raw_spki.data, cert2->raw_spki.data, cert1->raw_spki.size) == 0))
+ result = 1;
+ else
+ result = 0;
+
+ return result;
+}
+
+unsigned
+_gnutls_check_if_same_key2(gnutls_x509_crt_t cert1,
+ gnutls_datum_t * cert2bin)
+{
+ int ret;
+ gnutls_x509_crt_t cert2;
+
+ ret = gnutls_x509_crt_init(&cert2);
+ if (ret < 0)
+ return gnutls_assert_val(0);
+
+ ret = gnutls_x509_crt_import(cert2, cert2bin, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(cert2);
+ return gnutls_assert_val(0);
+ }
+
+ ret = _gnutls_check_if_same_key(cert1, cert2, 1);
+
+ gnutls_x509_crt_deinit(cert2);
+ return ret;
+}
+
+/* checks whether there are present unknown/unsupported critical extensions.
+ *
+ * Returns true if they are present.
+ */
+static unsigned check_for_unknown_exts(gnutls_x509_crt_t cert)
+{
+ unsigned i;
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ unsigned critical;
+ int ret;
+
+ for (i=0;;i++) {
+ oid_size = sizeof(oid);
+ oid[0] = 0;
+ critical = 0;
+
+ ret = gnutls_x509_crt_get_extension_info(cert, i, oid, &oid_size, &critical);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ return 0;
+ } else if (ret < 0) {
+ gnutls_assert();
+ /* could not decode? */
+ _gnutls_debug_log("Could not decode extension %d\n", i);
+ return 1;
+ }
+
+ if (critical == 0)
+ continue;
+
+ if (is_ext_oid_supported(oid, oid_size) == NULL) {
+ gnutls_assert();
+ _gnutls_debug_log("Unsupported critical extension: %s\n", oid);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Checks if the issuer of a certificate is a
+ * Certificate Authority, or if the certificate is the same
+ * as the issuer (and therefore it doesn't need to be a CA).
+ *
+ * Returns true or false, if the issuer is a CA,
+ * or not.
+ */
+static unsigned
+check_if_ca(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer,
+ unsigned int *max_path, unsigned int flags)
+{
+ gnutls_datum_t cert_signed_data = { NULL, 0 };
+ gnutls_datum_t issuer_signed_data = { NULL, 0 };
+ gnutls_datum_t cert_signature = { NULL, 0 };
+ gnutls_datum_t issuer_signature = { NULL, 0 };
+ int pathlen = -1, ret;
+ unsigned result;
+ unsigned int ca_status = 0;
+
+ /* Check if the issuer is the same with the
+ * certificate. This is added in order for trusted
+ * certificates to be able to verify themselves.
+ */
+
+ ret =
+ _gnutls_x509_get_signed_data(issuer->cert, &issuer->der, "tbsCertificate",
+ &issuer_signed_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate",
+ &cert_signed_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ _gnutls_x509_get_signature(issuer->cert, "signature",
+ &issuer_signature);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ _gnutls_x509_get_signature(cert->cert, "signature",
+ &cert_signature);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ /* If the subject certificate is the same as the issuer
+ * return true.
+ */
+ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
+ if (cert_signed_data.size == issuer_signed_data.size) {
+ if ((memcmp
+ (cert_signed_data.data,
+ issuer_signed_data.data,
+ cert_signed_data.size) == 0)
+ && (cert_signature.size ==
+ issuer_signature.size)
+ &&
+ (memcmp
+ (cert_signature.data, issuer_signature.data,
+ cert_signature.size) == 0)) {
+ result = 1;
+ goto cleanup;
+ }
+ }
+
+ ret =
+ gnutls_x509_crt_get_basic_constraints(issuer, NULL, &ca_status,
+ &pathlen);
+ if (ret < 0) {
+ ca_status = 0;
+ pathlen = -1;
+ }
+
+ if (ca_status != 0 && pathlen != -1) {
+ if ((unsigned) pathlen < *max_path)
+ *max_path = pathlen;
+ }
+
+ if (ca_status != 0) {
+ result = 1;
+ goto cleanup;
+ }
+ /* Handle V1 CAs that do not have a basicConstraint, but accept
+ these certs only if the appropriate flags are set. */
+ else if ((ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) &&
+ ((flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT) ||
+ (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT) &&
+ (gnutls_x509_crt_check_issuer(issuer, issuer) != 0)))) {
+ gnutls_assert();
+ result = 1;
+ goto cleanup;
+ } else {
+ gnutls_assert();
+ }
+
+ fail:
+ result = 0;
+
+ cleanup:
+ _gnutls_free_datum(&cert_signed_data);
+ _gnutls_free_datum(&issuer_signed_data);
+ _gnutls_free_datum(&cert_signature);
+ _gnutls_free_datum(&issuer_signature);
+ return result;
+}
+
+
+/* This function checks if cert's issuer is issuer.
+ * This does a straight (DER) compare of the issuer/subject DN fields in
+ * the given certificates, as well as check the authority key ID.
+ *
+ * Returns 1 if they match and (0) if they don't match.
+ */
+static unsigned is_issuer(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer)
+{
+ uint8_t id1[MAX_KEY_ID_SIZE];
+ uint8_t id2[MAX_KEY_ID_SIZE];
+ size_t id1_size;
+ size_t id2_size;
+ int ret;
+ unsigned result;
+
+ if (_gnutls_x509_compare_raw_dn
+ (&cert->raw_issuer_dn, &issuer->raw_dn) != 0)
+ result = 1;
+ else
+ result = 0;
+
+ if (result != 0) {
+ /* check if the authority key identifier matches the subject key identifier
+ * of the issuer */
+ id1_size = sizeof(id1);
+
+ ret =
+ gnutls_x509_crt_get_authority_key_id(cert, id1,
+ &id1_size, NULL);
+ if (ret < 0) {
+ /* If there is no authority key identifier in the
+ * certificate, assume they match */
+ result = 1;
+ goto cleanup;
+ }
+
+ id2_size = sizeof(id2);
+ ret =
+ gnutls_x509_crt_get_subject_key_id(issuer, id2,
+ &id2_size, NULL);
+ if (ret < 0) {
+ /* If there is no subject key identifier in the
+ * issuer certificate, assume they match */
+ result = 1;
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (id1_size == id2_size
+ && memcmp(id1, id2, id1_size) == 0)
+ result = 1;
+ else
+ result = 0;
+ }
+
+ cleanup:
+ return result;
+}
+
+/* Check if the given certificate is the issuer of the CRL.
+ * Returns 1 on success and 0 otherwise.
+ */
+static unsigned is_crl_issuer(gnutls_x509_crl_t crl, gnutls_x509_crt_t issuer)
+{
+ if (_gnutls_x509_compare_raw_dn
+ (&crl->raw_issuer_dn, &issuer->raw_dn) != 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Checks if the DN of two certificates is the same.
+ * Returns 1 if they match and (0) if they don't match. Otherwise
+ * a negative error code is returned to indicate error.
+ */
+unsigned _gnutls_is_same_dn(gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2)
+{
+ if (_gnutls_x509_compare_raw_dn(&cert1->raw_dn, &cert2->raw_dn) !=
+ 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Finds an issuer of the certificate. If multiple issuers
+ * are present, returns one that is activated and not expired.
+ */
+static inline gnutls_x509_crt_t
+find_issuer(gnutls_x509_crt_t cert,
+ const gnutls_x509_crt_t * trusted_cas, int tcas_size)
+{
+ int i;
+ gnutls_x509_crt_t issuer = NULL;
+
+ /* this is serial search.
+ */
+ for (i = 0; i < tcas_size; i++) {
+ if (is_issuer(cert, trusted_cas[i]) != 0) {
+ if (issuer == NULL) {
+ issuer = trusted_cas[i];
+ } else {
+ time_t now = gnutls_time(0);
+
+ if (now <
+ gnutls_x509_crt_get_expiration_time
+ (trusted_cas[i])
+ && now >=
+ gnutls_x509_crt_get_activation_time
+ (trusted_cas[i])) {
+ issuer = trusted_cas[i];
+ }
+ }
+ }
+ }
+
+ return issuer;
+}
+
+static unsigned int check_time_status(gnutls_x509_crt_t crt, time_t now)
+{
+ int status = 0;
+ time_t t;
+
+ t = gnutls_x509_crt_get_activation_time(crt);
+ if (t == (time_t) - 1 || now < t) {
+ status |= GNUTLS_CERT_NOT_ACTIVATED;
+ status |= GNUTLS_CERT_INVALID;
+ return status;
+ }
+
+ t = gnutls_x509_crt_get_expiration_time(crt);
+ if (t == (time_t) - 1 || now > t) {
+ status |= GNUTLS_CERT_EXPIRED;
+ status |= GNUTLS_CERT_INVALID;
+ return status;
+ }
+
+ return 0;
+}
+
+unsigned _gnutls_is_broken_sig_allowed(const gnutls_sign_entry_st *se, unsigned int flags)
+{
+ gnutls_digest_algorithm_t hash;
+
+ /* we have a catch all */
+ if ((flags & GNUTLS_VERIFY_ALLOW_BROKEN) == GNUTLS_VERIFY_ALLOW_BROKEN)
+ return 1;
+
+ /* the first two are for backwards compatibility */
+ if ((se->id == GNUTLS_SIGN_RSA_MD2)
+ && (flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2))
+ return 1;
+ if ((se->id == GNUTLS_SIGN_RSA_MD5)
+ && (flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))
+ return 1;
+
+ hash = se->hash;
+ if (hash == GNUTLS_DIG_SHA1 && (flags & GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1))
+ return 1;
+
+ return 0;
+}
+
+#define CASE_SEC_PARAM(profile, level) \
+ case profile: \
+ sym_bits = gnutls_sec_param_to_symmetric_bits(level); \
+ se = _gnutls_sign_to_entry(sigalg); \
+ if (unlikely(se == NULL)) { \
+ _gnutls_cert_log("cert", crt); \
+ _gnutls_debug_log(#level": certificate's signature algorithm is unknown\n"); \
+ return gnutls_assert_val(0); \
+ } \
+ if (unlikely(se->hash == GNUTLS_DIG_UNKNOWN)) { \
+ _gnutls_cert_log("cert", crt); \
+ _gnutls_debug_log(#level": certificate's signature hash is unknown\n"); \
+ return gnutls_assert_val(0); \
+ } \
+ if (!trusted && \
+ _gnutls_sign_get_hash_strength(sigalg) < sym_bits) { \
+ _gnutls_cert_log("cert", crt); \
+ _gnutls_debug_log(#level": certificate's signature hash strength is unacceptable (is %u bits, needed %u)\n", _gnutls_sign_get_hash_strength(sigalg), sym_bits); \
+ return gnutls_assert_val(0); \
+ } \
+ sp = gnutls_pk_bits_to_sec_param(pkalg, bits); \
+ if (sp < level) { \
+ _gnutls_cert_log("cert", crt); \
+ _gnutls_debug_log(#level": certificate's security level is unacceptable\n"); \
+ return gnutls_assert_val(0); \
+ } \
+ if (issuer) { \
+ sp = gnutls_pk_bits_to_sec_param(issuer_pkalg, issuer_bits); \
+ if (sp < level) { \
+ _gnutls_cert_log("issuer", issuer); \
+ _gnutls_debug_log(#level": certificate's issuer security level is unacceptable\n"); \
+ return gnutls_assert_val(0); \
+ } \
+ } \
+ break;
+
+/* Checks whether the provided certificates are acceptable
+ * according to verification profile specified.
+ *
+ * @crt: a certificate
+ * @issuer: the certificates issuer (allowed to be NULL)
+ * @sigalg: the signature algorithm used
+ * @trusted: whether @crt is treated as trusted (e.g., present in the system
+ * trust list); if it is true, the check on signature algorithm will
+ * be skipped
+ * @flags: the specified verification flags
+ */
+static unsigned is_level_acceptable(
+ gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer,
+ gnutls_sign_algorithm_t sigalg, bool trusted,
+ unsigned flags)
+{
+ gnutls_certificate_verification_profiles_t profile = GNUTLS_VFLAGS_TO_PROFILE(flags);
+ int issuer_pkalg = 0, pkalg, ret;
+ unsigned bits = 0, issuer_bits = 0, sym_bits = 0;
+ gnutls_pk_params_st params;
+ gnutls_sec_param_t sp;
+ const gnutls_sign_entry_st *se;
+ gnutls_certificate_verification_profiles_t min_profile;
+
+ min_profile = _gnutls_get_system_wide_verification_profile();
+
+ if (min_profile) {
+ if (profile < min_profile) {
+ gnutls_assert();
+ profile = min_profile;
+ }
+ }
+
+ if (profile == GNUTLS_PROFILE_UNKNOWN) {
+ return 1;
+ }
+
+ pkalg = gnutls_x509_crt_get_pk_algorithm(crt, &bits);
+ if (pkalg < 0)
+ return gnutls_assert_val(0);
+
+ if (issuer) {
+ issuer_pkalg = gnutls_x509_crt_get_pk_algorithm(issuer, &issuer_bits);
+ if (issuer_pkalg < 0)
+ return gnutls_assert_val(0);
+ }
+
+ switch (profile) {
+ CASE_SEC_PARAM(GNUTLS_PROFILE_VERY_WEAK, GNUTLS_SEC_PARAM_VERY_WEAK);
+ CASE_SEC_PARAM(GNUTLS_PROFILE_LOW, GNUTLS_SEC_PARAM_LOW);
+ CASE_SEC_PARAM(GNUTLS_PROFILE_LEGACY, GNUTLS_SEC_PARAM_LEGACY);
+ CASE_SEC_PARAM(GNUTLS_PROFILE_MEDIUM, GNUTLS_SEC_PARAM_MEDIUM);
+ CASE_SEC_PARAM(GNUTLS_PROFILE_HIGH, GNUTLS_SEC_PARAM_HIGH);
+ CASE_SEC_PARAM(GNUTLS_PROFILE_ULTRA, GNUTLS_SEC_PARAM_ULTRA);
+ CASE_SEC_PARAM(GNUTLS_PROFILE_FUTURE, GNUTLS_SEC_PARAM_FUTURE);
+ case GNUTLS_PROFILE_SUITEB128:
+ case GNUTLS_PROFILE_SUITEB192: {
+ unsigned curve, issuer_curve;
+
+ /* check suiteB params validity: rfc5759 */
+
+ if (gnutls_x509_crt_get_version(crt) != 3) {
+ _gnutls_debug_log("SUITEB: certificate uses an unacceptable version number\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (sigalg != GNUTLS_SIGN_ECDSA_SHA256 && sigalg != GNUTLS_SIGN_ECDSA_SHA384) {
+ _gnutls_debug_log("SUITEB: certificate is not signed using ECDSA-SHA256 or ECDSA-SHA384\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (pkalg != GNUTLS_PK_EC) {
+ _gnutls_debug_log("SUITEB: certificate does not contain ECC parameters\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (issuer_pkalg != GNUTLS_PK_EC) {
+ _gnutls_debug_log("SUITEB: certificate's issuer does not have ECC parameters\n");
+ return gnutls_assert_val(0);
+ }
+
+ ret = _gnutls_x509_crt_get_mpis(crt, &params);
+ if (ret < 0) {
+ _gnutls_debug_log("SUITEB: cannot read certificate params\n");
+ return gnutls_assert_val(0);
+ }
+
+ curve = params.curve;
+ gnutls_pk_params_release(&params);
+
+ if (curve != GNUTLS_ECC_CURVE_SECP256R1 &&
+ curve != GNUTLS_ECC_CURVE_SECP384R1) {
+ _gnutls_debug_log("SUITEB: certificate's ECC params do not contain SECP256R1 or SECP384R1\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (profile == GNUTLS_PROFILE_SUITEB192) {
+ if (curve != GNUTLS_ECC_CURVE_SECP384R1) {
+ _gnutls_debug_log("SUITEB192: certificate does not use SECP384R1\n");
+ return gnutls_assert_val(0);
+ }
+ }
+
+ if (issuer != NULL) {
+ if (gnutls_x509_crt_get_version(issuer) != 3) {
+ _gnutls_debug_log("SUITEB: certificate's issuer uses an unacceptable version number\n");
+ return gnutls_assert_val(0);
+ }
+
+ ret = _gnutls_x509_crt_get_mpis(issuer, &params);
+ if (ret < 0) {
+ _gnutls_debug_log("SUITEB: cannot read certificate params\n");
+ return gnutls_assert_val(0);
+ }
+
+ issuer_curve = params.curve;
+ gnutls_pk_params_release(&params);
+
+ if (issuer_curve != GNUTLS_ECC_CURVE_SECP256R1 &&
+ issuer_curve != GNUTLS_ECC_CURVE_SECP384R1) {
+ _gnutls_debug_log("SUITEB: certificate's issuer ECC params do not contain SECP256R1 or SECP384R1\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (issuer_curve < curve) {
+ _gnutls_debug_log("SUITEB: certificate's issuer ECC params are weaker than the certificate's\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (sigalg == GNUTLS_SIGN_ECDSA_SHA256 &&
+ issuer_curve == GNUTLS_ECC_CURVE_SECP384R1) {
+ _gnutls_debug_log("SUITEB: certificate is signed with ECDSA-SHA256 when using SECP384R1\n");
+ return gnutls_assert_val(0);
+ }
+ }
+
+ break;
+ case GNUTLS_PROFILE_UNKNOWN: /* already checked; avoid compiler warnings */
+ _gnutls_debug_log("An unknown profile (%d) was encountered\n", (int)profile);
+ }
+ }
+
+ return 1;
+}
+
+typedef struct verify_state_st {
+ time_t now;
+ unsigned int max_path;
+ gnutls_x509_name_constraints_t nc;
+ gnutls_x509_tlsfeatures_t tls_feat;
+ gnutls_verify_output_function *func;
+} verify_state_st;
+
+#define MARK_INVALID(x) { gnutls_assert(); \
+ out |= (x|GNUTLS_CERT_INVALID); \
+ result = 0; }
+
+static int _gnutls_x509_verify_data(gnutls_sign_algorithm_t sign,
+ const gnutls_datum_t * data,
+ const gnutls_datum_t * signature,
+ gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t issuer,
+ unsigned vflags);
+
+/*
+ * Verifies the given certificate against a certificate list of
+ * trusted CAs.
+ *
+ * Returns only 0 or 1. If 1 it means that the certificate
+ * was successfully verified.
+ *
+ * 'flags': an OR of the gnutls_certificate_verify_flags enumeration.
+ *
+ * Output will hold some extra information about the verification
+ * procedure.
+ */
+static unsigned verify_crt(gnutls_x509_trust_list_t tlist,
+ gnutls_x509_crt_t cert,
+ const gnutls_x509_crt_t * trusted_cas,
+ int tcas_size, unsigned int flags,
+ unsigned int *output,
+ verify_state_st *vparams,
+ unsigned end_cert)
+{
+ gnutls_datum_t cert_signed_data = { NULL, 0 };
+ gnutls_datum_t cert_signature = { NULL, 0 };
+ gnutls_x509_crt_t issuer = NULL;
+ int issuer_version;
+ unsigned result = 1;
+ unsigned int out = 0, usage;
+ int sigalg, ret;
+ const gnutls_sign_entry_st *se;
+
+ if (output)
+ *output = 0;
+
+ if (vparams->max_path == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ /* bail immediately, to avoid inconistency */
+ goto cleanup;
+ }
+ vparams->max_path--;
+
+ if (tcas_size >= 1)
+ issuer = find_issuer(cert, trusted_cas, tcas_size);
+
+ ret =
+ _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate",
+ &cert_signed_data);
+ if (ret < 0) {
+ MARK_INVALID(0);
+ cert_signed_data.data = NULL;
+ }
+
+ ret =
+ _gnutls_x509_get_signature(cert->cert, "signature",
+ &cert_signature);
+ if (ret < 0) {
+ MARK_INVALID(0);
+ cert_signature.data = NULL;
+ }
+
+ ret =
+ _gnutls_x509_get_signature_algorithm(cert->cert,
+ "signatureAlgorithm");
+ if (ret < 0) {
+ MARK_INVALID(0);
+ }
+ sigalg = ret;
+
+ se = _gnutls_sign_to_entry(sigalg);
+
+ /* issuer is not in trusted certificate
+ * authorities.
+ */
+ if (issuer == NULL) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_FOUND);
+ } else {
+ if (vparams->nc != NULL) {
+ /* append the issuer's constraints */
+ ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc,
+ GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto nc_done;
+ }
+
+ /* only check name constraints in server certificates, not CAs */
+ if (end_cert != 0) {
+ ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DNSNAME, cert);
+ if (ret == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto nc_done;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_RFC822NAME, cert);
+ if (ret == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto nc_done;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_DN, cert);
+ if (ret == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto nc_done;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_URI, cert);
+ if (ret == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto nc_done;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(vparams->nc, GNUTLS_SAN_IPADDRESS, cert);
+ if (ret == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto nc_done;
+ }
+ }
+ }
+
+ nc_done:
+ if (vparams->tls_feat != NULL) {
+ /* append the issuer's constraints */
+ ret = gnutls_x509_crt_get_tlsfeatures(issuer, vparams->tls_feat, GNUTLS_EXT_FLAG_APPEND, NULL);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto feat_done;
+ }
+
+ ret = gnutls_x509_tlsfeatures_check_crt(vparams->tls_feat, cert);
+ if (ret == 0) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ goto feat_done;
+ }
+ }
+
+ feat_done:
+ issuer_version = gnutls_x509_crt_get_version(issuer);
+
+ if (issuer_version < 0) {
+ MARK_INVALID(0);
+ } else if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) &&
+ ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT)
+ || issuer_version != 1)) {
+ if (check_if_ca(cert, issuer, &vparams->max_path, flags) != 1) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_NOT_CA);
+ }
+
+ ret =
+ gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ if (ret < 0) {
+ MARK_INVALID(0);
+ } else if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ }
+ }
+ }
+
+ if (sigalg < 0) {
+ MARK_INVALID(0);
+ } else if (cert_signed_data.data != NULL &&
+ cert_signature.data != NULL) {
+ ret =
+ _gnutls_x509_verify_data(sigalg,
+ &cert_signed_data,
+ &cert_signature,
+ cert,
+ issuer, flags);
+
+ if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ MARK_INVALID(GNUTLS_CERT_SIGNATURE_FAILURE);
+ } else if (ret == GNUTLS_E_CONSTRAINT_ERROR) {
+ MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ } else if (ret < 0) {
+ MARK_INVALID(0);
+ }
+ }
+ }
+
+ /* we always check the issuer for unsupported critical extensions */
+ if (issuer && check_for_unknown_exts(issuer) != 0) {
+ if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) {
+ MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS);
+ }
+ }
+
+ /* we only check the end-certificate for critical extensions; that
+ * way do not perform this check twice on the certificates when
+ * verifying a large list */
+ if (end_cert && check_for_unknown_exts(cert) != 0) {
+ if (!(flags & GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS)) {
+ MARK_INVALID(GNUTLS_CERT_UNKNOWN_CRIT_EXTENSIONS);
+ }
+ }
+
+ if (sigalg >= 0 && se) {
+ if (is_level_acceptable(cert, issuer, sigalg, false, flags) == 0) {
+ MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM);
+ }
+
+ /* If the certificate is not self signed check if the algorithms
+ * used are secure. If the certificate is self signed it doesn't
+ * really matter.
+ */
+ if (_gnutls_sign_is_secure2(se, GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS) == 0 &&
+ _gnutls_is_broken_sig_allowed(se, flags) == 0 &&
+ is_issuer(cert, cert) == 0) {
+ MARK_INVALID(GNUTLS_CERT_INSECURE_ALGORITHM);
+ }
+ }
+
+ /* Check activation/expiration times
+ */
+ if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
+ /* check the time of the issuer first */
+ if (issuer != NULL &&
+ !(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) {
+ out |= check_time_status(issuer, vparams->now);
+ if (out != 0) {
+ gnutls_assert();
+ result = 0;
+ }
+ }
+
+ out |= check_time_status(cert, vparams->now);
+ if (out != 0) {
+ gnutls_assert();
+ result = 0;
+ }
+ }
+
+ cleanup:
+ if (output)
+ *output |= out;
+
+ if (vparams->func) {
+ if (result == 0) {
+ out |= GNUTLS_CERT_INVALID;
+ }
+ vparams->func(cert, issuer, NULL, out);
+ }
+ _gnutls_free_datum(&cert_signed_data);
+ _gnutls_free_datum(&cert_signature);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_check_issuer:
+ * @cert: is the certificate to be checked
+ * @issuer: is the certificate of a possible issuer
+ *
+ * This function will check if the given certificate was issued by the
+ * given issuer. It checks the DN fields and the authority
+ * key identifier and subject key identifier fields match.
+ *
+ * If the same certificate is provided at the @cert and @issuer fields,
+ * it will check whether the certificate is self-signed.
+ *
+ * Returns: It will return true (1) if the given certificate is issued
+ * by the given issuer, and false (0) if not.
+ **/
+unsigned
+gnutls_x509_crt_check_issuer(gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t issuer)
+{
+ return is_issuer(cert, issuer);
+}
+
+static
+unsigned check_ca_sanity(const gnutls_x509_crt_t issuer,
+ time_t now, unsigned int flags)
+{
+ unsigned int status = 0;
+ unsigned sigalg;
+ int ret;
+
+ /* explicit time check for trusted CA that we remove from
+ * list. GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS
+ */
+ if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) &&
+ !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
+ status |= check_time_status(issuer, now);
+ }
+
+ ret =
+ _gnutls_x509_get_signature_algorithm(issuer->cert, "signatureAlgorithm");
+ sigalg = ret;
+
+ /* we explicitly allow CAs which we do not support their self-algorithms
+ * to pass. */
+ if (ret >= 0 && !is_level_acceptable(issuer, NULL, sigalg, true, flags)) {
+ status |= GNUTLS_CERT_INSECURE_ALGORITHM|GNUTLS_CERT_INVALID;
+ }
+
+ return status;
+
+}
+
+/* Verify X.509 certificate chain.
+ *
+ * Note that the return value is an OR of GNUTLS_CERT_* elements.
+ *
+ * This function verifies a X.509 certificate list. The certificate
+ * list should lead to a trusted certificate in order to be trusted.
+ */
+unsigned int
+_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist,
+ const gnutls_x509_crt_t * certificate_list,
+ int clist_size,
+ const gnutls_x509_crt_t * trusted_cas,
+ int tcas_size,
+ unsigned int flags,
+ const char *purpose,
+ gnutls_verify_output_function func)
+{
+ int i = 0, ret;
+ unsigned int status = 0, output;
+ time_t now = gnutls_time(0);
+ verify_state_st vparams;
+
+ if (clist_size > 1) {
+ /* Check if the last certificate in the path is self signed.
+ * In that case ignore it (a certificate is trusted only if it
+ * leads to a trusted party by us, not the server's).
+ *
+ * This prevents from verifying self signed certificates against
+ * themselves. This (although not bad) caused verification
+ * failures on some root self signed certificates that use the
+ * MD2 algorithm.
+ */
+ if (gnutls_x509_crt_check_issuer
+ (certificate_list[clist_size - 1],
+ certificate_list[clist_size - 1]) != 0) {
+ clist_size--;
+ }
+ }
+
+ /* We want to shorten the chain by removing the cert that matches
+ * one of the certs we trust and all the certs after that i.e. if
+ * cert chain is A signed-by B signed-by C signed-by D (signed-by
+ * self-signed E but already removed above), and we trust B, remove
+ * B, C and D. */
+ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
+ i = 0; /* also replace the first one */
+ else
+ i = 1; /* do not replace the first one */
+
+ for (; i < clist_size; i++) {
+ int j;
+
+ for (j = 0; j < tcas_size; j++) {
+ /* we check for a certificate that may not be identical with the one
+ * sent by the client, but will have the same name and key. That is
+ * because it can happen that a CA certificate is upgraded from intermediate
+ * CA to self-signed CA at some point. */
+ if (_gnutls_check_if_same_key
+ (certificate_list[i], trusted_cas[j], i) != 0) {
+
+ status |= check_ca_sanity(trusted_cas[j], now, flags);
+
+ if (func)
+ func(certificate_list[i],
+ trusted_cas[j], NULL, status);
+
+ if (status != 0) {
+ return gnutls_assert_val(status);
+ }
+
+ clist_size = i;
+ break;
+ }
+ }
+ /* clist_size may have been changed which gets out of loop */
+ }
+
+ if (clist_size == 0) {
+ /* The certificate is already present in the trusted certificate list.
+ * Nothing to verify. */
+ return status;
+ }
+
+ memset(&vparams, 0, sizeof(vparams));
+ vparams.now = now;
+ vparams.max_path = MAX_VERIFY_DEPTH;
+ vparams.func = func;
+
+ ret = gnutls_x509_name_constraints_init(&vparams.nc);
+ if (ret < 0) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ return status;
+ }
+
+ ret = gnutls_x509_tlsfeatures_init(&vparams.tls_feat);
+ if (ret < 0) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
+
+ /* Verify the last certificate in the certificate path
+ * against the trusted CA certificate list.
+ *
+ * If no CAs are present returns CERT_INVALID. Thus works
+ * in self signed etc certificates.
+ */
+ output = 0;
+
+ ret = verify_crt(tlist,
+ certificate_list[clist_size - 1],
+ trusted_cas, tcas_size, flags,
+ &output,
+ &vparams,
+ clist_size==1?1:0);
+ if (ret != 1) {
+ /* if the last certificate in the certificate
+ * list is invalid, then the certificate is not
+ * trusted.
+ */
+ gnutls_assert();
+ status |= output;
+ status |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
+
+ /* Verify the certificate path (chain)
+ */
+ for (i = clist_size - 1; i > 0; i--) {
+ output = 0;
+
+ if (purpose != NULL) {
+ ret = _gnutls_check_key_purpose(certificate_list[i], purpose, 1);
+ if (ret != 1) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_PURPOSE_MISMATCH;
+
+ if (func)
+ func(certificate_list[i-1],
+ certificate_list[i], NULL, status);
+ goto cleanup;
+ }
+ }
+
+ /* note that here we disable this V1 CA flag. So that no version 1
+ * certificates can exist in a supplied chain.
+ */
+ if (!(flags & GNUTLS_VERIFY_ALLOW_ANY_X509_V1_CA_CRT)) {
+ flags |= GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT;
+ }
+
+ if (!verify_crt(tlist,
+ certificate_list[i - 1],
+ &certificate_list[i], 1,
+ flags, &output,
+ &vparams,
+ i==1?1:0)) {
+ gnutls_assert();
+ status |= output;
+ status |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ gnutls_x509_name_constraints_deinit(vparams.nc);
+ gnutls_x509_tlsfeatures_deinit(vparams.tls_feat);
+ return status;
+}
+
+
+#define PURPOSE_NSSGC "2.16.840.1.113730.4.1"
+#define PURPOSE_VSGC "2.16.840.1.113733.1.8.1"
+
+/* Returns true if the provided purpose is in accordance with the certificate.
+ */
+unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, unsigned no_any)
+{
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ int ret;
+ unsigned critical = 0;
+ unsigned check_obsolete_oids = 0;
+ unsigned i;
+
+ /* The check_obsolete_oids hack is because of certain very old CA certificates
+ * around which instead of having the GNUTLS_KP_TLS_WWW_SERVER have some old
+ * OIDs for that purpose. Assume these OIDs equal GNUTLS_KP_TLS_WWW_SERVER in
+ * CA certs */
+ if (strcmp(purpose, GNUTLS_KP_TLS_WWW_SERVER) == 0) {
+ unsigned ca_status;
+ ret =
+ gnutls_x509_crt_get_basic_constraints(cert, NULL, &ca_status,
+ NULL);
+ if (ret < 0)
+ ca_status = 0;
+
+ if (ca_status)
+ check_obsolete_oids = 1;
+ }
+
+ for (i=0;;i++) {
+ oid_size = sizeof(oid);
+ ret = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid, &oid_size, &critical);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ if (i==0) {
+ /* no key purpose in certificate, assume ANY */
+ return 1;
+ } else {
+ gnutls_assert();
+ break;
+ }
+ } else if (ret < 0) {
+ gnutls_assert();
+ break;
+ }
+
+ if (check_obsolete_oids) {
+ if (strcmp(oid, PURPOSE_NSSGC) == 0) {
+ return 1;
+ } else if (strcmp(oid, PURPOSE_VSGC) == 0) {
+ return 1;
+ }
+ }
+
+ if (strcmp(oid, purpose) == 0 || (no_any == 0 && strcmp(oid, GNUTLS_KP_ANY) == 0)) {
+ return 1;
+ }
+ _gnutls_debug_log("looking for key purpose '%s', but have '%s'\n", purpose, oid);
+ }
+ return 0;
+}
+
+#ifdef ENABLE_PKCS11
+/* Verify X.509 certificate chain using a PKCS #11 token.
+ *
+ * Note that the return value is an OR of GNUTLS_CERT_* elements.
+ *
+ * Unlike the non-PKCS#11 version, this function accepts a key purpose
+ * (from GNUTLS_KP_...). That is because in the p11-kit trust modules
+ * anchors are mixed and get assigned a purpose.
+ *
+ * This function verifies a X.509 certificate list. The certificate
+ * list should lead to a trusted certificate in order to be trusted.
+ */
+unsigned int
+_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist,
+ const char* url,
+ const gnutls_x509_crt_t * certificate_list,
+ unsigned clist_size,
+ const char *purpose,
+ unsigned int flags,
+ gnutls_verify_output_function func)
+{
+ int ret;
+ unsigned int status = 0, i;
+ gnutls_x509_crt_t issuer = NULL;
+ gnutls_datum_t raw_issuer = {NULL, 0};
+ time_t now = gnutls_time(0);
+
+ if (clist_size > 1) {
+ /* Check if the last certificate in the path is self signed.
+ * In that case ignore it (a certificate is trusted only if it
+ * leads to a trusted party by us, not the server's).
+ *
+ * This prevents from verifying self signed certificates against
+ * themselves. This (although not bad) caused verification
+ * failures on some root self signed certificates that use the
+ * MD2 algorithm.
+ */
+ if (gnutls_x509_crt_check_issuer
+ (certificate_list[clist_size - 1],
+ certificate_list[clist_size - 1]) != 0) {
+ clist_size--;
+ }
+ }
+
+ /* We want to shorten the chain by removing the cert that matches
+ * one of the certs we trust and all the certs after that i.e. if
+ * cert chain is A signed-by B signed-by C signed-by D (signed-by
+ * self-signed E but already removed above), and we trust B, remove
+ * B, C and D. */
+ if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
+ i = 0; /* also replace the first one */
+ else
+ i = 1; /* do not replace the first one */
+
+ for (; i < clist_size; i++) {
+ unsigned vflags;
+ gnutls_x509_crt_t trusted_cert;
+
+ if (i == 0) /* in the end certificate do full comparison */
+ vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
+ GNUTLS_PKCS11_OBJ_FLAG_COMPARE|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED;
+ else
+ vflags = GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
+ GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED;
+
+ if (_gnutls_pkcs11_crt_is_known (url, certificate_list[i], vflags, &trusted_cert) != 0) {
+
+ status |= check_ca_sanity(trusted_cert, now, flags);
+
+ if (func)
+ func(trusted_cert,
+ certificate_list[i], NULL, status);
+
+ gnutls_x509_crt_deinit(trusted_cert);
+
+ if (status != 0) {
+ return gnutls_assert_val(status);
+ }
+
+ clist_size = i;
+ break;
+ }
+ /* clist_size may have been changed which gets out of loop */
+ }
+
+ if (clist_size == 0) {
+ /* The certificate is already present in the trusted certificate list.
+ * Nothing to verify. */
+ return status;
+ }
+
+ /* check for blacklists */
+ for (i = 0; i < clist_size; i++) {
+ if (gnutls_pkcs11_crt_is_known (url, certificate_list[i],
+ GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
+ GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) {
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_REVOKED;
+ if (func)
+ func(certificate_list[i], certificate_list[i], NULL, status);
+ goto cleanup;
+ }
+ }
+
+ /* check against issuer */
+ ret = gnutls_pkcs11_get_raw_issuer(url, certificate_list[clist_size - 1],
+ &raw_issuer, GNUTLS_X509_FMT_DER,
+ GNUTLS_PKCS11_OBJ_FLAG_OVERWRITE_TRUSTMOD_EXT|GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE);
+ if (ret < 0) {
+ gnutls_assert();
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE && clist_size > 2) {
+
+ /* check if the last certificate in the chain is present
+ * in our trusted list, and if yes, verify against it. */
+ ret = gnutls_pkcs11_crt_is_known(url, certificate_list[clist_size - 1],
+ GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_COMPARE);
+ if (ret != 0) {
+ return _gnutls_verify_crt_status(tlist,
+ certificate_list, clist_size,
+ &certificate_list[clist_size - 1],
+ 1, flags, purpose, func);
+ }
+ }
+
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
+ /* verify the certificate list against 0 trusted CAs in order
+ * to get, any additional flags from the certificate list (e.g.,
+ * insecure algorithms or expired */
+ status |= _gnutls_verify_crt_status(tlist, certificate_list, clist_size,
+ NULL, 0, flags, purpose, func);
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_init(&issuer);
+ if (ret < 0) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_import(issuer, &raw_issuer, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
+ goto cleanup;
+ }
+
+ /* check if the raw issuer is blacklisted (it can happen if
+ * the issuer is both in the trusted list and the blacklisted)
+ */
+ if (gnutls_pkcs11_crt_is_known (url, issuer,
+ GNUTLS_PKCS11_OBJ_FLAG_PRESENT_IN_TRUSTED_MODULE|
+ GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED) != 0) {
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_SIGNER_NOT_FOUND; /* if the signer is revoked - it is as if it doesn't exist */
+ goto cleanup;
+ }
+
+ /* security modules that provide trust, bundle all certificates (of all purposes)
+ * together. In software that doesn't specify any purpose assume the default to
+ * be www-server. */
+ ret = _gnutls_check_key_purpose(issuer, purpose==NULL?GNUTLS_KP_TLS_WWW_SERVER:purpose, 0);
+ if (ret != 1) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
+ goto cleanup;
+ }
+
+ status = _gnutls_verify_crt_status(tlist, certificate_list, clist_size,
+ &issuer, 1, flags, purpose, func);
+
+cleanup:
+ gnutls_free(raw_issuer.data);
+ if (issuer != NULL)
+ gnutls_x509_crt_deinit(issuer);
+
+ return status;
+}
+#endif
+
+static int
+_gnutls_x509_validate_sign_params(gnutls_pk_algorithm_t pk_algorithm,
+ asn1_node cert,
+ const char *name,
+ gnutls_x509_spki_st *sig_params)
+{
+ /* The signature parameter validation is only needed for RSA-PSS */
+ if (pk_algorithm == GNUTLS_PK_RSA_PSS) {
+ int result;
+ gnutls_x509_spki_st params;
+
+ result = _gnutls_x509_read_sign_params(cert, name, &params);
+ if (result < 0) {
+ /* If parameters field is absent, no parameter
+ * validation is needed */
+ if (result != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND &&
+ result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) {
+ gnutls_assert();
+ return result;
+ }
+ } else {
+ /* Check if the underlying hash algorithms are same. */
+ if (sig_params->rsa_pss_dig != params.rsa_pss_dig) {
+ gnutls_assert();
+ return GNUTLS_E_CONSTRAINT_ERROR;
+ }
+
+ /* The salt length used to generate the
+ * signature must be equal to or larger than
+ * the one in the key parameter. */
+ if (sig_params->salt_size < params.salt_size) {
+ gnutls_assert();
+ return GNUTLS_E_CONSTRAINT_ERROR;
+ }
+ }
+ }
+ return 0;
+}
+
+/* verifies if the certificate is properly signed.
+ * returns GNUTLS_E_PK_VERIFY_SIG_FAILED on failure and 1 on success.
+ *
+ * 'data' is the signed data
+ * 'signature' is the signature!
+ */
+static int
+_gnutls_x509_verify_data(gnutls_sign_algorithm_t sign,
+ const gnutls_datum_t * data,
+ const gnutls_datum_t * signature,
+ gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t issuer,
+ unsigned vflags)
+{
+ gnutls_pk_params_st params;
+ gnutls_pk_algorithm_t issuer_pk;
+ int ret;
+ gnutls_x509_spki_st sign_params;
+ const gnutls_sign_entry_st *se;
+
+ /* Read the MPI parameters from the issuer's certificate.
+ */
+ ret = _gnutls_x509_crt_get_mpis(issuer, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ issuer_pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL);
+
+ se = _gnutls_sign_to_entry(sign);
+ if (se == NULL)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+
+ if (cert != NULL) {
+ ret = _gnutls_x509_read_sign_params(cert->cert,
+ "signatureAlgorithm",
+ &sign_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_validate_sign_params(issuer_pk,
+ issuer->cert,
+ "tbsCertificate."
+ "subjectPublicKeyInfo."
+ "algorithm",
+ &sign_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ memcpy(&sign_params, &params.spki,
+ sizeof(gnutls_x509_spki_st));
+
+ sign_params.pk = se->pk;
+ if (sign_params.pk == GNUTLS_PK_RSA_PSS)
+ sign_params.rsa_pss_dig = se->hash;
+ }
+
+ ret = pubkey_verify_data(se, hash_to_entry(se->hash), data, signature, &params,
+ &sign_params, vflags);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ cleanup:
+ /* release all allocated MPIs
+ */
+ gnutls_pk_params_release(&params);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_list_verify:
+ * @cert_list: is the certificate list to be verified
+ * @cert_list_length: holds the number of certificate in cert_list
+ * @CA_list: is the CA list which will be used in verification
+ * @CA_list_length: holds the number of CA certificate in CA_list
+ * @CRL_list: holds a list of CRLs.
+ * @CRL_list_length: the length of CRL list.
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ * @verify: will hold the certificate verification output.
+ *
+ *
+ * This function will try to verify the given certificate list and
+ * return its status. The details of the verification are the same
+ * as in gnutls_x509_trust_list_verify_crt2().
+ *
+ * You must check the peer's name in order to check if the verified
+ * certificate belongs to the actual peer.
+ *
+ * The certificate verification output will be put in @verify and will
+ * be one or more of the gnutls_certificate_status_t enumerated
+ * elements bitwise or'd. For a more detailed verification status use
+ * gnutls_x509_crt_verify() per list element.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list,
+ unsigned cert_list_length,
+ const gnutls_x509_crt_t * CA_list,
+ unsigned CA_list_length,
+ const gnutls_x509_crl_t * CRL_list,
+ unsigned CRL_list_length, unsigned int flags,
+ unsigned int *verify)
+{
+ unsigned i;
+ int ret;
+ gnutls_x509_trust_list_t tlist;
+
+ if (cert_list == NULL || cert_list_length == 0)
+ return GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+ gnutls_x509_trust_list_init(&tlist, 0);
+
+ /* Verify certificate
+ */
+ *verify = _gnutls_verify_crt_status(tlist, cert_list, cert_list_length,
+ CA_list, CA_list_length,
+ flags, NULL, NULL);
+
+ /* Check for revoked certificates in the chain.
+ */
+ for (i = 0; i < cert_list_length; i++) {
+ ret = gnutls_x509_crt_check_revocation(cert_list[i],
+ CRL_list,
+ CRL_list_length);
+ if (ret == 1) { /* revoked */
+ *verify |= GNUTLS_CERT_REVOKED;
+ *verify |= GNUTLS_CERT_INVALID;
+ }
+ }
+
+ gnutls_x509_trust_list_deinit(tlist, 0);
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_verify:
+ * @cert: is the certificate to be verified
+ * @CA_list: is one certificate that is considered to be trusted one
+ * @CA_list_length: holds the number of CA certificate in CA_list
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ * @verify: will hold the certificate verification output.
+ *
+ * This function will try to verify the given certificate and return
+ * its status. Note that a verification error does not imply a negative
+ * return status. In that case the @verify status is set.
+ *
+ * The details of the verification are the same
+ * as in gnutls_x509_trust_list_verify_crt2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_verify(gnutls_x509_crt_t cert,
+ const gnutls_x509_crt_t * CA_list,
+ unsigned CA_list_length, unsigned int flags,
+ unsigned int *verify)
+{
+ gnutls_x509_trust_list_t tlist;
+
+ gnutls_x509_trust_list_init(&tlist, 0);
+
+ /* Verify certificate
+ */
+ *verify = _gnutls_verify_crt_status(tlist, &cert, 1,
+ CA_list, CA_list_length,
+ flags, NULL, NULL);
+
+ gnutls_x509_trust_list_deinit(tlist, 0);
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_check_issuer:
+ * @crl: is the CRL to be checked
+ * @issuer: is the certificate of a possible issuer
+ *
+ * This function will check if the given CRL was issued by the given
+ * issuer certificate.
+ *
+ * Returns: true (1) if the given CRL was issued by the given issuer,
+ * and false (0) if not.
+ **/
+unsigned
+gnutls_x509_crl_check_issuer(gnutls_x509_crl_t crl,
+ gnutls_x509_crt_t issuer)
+{
+ return is_crl_issuer(crl, issuer);
+}
+
+static inline gnutls_x509_crt_t
+find_crl_issuer(gnutls_x509_crl_t crl,
+ const gnutls_x509_crt_t * trusted_cas, int tcas_size)
+{
+ int i;
+
+ /* this is serial search.
+ */
+
+ for (i = 0; i < tcas_size; i++) {
+ if (is_crl_issuer(crl, trusted_cas[i]) != 0)
+ return trusted_cas[i];
+ }
+
+ gnutls_assert();
+ return NULL;
+}
+
+/**
+ * gnutls_x509_crl_verify:
+ * @crl: is the crl to be verified
+ * @trusted_cas: is a certificate list that is considered to be trusted one
+ * @tcas_size: holds the number of CA certificates in CA_list
+ * @flags: Flags that may be used to change the verification algorithm. Use OR of the gnutls_certificate_verify_flags enumerations.
+ * @verify: will hold the crl verification output.
+ *
+ * This function will try to verify the given crl and return its verification status.
+ * See gnutls_x509_crt_list_verify() for a detailed description of
+ * return values. Note that since GnuTLS 3.1.4 this function includes
+ * the time checks.
+ *
+ * Note that value in @verify is set only when the return value of this
+ * function is success (i.e, failure to trust a CRL a certificate does not imply
+ * a negative return value).
+ *
+ * Before GnuTLS 3.5.7 this function would return zero or a positive
+ * number on success.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crl_verify(gnutls_x509_crl_t crl,
+ const gnutls_x509_crt_t * trusted_cas,
+ unsigned tcas_size, unsigned int flags,
+ unsigned int *verify)
+{
+/* CRL is ignored for now */
+ gnutls_datum_t crl_signed_data = { NULL, 0 };
+ gnutls_datum_t crl_signature = { NULL, 0 };
+ gnutls_x509_crt_t issuer = NULL;
+ int result, sigalg;
+ time_t now = gnutls_time(0);
+ time_t nextu;
+ unsigned int usage;
+
+ if (verify)
+ *verify = 0;
+
+ if (tcas_size >= 1)
+ issuer = find_crl_issuer(crl, trusted_cas, tcas_size);
+
+ result =
+ _gnutls_x509_get_signed_data(crl->crl, &crl->der, "tbsCertList",
+ &crl_signed_data);
+ if (result < 0) {
+ gnutls_assert();
+ if (verify)
+ *verify |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_get_signature(crl->crl, "signature",
+ &crl_signature);
+ if (result < 0) {
+ gnutls_assert();
+ if (verify)
+ *verify |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
+
+ sigalg =
+ _gnutls_x509_get_signature_algorithm(crl->crl,
+ "signatureAlgorithm");
+ if (sigalg < 0) {
+ gnutls_assert();
+ if (verify)
+ *verify |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
+
+ /* issuer is not in trusted certificate
+ * authorities.
+ */
+ if (issuer == NULL) {
+ gnutls_assert();
+ if (verify)
+ *verify |=
+ GNUTLS_CERT_SIGNER_NOT_FOUND |
+ GNUTLS_CERT_INVALID;
+ } else {
+ if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) {
+ if (gnutls_x509_crt_get_ca_status(issuer, NULL) != 1) {
+ gnutls_assert();
+ if (verify)
+ *verify |=
+ GNUTLS_CERT_SIGNER_NOT_CA |
+ GNUTLS_CERT_INVALID;
+ }
+
+ result =
+ gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
+ if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ if (result < 0) {
+ gnutls_assert();
+ if (verify)
+ *verify |= GNUTLS_CERT_INVALID;
+ } else if (!(usage & GNUTLS_KEY_CRL_SIGN)) {
+ gnutls_assert();
+ if (verify)
+ *verify |=
+ GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE
+ | GNUTLS_CERT_INVALID;
+ }
+ }
+ }
+
+ result =
+ _gnutls_x509_verify_data(sigalg,
+ &crl_signed_data, &crl_signature,
+ NULL,
+ issuer, flags);
+ if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ gnutls_assert();
+ /* error. ignore it */
+ if (verify)
+ *verify |= GNUTLS_CERT_SIGNATURE_FAILURE;
+ result = 0;
+ } else if (result == GNUTLS_E_CONSTRAINT_ERROR) {
+ if (verify)
+ *verify |= GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE;
+ result = 0;
+ } else if (result < 0) {
+ gnutls_assert();
+ if (verify)
+ *verify |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ } else {
+ result = 0; /* everything ok */
+ }
+ }
+
+ {
+ sigalg = gnutls_x509_crl_get_signature_algorithm(crl);
+
+ if (((sigalg == GNUTLS_SIGN_RSA_MD2) &&
+ !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) ||
+ ((sigalg == GNUTLS_SIGN_RSA_MD5) &&
+ !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5))) {
+ if (verify)
+ *verify |= GNUTLS_CERT_INSECURE_ALGORITHM;
+ result = 0;
+ }
+ }
+
+ if (gnutls_x509_crl_get_this_update(crl) > now && verify)
+ *verify |= GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE;
+
+ nextu = gnutls_x509_crl_get_next_update(crl);
+ if (nextu != -1 && nextu < now && verify)
+ *verify |= GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED;
+
+
+ cleanup:
+ if (verify && *verify != 0)
+ *verify |= GNUTLS_CERT_INVALID;
+
+ _gnutls_free_datum(&crl_signed_data);
+ _gnutls_free_datum(&crl_signature);
+
+ return result;
+}
diff --git a/lib/x509/virt-san.c b/lib/x509/virt-san.c
new file mode 100644
index 0000000..e62bd4a
--- /dev/null
+++ b/lib/x509/virt-san.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2015-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains functions to handle the virtual subject alternative names,
+ * based on othernames, such as GNUTLS_SAN_OTHERNAME_XMPP.
+ */
+
+#include "gnutls_int.h"
+#include "x509_int.h"
+#include "x509_ext_int.h"
+#include "common.h"
+#include "krb5.h"
+#include "virt-san.h"
+
+static
+int san_othername_to_virtual(const char *oid, size_t size)
+{
+ if (oid) {
+ if ((unsigned) size == (sizeof(XMPP_OID)-1)
+ && memcmp(oid, XMPP_OID, sizeof(XMPP_OID)-1) == 0)
+ return GNUTLS_SAN_OTHERNAME_XMPP;
+ else if ((unsigned) size == (sizeof(KRB5_PRINCIPAL_OID)-1)
+ && memcmp(oid, KRB5_PRINCIPAL_OID, sizeof(KRB5_PRINCIPAL_OID)-1) == 0)
+ return GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL;
+ else if ((unsigned) size == (sizeof(MSUSER_PRINCIPAL_NAME_OID)-1)
+ && memcmp(oid, MSUSER_PRINCIPAL_NAME_OID, sizeof(MSUSER_PRINCIPAL_NAME_OID)-1) == 0)
+ return GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL;
+ }
+
+ return GNUTLS_SAN_OTHERNAME;
+}
+
+static
+const char * virtual_to_othername_oid(unsigned type)
+{
+ switch(type) {
+ case GNUTLS_SAN_OTHERNAME_XMPP:
+ return XMPP_OID;
+ case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL:
+ return KRB5_PRINCIPAL_OID;
+ case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL:
+ return MSUSER_PRINCIPAL_NAME_OID;
+ default:
+ return NULL;
+ }
+}
+
+int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutls_datum_t *san, const char *othername_oid, unsigned raw)
+{
+ gnutls_datum_t encoded = {NULL, 0};
+ gnutls_datum_t xmpp = {NULL,0};
+ int ret;
+
+ if (type < 1000) {
+ name->type = type;
+ ret = _gnutls_alt_name_process(&name->san, type, san, raw);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ gnutls_free(san->data);
+
+ if (othername_oid) {
+ name->othername_oid.data = (uint8_t *) othername_oid;
+ name->othername_oid.size = strlen(othername_oid);
+ } else {
+ name->othername_oid.data = NULL;
+ name->othername_oid.size = 0;
+ }
+ } else { /* virtual types */
+ const char *oid = virtual_to_othername_oid(type);
+
+ if (oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ switch(type) {
+ case GNUTLS_SAN_OTHERNAME_XMPP:
+
+ ret = gnutls_idna_map((char*)san->data, san->size, &xmpp, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_x509_encode_string(ASN1_ETYPE_UTF8_STRING,
+ xmpp.data, xmpp.size, &encoded);
+
+ gnutls_free(xmpp.data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ name->type = GNUTLS_SAN_OTHERNAME;
+ name->san.data = encoded.data;
+ name->san.size = encoded.size;
+ name->othername_oid.data = (void*)gnutls_strdup(oid);
+ name->othername_oid.size = strlen(oid);
+ break;
+
+ case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL:
+ ret = _gnutls_krb5_principal_to_der((char*)san->data, &name->san);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ name->othername_oid.data = (void*)gnutls_strdup(oid);
+ name->othername_oid.size = strlen(oid);
+ name->type = GNUTLS_SAN_OTHERNAME;
+ break;
+
+ default:
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ gnutls_free(san->data);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_othername_to_virtual:
+ * @oid: The othername object identifier
+ * @othername: The othername data
+ * @virt_type: GNUTLS_SAN_OTHERNAME_XXX
+ * @virt: allocated printable data
+ *
+ * This function will parse and convert the othername data to a virtual
+ * type supported by gnutls.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.8
+ **/
+int gnutls_x509_othername_to_virtual(const char *oid,
+ const gnutls_datum_t *othername,
+ unsigned int *virt_type,
+ gnutls_datum_t *virt)
+{
+ int ret;
+ unsigned type;
+
+ type = san_othername_to_virtual(oid, strlen(oid));
+ if (type == GNUTLS_SAN_OTHERNAME)
+ return gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN);
+
+ if (virt_type)
+ *virt_type = type;
+
+ switch(type) {
+ case GNUTLS_SAN_OTHERNAME_XMPP:
+ ret = _gnutls_x509_decode_string
+ (ASN1_ETYPE_UTF8_STRING, othername->data,
+ othername->size, virt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ return 0;
+ case GNUTLS_SAN_OTHERNAME_KRB5PRINCIPAL:
+ ret = _gnutls_krb5_der_to_principal(othername, virt);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ return 0;
+ case GNUTLS_SAN_OTHERNAME_MSUSERPRINCIPAL:
+ ret = _gnutls_x509_decode_string
+ (ASN1_ETYPE_UTF8_STRING, othername->data,
+ othername->size, virt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+}
diff --git a/lib/x509/virt-san.h b/lib/x509/virt-san.h
new file mode 100644
index 0000000..44b6fe6
--- /dev/null
+++ b/lib/x509/virt-san.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Nikos Mavrogiannopoulos
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_VIRT_SAN_H
+#define GNUTLS_LIB_X509_VIRT_SAN_H
+
+#include "x509_ext_int.h"
+
+int _gnutls_alt_name_assign_virt_type(struct name_st *name, unsigned type, gnutls_datum_t *san, const char *othername_oid, unsigned raw);
+
+#endif /* GNUTLS_LIB_X509_VIRT_SAN_H */
diff --git a/lib/x509/x509.c b/lib/x509/x509.c
new file mode 100644
index 0000000..50dcc8e
--- /dev/null
+++ b/lib/x509/x509.c
@@ -0,0 +1,4689 @@
+/*
+ * Copyright (C) 2003-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions on X.509 Certificate parsing
+ */
+
+#include "gnutls_int.h"
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <gnutls/x509-ext.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include <x509_int.h>
+#include <libtasn1.h>
+#include <pk.h>
+#include <pkcs11_int.h>
+#include "urls.h"
+#include "system-keys.h"
+#include "hash.h"
+#include "hash-pjw-bare.h"
+
+static int crt_reinit(gnutls_x509_crt_t crt)
+{
+ int result;
+
+ _gnutls_free_datum(&crt->der);
+ crt->raw_dn.size = 0;
+ crt->raw_issuer_dn.size = 0;
+ crt->raw_spki.size = 0;
+
+ asn1_delete_structure(&crt->cert);
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Certificate",
+ &crt->cert);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ return result;
+ }
+
+ gnutls_subject_alt_names_deinit(crt->san);
+ result = gnutls_subject_alt_names_init(&crt->san);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ gnutls_subject_alt_names_deinit(crt->ian);
+ result = gnutls_subject_alt_names_init(&crt->ian);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_equals - This function compares two gnutls_x509_crt_t certificates
+ * @cert1: The first certificate
+ * @cert2: The second certificate
+ *
+ * This function will compare two X.509 certificate structures.
+ *
+ * Returns: On equality non-zero is returned, otherwise zero.
+ *
+ * Since: 3.5.0
+ **/
+unsigned gnutls_x509_crt_equals(gnutls_x509_crt_t cert1,
+ gnutls_x509_crt_t cert2)
+{
+ int ret;
+ bool result;
+
+ if (cert1->modified == 0 && cert2->modified == 0 &&
+ cert1->raw_dn.size > 0 && cert2->raw_dn.size > 0) {
+ ret = _gnutls_is_same_dn(cert1, cert2);
+ if (ret == 0)
+ return 0;
+ }
+
+ if (cert1->der.size == 0 || cert2->der.size == 0 ||
+ cert1->modified != 0 || cert2->modified != 0) {
+ gnutls_datum_t tmp1, tmp2;
+
+ /* on uninitialized or modified certificates, we have to re-encode */
+ ret =
+ gnutls_x509_crt_export2(cert1, GNUTLS_X509_FMT_DER, &tmp1);
+ if (ret < 0)
+ return gnutls_assert_val(0);
+
+ ret =
+ gnutls_x509_crt_export2(cert2, GNUTLS_X509_FMT_DER, &tmp2);
+ if (ret < 0) {
+ gnutls_free(tmp1.data);
+ return gnutls_assert_val(0);
+ }
+
+ if ((tmp1.size == tmp2.size) &&
+ (memcmp(tmp1.data, tmp2.data, tmp1.size) == 0))
+ result = 1;
+ else
+ result = 0;
+
+ gnutls_free(tmp1.data);
+ gnutls_free(tmp2.data);
+ } else {
+ if ((cert1->der.size == cert2->der.size) &&
+ (memcmp(cert1->der.data, cert2->der.data, cert1->der.size) == 0))
+ result = 1;
+ else
+ result = 0;
+ }
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_equals2 - This function compares a gnutls_x509_crt_t cert with DER data
+ * @cert1: The first certificate
+ * @der: A DER encoded certificate
+ *
+ * This function will compare an X.509 certificate structures, with DER
+ * encoded certificate data.
+ *
+ * Returns: On equality non-zero is returned, otherwise zero.
+ *
+ * Since: 3.5.0
+ **/
+unsigned
+gnutls_x509_crt_equals2(gnutls_x509_crt_t cert1,
+ const gnutls_datum_t * der)
+{
+ bool result;
+
+ if (cert1 == NULL || der == NULL)
+ return 0;
+
+ if (cert1->der.size == 0 || cert1->modified) {
+ gnutls_datum_t tmp1;
+ int ret;
+
+ /* on uninitialized or modified certificates, we have to re-encode */
+ ret =
+ gnutls_x509_crt_export2(cert1, GNUTLS_X509_FMT_DER, &tmp1);
+ if (ret < 0)
+ return gnutls_assert_val(0);
+
+ if ((tmp1.size == der->size) &&
+ (memcmp(tmp1.data, der->data, tmp1.size) == 0))
+ result = 1;
+ else
+ result = 0;
+
+ gnutls_free(tmp1.data);
+ } else {
+ if ((cert1->der.size == der->size) &&
+ (memcmp(cert1->der.data, der->data, cert1->der.size) == 0))
+ result = 1;
+ else
+ result = 0;
+ }
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_init:
+ * @cert: A pointer to the type to be initialized
+ *
+ * This function will initialize an X.509 certificate structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crt_init(gnutls_x509_crt_t * cert)
+{
+ gnutls_x509_crt_t tmp;
+ int result;
+
+ FAIL_IF_LIB_ERROR;
+
+ tmp =
+ gnutls_calloc(1, sizeof(gnutls_x509_crt_int));
+
+ if (!tmp)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Certificate", &tmp->cert);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(tmp);
+ return _gnutls_asn2err(result);
+ }
+
+ result = gnutls_subject_alt_names_init(&tmp->san);
+ if (result < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&tmp->cert);
+ gnutls_free(tmp);
+ return result;
+ }
+
+ result = gnutls_subject_alt_names_init(&tmp->ian);
+ if (result < 0) {
+ gnutls_assert();
+ asn1_delete_structure(&tmp->cert);
+ gnutls_subject_alt_names_deinit(tmp->san);
+ gnutls_free(tmp);
+ return result;
+ }
+
+ /* If you add anything here, be sure to check if it has to be added
+ to gnutls_x509_crt_import as well. */
+
+ *cert = tmp;
+
+ return 0; /* success */
+}
+
+/*-
+ * _gnutls_x509_crt_cpy - This function copies a gnutls_x509_crt_t type
+ * @dest: The data where to copy
+ * @src: The data to be copied
+ * @flags: zero or CRT_CPY_FAST
+ *
+ * This function will copy an X.509 certificate structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ -*/
+int _gnutls_x509_crt_cpy(gnutls_x509_crt_t dest, gnutls_x509_crt_t src)
+{
+ int ret;
+ gnutls_datum_t tmp;
+ unsigned dealloc = 0;
+
+ if (src->der.size == 0 || src->modified) {
+ ret =
+ gnutls_x509_crt_export2(src, GNUTLS_X509_FMT_DER, &tmp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ dealloc = 1;
+ } else {
+ tmp.data = src->der.data;
+ tmp.size = src->der.size;
+ }
+
+ ret = gnutls_x509_crt_import(dest, &tmp, GNUTLS_X509_FMT_DER);
+
+ if (dealloc) {
+ gnutls_free(tmp.data);
+ }
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_deinit:
+ * @cert: The data to be deinitialized
+ *
+ * This function will deinitialize a certificate structure.
+ **/
+void gnutls_x509_crt_deinit(gnutls_x509_crt_t cert)
+{
+ if (!cert)
+ return;
+
+ if (cert->cert)
+ asn1_delete_structure(&cert->cert);
+ gnutls_free(cert->der.data);
+ gnutls_subject_alt_names_deinit(cert->san);
+ gnutls_subject_alt_names_deinit(cert->ian);
+ gnutls_free(cert);
+}
+
+static int compare_sig_algorithm(gnutls_x509_crt_t cert)
+{
+ int ret, len1, len2, result;
+ char oid1[MAX_OID_SIZE];
+ char oid2[MAX_OID_SIZE];
+ gnutls_datum_t sp1 = {NULL, 0};
+ gnutls_datum_t sp2 = {NULL, 0};
+ unsigned empty1 = 0, empty2 = 0;
+
+ len1 = sizeof(oid1);
+ result = asn1_read_value(cert->cert, "signatureAlgorithm.algorithm", oid1, &len1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ len2 = sizeof(oid2);
+ result = asn1_read_value(cert->cert, "tbsCertificate.signature.algorithm", oid2, &len2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (len1 != len2 || memcmp(oid1, oid2, len1) != 0) {
+ _gnutls_debug_log("signatureAlgorithm.algorithm differs from tbsCertificate.signature.algorithm: %s, %s\n",
+ oid1, oid2);
+ gnutls_assert();
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ /* compare the parameters */
+ ret = _gnutls_x509_read_value(cert->cert, "signatureAlgorithm.parameters", &sp1);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ empty1 = 1;
+ } else if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_x509_read_value(cert->cert, "tbsCertificate.signature.parameters", &sp2);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ empty2 = 1;
+ } else if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* handle equally empty parameters with missing parameters */
+ if (sp1.size == 2 && memcmp(sp1.data, "\x05\x00", 2) == 0) {
+ empty1 = 1;
+ _gnutls_free_datum(&sp1);
+ }
+
+ if (sp2.size == 2 && memcmp(sp2.data, "\x05\x00", 2) == 0) {
+ empty2 = 1;
+ _gnutls_free_datum(&sp2);
+ }
+
+ if (empty1 != empty2 ||
+ sp1.size != sp2.size ||
+ (sp1.size > 0 && memcmp(sp1.data, sp2.data, sp1.size) != 0)) {
+ gnutls_assert();
+ ret = GNUTLS_E_CERTIFICATE_ERROR;
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&sp1);
+ _gnutls_free_datum(&sp2);
+ return ret;
+}
+
+static int cache_alt_names(gnutls_x509_crt_t cert)
+{
+ gnutls_datum_t tmpder = {NULL, 0};
+ int ret;
+
+ /* pre-parse subject alt name */
+ ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.17", 0, &tmpder, NULL);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_free(tmpder.data);
+ return gnutls_assert_val(ret);
+ }
+
+ if (ret >= 0) {
+ ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->san, 0);
+ gnutls_free(tmpder.data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_x509_crt_get_extension(cert, "2.5.29.18", 0, &tmpder, NULL);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ return gnutls_assert_val(ret);
+
+ if (ret >= 0) {
+ ret = gnutls_x509_ext_import_subject_alt_names(&tmpder, cert->ian, 0);
+ gnutls_free(tmpder.data);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+static bool hcomparator(const void *v1, const void *v2)
+{
+ return (strcmp(v1, v2)==0);
+}
+
+static size_t hhasher(const void *entry, size_t n)
+{
+ const char *e = entry;
+ if (e == NULL || e[0] == 0)
+ return 0;
+
+ return hash_pjw_bare(e, strlen(e)) % n;
+}
+
+#ifdef STRICT_X509
+
+/* Check whether certificates serial number is RFC5280 compliant */
+static bool has_valid_serial(gnutls_x509_crt_t cert)
+{
+ int err, is_zero;
+ unsigned i;
+ unsigned char serial[128];
+ size_t serial_size = sizeof(serial);
+
+ err = gnutls_x509_crt_get_serial(cert, serial, &serial_size);
+ if (err < 0) {
+ _gnutls_debug_log("error: could not read serial number\n");
+ return false;
+ }
+
+ if (serial_size > 20) {
+ _gnutls_debug_log("error: serial number value is longer than 20 octets\n");
+ return false;
+ }
+
+ if (serial[0] & 0x80) {
+ _gnutls_debug_log("error: serial number is negative\n");
+ return false;
+ }
+
+ is_zero = 1;
+ for (i = 0; i < serial_size; ++i) {
+ if (serial[i]) {
+ is_zero = 0;
+ break;
+ }
+ }
+
+ if (is_zero) {
+ _gnutls_debug_log("error: serial number is zero\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* Check if extension can be successfully parsed */
+static bool is_valid_extension(const char *oid, gnutls_datum_t *der)
+{
+ int err = 0, i;
+ unsigned u;
+ size_t sz;
+ time_t t1, t2;
+ char *s1 = NULL, *s2 = NULL;
+ gnutls_datum_t datum = {NULL, 0};
+
+ if (!strcmp(oid, GNUTLS_X509EXT_OID_BASIC_CONSTRAINTS)) {
+ err = gnutls_x509_ext_import_basic_constraints(der, &u, &i);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_SUBJECT_KEY_ID)) {
+ err = gnutls_x509_ext_import_subject_key_id(der, &datum);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CRT_POLICY)) {
+ gnutls_x509_policies_t policies;
+ if (gnutls_x509_policies_init(&policies) < 0)
+ return false;
+ err = gnutls_x509_ext_import_policies(der, policies, 0);
+ gnutls_x509_policies_deinit(policies);
+ } else if (!strcmp(oid, GNUTLS_X509_OID_POLICY_ANY)) {
+ err = gnutls_x509_ext_import_inhibit_anypolicy(der, &u);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_AUTHORITY_KEY_ID)) {
+ gnutls_x509_aki_t aki;
+ if (gnutls_x509_aki_init(&aki) < 0)
+ return false;
+ err = gnutls_x509_ext_import_authority_key_id(der, aki, 0);
+ gnutls_x509_aki_deinit(aki);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_KEY_USAGE)) {
+ err = gnutls_x509_ext_import_key_usage(der, &u);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_PRIVATE_KEY_USAGE_PERIOD)) {
+ err = gnutls_x509_ext_import_private_key_usage_period(der, &t1, &t2);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_EXTENDED_KEY_USAGE)) {
+ gnutls_x509_key_purposes_t purposes;
+ if (gnutls_x509_key_purpose_init(&purposes) < 0)
+ return false;
+ err = gnutls_x509_ext_import_key_purposes(der, purposes, 0);
+ gnutls_x509_key_purpose_deinit(purposes);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_SAN) ||
+ !strcmp(oid, GNUTLS_X509EXT_OID_IAN)) {
+ gnutls_subject_alt_names_t names;
+ if (gnutls_subject_alt_names_init(&names) < 0)
+ return false;
+ err = gnutls_x509_ext_import_subject_alt_names(der, names, 0);
+ gnutls_subject_alt_names_deinit(names);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CRL_DIST_POINTS)) {
+ gnutls_x509_crl_dist_points_t dp;
+ if (gnutls_x509_crl_dist_points_init(&dp) < 0)
+ return false;
+ err = gnutls_x509_ext_import_crl_dist_points(der, dp, 0);
+ gnutls_x509_crl_dist_points_deinit(dp);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_PROXY_CRT_INFO)) {
+ err = gnutls_x509_ext_import_proxy(der, &i, &s1, &s2, &sz);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_AUTHORITY_INFO_ACCESS)) {
+ gnutls_x509_aia_t aia;
+ if (gnutls_x509_aia_init(&aia) < 0)
+ return false;
+ err = gnutls_x509_ext_import_aia(der, aia, 0);
+ gnutls_x509_aia_deinit(aia);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_CT_SCT_V1)) {
+ gnutls_x509_ct_scts_t scts;
+ if (gnutls_x509_ext_ct_scts_init(&scts) < 0)
+ return false;
+ err = gnutls_x509_ext_ct_import_scts(der, scts, 0);
+ gnutls_x509_ext_ct_scts_deinit(scts);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_NAME_CONSTRAINTS)) {
+ gnutls_x509_name_constraints_t nc;
+ if (gnutls_x509_name_constraints_init(&nc) < 0)
+ return false;
+ err = gnutls_x509_ext_import_name_constraints(der, nc, 0);
+ gnutls_x509_name_constraints_deinit(nc);
+ } else if (!strcmp(oid, GNUTLS_X509EXT_OID_TLSFEATURES)) {
+ gnutls_x509_tlsfeatures_t features;
+ if (gnutls_x509_tlsfeatures_init(&features) < 0)
+ return false;
+ err = gnutls_x509_ext_import_tlsfeatures(der, features, 0);
+ gnutls_x509_tlsfeatures_deinit(features);
+ } else {
+ return true;
+ }
+
+ gnutls_free(s1);
+ gnutls_free(s2);
+ _gnutls_free_datum(&datum);
+
+ return err == 0;
+}
+
+#endif /* STRICT_X509 */
+
+int _gnutls_check_cert_sanity(gnutls_x509_crt_t cert)
+{
+ int ret = 0, version;
+ gnutls_datum_t exts;
+ Hash_table *htable = NULL;
+
+ if (cert->flags & GNUTLS_X509_CRT_FLAG_IGNORE_SANITY)
+ return 0;
+
+ /* enforce the rule that only version 3 certificates carry extensions */
+ ret = gnutls_x509_crt_get_version(cert);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ version = ret;
+
+#ifdef STRICT_X509
+ /* enforce upper bound on certificate version (RFC5280 compliant) */
+ if (version > 3) {
+ _gnutls_debug_log("error: invalid certificate version %d\n", version);
+ return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ }
+#endif
+
+ if (version < 3) {
+ if (!cert->modified) {
+ ret = _gnutls_x509_get_raw_field2(cert->cert, &cert->der,
+ "tbsCertificate.extensions", &exts);
+ if (ret >= 0 && exts.size > 0) {
+ _gnutls_debug_log("error: extensions present in certificate with version %d\n", version);
+ return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ }
+ } else {
+ if (cert->use_extensions) {
+ _gnutls_debug_log("error: extensions set in certificate with version %d\n", version);
+ return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ }
+ }
+ } else {
+ /* Version is 3; ensure no duplicate extensions are present. */
+ unsigned i, critical;
+ char oid[MAX_OID_SIZE];
+ size_t oid_size;
+ char *o;
+
+ htable = hash_initialize(16, NULL, hhasher, hcomparator, gnutls_free);
+ if (htable == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ for (i=0;;i++) {
+ oid_size = sizeof(oid);
+ ret = gnutls_x509_crt_get_extension_info(cert, i, oid, &oid_size, &critical);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ gnutls_assert();
+ goto cleanup;
+ }
+ o = gnutls_strdup(oid);
+ if (o == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ ret = hash_insert_if_absent(htable, o, NULL);
+ if (ret == -1) {
+ gnutls_free(o);
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ } else if (ret == 0) {
+ /* duplicate */
+ gnutls_free(o);
+ _gnutls_debug_log("error: duplicate extension (%s) detected\n", oid);
+ ret = gnutls_assert_val(GNUTLS_E_X509_DUPLICATE_EXTENSION);
+ goto cleanup;
+ }
+
+#ifdef STRICT_X509
+ gnutls_datum_t der = { NULL, 0 };
+ ret = gnutls_x509_crt_get_extension_data2(cert, i, &der);
+ if (ret < 0)
+ continue;
+ if (critical && !is_valid_extension(oid, &der)) {
+ _gnutls_free_datum(&der);
+ _gnutls_debug_log("error: could not parse extension (%s)\n");
+ return gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ }
+ _gnutls_free_datum(&der);
+#endif
+ }
+
+ hash_free(htable);
+ htable = NULL;
+ }
+
+ if (version < 2) {
+ char id[128];
+ size_t id_size;
+
+ id_size = sizeof(id);
+ ret = gnutls_x509_crt_get_subject_unique_id(cert, id, &id_size);
+ if (ret >= 0 || ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ _gnutls_debug_log("error: subjectUniqueID present in certificate with version %d\n", version);
+ ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ goto cleanup;
+ }
+
+ id_size = sizeof(id);
+ ret = gnutls_x509_crt_get_issuer_unique_id(cert, id, &id_size);
+ if (ret >= 0 || ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ _gnutls_debug_log("error: subjectUniqueID present in certificate with version %d\n", version);
+ ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ goto cleanup;
+ }
+ }
+
+#ifdef STRICT_X509
+ if (!has_valid_serial(cert)) {
+ ret = gnutls_assert_val(GNUTLS_E_X509_CERTIFICATE_ERROR);
+ goto cleanup;
+ }
+#endif
+
+ if (gnutls_x509_crt_get_expiration_time(cert) == -1 ||
+ gnutls_x509_crt_get_activation_time(cert) == -1) {
+ _gnutls_debug_log("error: invalid expiration or activation time in certificate\n");
+ ret = gnutls_assert_val(GNUTLS_E_CERTIFICATE_TIME_ERROR);
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (htable)
+ hash_free(htable);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_import:
+ * @cert: The data to store the parsed certificate.
+ * @data: The DER or PEM encoded certificate.
+ * @format: One of DER or PEM
+ *
+ * This function will convert the given DER or PEM encoded Certificate
+ * to the native gnutls_x509_crt_t format. The output will be stored
+ * in @cert.
+ *
+ * If the Certificate is PEM encoded it should have a header of "X509
+ * CERTIFICATE", or "CERTIFICATE".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_import(gnutls_x509_crt_t cert,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format)
+{
+ int result;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (cert->expanded) {
+ /* Any earlier _asn1_strict_der_decode will modify the ASN.1
+ structure, so we need to replace it with a fresh
+ structure. */
+ result = crt_reinit(cert);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* If the Certificate is in PEM format then decode it
+ */
+ if (format == GNUTLS_X509_FMT_PEM) {
+ /* Try the first header */
+ result =
+ _gnutls_fbase64_decode(PEM_X509_CERT2, data->data,
+ data->size, &cert->der);
+
+ if (result < 0) {
+ /* try for the second header */
+ result =
+ _gnutls_fbase64_decode(PEM_X509_CERT,
+ data->data, data->size,
+ &cert->der);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+ } else {
+ result = _gnutls_set_datum(&cert->der, data->data, data->size);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ cert->expanded = 1;
+ cert->modified = 0;
+
+ result =
+ _asn1_strict_der_decode(&cert->cert, cert->der.data, cert->der.size, NULL);
+ if (result != ASN1_SUCCESS) {
+ result = _gnutls_asn2err(result);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = compare_sig_algorithm(cert);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* The following do not allocate but rather point to DER data */
+ result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der,
+ "tbsCertificate.issuer.rdnSequence",
+ &cert->raw_issuer_dn);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der,
+ "tbsCertificate.subject.rdnSequence",
+ &cert->raw_dn);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_get_raw_field2(cert->cert, &cert->der,
+ "tbsCertificate.subjectPublicKeyInfo",
+ &cert->raw_spki);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = cache_alt_names(cert);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_check_cert_sanity(cert);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Since we do not want to disable any extension
+ */
+ cert->use_extensions = 1;
+
+ return 0;
+
+ cleanup:
+ _gnutls_free_datum(&cert->der);
+ return result;
+}
+
+
+/**
+ * gnutls_x509_crt_get_issuer_dn:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @buf: a pointer to a structure to hold the name (may be null)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will copy the name of the Certificate issuer in the
+ * provided buffer. The name will be in the form
+ * "C=xxxx,O=yyyy,CN=zzzz" as described in RFC4514. The output string
+ * will be ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * If @buf is null then only the size will be filled.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crt_get_issuer_dn3().
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the @buf_size will be updated
+ * with the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if
+ * the DN does not exist, or another error value on error. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_dn(gnutls_x509_crt_t cert, char *buf,
+ size_t * buf_size)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_parse_dn(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_dn2:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ *
+ * This function will allocate buffer and copy the name of issuer of the Certificate.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crt_get_issuer_dn3().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.10
+ **/
+int
+gnutls_x509_crt_get_issuer_dn2(gnutls_x509_crt_t cert, gnutls_datum_t * dn)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ dn, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_dn3:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will allocate buffer and copy the name of issuer of the Certificate.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.7
+ **/
+int
+gnutls_x509_crt_get_issuer_dn3(gnutls_x509_crt_t cert, gnutls_datum_t *dn, unsigned flags)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ dn, flags);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_dn_by_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @oid: holds an Object Identified in null terminated string
+ * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one.
+ * @raw_flag: If non-zero returns the raw DER data of the DN part.
+ * @buf: a pointer to a structure to hold the name (may be null)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will extract the part of the name of the Certificate
+ * issuer specified by the given OID. The output, if the raw flag is not
+ * used, will be encoded as described in RFC4514. Thus a string that is
+ * ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * If raw flag is (0), this function will only return known OIDs as
+ * text. Other OIDs will be DER encoded, as described in RFC4514 --
+ * in hex format with a '#' prefix. You can check about known OIDs
+ * using gnutls_x509_dn_oid_known().
+ *
+ * If @buf is null then only the size will be filled. If the @raw_flag
+ * is not specified the output is always null terminated, although the
+ * @buf_size will not include the null character.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the @buf_size will be updated with
+ * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there
+ * are no data in the current index. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_dn_by_oid(gnutls_x509_crt_t cert,
+ const char *oid, unsigned indx,
+ unsigned int raw_flag, void *buf,
+ size_t * buf_size)
+{
+ gnutls_datum_t td;
+ int ret;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_parse_dn_oid(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ oid, indx, raw_flag, &td);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_strdatum_to_buf(&td, buf, buf_size);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_dn_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: This specifies which OID to return. Use (0) to get the first one.
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will extract the OIDs of the name of the Certificate
+ * issuer specified by the given index.
+ *
+ * If @oid is null then only the size will be filled. The @oid
+ * returned will be null terminated, although @oid_size will not
+ * account for the trailing null.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the @buf_size will be updated with
+ * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there
+ * are no data in the current index. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_dn_oid(gnutls_x509_crt_t cert,
+ unsigned indx, void *oid, size_t * oid_size)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn_oid(cert->cert,
+ "tbsCertificate.issuer.rdnSequence",
+ indx, oid, oid_size);
+}
+
+/**
+ * gnutls_x509_crt_get_dn:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @buf: a pointer to a structure to hold the name (may be null)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will copy the name of the Certificate in the provided
+ * buffer. The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * The @buf returned will be null terminated and the @buf_size will account
+ * for the trailing null. If @buf is null then only the size will be filled.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crt_get_dn3().
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the @buf_size will be updated
+ * with the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if
+ * the DN does not exist, or another error value on error. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_dn(gnutls_x509_crt_t cert, char *buf,
+ size_t * buf_size)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_parse_dn(cert->cert,
+ "tbsCertificate.subject.rdnSequence",
+ buf, buf_size, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crt_get_dn2:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ *
+ * This function will allocate buffer and copy the name of the Certificate.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * This function does not output a fully RFC4514 compliant string, if
+ * that is required see gnutls_x509_crt_get_dn3().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.10
+ **/
+int gnutls_x509_crt_get_dn2(gnutls_x509_crt_t cert, gnutls_datum_t * dn)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(cert->cert,
+ "tbsCertificate.subject.rdnSequence",
+ dn, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_crt_get_dn3:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: a pointer to a structure to hold the name; must be freed using gnutls_free()
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will allocate buffer and copy the name of the Certificate.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.7
+ **/
+int gnutls_x509_crt_get_dn3(gnutls_x509_crt_t cert, gnutls_datum_t *dn, unsigned flags)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(cert->cert,
+ "tbsCertificate.subject.rdnSequence",
+ dn, flags);
+}
+
+/**
+ * gnutls_x509_crt_get_dn_by_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @oid: holds an Object Identified in null terminated string
+ * @indx: In case multiple same OIDs exist in the RDN, this specifies which to send. Use (0) to get the first one.
+ * @raw_flag: If non-zero returns the raw DER data of the DN part.
+ * @buf: a pointer where the DN part will be copied (may be null).
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will extract the part of the name of the Certificate
+ * subject specified by the given OID. The output, if the raw flag is
+ * not used, will be encoded as described in RFC4514. Thus a string
+ * that is ASCII or UTF-8 encoded, depending on the certificate data.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * If raw flag is (0), this function will only return known OIDs as
+ * text. Other OIDs will be DER encoded, as described in RFC4514 --
+ * in hex format with a '#' prefix. You can check about known OIDs
+ * using gnutls_x509_dn_oid_known().
+ *
+ * If @buf is null then only the size will be filled. If the @raw_flag
+ * is not specified the output is always null terminated, although the
+ * @buf_size will not include the null character.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the @buf_size will be updated with
+ * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there
+ * are no data in the current index. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_dn_by_oid(gnutls_x509_crt_t cert, const char *oid,
+ unsigned indx, unsigned int raw_flag,
+ void *buf, size_t * buf_size)
+{
+ gnutls_datum_t td;
+ int ret;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_parse_dn_oid(cert->cert,
+ "tbsCertificate.subject.rdnSequence",
+ oid, indx, raw_flag, &td);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return _gnutls_strdatum_to_buf(&td, buf, buf_size);
+}
+
+/**
+ * gnutls_x509_crt_get_dn_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: This specifies which OID to return. Use (0) to get the first one.
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will extract the OIDs of the name of the Certificate
+ * subject specified by the given index.
+ *
+ * If @oid is null then only the size will be filled. The @oid
+ * returned will be null terminated, although @oid_size will not
+ * account for the trailing null.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not
+ * long enough, and in that case the @buf_size will be updated with
+ * the required size. %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if there
+ * are no data in the current index. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_dn_oid(gnutls_x509_crt_t cert,
+ unsigned indx, void *oid, size_t * oid_size)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn_oid(cert->cert,
+ "tbsCertificate.subject.rdnSequence",
+ indx, oid, oid_size);
+}
+
+/**
+ * gnutls_x509_crt_get_signature_algorithm:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ *
+ * This function will return a value of the #gnutls_sign_algorithm_t
+ * enumeration that is the signature algorithm that has been used to
+ * sign this certificate.
+ *
+ * Since 3.6.0 this function never returns a negative error code.
+ * Error cases and unknown/unsupported signature algorithms are
+ * mapped to %GNUTLS_SIGN_UNKNOWN.
+ *
+ * Returns: a #gnutls_sign_algorithm_t value
+ **/
+int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert)
+{
+ return map_errs_to_zero(_gnutls_x509_get_signature_algorithm(cert->cert,
+ "signatureAlgorithm"));
+}
+
+/**
+ * gnutls_x509_crt_get_signature_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will return the OID of the signature algorithm
+ * that has been used to sign this certificate. This is function
+ * is useful in the case gnutls_x509_crt_get_signature_algorithm()
+ * returned %GNUTLS_SIGN_UNKNOWN.
+ *
+ * Returns: zero or a negative error code on error.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_crt_get_signature_oid(gnutls_x509_crt_t cert, char *oid, size_t *oid_size)
+{
+ char str[MAX_OID_SIZE];
+ int len, result, ret;
+ gnutls_datum_t out;
+
+ len = sizeof(str);
+ result = asn1_read_value(cert->cert, "signatureAlgorithm.algorithm", str, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ out.data = (void*)str;
+ out.size = len;
+
+ ret = _gnutls_copy_string(&out, (void*)oid, oid_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_pk_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will return the OID of the public key algorithm
+ * on that certificate. This is function
+ * is useful in the case gnutls_x509_crt_get_pk_algorithm()
+ * returned %GNUTLS_PK_UNKNOWN.
+ *
+ * Returns: zero or a negative error code on error.
+ *
+ * Since: 3.5.0
+ **/
+int gnutls_x509_crt_get_pk_oid(gnutls_x509_crt_t cert, char *oid, size_t *oid_size)
+{
+ char str[MAX_OID_SIZE];
+ int len, result, ret;
+ gnutls_datum_t out;
+
+ len = sizeof(str);
+ result = asn1_read_value(cert->cert, "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm", str, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ out.data = (void*)str;
+ out.size = len;
+
+ ret = _gnutls_copy_string(&out, (void*)oid, oid_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_signature:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @sig: a pointer where the signature part will be copied (may be null).
+ * @sig_size: initially holds the size of @sig
+ *
+ * This function will extract the signature field of a certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_get_signature(gnutls_x509_crt_t cert,
+ char *sig, size_t * sig_size)
+{
+ gnutls_datum_t dsig = {NULL, 0};
+ int ret;
+
+ if (cert == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gnutls_x509_get_signature(cert->cert, "signature", &dsig);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_copy_data(&dsig, (uint8_t*)sig, sig_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(dsig.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_version:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ *
+ * This function will return the version of the specified Certificate.
+ *
+ * Returns: version of certificate, or a negative error code on error.
+ **/
+int gnutls_x509_crt_get_version(gnutls_x509_crt_t cert)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_version(cert->cert, "tbsCertificate.version");
+}
+
+/**
+ * gnutls_x509_crt_get_activation_time:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ *
+ * This function will return the time this Certificate was or will be
+ * activated.
+ *
+ * Returns: activation time, or (time_t)-1 on error.
+ **/
+time_t gnutls_x509_crt_get_activation_time(gnutls_x509_crt_t cert)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+ return _gnutls_x509_get_time(cert->cert,
+ "tbsCertificate.validity.notBefore",
+ 0);
+}
+
+/**
+ * gnutls_x509_crt_get_expiration_time:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ *
+ * This function will return the time this certificate was or will be
+ * expired.
+ *
+ * Returns: expiration time, or (time_t)-1 on error.
+ **/
+time_t gnutls_x509_crt_get_expiration_time(gnutls_x509_crt_t cert)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return (time_t) - 1;
+ }
+
+ return _gnutls_x509_get_time(cert->cert,
+ "tbsCertificate.validity.notAfter",
+ 0);
+}
+
+/**
+ * gnutls_x509_crt_get_private_key_usage_period:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @activation: The activation time
+ * @expiration: The expiration time
+ * @critical: the extension status
+ *
+ * This function will return the expiration and activation
+ * times of the private key of the certificate. It relies on
+ * the PKIX extension 2.5.29.16 being present.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ **/
+int
+gnutls_x509_crt_get_private_key_usage_period(gnutls_x509_crt_t cert,
+ time_t * activation,
+ time_t * expiration,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t der = { NULL, 0 };
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.16", 0, &der,
+ critical);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (der.size == 0 || der.data == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ ret = gnutls_x509_ext_import_private_key_usage_period(&der, activation, expiration);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ _gnutls_free_datum(&der);
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crt_get_serial:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @result: The place where the serial number will be copied
+ * @result_size: Holds the size of the result field.
+ *
+ * This function will return the X.509 certificate's serial number.
+ * This is obtained by the X509 Certificate serialNumber field. Serial
+ * is not always a 32 or 64bit number. Some CAs use large serial
+ * numbers, thus it may be wise to handle it as something uint8_t.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_get_serial(gnutls_x509_crt_t cert, void *result,
+ size_t * result_size)
+{
+ int ret, len;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ len = *result_size;
+ ret =
+ asn1_read_value(cert->cert, "tbsCertificate.serialNumber",
+ result, &len);
+ *result_size = len;
+
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_subject_key_id:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @ret: The place where the identifier will be copied
+ * @ret_size: Holds the size of the result field.
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the X.509v3 certificate's subject key
+ * identifier. This is obtained by the X.509 Subject Key identifier
+ * extension field (2.5.29.14).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ **/
+int
+gnutls_x509_crt_get_subject_key_id(gnutls_x509_crt_t cert, void *ret,
+ size_t * ret_size,
+ unsigned int *critical)
+{
+ int result;
+ gnutls_datum_t id = {NULL,0};
+ gnutls_datum_t der = {NULL, 0};
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (ret == NULL)
+ *ret_size = 0;
+
+ if ((result =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &der,
+ critical)) < 0) {
+ return result;
+ }
+
+ result = gnutls_x509_ext_import_subject_key_id(&der, &id);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_copy_data(&id, ret, ret_size);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ gnutls_free(der.data);
+ gnutls_free(id.data);
+ return result;
+}
+
+inline static int is_type_printable(int type)
+{
+ if (type == GNUTLS_SAN_DNSNAME || type == GNUTLS_SAN_RFC822NAME ||
+ type == GNUTLS_SAN_URI || type == GNUTLS_SAN_OTHERNAME_XMPP ||
+ type == GNUTLS_SAN_OTHERNAME || type == GNUTLS_SAN_REGISTERED_ID)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_authority_key_gn_serial:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @alt: is the place where the alternative name will be copied to
+ * @alt_size: holds the size of alt.
+ * @alt_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t).
+ * @serial: buffer to store the serial number (may be null)
+ * @serial_size: Holds the size of the serial field (may be null)
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the X.509 authority key
+ * identifier when stored as a general name (authorityCertIssuer)
+ * and serial number.
+ *
+ * Because more than one general names might be stored
+ * @seq can be used as a counter to request them all until
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crt_get_authority_key_gn_serial(gnutls_x509_crt_t cert,
+ unsigned int seq, void *alt,
+ size_t * alt_size,
+ unsigned int *alt_type,
+ void *serial,
+ size_t * serial_size,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t der, san, iserial;
+ gnutls_x509_aki_t aki = NULL;
+ unsigned san_type;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &der,
+ critical)) < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ if (der.size == 0 || der.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_x509_aki_init(&aki);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_import_authority_key_id(&der, aki, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_aki_get_cert_issuer(aki, seq, &san_type, &san, NULL, &iserial);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (is_type_printable(san_type))
+ ret = _gnutls_copy_string(&san, alt, alt_size);
+ else
+ ret = _gnutls_copy_data(&san, alt, alt_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (alt_type)
+ *alt_type = san_type;
+
+ ret = _gnutls_copy_data(&iserial, serial, serial_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ if (aki != NULL)
+ gnutls_x509_aki_deinit(aki);
+ gnutls_free(der.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_authority_key_id:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @id: The place where the identifier will be copied
+ * @id_size: Holds the size of the id field.
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the X.509v3 certificate authority's key
+ * identifier. This is obtained by the X.509 Authority Key
+ * identifier extension field (2.5.29.35). Note that this function
+ * only returns the keyIdentifier field of the extension and
+ * %GNUTLS_E_X509_UNSUPPORTED_EXTENSION, if the extension contains
+ * the name and serial number of the certificate. In that case
+ * gnutls_x509_crt_get_authority_key_gn_serial() may be used.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ **/
+int
+gnutls_x509_crt_get_authority_key_id(gnutls_x509_crt_t cert, void *id,
+ size_t * id_size,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t der, l_id;
+ gnutls_x509_aki_t aki = NULL;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &der,
+ critical)) < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ if (der.size == 0 || der.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_x509_aki_init(&aki);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_import_authority_key_id(&der, aki, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_aki_get_id(aki, &l_id);
+
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_datum_t serial;
+ ret = gnutls_x509_aki_get_cert_issuer(aki, 0, NULL, NULL, NULL, &serial);
+ if (ret >= 0) {
+ ret = gnutls_assert_val(GNUTLS_E_X509_UNSUPPORTED_EXTENSION);
+ } else {
+ ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ }
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_copy_data(&l_id, id, id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ if (aki != NULL)
+ gnutls_x509_aki_deinit(aki);
+ gnutls_free(der.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_pk_algorithm:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @bits: if bits is non null it will hold the size of the parameters' in bits
+ *
+ * This function will return the public key algorithm of an X.509
+ * certificate.
+ *
+ * If bits is non null, it should have enough size to hold the parameters
+ * size in bits. For RSA the bits returned is the modulus.
+ * For DSA the bits returned are of the public
+ * exponent.
+ *
+ * Unknown/unsupported algorithms are mapped to %GNUTLS_PK_UNKNOWN.
+ *
+ * Returns: a member of the #gnutls_pk_algorithm_t enumeration on
+ * success, or a negative error code on error.
+ **/
+int
+gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert,
+ unsigned int *bits)
+{
+ int result;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (bits)
+ *bits = 0;
+
+ result =
+ _gnutls_x509_get_pk_algorithm(cert->cert,
+ "tbsCertificate.subjectPublicKeyInfo",
+ NULL,
+ bits);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_get_spki:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t
+ * @flags: must be zero
+ *
+ * This function will return the public key information of an X.509
+ * certificate. The provided @spki must be initialized.
+ *
+ * Since: 3.6.0
+ **/
+int
+gnutls_x509_crt_get_spki(gnutls_x509_crt_t cert, gnutls_x509_spki_t spki, unsigned int flags)
+{
+ int result;
+ gnutls_x509_spki_st params;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+
+ spki->pk = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
+
+ memset(&params, 0, sizeof(params));
+
+ result = _gnutls_x509_crt_read_spki_params(cert, &params);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if (params.pk == GNUTLS_PK_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ spki->rsa_pss_dig = params.rsa_pss_dig;
+ spki->salt_size = params.salt_size;
+
+ return 0;
+}
+
+/* returns the type and the name on success.
+ * Type is also returned as a parameter in case of an error.
+ *
+ * @seq: in case of GeneralNames it will return the corresponding name.
+ * in case of GeneralName, it must be -1
+ * @dname: the name returned
+ * @ret_type: The type of the name
+ * @othername_oid: if the name is otherName return the OID
+ *
+ */
+int
+_gnutls_parse_general_name2(asn1_node src, const char *src_name,
+ int seq, gnutls_datum_t *dname,
+ unsigned int *ret_type, int othername_oid)
+{
+ int len, ret;
+ char nptr[MAX_NAME_SIZE];
+ int result;
+ gnutls_datum_t tmp = {NULL, 0};
+ char choice_type[128];
+ gnutls_x509_subject_alt_name_t type;
+
+ if (seq != -1) {
+ seq++; /* 0->1, 1->2 etc */
+
+ if (src_name[0] != 0)
+ snprintf(nptr, sizeof(nptr), "%s.?%d", src_name, seq);
+ else
+ snprintf(nptr, sizeof(nptr), "?%d", seq);
+ } else {
+ snprintf(nptr, sizeof(nptr), "%s", src_name);
+ }
+
+ len = sizeof(choice_type);
+ result = asn1_read_value(src, nptr, choice_type, &len);
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ type = _gnutls_x509_san_find_type(choice_type);
+ if (type == (gnutls_x509_subject_alt_name_t) - 1) {
+ gnutls_assert();
+ return GNUTLS_E_X509_UNKNOWN_SAN;
+ }
+
+ if (ret_type)
+ *ret_type = type;
+
+ if (type == GNUTLS_SAN_OTHERNAME) {
+ if (othername_oid)
+ _gnutls_str_cat(nptr, sizeof(nptr),
+ ".otherName.type-id");
+ else
+ _gnutls_str_cat(nptr, sizeof(nptr),
+ ".otherName.value");
+
+ ret = _gnutls_x509_read_value(src, nptr, &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (othername_oid) {
+ dname->size = tmp.size;
+ dname->data = tmp.data;
+ } else {
+ char oid[MAX_OID_SIZE];
+
+ if (src_name[0] != 0 && seq != -1)
+ snprintf(nptr, sizeof(nptr),
+ "%s.?%d.otherName.type-id",
+ src_name, seq);
+ else if (src_name[0] != 0)
+ snprintf(nptr, sizeof(nptr),
+ "%s.otherName.type-id",
+ src_name);
+ else
+ snprintf(nptr, sizeof(nptr),
+ "?%d.otherName.type-id", seq);
+
+ len = sizeof(oid);
+
+ result = asn1_read_value(src, nptr, oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ if (len > 0) len--;
+
+ dname->size = tmp.size;
+ dname->data = tmp.data;
+ }
+ } else if (type == GNUTLS_SAN_DN) {
+ _gnutls_str_cat(nptr, sizeof(nptr), ".directoryName");
+ ret = _gnutls_x509_get_dn(src, nptr, dname, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else if (othername_oid) {
+ gnutls_assert();
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ } else {
+ _gnutls_str_cat(nptr, sizeof(nptr), ".");
+ _gnutls_str_cat(nptr, sizeof(nptr), choice_type);
+
+ ret = _gnutls_x509_read_null_value(src, nptr, &tmp);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* _gnutls_x509_read_value() null terminates */
+ dname->size = tmp.size;
+ dname->data = tmp.data;
+ }
+
+ return type;
+
+ cleanup:
+ gnutls_free(tmp.data);
+ return ret;
+}
+
+/* returns the type and the name on success.
+ * Type is also returned as a parameter in case of an error.
+ */
+int
+_gnutls_parse_general_name(asn1_node src, const char *src_name,
+ int seq, void *name, size_t * name_size,
+ unsigned int *ret_type, int othername_oid)
+{
+ int ret;
+ gnutls_datum_t res = {NULL,0};
+ unsigned type;
+
+ ret = _gnutls_parse_general_name2(src, src_name, seq, &res, ret_type, othername_oid);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ type = ret;
+
+ if (is_type_printable(type)) {
+ ret = _gnutls_copy_string(&res, name, name_size);
+ } else {
+ ret = _gnutls_copy_data(&res, name, name_size);
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = type;
+cleanup:
+ gnutls_free(res.data);
+ return ret;
+}
+
+static int
+get_alt_name(gnutls_subject_alt_names_t san,
+ unsigned int seq, uint8_t *alt,
+ size_t * alt_size, unsigned int *alt_type,
+ unsigned int *critical, int othername_oid)
+{
+ int ret;
+ gnutls_datum_t ooid = {NULL, 0};
+ gnutls_datum_t oname;
+ gnutls_datum_t virt = {NULL, 0};
+ unsigned int type;
+
+ if (san == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ if (alt == NULL)
+ *alt_size = 0;
+
+ ret = gnutls_subject_alt_names_get(san, seq, &type, &oname, &ooid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (type == GNUTLS_SAN_OTHERNAME && ooid.data) {
+ unsigned vtype;
+ ret = gnutls_x509_othername_to_virtual((char*)ooid.data, &oname, &vtype, &virt);
+ if (ret >= 0) {
+ type = vtype;
+ oname.data = virt.data;
+ oname.size = virt.size;
+ }
+ }
+
+ if (alt_type)
+ *alt_type = type;
+
+ if (othername_oid) {
+ ret = _gnutls_copy_string(&ooid, alt, alt_size);
+ } else {
+ if (is_type_printable(type)) {
+ ret = _gnutls_copy_string(&oname, alt, alt_size);
+ } else {
+ ret = _gnutls_copy_data(&oname, alt, alt_size);
+ }
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = type;
+cleanup:
+ gnutls_free(virt.data);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_subject_alt_name:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @san: is the place where the alternative name will be copied to
+ * @san_size: holds the size of san.
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function retrieves the Alternative Name (2.5.29.17), contained
+ * in the given certificate in the X509v3 Certificate Extensions.
+ *
+ * When the SAN type is otherName, it will extract the data in the
+ * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned.
+ * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get
+ * the corresponding OID and the "virtual" SAN types (e.g.,
+ * %GNUTLS_SAN_OTHERNAME_XMPP).
+ *
+ * If an otherName OID is known, the data will be decoded. Otherwise
+ * the returned data will be DER encoded, and you will have to decode
+ * it yourself. Currently, only the RFC 3920 id-on-xmppAddr SAN is
+ * recognized.
+ *
+ * Returns: the alternative subject name type on success, one of the
+ * enumerated #gnutls_x509_subject_alt_name_t. It will return
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER if @san_size is not large enough to
+ * hold the value. In that case @san_size will be updated with the
+ * required size. If the certificate does not have an Alternative
+ * name with the specified sequence number then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert,
+ unsigned int seq, void *san,
+ size_t * san_size,
+ unsigned int *critical)
+{
+ return get_alt_name(cert->san, seq, san, san_size, NULL,
+ critical, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_name:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ian: is the place where the alternative name will be copied to
+ * @ian_size: holds the size of ian.
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function retrieves the Issuer Alternative Name (2.5.29.18),
+ * contained in the given certificate in the X509v3 Certificate
+ * Extensions.
+ *
+ * When the SAN type is otherName, it will extract the data in the
+ * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned.
+ * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get
+ * the corresponding OID and the "virtual" SAN types (e.g.,
+ * %GNUTLS_SAN_OTHERNAME_XMPP).
+ *
+ * If an otherName OID is known, the data will be decoded. Otherwise
+ * the returned data will be DER encoded, and you will have to decode
+ * it yourself. Currently, only the RFC 3920 id-on-xmppAddr Issuer
+ * AltName is recognized.
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ * enumerated #gnutls_x509_subject_alt_name_t. It will return
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ian_size is not large enough
+ * to hold the value. In that case @ian_size will be updated with
+ * the required size. If the certificate does not have an
+ * Alternative name with the specified sequence number then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_name(gnutls_x509_crt_t cert,
+ unsigned int seq, void *ian,
+ size_t * ian_size,
+ unsigned int *critical)
+{
+ return get_alt_name(cert->ian, seq, ian, ian_size, NULL,
+ critical, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_subject_alt_name2:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @san: is the place where the alternative name will be copied to
+ * @san_size: holds the size of ret.
+ * @san_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t).
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the alternative names, contained in the
+ * given certificate. It is the same as
+ * gnutls_x509_crt_get_subject_alt_name() except for the fact that it
+ * will return the type of the alternative name in @san_type even if
+ * the function fails for some reason (i.e. the buffer provided is
+ * not enough).
+ *
+ * Returns: the alternative subject name type on success, one of the
+ * enumerated #gnutls_x509_subject_alt_name_t. It will return
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER if @san_size is not large enough
+ * to hold the value. In that case @san_size will be updated with
+ * the required size. If the certificate does not have an
+ * Alternative name with the specified sequence number then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_subject_alt_name2(gnutls_x509_crt_t cert,
+ unsigned int seq, void *san,
+ size_t * san_size,
+ unsigned int *san_type,
+ unsigned int *critical)
+{
+ return get_alt_name(cert->san, seq, san, san_size,
+ san_type, critical, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_name2:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ian: is the place where the alternative name will be copied to
+ * @ian_size: holds the size of ret.
+ * @ian_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t).
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the alternative names, contained in the
+ * given certificate. It is the same as
+ * gnutls_x509_crt_get_issuer_alt_name() except for the fact that it
+ * will return the type of the alternative name in @ian_type even if
+ * the function fails for some reason (i.e. the buffer provided is
+ * not enough).
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ * enumerated #gnutls_x509_subject_alt_name_t. It will return
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER if @ian_size is not large enough
+ * to hold the value. In that case @ian_size will be updated with
+ * the required size. If the certificate does not have an
+ * Alternative name with the specified sequence number then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Since: 2.10.0
+ *
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_name2(gnutls_x509_crt_t cert,
+ unsigned int seq, void *ian,
+ size_t * ian_size,
+ unsigned int *ian_type,
+ unsigned int *critical)
+{
+ return get_alt_name(cert->ian, seq, ian, ian_size,
+ ian_type, critical, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_subject_alt_othername_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @oid: is the place where the otherName OID will be copied to
+ * @oid_size: holds the size of ret.
+ *
+ * This function will extract the type OID of an otherName Subject
+ * Alternative Name, contained in the given certificate, and return
+ * the type as an enumerated element.
+ *
+ * This function is only useful if
+ * gnutls_x509_crt_get_subject_alt_name() returned
+ * %GNUTLS_SAN_OTHERNAME.
+ *
+ * If @oid is null then only the size will be filled. The @oid
+ * returned will be null terminated, although @oid_size will not
+ * account for the trailing null.
+ *
+ * Returns: the alternative subject name type on success, one of the
+ * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it
+ * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types,
+ * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for
+ * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if
+ * @ian_size is not large enough to hold the value. In that case
+ * @ian_size will be updated with the required size. If the
+ * certificate does not have an Alternative name with the specified
+ * sequence number and with the otherName type then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_subject_alt_othername_oid(gnutls_x509_crt_t cert,
+ unsigned int seq,
+ void *oid, size_t * oid_size)
+{
+ return get_alt_name(cert->san, seq, oid, oid_size, NULL,
+ NULL, 1);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_othername_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ret: is the place where the otherName OID will be copied to
+ * @ret_size: holds the size of ret.
+ *
+ * This function will extract the type OID of an otherName Subject
+ * Alternative Name, contained in the given certificate, and return
+ * the type as an enumerated element.
+ *
+ * If @oid is null then only the size will be filled. The @oid
+ * returned will be null terminated, although @oid_size will not
+ * account for the trailing null.
+ *
+ * This function is only useful if
+ * gnutls_x509_crt_get_issuer_alt_name() returned
+ * %GNUTLS_SAN_OTHERNAME.
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ * enumerated gnutls_x509_subject_alt_name_t. For supported OIDs, it
+ * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types,
+ * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for
+ * unknown OIDs. It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if
+ * @ret_size is not large enough to hold the value. In that case
+ * @ret_size will be updated with the required size. If the
+ * certificate does not have an Alternative name with the specified
+ * sequence number and with the otherName type then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_othername_oid(gnutls_x509_crt_t cert,
+ unsigned int seq,
+ void *ret, size_t * ret_size)
+{
+ return get_alt_name(cert->ian, seq, ret, ret_size, NULL,
+ NULL, 1);
+}
+
+/**
+ * gnutls_x509_crt_get_basic_constraints:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @critical: will be non-zero if the extension is marked as critical
+ * @ca: pointer to output integer indicating CA status, may be NULL,
+ * value is 1 if the certificate CA flag is set, 0 otherwise.
+ * @pathlen: pointer to output integer indicating path length (may be
+ * NULL), non-negative error codes indicate a present pathLenConstraint
+ * field and the actual value, -1 indicate that the field is absent.
+ *
+ * This function will read the certificate's basic constraints, and
+ * return the certificates CA status. It reads the basicConstraints
+ * X.509 extension (2.5.29.19).
+ *
+ * Returns: If the certificate is a CA a positive value will be
+ * returned, or (0) if the certificate does not have CA flag set. A
+ * negative error code may be returned in case of errors. If the
+ * certificate does not contain the basicConstraints extension
+ * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ **/
+int
+gnutls_x509_crt_get_basic_constraints(gnutls_x509_crt_t cert,
+ unsigned int *critical,
+ unsigned int *ca, int *pathlen)
+{
+ int result;
+ gnutls_datum_t basicConstraints;
+ unsigned int tmp_ca;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.19", 0,
+ &basicConstraints,
+ critical)) < 0) {
+ return result;
+ }
+
+ if (basicConstraints.size == 0 || basicConstraints.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ result = gnutls_x509_ext_import_basic_constraints(&basicConstraints, &tmp_ca, pathlen);
+ if (ca)
+ *ca = tmp_ca;
+
+ _gnutls_free_datum(&basicConstraints);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return tmp_ca;
+}
+
+/**
+ * gnutls_x509_crt_get_ca_status:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return certificates CA status, by reading the
+ * basicConstraints X.509 extension (2.5.29.19). If the certificate is
+ * a CA a positive value will be returned, or (0) if the certificate
+ * does not have CA flag set.
+ *
+ * Use gnutls_x509_crt_get_basic_constraints() if you want to read the
+ * pathLenConstraint field too.
+ *
+ * Returns: If the certificate is a CA a positive value will be
+ * returned, or (0) if the certificate does not have CA flag set. A
+ * negative error code may be returned in case of errors. If the
+ * certificate does not contain the basicConstraints extension
+ * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ **/
+int
+gnutls_x509_crt_get_ca_status(gnutls_x509_crt_t cert,
+ unsigned int *critical)
+{
+ int pathlen;
+ unsigned int ca;
+ return gnutls_x509_crt_get_basic_constraints(cert, critical, &ca,
+ &pathlen);
+}
+
+/**
+ * gnutls_x509_crt_get_key_usage:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @key_usage: where the key usage bits will be stored
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return certificate's key usage, by reading the
+ * keyUsage X.509 extension (2.5.29.15). The key usage value will ORed
+ * values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE,
+ * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT,
+ * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT,
+ * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN,
+ * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY.
+ *
+ * Returns: zero on success, or a negative error code in case of
+ * parsing error. If the certificate does not contain the keyUsage
+ * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ **/
+int
+gnutls_x509_crt_get_key_usage(gnutls_x509_crt_t cert,
+ unsigned int *key_usage,
+ unsigned int *critical)
+{
+ int result;
+ gnutls_datum_t keyUsage;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.15", 0,
+ &keyUsage, critical)) < 0) {
+ return result;
+ }
+
+ if (keyUsage.size == 0 || keyUsage.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ result = gnutls_x509_ext_import_key_usage(&keyUsage, key_usage);
+ _gnutls_free_datum(&keyUsage);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_inhibit_anypolicy:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable.
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return certificate's value of the SkipCerts, i.e.,
+ * the Inhibit anyPolicy X.509 extension (2.5.29.54).
+ *
+ * The returned value is the number of additional certificates that
+ * may appear in the path before the anyPolicy is no longer acceptable.
+
+ * Returns: zero on success, or a negative error code in case of
+ * parsing error. If the certificate does not contain the Inhibit anyPolicy
+ * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ *
+ * Since: 3.6.0
+ **/
+int
+gnutls_x509_crt_get_inhibit_anypolicy(gnutls_x509_crt_t cert,
+ unsigned int *skipcerts,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t ext;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.54", 0,
+ &ext, critical)) < 0) {
+ return ret;
+ }
+
+ if (ext.size == 0 || ext.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_x509_ext_import_key_usage(&ext, skipcerts);
+ _gnutls_free_datum(&ext);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_proxy:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @critical: will be non-zero if the extension is marked as critical
+ * @pathlen: pointer to output integer indicating path length (may be
+ * NULL), non-negative error codes indicate a present pCPathLenConstraint
+ * field and the actual value, -1 indicate that the field is absent.
+ * @policyLanguage: output variable with OID of policy language
+ * @policy: output variable with policy data
+ * @sizeof_policy: output variable size of policy data
+ *
+ * This function will get information from a proxy certificate. It
+ * reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned.
+ **/
+int
+gnutls_x509_crt_get_proxy(gnutls_x509_crt_t cert,
+ unsigned int *critical,
+ int *pathlen,
+ char **policyLanguage,
+ char **policy, size_t * sizeof_policy)
+{
+ int result;
+ gnutls_datum_t proxyCertInfo;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ _gnutls_x509_crt_get_extension(cert, "1.3.6.1.5.5.7.1.14", 0,
+ &proxyCertInfo, critical)) < 0)
+ {
+ return result;
+ }
+
+ if (proxyCertInfo.size == 0 || proxyCertInfo.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ result = gnutls_x509_ext_import_proxy(&proxyCertInfo, pathlen,
+ policyLanguage,
+ policy,
+ sizeof_policy);
+ _gnutls_free_datum(&proxyCertInfo);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+
+/**
+ * gnutls_x509_policy_release:
+ * @policy: a certificate policy
+ *
+ * This function will deinitialize all memory associated with the provided
+ * @policy. The policy is allocated using gnutls_x509_crt_get_policy().
+ *
+ * Since: 3.1.5
+ **/
+void gnutls_x509_policy_release(struct gnutls_x509_policy_st *policy)
+{
+ unsigned i;
+
+ gnutls_free(policy->oid);
+ for (i = 0; i < policy->qualifiers; i++)
+ gnutls_free(policy->qualifier[i].data);
+}
+
+
+/**
+ * gnutls_x509_crt_get_policy:
+ * @crt: should contain a #gnutls_x509_crt_t type
+ * @indx: This specifies which policy to return. Use (0) to get the first one.
+ * @policy: A pointer to a policy structure.
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will extract the certificate policy (extension 2.5.29.32)
+ * specified by the given index.
+ *
+ * The policy returned by this function must be deinitialized by using
+ * gnutls_x509_policy_release().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.1.5
+ **/
+int
+gnutls_x509_crt_get_policy(gnutls_x509_crt_t crt, unsigned indx,
+ struct gnutls_x509_policy_st *policy,
+ unsigned int *critical)
+{
+ gnutls_datum_t tmpd = { NULL, 0 };
+ int ret;
+ gnutls_x509_policies_t policies = NULL;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ memset(policy, 0, sizeof(*policy));
+
+ ret = gnutls_x509_policies_init(&policies);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.32", 0, &tmpd,
+ critical)) < 0) {
+ goto cleanup;
+ }
+
+ if (tmpd.size == 0 || tmpd.data == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_import_policies(&tmpd, policies, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_policies_get(policies, indx, policy);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_x509_policies_erase(policies, indx);
+
+ ret = 0;
+
+ cleanup:
+ if (policies != NULL)
+ gnutls_x509_policies_deinit(policies);
+ _gnutls_free_datum(&tmpd);
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crt_get_extension_by_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @oid: holds an Object Identified in null terminated string
+ * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use (0) to get the first one.
+ * @buf: a pointer to a structure to hold the name (may be null)
+ * @buf_size: initially holds the size of @buf
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return the extension specified by the OID in the
+ * certificate. The extensions will be returned as binary data DER
+ * encoded, in the provided buffer.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If the certificate does not
+ * contain the specified extension
+ * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ **/
+int
+gnutls_x509_crt_get_extension_by_oid(gnutls_x509_crt_t cert,
+ const char *oid, unsigned indx,
+ void *buf, size_t * buf_size,
+ unsigned int *critical)
+{
+ int result;
+ gnutls_datum_t output;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((result =
+ _gnutls_x509_crt_get_extension(cert, oid, indx, &output,
+ critical)) < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ if (output.size == 0 || output.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ if (output.size > (unsigned int) *buf_size) {
+ *buf_size = output.size;
+ _gnutls_free_datum(&output);
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ *buf_size = output.size;
+
+ if (buf)
+ memcpy(buf, output.data, output.size);
+
+ _gnutls_free_datum(&output);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_extension_by_oid2:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @oid: holds an Object Identified in null terminated string
+ * @indx: In case multiple same OIDs exist in the extensions, this specifies which to send. Use (0) to get the first one.
+ * @output: will hold the allocated extension data
+ * @critical: will be non-zero if the extension is marked as critical
+ *
+ * This function will return the extension specified by the OID in the
+ * certificate. The extensions will be returned as binary data DER
+ * encoded, in the provided buffer.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If the certificate does not
+ * contain the specified extension
+ * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned.
+ *
+ * Since: 3.3.8
+ **/
+int
+gnutls_x509_crt_get_extension_by_oid2(gnutls_x509_crt_t cert,
+ const char *oid, unsigned indx,
+ gnutls_datum_t *output,
+ unsigned int *critical)
+{
+ int ret;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(cert, oid, indx, output,
+ critical)) < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (output->size == 0 || output->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_extension_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: Specifies which extension OID to send. Use (0) to get the first one.
+ * @oid: a pointer to a structure to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ *
+ * This function will return the requested extension OID in the certificate.
+ * The extension OID will be stored as a string in the provided buffer.
+ *
+ * The @oid returned will be null terminated, although @oid_size will not
+ * account for the trailing null.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If you have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ **/
+int
+gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert, unsigned indx,
+ void *oid, size_t * oid_size)
+{
+ int result;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ _gnutls_x509_crt_get_extension_oid(cert, indx, oid, oid_size);
+ if (result < 0) {
+ return result;
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crt_get_extension_info:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: Specifies which extension OID to send. Use (0) to get the first one.
+ * @oid: a pointer to a structure to hold the OID
+ * @oid_size: initially holds the maximum size of @oid, on return
+ * holds actual size of @oid.
+ * @critical: output variable with critical flag, may be NULL.
+ *
+ * This function will return the requested extension OID in the
+ * certificate, and the critical flag for it. The extension OID will
+ * be stored as a string in the provided buffer. Use
+ * gnutls_x509_crt_get_extension() to extract the data.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * @oid_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER will be
+ * returned. The @oid returned will be null terminated, although
+ * @oid_size will not account for the trailing null (the latter is not
+ * true for GnuTLS prior to 3.6.0).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If you have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ **/
+int
+gnutls_x509_crt_get_extension_info(gnutls_x509_crt_t cert, unsigned indx,
+ void *oid, size_t * oid_size,
+ unsigned int *critical)
+{
+ int result;
+ char str_critical[10];
+ char name[MAX_NAME_SIZE];
+ int len;
+
+ if (!cert) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsCertificate.extensions.?%u.extnID", indx + 1);
+
+ len = *oid_size;
+ result = asn1_read_value(cert->cert, name, oid, &len);
+ *oid_size = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* remove any trailing null */
+ if (oid && len > 0 && ((uint8_t*)oid)[len-1] == 0)
+ (*oid_size)--;
+
+ if (critical) {
+ snprintf(name, sizeof(name),
+ "tbsCertificate.extensions.?%u.critical", indx + 1);
+ len = sizeof(str_critical);
+ result = asn1_read_value(cert->cert, name, str_critical, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (str_critical[0] == 'T')
+ *critical = 1;
+ else
+ *critical = 0;
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crt_get_extension_data:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: Specifies which extension OID to send. Use (0) to get the first one.
+ * @data: a pointer to a structure to hold the data (may be null)
+ * @sizeof_data: initially holds the size of @data
+ *
+ * This function will return the requested extension data in the
+ * certificate. The extension data will be stored in the
+ * provided buffer.
+ *
+ * Use gnutls_x509_crt_get_extension_info() to extract the OID and
+ * critical flag. Use gnutls_x509_crt_get_extension_by_oid() instead,
+ * if you want to get data indexed by the extension OID rather than
+ * sequence.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned. If you have reached the
+ * last extension available %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * will be returned.
+ **/
+int
+gnutls_x509_crt_get_extension_data(gnutls_x509_crt_t cert, unsigned indx,
+ void *data, size_t * sizeof_data)
+{
+ int result, len;
+ char name[MAX_NAME_SIZE];
+
+ if (!cert) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ snprintf(name, sizeof(name),
+ "tbsCertificate.extensions.?%u.extnValue", indx + 1);
+
+ len = *sizeof_data;
+ result = asn1_read_value(cert->cert, name, data, &len);
+ *sizeof_data = len;
+
+ if (result == ASN1_ELEMENT_NOT_FOUND) {
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ } else if (result == ASN1_MEM_ERROR && data == NULL) {
+ /* normally we should return GNUTLS_E_SHORT_MEMORY_BUFFER,
+ * but we haven't done that for long time, so use
+ * backwards compatible behavior */
+ return 0;
+ } else if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_raw_issuer_dn:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: will hold the starting point of the DN
+ *
+ * This function will return a pointer to the DER encoded DN structure
+ * and the length. This points to allocated data that must be free'd using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.or a negative error code on error.
+ *
+ **/
+int
+gnutls_x509_crt_get_raw_issuer_dn(gnutls_x509_crt_t cert,
+ gnutls_datum_t * dn)
+{
+ if (cert->raw_issuer_dn.size > 0 && cert->modified == 0) {
+ return _gnutls_set_datum(dn, cert->raw_issuer_dn.data,
+ cert->raw_issuer_dn.size);
+ } else {
+ return _gnutls_x509_get_raw_field(cert->cert, "tbsCertificate.issuer.rdnSequence", dn);
+ }
+}
+
+/**
+ * gnutls_x509_crt_get_raw_dn:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: will hold the starting point of the DN
+ *
+ * This function will return a pointer to the DER encoded DN structure and
+ * the length. This points to allocated data that must be free'd using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value. or a negative error code on error.
+ *
+ **/
+int gnutls_x509_crt_get_raw_dn(gnutls_x509_crt_t cert, gnutls_datum_t * dn)
+{
+ if (cert->raw_dn.size > 0 && cert->modified == 0) {
+ return _gnutls_set_datum(dn, cert->raw_dn.data, cert->raw_dn.size);
+ } else {
+ return _gnutls_x509_get_raw_field(cert->cert, "tbsCertificate.subject.rdnSequence", dn);
+ }
+}
+
+static int
+get_dn(gnutls_x509_crt_t cert, const char *whom, gnutls_x509_dn_t * dn, unsigned subject)
+{
+ gnutls_x509_dn_st *store;
+
+ if (subject)
+ store = &cert->dn;
+ else
+ store = &cert->idn;
+
+ store->asn = asn1_find_node(cert->cert, whom);
+ if (!store->asn)
+ return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
+
+ *dn = store;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_subject:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: output variable with pointer to uint8_t DN.
+ *
+ * Return the Certificate's Subject DN as a %gnutls_x509_dn_t data type,
+ * that can be decoded using gnutls_x509_dn_get_rdn_ava().
+ *
+ * Note that @dn should be treated as constant. Because it points
+ * into the @cert object, you should not use @dn after @cert is
+ * deallocated.
+ *
+ * Returns: Returns 0 on success, or an error code.
+ **/
+int
+gnutls_x509_crt_get_subject(gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn)
+{
+ return get_dn(cert, "tbsCertificate.subject.rdnSequence", dn, 1);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @dn: output variable with pointer to uint8_t DN
+ *
+ * Return the Certificate's Issuer DN as a %gnutls_x509_dn_t data type,
+ * that can be decoded using gnutls_x509_dn_get_rdn_ava().
+ *
+ * Note that @dn should be treated as constant. Because it points
+ * into the @cert object, you should not use @dn after @cert is
+ * deallocated.
+ *
+ * Returns: Returns 0 on success, or an error code.
+ **/
+int
+gnutls_x509_crt_get_issuer(gnutls_x509_crt_t cert, gnutls_x509_dn_t * dn)
+{
+ return get_dn(cert, "tbsCertificate.issuer.rdnSequence", dn, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_fingerprint:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @algo: is a digest algorithm
+ * @buf: a pointer to a structure to hold the fingerprint (may be null)
+ * @buf_size: initially holds the size of @buf
+ *
+ * This function will calculate and copy the certificate's fingerprint
+ * in the provided buffer. The fingerprint is a hash of the DER-encoded
+ * data of the certificate.
+ *
+ * If the buffer is null then only the size will be filled.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the *buf_size will be updated
+ * with the required size. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_fingerprint(gnutls_x509_crt_t cert,
+ gnutls_digest_algorithm_t algo,
+ void *buf, size_t * buf_size)
+{
+ uint8_t *cert_buf;
+ int cert_buf_size;
+ int result;
+ gnutls_datum_t tmp;
+
+ if (buf_size == 0 || cert == NULL) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ cert_buf_size = 0;
+ result = asn1_der_coding(cert->cert, "", NULL, &cert_buf_size, NULL);
+ if (result != ASN1_MEM_ERROR) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ cert_buf = gnutls_malloc(cert_buf_size);
+ if (cert_buf == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result =
+ asn1_der_coding(cert->cert, "", cert_buf, &cert_buf_size,
+ NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(cert_buf);
+ return _gnutls_asn2err(result);
+ }
+
+ tmp.data = cert_buf;
+ tmp.size = cert_buf_size;
+
+ result = gnutls_fingerprint(algo, &tmp, buf, buf_size);
+ gnutls_free(cert_buf);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_export:
+ * @cert: Holds the certificate
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a certificate PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the certificate to DER or PEM format.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will
+ * be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN CERTIFICATE".
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int
+gnutls_x509_crt_export(gnutls_x509_crt_t cert,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ gnutls_datum_t out;
+ int ret;
+
+ ret = gnutls_x509_crt_export2(cert, format, &out);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (format == GNUTLS_X509_FMT_PEM)
+ ret = _gnutls_copy_string(&out, (uint8_t*)output_data, output_data_size);
+ else
+ ret = _gnutls_copy_data(&out, (uint8_t*)output_data, output_data_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(out.data);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_export2:
+ * @cert: Holds the certificate
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a certificate PEM or DER encoded
+ *
+ * This function will export the certificate to DER or PEM format.
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN CERTIFICATE".
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_x509_crt_export2(gnutls_x509_crt_t cert,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (!cert->modified && cert->der.size) {
+ if (format == GNUTLS_X509_FMT_DER)
+ return _gnutls_set_datum(out, cert->der.data, cert->der.size);
+ else {
+ int ret = _gnutls_fbase64_encode(PEM_X509_CERT2,
+ cert->der.data,
+ cert->der.size,
+ out);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+ }
+
+ return _gnutls_x509_export_int2(cert->cert, format, PEM_X509_CERT2,
+ out);
+}
+
+int
+_gnutls_get_key_id(gnutls_pk_params_st * params,
+ unsigned char *output_data, size_t * output_data_size,
+ unsigned flags)
+{
+ int ret = 0;
+ gnutls_datum_t der = { NULL, 0 };
+ gnutls_digest_algorithm_t hash = GNUTLS_DIG_SHA1;
+ unsigned int digest_len;
+
+ if ((flags & GNUTLS_KEYID_USE_SHA512) || (flags & GNUTLS_KEYID_USE_BEST_KNOWN))
+ hash = GNUTLS_DIG_SHA512;
+ else if (flags & GNUTLS_KEYID_USE_SHA256)
+ hash = GNUTLS_DIG_SHA256;
+
+ digest_len =
+ _gnutls_hash_get_algo_len(hash_to_entry(hash));
+
+ if (output_data == NULL || *output_data_size < digest_len) {
+ gnutls_assert();
+ *output_data_size = digest_len;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ ret = _gnutls_x509_encode_PKI_params(&der, params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_hash_fast(hash, der.data, der.size, output_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ *output_data_size = digest_len;
+
+ ret = 0;
+
+ cleanup:
+
+ _gnutls_free_datum(&der);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_key_id:
+ * @crt: Holds the certificate
+ * @flags: should be one of the flags from %gnutls_keyid_flags_t
+ * @output_data: will contain the key ID
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will return a unique ID that depends on the public
+ * key parameters. This ID can be used in checking whether a
+ * certificate corresponds to the given private key.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will
+ * be returned. The output will normally be a SHA-1 hash output,
+ * which is 20 bytes.
+ *
+ * Returns: In case of failure a negative error code will be
+ * returned, and 0 on success.
+ **/
+int
+gnutls_x509_crt_get_key_id(gnutls_x509_crt_t crt, unsigned int flags,
+ unsigned char *output_data,
+ size_t * output_data_size)
+{
+ int ret = 0;
+ gnutls_pk_params_st params;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* initializes params */
+ ret = _gnutls_x509_crt_get_mpis(crt, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_get_key_id(&params, output_data, output_data_size, flags);
+
+ gnutls_pk_params_release(&params);
+
+ return ret;
+}
+
+static int
+crl_issuer_matches(gnutls_x509_crl_t crl, gnutls_x509_crt_t cert)
+{
+ if (_gnutls_x509_compare_raw_dn
+ (&crl->raw_issuer_dn, &cert->raw_issuer_dn) != 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* This is exactly as gnutls_x509_crt_check_revocation() except that
+ * it calls func.
+ */
+int
+_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
+ const gnutls_x509_crl_t * crl_list,
+ int crl_list_length,
+ gnutls_verify_output_function func)
+{
+ uint8_t serial[128];
+ uint8_t cert_serial[128];
+ size_t serial_size, cert_serial_size;
+ int ret, j;
+ gnutls_x509_crl_iter_t iter = NULL;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ for (j = 0; j < crl_list_length; j++) { /* do for all the crls */
+
+ /* Step 1. check if issuer's DN match
+ */
+ ret = crl_issuer_matches(crl_list[j], cert);
+ if (ret == 0) {
+ /* issuers do not match so don't even
+ * bother checking.
+ */
+ gnutls_assert();
+ continue;
+ }
+
+ /* Step 2. Read the certificate's serial number
+ */
+ cert_serial_size = sizeof(cert_serial);
+ ret =
+ gnutls_x509_crt_get_serial(cert, cert_serial,
+ &cert_serial_size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* Step 3. cycle through the CRL serials and compare with
+ * certificate serial we have.
+ */
+
+ iter = NULL;
+ do {
+ serial_size = sizeof(serial);
+ ret =
+ gnutls_x509_crl_iter_crt_serial(crl_list[j],
+ &iter,
+ serial,
+ &serial_size,
+ NULL);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ break;
+ } else if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (serial_size == cert_serial_size) {
+ if (memcmp
+ (serial, cert_serial,
+ serial_size) == 0) {
+ /* serials match */
+ if (func)
+ func(cert, NULL,
+ crl_list[j],
+ GNUTLS_CERT_REVOKED |
+ GNUTLS_CERT_INVALID);
+ ret = 1; /* revoked! */
+ goto fail;
+ }
+ }
+ } while(1);
+
+ gnutls_x509_crl_iter_deinit(iter);
+ iter = NULL;
+
+ if (func)
+ func(cert, NULL, crl_list[j], 0);
+
+ }
+ return 0; /* not revoked. */
+
+ fail:
+ gnutls_x509_crl_iter_deinit(iter);
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crt_check_revocation:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @crl_list: should contain a list of gnutls_x509_crl_t types
+ * @crl_list_length: the length of the crl_list
+ *
+ * This function will check if the given certificate is
+ * revoked. It is assumed that the CRLs have been verified before.
+ *
+ * Returns: 0 if the certificate is NOT revoked, and 1 if it is. A
+ * negative error code is returned on error.
+ **/
+int
+gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
+ const gnutls_x509_crl_t * crl_list,
+ unsigned crl_list_length)
+{
+ return _gnutls_x509_crt_check_revocation(cert, crl_list,
+ crl_list_length, NULL);
+}
+
+/**
+ * gnutls_x509_crt_check_key_purpose:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @purpose: a key purpose OID (e.g., %GNUTLS_KP_CODE_SIGNING)
+ * @flags: zero or %GNUTLS_KP_FLAG_DISALLOW_ANY
+ *
+ * This function will check whether the given certificate matches
+ * the provided key purpose. If @flags contains %GNUTLS_KP_FLAG_ALLOW_ANY then
+ * it a certificate marked for any purpose will not match.
+ *
+ * Returns: zero if the key purpose doesn't match, and non-zero otherwise.
+ *
+ * Since: 3.5.6
+ **/
+unsigned
+gnutls_x509_crt_check_key_purpose(gnutls_x509_crt_t cert,
+ const char *purpose,
+ unsigned flags)
+{
+ return _gnutls_check_key_purpose(cert, purpose, (flags&GNUTLS_KP_FLAG_DISALLOW_ANY)?1:0);
+}
+
+/**
+ * gnutls_x509_crt_get_preferred_hash_algorithm:
+ * @crt: Holds the certificate
+ * @hash: The result of the call with the hash algorithm used for signature
+ * @mand: If non-zero it means that the algorithm MUST use this hash. May be %NULL.
+ *
+ * This function will read the certificate and return the appropriate digest
+ * algorithm to use for signing with this certificate. Some certificates (i.e.
+ * DSA might not be able to sign without the preferred algorithm).
+ *
+ * Deprecated: Please use gnutls_pubkey_get_preferred_hash_algorithm().
+ *
+ * Returns: the 0 if the hash algorithm is found. A negative error code is
+ * returned on error.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_x509_crt_get_preferred_hash_algorithm(gnutls_x509_crt_t crt,
+ gnutls_digest_algorithm_t *
+ hash, unsigned int *mand)
+{
+ int ret;
+ gnutls_pubkey_t pubkey;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_get_preferred_hash_algorithm(pubkey, hash, mand);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_crl_dist_points:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.)
+ * @san: is the place where the distribution point will be copied to
+ * @san_size: holds the size of ret.
+ * @reason_flags: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
+ * @critical: will be non-zero if the extension is marked as critical (may be null)
+ *
+ * This function retrieves the CRL distribution points (2.5.29.31),
+ * contained in the given certificate in the X509v3 Certificate
+ * Extensions.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER and updates @ret_size if
+ * @ret_size is not enough to hold the distribution point, or the
+ * type of the distribution point if everything was ok. The type is
+ * one of the enumerated %gnutls_x509_subject_alt_name_t. If the
+ * certificate does not have an Alternative name with the specified
+ * sequence number then %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is
+ * returned.
+ **/
+int
+gnutls_x509_crt_get_crl_dist_points(gnutls_x509_crt_t cert,
+ unsigned int seq, void *san,
+ size_t * san_size,
+ unsigned int *reason_flags,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t dist_points = { NULL, 0 };
+ unsigned type;
+ gnutls_x509_crl_dist_points_t cdp = NULL;
+ gnutls_datum_t t_san;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_crl_dist_points_init(&cdp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (reason_flags)
+ *reason_flags = 0;
+
+ ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.31", 0,
+ &dist_points, critical);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (dist_points.size == 0 || dist_points.data == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_import_crl_dist_points(&dist_points, cdp, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crl_dist_points_get(cdp, seq, &type, &t_san, reason_flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_copy_string(&t_san, san, san_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = type;
+
+ cleanup:
+ _gnutls_free_datum(&dist_points);
+ if (cdp != NULL)
+ gnutls_x509_crl_dist_points_deinit(cdp);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_key_purpose_oid:
+ * @cert: should contain a #gnutls_x509_crt_t type
+ * @indx: This specifies which OID to return. Use (0) to get the first one.
+ * @oid: a pointer to a buffer to hold the OID (may be null)
+ * @oid_size: initially holds the size of @oid
+ * @critical: output flag to indicate criticality of extension
+ *
+ * This function will extract the key purpose OIDs of the Certificate
+ * specified by the given index. These are stored in the Extended Key
+ * Usage extension (2.5.29.37) See the GNUTLS_KP_* definitions for
+ * human readable names.
+ *
+ * If @oid is null then only the size will be filled. The @oid
+ * returned will be null terminated, although @oid_size will not
+ * account for the trailing null.
+ *
+ * Returns: %GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is
+ * not long enough, and in that case the *oid_size will be updated
+ * with the required size. On success 0 is returned.
+ **/
+int
+gnutls_x509_crt_get_key_purpose_oid(gnutls_x509_crt_t cert,
+ unsigned indx, void *oid, size_t * oid_size,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t ext;
+ gnutls_x509_key_purposes_t p = NULL;
+ gnutls_datum_t out;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (oid)
+ memset(oid, 0, *oid_size);
+ else
+ *oid_size = 0;
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.37", 0, &ext,
+ critical)) < 0) {
+ return ret;
+ }
+
+ if (ext.size == 0 || ext.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = gnutls_x509_key_purpose_init(&p);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_import_key_purposes(&ext, p, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_key_purpose_get(p, indx, &out);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_copy_string(&out, oid, oid_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(ext.data);
+ if (p!=NULL)
+ gnutls_x509_key_purpose_deinit(p);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_pk_rsa_raw:
+ * @crt: Holds the certificate
+ * @m: will hold the modulus
+ * @e: will hold the public exponent
+ *
+ * This function will export the RSA public key's parameters found in
+ * the given structure. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ **/
+int
+gnutls_x509_crt_get_pk_rsa_raw(gnutls_x509_crt_t crt,
+ gnutls_datum_t * m, gnutls_datum_t * e)
+{
+ int ret;
+ gnutls_pubkey_t pubkey;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export_rsa_raw(pubkey, m, e);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_pk_ecc_raw:
+ * @crt: Holds the certificate
+ * @curve: will hold the curve
+ * @x: will hold the x-coordinate
+ * @y: will hold the y-coordinate
+ *
+ * This function will export the ECC public key's parameters found in
+ * the given certificate. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * In EdDSA curves the @y parameter will be %NULL and the other parameters
+ * will be in the native format for the curve.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.4.1
+ **/
+int
+gnutls_x509_crt_get_pk_ecc_raw(gnutls_x509_crt_t crt,
+ gnutls_ecc_curve_t *curve,
+ gnutls_datum_t *x, gnutls_datum_t *y)
+{
+ int ret;
+ gnutls_pubkey_t pubkey;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export_ecc_raw(pubkey, curve, x, y);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_pk_gost_raw:
+ * @crt: Holds the certificate
+ * @curve: will hold the curve
+ * @digest: will hold the digest
+ * @paramset: will hold the GOST parameter set ID
+ * @x: will hold the x-coordinate
+ * @y: will hold the y-coordinate
+ *
+ * This function will export the GOST public key's parameters found in
+ * the given certificate. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_x509_crt_get_pk_gost_raw(gnutls_x509_crt_t crt,
+ gnutls_ecc_curve_t *curve,
+ gnutls_digest_algorithm_t *digest,
+ gnutls_gost_paramset_t *paramset,
+ gnutls_datum_t *x, gnutls_datum_t *y)
+{
+ int ret;
+ gnutls_pubkey_t pubkey;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export_gost_raw2(pubkey, curve, digest,
+ paramset, x, y, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_pk_dsa_raw:
+ * @crt: Holds the certificate
+ * @p: will hold the p
+ * @q: will hold the q
+ * @g: will hold the g
+ * @y: will hold the y
+ *
+ * This function will export the DSA public key's parameters found in
+ * the given certificate. The new parameters will be allocated using
+ * gnutls_malloc() and will be stored in the appropriate datum.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ **/
+int
+gnutls_x509_crt_get_pk_dsa_raw(gnutls_x509_crt_t crt,
+ gnutls_datum_t * p, gnutls_datum_t * q,
+ gnutls_datum_t * g, gnutls_datum_t * y)
+{
+ int ret;
+ gnutls_pubkey_t pubkey;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_pubkey_export_dsa_raw(pubkey, p, q, g, y);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ gnutls_pubkey_deinit(pubkey);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_list_import2:
+ * @certs: Will hold the parsed certificate list.
+ * @size: It will contain the size of the list.
+ * @data: The PEM encoded certificate.
+ * @format: One of DER or PEM.
+ * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
+ *
+ * This function will convert the given PEM encoded certificate list
+ * to the native gnutls_x509_crt_t format. The output will be stored
+ * in @certs which will be allocated and initialized.
+ *
+ * If the Certificate is PEM encoded it should have a header of "X509
+ * CERTIFICATE", or "CERTIFICATE".
+ *
+ * To deinitialize @certs, you need to deinitialize each crt structure
+ * independently, and use gnutls_free() at @certs.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crt_list_import2(gnutls_x509_crt_t ** certs,
+ unsigned int *size,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int flags)
+{
+ unsigned int init = 1024;
+ int ret;
+
+ *certs = _gnutls_reallocarray(NULL, init, sizeof(gnutls_x509_crt_t));
+ if (*certs == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret =
+ gnutls_x509_crt_list_import(*certs, &init, data, format,
+ flags | GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ *certs = _gnutls_reallocarray_fast(*certs, init,
+ sizeof(gnutls_x509_crt_t));
+ if (*certs == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret =
+ gnutls_x509_crt_list_import(*certs, &init, data,
+ format, flags);
+ }
+
+ if (ret < 0) {
+ gnutls_free(*certs);
+ return ret;
+ }
+
+ *size = init;
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_list_import:
+ * @certs: Indicates where the parsed list will be copied to. Must not be initialized.
+ * @cert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available.
+ * @data: The PEM encoded certificate.
+ * @format: One of DER or PEM.
+ * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
+ *
+ * This function will convert the given PEM encoded certificate list
+ * to the native gnutls_x509_crt_t format. The output will be stored
+ * in @certs. They will be automatically initialized.
+ *
+ * The flag %GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED will cause
+ * import to fail if the certificates in the provided buffer are more
+ * than the available structures. The %GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED
+ * flag will cause the function to fail if the provided list is not
+ * sorted from subject to issuer.
+ *
+ * If the Certificate is PEM encoded it should have a header of "X509
+ * CERTIFICATE", or "CERTIFICATE".
+ *
+ * Returns: the number of certificates read or a negative error value.
+ **/
+int
+gnutls_x509_crt_list_import(gnutls_x509_crt_t * certs,
+ unsigned int *cert_max,
+ const gnutls_datum_t * data,
+ gnutls_x509_crt_fmt_t format,
+ unsigned int flags)
+{
+ int size;
+ const char *ptr;
+ gnutls_datum_t tmp;
+ int ret, nocopy = 0;
+ unsigned int count = 0, j, copied = 0;
+
+ if (format == GNUTLS_X509_FMT_DER) {
+ if (*cert_max < 1) {
+ *cert_max = 1;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ count = 1; /* import only the first one */
+
+ ret = gnutls_x509_crt_init(&certs[0]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = gnutls_x509_crt_import(certs[0], data, format);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ *cert_max = 1;
+ return 1;
+ }
+
+ /* move to the certificate
+ */
+ ptr = memmem(data->data, data->size,
+ PEM_CERT_SEP, sizeof(PEM_CERT_SEP) - 1);
+ if (ptr == NULL)
+ ptr = memmem(data->data, data->size,
+ PEM_CERT_SEP2, sizeof(PEM_CERT_SEP2) - 1);
+
+ if (ptr == NULL)
+ return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+
+ count = 0;
+
+ do {
+ if (count >= *cert_max) {
+ if (!
+ (flags &
+ GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED))
+ break;
+ else
+ nocopy = 1;
+ }
+
+ if (!nocopy) {
+ ret = gnutls_x509_crt_init(&certs[count]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ tmp.data = (void *) ptr;
+ tmp.size =
+ data->size - (ptr - (char *) data->data);
+
+ ret =
+ gnutls_x509_crt_import(certs[count], &tmp,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ count++;
+ gnutls_assert();
+ goto error;
+ }
+
+ copied++;
+ }
+
+ /* now we move ptr after the pem header
+ */
+ ptr++;
+ /* find the next certificate (if any)
+ */
+ size = data->size - (ptr - (char *) data->data);
+
+ if (size > 0) {
+ char *ptr2;
+
+ ptr2 =
+ memmem(ptr, size, PEM_CERT_SEP,
+ sizeof(PEM_CERT_SEP) - 1);
+ if (ptr2 == NULL)
+ ptr2 = memmem(ptr, size, PEM_CERT_SEP2,
+ sizeof(PEM_CERT_SEP2) - 1);
+
+ ptr = ptr2;
+ } else
+ ptr = NULL;
+
+ count++;
+ }
+ while (ptr != NULL);
+
+ *cert_max = count;
+
+ if (nocopy == 0) {
+ if (flags & GNUTLS_X509_CRT_LIST_SORT && *cert_max > 1) {
+ if (*cert_max > DEFAULT_MAX_VERIFY_DEPTH) {
+ ret = GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ goto error;
+ }
+ count = _gnutls_sort_clist(certs, *cert_max);
+ if (count < *cert_max) {
+ for (j = count; j < *cert_max; j++) {
+ gnutls_x509_crt_deinit(certs[j]);
+ }
+ }
+ *cert_max = count;
+ }
+
+ if (flags & GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED) {
+ ret = _gnutls_check_if_sorted(certs, *cert_max);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ return count;
+ } else {
+ count = copied;
+ ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ }
+
+ error:
+ for (j = 0; j < count; j++)
+ gnutls_x509_crt_deinit(certs[j]);
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_get_subject_unique_id:
+ * @crt: Holds the certificate
+ * @buf: user allocated memory buffer, will hold the unique id
+ * @buf_size: size of user allocated memory buffer (on input), will hold
+ * actual size of the unique ID on return.
+ *
+ * This function will extract the subjectUniqueID value (if present) for
+ * the given certificate.
+ *
+ * If the user allocated memory buffer is not large enough to hold the
+ * full subjectUniqueID, then a GNUTLS_E_SHORT_MEMORY_BUFFER error will be
+ * returned, and buf_size will be set to the actual length.
+ *
+ * This function had a bug prior to 3.4.8 that prevented the setting
+ * of %NULL @buf to discover the @buf_size. To use this function safely
+ * with the older versions the @buf must be a valid buffer that can hold
+ * at least a single byte if @buf_size is zero.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ **/
+int
+gnutls_x509_crt_get_subject_unique_id(gnutls_x509_crt_t crt, char *buf,
+ size_t * buf_size)
+{
+ int result;
+ gnutls_datum_t datum = { NULL, 0 };
+
+ result =
+ _gnutls_x509_read_value(crt->cert,
+ "tbsCertificate.subjectUniqueID",
+ &datum);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ if (datum.size > *buf_size) { /* then we're not going to fit */
+ *buf_size = datum.size;
+ result = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ } else {
+ *buf_size = datum.size;
+ memcpy(buf, datum.data, datum.size);
+ }
+
+ _gnutls_free_datum(&datum);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_unique_id:
+ * @crt: Holds the certificate
+ * @buf: user allocated memory buffer, will hold the unique id
+ * @buf_size: size of user allocated memory buffer (on input), will hold
+ * actual size of the unique ID on return.
+ *
+ * This function will extract the issuerUniqueID value (if present) for
+ * the given certificate.
+ *
+ * If the user allocated memory buffer is not large enough to hold the
+ * full subjectUniqueID, then a GNUTLS_E_SHORT_MEMORY_BUFFER error will be
+ * returned, and buf_size will be set to the actual length.
+ *
+ * This function had a bug prior to 3.4.8 that prevented the setting
+ * of %NULL @buf to discover the @buf_size. To use this function safely
+ * with the older versions the @buf must be a valid buffer that can hold
+ * at least a single byte if @buf_size is zero.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_x509_crt_get_issuer_unique_id(gnutls_x509_crt_t crt, char *buf,
+ size_t * buf_size)
+{
+ int result;
+ gnutls_datum_t datum = { NULL, 0 };
+
+ result =
+ _gnutls_x509_read_value(crt->cert,
+ "tbsCertificate.issuerUniqueID",
+ &datum);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ if (datum.size > *buf_size) { /* then we're not going to fit */
+ *buf_size = datum.size;
+ result = GNUTLS_E_SHORT_MEMORY_BUFFER;
+ } else {
+ *buf_size = datum.size;
+ memcpy(buf, datum.data, datum.size);
+ }
+
+ _gnutls_free_datum(&datum);
+
+ return result;
+}
+
+static int
+legacy_parse_aia(asn1_node src,
+ unsigned int seq, int what, gnutls_datum_t * data)
+{
+ int len;
+ char nptr[MAX_NAME_SIZE];
+ int result;
+ gnutls_datum_t d;
+ const char *oid = NULL;
+
+ seq++; /* 0->1, 1->2 etc */
+ switch (what) {
+ case GNUTLS_IA_ACCESSMETHOD_OID:
+ snprintf(nptr, sizeof(nptr), "?%u.accessMethod", seq);
+ break;
+
+ case GNUTLS_IA_ACCESSLOCATION_GENERALNAME_TYPE:
+ snprintf(nptr, sizeof(nptr), "?%u.accessLocation", seq);
+ break;
+
+ case GNUTLS_IA_CAISSUERS_URI:
+ oid = GNUTLS_OID_AD_CAISSUERS;
+ FALLTHROUGH;
+
+ case GNUTLS_IA_OCSP_URI:
+ if (oid == NULL)
+ oid = GNUTLS_OID_AD_OCSP;
+ {
+ char tmpoid[MAX_OID_SIZE];
+ snprintf(nptr, sizeof(nptr), "?%u.accessMethod",
+ seq);
+ len = sizeof(tmpoid);
+ result = asn1_read_value(src, nptr, tmpoid, &len);
+
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+ if ((unsigned) len != strlen(oid) + 1
+ || memcmp(tmpoid, oid, len) != 0)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_UNKNOWN_ALGORITHM);
+ }
+ FALLTHROUGH;
+
+ case GNUTLS_IA_URI:
+ snprintf(nptr, sizeof(nptr),
+ "?%u.accessLocation.uniformResourceIdentifier",
+ seq);
+ break;
+
+ default:
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ len = 0;
+ result = asn1_read_value(src, nptr, NULL, &len);
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (result != ASN1_MEM_ERROR) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ d.size = len;
+
+ d.data = gnutls_malloc(d.size);
+ if (d.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ result = asn1_read_value(src, nptr, d.data, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(d.data);
+ return _gnutls_asn2err(result);
+ }
+
+ if (data) {
+ data->data = d.data;
+ data->size = d.size;
+ } else
+ gnutls_free(d.data);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_get_authority_info_access:
+ * @crt: Holds the certificate
+ * @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.)
+ * @what: what data to get, a #gnutls_info_access_what_t type.
+ * @data: output data to be freed with gnutls_free().
+ * @critical: pointer to output integer that is set to non-zero if the extension is marked as critical (may be %NULL)
+ *
+ * Note that a simpler API to access the authority info data is provided
+ * by gnutls_x509_aia_get() and gnutls_x509_ext_import_aia().
+ *
+ * This function extracts the Authority Information Access (AIA)
+ * extension, see RFC 5280 section 4.2.2.1 for more information. The
+ * AIA extension holds a sequence of AccessDescription (AD) data.
+ *
+ * The @seq input parameter is used to indicate which member of the
+ * sequence the caller is interested in. The first member is 0, the
+ * second member 1 and so on. When the @seq value is out of bounds,
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * The type of data returned in @data is specified via @what which
+ * should be #gnutls_info_access_what_t values.
+ *
+ * If @what is %GNUTLS_IA_ACCESSMETHOD_OID then @data will hold the
+ * accessMethod OID (e.g., "1.3.6.1.5.5.7.48.1").
+ *
+ * If @what is %GNUTLS_IA_ACCESSLOCATION_GENERALNAME_TYPE, @data will
+ * hold the accessLocation GeneralName type (e.g.,
+ * "uniformResourceIdentifier").
+ *
+ * If @what is %GNUTLS_IA_URI, @data will hold the accessLocation URI
+ * data. Requesting this @what value leads to an error if the
+ * accessLocation is not of the "uniformResourceIdentifier" type.
+ *
+ * If @what is %GNUTLS_IA_OCSP_URI, @data will hold the OCSP URI.
+ * Requesting this @what value leads to an error if the accessMethod
+ * is not 1.3.6.1.5.5.7.48.1 aka OCSP, or if accessLocation is not of
+ * the "uniformResourceIdentifier" type. In that case %GNUTLS_E_UNKNOWN_ALGORITHM
+ * will be returned, and @seq should be increased and this function
+ * called again.
+ *
+ * If @what is %GNUTLS_IA_CAISSUERS_URI, @data will hold the caIssuers
+ * URI. Requesting this @what value leads to an error if the
+ * accessMethod is not 1.3.6.1.5.5.7.48.2 aka caIssuers, or if
+ * accessLocation is not of the "uniformResourceIdentifier" type.
+ * In that case handle as in %GNUTLS_IA_OCSP_URI.
+ *
+ * More @what values may be allocated in the future as needed.
+ *
+ * If @data is NULL, the function does the same without storing the
+ * output data, that is, it will set @critical and do error checking
+ * as usual.
+ *
+ * The value of the critical flag is returned in *@critical. Supply a
+ * NULL @critical if you want the function to make sure the extension
+ * is non-critical, as required by RFC 5280.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, %GNUTLS_E_INVALID_REQUEST on
+ * invalid @crt, %GNUTLS_E_CONSTRAINT_ERROR if the extension is
+ * incorrectly marked as critical (use a non-NULL @critical to
+ * override), %GNUTLS_E_UNKNOWN_ALGORITHM if the requested OID does
+ * not match (e.g., when using %GNUTLS_IA_OCSP_URI), otherwise a
+ * negative error code.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crt_get_authority_info_access(gnutls_x509_crt_t crt,
+ unsigned int seq,
+ int what,
+ gnutls_datum_t * data,
+ unsigned int *critical)
+{
+ int ret;
+ gnutls_datum_t aia;
+ asn1_node c2 = NULL;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if ((ret =
+ _gnutls_x509_crt_get_extension(crt, GNUTLS_OID_AIA, 0, &aia,
+ critical)) < 0)
+ return ret;
+
+ if (aia.size == 0 || aia.data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ if (critical && *critical)
+ return GNUTLS_E_CONSTRAINT_ERROR;
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.AuthorityInfoAccessSyntax", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ _gnutls_free_datum(&aia);
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _asn1_strict_der_decode(&c2, aia.data, aia.size, NULL);
+ /* asn1_print_structure (stdout, c2, "", ASN1_PRINT_ALL); */
+ _gnutls_free_datum(&aia);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = legacy_parse_aia(c2, seq, what, data);
+
+ asn1_delete_structure(&c2);
+ if (ret < 0)
+ gnutls_assert();
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_set_pin_function:
+ * @crt: The certificate structure
+ * @fn: the callback
+ * @userdata: data associated with the callback
+ *
+ * This function will set a callback function to be used when
+ * it is required to access a protected object. This function overrides
+ * the global function set using gnutls_pkcs11_set_pin_function().
+ *
+ * Note that this callback is currently used only during the import
+ * of a PKCS #11 certificate with gnutls_x509_crt_import_url().
+ *
+ * Since: 3.1.0
+ *
+ **/
+void gnutls_x509_crt_set_pin_function(gnutls_x509_crt_t crt,
+ gnutls_pin_callback_t fn,
+ void *userdata)
+{
+ if (crt) {
+ crt->pin.cb = fn;
+ crt->pin.data = userdata;
+ }
+}
+
+/**
+ * gnutls_x509_crt_import_url:
+ * @crt: A certificate of type #gnutls_x509_crt_t
+ * @url: A PKCS 11 url
+ * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise
+ *
+ * This function will import a certificate present in a PKCS#11 token
+ * or any type of back-end that supports URLs.
+ *
+ * In previous versions of gnutls this function was named
+ * gnutls_x509_crt_import_pkcs11_url, and the old name is
+ * an alias to this one.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_x509_crt_import_url(gnutls_x509_crt_t crt,
+ const char *url, unsigned int flags)
+{
+ int ret;
+ unsigned i;
+
+ for (i=0;i<_gnutls_custom_urls_size;i++) {
+ if (strncmp(url, _gnutls_custom_urls[i].name, _gnutls_custom_urls[i].name_size) == 0) {
+ if (_gnutls_custom_urls[i].import_crt) {
+ ret = _gnutls_custom_urls[i].import_crt(crt, url, flags);
+ goto cleanup;
+ }
+ break;
+ }
+ }
+
+ if (strncmp(url, SYSTEM_URL, SYSTEM_URL_SIZE) == 0) {
+ ret = _gnutls_x509_crt_import_system_url(crt, url);
+#ifdef ENABLE_PKCS11
+ } else if (strncmp(url, PKCS11_URL, PKCS11_URL_SIZE) == 0) {
+ ret = _gnutls_x509_crt_import_pkcs11_url(crt, url, flags);
+#endif
+ } else {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ cleanup:
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_list_import_url:
+ * @certs: Will hold the allocated certificate list.
+ * @size: It will contain the size of the list.
+ * @url: A PKCS 11 url
+ * @pin_fn: a PIN callback if not globally set
+ * @pin_fn_userdata: parameter for the PIN callback
+ * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise
+ *
+ * This function will import a certificate chain present in a PKCS#11 token
+ * or any type of back-end that supports URLs. The certificates
+ * must be deinitialized afterwards using gnutls_x509_crt_deinit()
+ * and the returned pointer must be freed using gnutls_free().
+ *
+ * The URI provided must be the first certificate in the chain; subsequent
+ * certificates will be retrieved using gnutls_pkcs11_get_raw_issuer() or
+ * equivalent functionality for the supported URI.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_x509_crt_list_import_url(gnutls_x509_crt_t **certs,
+ unsigned int *size,
+ const char *url,
+ gnutls_pin_callback_t pin_fn,
+ void *pin_fn_userdata,
+ unsigned int flags)
+{
+ int ret;
+ unsigned i;
+ gnutls_x509_crt_t crts[DEFAULT_MAX_VERIFY_DEPTH];
+ gnutls_datum_t issuer = {NULL, 0};
+ unsigned total = 0;
+
+ memset(crts, 0, sizeof(crts));
+
+ ret = gnutls_x509_crt_init(&crts[0]);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ gnutls_x509_crt_set_pin_function(crts[0], pin_fn, pin_fn_userdata);
+
+ total = 1;
+
+ ret = gnutls_x509_crt_import_url(crts[0], url, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ for (i=1;i<DEFAULT_MAX_VERIFY_DEPTH;i++) {
+ ret = _gnutls_get_raw_issuer(url, crts[i-1], &issuer, flags|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY);
+ if (ret < 0) {
+ issuer.data = NULL;
+ break;
+ }
+
+ if (gnutls_x509_crt_equals2(crts[i-1], &issuer)) {
+ gnutls_free(issuer.data);
+ break;
+ }
+
+ ret = gnutls_x509_crt_init(&crts[i]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ total++;
+
+ gnutls_x509_crt_set_pin_function(crts[i], pin_fn, pin_fn_userdata);
+
+ ret = gnutls_x509_crt_import(crts[i], &issuer, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ gnutls_free(issuer.data);
+ }
+
+ *certs = _gnutls_reallocarray(NULL, total, sizeof(gnutls_x509_crt_t));
+ if (*certs == NULL) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ memcpy(*certs, crts, total*sizeof(gnutls_x509_crt_t));
+ *size = total;
+
+ return 0;
+ cleanup:
+ gnutls_free(issuer.data);
+ for (i=0;i<total;i++)
+ gnutls_x509_crt_deinit(crts[i]);
+
+ return ret;
+}
+
+/*-
+ * gnutls_x509_crt_verify_data3:
+ * @crt: Holds the certificate to verify with
+ * @algo: The signature algorithm used
+ * @flags: Zero or an OR list of #gnutls_certificate_verify_flags
+ * @data: holds the signed data
+ * @signature: contains the signature
+ *
+ * This function will verify the given signed data, using the
+ * parameters from the certificate.
+ *
+ * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED
+ * is returned, %GNUTLS_E_EXPIRED or %GNUTLS_E_NOT_YET_ACTIVATED on expired
+ * or not yet activated certificate and zero or positive code on success.
+ *
+ * Since: 3.5.6
+ -*/
+int
+gnutls_x509_crt_verify_data3(gnutls_x509_crt_t crt,
+ gnutls_sign_algorithm_t algo,
+ gnutls_typed_vdata_st *vdata,
+ unsigned int vdata_size,
+ const gnutls_datum_t *data,
+ const gnutls_datum_t *signature,
+ unsigned int flags)
+{
+ int ret;
+ gnutls_pubkey_t pubkey;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_pubkey_verify_data2(pubkey, algo, flags, data, signature);
+ gnutls_pubkey_deinit(pubkey);
+
+ if (ret >= 0) {
+ time_t now = gnutls_time(0);
+ int res;
+ unsigned usage, i;
+
+ if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS) ||
+ !(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
+ if (now > gnutls_x509_crt_get_expiration_time(crt)) {
+ return gnutls_assert_val(GNUTLS_E_EXPIRED);
+ }
+
+ if (now < gnutls_x509_crt_get_activation_time(crt)) {
+ return gnutls_assert_val(GNUTLS_E_NOT_YET_ACTIVATED);
+ }
+ }
+
+ res = gnutls_x509_crt_get_key_usage(crt, &usage, NULL);
+ if (res >= 0) {
+ if (!(usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
+ return gnutls_assert_val(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ }
+ }
+
+ for (i=0;i<vdata_size;i++) {
+ if (vdata[i].type == GNUTLS_DT_KEY_PURPOSE_OID) {
+ res = _gnutls_check_key_purpose(crt, (char *)vdata[i].data, 0);
+ if (res == 0)
+ return gnutls_assert_val(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE);
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_verify_data2:
+ * @crt: Holds the certificate to verify with
+ * @algo: The signature algorithm used
+ * @flags: Zero or an OR list of #gnutls_certificate_verify_flags
+ * @data: holds the signed data
+ * @signature: contains the signature
+ *
+ * This function will verify the given signed data, using the
+ * parameters from the certificate.
+ *
+ * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED
+ * is returned, %GNUTLS_E_EXPIRED or %GNUTLS_E_NOT_YET_ACTIVATED on expired
+ * or not yet activated certificate and zero or positive code on success.
+ *
+ * Note that since GnuTLS 3.5.6 this function introduces checks in the
+ * end certificate (@crt), including time checks and key usage checks.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_x509_crt_verify_data2(gnutls_x509_crt_t crt,
+ gnutls_sign_algorithm_t algo,
+ unsigned int flags,
+ const gnutls_datum_t *data,
+ const gnutls_datum_t *signature)
+{
+ return gnutls_x509_crt_verify_data3(crt, algo, NULL, 0,
+ data, signature, flags);
+}
+
+/**
+ * gnutls_x509_crt_set_flags:
+ * @cert: A type #gnutls_x509_crt_t
+ * @flags: flags from the %gnutls_x509_crt_flags
+ *
+ * This function will set flags for the specified certificate.
+ * Currently this is useful for the %GNUTLS_X509_CRT_FLAG_IGNORE_SANITY
+ * which allows importing certificates even if they have known issues.
+ *
+ * Since: 3.6.0
+ *
+ **/
+void gnutls_x509_crt_set_flags(gnutls_x509_crt_t cert,
+ unsigned int flags)
+{
+ cert->flags = flags;
+}
+
diff --git a/lib/x509/x509_dn.c b/lib/x509/x509_dn.c
new file mode 100644
index 0000000..1dde410
--- /dev/null
+++ b/lib/x509/x509_dn.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2013-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains functions to handle X.509 certificate generation.
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include <c-ctype.h>
+
+typedef int (*set_dn_func) (void *, const char *oid, unsigned int raw_flag,
+ const void *name, unsigned int name_size);
+
+static
+int dn_attr_crt_set(set_dn_func f, void *crt, const gnutls_datum_t * name,
+ const gnutls_datum_t * val, unsigned is_raw)
+{
+ char _oid[MAX_OID_SIZE];
+ gnutls_datum_t tmp;
+ const char *oid;
+ int ret;
+ unsigned i,j;
+
+ if (name->size == 0 || val->size == 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ if (c_isdigit(name->data[0]) != 0) {
+ if (name->size >= sizeof(_oid))
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ memcpy(_oid, name->data, name->size);
+ _oid[name->size] = 0;
+
+ oid = _oid;
+
+ if (gnutls_x509_dn_oid_known(oid) == 0 && !is_raw) {
+ _gnutls_debug_log("Unknown OID: '%s'\n", oid);
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ }
+ } else {
+ oid =
+ _gnutls_ldap_string_to_oid((char *) name->data,
+ name->size);
+ }
+
+ if (oid == NULL) {
+ _gnutls_debug_log("Unknown DN attribute: '%.*s'\n",
+ (int) name->size, name->data);
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ }
+
+ if (is_raw) {
+ gnutls_datum_t hex = {val->data+1, val->size-1};
+
+ ret = gnutls_hex_decode2(&hex, &tmp);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ } else {
+ tmp.size = val->size;
+ tmp.data = gnutls_malloc(tmp.size+1);
+ if (tmp.data == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ /* unescape */
+ for (j=i=0;i<tmp.size;i++) {
+ if (1+j!=val->size && val->data[j] == '\\') {
+ if (val->data[j+1] == ',' || val->data[j+1] == '#' ||
+ val->data[j+1] == ' ' || val->data[j+1] == '+' ||
+ val->data[j+1] == '"' || val->data[j+1] == '<' ||
+ val->data[j+1] == '>' || val->data[j+1] == ';' ||
+ val->data[j+1] == '\\' || val->data[j+1] == '=') {
+ tmp.data[i] = val->data[j+1];
+ j+=2;
+ tmp.size--;
+ } else {
+ ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ goto fail;
+ }
+ } else {
+ tmp.data[i] = val->data[j++];
+ }
+ }
+ tmp.data[tmp.size] = 0;
+ }
+
+ ret = f(crt, oid, is_raw, tmp.data, tmp.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = 0;
+ fail:
+ gnutls_free(tmp.data);
+ return ret;
+}
+
+static int read_attr_and_val(const char **ptr,
+ gnutls_datum_t *name, gnutls_datum_t *val,
+ unsigned *is_raw)
+{
+ const unsigned char *p = (void *) *ptr;
+
+ *is_raw = 0;
+
+ /* skip any space */
+ while (c_isspace(*p))
+ p++;
+
+ /* Read the name */
+ name->data = (void *) p;
+ while (*p != '=' && *p != 0 && !c_isspace(*p))
+ p++;
+
+ name->size = p - name->data;
+
+ /* skip any space */
+ while (c_isspace(*p))
+ p++;
+
+ if (*p != '=')
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ p++;
+
+ while (c_isspace(*p))
+ p++;
+
+ if (*p == '#') {
+ *is_raw = 1;
+ }
+
+ /* Read value */
+ val->data = (void *) p;
+ while (*p != 0 && (*p != ',' || (*p == ',' && *(p - 1) == '\\'))
+ && *p != '\n') {
+ p++;
+ }
+ val->size = p - (val->data);
+ *ptr = (void*)p;
+
+ p = val->data;
+ /* check for unescaped '+' - we do not support them */
+ while (*p != 0) {
+ if (*p == '+' && (*(p - 1) != '\\'))
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ p++;
+ }
+
+ /* remove spaces from the end */
+ while(val->size > 0 && c_isspace(val->data[val->size-1])) {
+ if (val->size > 2 && val->data[val->size-2] == '\\')
+ break;
+ val->size--;
+ }
+
+ if (val->size == 0 || name->size == 0)
+ return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+ return 0;
+}
+
+typedef struct elem_list_st {
+ gnutls_datum_t name;
+ gnutls_datum_t val;
+ const char *pos;
+ unsigned is_raw;
+ struct elem_list_st *next;
+} elem_list_st;
+
+static int add_new_elem(elem_list_st **head, const gnutls_datum_t *name, const gnutls_datum_t *val, const char *pos, unsigned is_raw)
+{
+ elem_list_st *elem = gnutls_malloc(sizeof(*elem));
+ if (elem == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(&elem->name, name, sizeof(*name));
+ memcpy(&elem->val, val, sizeof(*val));
+ elem->pos = pos;
+ elem->is_raw = is_raw;
+ elem->next = *head;
+ *head = elem;
+
+ return 0;
+}
+
+static int
+crt_set_dn(set_dn_func f, void *crt, const char *dn, const char **err)
+{
+ const char *p = dn;
+ int ret;
+ gnutls_datum_t name, val;
+ unsigned is_raw;
+ elem_list_st *list = NULL, *plist, *next;
+
+ if (crt == NULL || dn == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* We parse the string and set all elements to a linked list in
+ * reverse order. That way we can encode in reverse order,
+ * the way RFC4514 requires. */
+
+ /* For each element */
+ while (*p != 0 && *p != '\n') {
+ if (err)
+ *err = p;
+
+ is_raw = 0;
+ ret = read_attr_and_val(&p, &name, &val, &is_raw);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ /* skip spaces and look for comma */
+ while (c_isspace(*p))
+ p++;
+
+ ret = add_new_elem(&list, &name, &val, p, is_raw);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (*p != ',' && *p != 0 && *p != '\n') {
+ ret = gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+ goto fail;
+ }
+ if (*p == ',')
+ p++;
+ }
+
+ plist = list;
+ while(plist) {
+ if (err)
+ *err = plist->pos;
+ ret = dn_attr_crt_set(f, crt, &plist->name, &plist->val, plist->is_raw);
+ if (ret < 0)
+ goto fail;
+
+ plist = plist->next;
+ }
+
+ ret = 0;
+fail:
+ plist = list;
+ while(plist) {
+ next = plist->next;
+ gnutls_free(plist);
+ plist = next;
+ }
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crt_set_dn:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @dn: a comma separated DN string (RFC4514)
+ * @err: indicates the error position (if any)
+ *
+ * This function will set the DN on the provided certificate.
+ * The input string should be plain ASCII or UTF-8 encoded. On
+ * DN parsing error %GNUTLS_E_PARSING_ERROR is returned.
+ *
+ * Note that DNs are not expected to hold DNS information, and thus
+ * no automatic IDNA conversions are attempted when using this function.
+ * If that is required (e.g., store a domain in CN), process the corresponding
+ * input with gnutls_idna_map().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_dn(gnutls_x509_crt_t crt, const char *dn,
+ const char **err)
+{
+ return crt_set_dn((set_dn_func) gnutls_x509_crt_set_dn_by_oid, crt,
+ dn, err);
+}
+
+/**
+ * gnutls_x509_crt_set_issuer_dn:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @dn: a comma separated DN string (RFC4514)
+ * @err: indicates the error position (if any)
+ *
+ * This function will set the DN on the provided certificate.
+ * The input string should be plain ASCII or UTF-8 encoded. On
+ * DN parsing error %GNUTLS_E_PARSING_ERROR is returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_issuer_dn(gnutls_x509_crt_t crt, const char *dn,
+ const char **err)
+{
+ return crt_set_dn((set_dn_func)
+ gnutls_x509_crt_set_issuer_dn_by_oid, crt, dn,
+ err);
+}
+
+/**
+ * gnutls_x509_crq_set_dn:
+ * @crq: a certificate of type #gnutls_x509_crq_t
+ * @dn: a comma separated DN string (RFC4514)
+ * @err: indicates the error position (if any)
+ *
+ * This function will set the DN on the provided certificate.
+ * The input string should be plain ASCII or UTF-8 encoded. On
+ * DN parsing error %GNUTLS_E_PARSING_ERROR is returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crq_set_dn(gnutls_x509_crq_t crq, const char *dn,
+ const char **err)
+{
+ return crt_set_dn((set_dn_func) gnutls_x509_crq_set_dn_by_oid, crq,
+ dn, err);
+}
+
+static
+int set_dn_by_oid(gnutls_x509_dn_t dn, const char *oid, unsigned int raw_flag, const void *name, unsigned name_size)
+{
+ return _gnutls_x509_set_dn_oid(dn->asn, "", oid, raw_flag, name, name_size);
+}
+
+/**
+ * gnutls_x509_dn_set_str:
+ * @dn: a pointer to DN
+ * @str: a comma separated DN string (RFC4514)
+ * @err: indicates the error position (if any)
+ *
+ * This function will set the DN on the provided DN structure.
+ * The input string should be plain ASCII or UTF-8 encoded. On
+ * DN parsing error %GNUTLS_E_PARSING_ERROR is returned.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.3
+ **/
+int
+gnutls_x509_dn_set_str(gnutls_x509_dn_t dn, const char *str, const char **err)
+{
+ if (dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return crt_set_dn((set_dn_func) set_dn_by_oid, dn,
+ str, err);
+}
+
+/**
+ * gnutls_x509_dn_init:
+ * @dn: the object to be initialized
+ *
+ * This function initializes a #gnutls_x509_dn_t type.
+ *
+ * The object returned must be deallocated using
+ * gnutls_x509_dn_deinit().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.4.0
+ **/
+int gnutls_x509_dn_init(gnutls_x509_dn_t * dn)
+{
+ int result;
+
+ *dn = gnutls_calloc(1, sizeof(gnutls_x509_dn_st));
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.Name", &(*dn)->asn)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(*dn);
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_dn_import:
+ * @dn: the structure that will hold the imported DN
+ * @data: should contain a DER encoded RDN sequence
+ *
+ * This function parses an RDN sequence and stores the result to a
+ * #gnutls_x509_dn_t type. The data must have been initialized
+ * with gnutls_x509_dn_init(). You may use gnutls_x509_dn_get_rdn_ava() to
+ * decode the DN.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.4.0
+ **/
+int gnutls_x509_dn_import(gnutls_x509_dn_t dn, const gnutls_datum_t * data)
+{
+ int result;
+ char err[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+ if (data->data == NULL || data->size == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ result = _asn1_strict_der_decode(&dn->asn,
+ data->data, data->size, err);
+ if (result != ASN1_SUCCESS) {
+ /* couldn't decode DER */
+ _gnutls_debug_log("ASN.1 Decoding error: %s\n", err);
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_dn_deinit:
+ * @dn: a DN uint8_t object pointer.
+ *
+ * This function deallocates the DN object as returned by
+ * gnutls_x509_dn_import().
+ *
+ * Since: 2.4.0
+ **/
+void gnutls_x509_dn_deinit(gnutls_x509_dn_t dn)
+{
+ asn1_delete_structure(&dn->asn);
+ gnutls_free(dn);
+}
+
+/**
+ * gnutls_x509_dn_export:
+ * @dn: Holds the uint8_t DN object
+ * @format: the format of output params. One of PEM or DER.
+ * @output_data: will contain a DN PEM or DER encoded
+ * @output_data_size: holds the size of output_data (and will be
+ * replaced by the actual size of parameters)
+ *
+ * This function will export the DN to DER or PEM format.
+ *
+ * If the buffer provided is not long enough to hold the output, then
+ * *@output_data_size is updated and %GNUTLS_E_SHORT_MEMORY_BUFFER
+ * will be returned.
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN NAME".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_dn_export(gnutls_x509_dn_t dn,
+ gnutls_x509_crt_fmt_t format, void *output_data,
+ size_t * output_data_size)
+{
+ if (dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_export_int_named(dn->asn, "rdnSequence",
+ format, "NAME",
+ output_data,
+ output_data_size);
+}
+
+/**
+ * gnutls_x509_dn_export2:
+ * @dn: Holds the uint8_t DN object
+ * @format: the format of output params. One of PEM or DER.
+ * @out: will contain a DN PEM or DER encoded
+ *
+ * This function will export the DN to DER or PEM format.
+ *
+ * The output buffer is allocated using gnutls_malloc().
+ *
+ * If the structure is PEM encoded, it will have a header
+ * of "BEGIN NAME".
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.3
+ **/
+int
+gnutls_x509_dn_export2(gnutls_x509_dn_t dn,
+ gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)
+{
+ if (dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_export_int_named2(dn->asn, "rdnSequence",
+ format, "NAME", out);
+}
+
+/**
+ * gnutls_x509_dn_get_rdn_ava:
+ * @dn: a pointer to DN
+ * @irdn: index of RDN
+ * @iava: index of AVA.
+ * @ava: Pointer to structure which will hold output information.
+ *
+ * Get pointers to data within the DN. The format of the @ava structure
+ * is shown below.
+ *
+ * struct gnutls_x509_ava_st {
+ * gnutls_datum_t oid;
+ * gnutls_datum_t value;
+ * unsigned long value_tag;
+ * };
+ *
+ * The X.509 distinguished name is a sequence of sequences of strings
+ * and this is what the @irdn and @iava indexes model.
+ *
+ * Note that @ava will contain pointers into the @dn structure which
+ * in turns points to the original certificate. Thus you should not
+ * modify any data or deallocate any of those.
+ *
+ * This is a low-level function that requires the caller to do the
+ * value conversions when necessary (e.g. from UCS-2).
+ *
+ * Returns: Returns 0 on success, or an error code.
+ **/
+int
+gnutls_x509_dn_get_rdn_ava(gnutls_x509_dn_t dn,
+ int irdn, int iava, gnutls_x509_ava_st * ava)
+{
+ asn1_node rdn, elem;
+ asn1_data_node_st vnode;
+ long len;
+ int lenlen, remlen, ret;
+ char rbuf[MAX_NAME_SIZE];
+ unsigned char cls;
+ const unsigned char *ptr;
+
+ iava++;
+ irdn++; /* 0->1, 1->2 etc */
+
+ snprintf(rbuf, sizeof(rbuf), "rdnSequence.?%d.?%d", irdn, iava);
+ rdn = asn1_find_node(dn->asn, rbuf);
+ if (!rdn) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
+ }
+
+ snprintf(rbuf, sizeof(rbuf), "?%d.type", iava);
+ elem = asn1_find_node(rdn, rbuf);
+ if (!elem) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
+ }
+
+ ret = asn1_read_node_value(elem, &vnode);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
+ }
+
+ ava->oid.data = (void *) vnode.value;
+ ava->oid.size = vnode.value_len;
+
+ snprintf(rbuf, sizeof(rbuf), "?%d.value", iava);
+ elem = asn1_find_node(rdn, rbuf);
+ if (!elem) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
+ }
+
+ ret = asn1_read_node_value(elem, &vnode);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
+ }
+ /* The value still has the previous tag's length bytes, plus the
+ * current value's tag and length bytes. Decode them.
+ */
+
+ ptr = vnode.value;
+ remlen = vnode.value_len;
+ len = asn1_get_length_der(ptr, remlen, &lenlen);
+ if (len < 0) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_DER_ERROR;
+ }
+
+ ptr += lenlen;
+ remlen -= lenlen;
+ ret =
+ asn1_get_tag_der(ptr, remlen, &cls, &lenlen, &ava->value_tag);
+ if (ret) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ptr += lenlen;
+ remlen -= lenlen;
+
+ {
+ signed long tmp;
+
+ tmp = asn1_get_length_der(ptr, remlen, &lenlen);
+ if (tmp < 0) {
+ gnutls_assert();
+ return GNUTLS_E_ASN1_DER_ERROR;
+ }
+ ava->value.size = tmp;
+ }
+ ava->value.data = (void *) (ptr + lenlen);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_dn_get_str:
+ * @dn: a pointer to DN
+ * @str: a datum that will hold the name
+ *
+ * This function will allocate buffer and copy the name in the provided DN.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.2
+ **/
+int
+gnutls_x509_dn_get_str(gnutls_x509_dn_t dn, gnutls_datum_t *str)
+{
+ if (dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(dn->asn, "rdnSequence", str, GNUTLS_X509_DN_FLAG_COMPAT);
+}
+
+/**
+ * gnutls_x509_dn_get_str:
+ * @dn: a pointer to DN
+ * @str: a datum that will hold the name
+ * @flags: zero or %GNUTLS_X509_DN_FLAG_COMPAT
+ *
+ * This function will allocate buffer and copy the name in the provided DN.
+ * The name will be in the form "C=xxxx,O=yyyy,CN=zzzz" as
+ * described in RFC4514. The output string will be ASCII or UTF-8
+ * encoded, depending on the certificate data.
+ *
+ * When the flag %GNUTLS_X509_DN_FLAG_COMPAT is specified, the output
+ * format will match the format output by previous to 3.5.6 versions of GnuTLS
+ * which was not not fully RFC4514-compliant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.7
+ **/
+int
+gnutls_x509_dn_get_str2(gnutls_x509_dn_t dn, gnutls_datum_t *str, unsigned flags)
+{
+ if (dn == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return _gnutls_x509_get_dn(dn->asn, "rdnSequence", str, flags);
+}
diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c
new file mode 100644
index 0000000..0b4c6a0
--- /dev/null
+++ b/lib/x509/x509_ext.c
@@ -0,0 +1,4050 @@
+/*
+ * Copyright (C) 2014-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains functions to handle X.509 certificate extensions (the x509-ext API)
+ */
+
+#include "gnutls_int.h"
+#include <datum.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <x509_b64.h>
+#include "x509_ext_int.h"
+#include "virt-san.h"
+#include <gnutls/x509-ext.h>
+#include "intprops.h"
+
+#define MAX_ENTRIES 64
+struct gnutls_subject_alt_names_st {
+ struct name_st *names;
+ unsigned int size;
+};
+
+/**
+ * gnutls_subject_alt_names_init:
+ * @sans: The alternative names
+ *
+ * This function will initialize an alternative names structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_subject_alt_names_init(gnutls_subject_alt_names_t * sans)
+{
+ *sans = gnutls_calloc(1, sizeof(struct gnutls_subject_alt_names_st));
+ if (*sans == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+static void subject_alt_names_deinit(gnutls_subject_alt_names_t sans)
+{
+ unsigned int i;
+
+ for (i = 0; i < sans->size; i++) {
+ gnutls_free(sans->names[i].san.data);
+ gnutls_free(sans->names[i].othername_oid.data);
+ }
+ gnutls_free(sans->names);
+}
+
+/**
+ * gnutls_subject_alt_names_deinit:
+ * @sans: The alternative names
+ *
+ * This function will deinitialize an alternative names structure.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_subject_alt_names_deinit(gnutls_subject_alt_names_t sans)
+{
+ subject_alt_names_deinit(sans);
+ gnutls_free(sans);
+}
+
+/**
+ * gnutls_subject_alt_names_get:
+ * @sans: The alternative names
+ * @seq: The index of the name to get
+ * @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t)
+ * @san: The alternative name data (should be treated as constant)
+ * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME (should be treated as constant)
+ *
+ * This function will return a specific alternative name as stored in
+ * the @sans type. The returned values should be treated as constant
+ * and valid for the lifetime of @sans.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the index is out of bounds, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_subject_alt_names_get(gnutls_subject_alt_names_t sans,
+ unsigned int seq, unsigned int *san_type,
+ gnutls_datum_t * san,
+ gnutls_datum_t * othername_oid)
+{
+ if (seq >= sans->size)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (san) {
+ memcpy(san, &sans->names[seq].san, sizeof(gnutls_datum_t));
+ }
+
+ if (san_type)
+ *san_type = sans->names[seq].type;
+
+ if (othername_oid != NULL && sans->names[seq].type == GNUTLS_SAN_OTHERNAME) {
+ othername_oid->data = sans->names[seq].othername_oid.data;
+ othername_oid->size = sans->names[seq].othername_oid.size;
+ }
+
+ return 0;
+}
+
+/* This is the same as gnutls_subject_alt_names_set() but will not
+ * copy the strings. It expects all the provided input to be already
+ * allocated by gnutls. */
+static
+int subject_alt_names_set(struct name_st **names,
+ unsigned int *size,
+ unsigned int san_type,
+ gnutls_datum_t * san, char *othername_oid,
+ unsigned raw)
+{
+ void *tmp;
+ int ret;
+
+ if (unlikely(INT_ADD_OVERFLOW(*size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ tmp = _gnutls_reallocarray(*names, *size + 1, sizeof((*names)[0]));
+ if (tmp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ *names = tmp;
+
+ ret = _gnutls_alt_name_assign_virt_type(&(*names)[*size], san_type, san, othername_oid, raw);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ (*size)++;
+ return 0;
+}
+
+/**
+ * gnutls_subject_alt_names_set:
+ * @sans: The alternative names
+ * @san_type: The type of the name (of %gnutls_subject_alt_names_t)
+ * @san: The alternative name data
+ * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
+ *
+ * This function will store the specified alternative name in
+ * the @sans.
+ *
+ * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
+ * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_subject_alt_names_set(gnutls_subject_alt_names_t sans,
+ unsigned int san_type,
+ const gnutls_datum_t * san,
+ const char *othername_oid)
+{
+ int ret;
+ gnutls_datum_t copy;
+ char *ooc;
+
+ ret = _gnutls_set_strdatum(&copy, san->data, san->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (othername_oid != NULL)
+ ooc = gnutls_strdup(othername_oid);
+ else
+ ooc = NULL;
+ ret = subject_alt_names_set(&sans->names, &sans->size,
+ san_type, &copy, ooc, 0);
+ if (ret < 0) {
+ gnutls_free(copy.data);
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_subject_alt_names:
+ * @ext: The DER-encoded extension data
+ * @sans: The alternative names
+ * @flags: should be zero
+ *
+ * This function will export the alternative names in the provided DER-encoded
+ * SubjectAltName PKIX extension, to a %gnutls_subject_alt_names_t type. @sans
+ * must be initialized.
+ *
+ * This function will succeed even if there no subject alternative names
+ * in the structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_subject_alt_names(const gnutls_datum_t * ext,
+ gnutls_subject_alt_names_t sans,
+ unsigned int flags)
+{
+ asn1_node c2 = NULL;
+ int result, ret;
+ unsigned int i;
+ gnutls_datum_t san, othername_oid;
+ unsigned type;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ for (i=0;;i++) {
+ san.data = NULL;
+ san.size = 0;
+ othername_oid.data = NULL;
+
+ ret = _gnutls_parse_general_name2(c2, "", i, &san, &type, 0);
+ if (ret < 0)
+ break;
+
+ if (type == GNUTLS_SAN_OTHERNAME) {
+ ret =
+ _gnutls_parse_general_name2(c2, "", i,
+ &othername_oid,
+ NULL, 1);
+ if (ret < 0)
+ break;
+
+ } else if (san.size == 0 || san.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_X509_UNKNOWN_SAN);
+ break;
+ }
+
+ ret = subject_alt_names_set(&sans->names, &sans->size,
+ type, &san,
+ (char *)othername_oid.data, 1);
+ if (ret < 0)
+ break;
+ }
+
+ sans->size = i;
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_free(san.data);
+ gnutls_free(othername_oid.data);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_subject_alt_names:
+ * @sans: The alternative names
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided alternative names structure to a
+ * DER-encoded SubjectAltName PKIX extension. The output data in @ext will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_subject_alt_names(gnutls_subject_alt_names_t sans,
+ gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ int result, ret;
+ unsigned i;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(), "PKIX1.GeneralNames", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ for (i = 0; i < sans->size; i++) {
+ if (sans->names[i].type == GNUTLS_SAN_OTHERNAME) {
+ ret = _gnutls_write_new_othername(c2, "", (char*)sans->names[i].othername_oid.data,
+ sans->names[i].san.data, sans->names[i].san.size);
+ } else {
+ ret =
+ _gnutls_write_new_general_name(c2, "", sans->names[i].type,
+ sans->names[i].san.data,
+ sans->names[i].san.size);
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_import_name_constraints:
+ * @ext: a DER encoded extension
+ * @nc: The nameconstraints
+ * @flags: zero or %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND
+ *
+ * This function will return an intermediate type containing
+ * the name constraints of the provided NameConstraints extension. That
+ * can be used in combination with gnutls_x509_name_constraints_check()
+ * to verify whether a server's name is in accordance with the constraints.
+ *
+ * When the @flags is set to %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, then if
+ * the @nc type is empty this function will behave identically as if the flag was not set.
+ * Otherwise if there are elements in the @nc structure then the
+ * constraints will be merged with the existing constraints following
+ * RFC5280 p6.1.4 (excluded constraints will be appended, permitted
+ * will be intersected).
+ *
+ * Note that @nc must be initialized prior to calling this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_name_constraints(const gnutls_datum_t * ext,
+ gnutls_x509_name_constraints_t nc,
+ unsigned int flags)
+{
+ int result, ret;
+ asn1_node c2 = NULL;
+ gnutls_x509_name_constraints_t nc2 = NULL;
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (flags & GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND &&
+ (nc->permitted != NULL || nc->excluded != NULL)) {
+ ret = gnutls_x509_name_constraints_init (&nc2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "permittedSubtrees",
+ &nc2->permitted);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "excludedSubtrees",
+ &nc2->excluded);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_name_constraints_merge(nc, nc2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ _gnutls_name_constraints_node_free(nc->permitted);
+ _gnutls_name_constraints_node_free(nc->excluded);
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "permittedSubtrees",
+ &nc->permitted);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_extract_name_constraints(c2, "excludedSubtrees",
+ &nc->excluded);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ if (nc2)
+ gnutls_x509_name_constraints_deinit (nc2);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_name_constraints:
+ * @nc: The nameconstraints
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided name constraints type to a
+ * DER-encoded PKIX NameConstraints (2.5.29.30) extension. The output data in
+ * @ext will be allocated using gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_name_constraints(gnutls_x509_name_constraints_t nc,
+ gnutls_datum_t * ext)
+{
+ int ret, result;
+ uint8_t null = 0;
+ asn1_node c2 = NULL;
+ struct name_constraints_node_st *tmp;
+
+ if (nc->permitted == NULL && nc->excluded == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.NameConstraints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (nc->permitted == NULL) {
+ (void)asn1_write_value(c2, "permittedSubtrees", NULL, 0);
+ } else {
+ tmp = nc->permitted;
+ do {
+ result =
+ asn1_write_value(c2, "permittedSubtrees", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2,
+ "permittedSubtrees.?LAST.maximum",
+ NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2,
+ "permittedSubtrees.?LAST.minimum",
+ &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_write_general_name(c2,
+ "permittedSubtrees.?LAST.base",
+ tmp->type,
+ tmp->name.data,
+ tmp->name.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tmp = tmp->next;
+ } while (tmp != NULL);
+ }
+
+ if (nc->excluded == NULL) {
+ (void)asn1_write_value(c2, "excludedSubtrees", NULL, 0);
+ } else {
+ tmp = nc->excluded;
+ do {
+ result =
+ asn1_write_value(c2, "excludedSubtrees", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2,
+ "excludedSubtrees.?LAST.maximum",
+ NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2,
+ "excludedSubtrees.?LAST.minimum",
+ &null, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_write_general_name(c2,
+ "excludedSubtrees.?LAST.base",
+ tmp->type,
+ tmp->name.data,
+ tmp->name.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tmp = tmp->next;
+ } while (tmp != NULL);
+
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_import_subject_key_id:
+ * @ext: a DER encoded extension
+ * @id: will contain the subject key ID
+ *
+ * This function will return the subject key ID stored in the provided
+ * SubjectKeyIdentifier extension. The ID will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_subject_key_id(const gnutls_datum_t * ext,
+ gnutls_datum_t * id)
+{
+ int result, ret;
+ asn1_node c2 = NULL;
+
+ if (ext->size == 0 || ext->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.SubjectKeyIdentifier", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_read_value(c2, "", id);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_ext_export_subject_key_id:
+ * @id: The key identifier
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided key identifier to a
+ * DER-encoded PKIX SubjectKeyIdentifier extension.
+ * The output data in @ext will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_subject_key_id(const gnutls_datum_t * id,
+ gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ int ret, result;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.SubjectKeyIdentifier", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = asn1_write_value(c2, "", id->data, id->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+struct gnutls_x509_aki_st {
+ gnutls_datum_t id;
+ struct gnutls_subject_alt_names_st cert_issuer;
+ gnutls_datum_t serial;
+};
+
+/**
+ * gnutls_x509_aki_init:
+ * @aki: The authority key ID type
+ *
+ * This function will initialize an authority key ID.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aki_init(gnutls_x509_aki_t * aki)
+{
+ *aki = gnutls_calloc(1, sizeof(struct gnutls_x509_aki_st));
+ if (*aki == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_aki_deinit:
+ * @aki: The authority key identifier type
+ *
+ * This function will deinitialize an authority key identifier.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_aki_deinit(gnutls_x509_aki_t aki)
+{
+ gnutls_free(aki->serial.data);
+ gnutls_free(aki->id.data);
+ subject_alt_names_deinit(&aki->cert_issuer);
+ gnutls_free(aki);
+}
+
+/**
+ * gnutls_x509_aki_get_id:
+ * @aki: The authority key ID
+ * @id: Will hold the identifier
+ *
+ * This function will return the key identifier as stored in
+ * the @aki type. The identifier should be treated as constant.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the index is out of bounds, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aki_get_id(gnutls_x509_aki_t aki, gnutls_datum_t * id)
+{
+ if (aki->id.size == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ memcpy(id, &aki->id, sizeof(gnutls_datum_t));
+ return 0;
+}
+
+/**
+ * gnutls_x509_aki_set_id:
+ * @aki: The authority key ID
+ * @id: the key identifier
+ *
+ * This function will set the keyIdentifier to be stored in the @aki
+ * type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aki_set_id(gnutls_x509_aki_t aki, const gnutls_datum_t * id)
+{
+ return _gnutls_set_datum(&aki->id, id->data, id->size);
+}
+
+/**
+ * gnutls_x509_aki_set_cert_issuer:
+ * @aki: The authority key ID
+ * @san_type: the type of the name (of %gnutls_subject_alt_names_t), may be null
+ * @san: The alternative name data
+ * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
+ * @serial: The authorityCertSerialNumber number (may be null)
+ *
+ * This function will set the authorityCertIssuer name and the authorityCertSerialNumber
+ * to be stored in the @aki type. When storing multiple names, the serial
+ * should be set on the first call, and subsequent calls should use a %NULL serial.
+ *
+ * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
+ * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aki_set_cert_issuer(gnutls_x509_aki_t aki,
+ unsigned int san_type,
+ const gnutls_datum_t *san,
+ const char *othername_oid,
+ const gnutls_datum_t *serial)
+{
+ int ret;
+ gnutls_datum_t t_san, t_othername_oid = { NULL, 0 };
+
+ ret = _gnutls_set_datum(&aki->serial, serial->data, serial->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ aki->cert_issuer.names[aki->cert_issuer.size].type = san_type;
+
+ ret = _gnutls_set_strdatum(&t_san, san->data, san->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (othername_oid) {
+ t_othername_oid.data = (uint8_t *) gnutls_strdup(othername_oid);
+ if (t_othername_oid.data == NULL) {
+ gnutls_free(t_san.data);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ t_othername_oid.size = strlen(othername_oid);
+ }
+
+ ret =
+ subject_alt_names_set(&aki->cert_issuer.names,
+ &aki->cert_issuer.size, san_type, &t_san,
+ (char *)t_othername_oid.data, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_aki_get_cert_issuer:
+ * @aki: The authority key ID
+ * @seq: The index of the name to get
+ * @san_type: Will hold the type of the name (of %gnutls_subject_alt_names_t)
+ * @san: The alternative name data
+ * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
+ * @serial: The authorityCertSerialNumber number
+ *
+ * This function will return a specific authorityCertIssuer name as stored in
+ * the @aki type, as well as the authorityCertSerialNumber. All the returned
+ * values should be treated as constant, and may be set to %NULL when are not required.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the index is out of bounds, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aki_get_cert_issuer(gnutls_x509_aki_t aki, unsigned int seq,
+ unsigned int *san_type,
+ gnutls_datum_t *san,
+ gnutls_datum_t *othername_oid,
+ gnutls_datum_t *serial)
+{
+ if (seq >= aki->cert_issuer.size)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (aki->serial.size == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (serial)
+ memcpy(serial, &aki->serial, sizeof(gnutls_datum_t));
+
+ if (san) {
+ memcpy(san, &aki->cert_issuer.names[seq].san,
+ sizeof(gnutls_datum_t));
+ }
+
+ if (othername_oid != NULL
+ && aki->cert_issuer.names[seq].type == GNUTLS_SAN_OTHERNAME) {
+ othername_oid->data =
+ aki->cert_issuer.names[seq].othername_oid.data;
+ othername_oid->size =
+ aki->cert_issuer.names[seq].othername_oid.size;
+ }
+
+ if (san_type)
+ *san_type = aki->cert_issuer.names[seq].type;
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_ext_import_authority_key_id:
+ * @ext: a DER encoded extension
+ * @aki: An initialized authority key identifier type
+ * @flags: should be zero
+ *
+ * This function will return the subject key ID stored in the provided
+ * AuthorityKeyIdentifier extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the extension is not present, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_authority_key_id(const gnutls_datum_t * ext,
+ gnutls_x509_aki_t aki,
+ unsigned int flags)
+{
+ int ret;
+ unsigned i;
+ asn1_node c2 = NULL;
+ gnutls_datum_t san, othername_oid;
+ unsigned type;
+
+ ret = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.AuthorityKeyIdentifier", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ /* Read authorityCertIssuer */
+ for (i=0;;i++) {
+ san.data = NULL;
+ san.size = 0;
+ othername_oid.data = NULL;
+
+ ret = _gnutls_parse_general_name2(c2, "authorityCertIssuer", i,
+ &san, &type, 0);
+ if (ret < 0)
+ break;
+
+ if (type == GNUTLS_SAN_OTHERNAME) {
+ ret =
+ _gnutls_parse_general_name2(c2,
+ "authorityCertIssuer",
+ i,
+ &othername_oid,
+ NULL, 1);
+ if (ret < 0)
+ break;
+ }
+
+ ret = subject_alt_names_set(&aki->cert_issuer.names,
+ &aki->cert_issuer.size,
+ type, &san,
+ (char *)othername_oid.data, 1);
+ if (ret < 0)
+ break;
+ }
+
+ assert(ret < 0);
+ aki->cert_issuer.size = i;
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ && ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ gnutls_free(san.data);
+ gnutls_free(othername_oid.data);
+ goto cleanup;
+ }
+
+ /* Read the serial number */
+ ret =
+ _gnutls_x509_read_value(c2, "authorityCertSerialNumber",
+ &aki->serial);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ && ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Read the key identifier */
+ ret = _gnutls_x509_read_value(c2, "keyIdentifier", &aki->id);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ && ret != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_authority_key_id:
+ * @aki: An initialized authority key identifier
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided key identifier to a
+ * DER-encoded PKIX AuthorityKeyIdentifier extension.
+ * The output data in @ext will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_authority_key_id(gnutls_x509_aki_t aki,
+ gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ unsigned i;
+ int result, ret;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.AuthorityKeyIdentifier", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (aki->id.data != NULL) {
+ result =
+ asn1_write_value(c2, "keyIdentifier", aki->id.data,
+ aki->id.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ } else {
+ (void)asn1_write_value(c2, "keyIdentifier", NULL, 0);
+ }
+
+ if (aki->serial.data != NULL) {
+ result =
+ asn1_write_value(c2, "authorityCertSerialNumber",
+ aki->serial.data, aki->serial.size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ } else {
+ (void)asn1_write_value(c2, "authorityCertSerialNumber", NULL, 0);
+ }
+
+ if (aki->cert_issuer.size == 0) {
+ (void)asn1_write_value(c2, "authorityCertIssuer", NULL, 0);
+ } else {
+ for (i = 0; i < aki->cert_issuer.size; i++) {
+ ret =
+ _gnutls_write_new_general_name(c2,
+ "authorityCertIssuer",
+ aki->cert_issuer.
+ names[i].type,
+ aki->
+ cert_issuer.names[i].
+ san.data,
+ aki->cert_issuer.
+ names[i].san.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_ext_import_key_usage:
+ * @ext: the DER encoded extension data
+ * @key_usage: where the key usage bits will be stored
+ *
+ * This function will return certificate's key usage, by reading the DER
+ * data of the keyUsage X.509 extension (2.5.29.15). The key usage value will ORed
+ * values of the: %GNUTLS_KEY_DIGITAL_SIGNATURE,
+ * %GNUTLS_KEY_NON_REPUDIATION, %GNUTLS_KEY_KEY_ENCIPHERMENT,
+ * %GNUTLS_KEY_DATA_ENCIPHERMENT, %GNUTLS_KEY_KEY_AGREEMENT,
+ * %GNUTLS_KEY_KEY_CERT_SIGN, %GNUTLS_KEY_CRL_SIGN,
+ * %GNUTLS_KEY_ENCIPHER_ONLY, %GNUTLS_KEY_DECIPHER_ONLY.
+ *
+ * Returns: the certificate key usage, or a negative error code in case of
+ * parsing error. If the certificate does not contain the keyUsage
+ * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_key_usage(const gnutls_datum_t * ext,
+ unsigned int *key_usage)
+{
+ asn1_node c2 = NULL;
+ int len, result;
+ uint8_t str[2];
+
+ str[0] = str[1] = 0;
+ *key_usage = 0;
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ len = sizeof(str);
+ result = asn1_read_value(c2, "", str, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ *key_usage = str[0] | (str[1] << 8);
+
+ asn1_delete_structure(&c2);
+
+ return 0;
+}
+
+static int _last_key_usage_set_bit(int usage)
+{
+/* the byte ordering is a bit strange here, see how GNUTLS_KEY_* is laid out, and how
+ * asn1_write_value() writes out BIT STRING objects.
+ */
+ if (usage & GNUTLS_KEY_DECIPHER_ONLY)
+ return 9;
+ else if (usage & GNUTLS_KEY_ENCIPHER_ONLY)
+ return 8;
+ else if (usage & GNUTLS_KEY_CRL_SIGN)
+ return 7;
+ else if (usage & GNUTLS_KEY_KEY_CERT_SIGN)
+ return 6;
+ else if (usage & GNUTLS_KEY_KEY_AGREEMENT)
+ return 5;
+ else if (usage & GNUTLS_KEY_DATA_ENCIPHERMENT)
+ return 4;
+ else if (usage & GNUTLS_KEY_KEY_ENCIPHERMENT)
+ return 3;
+ else if (usage & GNUTLS_KEY_NON_REPUDIATION)
+ return 2;
+ else if (usage & GNUTLS_KEY_DIGITAL_SIGNATURE)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_export_key_usage:
+ * @usage: an ORed sequence of the GNUTLS_KEY_* elements.
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the keyUsage bit string to a DER
+ * encoded PKIX extension. The @ext data will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_key_usage(unsigned int usage, gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ int result;
+ uint8_t str[2];
+
+ result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.KeyUsage", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ str[0] = usage & 0xff;
+ str[1] = usage >> 8;
+
+ /* Since KeyUsage is a BIT STRING, the input to asn1_write_value
+ * is the number of bits to be written/read. */
+ result = asn1_write_value(c2, "", str, _last_key_usage_set_bit(usage));
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ asn1_delete_structure(&c2);
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", ext, 0);
+
+ asn1_delete_structure(&c2);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_inhibit_anypolicy:
+ * @ext: the DER encoded extension data
+ * @skipcerts: will hold the number of certificates after which anypolicy is no longer acceptable.
+ *
+ * This function will return certificate's value of SkipCerts,
+ * by reading the DER data of the Inhibit anyPolicy X.509 extension (2.5.29.54).
+ *
+ * The @skipcerts value is the number of additional certificates that
+ * may appear in the path before the anyPolicy (%GNUTLS_X509_OID_POLICY_ANY)
+ * is no longer acceptable.
+ *
+ * Returns: zero, or a negative error code in case of
+ * parsing error. If the certificate does not contain the Inhibit anyPolicy
+ * extension %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be
+ * returned.
+ *
+ * Since: 3.6.0
+ **/
+int gnutls_x509_ext_import_inhibit_anypolicy(const gnutls_datum_t * ext,
+ unsigned int *skipcerts)
+{
+ int ret;
+
+ ret = _gnutls_x509_read_der_uint(ext->data, ext->size, skipcerts);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_inhibit_anypolicy:
+ * @skipcerts: number of certificates after which anypolicy is no longer acceptable.
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the @skipcerts value to a DER
+ * encoded Inhibit AnyPolicy PKIX extension. The @ext data will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.0
+ **/
+int gnutls_x509_ext_export_inhibit_anypolicy(unsigned int skipcerts, gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ int result, ret;
+
+ result = asn1_create_element(_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ ret = _gnutls_x509_write_uint32(c2, "", skipcerts);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_import_private_key_usage_period:
+ * @ext: the DER encoded extension data
+ * @activation: Will hold the activation time
+ * @expiration: Will hold the expiration time
+ *
+ * This function will return the expiration and activation
+ * times of the private key as written in the
+ * PKIX extension 2.5.29.16.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_private_key_usage_period(const gnutls_datum_t * ext,
+ time_t * activation,
+ time_t * expiration)
+{
+ int result, ret;
+ asn1_node c2 = NULL;
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.PrivateKeyUsagePeriod", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (activation)
+ *activation = _gnutls_x509_get_time(c2, "notBefore", 1);
+
+ if (expiration)
+ *expiration = _gnutls_x509_get_time(c2, "notAfter", 1);
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_private_key_usage_period:
+ * @activation: The activation time
+ * @expiration: The expiration time
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the periods provided to a private key
+ * usage DER encoded extension (2.5.29.16).
+ (
+ * The @ext data will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_private_key_usage_period(time_t activation,
+ time_t expiration,
+ gnutls_datum_t * ext)
+{
+ int result;
+ asn1_node c2 = NULL;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.PrivateKeyUsagePeriod", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _gnutls_x509_set_time(c2, "notBefore", activation, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_set_time(c2, "notAfter", expiration, 1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return result;
+
+}
+
+/**
+ * gnutls_x509_ext_import_basic_constraints:
+ * @ext: the DER encoded extension data
+ * @ca: will be non zero if the CA status is true
+ * @pathlen: the path length constraint; will be set to -1 for no limit
+ *
+ * This function will return the CA status and path length constraint
+ * as written in the PKIX extension 2.5.29.19.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_basic_constraints(const gnutls_datum_t * ext,
+ unsigned int *ca, int *pathlen)
+{
+ asn1_node c2 = NULL;
+ char str[128]="";
+ int len, result;
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.BasicConstraints",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (pathlen) {
+ result = _gnutls_x509_read_uint(c2, "pathLenConstraint",
+ (unsigned int *)
+ pathlen);
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ *pathlen = -1;
+ else if (result != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ /* the default value of cA is false.
+ */
+ len = sizeof(str) - 1;
+ result = asn1_read_value(c2, "cA", str, &len);
+ if (result == ASN1_SUCCESS && strcmp(str, "TRUE") == 0)
+ *ca = 1;
+ else
+ *ca = 0;
+
+ result = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return result;
+
+}
+
+/**
+ * gnutls_x509_ext_export_basic_constraints:
+ * @ca: non-zero for a CA
+ * @pathlen: The path length constraint (set to -1 for no constraint)
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the parameters provided to a basic constraints
+ * DER encoded extension (2.5.29.19).
+ (
+ * The @ext data will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_basic_constraints(unsigned int ca, int pathlen,
+ gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ const char *str;
+ int result;
+
+ if (ca == 0)
+ str = "FALSE";
+ else
+ str = "TRUE";
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.BasicConstraints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "cA", str, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (pathlen < 0) {
+ result = asn1_write_value(c2, "pathLenConstraint", NULL, 0);
+ if (result < 0)
+ result = _gnutls_asn2err(result);
+ } else
+ result =
+ _gnutls_x509_write_uint32(c2, "pathLenConstraint", pathlen);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/**
+ * gnutls_x509_ext_import_proxy:
+ * @ext: the DER encoded extension data
+ * @pathlen: pointer to output integer indicating path length (may be
+ * NULL), non-negative error codes indicate a present pCPathLenConstraint
+ * field and the actual value, -1 indicate that the field is absent.
+ * @policyLanguage: output variable with OID of policy language
+ * @policy: output variable with policy data
+ * @sizeof_policy: output variable with size of policy data
+ *
+ * This function will return the information from a proxy certificate
+ * extension. It reads the ProxyCertInfo X.509 extension (1.3.6.1.5.5.7.1.14).
+ * The @policyLanguage and @policy values must be deinitialized using gnutls_free() after use.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_proxy(const gnutls_datum_t *ext, int *pathlen,
+ char **policyLanguage, char **policy,
+ size_t *sizeof_policy)
+{
+ asn1_node c2 = NULL;
+ int result;
+ gnutls_datum_t value1 = { NULL, 0 };
+ gnutls_datum_t value2 = { NULL, 0 };
+
+ if ((result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.ProxyCertInfo",
+ &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (pathlen) {
+ result = _gnutls_x509_read_uint(c2, "pCPathLenConstraint",
+ (unsigned int *)
+ pathlen);
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ *pathlen = -1;
+ else if (result != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ result = _gnutls_x509_read_value(c2, "proxyPolicy.policyLanguage",
+ &value1);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_read_value(c2, "proxyPolicy.policy", &value2);
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ if (policy)
+ *policy = NULL;
+ if (sizeof_policy)
+ *sizeof_policy = 0;
+ } else if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ } else {
+ if (policy) {
+ *policy = (char *)value2.data;
+ value2.data = NULL;
+ }
+ if (sizeof_policy)
+ *sizeof_policy = value2.size;
+ }
+
+ if (policyLanguage) {
+ *policyLanguage = (char *)value1.data;
+ value1.data = NULL;
+ }
+
+ result = 0;
+ cleanup:
+ gnutls_free(value1.data);
+ gnutls_free(value2.data);
+ asn1_delete_structure(&c2);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_ext_export_proxy:
+ * @pathLenConstraint: A negative value will remove the path length constraint,
+ * while non-negative values will be set as the length of the pathLenConstraints field.
+ * @policyLanguage: OID describing the language of @policy.
+ * @policy: uint8_t byte array with policy language, can be %NULL
+ * @sizeof_policy: size of @policy.
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the parameters provided to a proxyCertInfo extension.
+ *
+ * The @ext data will be allocated using gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_proxy(int pathLenConstraint, const char *policyLanguage,
+ const char *policy, size_t sizeof_policy,
+ gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ int result;
+
+ result = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.ProxyCertInfo", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (pathLenConstraint < 0) {
+ result = asn1_write_value(c2, "pCPathLenConstraint", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ } else {
+ result =
+ _gnutls_x509_write_uint32(c2, "pCPathLenConstraint",
+ pathLenConstraint);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ result = asn1_write_value(c2, "proxyPolicy.policyLanguage",
+ policyLanguage, 1);
+ if (result < 0) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = asn1_write_value(c2, "proxyPolicy.policy",
+ policy, sizeof_policy);
+ if (result < 0) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+static int decode_user_notice(const void *data, size_t size,
+ gnutls_datum_t * txt)
+{
+ asn1_node c2 = NULL;
+ int ret, len;
+ char choice_type[64];
+ char name[128];
+ gnutls_datum_t td = {NULL,0}, utd;
+
+ ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.UserNotice", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+
+ ret = _asn1_strict_der_decode(&c2, data, size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+
+ len = sizeof(choice_type);
+ ret = asn1_read_value(c2, "explicitText", choice_type, &len);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+
+ if (strcmp(choice_type, "utf8String") != 0
+ && strcmp(choice_type, "ia5String") != 0
+ && strcmp(choice_type, "bmpString") != 0
+ && strcmp(choice_type, "visibleString") != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PARSING_ERROR;
+ goto cleanup;
+ }
+
+ snprintf(name, sizeof(name), "explicitText.%s", choice_type);
+
+ ret = _gnutls_x509_read_value(c2, name, &td);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (strcmp(choice_type, "bmpString") == 0) { /* convert to UTF-8 */
+ ret = _gnutls_ucs2_to_utf8(td.data, td.size, &utd, 1);
+ _gnutls_free_datum(&td);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ td.data = utd.data;
+ td.size = utd.size;
+ } else {
+ /* _gnutls_x509_read_value allows that */
+ td.data[td.size] = 0;
+ }
+
+ txt->data = (void *)td.data;
+ txt->size = td.size;
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+
+}
+
+struct gnutls_x509_policies_st {
+ struct gnutls_x509_policy_st policy[MAX_ENTRIES];
+ unsigned int size;
+};
+
+/**
+ * gnutls_x509_policies_init:
+ * @policies: The authority key ID
+ *
+ * This function will initialize an authority key ID type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_policies_init(gnutls_x509_policies_t * policies)
+{
+ *policies = gnutls_calloc(1, sizeof(struct gnutls_x509_policies_st));
+ if (*policies == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_policies_deinit:
+ * @policies: The authority key identifier
+ *
+ * This function will deinitialize an authority key identifier type.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_policies_deinit(gnutls_x509_policies_t policies)
+{
+ unsigned i;
+
+ for (i = 0; i < policies->size; i++) {
+ gnutls_x509_policy_release(&policies->policy[i]);
+ }
+ gnutls_free(policies);
+}
+
+/**
+ * gnutls_x509_policies_get:
+ * @policies: The policies
+ * @seq: The index of the name to get
+ * @policy: Will hold the policy
+ *
+ * This function will return a specific policy as stored in
+ * the @policies type. The returned values should be treated as constant
+ * and valid for the lifetime of @policies.
+ *
+ * The any policy OID is available as the %GNUTLS_X509_OID_POLICY_ANY macro.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the index is out of bounds, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_policies_get(gnutls_x509_policies_t policies,
+ unsigned int seq,
+ struct gnutls_x509_policy_st *policy)
+{
+ if (seq >= policies->size)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (policy) {
+ memcpy(policy, &policies->policy[seq],
+ sizeof(struct gnutls_x509_policy_st));
+ }
+
+ return 0;
+}
+
+void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies,
+ unsigned int seq)
+{
+ if (seq >= policies->size)
+ return;
+
+ memset(&policies->policy[seq], 0, sizeof(struct gnutls_x509_policy_st));
+}
+
+/**
+ * gnutls_x509_policies_set:
+ * @policies: An initialized policies
+ * @seq: The index of the name to get
+ * @policy: Contains the policy to set
+ *
+ * This function will store the specified policy in
+ * the provided @policies.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_policies_set(gnutls_x509_policies_t policies,
+ const struct gnutls_x509_policy_st *policy)
+{
+ unsigned i;
+
+ if (policies->size + 1 > MAX_ENTRIES)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ policies->policy[policies->size].oid = gnutls_strdup(policy->oid);
+ if (policies->policy[policies->size].oid == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ for (i = 0; i < policy->qualifiers; i++) {
+ policies->policy[policies->size].qualifier[i].type =
+ policy->qualifier[i].type;
+ policies->policy[policies->size].qualifier[i].size =
+ policy->qualifier[i].size;
+ policies->policy[policies->size].qualifier[i].data =
+ gnutls_malloc(policy->qualifier[i].size + 1);
+ if (policies->policy[policies->size].qualifier[i].data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ memcpy(policies->policy[policies->size].qualifier[i].data,
+ policy->qualifier[i].data, policy->qualifier[i].size);
+ policies->policy[policies->size].qualifier[i].data[policy->
+ qualifier[i].
+ size] = 0;
+ }
+
+ policies->policy[policies->size].qualifiers = policy->qualifiers;
+ policies->size++;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_policies:
+ * @ext: the DER encoded extension data
+ * @policies: A pointer to an initialized policies.
+ * @flags: should be zero
+ *
+ * This function will extract the certificate policy extension (2.5.29.32)
+ * and store it the provided policies.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_policies(const gnutls_datum_t * ext,
+ gnutls_x509_policies_t policies,
+ unsigned int flags)
+{
+ asn1_node c2 = NULL;
+ char tmpstr[128];
+ char tmpoid[MAX_OID_SIZE];
+ gnutls_datum_t tmpd = { NULL, 0 };
+ int ret, len;
+ unsigned i, j, current = 0;
+
+ ret = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.certificatePolicies", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ for (j = 0;; j++) {
+ if (j >= MAX_ENTRIES)
+ break;
+
+ memset(&policies->policy[j], 0,
+ sizeof(struct gnutls_x509_policy_st));
+
+ /* create a string like "?1"
+ */
+ snprintf(tmpstr, sizeof(tmpstr), "?%u.policyIdentifier", j + 1);
+ current = j+1;
+
+ ret = _gnutls_x509_read_value(c2, tmpstr, &tmpd);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND)
+ break;
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto full_cleanup;
+ }
+
+ policies->policy[j].oid = (void *)tmpd.data;
+ tmpd.data = NULL;
+
+ for (i = 0; i < GNUTLS_MAX_QUALIFIERS; i++) {
+ gnutls_datum_t td;
+
+ snprintf(tmpstr, sizeof(tmpstr),
+ "?%u.policyQualifiers.?%u.policyQualifierId",
+ j + 1, i + 1);
+
+ len = sizeof(tmpoid);
+ ret = asn1_read_value(c2, tmpstr, tmpoid, &len);
+
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
+ break; /* finished */
+
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto full_cleanup;
+ }
+
+ if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.1") == 0) {
+ snprintf(tmpstr, sizeof(tmpstr),
+ "?%u.policyQualifiers.?%u.qualifier",
+ j + 1, i + 1);
+
+ ret =
+ _gnutls_x509_read_string(c2, tmpstr, &td,
+ ASN1_ETYPE_IA5_STRING, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto full_cleanup;
+ }
+
+ policies->policy[j].qualifier[i].data =
+ (void *)td.data;
+ policies->policy[j].qualifier[i].size = td.size;
+ td.data = NULL;
+ policies->policy[j].qualifier[i].type =
+ GNUTLS_X509_QUALIFIER_URI;
+ } else if (strcmp(tmpoid, "1.3.6.1.5.5.7.2.2") == 0) {
+ gnutls_datum_t txt = {NULL, 0};
+
+ snprintf(tmpstr, sizeof(tmpstr),
+ "?%u.policyQualifiers.?%u.qualifier",
+ j + 1, i + 1);
+
+ ret = _gnutls_x509_read_value(c2, tmpstr, &td);
+ if (ret < 0) {
+ gnutls_assert();
+ goto full_cleanup;
+ }
+
+ ret =
+ decode_user_notice(td.data, td.size, &txt);
+ gnutls_free(td.data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto full_cleanup;
+ }
+
+ policies->policy[j].qualifier[i].data =
+ (void *)txt.data;
+ policies->policy[j].qualifier[i].size =
+ txt.size;
+ policies->policy[j].qualifier[i].type =
+ GNUTLS_X509_QUALIFIER_NOTICE;
+ } else
+ policies->policy[j].qualifier[i].type =
+ GNUTLS_X509_QUALIFIER_UNKNOWN;
+
+ policies->policy[j].qualifiers++;
+ }
+
+ }
+
+ policies->size = j;
+
+ ret = 0;
+ goto cleanup;
+
+ full_cleanup:
+ for (j = 0; j < current; j++)
+ gnutls_x509_policy_release(&policies->policy[j]);
+
+ cleanup:
+ _gnutls_free_datum(&tmpd);
+ asn1_delete_structure(&c2);
+ return ret;
+
+}
+
+static int encode_user_notice(const gnutls_datum_t * txt,
+ gnutls_datum_t * der_data)
+{
+ int result;
+ asn1_node c2 = NULL;
+
+ if ((result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.UserNotice", &c2)) != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ /* delete noticeRef */
+ result = asn1_write_value(c2, "noticeRef", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = asn1_write_value(c2, "explicitText", "utf8String", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result =
+ asn1_write_value(c2, "explicitText.utf8String", txt->data,
+ txt->size);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto error;
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", der_data, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ result = 0;
+
+ error:
+ asn1_delete_structure(&c2);
+ return result;
+
+}
+
+/**
+ * gnutls_x509_ext_export_policies:
+ * @policies: A pointer to an initialized policies.
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided policies, to a certificate policy
+ * DER encoded extension (2.5.29.32).
+ *
+ * The @ext data will be allocated using gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_policies(gnutls_x509_policies_t policies,
+ gnutls_datum_t * ext)
+{
+ int result;
+ unsigned i, j;
+ gnutls_datum_t der_data = {NULL, 0}, tmpd;
+ asn1_node c2 = NULL;
+ const char *oid;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.certificatePolicies", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ for (j = 0; j < policies->size; j++) {
+ /* 1. write a new policy */
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* 2. Add the OID.
+ */
+ result =
+ asn1_write_value(c2, "?LAST.policyIdentifier",
+ policies->policy[j].oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (policies->policy[j].qualifiers == 0) {
+ /* remove the optional policyQualifiers if none are present. */
+ result = asn1_write_value(c2, "?LAST.policyQualifiers", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ for (i = 0;
+ i < MIN(policies->policy[j].qualifiers,
+ GNUTLS_MAX_QUALIFIERS); i++) {
+ result =
+ asn1_write_value(c2, "?LAST.policyQualifiers",
+ "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (policies->policy[j].qualifier[i].type ==
+ GNUTLS_X509_QUALIFIER_URI)
+ oid = "1.3.6.1.5.5.7.2.1";
+ else if (policies->policy[j].qualifier[i].type ==
+ GNUTLS_X509_QUALIFIER_NOTICE)
+ oid = "1.3.6.1.5.5.7.2.2";
+ else {
+ result =
+ gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2,
+ "?LAST.policyQualifiers.?LAST.policyQualifierId",
+ oid, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (policies->policy[j].qualifier[i].type ==
+ GNUTLS_X509_QUALIFIER_URI) {
+ tmpd.data =
+ (void *)policies->policy[j].qualifier[i].
+ data;
+ tmpd.size =
+ policies->policy[j].qualifier[i].size;
+ result =
+ _gnutls_x509_write_string(c2,
+ "?LAST.policyQualifiers.?LAST.qualifier",
+ &tmpd,
+ ASN1_ETYPE_IA5_STRING);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else if (policies->policy[j].qualifier[i].type ==
+ GNUTLS_X509_QUALIFIER_NOTICE) {
+ tmpd.data =
+ (void *)policies->policy[j].qualifier[i].
+ data;
+ tmpd.size =
+ policies->policy[j].qualifier[i].size;
+
+ if (tmpd.size > 200) {
+ gnutls_assert();
+ result = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ result = encode_user_notice(&tmpd, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result =
+ _gnutls_x509_write_value(c2,
+ "?LAST.policyQualifiers.?LAST.qualifier",
+ &der_data);
+ _gnutls_free_datum(&der_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return result;
+}
+
+struct crl_dist_point_st {
+ unsigned int type;
+ gnutls_datum_t san;
+ unsigned int reasons;
+};
+
+struct gnutls_x509_crl_dist_points_st {
+ struct crl_dist_point_st *points;
+ unsigned int size;
+};
+
+/**
+ * gnutls_x509_crl_dist_points_init:
+ * @cdp: The CRL distribution points
+ *
+ * This function will initialize a CRL distribution points type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_crl_dist_points_init(gnutls_x509_crl_dist_points_t * cdp)
+{
+ *cdp = gnutls_calloc(1, sizeof(struct gnutls_x509_crl_dist_points_st));
+ if (*cdp == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crl_dist_points_deinit:
+ * @cdp: The CRL distribution points
+ *
+ * This function will deinitialize a CRL distribution points type.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_crl_dist_points_deinit(gnutls_x509_crl_dist_points_t cdp)
+{
+ unsigned i;
+
+ for (i = 0; i < cdp->size; i++) {
+ gnutls_free(cdp->points[i].san.data);
+ }
+ gnutls_free(cdp->points);
+ gnutls_free(cdp);
+}
+
+/**
+ * gnutls_x509_crl_dist_points_get:
+ * @cdp: The CRL distribution points
+ * @seq: specifies the sequence number of the distribution point (0 for the first one, 1 for the second etc.)
+ * @type: The name type of the corresponding name (gnutls_x509_subject_alt_name_t)
+ * @san: The distribution point names (to be treated as constant)
+ * @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
+ *
+ * This function retrieves the individual CRL distribution points (2.5.29.31),
+ * contained in provided type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the index is out of bounds, otherwise a negative error value.
+ **/
+
+int gnutls_x509_crl_dist_points_get(gnutls_x509_crl_dist_points_t cdp,
+ unsigned int seq, unsigned int *type,
+ gnutls_datum_t * san, unsigned int *reasons)
+{
+ if (seq >= cdp->size)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (reasons)
+ *reasons = cdp->points[seq].reasons;
+
+ if (type)
+ *type = cdp->points[seq].type;
+
+ if (san) {
+ san->data = cdp->points[seq].san.data;
+ san->size = cdp->points[seq].san.size;
+ }
+
+ return 0;
+}
+
+static
+int crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * san, unsigned int reasons)
+{
+ void *tmp;
+
+ if (unlikely(INT_ADD_OVERFLOW(cdp->size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ /* new dist point */
+ tmp = _gnutls_reallocarray(cdp->points, cdp->size + 1,
+ sizeof(cdp->points[0]));
+ if (tmp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ cdp->points = tmp;
+
+ cdp->points[cdp->size].type = type;
+ cdp->points[cdp->size].san.data = san->data;
+ cdp->points[cdp->size].san.size = san->size;
+ cdp->points[cdp->size].reasons = reasons;
+
+ cdp->size++;
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crl_dist_points_set:
+ * @cdp: The CRL distribution points
+ * @type: The type of the name (of %gnutls_subject_alt_names_t)
+ * @san: The point name data
+ * @reasons: Revocation reasons. An ORed sequence of flags from %gnutls_x509_crl_reason_flags_t.
+ *
+ * This function will store the specified CRL distribution point value
+ * the @cdp type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_crl_dist_points_set(gnutls_x509_crl_dist_points_t cdp,
+ gnutls_x509_subject_alt_name_t type,
+ const gnutls_datum_t * san,
+ unsigned int reasons)
+{
+ int ret;
+ gnutls_datum_t t_san;
+
+ ret = _gnutls_set_datum(&t_san, san->data, san->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = crl_dist_points_set(cdp, type, &t_san, reasons);
+ if (ret < 0) {
+ gnutls_free(t_san.data);
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_crl_dist_points:
+ * @ext: the DER encoded extension data
+ * @cdp: A pointer to an initialized CRL distribution points.
+ * @flags: should be zero
+ *
+ * This function will extract the CRL distribution points extension (2.5.29.31)
+ * and store it into the provided type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_crl_dist_points(const gnutls_datum_t * ext,
+ gnutls_x509_crl_dist_points_t cdp,
+ unsigned int flags)
+{
+ int result;
+ asn1_node c2 = NULL;
+ char name[MAX_NAME_SIZE];
+ int len, ret;
+ uint8_t reasons[2];
+ unsigned i, type, rflags, j;
+ gnutls_datum_t san = {NULL, 0};
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.CRLDistributionPoints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* Return the different names from the first CRLDistr. point.
+ * The whole thing is a mess.
+ */
+
+ i = 0;
+ do {
+ snprintf(name, sizeof(name), "?%u.reasons", (unsigned)i + 1);
+
+ len = sizeof(reasons);
+ result = asn1_read_value(c2, name, reasons, &len);
+
+ if (result != ASN1_VALUE_NOT_FOUND &&
+ result != ASN1_ELEMENT_NOT_FOUND &&
+ result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ break;
+ }
+
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND)
+ rflags = 0;
+ else
+ rflags = reasons[0] | (reasons[1] << 8);
+
+ snprintf(name, sizeof(name),
+ "?%u.distributionPoint.fullName", (unsigned)i + 1);
+
+ for (j=0;;j++) {
+ san.data = NULL;
+ san.size = 0;
+
+ ret =
+ _gnutls_parse_general_name2(c2, name, j, &san,
+ &type, 0);
+ if (j > 0
+ && ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ break;
+
+ ret = crl_dist_points_set(cdp, type, &san, rflags);
+ if (ret < 0)
+ break;
+ san.data = NULL; /* it is now in cdp */
+ }
+
+ i++;
+ } while (ret >= 0);
+
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ gnutls_free(san.data);
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_crl_dist_points:
+ * @cdp: A pointer to an initialized CRL distribution points.
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided policies, to a certificate policy
+ * DER encoded extension (2.5.29.31).
+ *
+ * The @ext data will be allocated using gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_crl_dist_points(gnutls_x509_crl_dist_points_t cdp,
+ gnutls_datum_t * ext)
+{
+ asn1_node c2 = NULL;
+ int result;
+ uint8_t reasons[2];
+ unsigned i;
+
+ result =
+ asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.CRLDistributionPoints", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ for (i = 0; i < cdp->size; i++) {
+
+ if (i == 0
+ || cdp->points[i].reasons != cdp->points[i - 1].reasons) {
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (cdp->points[i].reasons) {
+ reasons[0] = cdp->points[i].reasons & 0xff;
+ reasons[1] = cdp->points[i].reasons >> 8;
+
+ result =
+ asn1_write_value(c2, "?LAST.reasons",
+ reasons, 2);
+ } else {
+ result =
+ asn1_write_value(c2, "?LAST.reasons", NULL,
+ 0);
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ result =
+ asn1_write_value(c2, "?LAST.cRLIssuer", NULL, 0);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ /* When used as type CHOICE.
+ */
+ result =
+ asn1_write_value(c2, "?LAST.distributionPoint",
+ "fullName", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ result = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ result =
+ _gnutls_write_new_general_name(c2,
+ "?LAST.distributionPoint.fullName",
+ cdp->points[i].type,
+ cdp->points[i].san.data,
+ cdp->points[i].san.size);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ result = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return result;
+
+}
+
+struct gnutls_x509_aia_st {
+ struct {
+ gnutls_datum_t oid;
+ unsigned int san_type;
+ gnutls_datum_t san;
+ } *aia;
+ unsigned int size;
+};
+
+/**
+ * gnutls_x509_aia_init:
+ * @aia: The authority info access
+ *
+ * This function will initialize an authority info access type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aia_init(gnutls_x509_aia_t * aia)
+{
+ *aia = gnutls_calloc(1, sizeof(struct gnutls_x509_aia_st));
+ if (*aia == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_aia_deinit:
+ * @aia: The authority info access
+ *
+ * This function will deinitialize an authority info access type.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_aia_deinit(gnutls_x509_aia_t aia)
+{
+ unsigned i;
+
+ for (i = 0; i < aia->size; i++) {
+ gnutls_free(aia->aia[i].san.data);
+ gnutls_free(aia->aia[i].oid.data);
+ }
+ gnutls_free(aia->aia);
+ gnutls_free(aia);
+}
+
+/**
+ * gnutls_x509_aia_get:
+ * @aia: The authority info access
+ * @seq: specifies the sequence number of the access descriptor (0 for the first one, 1 for the second etc.)
+ * @oid: the type of available data; to be treated as constant.
+ * @san_type: Will hold the type of the name of %gnutls_subject_alt_names_t (may be null).
+ * @san: the access location name; to be treated as constant (may be null).
+ *
+ * This function reads from the Authority Information Access type.
+ *
+ * The @seq input parameter is used to indicate which member of the
+ * sequence the caller is interested in. The first member is 0, the
+ * second member 1 and so on. When the @seq value is out of bounds,
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ *
+ * Typically @oid is %GNUTLS_OID_AD_CAISSUERS or %GNUTLS_OID_AD_OCSP.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aia_get(gnutls_x509_aia_t aia, unsigned int seq,
+ gnutls_datum_t *oid,
+ unsigned *san_type,
+ gnutls_datum_t *san)
+{
+ if (seq >= aia->size)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if (san_type)
+ *san_type = aia->aia[seq].san_type;
+ if (san) {
+ san->data = aia->aia[seq].san.data;
+ san->size = aia->aia[seq].san.size;
+ }
+
+ if (oid) {
+ oid->data = aia->aia[seq].oid.data;
+ oid->size = aia->aia[seq].oid.size;
+ }
+
+ return 0;
+}
+
+int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, const gnutls_datum_t *san, unsigned raw)
+{
+ int ret;
+ if (type == GNUTLS_SAN_DNSNAME && !raw) {
+ ret = gnutls_idna_map((char*)san->data, san->size, out, 0);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ } else if (type == GNUTLS_SAN_RFC822NAME && !raw) {
+ ret = _gnutls_idna_email_map((char*)san->data, san->size, out);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ } else if (type == GNUTLS_SAN_URI && !raw) {
+ if (!_gnutls_str_is_print((char*)san->data, san->size)) {
+ _gnutls_debug_log("non-ASCII URIs are not supported\n");
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ } else {
+ ret = _gnutls_set_strdatum(out, san->data, san->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+ } else {
+ ret = _gnutls_set_strdatum(out, san->data, san->size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_aia_set:
+ * @aia: The authority info access
+ * @oid: the type of data.
+ * @san_type: The type of the name (of %gnutls_subject_alt_names_t)
+ * @san: The alternative name data
+ * @othername_oid: The object identifier if @san_type is %GNUTLS_SAN_OTHERNAME
+ *
+ * This function will store the specified alternative name in
+ * the @aia type.
+ *
+ * Typically the value for @oid should be %GNUTLS_OID_AD_OCSP, or
+ * %GNUTLS_OID_AD_CAISSUERS.
+ *
+ * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, and %GNUTLS_SAN_DNSNAME,
+ * are converted to ACE format when necessary.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_aia_set(gnutls_x509_aia_t aia,
+ const char *oid,
+ unsigned san_type,
+ const gnutls_datum_t * san)
+{
+ int ret;
+ void *tmp;
+ unsigned indx;
+
+ if (unlikely(INT_ADD_OVERFLOW(aia->size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ tmp = _gnutls_reallocarray(aia->aia, aia->size + 1, sizeof(aia->aia[0]));
+ if (tmp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ aia->aia = tmp;
+ indx = aia->size;
+
+ aia->aia[indx].san_type = san_type;
+ if (oid) {
+ aia->aia[indx].oid.data = (void*)gnutls_strdup(oid);
+ aia->aia[indx].oid.size = strlen(oid);
+ } else {
+ aia->aia[indx].oid.data = NULL;
+ aia->aia[indx].oid.size = 0;
+ }
+
+ ret = _gnutls_alt_name_process(&aia->aia[indx].san, san_type, san, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ aia->size++;
+
+ return 0;
+}
+
+
+static int parse_aia(asn1_node c2, gnutls_x509_aia_t aia)
+{
+ int len;
+ char nptr[MAX_NAME_SIZE];
+ int ret, result;
+ char tmpoid[MAX_OID_SIZE];
+ void * tmp;
+ unsigned i, indx;
+
+ for (i = 1;; i++) {
+ snprintf(nptr, sizeof(nptr), "?%u.accessMethod", i);
+
+ len = sizeof(tmpoid);
+ result = asn1_read_value(c2, nptr, tmpoid, &len);
+ if (result == ASN1_VALUE_NOT_FOUND
+ || result == ASN1_ELEMENT_NOT_FOUND) {
+ ret = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ break;
+ }
+
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ indx = aia->size;
+ if (unlikely(INT_ADD_OVERFLOW(aia->size, 1))) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ tmp = _gnutls_reallocarray(aia->aia, aia->size + 1,
+ sizeof(aia->aia[0]));
+ if (tmp == NULL) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ aia->aia = tmp;
+
+ snprintf(nptr, sizeof(nptr), "?%u.accessLocation", i);
+
+
+ ret = _gnutls_parse_general_name2(c2, nptr, -1, &aia->aia[indx].san,
+ &aia->aia[indx].san_type, 0);
+ if (ret < 0)
+ break;
+
+ /* we do the strdup after parsing to avoid a memory leak */
+ aia->aia[indx].oid.data = (void*)gnutls_strdup(tmpoid);
+ aia->aia[indx].oid.size = strlen(tmpoid);
+
+ aia->size++;
+
+ if (aia->aia[indx].oid.data == NULL) {
+ gnutls_assert();
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ }
+
+ assert(ret < 0);
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_aia:
+ * @ext: The DER-encoded extension data
+ * @aia: The authority info access
+ * @flags: should be zero
+ *
+ * This function extracts the Authority Information Access (AIA)
+ * extension from the provided DER-encoded data; see RFC 5280 section 4.2.2.1
+ * for more information on the extension. The
+ * AIA extension holds a sequence of AccessDescription (AD) data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_aia(const gnutls_datum_t * ext,
+ gnutls_x509_aia_t aia,
+ unsigned int flags)
+{
+ int ret;
+ asn1_node c2 = NULL;
+
+ if (ext->size == 0 || ext->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.AuthorityInfoAccessSyntax", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = parse_aia(c2, aia);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_ext_export_aia:
+ * @aia: The authority info access
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will DER encode the Authority Information Access (AIA)
+ * extension; see RFC 5280 section 4.2.2.1 for more information on the
+ * extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_aia(gnutls_x509_aia_t aia,
+ gnutls_datum_t * ext)
+{
+ int ret, result;
+ asn1_node c2 = NULL;
+ unsigned int i;
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.AuthorityInfoAccessSyntax", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ /* 1. create a new element.
+ */
+ for (i=0;i<aia->size;i++) {
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* 2. Add the OID.
+ */
+ result = asn1_write_value(c2, "?LAST.accessMethod", aia->aia[i].oid.data, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_write_general_name(c2,
+ "?LAST.accessLocation",
+ aia->aia[i].san_type,
+ aia->aia[i].san.data,
+ aia->aia[i].san.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+
+struct gnutls_x509_key_purposes_st {
+ gnutls_datum_t oid[MAX_ENTRIES];
+ unsigned int size;
+};
+
+/**
+ * gnutls_subject_alt_names_init:
+ * @p: The key purposes
+ *
+ * This function will initialize an alternative names type.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_key_purpose_init(gnutls_x509_key_purposes_t * p)
+{
+ *p = gnutls_calloc(1, sizeof(struct gnutls_x509_key_purposes_st));
+ if (*p == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ return 0;
+}
+
+static void key_purposes_deinit(gnutls_x509_key_purposes_t p)
+{
+ unsigned int i;
+
+ for (i = 0; i < p->size; i++) {
+ gnutls_free(p->oid[i].data);
+ }
+}
+
+/**
+ * gnutls_x509_key_purpose_deinit:
+ * @p: The key purposes
+ *
+ * This function will deinitialize a key purposes type.
+ *
+ * Since: 3.3.0
+ **/
+void gnutls_x509_key_purpose_deinit(gnutls_x509_key_purposes_t p)
+{
+ key_purposes_deinit(p);
+ gnutls_free(p);
+}
+
+/**
+ * gnutls_x509_key_purpose_set:
+ * @p: The key purposes
+ * @oid: The object identifier of the key purpose
+ *
+ * This function will store the specified key purpose in the
+ * purposes.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0), otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_key_purpose_set(gnutls_x509_key_purposes_t p, const char *oid)
+{
+ if (p->size + 1 > MAX_ENTRIES)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ p->oid[p->size].data = (void*)gnutls_strdup(oid);
+ if (p->oid[p->size].data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ p->oid[p->size].size = strlen(oid);
+ p->size++;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_key_purpose_get:
+ * @p: The key purposes
+ * @idx: The index of the key purpose to retrieve
+ * @oid: Will hold the object identifier of the key purpose (to be treated as constant)
+ *
+ * This function will retrieve the specified by the index key purpose in the
+ * purposes type. The object identifier will be a null terminated string.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ * if the index is out of bounds, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_key_purpose_get(gnutls_x509_key_purposes_t p, unsigned idx, gnutls_datum_t *oid)
+{
+ if (idx >= p->size)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ oid->data = p->oid[idx].data;
+ oid->size = p->oid[idx].size;
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_key_purposes:
+ * @ext: The DER-encoded extension data
+ * @p: The key purposes
+ * @flags: should be zero
+ *
+ * This function will extract the key purposes in the provided DER-encoded
+ * ExtKeyUsageSyntax PKIX extension, to a %gnutls_x509_key_purposes_t type.
+ * The data must be initialized.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_import_key_purposes(const gnutls_datum_t * ext,
+ gnutls_x509_key_purposes_t p,
+ unsigned int flags)
+{
+ char tmpstr[MAX_NAME_SIZE];
+ int result, ret;
+ asn1_node c2 = NULL;
+ gnutls_datum_t oid = {NULL, 0};
+ unsigned i;
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ key_purposes_deinit(p);
+ i = 0;
+ p->size = 0;
+
+ for (;i<MAX_ENTRIES;i++) {
+ /* create a string like "?1"
+ */
+ snprintf(tmpstr, sizeof(tmpstr), "?%u", i+1);
+
+ ret = _gnutls_x509_read_value(c2, tmpstr, &oid);
+ if (ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ break;
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ p->oid[i].data = oid.data;
+ p->oid[i].size = oid.size;
+
+ oid.data = NULL;
+ oid.size = 0;
+ p->size++;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_free(oid.data);
+ asn1_delete_structure(&c2);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_ext_export_key_purposes:
+ * @p: The key purposes
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the key purposes type to a
+ * DER-encoded PKIX ExtKeyUsageSyntax (2.5.29.37) extension. The output data in
+ * @ext will be allocated using gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int gnutls_x509_ext_export_key_purposes(gnutls_x509_key_purposes_t p,
+ gnutls_datum_t * ext)
+{
+ int result, ret;
+ asn1_node c2 = NULL;
+ unsigned i;
+
+ result = asn1_create_element
+ (_gnutls_get_pkix(), "PKIX1.ExtKeyUsageSyntax", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ /* generate the extension.
+ */
+ for (i=0;i<p->size;i++) {
+ /* 1. create a new element.
+ */
+ result = asn1_write_value(c2, "", "NEW", 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ /* 2. Add the OID.
+ */
+ result = asn1_write_value(c2, "?LAST", p->oid[i].data, 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_ext_deinit:
+ * @ext: The extensions structure
+ *
+ * This function will deinitialize an extensions structure.
+ *
+ * Since: 3.3.8
+ **/
+void gnutls_x509_ext_deinit(gnutls_x509_ext_st *ext)
+{
+ gnutls_free(ext->oid);
+ gnutls_free(ext->data.data);
+}
+
+int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out)
+{
+ asn1_node c2 = NULL;
+ char str_critical[10];
+ char oid[MAX_OID_SIZE];
+ int result, len, ret;
+
+ memset(out, 0, sizeof(*out));
+
+ /* decode der */
+ result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.Extension", &c2);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result = _asn1_strict_der_decode(&c2, der->data, der->size, NULL);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ len = sizeof(oid)-1;
+ result = asn1_read_value(c2, "extnID", oid, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ len = sizeof(str_critical)-1;
+ result = asn1_read_value(c2, "critical", str_critical, &len);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(result);
+ goto cleanup;
+ }
+
+ if (str_critical[0] == 'T')
+ out->critical = 1;
+ else
+ out->critical = 0;
+
+ ret = _gnutls_x509_read_value(c2, "extnValue", &out->data);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE || ret == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) {
+ out->data.data = NULL;
+ out->data.size = 0;
+ } else if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ out->oid = gnutls_strdup(oid);
+ if (out->oid == NULL) {
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto fail;
+ }
+
+ ret = 0;
+ goto cleanup;
+ fail:
+ memset(out, 0, sizeof(*out));
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+
+}
+
+/* flags can be zero or GNUTLS_EXT_FLAG_APPEND
+ */
+static int parse_tlsfeatures(asn1_node c2, gnutls_x509_tlsfeatures_t f, unsigned flags)
+{
+ char nptr[MAX_NAME_SIZE];
+ int result;
+ unsigned i, indx, j;
+ unsigned int feature;
+
+ if (!(flags & GNUTLS_EXT_FLAG_APPEND))
+ f->size = 0;
+
+ for (i = 1;; i++) {
+ unsigned skip = 0;
+ snprintf(nptr, sizeof(nptr), "?%u", i);
+
+ result = _gnutls_x509_read_uint(c2, nptr, &feature);
+
+ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) {
+ break;
+ }
+ else if (result != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (feature > UINT16_MAX) {
+ gnutls_assert();
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ /* skip duplicates */
+ for (j=0;j<f->size;j++) {
+ if (f->feature[j] == feature) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (!skip) {
+ if (f->size >= sizeof(f->feature)/sizeof(f->feature[0])) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ indx = f->size;
+ f->feature[indx] = feature;
+ f->size++;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_import_tlsfeatures:
+ * @ext: The DER-encoded extension data
+ * @f: The features structure
+ * @flags: zero or %GNUTLS_EXT_FLAG_APPEND
+ *
+ * This function will export the features in the provided DER-encoded
+ * TLS Features PKIX extension, to a %gnutls_x509_tlsfeatures_t type. @f
+ * must be initialized.
+ *
+ * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND,
+ * then if the @features structure is empty this function will behave
+ * identically as if the flag was not set. Otherwise if there are elements
+ * in the @features structure then they will be merged with.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_ext_import_tlsfeatures(const gnutls_datum_t * ext,
+ gnutls_x509_tlsfeatures_t f,
+ unsigned int flags)
+{
+ int ret;
+ asn1_node c2 = NULL;
+
+ if (ext->size == 0 || ext->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ret = asn1_create_element(_gnutls_get_pkix(),
+ "PKIX1.TlsFeatures", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ ret = _asn1_strict_der_decode(&c2, ext->data, ext->size, NULL);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = parse_tlsfeatures(c2, f, flags);
+ if (ret < 0) {
+ gnutls_assert();
+ }
+
+ cleanup:
+ asn1_delete_structure(&c2);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_ext_export_tlsfeatures:
+ * @f: The features structure
+ * @ext: The DER-encoded extension data; must be freed using gnutls_free().
+ *
+ * This function will convert the provided TLS features structure structure to a
+ * DER-encoded TLS features PKIX extension. The output data in @ext will be allocated using
+ * gnutls_malloc().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_ext_export_tlsfeatures(gnutls_x509_tlsfeatures_t f,
+ gnutls_datum_t * ext)
+{
+ if (f == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ asn1_node c2 = NULL;
+ int ret;
+ unsigned i;
+
+ ret = asn1_create_element(_gnutls_get_pkix(), "PKIX1.TlsFeatures", &c2);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ for (i = 0; i < f->size; ++i) {
+
+ ret = asn1_write_value(c2, "", "NEW", 1);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ ret = _gnutls_asn2err(ret);
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_write_uint32(c2, "?LAST", f->feature[i]);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = _gnutls_x509_der_encode(c2, "", ext, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ asn1_delete_structure(&c2);
+ return ret;
+}
+
+/**
+ * gnutls_x509_tlsfeatures_add:
+ * @f: The TLS features
+ * @feature: The feature to add
+ *
+ * This function will append a feature to the X.509 TLS features
+ * extension structure.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int gnutls_x509_tlsfeatures_add(gnutls_x509_tlsfeatures_t f, unsigned int feature)
+{
+ if (f == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (feature > UINT16_MAX)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (f->size >= sizeof(f->feature)/sizeof(f->feature[0]))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ f->feature[f->size++] = feature;
+
+ return 0;
+}
+
+#define SCT_V1_LOGID_SIZE 32
+struct ct_sct_st {
+ int version;
+ uint8_t logid[SCT_V1_LOGID_SIZE];
+ uint64_t timestamp;
+ gnutls_sign_algorithm_t sigalg;
+ gnutls_datum_t signature;
+};
+
+struct gnutls_x509_ct_scts_st {
+ struct ct_sct_st *scts;
+ size_t size;
+};
+
+static void _gnutls_free_scts(struct gnutls_x509_ct_scts_st *scts)
+{
+ for (size_t i = 0; i < scts->size; i++)
+ _gnutls_free_datum(&scts->scts[i].signature);
+ gnutls_free(scts->scts);
+ scts->size = 0;
+}
+
+/**
+ * gnutls_x509_ext_ct_scts_init:
+ * @scts: The SCT list
+ *
+ * This function will initialize a Certificate Transparency SCT list.
+ *
+ * Returns: %GNUTLS_E_SUCCESS (0) on success, otherwise a negative error value.
+ **/
+int gnutls_x509_ext_ct_scts_init(gnutls_x509_ct_scts_t * scts)
+{
+ *scts = gnutls_calloc(1, sizeof(struct gnutls_x509_ct_scts_st));
+ if (*scts == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_ct_scts_deinit:
+ * @scts: The SCT list
+ *
+ * This function will deinitialize a Certificate Transparency SCT list.
+ **/
+void gnutls_x509_ext_ct_scts_deinit(gnutls_x509_ct_scts_t scts)
+{
+ _gnutls_free_scts(scts);
+ gnutls_free(scts);
+}
+
+struct sct_sign_algorithm_st {
+ uint8_t codepoint[2];
+ gnutls_sign_algorithm_t sign_algo;
+};
+
+static const struct sct_sign_algorithm_st algos[] = {
+ {
+ .codepoint = { 0x01, 0x01 },
+ .sign_algo = GNUTLS_SIGN_RSA_MD5
+ },
+ {
+ .codepoint = { 0x02, 0x01 },
+ .sign_algo = GNUTLS_SIGN_RSA_SHA1
+ },
+ {
+ .codepoint = { 0x03, 0x01 },
+ .sign_algo = GNUTLS_SIGN_RSA_SHA224
+ },
+ {
+ .codepoint = { 0x04, 0x01 },
+ .sign_algo = GNUTLS_SIGN_RSA_SHA256
+ },
+ {
+ .codepoint = { 0x05, 0x01 },
+ .sign_algo = GNUTLS_SIGN_RSA_SHA384
+ },
+ {
+ .codepoint = { 0x06, 0x01 },
+ .sign_algo = GNUTLS_SIGN_RSA_SHA512,
+ },
+ {
+ .codepoint = { 0x02, 0x02 },
+ .sign_algo = GNUTLS_SIGN_DSA_SHA1
+ },
+ {
+ .codepoint = { 0x03, 0x02 },
+ .sign_algo = GNUTLS_SIGN_DSA_SHA224
+ },
+ {
+ .codepoint = { 0x04, 0x02 },
+ .sign_algo = GNUTLS_SIGN_DSA_SHA256
+ },
+ {
+ .codepoint = { 0x05, 0x02 },
+ .sign_algo = GNUTLS_SIGN_DSA_SHA384
+ },
+ {
+ .codepoint = { 0x06, 0x02 },
+ .sign_algo = GNUTLS_SIGN_DSA_SHA512,
+ },
+ {
+ .codepoint = { 0x02, 0x03 },
+ .sign_algo = GNUTLS_SIGN_ECDSA_SHA1
+ },
+ {
+ .codepoint = { 0x03, 0x03 },
+ .sign_algo = GNUTLS_SIGN_ECDSA_SHA224
+ },
+ {
+ .codepoint = { 0x04, 0x03 },
+ .sign_algo = GNUTLS_SIGN_ECDSA_SHA256
+ },
+ {
+ .codepoint = { 0x05, 0x03 },
+ .sign_algo = GNUTLS_SIGN_ECDSA_SHA384
+ },
+ {
+ .codepoint = { 0x06, 0x03 },
+ .sign_algo = GNUTLS_SIGN_ECDSA_SHA512,
+ }
+};
+
+static gnutls_sign_algorithm_t get_sigalg(uint8_t hash_algo, uint8_t sig_algo)
+{
+ const struct sct_sign_algorithm_st *algo;
+ size_t i, num_algos = sizeof(algos) / sizeof(algos[0]);
+
+ if (hash_algo == 0 || sig_algo == 0)
+ return GNUTLS_SIGN_UNKNOWN;
+
+ for (i = 0; i < num_algos; i++) {
+ algo = &algos[i];
+ if (algo->codepoint[0] == hash_algo && algo->codepoint[1] == sig_algo)
+ break;
+ }
+
+ if (i == num_algos)
+ return GNUTLS_SIGN_UNKNOWN;
+
+ return algo->sign_algo;
+}
+
+static int write_sigalg(gnutls_sign_algorithm_t sigalg, uint8_t out[])
+{
+ const struct sct_sign_algorithm_st *algo;
+ size_t i, num_algos = sizeof(algos) / sizeof(algos[0]);
+
+ for (i = 0; i < num_algos; i++) {
+ algo = &algos[i];
+ if (algo->sign_algo == sigalg)
+ break;
+ }
+
+ if (i == num_algos)
+ return GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM;
+
+ out[0] = algo->codepoint[0];
+ out[1] = algo->codepoint[1];
+ return 0;
+}
+
+static int _gnutls_parse_ct_sct(uint8_t *ptr, uint16_t length,
+ struct ct_sct_st *sct)
+{
+ uint16_t sig_length;
+ uint8_t hash_algo, sig_algo;
+
+ sct->signature.size = 0;
+ sct->signature.data = NULL;
+
+ DECR_LENGTH_RET(length, 1, GNUTLS_E_PREMATURE_TERMINATION);
+ sct->version = (int) *ptr;
+ ptr++;
+
+ /* LogID
+ * In version 1, it has a fixed length of 32 bytes.
+ */
+ DECR_LENGTH_RET(length, SCT_V1_LOGID_SIZE, GNUTLS_E_PREMATURE_TERMINATION);
+ memcpy(sct->logid, ptr, SCT_V1_LOGID_SIZE);
+ ptr += SCT_V1_LOGID_SIZE;
+
+ /* Timestamp */
+ DECR_LENGTH_RET(length, sizeof(uint64_t), GNUTLS_E_PREMATURE_TERMINATION);
+ sct->timestamp = (uint64_t) _gnutls_read_uint64(ptr);
+ ptr += sizeof(uint64_t);
+
+ /*
+ * There are no extensions defined in SCT v1.
+ * Check that there are actually no extensions - the following two bytes should be zero.
+ */
+ DECR_LENGTH_RET(length, 2, GNUTLS_E_PREMATURE_TERMINATION);
+ if (*ptr != 0 || *(ptr+1) != 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH);
+ ptr += 2;
+
+ /*
+ * Hash and signature algorithms, modeled after
+ * SignatureAndHashAlgorithm structure, as defined in
+ * RFC 5246, section 7.4.1.4.1.
+ * We take both values separately (hash and signature),
+ * and return them as a gnutls_sign_algorithm_t enum value.
+ */
+ DECR_LENGTH_RET(length, 2, GNUTLS_E_PREMATURE_TERMINATION);
+ hash_algo = *ptr++;
+ sig_algo = *ptr++;
+
+ sct->sigalg = get_sigalg(hash_algo, sig_algo);
+ if (sct->sigalg == GNUTLS_SIGN_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+
+ /* Signature, length and content */
+ DECR_LENGTH_RET(length, sizeof(uint16_t), GNUTLS_E_PREMATURE_TERMINATION);
+ sig_length = _gnutls_read_uint16(ptr);
+ ptr += sizeof(uint16_t);
+ if (sig_length == 0)
+ return gnutls_assert_val(GNUTLS_E_PREMATURE_TERMINATION);
+
+ /* Remaining length should be sig_length at this point.
+ * If not, that means there is more data than what the length field said it was,
+ * and hence we must treat this as an error. */
+ if (length != sig_length)
+ return gnutls_assert_val(GNUTLS_E_ASN1_DER_OVERFLOW);
+
+ if (_gnutls_set_datum(&sct->signature, ptr, sig_length) < 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ return 0;
+}
+
+static int _gnutls_ct_sct_add(struct ct_sct_st *sct,
+ struct ct_sct_st **scts, size_t *size)
+{
+ struct ct_sct_st *new_scts;
+
+ new_scts = _gnutls_reallocarray(*scts, *size + 1, sizeof(struct ct_sct_st));
+ if (new_scts == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(&new_scts[*size], sct, sizeof(struct ct_sct_st));
+ (*size)++;
+ *scts = new_scts;
+
+ return 0;
+}
+
+static int _gnutls_export_ct_v1_sct(gnutls_buffer_st *buf,
+ const struct ct_sct_st *sct)
+{
+ int ret;
+ uint8_t tstamp_out[8], sigalg[2];
+ /* There are no extensions defined for v1 */
+ const uint8_t extensions[2] = { 0x00, 0x00 };
+ size_t length_offset;
+
+ /* Length field; filled later */
+ length_offset = buf->length;
+ if ((ret = _gnutls_buffer_append_prefix(buf, 16, 0)) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Version */
+ if ((ret = _gnutls_buffer_append_data(buf,
+ &sct->version, sizeof(uint8_t))) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Log ID - has a fixed 32-byte size in version 1 */
+ if ((ret = _gnutls_buffer_append_data(buf,
+ sct->logid, SCT_V1_LOGID_SIZE)) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Timestamp */
+ _gnutls_write_uint64(sct->timestamp, tstamp_out);
+ if ((ret = _gnutls_buffer_append_data(buf,
+ tstamp_out, sizeof(tstamp_out))) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Extensions */
+ if ((ret = _gnutls_buffer_append_data(buf,
+ extensions, sizeof(extensions))) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Hash and signature algorithms */
+ if ((ret = write_sigalg(sct->sigalg, sigalg)) < 0)
+ return gnutls_assert_val(ret);
+
+ if ((ret = _gnutls_buffer_append_data(buf,
+ sigalg, sizeof(sigalg))) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Signature */
+ if ((ret = _gnutls_buffer_append_data_prefix(buf, 16,
+ sct->signature.data, sct->signature.size)) < 0)
+ return gnutls_assert_val(ret);
+
+ /* Fill the length */
+ _gnutls_write_uint16(buf->length - length_offset - 2,
+ buf->data + length_offset);
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_ext_ct_import_scts:
+ * @ext: a DER-encoded extension
+ * @scts: The SCT list
+ * @flags: should be zero
+ *
+ * This function will read a SignedCertificateTimestampList structure
+ * from the DER data of the X.509 Certificate Transparency SCT extension
+ * (OID 1.3.6.1.4.1.11129.2.4.2).
+ *
+ * The list of SCTs (Signed Certificate Timestamps) is placed on @scts,
+ * which must be previously initialized with gnutls_x509_ext_ct_scts_init().
+ *
+ * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error value.
+ **/
+int gnutls_x509_ext_ct_import_scts(const gnutls_datum_t *ext, gnutls_x509_ct_scts_t scts,
+ unsigned int flags)
+{
+ int retval;
+ uint8_t *ptr;
+ uint16_t length, sct_length;
+ struct ct_sct_st sct;
+ gnutls_datum_t scts_content;
+
+ if (flags != 0)
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+
+ retval =
+ _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING,
+ ext->data, ext->size, &scts_content,
+ 0);
+ if (retval < 0)
+ return gnutls_assert_val(retval);
+
+ if (scts_content.size < 2) {
+ gnutls_free(scts_content.data);
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ length = _gnutls_read_uint16(scts_content.data);
+ if (length < 4) {
+ gnutls_free(scts_content.data);
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ ptr = &scts_content.data[2];
+ while (length > 0) {
+ if (length < 2)
+ break;
+
+ sct_length = _gnutls_read_uint16(ptr);
+ if (sct_length == 0 || sct_length > length)
+ break;
+
+ ptr += sizeof(uint16_t);
+ length -= sizeof(uint16_t);
+
+ /*
+ * _gnutls_parse_ct_sct() will try to read exactly sct_length bytes,
+ * returning an error if it can't
+ */
+ if (_gnutls_parse_ct_sct(ptr, sct_length, &sct) < 0)
+ break;
+ if (_gnutls_ct_sct_add(&sct, &scts->scts, &scts->size) < 0)
+ break;
+
+ ptr += sct_length;
+ length -= sct_length;
+ }
+
+ _gnutls_free_datum(&scts_content);
+
+ if (length > 0) {
+ gnutls_assert();
+ _gnutls_free_scts(scts);
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_x509_ext_ct_export_scts:
+ * @scts: An initialized SCT list
+ * @ext: The DER-encoded extension data; must be freed with gnutls_free()
+ *
+ * This function will convert the provided list of SCTs to a DER-encoded
+ * SignedCertificateTimestampList extension (1.3.6.1.4.1.11129.2.4.2).
+ * The output data in @ext will be allocated using gnutls_malloc().
+ *
+ * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error value.
+ **/
+int gnutls_x509_ext_ct_export_scts(const gnutls_x509_ct_scts_t scts, gnutls_datum_t *ext)
+{
+ int ret;
+ gnutls_buffer_st buf;
+
+ _gnutls_buffer_init(&buf);
+
+ /* Start with the length of the whole string; the actual
+ * length is filled later */
+ _gnutls_buffer_append_prefix(&buf, 16, 0);
+
+ for (size_t i = 0; i < scts->size; i++) {
+ if ((ret = _gnutls_export_ct_v1_sct(&buf,
+ &scts->scts[i])) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* Fill the length */
+ _gnutls_write_uint16(buf.length - 2, buf.data);
+
+ /* DER-encode the whole thing as an opaque OCTET STRING, as the spec mandates */
+ ret = _gnutls_x509_encode_string(
+ ASN1_ETYPE_OCTET_STRING,
+ buf.data, buf.length,
+ ext);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = GNUTLS_E_SUCCESS;
+
+cleanup:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+/**
+ * gnutls_x509_ct_sct_get_version:
+ * @scts: A list of SCTs
+ * @idx: The index of the target SCT in the list
+ * @version_out: The version of the target SCT.
+ *
+ * This function obtains the version of the SCT at the given position
+ * in the SCT list.
+ *
+ * The version of that SCT will be placed on @version_out.
+ *
+ * Return : %GNUTLS_E_SUCCESS (0) is returned on success,
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if @idx exceeds the number of SCTs in the list
+ * and %GNUTLS_E_INVALID_REQUEST if the SCT's version is different than 1, as that's currently
+ * the only defined version.
+ **/
+int gnutls_x509_ct_sct_get_version(gnutls_x509_ct_scts_t scts, unsigned idx,
+ unsigned int *version_out)
+{
+ int version;
+
+ if (idx >= scts->size)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ /*
+ * Currently, only version 1 SCTs are defined (RFC 6962).
+ * A version 1 SCT has actually the value 0 in the 'version' field.
+ */
+ version = scts->scts[idx].version;
+ if (version != 0 || version_out == NULL)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ *version_out = 1;
+ return GNUTLS_E_SUCCESS;
+}
+
+/**
+ * gnutls_x509_ct_sct_get:
+ * @scts: A list of SCTs
+ * @idx: The index of the target SCT in the list
+ * @timestamp: The timestamp of the SCT
+ * @logid: The LogID field of the SCT; must be freed with gnutls_free()
+ * @sigalg: The signature algorithm
+ * @signature: The signature of the SCT; must be freed with gnutls_free()
+ *
+ * This function will return a specific SCT (Signed Certificate Timestamp)
+ * stored in the SCT list @scts.
+ *
+ * The datums holding the SCT's LogId and signature will be allocated
+ * using gnutls_malloc().
+ *
+ * Returns: %GNUTLS_E_SUCCESS (0) will be returned on success,
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE if @idx exceeds the number of SCTs in the list
+ * or a negative error value.
+ **/
+int gnutls_x509_ct_sct_get(const gnutls_x509_ct_scts_t scts, unsigned idx,
+ time_t *timestamp,
+ gnutls_datum_t *logid,
+ gnutls_sign_algorithm_t *sigalg,
+ gnutls_datum_t *signature)
+{
+ int retval = 0;
+ struct ct_sct_st *sct;
+
+ if (idx >= scts->size)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ sct = &scts->scts[idx];
+ if (sct->version != 0)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ if (signature) {
+ retval = _gnutls_set_datum(signature,
+ sct->signature.data,
+ sct->signature.size);
+ if (retval < 0)
+ return retval;
+ }
+
+ if (logid) {
+ retval = _gnutls_set_datum(logid,
+ sct->logid,
+ SCT_V1_LOGID_SIZE);
+ if (retval < 0) {
+ _gnutls_free_datum(signature);
+ return retval;
+ }
+ }
+
+ if (timestamp)
+ *timestamp = sct->timestamp / 1000;
+
+ if (sigalg)
+ *sigalg = sct->sigalg;
+
+ return GNUTLS_E_SUCCESS;
+}
diff --git a/lib/x509/x509_ext_int.h b/lib/x509/x509_ext_int.h
new file mode 100644
index 0000000..2e3f162
--- /dev/null
+++ b/lib/x509/x509_ext_int.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014-2016 Free Software Foundation
+ * Copyright (C) 2014-2016 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_X509_EXT_INT_H
+#define GNUTLS_LIB_X509_X509_EXT_INT_H
+
+#include "gnutls_int.h"
+struct name_st {
+ unsigned int type;
+ gnutls_datum_t san;
+ gnutls_datum_t othername_oid;
+};
+
+int _gnutls_alt_name_process(gnutls_datum_t *out, unsigned type, const gnutls_datum_t *san, unsigned raw);
+
+#endif /* GNUTLS_LIB_X509_X509_EXT_INT_H */
diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h
new file mode 100644
index 0000000..acbc185
--- /dev/null
+++ b/lib/x509/x509_int.h
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2003-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_X509_INT_H
+#define GNUTLS_LIB_X509_X509_INT_H
+
+#include <gnutls/x509.h>
+#include <gnutls/x509-ext.h>
+#include <gnutls/abstract.h>
+
+#include <libtasn1.h>
+
+#define MAX_CRQ_EXTENSIONS_SIZE 8*1024
+#define MAX_OID_SIZE 128
+#define MAX_KEY_ID_SIZE 128
+#define MAX_SALT_SIZE 256
+#define MAX_NAME_SIZE (3*ASN1_MAX_NAME_SIZE)
+
+#define HASH_OID_SHA1 "1.3.14.3.2.26"
+#define HASH_OID_MD5 "1.2.840.113549.2.5"
+#define HASH_OID_MD2 "1.2.840.113549.2.2"
+#define HASH_OID_RMD160 "1.3.36.3.2.1"
+#define HASH_OID_SHA224 "2.16.840.1.101.3.4.2.4"
+#define HASH_OID_SHA256 "2.16.840.1.101.3.4.2.1"
+#define HASH_OID_SHA384 "2.16.840.1.101.3.4.2.2"
+#define HASH_OID_SHA512 "2.16.840.1.101.3.4.2.3"
+#define HASH_OID_SHA3_224 "2.16.840.1.101.3.4.2.7"
+#define HASH_OID_SHA3_256 "2.16.840.1.101.3.4.2.8"
+#define HASH_OID_SHA3_384 "2.16.840.1.101.3.4.2.9"
+#define HASH_OID_SHA3_512 "2.16.840.1.101.3.4.2.10"
+#define HASH_OID_SHAKE_128 "2.16.840.1.101.3.4.2.11"
+#define HASH_OID_SHAKE_256 "2.16.840.1.101.3.4.2.12"
+#define HASH_OID_GOST_R_3411_94 "1.2.643.2.2.9"
+#define HASH_OID_STREEBOG_256 "1.2.643.7.1.1.2.2"
+#define HASH_OID_STREEBOG_512 "1.2.643.7.1.1.2.3"
+
+#define HASH_OID_GOST_R_3411_94_CRYPTOPRO_PARAMS "1.2.643.2.2.30.1"
+
+/* from rfc8479 */
+#define OID_ATTR_PROV_SEED "1.3.6.1.4.1.2312.18.8.1"
+
+struct gnutls_x509_crl_iter {
+ /* This is used to optimize reads by gnutls_x509_crl_iter_crt_serial() */
+ asn1_node rcache;
+ unsigned rcache_idx;
+};
+
+typedef struct gnutls_x509_crl_int {
+ asn1_node crl;
+
+ unsigned expanded;
+ /* This is used to optimize reads by gnutls_x509_crl_get_crt_serial2() */
+ asn1_node rcache;
+ unsigned rcache_idx;
+ int use_extensions;
+
+ gnutls_datum_t der;
+ gnutls_datum_t raw_issuer_dn;
+} gnutls_x509_crl_int;
+
+typedef struct gnutls_x509_dn_st {
+ asn1_node asn;
+} gnutls_x509_dn_st;
+
+typedef struct gnutls_x509_crt_int {
+ asn1_node cert;
+ int use_extensions;
+ unsigned expanded; /* a certificate has been expanded */
+ unsigned modified; /* the cached values below may no longer be valid */
+ unsigned flags;
+
+ struct pin_info_st pin;
+
+ /* These two cached values allow fast calls to
+ * get_raw_*_dn(). */
+ gnutls_datum_t raw_dn;
+ gnutls_datum_t raw_issuer_dn;
+ gnutls_datum_t raw_spki;
+
+ gnutls_datum_t der;
+
+ /* this cached value allows fast access to alt names */
+ gnutls_subject_alt_names_t san;
+ gnutls_subject_alt_names_t ian;
+
+ /* backwards compatibility for gnutls_x509_crt_get_subject()
+ * and gnutls_x509_crt_get_issuer() */
+ gnutls_x509_dn_st dn;
+ gnutls_x509_dn_st idn;
+} gnutls_x509_crt_int;
+
+#define MODIFIED(crt) crt->modified=1
+
+typedef struct gnutls_x509_crq_int {
+ asn1_node crq;
+} gnutls_x509_crq_int;
+
+typedef struct gnutls_pkcs7_attrs_st {
+ char *oid;
+ gnutls_datum_t data;
+ struct gnutls_pkcs7_attrs_st *next;
+} gnutls_pkcs7_attrs_st;
+
+typedef struct gnutls_pkcs7_int {
+ asn1_node pkcs7;
+
+ char encap_data_oid[MAX_OID_SIZE];
+
+ gnutls_datum_t der_signed_data;
+ asn1_node signed_data;
+ unsigned expanded;
+} gnutls_pkcs7_int;
+
+struct pbkdf2_params {
+ uint8_t salt[MAX_SALT_SIZE];
+ int salt_size;
+ unsigned iter_count;
+ unsigned key_size;
+ gnutls_mac_algorithm_t mac;
+};
+
+typedef struct gnutls_x509_privkey_int {
+ /* the size of params depends on the public
+ * key algorithm
+ */
+ gnutls_pk_params_st params;
+
+ unsigned expanded;
+ unsigned flags;
+
+ asn1_node key;
+ struct pin_info_st pin;
+} gnutls_x509_privkey_int;
+
+int _gnutls_x509_crt_cpy(gnutls_x509_crt_t dest, gnutls_x509_crt_t src);
+
+int _gnutls_x509_compare_raw_dn(const gnutls_datum_t * dn1,
+ const gnutls_datum_t * dn2);
+
+int _gnutls_x509_crl_cpy(gnutls_x509_crl_t dest, gnutls_x509_crl_t src);
+int _gnutls_x509_crl_get_raw_issuer_dn(gnutls_x509_crl_t crl,
+ gnutls_datum_t * dn);
+
+/* sign.c */
+int _gnutls_x509_get_tbs(asn1_node cert, const char *tbs_name,
+ gnutls_datum_t * tbs);
+int _gnutls_x509_pkix_sign(asn1_node src, const char *src_name,
+ gnutls_digest_algorithm_t,
+ unsigned int flags,
+ gnutls_x509_crt_t issuer,
+ gnutls_privkey_t issuer_key);
+int _gnutls_x509_crt_get_spki_params(gnutls_x509_crt_t issuer,
+ const gnutls_x509_spki_st *key_params,
+ gnutls_x509_spki_st *params);
+
+#define map_errs_to_zero(x) ((x)<0?0:(x))
+
+/* dn.c */
+#define OID_X520_COUNTRY_NAME "2.5.4.6"
+#define OID_X520_ORGANIZATION_NAME "2.5.4.10"
+#define OID_X520_ORGANIZATIONAL_UNIT_NAME "2.5.4.11"
+#define OID_X520_COMMON_NAME "2.5.4.3"
+#define OID_X520_LOCALITY_NAME "2.5.4.7"
+#define OID_X520_STATE_OR_PROVINCE_NAME "2.5.4.8"
+#define OID_LDAP_DC "0.9.2342.19200300.100.1.25"
+#define OID_LDAP_UID "0.9.2342.19200300.100.1.1"
+#define OID_PKCS9_EMAIL "1.2.840.113549.1.9.1"
+
+int _gnutls_x509_parse_dn(asn1_node asn1_struct,
+ const char *asn1_rdn_name, char *buf,
+ size_t * sizeof_buf,
+ unsigned flags);
+
+int
+_gnutls_x509_get_dn(asn1_node asn1_struct,
+ const char *asn1_rdn_name, gnutls_datum_t * dn,
+ unsigned flags);
+
+int
+_gnutls_x509_parse_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name,
+ const char *given_oid, int indx,
+ unsigned int raw_flag, gnutls_datum_t * out);
+
+int _gnutls_x509_set_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name, const char *oid,
+ int raw_flag, const char *name,
+ int sizeof_name);
+
+int _gnutls_x509_get_dn_oid(asn1_node asn1_struct,
+ const char *asn1_rdn_name,
+ int indx, void *_oid, size_t * sizeof_oid);
+
+int _gnutls_encode_othername_data(unsigned flags, const void *data, unsigned data_size, gnutls_datum_t *output);
+
+int _gnutls_parse_general_name(asn1_node src, const char *src_name,
+ int seq, void *name, size_t * name_size,
+ unsigned int *ret_type, int othername_oid);
+
+int
+_gnutls_parse_general_name2(asn1_node src, const char *src_name,
+ int seq, gnutls_datum_t *dname,
+ unsigned int *ret_type, int othername_oid);
+
+int
+_gnutls_write_new_general_name(asn1_node ext, const char *ext_name,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data, unsigned int data_size);
+
+int
+_gnutls_write_new_othername(asn1_node ext, const char *ext_name,
+ const char *oid,
+ const void *data, unsigned int data_size);
+
+/* dsa.c */
+
+
+/* verify.c */
+int gnutls_x509_crt_is_issuer(gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t issuer);
+
+int
+_gnutls_x509_verify_algorithm(gnutls_digest_algorithm_t * hash,
+ const gnutls_datum_t * signature,
+ gnutls_pk_algorithm_t pk,
+ gnutls_pk_params_st * issuer_params);
+
+/* privkey.h */
+void _gnutls_x509_privkey_reinit(gnutls_x509_privkey_t key);
+
+asn1_node _gnutls_privkey_decode_pkcs1_rsa_key(const gnutls_datum_t *
+ raw_key,
+ gnutls_x509_privkey_t pkey);
+int _gnutls_privkey_decode_ecc_key(asn1_node* pkey_asn, const gnutls_datum_t *
+ raw_key,
+ gnutls_x509_privkey_t pkey,
+ gnutls_ecc_curve_t curve);
+
+int _gnutls_privkey_decode_eddsa_key(asn1_node* pkey_asn,
+ const gnutls_datum_t *raw_key,
+ gnutls_x509_privkey_t pkey,
+ gnutls_ecc_curve_t curve);
+
+int
+_gnutls_x509_read_ecc_params(uint8_t * der, int dersize,
+ unsigned int *curve);
+
+int
+_gnutls_x509_read_gost_params(uint8_t * der, int dersize,
+ gnutls_pk_params_st * params,
+ gnutls_pk_algorithm_t algo);
+
+int _gnutls_asn1_encode_privkey(asn1_node * c2,
+ gnutls_pk_params_st * params);
+
+void _gnutls_x509_privkey_get_spki_params(gnutls_x509_privkey_t key,
+ gnutls_x509_spki_st * params);
+
+int _gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize,
+ gnutls_x509_spki_st * params);
+int _gnutls_x509_write_rsa_pss_params(const gnutls_x509_spki_st * params,
+ gnutls_datum_t * der);
+
+/* extensions.c */
+int _gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl,
+ int indx, void *oid,
+ size_t * sizeof_oid);
+
+int _gnutls_x509_crl_set_extension(gnutls_x509_crl_t crl,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data,
+ unsigned int critical);
+
+int
+_gnutls_x509_crl_get_extension(gnutls_x509_crl_t crl,
+ const char *extension_id, int indx,
+ gnutls_datum_t * data,
+ unsigned int *critical);
+
+int
+_gnutls_x509_crt_get_extension(gnutls_x509_crt_t cert,
+ const char *extension_id, int indx,
+ gnutls_datum_t * data, unsigned int *critical);
+
+int _gnutls_x509_crt_get_extension_oid(gnutls_x509_crt_t cert,
+ int indx, void *ret,
+ size_t * ret_size);
+int _gnutls_x509_crt_set_extension(gnutls_x509_crt_t cert,
+ const char *extension_id,
+ const gnutls_datum_t * ext_data,
+ unsigned int critical);
+
+int
+_gnutls_x509_ext_extract_number(uint8_t * number,
+ size_t * nr_size,
+ uint8_t * extnValue, int extnValueLen);
+int
+_gnutls_x509_ext_gen_number(const uint8_t * number, size_t nr_size,
+ gnutls_datum_t * der_ext);
+
+
+int
+_gnutls_write_general_name(asn1_node ext, const char *ext_name,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data, unsigned int data_size);
+
+int _gnutls_x509_ext_gen_subject_alt_name(gnutls_x509_subject_alt_name_t
+ type,
+ const char *othername_oid,
+ const void *data,
+ unsigned int data_size,
+ const gnutls_datum_t * prev_der_ext,
+ gnutls_datum_t * der_ext);
+int _gnutls_x509_ext_gen_auth_key_id(const void *id, size_t id_size,
+ gnutls_datum_t * der_data);
+
+/* mpi.c */
+int _gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert,
+ gnutls_pk_params_st *);
+
+int _gnutls_x509_crt_get_mpis(gnutls_x509_crt_t cert,
+ gnutls_pk_params_st * params);
+
+int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t, uint8_t * der,
+ int dersize,
+ gnutls_pk_params_st * params);
+int _gnutls_x509_check_pubkey_params(gnutls_pk_params_st * params);
+
+int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t, uint8_t * der,
+ int dersize, gnutls_pk_params_st * params);
+
+int _gnutls_x509_read_pubkey_signature_params(gnutls_pk_algorithm_t algo,
+ uint8_t * der, int dersize,
+ gnutls_pk_params_st * params);
+
+int _gnutls_x509_write_ecc_params(const gnutls_ecc_curve_t curve,
+ gnutls_datum_t * der);
+int _gnutls_x509_write_ecc_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+
+int _gnutls_x509_write_eddsa_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+
+int
+_gnutls_x509_write_pubkey_params(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+int _gnutls_x509_write_pubkey(const gnutls_pk_params_st * params,
+ gnutls_datum_t * der);
+
+int _gnutls_x509_read_uint(asn1_node node, const char *value,
+ unsigned int *ret);
+
+int _gnutls_x509_read_der_int(uint8_t * der, int dersize, bigint_t * out);
+int _gnutls_x509_read_der_uint(uint8_t * der, int dersize, unsigned int *out);
+
+int _gnutls_x509_read_int(asn1_node node, const char *value,
+ bigint_t * ret_mpi);
+int _gnutls_x509_write_int(asn1_node node, const char *value, bigint_t mpi,
+ int lz);
+
+int _gnutls_x509_write_uint32(asn1_node node, const char *value,
+ uint32_t num);
+
+
+int _gnutls_x509_read_key_int(asn1_node node, const char *value,
+ bigint_t * ret_mpi);
+int _gnutls_x509_write_key_int(asn1_node node, const char *value, bigint_t mpi,
+ int lz);
+
+int _gnutls_x509_read_key_int_le(asn1_node node, const char *value,
+ bigint_t * ret_mpi);
+int _gnutls_x509_write_key_int_le(asn1_node node, const char *value,
+ bigint_t mpi);
+
+int _gnutls_x509_read_pkalgo_params(asn1_node src, const char *src_name,
+ gnutls_x509_spki_st *params, unsigned is_sig);
+int _gnutls_x509_write_sign_params(asn1_node dst, const char *dst_name,
+ const gnutls_sign_entry_st *se, gnutls_x509_spki_st *params);
+
+#define _gnutls_x509_read_sign_params(src,name,params) _gnutls_x509_read_pkalgo_params(src,name,params,1)
+#define _gnutls_x509_read_spki_params(src,name,params) _gnutls_x509_read_pkalgo_params(src,name,params,0)
+int _gnutls_x509_write_spki_params(asn1_node dst, const char *dst_name,
+ gnutls_x509_spki_st *params);
+
+inline static int
+_gnutls_x509_crt_read_spki_params(gnutls_x509_crt_t crt,
+ gnutls_x509_spki_st *params)
+{
+ return _gnutls_x509_read_spki_params(crt->cert,
+ "tbsCertificate."
+ "subjectPublicKeyInfo."
+ "algorithm",
+ params);
+}
+
+inline static int
+_gnutls_x509_crq_read_spki_params(gnutls_x509_crq_t crt,
+ gnutls_x509_spki_st *params)
+{
+ return _gnutls_x509_read_spki_params(crt->crq,
+ "certificationRequestInfo."
+ "subjectPKInfo."
+ "algorithm",
+ params);
+}
+
+
+/* pkcs12.h */
+#include <gnutls/pkcs12.h>
+
+typedef struct gnutls_pkcs12_int {
+ asn1_node pkcs12;
+ unsigned expanded;
+} gnutls_pkcs12_int;
+
+#define MAX_BAG_ELEMENTS 32
+
+struct bag_element {
+ gnutls_datum_t data;
+ gnutls_pkcs12_bag_type_t type;
+ gnutls_datum_t local_key_id;
+ char *friendly_name;
+};
+
+typedef struct gnutls_pkcs12_bag_int {
+ struct bag_element element[MAX_BAG_ELEMENTS];
+ unsigned bag_elements;
+} gnutls_pkcs12_bag_int;
+
+#define BAG_PKCS8_KEY "1.2.840.113549.1.12.10.1.1"
+#define BAG_PKCS8_ENCRYPTED_KEY "1.2.840.113549.1.12.10.1.2"
+#define BAG_CERTIFICATE "1.2.840.113549.1.12.10.1.3"
+#define BAG_CRL "1.2.840.113549.1.12.10.1.4"
+#define BAG_SECRET "1.2.840.113549.1.12.10.1.5"
+
+/* Bag attributes
+ */
+#define FRIENDLY_NAME_OID "1.2.840.113549.1.9.20"
+#define KEY_ID_OID "1.2.840.113549.1.9.21"
+
+int
+_gnutls_pkcs12_string_to_key(const mac_entry_st * me,
+ unsigned int id, const uint8_t * salt,
+ unsigned int salt_size, unsigned int iter,
+ const char *pw, unsigned int req_keylen,
+ uint8_t * keybuf);
+
+
+int _pkcs12_decode_safe_contents(const gnutls_datum_t * content,
+ gnutls_pkcs12_bag_t bag);
+
+int
+_pkcs12_encode_safe_contents(gnutls_pkcs12_bag_t bag, asn1_node * content,
+ int *enc);
+
+int _pkcs12_decode_crt_bag(gnutls_pkcs12_bag_type_t type,
+ const gnutls_datum_t * in,
+ gnutls_datum_t * out);
+int _pkcs12_encode_crt_bag(gnutls_pkcs12_bag_type_t type,
+ const gnutls_datum_t * raw,
+ gnutls_datum_t * out);
+
+/* crq */
+int _gnutls_x509_crq_set_extension(gnutls_x509_crq_t crq,
+ const char *ext_id,
+ const gnutls_datum_t * ext_data,
+ unsigned int critical);
+
+int
+gnutls_x509_crt_verify_data3(gnutls_x509_crt_t crt,
+ gnutls_sign_algorithm_t algo,
+ gnutls_typed_vdata_st *vdata,
+ unsigned int vdata_size,
+ const gnutls_datum_t *data,
+ const gnutls_datum_t *signature,
+ unsigned int flags);
+
+int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list,
+ gnutls_x509_crt_t cert,
+ gnutls_x509_crt_t * issuer,
+ unsigned int flags);
+
+unsigned int
+_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist,
+ const gnutls_x509_crt_t * certificate_list,
+ int clist_size,
+ const gnutls_x509_crt_t * trusted_cas,
+ int tcas_size,
+ unsigned int flags,
+ const char *purpose,
+ gnutls_verify_output_function func);
+
+#ifdef ENABLE_PKCS11
+unsigned int
+_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist,
+ const char* url,
+ const gnutls_x509_crt_t * certificate_list,
+ unsigned clist_size,
+ const char *purpose,
+ unsigned int flags,
+ gnutls_verify_output_function func);
+#endif
+
+int _gnutls_check_cert_sanity(gnutls_x509_crt_t cert);
+
+int
+_gnutls_x509_crt_check_revocation(gnutls_x509_crt_t cert,
+ const gnutls_x509_crl_t * crl_list,
+ int crl_list_length,
+ gnutls_verify_output_function func);
+
+typedef struct gnutls_name_constraints_st {
+ struct name_constraints_node_st * permitted;
+ struct name_constraints_node_st * excluded;
+} gnutls_name_constraints_st;
+
+typedef struct name_constraints_node_st {
+ unsigned type;
+ gnutls_datum_t name;
+ struct name_constraints_node_st *next;
+} name_constraints_node_st;
+
+int _gnutls_extract_name_constraints(asn1_node c2, const char *vstr,
+ name_constraints_node_st ** _nc);
+void _gnutls_name_constraints_node_free (name_constraints_node_st *node);
+int _gnutls_x509_name_constraints_merge(gnutls_x509_name_constraints_t nc,
+ gnutls_x509_name_constraints_t nc2);
+
+void _gnutls_x509_policies_erase(gnutls_x509_policies_t policies, unsigned int seq);
+
+struct gnutls_x509_tlsfeatures_st {
+ uint16_t feature[MAX_EXT_TYPES];
+ unsigned int size;
+};
+
+unsigned _gnutls_is_broken_sig_allowed(const gnutls_sign_entry_st *se, unsigned int flags);
+
+#endif /* GNUTLS_LIB_X509_X509_INT_H */
diff --git a/lib/x509/x509_write.c b/lib/x509/x509_write.c
new file mode 100644
index 0000000..e9a7087
--- /dev/null
+++ b/lib/x509/x509_write.c
@@ -0,0 +1,2125 @@
+/*
+ * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains functions to handle X.509 certificate generation.
+ */
+
+#include "gnutls_int.h"
+
+#include <datum.h>
+#include <global.h>
+#include "errors.h"
+#include <common.h>
+#include <x509.h>
+#include <gnutls/x509-ext.h>
+#include <x509_b64.h>
+#include "x509_int.h"
+#include <libtasn1.h>
+#include <pk.h>
+
+static void disable_optional_stuff(gnutls_x509_crt_t cert);
+
+/**
+ * gnutls_x509_crt_set_dn_by_oid:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @oid: holds an Object Identifier in a null terminated string
+ * @raw_flag: must be 0, or 1 if the data are DER encoded
+ * @name: a pointer to the name
+ * @sizeof_name: holds the size of @name
+ *
+ * This function will set the part of the name of the Certificate
+ * subject, specified by the given OID. The input string should be
+ * ASCII or UTF-8 encoded.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * With this function you can only set the known OIDs. You can test
+ * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are
+ * not known (by gnutls) you should properly DER encode your data,
+ * and call this function with @raw_flag set.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_dn_by_oid(gnutls_x509_crt_t crt, const char *oid,
+ unsigned int raw_flag, const void *name,
+ unsigned int sizeof_name)
+{
+ if (sizeof_name == 0 || name == NULL || crt == NULL) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ return _gnutls_x509_set_dn_oid(crt->cert, "tbsCertificate.subject",
+ oid, raw_flag, name, sizeof_name);
+}
+
+/**
+ * gnutls_x509_crt_set_issuer_dn_by_oid:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @oid: holds an Object Identifier in a null terminated string
+ * @raw_flag: must be 0, or 1 if the data are DER encoded
+ * @name: a pointer to the name
+ * @sizeof_name: holds the size of @name
+ *
+ * This function will set the part of the name of the Certificate
+ * issuer, specified by the given OID. The input string should be
+ * ASCII or UTF-8 encoded.
+ *
+ * Some helper macros with popular OIDs can be found in gnutls/x509.h
+ * With this function you can only set the known OIDs. You can test
+ * for known OIDs using gnutls_x509_dn_oid_known(). For OIDs that are
+ * not known (by gnutls) you should properly DER encode your data,
+ * and call this function with @raw_flag set.
+ *
+ * Normally you do not need to call this function, since the signing
+ * operation will copy the signer's name as the issuer of the
+ * certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_issuer_dn_by_oid(gnutls_x509_crt_t crt,
+ const char *oid,
+ unsigned int raw_flag,
+ const void *name,
+ unsigned int sizeof_name)
+{
+ if (sizeof_name == 0 || name == NULL || crt == NULL) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ return _gnutls_x509_set_dn_oid(crt->cert, "tbsCertificate.issuer",
+ oid, raw_flag, name, sizeof_name);
+}
+
+/**
+ * gnutls_x509_crt_set_proxy_dn:
+ * @crt: a gnutls_x509_crt_t type with the new proxy cert
+ * @eecrt: the end entity certificate that will be issuing the proxy
+ * @raw_flag: must be 0, or 1 if the CN is DER encoded
+ * @name: a pointer to the CN name, may be NULL (but MUST then be added later)
+ * @sizeof_name: holds the size of @name
+ *
+ * This function will set the subject in @crt to the end entity's
+ * @eecrt subject name, and add a single Common Name component @name
+ * of size @sizeof_name. This corresponds to the required proxy
+ * certificate naming style. Note that if @name is %NULL, you MUST
+ * set it later by using gnutls_x509_crt_set_dn_by_oid() or similar.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_proxy_dn(gnutls_x509_crt_t crt,
+ gnutls_x509_crt_t eecrt,
+ unsigned int raw_flag, const void *name,
+ unsigned int sizeof_name)
+{
+ int result;
+
+ if (crt == NULL || eecrt == NULL) {
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ result = asn1_copy_node(crt->cert, "tbsCertificate.subject",
+ eecrt->cert, "tbsCertificate.subject");
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ if (name && sizeof_name) {
+ return _gnutls_x509_set_dn_oid(crt->cert,
+ "tbsCertificate.subject",
+ GNUTLS_OID_X520_COMMON_NAME,
+ raw_flag, name,
+ sizeof_name);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_version:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @version: holds the version number. For X.509v1 certificates must be 1.
+ *
+ * This function will set the version of the certificate. This must
+ * be one for X.509 version 1, and so on. Plain certificates without
+ * extensions must have version set to one.
+ *
+ * To create well-formed certificates, you must specify version 3 if
+ * you use any certificate extensions. Extensions are created by
+ * functions such as gnutls_x509_crt_set_subject_alt_name()
+ * or gnutls_x509_crt_set_key_usage().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_version(gnutls_x509_crt_t crt, unsigned int version)
+{
+ int result;
+ unsigned char null = version;
+
+ if (crt == NULL || version == 0 || version >= 0x80) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ if (null > 0)
+ null--;
+
+ result =
+ asn1_write_value(crt->cert, "tbsCertificate.version", &null,
+ 1);
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_key:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @key: holds a private key
+ *
+ * This function will set the public parameters from the given
+ * private key to the certificate.
+ *
+ * To export the public key (i.e., the SubjectPublicKeyInfo part), check
+ * gnutls_pubkey_import_x509().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ **/
+int
+gnutls_x509_crt_set_key(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key)
+{
+ int result;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ result = _gnutls_x509_encode_and_copy_PKI_params(crt->cert,
+ "tbsCertificate.subjectPublicKeyInfo",
+ &key->params);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_crq:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @crq: holds a certificate request
+ *
+ * This function will set the name and public parameters as well as
+ * the extensions from the given certificate request to the certificate.
+ * Only RSA keys are currently supported.
+ *
+ * Note that this function will only set the @crq if it is self
+ * signed and the signature is correct. See gnutls_x509_crq_sign2().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crt_set_crq(gnutls_x509_crt_t crt, gnutls_x509_crq_t crq)
+{
+ int result;
+
+ if (crt == NULL || crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ result = gnutls_x509_crq_verify(crq, 0);
+ if (result < 0)
+ return gnutls_assert_val(result);
+
+ result = asn1_copy_node(crt->cert, "tbsCertificate.subject",
+ crq->crq,
+ "certificationRequestInfo.subject");
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ result =
+ asn1_copy_node(crt->cert,
+ "tbsCertificate.subjectPublicKeyInfo", crq->crq,
+ "certificationRequestInfo.subjectPKInfo");
+ if (result != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(result);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_crq_extensions:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @crq: holds a certificate request
+ *
+ * This function will set the extensions from the given request to the
+ * certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.8.0
+ **/
+int
+gnutls_x509_crt_set_crq_extensions(gnutls_x509_crt_t crt,
+ gnutls_x509_crq_t crq)
+{
+ return gnutls_x509_crt_set_crq_extension_by_oid(crt, crq, NULL, 0);
+}
+
+/**
+ * gnutls_x509_crt_set_crq_extension_by_oid:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @crq: holds a certificate request
+ * @oid: the object identifier of the OID to copy
+ * @flags: should be zero
+ *
+ * This function will set the extension specify by @oid from the given request to the
+ * certificate.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.1
+ **/
+int
+gnutls_x509_crt_set_crq_extension_by_oid(gnutls_x509_crt_t crt,
+ gnutls_x509_crq_t crq, const char *oid,
+ unsigned flags)
+{
+ size_t i;
+
+ if (crt == NULL || crq == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ for (i = 0;; i++) {
+ int result;
+ char local_oid[MAX_OID_SIZE];
+ size_t local_oid_size;
+ uint8_t *extensions;
+ size_t extensions_size;
+ unsigned int critical;
+ gnutls_datum_t ext;
+
+ local_oid_size = sizeof(local_oid);
+ result = gnutls_x509_crq_get_extension_info(crq, i, local_oid,
+ &local_oid_size,
+ &critical);
+ if (result < 0) {
+ if (result ==
+ GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+
+ gnutls_assert();
+ return result;
+ }
+
+ if (oid && strcmp(local_oid, oid) != 0)
+ continue;
+
+ extensions_size = 0;
+ result = gnutls_x509_crq_get_extension_data(crq, i, NULL,
+ &extensions_size);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ extensions = gnutls_malloc(extensions_size);
+ if (extensions == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ result =
+ gnutls_x509_crq_get_extension_data(crq, i, extensions,
+ &extensions_size);
+ if (result < 0) {
+ gnutls_assert();
+ gnutls_free(extensions);
+ return result;
+ }
+
+ ext.data = extensions;
+ ext.size = extensions_size;
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, local_oid, &ext,
+ critical);
+ gnutls_free(extensions);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_extension_by_oid:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @oid: holds an Object Identifier in null terminated string
+ * @buf: a pointer to a DER encoded data
+ * @sizeof_buf: holds the size of @buf
+ * @critical: should be non-zero if the extension is to be marked as critical
+ *
+ * This function will set an the extension, by the specified OID, in
+ * the certificate. The extension data should be binary data DER
+ * encoded.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_extension_by_oid(gnutls_x509_crt_t crt,
+ const char *oid, const void *buf,
+ size_t sizeof_buf,
+ unsigned int critical)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ der_data.data = (void *) buf;
+ der_data.size = sizeof_buf;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, oid, &der_data, critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crt_set_basic_constraints:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @ca: true(1) or false(0). Depending on the Certificate authority status.
+ * @pathLenConstraint: non-negative error codes indicate maximum length of path,
+ * and negative error codes indicate that the pathLenConstraints field should
+ * not be present.
+ *
+ * This function will set the basicConstraints certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_basic_constraints(gnutls_x509_crt_t crt,
+ unsigned int ca,
+ int pathLenConstraint)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result = gnutls_x509_ext_export_basic_constraints(ca, pathLenConstraint, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.19", &der_data, 1);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_ca_status:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @ca: true(1) or false(0). Depending on the Certificate authority status.
+ *
+ * This function will set the basicConstraints certificate extension.
+ * Use gnutls_x509_crt_set_basic_constraints() if you want to control
+ * the pathLenConstraint field too.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int gnutls_x509_crt_set_ca_status(gnutls_x509_crt_t crt, unsigned int ca)
+{
+ return gnutls_x509_crt_set_basic_constraints(crt, ca, -1);
+}
+
+/**
+ * gnutls_x509_crt_set_key_usage:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @usage: an ORed sequence of the GNUTLS_KEY_* elements.
+ *
+ * This function will set the keyUsage certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_key_usage(gnutls_x509_crt_t crt, unsigned int usage)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result =
+ gnutls_x509_ext_export_key_usage(usage, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.15", &der_data, 1);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_inhibit_anypolicy:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @skipcerts: number of certificates after which anypolicy is no longer acceptable.
+ *
+ * This function will set the Inhibit anyPolicy certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_inhibit_anypolicy(gnutls_x509_crt_t crt, unsigned int skipcerts)
+{
+ int ret;
+ gnutls_datum_t der_data;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ ret =
+ gnutls_x509_ext_export_inhibit_anypolicy(skipcerts, &der_data);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.54", &der_data, 1);
+ _gnutls_free_datum(&der_data);
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_subject_alternative_name:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @type: is one of the gnutls_x509_subject_alt_name_t enumerations
+ * @data_string: The data to be set, a (0) terminated string
+ *
+ * This function will set the subject alternative name certificate
+ * extension. This function assumes that data can be expressed as a null
+ * terminated string.
+ *
+ * The name of the function is unfortunate since it is inconsistent with
+ * gnutls_x509_crt_get_subject_alt_name().
+ *
+ * See gnutls_x509_crt_set_subject_alt_name() for more information.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_subject_alternative_name(gnutls_x509_crt_t crt,
+ gnutls_x509_subject_alt_name_t
+ type, const char *data_string)
+{
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* only handle text extensions */
+ if (type != GNUTLS_SAN_DNSNAME && type != GNUTLS_SAN_RFC822NAME &&
+ type != GNUTLS_SAN_URI) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return gnutls_x509_crt_set_subject_alt_name(crt, type, data_string,
+ strlen(data_string),
+ GNUTLS_FSAN_SET);
+}
+
+/**
+ * gnutls_x509_crt_set_subject_alt_name:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @type: is one of the gnutls_x509_subject_alt_name_t enumerations
+ * @data: The data to be set
+ * @data_size: The size of data to be set
+ * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append.
+ *
+ * This function will set the subject alternative name certificate
+ * extension. It can set the following types: %GNUTLS_SAN_DNSNAME as a text string,
+ * %GNUTLS_SAN_RFC822NAME as a text string, %GNUTLS_SAN_URI as a text string,
+ * %GNUTLS_SAN_IPADDRESS as a binary IP address (4 or 16 bytes),
+ * %GNUTLS_SAN_OTHERNAME_XMPP as a UTF8 string (since 3.5.0).
+ *
+ * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
+ * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.6.0
+ **/
+int
+gnutls_x509_crt_set_subject_alt_name(gnutls_x509_crt_t crt,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data,
+ unsigned int data_size,
+ unsigned int flags)
+{
+ int result;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t prev_der_data = { NULL, 0 };
+ unsigned int critical = 0;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+
+ if (flags & GNUTLS_FSAN_APPEND) {
+ result =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.17", 0,
+ &prev_der_data,
+ &critical);
+ if (result < 0
+ && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* generate the extension.
+ */
+ result =
+ _gnutls_x509_ext_gen_subject_alt_name(type, NULL, data, data_size,
+ &prev_der_data,
+ &der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.17", &der_data,
+ critical);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = 0;
+
+ finish:
+ _gnutls_free_datum(&prev_der_data);
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_set_issuer_alt_name:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @type: is one of the gnutls_x509_subject_alt_name_t enumerations
+ * @data: The data to be set
+ * @data_size: The size of data to be set
+ * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append.
+ *
+ * This function will set the issuer alternative name certificate
+ * extension. It can set the same types as gnutls_x509_crt_set_subject_alt_name().
+ *
+ * Since version 3.5.7 the %GNUTLS_SAN_RFC822NAME, %GNUTLS_SAN_DNSNAME, and
+ * %GNUTLS_SAN_OTHERNAME_XMPP are converted to ACE format when necessary.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.3.0
+ **/
+int
+gnutls_x509_crt_set_issuer_alt_name(gnutls_x509_crt_t crt,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data,
+ unsigned int data_size,
+ unsigned int flags)
+{
+ int result;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t prev_der_data = { NULL, 0 };
+ unsigned int critical = 0;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+
+ if (flags & GNUTLS_FSAN_APPEND) {
+ result =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.18", 0,
+ &prev_der_data,
+ &critical);
+ if (result < 0
+ && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ /* generate the extension.
+ */
+ result =
+ _gnutls_x509_ext_gen_subject_alt_name(type, NULL, data, data_size,
+ &prev_der_data,
+ &der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.18", &der_data,
+ critical);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = 0;
+
+ finish:
+ _gnutls_free_datum(&prev_der_data);
+ return result;
+}
+
+int _gnutls_encode_othername_data(unsigned flags, const void *data, unsigned data_size, gnutls_datum_t *output)
+{
+ int ret;
+ if (flags & GNUTLS_FSAN_ENCODE_OCTET_STRING) {
+ ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING,
+ data, data_size, output);
+ } else if (flags & GNUTLS_FSAN_ENCODE_UTF8_STRING) {
+ ret = _gnutls_x509_encode_string(ASN1_ETYPE_UTF8_STRING,
+ data, data_size, output);
+ } else {
+ ret = _gnutls_set_datum(output, data, data_size);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_set_subject_alt_othername:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @oid: The other name OID
+ * @data: The data to be set
+ * @data_size: The size of data to be set
+ * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append.
+ *
+ * This function will set an "othername" to the subject alternative name certificate
+ * extension.
+ *
+ * The values set are set as binary values and are expected to have the proper DER encoding.
+ * For convenience the flags %GNUTLS_FSAN_ENCODE_OCTET_STRING and %GNUTLS_FSAN_ENCODE_UTF8_STRING
+ * can be used to encode the provided data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.0
+ **/
+int
+gnutls_x509_crt_set_subject_alt_othername(gnutls_x509_crt_t crt,
+ const char *oid,
+ const void *data,
+ unsigned int data_size,
+ unsigned int flags)
+{
+ int result;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t prev_der_data = { NULL, 0 };
+ gnutls_datum_t encoded_data = { NULL, 0 };
+ unsigned int critical = 0;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+
+ if (flags & GNUTLS_FSAN_APPEND) {
+ result =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.17", 0,
+ &prev_der_data,
+ &critical);
+ if (result < 0
+ && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ /* generate the extension.
+ */
+ result =
+ _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid,
+ encoded_data.data, encoded_data.size,
+ &prev_der_data, &der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.17", &der_data,
+ critical);
+
+
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result = 0;
+
+ finish:
+ _gnutls_free_datum(&der_data);
+ _gnutls_free_datum(&prev_der_data);
+ _gnutls_free_datum(&encoded_data);
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_set_issuer_alt_othername:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @oid: The other name OID
+ * @data: The data to be set
+ * @data_size: The size of data to be set
+ * @flags: GNUTLS_FSAN_SET to clear previous data or GNUTLS_FSAN_APPEND to append.
+ *
+ * This function will set an "othername" to the issuer alternative name certificate
+ * extension.
+ *
+ * The values set are set as binary values and are expected to have the proper DER encoding.
+ * For convenience the flags %GNUTLS_FSAN_ENCODE_OCTET_STRING and %GNUTLS_FSAN_ENCODE_UTF8_STRING
+ * can be used to encode the provided data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.5.0
+ **/
+int
+gnutls_x509_crt_set_issuer_alt_othername(gnutls_x509_crt_t crt,
+ const char *oid,
+ const void *data,
+ unsigned int data_size,
+ unsigned int flags)
+{
+ int result;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t prev_der_data = { NULL, 0 };
+ gnutls_datum_t encoded_data = {NULL, 0};
+ unsigned int critical = 0;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+
+ if (flags & GNUTLS_FSAN_APPEND) {
+ result =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.18", 0,
+ &prev_der_data,
+ &critical);
+ if (result < 0
+ && result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return result;
+ }
+ }
+
+ result = _gnutls_encode_othername_data(flags, data, data_size, &encoded_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ /* generate the extension.
+ */
+ result =
+ _gnutls_x509_ext_gen_subject_alt_name(GNUTLS_SAN_OTHERNAME, oid,
+ encoded_data.data, encoded_data.size,
+ &prev_der_data, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.18", &der_data,
+ critical);
+
+ if (result < 0) {
+ gnutls_assert();
+ goto finish;
+ }
+
+ result = 0;
+
+ finish:
+ _gnutls_free_datum(&der_data);
+ _gnutls_free_datum(&prev_der_data);
+ _gnutls_free_datum(&encoded_data);
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_set_proxy:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @pathLenConstraint: non-negative error codes indicate maximum length of path,
+ * and negative error codes indicate that the pathLenConstraints field should
+ * not be present.
+ * @policyLanguage: OID describing the language of @policy.
+ * @policy: uint8_t byte array with policy language, can be %NULL
+ * @sizeof_policy: size of @policy.
+ *
+ * This function will set the proxyCertInfo extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_proxy(gnutls_x509_crt_t crt,
+ int pathLenConstraint,
+ const char *policyLanguage,
+ const char *policy, size_t sizeof_policy)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result = gnutls_x509_ext_export_proxy(pathLenConstraint,
+ policyLanguage,
+ policy, sizeof_policy,
+ &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = _gnutls_x509_crt_set_extension(crt, "1.3.6.1.5.5.7.1.14",
+ &der_data, 1);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_private_key_usage_period:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @activation: The activation time
+ * @expiration: The expiration time
+ *
+ * This function will set the private key usage period extension (2.5.29.16).
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_private_key_usage_period(gnutls_x509_crt_t crt,
+ time_t activation,
+ time_t expiration)
+{
+ int result;
+ gnutls_datum_t der_data;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ result = gnutls_x509_ext_export_private_key_usage_period(activation,
+ expiration, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ result = _gnutls_x509_crt_set_extension(crt, "2.5.29.16",
+ &der_data, 0);
+
+ _gnutls_free_datum(&der_data);
+
+ cleanup:
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_sign2:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ * @dig: The message digest to use, %GNUTLS_DIG_SHA256 is a safe choice
+ * @flags: must be 0
+ *
+ * This function will sign the certificate with the issuer's private key, and
+ * will copy the issuer's information into the certificate.
+ *
+ * This must be the last step in a certificate generation since all
+ * the previously set parameters are now signed.
+ *
+ * A known limitation of this function is, that a newly-signed certificate will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN,
+ * and in that case, a suitable but reasonable for the key algorithm will be selected.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_sign2(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer,
+ gnutls_x509_privkey_t issuer_key,
+ gnutls_digest_algorithm_t dig, unsigned int flags)
+{
+ int result;
+ gnutls_privkey_t privkey;
+
+ if (crt == NULL || issuer == NULL || issuer_key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(crt);
+
+ result = gnutls_privkey_init(&privkey);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = gnutls_privkey_import_x509(privkey, issuer_key, 0);
+ if (result < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ result =
+ gnutls_x509_crt_privkey_sign(crt, issuer, privkey, dig, flags);
+ if (result < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ result = 0;
+
+ fail:
+ gnutls_privkey_deinit(privkey);
+
+ return result;
+}
+
+/**
+ * gnutls_x509_crt_sign:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ *
+ * This function is the same a gnutls_x509_crt_sign2() with no flags,
+ * and an appropriate hash algorithm. The hash algorithm used may
+ * vary between versions of GnuTLS, and it is tied to the security
+ * level of the issuer's public key.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_sign(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer,
+ gnutls_x509_privkey_t issuer_key)
+{
+ return gnutls_x509_crt_sign2(crt, issuer, issuer_key,
+ 0, 0);
+}
+
+/**
+ * gnutls_x509_crt_set_activation_time:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @act_time: The actual time
+ *
+ * This function will set the time this certificate was or will be
+ * activated.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_activation_time(gnutls_x509_crt_t cert,
+ time_t act_time)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(cert);
+
+ return _gnutls_x509_set_time(cert->cert,
+ "tbsCertificate.validity.notBefore",
+ act_time, 0);
+}
+
+/**
+ * gnutls_x509_crt_set_expiration_time:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @exp_time: The actual time
+ *
+ * This function will set the time this Certificate will expire.
+ * Setting an expiration time to (time_t)-1 will set
+ * to the no well-defined expiration date value.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_expiration_time(gnutls_x509_crt_t cert,
+ time_t exp_time)
+{
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(cert);
+
+ return _gnutls_x509_set_time(cert->cert,
+ "tbsCertificate.validity.notAfter",
+ exp_time, 0);
+}
+
+/**
+ * gnutls_x509_crt_set_serial:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @serial: The serial number
+ * @serial_size: Holds the size of the serial field.
+ *
+ * This function will set the X.509 certificate's serial number.
+ * While the serial number is an integer, it is often handled
+ * as an opaque field by several CAs. For this reason this function
+ * accepts any kind of data as a serial number. To be consistent
+ * with the X.509/PKIX specifications the provided @serial should be
+ * a big-endian positive number (i.e. its leftmost bit should be zero).
+ *
+ * The size of the serial is restricted to 20 bytes maximum by RFC5280.
+ * This function allows writing more than 20 bytes but the generated
+ * certificates in that case may be rejected by other implementations.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_serial(gnutls_x509_crt_t cert, const void *serial,
+ size_t serial_size)
+{
+ int ret;
+ unsigned all_zero, i;
+ const unsigned char *pserial = serial;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* check for non-zero serial */
+ all_zero = 1;
+ for (i=0;i<serial_size;i++) {
+ if (pserial[i] != 0) {
+ all_zero = 0;
+ break;
+ }
+ }
+
+ if (all_zero) {
+ _gnutls_debug_log("error: certificate serial is zero\n");
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(cert);
+
+ ret =
+ asn1_write_value(cert->cert, "tbsCertificate.serialNumber",
+ serial, serial_size);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+
+}
+
+/**
+ * gnutls_x509_crt_set_issuer_unique_id:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @id: The unique ID
+ * @id_size: Holds the size of the unique ID.
+ *
+ * This function will set the X.509 certificate's issuer unique ID field.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.7
+ **/
+int
+gnutls_x509_crt_set_issuer_unique_id(gnutls_x509_crt_t cert, const void *id,
+ size_t id_size)
+{
+ int ret;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(cert);
+
+ ret =
+ asn1_write_value(cert->cert, "tbsCertificate.issuerUniqueID",
+ id, id_size*8);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_subject_unique_id:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @id: The unique ID
+ * @id_size: Holds the size of the unique ID.
+ *
+ * This function will set the X.509 certificate's subject unique ID field.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.4.7
+ **/
+int
+gnutls_x509_crt_set_subject_unique_id(gnutls_x509_crt_t cert, const void *id,
+ size_t id_size)
+{
+ int ret;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ MODIFIED(cert);
+
+ ret =
+ asn1_write_value(cert->cert, "tbsCertificate.subjectUniqueID",
+ id, id_size*8);
+ if (ret != ASN1_SUCCESS) {
+ gnutls_assert();
+ return _gnutls_asn2err(ret);
+ }
+
+ return 0;
+}
+
+/* If OPTIONAL fields have not been initialized then
+ * disable them.
+ */
+static void disable_optional_stuff(gnutls_x509_crt_t cert)
+{
+ asn1_data_node_st n;
+ asn1_node node;
+ unsigned remove_subject_unique_id = 1;
+ unsigned remove_issuer_unique_id = 1;
+
+ node = asn1_find_node(cert->cert, "tbsCertificate.issuerUniqueID");
+ if (node) {
+ if (asn1_read_node_value(node, &n) == ASN1_SUCCESS && n.value_len != 0)
+ remove_issuer_unique_id = 0;
+ }
+
+ node = asn1_find_node(cert->cert, "tbsCertificate.subjectUniqueID");
+ if (node) {
+ if (asn1_read_node_value(node, &n) == ASN1_SUCCESS && n.value_len != 0)
+ remove_subject_unique_id = 0;
+ }
+
+ if (remove_issuer_unique_id)
+ (void)asn1_write_value(cert->cert, "tbsCertificate.issuerUniqueID", NULL,
+ 0);
+
+ if (remove_subject_unique_id)
+ (void)asn1_write_value(cert->cert, "tbsCertificate.subjectUniqueID",
+ NULL, 0);
+
+ if (cert->use_extensions == 0) {
+ _gnutls_debug_log("Disabling X.509 extensions.\n");
+ (void)asn1_write_value(cert->cert, "tbsCertificate.extensions",
+ NULL, 0);
+ }
+
+ return;
+}
+
+/**
+ * gnutls_x509_crt_set_crl_dist_points:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @type: is one of the gnutls_x509_subject_alt_name_t enumerations
+ * @data_string: The data to be set
+ * @reason_flags: revocation reasons
+ *
+ * This function will set the CRL distribution points certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_crl_dist_points(gnutls_x509_crt_t crt,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data_string,
+ unsigned int reason_flags)
+{
+ return gnutls_x509_crt_set_crl_dist_points2(crt, type, data_string,
+ strlen(data_string),
+ reason_flags);
+}
+
+/**
+ * gnutls_x509_crt_set_crl_dist_points2:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @type: is one of the gnutls_x509_subject_alt_name_t enumerations
+ * @data: The data to be set
+ * @data_size: The data size
+ * @reason_flags: revocation reasons
+ *
+ * This function will set the CRL distribution points certificate extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 2.6.0
+ **/
+int
+gnutls_x509_crt_set_crl_dist_points2(gnutls_x509_crt_t crt,
+ gnutls_x509_subject_alt_name_t type,
+ const void *data,
+ unsigned int data_size,
+ unsigned int reason_flags)
+{
+ int ret;
+ gnutls_datum_t der_data = { NULL, 0 };
+ gnutls_datum_t old_der = { NULL, 0 };
+ unsigned int critical;
+ gnutls_x509_crl_dist_points_t cdp = NULL;
+ gnutls_datum_t san;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_crl_dist_points_init(&cdp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Check if the extension already exists.
+ */
+ ret =
+ _gnutls_x509_crt_get_extension(crt, "2.5.29.31", 0, &old_der,
+ &critical);
+
+ if (ret >= 0 && old_der.data != NULL) {
+ ret = gnutls_x509_ext_import_crl_dist_points(&old_der, cdp, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ san.data = (void*)data;
+ san.size = data_size;
+ ret = gnutls_x509_crl_dist_points_set(cdp, type, &san, reason_flags);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* generate the extension.
+ */
+ ret =
+ gnutls_x509_ext_export_crl_dist_points(cdp, &der_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_x509_crt_set_extension(crt, "2.5.29.31", &der_data, 0);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&der_data);
+ _gnutls_free_datum(&old_der);
+ if (cdp != NULL)
+ gnutls_x509_crl_dist_points_deinit(cdp);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_crt_cpy_crl_dist_points:
+ * @dst: a certificate of type #gnutls_x509_crt_t
+ * @src: the certificate where the dist points will be copied from
+ *
+ * This function will copy the CRL distribution points certificate
+ * extension, from the source to the destination certificate.
+ * This may be useful to copy from a CA certificate to issued ones.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_cpy_crl_dist_points(gnutls_x509_crt_t dst,
+ gnutls_x509_crt_t src)
+{
+ int result;
+ gnutls_datum_t der_data;
+ unsigned int critical;
+
+ if (dst == NULL || src == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ result =
+ _gnutls_x509_crt_get_extension(src, "2.5.29.31", 0, &der_data,
+ &critical);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(dst, "2.5.29.31", &der_data,
+ critical);
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_subject_key_id:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @id: The key ID
+ * @id_size: Holds the size of the subject key ID field.
+ *
+ * This function will set the X.509 certificate's subject key ID
+ * extension.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_subject_key_id(gnutls_x509_crt_t cert,
+ const void *id, size_t id_size)
+{
+ int result;
+ gnutls_datum_t old_id, der_data;
+ gnutls_datum_t d_id;
+ unsigned int critical;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ result =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.14", 0, &old_id,
+ &critical);
+
+ if (result >= 0)
+ _gnutls_free_datum(&old_id);
+ if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ d_id.data = (void*)id;
+ d_id.size = id_size;
+
+ result = gnutls_x509_ext_export_subject_key_id(&d_id, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(cert, "2.5.29.14", &der_data,
+ 0);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_authority_key_id:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @id: The key ID
+ * @id_size: Holds the size of the key ID field.
+ *
+ * This function will set the X.509 certificate's authority key ID extension.
+ * Only the keyIdentifier field can be set with this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_set_authority_key_id(gnutls_x509_crt_t cert,
+ const void *id, size_t id_size)
+{
+ int result;
+ gnutls_datum_t old_id, der_data;
+ unsigned int critical;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* Check if the extension already exists.
+ */
+ result =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.35", 0, &old_id,
+ &critical);
+
+ if (result >= 0)
+ _gnutls_free_datum(&old_id);
+ if (result != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* generate the extension.
+ */
+ result = _gnutls_x509_ext_gen_auth_key_id(id, id_size, &der_data);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result =
+ _gnutls_x509_crt_set_extension(cert, "2.5.29.35", &der_data,
+ 0);
+
+ _gnutls_free_datum(&der_data);
+
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_key_purpose_oid:
+ * @cert: a certificate of type #gnutls_x509_crt_t
+ * @oid: a pointer to a null terminated string that holds the OID
+ * @critical: Whether this extension will be critical or not
+ *
+ * This function will set the key purpose OIDs of the Certificate.
+ * These are stored in the Extended Key Usage extension (2.5.29.37)
+ * See the GNUTLS_KP_* definitions for human readable names.
+ *
+ * Subsequent calls to this function will append OIDs to the OID list.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
+ * otherwise a negative error code is returned.
+ **/
+int
+gnutls_x509_crt_set_key_purpose_oid(gnutls_x509_crt_t cert,
+ const void *oid, unsigned int critical)
+{
+ int ret;
+ gnutls_datum_t old_id = {NULL,0};
+ gnutls_datum_t der = {NULL,0};
+ gnutls_x509_key_purposes_t p = NULL;
+
+ if (cert == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_key_purpose_init(&p);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Check if the extension already exists.
+ */
+ ret =
+ _gnutls_x509_crt_get_extension(cert, "2.5.29.37", 0, &old_id,
+ NULL);
+
+ if (ret >= 0) {
+ ret = gnutls_x509_ext_import_key_purposes(&old_id, p, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = gnutls_x509_key_purpose_set(p, oid);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_export_key_purposes(p, &der);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_crt_set_extension(cert, "2.5.29.37",
+ &der, critical);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ _gnutls_free_datum(&der);
+ _gnutls_free_datum(&old_id);
+ if (p != NULL)
+ gnutls_x509_key_purpose_deinit(p);
+
+ return ret;
+
+}
+
+/**
+ * gnutls_x509_crt_privkey_sign:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @issuer: is the certificate of the certificate issuer
+ * @issuer_key: holds the issuer's private key
+ * @dig: The message digest to use, %GNUTLS_DIG_SHA256 is a safe choice
+ * @flags: must be 0
+ *
+ * This function will sign the certificate with the issuer's private key, and
+ * will copy the issuer's information into the certificate.
+ *
+ * This must be the last step in a certificate generation since all
+ * the previously set parameters are now signed.
+ *
+ * A known limitation of this function is, that a newly-signed certificate will not
+ * be fully functional (e.g., for signature verification), until it
+ * is exported an re-imported.
+ *
+ * After GnuTLS 3.6.1 the value of @dig may be %GNUTLS_DIG_UNKNOWN,
+ * and in that case, a suitable but reasonable for the key algorithm will be selected.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ **/
+int
+gnutls_x509_crt_privkey_sign(gnutls_x509_crt_t crt,
+ gnutls_x509_crt_t issuer,
+ gnutls_privkey_t issuer_key,
+ gnutls_digest_algorithm_t dig,
+ unsigned int flags)
+{
+ int result;
+
+ if (crt == NULL || issuer == NULL || issuer_key == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ if (dig == 0) {
+ result = gnutls_x509_crt_get_preferred_hash_algorithm(issuer, &dig, NULL);
+ if (result < 0)
+ return gnutls_assert_val(result);
+ }
+
+ MODIFIED(crt);
+
+ /* disable all the unneeded OPTIONAL fields.
+ */
+ disable_optional_stuff(crt);
+
+ result = _gnutls_check_cert_sanity(crt);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ result = _gnutls_x509_pkix_sign(crt->cert, "tbsCertificate",
+ dig, flags, issuer, issuer_key);
+ if (result < 0) {
+ gnutls_assert();
+ return result;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_x509_crt_set_authority_info_access:
+ * @crt: Holds the certificate
+ * @what: what data to get, a #gnutls_info_access_what_t type.
+ * @data: output data to be freed with gnutls_free().
+ *
+ * This function sets the Authority Information Access (AIA)
+ * extension, see RFC 5280 section 4.2.2.1 for more information.
+ *
+ * The type of data stored in @data is specified via @what which
+ * should be #gnutls_info_access_what_t values.
+ *
+ * If @what is %GNUTLS_IA_OCSP_URI, @data will hold the OCSP URI.
+ * If @what is %GNUTLS_IA_CAISSUERS_URI, @data will hold the caIssuers
+ * URI.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_x509_crt_set_authority_info_access(gnutls_x509_crt_t crt,
+ int what, gnutls_datum_t * data)
+{
+ int ret;
+ gnutls_datum_t der = { NULL, 0 };
+ gnutls_datum_t new_der = { NULL, 0 };
+ gnutls_x509_aia_t aia_ctx = NULL;
+ const char *oid;
+ unsigned int c;
+
+ if (crt == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret = gnutls_x509_aia_init(&aia_ctx);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_x509_crt_get_extension(crt, GNUTLS_OID_AIA, 0, &der,
+ &c);
+ if (ret >= 0) { /* decode it */
+ ret = gnutls_x509_ext_import_aia(&der, aia_ctx, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ if (what == GNUTLS_IA_OCSP_URI)
+ oid = GNUTLS_OID_AD_OCSP;
+ else if (what == GNUTLS_IA_CAISSUERS_URI)
+ oid = GNUTLS_OID_AD_CAISSUERS;
+ else
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ ret = gnutls_x509_aia_set(aia_ctx, oid, GNUTLS_SAN_URI, data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_export_aia(aia_ctx, &new_der);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_OID_AIA,
+ &new_der, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ cleanup:
+ if (aia_ctx != NULL)
+ gnutls_x509_aia_deinit(aia_ctx);
+ _gnutls_free_datum(&new_der);
+ _gnutls_free_datum(&der);
+
+ return ret;
+}
+
+
+/**
+ * gnutls_x509_crt_set_policy:
+ * @crt: should contain a #gnutls_x509_crt_t type
+ * @policy: A pointer to a policy
+ * @critical: use non-zero if the extension is marked as critical
+ *
+ * This function will set the certificate policy extension (2.5.29.32).
+ * Multiple calls to this function append a new policy.
+ *
+ * Note the maximum text size for the qualifier %GNUTLS_X509_QUALIFIER_NOTICE
+ * is 200 characters. This function will fail with %GNUTLS_E_INVALID_REQUEST
+ * if this is exceeded.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.1.5
+ **/
+int
+gnutls_x509_crt_set_policy(gnutls_x509_crt_t crt,
+ const struct gnutls_x509_policy_st *policy,
+ unsigned int critical)
+{
+ int ret;
+ gnutls_datum_t der_data = {NULL, 0}, prev_der_data = { NULL, 0 };
+ gnutls_x509_policies_t policies = NULL;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = gnutls_x509_policies_init(&policies);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _gnutls_x509_crt_get_extension(crt, "2.5.29.32", 0,
+ &prev_der_data, NULL);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ ret = gnutls_x509_ext_import_policies(&prev_der_data,
+ policies, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = gnutls_x509_policies_set(policies, policy);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_ext_export_policies(policies, &der_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_crt_set_extension(crt, "2.5.29.32",
+ &der_data, 0);
+
+ cleanup:
+ if (policies != NULL)
+ gnutls_x509_policies_deinit(policies);
+ _gnutls_free_datum(&prev_der_data);
+ _gnutls_free_datum(&der_data);
+
+ return ret;
+}
+
+/**
+ * gnutls_x509_crt_set_spki:
+ * @crt: a certificate of type #gnutls_x509_crt_t
+ * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t
+ * @flags: must be zero
+ *
+ * This function will set the certificate's subject public key
+ * information explicitly. This is intended to be used in the cases
+ * where a single public key (e.g., RSA) can be used for multiple
+ * signature algorithms (RSA PKCS1-1.5, and RSA-PSS).
+ *
+ * To export the public key (i.e., the SubjectPublicKeyInfo part), check
+ * gnutls_pubkey_import_x509().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.0
+ **/
+int
+gnutls_x509_crt_set_spki(gnutls_x509_crt_t crt,
+ const gnutls_x509_spki_t spki,
+ unsigned int flags)
+{
+ int ret;
+ gnutls_pk_algorithm_t crt_pk;
+ gnutls_x509_spki_st tpki;
+ gnutls_pk_params_st params;
+ unsigned bits;
+
+ if (crt == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ ret = _gnutls_x509_crt_get_mpis(crt, &params);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ bits = pubkey_to_bits(&params);
+ crt_pk = params.algo;
+
+ if (!_gnutls_pk_are_compat(crt_pk, spki->pk)) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ if (spki->pk != GNUTLS_PK_RSA_PSS) {
+ if (crt_pk == spki->pk) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ memset(&tpki, 0, sizeof(gnutls_x509_spki_st));
+
+ if (crt_pk == GNUTLS_PK_RSA) {
+ const mac_entry_st *me;
+
+ me = hash_to_entry(spki->rsa_pss_dig);
+ if (unlikely(me == NULL)) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ tpki.pk = spki->pk;
+ tpki.rsa_pss_dig = spki->rsa_pss_dig;
+
+ /* If salt size is zero, find the optimal salt size. */
+ if (spki->salt_size == 0) {
+ ret = _gnutls_find_rsa_pss_salt_size(bits, me,
+ spki->salt_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ tpki.salt_size = ret;
+ } else
+ tpki.salt_size = spki->salt_size;
+ } else if (crt_pk == GNUTLS_PK_RSA_PSS) {
+ ret = _gnutls_x509_crt_read_spki_params(crt, &tpki);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ tpki.salt_size = spki->salt_size;
+ tpki.rsa_pss_dig = spki->rsa_pss_dig;
+ }
+
+ memcpy(&params.spki, &tpki, sizeof(tpki));
+ ret = _gnutls_x509_check_pubkey_params(&params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ MODIFIED(crt);
+
+ ret = _gnutls_x509_write_spki_params(crt->cert,
+ "tbsCertificate."
+ "subjectPublicKeyInfo.algorithm",
+ &tpki);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_pk_params_release(&params);
+ return ret;
+}
diff --git a/lib/x509_b64.c b/lib/x509_b64.c
new file mode 100644
index 0000000..668760a
--- /dev/null
+++ b/lib/x509_b64.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2000-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* Functions that relate to base64 encoding and decoding.
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <datum.h>
+#include <x509_b64.h>
+#include <nettle/base64.h>
+
+#define INCR(what, size, max_len) \
+ do { \
+ what+=size; \
+ if (what > max_len) { \
+ gnutls_assert(); \
+ gnutls_free( result->data); result->data = NULL; \
+ return GNUTLS_E_INTERNAL_ERROR; \
+ } \
+ } while(0)
+
+/* encodes data and puts the result into result (locally allocated)
+ * The result_size (including the null terminator) is the return value.
+ */
+int
+_gnutls_fbase64_encode(const char *msg, const uint8_t * data,
+ size_t data_size, gnutls_datum_t * result)
+{
+ int tmp;
+ unsigned int i;
+ uint8_t tmpres[66];
+ uint8_t *ptr;
+ char top[80];
+ char bottom[80];
+ size_t size, max, bytes;
+ int pos, top_len = 0, bottom_len = 0;
+ unsigned raw_encoding = 0;
+
+ if (msg == NULL || msg[0] == 0)
+ raw_encoding = 1;
+
+ if (!raw_encoding) {
+ if (strlen(msg) > 50) {
+ gnutls_assert();
+ return GNUTLS_E_BASE64_ENCODING_ERROR;
+ }
+
+ _gnutls_str_cpy(top, sizeof(top), "-----BEGIN ");
+ _gnutls_str_cat(top, sizeof(top), msg);
+ _gnutls_str_cat(top, sizeof(top), "-----\n");
+
+ _gnutls_str_cpy(bottom, sizeof(bottom), "-----END ");
+ _gnutls_str_cat(bottom, sizeof(bottom), msg);
+ _gnutls_str_cat(bottom, sizeof(bottom), "-----\n");
+
+ top_len = strlen(top);
+ bottom_len = strlen(bottom);
+ }
+
+ max = B64FSIZE(top_len + bottom_len, data_size);
+
+ result->data = gnutls_malloc(max + 1);
+ if (result->data == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ bytes = 0;
+ INCR(bytes, top_len, max);
+ pos = top_len;
+
+ memcpy(result->data, top, top_len);
+
+ for (i = 0; i < data_size; i += 48) {
+ if (data_size - i < 48)
+ tmp = data_size - i;
+ else
+ tmp = 48;
+
+ size = BASE64_ENCODE_RAW_LENGTH(tmp);
+ if (sizeof(tmpres) < size)
+ return gnutls_assert_val(GNUTLS_E_BASE64_ENCODING_ERROR);
+
+ base64_encode_raw((void*)tmpres, tmp, &data[i]);
+
+ INCR(bytes, size + 1, max);
+ ptr = &result->data[pos];
+
+ memcpy(ptr, tmpres, size);
+ ptr += size;
+ pos += size;
+ if (!raw_encoding) {
+ *ptr++ = '\n';
+ pos++;
+ } else {
+ bytes--;
+ }
+ }
+
+ INCR(bytes, bottom_len, max);
+
+ memcpy(&result->data[bytes - bottom_len], bottom, bottom_len);
+ result->data[bytes] = 0;
+ result->size = bytes;
+
+ return max + 1;
+}
+
+/**
+ * gnutls_pem_base64_encode:
+ * @msg: is a message to be put in the header (may be %NULL)
+ * @data: contain the raw data
+ * @result: the place where base64 data will be copied
+ * @result_size: holds the size of the result
+ *
+ * This function will convert the given data to printable data, using
+ * the base64 encoding. This is the encoding used in PEM messages.
+ *
+ * The output string will be null terminated, although the output size will
+ * not include the terminating null.
+ *
+ * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
+ * not long enough, or 0 on success.
+ **/
+int
+gnutls_pem_base64_encode(const char *msg, const gnutls_datum_t * data,
+ char *result, size_t * result_size)
+{
+ gnutls_datum_t res;
+ int ret;
+
+ ret = _gnutls_fbase64_encode(msg, data->data, data->size, &res);
+ if (ret < 0)
+ return ret;
+
+ if (result == NULL || *result_size < (unsigned) res.size) {
+ gnutls_free(res.data);
+ *result_size = res.size + 1;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ } else {
+ memcpy(result, res.data, res.size);
+ gnutls_free(res.data);
+ *result_size = res.size;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pem_base64_encode2:
+ * @header: is a message to be put in the encoded header (may be %NULL)
+ * @data: contains the raw data
+ * @result: will hold the newly allocated encoded data
+ *
+ * This function will convert the given data to printable data, using
+ * the base64 encoding. This is the encoding used in PEM messages.
+ * This function will allocate the required memory to hold the encoded
+ * data.
+ *
+ * You should use gnutls_free() to free the returned data.
+ *
+ * Note, that prior to GnuTLS 3.4.0 this function was available
+ * under the name gnutls_pem_base64_encode_alloc(). There is
+ * compatibility macro pointing to this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
+ * an error code is returned.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_pem_base64_encode2(const char *header,
+ const gnutls_datum_t * data,
+ gnutls_datum_t * result)
+{
+ int ret;
+
+ if (result == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gnutls_fbase64_encode(header, data->data, data->size, result);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+/* copies data to result but removes newlines and <CR>
+ * returns the size of the data copied.
+ *
+ * It will fail with GNUTLS_E_BASE64_DECODING_ERROR if the
+ * end-result is the empty string.
+ */
+inline static int
+cpydata(const uint8_t * data, int data_size, gnutls_datum_t * result)
+{
+ int i, j;
+
+ result->data = gnutls_malloc(data_size + 1);
+ if (result->data == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ for (j = i = 0; i < data_size; i++) {
+ if (data[i] == '\n' || data[i] == '\r' || data[i] == ' '
+ || data[i] == '\t')
+ continue;
+ else if (data[i] == '-')
+ break;
+ result->data[j] = data[i];
+ j++;
+ }
+
+ result->size = j;
+ result->data[j] = 0;
+
+ if (j==0) {
+ gnutls_free(result->data);
+ return gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
+ }
+
+ return j;
+}
+
+/* decodes data and puts the result into result (locally allocated).
+ * Note that encodings of zero-length strings are being rejected
+ * with GNUTLS_E_BASE64_DECODING_ERROR.
+ *
+ * The result_size is the return value.
+ */
+int
+_gnutls_base64_decode(const uint8_t * data, size_t data_size,
+ gnutls_datum_t * result)
+{
+ int ret;
+ size_t size;
+ gnutls_datum_t pdata;
+ struct base64_decode_ctx ctx;
+
+ if (data_size == 0) {
+ result->data = (unsigned char*)gnutls_strdup("");
+ if (result->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ result->size = 0;
+ return 0;
+ }
+
+ ret = cpydata(data, data_size, &pdata);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ base64_decode_init(&ctx);
+
+ size = BASE64_DECODE_LENGTH(pdata.size);
+ if (size == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
+ goto cleanup;
+ }
+
+ result->data = gnutls_malloc(size);
+ if (result->data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ ret = base64_decode_update(&ctx, &size, result->data,
+ pdata.size, (void*)pdata.data);
+ if (ret == 0 || size == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_BASE64_DECODING_ERROR;
+ goto fail;
+ }
+
+ ret = base64_decode_final(&ctx);
+ if (ret != 1) {
+ ret = gnutls_assert_val(GNUTLS_E_BASE64_DECODING_ERROR);
+ goto fail;
+ }
+
+ result->size = size;
+
+ ret = size;
+ goto cleanup;
+
+ fail:
+ gnutls_free(result->data);
+
+ cleanup:
+ gnutls_free(pdata.data);
+ return ret;
+}
+
+
+/* Searches the given string for ONE PEM encoded certificate, and
+ * stores it in the result.
+ *
+ * The result_size (always non-zero) is the return value,
+ * or a negative error code.
+ */
+#define ENDSTR "-----"
+int
+_gnutls_fbase64_decode(const char *header, const uint8_t * data,
+ size_t data_size, gnutls_datum_t * result)
+{
+ int ret;
+ static const char top[] = "-----BEGIN ";
+ static const char bottom[] = "-----END ";
+ uint8_t *rdata, *kdata;
+ int rdata_size;
+ char pem_header[128];
+
+ _gnutls_str_cpy(pem_header, sizeof(pem_header), top);
+ if (header != NULL)
+ _gnutls_str_cat(pem_header, sizeof(pem_header), header);
+
+ rdata = memmem(data, data_size, pem_header, strlen(pem_header));
+ if (rdata == NULL) {
+ gnutls_assert();
+ _gnutls_hard_log("Could not find '%s'\n", pem_header);
+ return GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR;
+ }
+
+ data_size -= MEMSUB(rdata, data);
+
+ if (data_size < 4 + strlen(bottom)) {
+ gnutls_assert();
+ return GNUTLS_E_BASE64_DECODING_ERROR;
+ }
+
+ kdata =
+ memmem(rdata + 1, data_size - 1, ENDSTR, sizeof(ENDSTR) - 1);
+ /* allow CR as well.
+ */
+ if (kdata == NULL) {
+ gnutls_assert();
+ _gnutls_hard_log("Could not find '%s'\n", ENDSTR);
+ return GNUTLS_E_BASE64_DECODING_ERROR;
+ }
+ data_size -= strlen(ENDSTR);
+ data_size -= MEMSUB(kdata, rdata);
+
+ rdata = kdata + strlen(ENDSTR);
+
+ /* position is now after the ---BEGIN--- headers */
+
+ kdata = memmem(rdata, data_size, bottom, strlen(bottom));
+ if (kdata == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_BASE64_DECODING_ERROR;
+ }
+
+ /* position of kdata is before the ----END--- footer
+ */
+ rdata_size = MEMSUB(kdata, rdata);
+
+ if (rdata_size < 4) {
+ gnutls_assert();
+ return GNUTLS_E_BASE64_DECODING_ERROR;
+ }
+
+ if ((ret = _gnutls_base64_decode(rdata, rdata_size, result)) < 0) {
+ gnutls_assert();
+ return GNUTLS_E_BASE64_DECODING_ERROR;
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_pem_base64_decode:
+ * @header: A null terminated string with the PEM header (eg. CERTIFICATE)
+ * @b64_data: contain the encoded data
+ * @result: the place where decoded data will be copied
+ * @result_size: holds the size of the result
+ *
+ * This function will decode the given encoded data. If the header
+ * given is non %NULL this function will search for "-----BEGIN header"
+ * and decode only this part. Otherwise it will decode the first PEM
+ * packet found.
+ *
+ * Returns: On success %GNUTLS_E_SUCCESS (0) is returned,
+ * %GNUTLS_E_SHORT_MEMORY_BUFFER is returned if the buffer given is
+ * not long enough, or 0 on success.
+ **/
+int
+gnutls_pem_base64_decode(const char *header,
+ const gnutls_datum_t * b64_data,
+ unsigned char *result, size_t * result_size)
+{
+ gnutls_datum_t res;
+ int ret;
+
+ ret =
+ _gnutls_fbase64_decode(header, b64_data->data, b64_data->size,
+ &res);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (result == NULL || *result_size < (unsigned) res.size) {
+ gnutls_free(res.data);
+ *result_size = res.size;
+ return GNUTLS_E_SHORT_MEMORY_BUFFER;
+ } else {
+ memcpy(result, res.data, res.size);
+ gnutls_free(res.data);
+ *result_size = res.size;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_pem_base64_decode2:
+ * @header: The PEM header (eg. CERTIFICATE)
+ * @b64_data: contains the encoded data
+ * @result: the location of decoded data
+ *
+ * This function will decode the given encoded data. The decoded data
+ * will be allocated, and stored into result. If the header given is
+ * non null this function will search for "-----BEGIN header" and
+ * decode only this part. Otherwise it will decode the first PEM
+ * packet found.
+ *
+ * You should use gnutls_free() to free the returned data.
+ *
+ * Note, that prior to GnuTLS 3.4.0 this function was available
+ * under the name gnutls_pem_base64_decode_alloc(). There is
+ * compatibility macro pointing to this function.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
+ * an error code is returned.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_pem_base64_decode2(const char *header,
+ const gnutls_datum_t * b64_data,
+ gnutls_datum_t * result)
+{
+ int ret;
+
+ if (result == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret =
+ _gnutls_fbase64_decode(header, b64_data->data, b64_data->size,
+ result);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+/**
+ * gnutls_base64_decode2:
+ * @base64: contains the encoded data
+ * @result: the location of decoded data
+ *
+ * This function will decode the given base64 encoded data. The decoded data
+ * will be allocated, and stored into result.
+ *
+ * You should use gnutls_free() to free the returned data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
+ * an error code is returned.
+ *
+ * Since: 3.6.0
+ **/
+int
+gnutls_base64_decode2(const gnutls_datum_t *base64,
+ gnutls_datum_t *result)
+{
+ int ret;
+
+ ret = _gnutls_base64_decode(base64->data, base64->size, result);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_base64_encode2:
+ * @data: contains the raw data
+ * @result: will hold the newly allocated encoded data
+ *
+ * This function will convert the given data to printable data, using
+ * the base64 encoding. This function will allocate the required
+ * memory to hold the encoded data.
+ *
+ * You should use gnutls_free() to free the returned data.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
+ * an error code is returned.
+ *
+ * Since: 3.6.0
+ **/
+int
+gnutls_base64_encode2(const gnutls_datum_t *data,
+ gnutls_datum_t *result)
+{
+ int ret;
+
+ if (result == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gnutls_fbase64_encode(NULL, data->data, data->size, result);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
diff --git a/lib/x509_b64.h b/lib/x509_b64.h
new file mode 100644
index 0000000..271850a
--- /dev/null
+++ b/lib/x509_b64.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2000-2012 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_X509_B64_H
+#define GNUTLS_LIB_X509_B64_H
+
+int _gnutls_fbase64_encode(const char *msg, const uint8_t * data,
+ size_t data_size, gnutls_datum_t * result);
+int _gnutls_fbase64_decode(const char *header, const uint8_t * data,
+ size_t data_size, gnutls_datum_t * result);
+
+int
+_gnutls_base64_decode(const uint8_t * data, size_t data_size,
+ gnutls_datum_t * result);
+
+#define B64SIZE( data_size) ((data_size%3==0)?((data_size*4)/3):(4+((data_size/3)*4)))
+
+/* The size for B64 encoding + newlines plus header
+ */
+
+#define B64FSIZE( hsize, dsize) \
+ (B64SIZE(dsize) + (hsize) + /*newlines*/ \
+ B64SIZE(dsize)/64 + (((B64SIZE(dsize) % 64) > 0) ? 1 : 0))
+
+#endif /* GNUTLS_LIB_X509_B64_H */