diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index e577bffc..58c1a3c3 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -24,12 +24,13 @@ endif() # Demos that are usable but only rarely make sense to be installed # # ----------------------------------------------------------------------------- -set(USABLE_DEMOS aesgcm constants crypt der_print_flexi latex-tables openssh-privkey openssl-enc sizes timing) +set(USABLE_DEMOS aesgcm constants crypt der_print_flexi latex-tables openssh-privkey openssl-enc sizes timing x509_verify) list(JOIN USABLE_DEMOS " " USABLE_DEMOS_STR) option(BUILD_USABLE_DEMOS "Build usable demos (${USABLE_DEMOS_STR})" FALSE) if(BUILD_USABLE_DEMOS) list(APPEND USABLE_DEMOS_TARGETS ${USABLE_DEMOS}) + x509_verify endif() # ----------------------------------------------------------------------------- diff --git a/demos/x509_verify.c b/demos/x509_verify.c new file mode 100644 index 00000000..8df7a1f3 --- /dev/null +++ b/demos/x509_verify.c @@ -0,0 +1,166 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +/* load a X.509 certificate chain and verify its validity */ +#include +#include + +#ifdef LTC_QUIET +static void print_err(const char *fmt, ...) +{ + LTC_UNUSED_PARAM(fmt); +} + +#define print_stderr(...) +#else +static void print_err(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +#define print_stderr(...) fprintf(stderr, ##__VA_ARGS__) +#endif + +#if defined(LTC_TEST_DBG) && LTC_TEST_DBG > 1 +#define LTC_DER_PRINT_FLEXI_NO_MAIN +#include "der_print_flexi.c" + +static void s_der_print_flexi(const ltc_asn1_list* l) +{ + print_stderr("\n\n"); + s_der_print_flexi_i(l, 0); + print_stderr("\n\n"); +} +#else +static void s_der_print_flexi(const ltc_asn1_list* l) +{ + LTC_UNUSED_PARAM(l); +} +#endif + +static unsigned long num_certs; +static const ltc_x509_certificate *cert[256] = {0}; +static FILE *f; + +static void die_(int err, int line) +{ + unsigned long n; + print_err("%3d: LTC sez %s\n", line, error_to_string(err)); + for (n = num_certs; n --> 0;) { + x509_free(&cert[n]); + } + if (f) fclose(f); + exit(EXIT_FAILURE); +} + +#define die(i) do { die_(i, __LINE__); } while(0) +#define DIE(s, ...) do { print_err("%3d: " s "\n", __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); } while(0) +#ifndef LTC_ARRAY_SIZE +#define LTC_ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) +#endif + +int main(int argc, char **argv) +{ + const unsigned char zero_cert_buf[sizeof(cert)] = {0}; + int err, argn = 1; + unsigned long len, processed, n; + long tell, tot_data = 0; + + if ((err = register_all_hashes()) != CRYPT_OK) { + die(err); + } + if ((err = crypt_mp_init("ltm")) != CRYPT_OK) { + die(err); + } + +next: + tot_data = processed = num_certs = n = 0; + if (argc > argn) f = fopen(argv[argn], "r"); + else f = stdin; + if (f == NULL) DIE("fopen sez no"); + if (f != stdin) { + fseek(f, 0, SEEK_END); + tot_data = ftell(f); + fseek(f, 0, SEEK_SET); + tell = 0; + } else { + tell = -1; + } + + print_stderr("-=-=-=-=-=-=-\nDecode %s\n=-=-=-=-=-=-=\n", argv[argn]); + + while (tell != tot_data) { + err = x509_import_pem_filehandle(f, &cert[n]); + if (err == CRYPT_PK_ASN1_ERROR || err == CRYPT_UNKNOWN_PEM) + continue; + else if (err != CRYPT_OK) + break; + if (cert[n] && cert[n]->asn1) + s_der_print_flexi(cert[n]->asn1); + if (f != stdin) { + tell = ftell(f); + print_stderr("%2lu len: %ld - tot: %ld - processed: %lu (%s)\n", n, tell, tot_data, processed, error_to_string(err)); + len = tell - processed; + processed += len; + } + n++; + if (n == LTC_ARRAY_SIZE(cert)) + break; + } + num_certs = n; + print_stderr("len: %ld - tot: %ld - processed: %lu (%s)\n", tell, tot_data, processed, error_to_string(err)); + if (err && argc > argn) goto check_next; + if (err && err != CRYPT_NOP) die(err); + for (n = 0; n < num_certs; ++n) { + unsigned long m = n + 1 == num_certs ? n : n + 1; + int stat; + if ((err = x509_cert_is_signed_by(cert[n], &cert[m]->tbs_certificate.subject_public_key_info, &stat)) != CRYPT_OK) { + print_err("%3d: LTC sez %s\n", __LINE__, error_to_string(err)); + if (m == n) { + print_stderr("Cert is last in chain, but not self-signed.\n"); + } else { + break; + } + } + { + const ltc_x509_string *subjects[4]; + const ltc_x509_name *subject, *issuer; + int issuer_matches_next_subject = x509_cmp_name(&cert[n]->tbs_certificate.issuer, &cert[m]->tbs_certificate.subject); + subject = &cert[n]->tbs_certificate.subject; + if (n != m) { + issuer = &cert[m]->tbs_certificate.subject; + } else { + issuer = &cert[m]->tbs_certificate.issuer; + } + x509_name_detail_get(subject, LTC_X509_CN, &subjects[0]); + x509_name_detail_get(subject, LTC_X509_O, &subjects[2]); + x509_name_detail_get(issuer, LTC_X509_CN, &subjects[1]); + x509_name_detail_get(issuer, LTC_X509_O, &subjects[3]); +#define X509_STRING_STR(s) (s) ? (s)->str : "NULL" + print_stderr("Cert: %s - %s\nCA: %s - %s\nIssuer matches next subject: %s\nVerify: %s\n", + X509_STRING_STR(subjects[0]), X509_STRING_STR(subjects[2]), + X509_STRING_STR(subjects[1]), X509_STRING_STR(subjects[3]), + issuer_matches_next_subject ? "True" : "False", stat ? "Success" : "Failed"); + /* In case of LTC_QUIET this would show up as unused. */ + LTC_UNUSED_PARAM(issuer_matches_next_subject); + } + } +check_next: + for (n = num_certs; n --> 0;) { + x509_free(&cert[n]); + } + if (XMEMCMP(cert, zero_cert_buf, sizeof(zero_cert_buf))) { + DIE("cert buf not completely cleaned"); + } + if (f != stdin) { + fclose(f); + argn++; + if (argc > argn) { + goto next; + } + } + return 0; +} diff --git a/makefile.mingw b/makefile.mingw index 1d3135ee..2efba255 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -309,6 +309,8 @@ timing.exe: demos/timing.o $(LIBMAIN_S) $(CC) demos/timing.o $(LIBMAIN_S) $(LTC_LDFLAGS) -o $@ der_print_flexi.exe: demos/der_print_flexi.o $(LIBMAIN_S) $(CC) demos/der_print_flexi.o $(LIBMAIN_S) $(LTC_LDFLAGS) -o $@ +x509_verify.exe: demos/x509_verify.o $(LIBMAIN_S) + $(CC) demos/x509_verify.o $(LIBMAIN_S) $(LTC_LDFLAGS) -o $@ #Tests test.exe: $(TOBJECTS) $(LIBMAIN_S) diff --git a/makefile.msvc b/makefile.msvc index 5770c468..be1f5c54 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -296,6 +296,8 @@ timing.exe: demos/timing.c $(LIBMAIN_S) cl $(LTC_CFLAGS) demos/timing.c tests/common.c $(LIBMAIN_S) $(LTC_LDFLAGS) /Fe$@ der_print_flexi.exe: demos/der_print_flexi.c $(LIBMAIN_S) cl $(LTC_CFLAGS) demos/der_print_flexi.c tests/common.c $(LIBMAIN_S) $(LTC_LDFLAGS) /Fe$@ +x509_verify.exe: demos/x509_verify.c $(LIBMAIN_S) + cl $(LTC_CFLAGS) demos/x509_verify.c tests/common.c $(LIBMAIN_S) $(LTC_LDFLAGS) /Fe$@ #Tests test.exe: $(LIBMAIN_S) $(TOBJECTS) diff --git a/makefile.unix b/makefile.unix index 5130bef3..ac3bce83 100644 --- a/makefile.unix +++ b/makefile.unix @@ -320,6 +320,8 @@ timing: demos/timing.o $(LIBMAIN_S) $(CC) demos/timing.o $(LIBMAIN_S) $(LTC_LDFLAGS) -o $@ der_print_flexi: demos/der_print_flexi.o $(LIBMAIN_S) $(CC) demos/der_print_flexi.o $(LIBMAIN_S) $(LTC_LDFLAGS) -o $@ +x509_verify: demos/x509_verify.o $(LIBMAIN_S) + $(CC) demos/x509_verify.o $(LIBMAIN_S) $(LTC_LDFLAGS) -o $@ #Tests test: $(TOBJECTS) $(LIBMAIN_S) diff --git a/makefile_include.mk b/makefile_include.mk index b849f810..a2a98b05 100644 --- a/makefile_include.mk +++ b/makefile_include.mk @@ -180,7 +180,7 @@ TEST=test USEFUL_DEMOS = hashsum # Demos that are usable but only rarely make sense to be installed -USEABLE_DEMOS = aesgcm constants crypt der_print_flexi latex-tables openssh-privkey openssl-enc sizes timing +USEABLE_DEMOS = aesgcm constants crypt der_print_flexi latex-tables openssh-privkey openssl-enc sizes timing x509_verify # Demos that are used for testing or measuring TEST_DEMOS = small tv_gen