diff --git a/lib/hx509/Makefile.am b/lib/hx509/Makefile.am index 5b1f997a7..4ebb56c77 100644 --- a/lib/hx509/Makefile.am +++ b/lib/hx509/Makefile.am @@ -169,12 +169,15 @@ fuzz_jose_SOURCES = fuzz_jose.c fuzz_jose_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/base/libheimbase.la fuzz_jose.$(OBJEXT): $(HX509_PROTOS) $(nodist_include_HEADERS) test_expr_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la +test_cert_binary_CPPFLAGS = $(AM_CPPFLAGS) -DSRCDIR=\"$(srcdir)\" +test_cert_binary_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la TESTS = $(SCRIPT_TESTS) $(PROGRAM_TESTS) PROGRAM_TESTS = \ test_name \ - test_expr + test_expr \ + test_cert_binary SCRIPT_TESTS = \ test_ca \ diff --git a/lib/hx509/cert.c b/lib/hx509/cert.c index df857bc23..d16d06807 100644 --- a/lib/hx509/cert.c +++ b/lib/hx509/cert.c @@ -78,6 +78,7 @@ struct hx509_cert_data { unsigned int ref; char *friendlyname; Certificate *data; + heim_octet_string encoding; hx509_private_key private_key; struct _hx509_cert_attrs attrs; hx509_name basename; @@ -635,6 +636,8 @@ cert_init(hx509_context context, heim_error_t *error) cert->release = NULL; cert->ctx = NULL; cert->data= NULL; + cert->encoding.data = NULL; + cert->encoding.length = 0; return cert; } @@ -693,7 +696,23 @@ hx509_cert_copy_no_private_key(hx509_context context, hx509_cert src, heim_error_t *error) { - return hx509_cert_init(context, src->data, error); + hx509_cert cert; + int ret; + + cert = hx509_cert_init(context, src->data, error); + if (cert == NULL) + return NULL; + + ret = der_copy_octet_string(&src->encoding, &cert->encoding); + if (ret) { + hx509_cert_free(cert); + if (error) + *error = heim_error_create_enomem(); + errno = ret; + return NULL; + } + + return cert; } /** @@ -747,6 +766,7 @@ hx509_cert_init_data(hx509_context context, heim_error_t *error) { hx509_cert cert; + heim_octet_string os; Certificate t; size_t size; int ret; @@ -769,6 +789,19 @@ hx509_cert_init_data(hx509_context context, cert = hx509_cert_init(context, &t, error); free_Certificate(&t); + if (cert == NULL) + return NULL; + + os.length = len; + os.data = rk_UNCONST(ptr); + ret = der_copy_octet_string(&os, &cert->encoding); + if (ret) { + hx509_cert_free(cert); + if (error) + *error = heim_error_create_enomem(); + errno = ret; + return NULL; + } return cert; } @@ -824,6 +857,7 @@ hx509_cert_free(hx509_cert cert) if (cert->data) free_Certificate(cert->data); free(cert->data); + der_free_octet_string(&cert->encoding); for (i = 0; i < cert->attrs.len; i++) { der_free_octet_string(&cert->attrs.val[i]->data); @@ -4057,6 +4091,9 @@ hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os) os->data = NULL; os->length = 0; + if (c->encoding.data != NULL) + return der_copy_octet_string(&c->encoding, os); + ASN1_MALLOC_ENCODE(Certificate, os->data, os->length, _hx509_get_cert(c), &size, ret); if (ret) { diff --git a/lib/hx509/test_cert_binary.c b/lib/hx509/test_cert_binary.c new file mode 100644 index 000000000..e8f80767e --- /dev/null +++ b/lib/hx509/test_cert_binary.c @@ -0,0 +1,179 @@ +#include "hx_locl.h" +#include + +#ifndef SRCDIR +#define SRCDIR "." +#endif + +struct pem_cert { + heim_octet_string der; + int found; +}; + +static int +pem_cert_reader(hx509_context context, const char *type, + const hx509_pem_header *headers, + const void *data, size_t length, void *ctx) +{ + struct pem_cert *pem = ctx; + + (void)context; + (void)headers; + + if (strcmp(type, "CERTIFICATE") != 0) + return 0; + + if (pem->found) + return 0; + + pem->der.data = malloc(length); + if (pem->der.data == NULL) + return ENOMEM; + memcpy(pem->der.data, data, length); + pem->der.length = length; + pem->found = 1; + + return 0; +} + +static void +read_cert_der(hx509_context context, const char *path, heim_octet_string *der) +{ + struct pem_cert pem; + FILE *f; + int ret; + + memset(&pem, 0, sizeof(pem)); + + f = fopen(path, "r"); + if (f == NULL) + err(1, "fopen: %s", path); + + ret = hx509_pem_read(context, f, pem_cert_reader, &pem); + fclose(f); + if (ret) + hx509_err(context, 1, ret, "hx509_pem_read: %s", path); + if (!pem.found) + errx(1, "did not find a certificate in %s", path); + + *der = pem.der; +} + +static void +compare_octet_string(const char *what, + const heim_octet_string *expected, + const heim_octet_string *actual) +{ + if (actual->length != expected->length) + errx(1, "%s length changed from %lu to %lu", + what, (unsigned long)expected->length, + (unsigned long)actual->length); + if (memcmp(actual->data, expected->data, expected->length) != 0) + errx(1, "%s data changed", what); +} + +static void +check_cert_binary(hx509_context context, const heim_octet_string *der) +{ + heim_octet_string os; + hx509_cert cert; + int ret; + + memset(&os, 0, sizeof(os)); + + cert = hx509_cert_init_data(context, der->data, der->length, NULL); + if (cert == NULL) + err(1, "hx509_cert_init_data"); + + ret = hx509_cert_binary(context, cert, &os); + if (ret) + hx509_err(context, 1, ret, "hx509_cert_binary"); + compare_octet_string("certificate encoding", der, &os); + der_free_octet_string(&os); + + hx509_cert_free(cert); +} + +static char * +make_store_name(void) +{ + char *store_name = NULL; + + if (asprintf(&store_name, "FILE:%s/data/test.crt,%s/data/test.key", + SRCDIR, SRCDIR) == -1 || store_name == NULL) + err(1, "asprintf"); + + return store_name; +} + +static void +check_keyless_store_cert_encoding(hx509_context context, + const heim_octet_string *der) +{ + heim_octet_string os; + hx509_certs certs = NULL; + hx509_cert cert = NULL; + char *store_name; + int ret; + + memset(&os, 0, sizeof(os)); + + ret = hx509_certs_init(context, "MEMORY:test-keyless-certs", + HX509_CERTS_NO_PRIVATE_KEYS, NULL, &certs); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_init"); + + store_name = make_store_name(); + ret = hx509_certs_append(context, certs, NULL, store_name); + free(store_name); + if (ret) + hx509_err(context, 1, ret, "hx509_certs_append"); + + ret = hx509_get_one_cert(context, certs, &cert); + if (ret) + hx509_err(context, 1, ret, "hx509_get_one_cert"); + if (hx509_cert_have_private_key(cert)) + errx(1, "HX509_CERTS_NO_PRIVATE_KEYS store kept a private key"); + + ret = hx509_cert_binary(context, cert, &os); + if (ret) + hx509_err(context, 1, ret, "hx509_cert_binary on keyless cert"); + compare_octet_string("keyless store certificate encoding", der, &os); + + der_free_octet_string(&os); + hx509_cert_free(cert); + hx509_certs_free(&certs); +} + +int +main(int argc, char **argv) +{ + heim_octet_string der; + hx509_context context; + char *cert_path = NULL; + int ret; + + (void)argc; + (void)argv; + + memset(&der, 0, sizeof(der)); + + ret = hx509_context_init(&context); + if (ret) + errx(1, "hx509_context_init failed with %d", ret); + + if (asprintf(&cert_path, "%s/data/test.crt", SRCDIR) == -1 || + cert_path == NULL) + err(1, "asprintf"); + + read_cert_der(context, cert_path, &der); + free(cert_path); + + check_cert_binary(context, &der); + check_keyless_store_cert_encoding(context, &der); + + der_free_octet_string(&der); + hx509_context_free(&context); + + return 0; +}