[gnutls-help] generating self signed cert - no way to set spki

Curtis Villamizar curtis at ipv6.occnc.com
Tue Aug 25 02:09:15 CEST 2020


I'm working with code that worked with prior releases of gnutls (a
year ago?) but with the current version (3.6.14) gnutls_x509_crt_sign2
produces a GNUTLS_E_ASN1_ELEMENT_NOT_FOUND apparently in
_gnutls_x509_pkix_sign when it tries _gnutls_privkey_get_spki_params .

fyi- My immediate need is to sign some new application certs.  I also
have a second level CA cert that needs resigning.  When the code
didn't work I tried generating a new top level CA for testing and that
led to some of the issues mentioned here.  These keys and certs are
normally used internally (mua-msa, mta-mda, imap, spamc-spamd,
internal https) and other internal use with a local copy of a CA cert
or DANE plus for SMTP with DANE for general email use.

Initially I tried gnutls_x509_*_get_spki on the crt, crq to see if I
could get a gnutls_x509_spki_t out of any of them.  Even the working
CA certs return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE .

The ugly short cut is to cheat and copy "typedef struct
gnutls_x509_spki_st { ... } gnutls_x509_spki_st;" from
crypto-backend.h into my code, fill it in and pass it to
gnutls_x509_privkey_set_spki but I don't want to do that and I'm sure
no one wants me to do that.  The code I used is:

    struct __spki {
      gnutls_pk_algorithm_t pk;
      gnutls_digest_algorithm_t rsa_pss_dig;  // RSA-PSS
      unsigned int salt_size;  // RSA-PSS
      unsigned int legacy;  // ignore - set to zero
      gnutls_digest_algorithm_t dsa_dig;
      unsigned int flags;  // 0 or GNUTLS_PK_FLAG_REPRODUCIBLE
    };
    struct __spki * pt;
    pt = (struct __spki *) spki;
    pt->pk = (gnutls_pk_algorithm_t)
      gnutls_x509_crt_get_pk_algorithm(auth_crt, 0);
    pt->rsa_pss_dig = GNUTLS_DIG_SHA256;
    pt->salt_size = 20;
    pt->legacy = 0;
    pt->dsa_dig = GNUTLS_DIG_SHA256;

There is no function in the interface (documented or that I found in
the .h files) to fill in a gnutls_x509_spki_t or alternate way to
provide it in signing.  There are functions to get it from some struct
and set it in something else.  There is no spki in the issuer yet
since this is a self signing and the crt and issuer pointers are the
same and is not yet signed.  Chicken and egg?  This should only affect
the epoch self signing.

This used to work as recently as August 2019 based on some of the
certs I checked.  The certs generated with a prior gnutls contain
Subject Public Key Info objects but with only the Public Key Algorith
filled in and they have Signature Algorithm from which the digest can
be determined.

Am I missing something?

Having temporarily worked around that with the hack above I got
problems in _gnutls_x509_pkix_sign calling _gnutls_x509_der_encode on
the cert.  Below is some debug code and an interim fix/workaround.
The debug code is obvios (writing to stderr and a /tmp file).  The
fix/workaround is the code involving buf and sizeof buf.  In digging
into asn1_der_coding I found the this commit
https://gitlab.com/gnutls/gnutls/-/commit/49d27a55031e72ade52984f5cd94e82e97b46228
and looked into the coding.

The higher level error that I got turned out to be a rejection of a
valid (13 char utcTime) tbsCertificate.validity dumped below.

name:validity  type:SEQUENCE
  name:notBefore  type:CHOICE
    name:utcTime  type:UTC_TIME  value:200824000118Z
  name:notAfter  type:CHOICE
    name:utcTime  type:UTC_TIME  value:250824000118Z

There was a comment by smav at a lower level about an in-place
modification for no apparent reason which prompted the buf and sizeof
buf hack below.  That turned out to fix it, though it should have had
no effect.  The patch with debug is here (for your entertainment, not
to apply the patch).

--- lib/x509/common.c.orig	2020-06-01 05:48:13.000000000 -0400
+++ lib/x509/common.c	2020-08-24 21:07:24.264406000 -0400
@@ -843,19 +843,66 @@
 _gnutls_x509_der_encode(ASN1_TYPE src, const char *src_name,
 			gnutls_datum_t * res, int str)
 {
+	char buf[4096];  // stack space is cheap
 	int size, result;
 	int asize;
 	uint8_t *data = NULL;
 	ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
 
-	size = 0;
-	result = asn1_der_coding(src, src_name, NULL, &size, NULL);
+	fprintf(stderr, "\n_gnutls_x509_der_encode: %s\n", src_name);
+
+	size = sizeof(buf) - 1;
+	result = asn1_der_coding(src, src_name, buf, &size, NULL);
 	/* this check explicitly covers the case where size == 0 && result == 0 */
-	if (result != ASN1_MEM_ERROR) {
+	if (result != ASN1_MEM_ERROR && result != 0) {
+
+		FILE *fio = fopen("/tmp/asn1_src", "w");
+		asn1_print_structure(fio, src, src_name, ASN1_PRINT_ALL);
+
+		fprintf(stderr,
+			"\n_gnutls_x509_der_encode: der coding err %s\n",
+			asn1_strerror(result));
+
+		if ((result == ASN1_VALUE_NOT_VALID)
+		    && (0 == strcmp(src_name, "tbsCertificate"))) {
+			const char * strlist[] = {
+				"tbsCertificate.version",
+				"tbsCertificate.serialNumber",
+				"tbsCertificate.signature",
+				"tbsCertificate.issuer",
+				"tbsCertificate.validity",
+				"tbsCertificate.subject",
+				"tbsCertificate.subjectPublicKeyInfo",
+				"tbsCertificate.extensions",
+				0
+			};
+			int r2;
+			const char **pt;
+			pt = &strlist[0];
+			for ( ; *pt; ++pt) {
+				size = sizeof(buf) - 1;
+		      		r2 = asn1_der_coding(src, *pt,
+						     buf, &size, NULL);
+				if (r2 != ASN1_MEM_ERROR) {
+					fprintf(stderr,
+						"\n_gnutls_x509_der_encode: "
+						"attr %s err %s\n",
+						*pt, asn1_strerror(r2));
+					fprintf(fio, "\n\n");
+					asn1_print_structure(fio, src, *pt,
+							     ASN1_PRINT_ALL);
+				}
+			}
+		}
+
+		fclose(fio);
+
 		gnutls_assert();
 		return _gnutls_asn2err(result);
 	}
 
+	fprintf(stderr, "\n_gnutls_x509_der_encode alloc\n");
+
 	/* allocate data for the der
 	 */
 
@@ -869,6 +916,8 @@
 		return GNUTLS_E_MEMORY_ERROR;
 	}
 
+	fprintf(stderr, "\n_gnutls_x509_der_encode asn1_der_coding\n");
+
 	result = asn1_der_coding(src, src_name, data, &size, NULL);
 	if (result != ASN1_SUCCESS) {
 		gnutls_assert();
@@ -876,6 +925,8 @@
 		goto cleanup;
 	}
 
+	fprintf(stderr, "\n_gnutls_x509_der_encode str fixup\n");
+
 	if (str) {
 		if ((result = asn1_create_element
 		     (_gnutls_get_pkix(), "PKIX1.pkcs-7-Data",
@@ -903,6 +954,8 @@
 
 		asn1_delete_structure(&c2);
 	}
+
+	fprintf(stderr, "\n_gnutls_x509_der_encode done!\n\n");
 
 	res->data = data;
 	res->size = (unsigned) size;

The part of the patch that constitutes a fix/workaround (that works
for no good reason) is here.

--- lib/x509/common.c.orig	2020-06-01 05:48:13.000000000 -0400
+++ lib/x509/common.c	2020-08-24 22:24:06.668138000 -0400
@@ -843,15 +843,16 @@
 _gnutls_x509_der_encode(ASN1_TYPE src, const char *src_name,
 			gnutls_datum_t * res, int str)
 {
+	char buf[4096];  // stack space is cheap
 	int size, result;
 	int asize;
 	uint8_t *data = NULL;
 	ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
 
-	size = 0;
-	result = asn1_der_coding(src, src_name, NULL, &size, NULL);
+	size = sizeof(buf) - 1;
+	result = asn1_der_coding(src, src_name, buf, &size, NULL);
 	/* this check explicitly covers the case where size == 0 && result == 0 */
-	if (result != ASN1_MEM_ERROR) {
+	if (result != ASN1_MEM_ERROR && result != 0) {
 		gnutls_assert();
 		return _gnutls_asn2err(result);
 	}

So there are two issues here:

  1.  No way to fill in a spki struct.  I may be missing something.

  2.  Fix/workaround in _gnutls_x509_der_encode that works for no
      apparent reason and may point to an issue downstack with a
      modify in place according to a comment or may be something else
      but is an issue.  It causes a legitimate struct to be rejected
      by asn1_der_coding which is itself a bug and why the patch above
      fixes it is a mystery.

I'm not even sure how to form a bug report (or two bugs) on this but
in both cases I have an interim workaround.  I have not cooked this
down to a simple test case to reproduce the error.  Not being familiar
with the internals here I figured I'd ask before filing a bug report.

Thanks in advance,

Curtis



More information about the Gnutls-help mailing list