/* glibc includes */ #include #include #include #include #if defined(USE_WOLFSSL) /* wolfssl includes */ #include #include #include #include #include #include #else /* openssl includes */ #include #include #include #include #endif static int acert_check_opts(const char * file, const char * cert, const char * pkey_file); static void acert_err(const char * what, int rc); static int acert_print_usage_and_die(void) __attribute__((noreturn)); static int acert_do_test(const char * file, const char * cert, const char * pkey_file); static X509_ACERT * acert_read(const char * file); #if !defined(USE_WOLFSSL) static int acert_write(const char * file, const X509_ACERT * acert); static int acert_write_pubkey(const char * file, EVP_PKEY * pkey); #endif /* if !USE_WOLFSSL */ static EVP_PKEY * acert_read_pubkey(const char * file); static int acert_print(X509_ACERT * x509); static EVP_PKEY * acert_read_x509_pubkey(const char * cert); static int acert_test_api_misc(X509_ACERT * x509); #if defined(USE_WOLFSSL) static int acert_parse_attr(const X509_ACERT * x509); static void acert_dump_hex(const char * what, const byte * data, size_t len); #endif /* if USE_WOLFSSL */ static int dump = 0; static int parse = 0; static const char * mdname = "SHA2-256"; static const char * mgf1_mdname = "SHA1"; static int salt_len = 0; static int rsa_pss = 0; static int print = 0; static int sign = 0; static int write_acert = 0; static int verbose = 0; int main(int argc, char * argv[]) { const char * cert = NULL; const char * file = NULL; const char * pkey_file = NULL; int opt = 0; int rc = 0; while ((opt = getopt(argc, argv, "c:f:g:k:l:m:dpqrsvw?")) != -1) { switch (opt) { case 'c': cert = optarg; break; case 'd': dump = 1; break; case 'f': file = optarg; break; case 'k': pkey_file = optarg; break; case 'l': salt_len = atoi(optarg); if (salt_len <= 0) { printf("error: invalid -s salt_len: %d\n", salt_len); return EXIT_FAILURE; } break; case 'm': mgf1_mdname = optarg; break; case 'p': print = 1; break; case 'q': parse = 1; break; case 'r': rsa_pss = 1; break; case 's': sign = 1; break; case 'v': verbose = 1; break; case 'w': write_acert = 1; break; case '?': default: acert_print_usage_and_die(); break; } } #if defined(USE_WOLFSSL) wolfSSL_Init(); if (verbose) { wolfSSL_Debugging_ON(); } #endif /* if USE_WOLFSSL */ rc = acert_check_opts(file, cert, pkey_file); if (rc < 0) { return EXIT_FAILURE; } if (file == NULL) { printf("error: file: NULL\n"); return EXIT_FAILURE; } rc = acert_do_test(file, cert, pkey_file); if (rc == 0) { printf("info: acert_do_test: good\n"); printf("success\n"); } else { printf("error: acert_do_test returned: %d\n", rc); printf("fail\n"); } return (rc == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } static int acert_check_opts(const char * file, const char * cert, const char * pkey_file) { if (file == NULL) { printf("error: acert file required\n"); return -1; } printf("info: using acert file: %s\n", file); if (cert != NULL) { printf("info: using cert file: %s\n", cert); } if (pkey_file != NULL) { printf("info: using pubkey file: %s\n", pkey_file); } if (pkey_file != NULL && cert != NULL) { printf("error: -c and -k are mutually exclusive\n"); return -1; } if ((pkey_file != NULL || cert != NULL) && sign != 0) { printf("error: -c[k] and -s are mutually exclusive\n"); return -1; } if (rsa_pss) { printf("info: using rsa_pss\n"); printf("info: using mdname: %s\n", mdname); printf("info: using mask alg: mgf1 with %s\n", mgf1_mdname); } return 0; } static int acert_do_test(const char * file, const char * cert, const char * pkey_file) { EVP_PKEY * pkey = NULL; X509_ACERT * x509 = NULL; uint8_t fail = 0; int rc = 0; #if !defined(USE_WOLFSSL) EVP_PKEY_CTX * pctx = NULL; #endif /* ! USE_WOLFSSL */ x509 = acert_read(file); if (x509 == NULL) { printf("error: acert_read returned: NULL\n"); fail = 1; goto end_acert_do_test; } rc = acert_print(x509); if (rc) { printf("error: acert_print returned: %d\n", rc); fail = 1; goto end_acert_do_test; } rc = acert_test_api_misc(x509); if (rc) { printf("error: acert_test_api_misc returned: %d\n", rc); fail = 1; goto end_acert_do_test; } #if defined(USE_WOLFSSL) rc = acert_parse_attr(x509); if (rc) { printf("error: acert_parse_attr returned: %d\n", rc); fail = 1; goto end_acert_do_test; } #endif /* if USE_WOLFSSL */ if (cert) { pkey = acert_read_x509_pubkey(cert); if (pkey == NULL) { printf("error: acert_read_print_pubkey returned: NULL\n"); fail = 1; goto end_acert_do_test; } } else if (pkey_file) { pkey = acert_read_pubkey(pkey_file); if (pkey == NULL) { printf("error: acert_read_pubkey returned: NULL\n"); fail = 1; goto end_acert_do_test; } } #if !defined(USE_WOLFSSL) /* todo: wolfssl sign acert support */ if (sign) { /* Generate a new pkey, and sign the x509 acert. * The pkey should not be loaded yet. */ if (pkey != NULL) { printf("error: pkey already exists: %p\n", pkey); fail = 1; goto end_acert_do_test; } if (!rsa_pss) { /* Use normal boring RSA keygen. */ pkey = EVP_RSA_gen(2048); if (pkey != NULL) { printf("info: EVP_RSA_gen(2048): good\n"); } else { printf("error: EVP_RSA_gen returned: NULL\n"); fail = 1; goto end_acert_do_test; } } else { int pss_rc = 0; pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, NULL); if (pctx == NULL) { printf("error: EVP_PKEY_CTX_new_id returned: NULL\n"); fail = 1; goto end_acert_do_test; } pss_rc = EVP_PKEY_keygen_init(pctx); if (pss_rc <= 0) { printf("error: EVP_PKEY_keygen_init returned: %d\n", pss_rc); fail = 1; goto end_acert_do_test; } pss_rc = EVP_PKEY_CTX_set_rsa_pss_keygen_md_name(pctx, mdname, NULL); if (pss_rc <= 0) { acert_err("EVP_PKEY_CTX_set_rsa_pss_keygen_md_name", pss_rc); fail = 1; goto end_acert_do_test; } pss_rc = EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name(pctx, mgf1_mdname); if (pss_rc <= 0) { acert_err("EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name", pss_rc); fail = 1; goto end_acert_do_test; } if (salt_len) { pss_rc = EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(pctx, salt_len); if (pss_rc <= 0) { acert_err("EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen", pss_rc); fail = 1; goto end_acert_do_test; } } pss_rc = EVP_PKEY_keygen(pctx, &pkey); if (pss_rc <= 0) { printf("error: EVP_PKEY_keygen returned: %d\n", pss_rc); fail = 1; goto end_acert_do_test; } if (pkey == NULL) { printf("error: EVP_PKEY_keygen returned: NULL\n"); fail = 1; goto end_acert_do_test; } } } if (sign && pkey) { int sign_rc = X509_ACERT_sign(x509, pkey, EVP_sha256()); if (sign_rc > 0) { printf("info: X509_ACERT_sign: good\n"); } else { acert_err("X509_ACERT_sign", sign_rc); fail = 1; goto end_acert_do_test; } } if (write_acert) { /* Save the signed acert to file. */ int write_rc = acert_write("acert_new.pem", x509); if (write_rc) { fail = 1; goto end_acert_do_test; } } if (write_acert && pkey) { /* Save the new pubkey to file. */ int write_rc = acert_write_pubkey("pkey_new.pem", pkey); if (write_rc) { fail = 1; goto end_acert_do_test; } } #endif /* if !USE_WOLFSSL */ if (pkey) { int verify_rc = X509_ACERT_verify(x509, pkey); if (verify_rc == 1) { printf("info: X509_ACERT_verify: good\n"); } else { acert_err("X509_ACERT_verify", verify_rc); fail = 1; goto end_acert_do_test; } } end_acert_do_test: if (x509 != NULL) { X509_ACERT_free(x509); x509 = NULL; } #if !defined(USE_WOLFSSL) if (pctx) { EVP_PKEY_CTX_free(pctx); pctx = NULL; } #endif /* ! USE_WOLFSSL */ if (pkey) { EVP_PKEY_free(pkey); pkey = NULL; } return fail ? -1 : 0; } static int acert_test_api_misc(X509_ACERT * x509) { int rc = 0; int ver = 0; if (x509 == NULL) { return -1; } ver = X509_ACERT_get_version(x509); if (ver <= 0) { printf("error: X509_ACERT_get_version returned: %d\n", ver); return -1; } printf("info: acert version: %x\n", (uint8_t) ver); return rc; } #if defined(USE_WOLFSSL) /* Given an x509 acert, retrieve the raw attributes buffer and * length, and then parses it a little. * * Returns 0 on success. * Returns < 0 on error. * */ static int acert_parse_attr(const X509_ACERT * x509) { const byte * attr = NULL; word32 attr_len = 0; word32 idx = 0; word32 max_idx = 0; byte tag; int rc = 0; int len = 0; rc = wolfSSL_X509_ACERT_get_attr_buf(x509, &attr, &attr_len); if (rc != SSL_SUCCESS) { printf("error: wolfSSL_X509_ACERT_get_attr_buf returned: %d\n", rc); return -1; } if (attr == NULL || attr_len <= 0) { printf("error: attr = %p, attr_len = %d\n", attr, attr_len); return -1; } if (!parse) { /* Just verify we could get the buffer and length and return early. */ return 0; } /* Try to parse the attributes. This is WIP. */ acert_dump_hex("Attributes", attr, attr_len); max_idx = attr_len; rc = GetASNTag(attr + idx, &idx, &tag, max_idx); if (rc < 0) { printf("error: GetASNTag(%p, %d, %d, %d) returned: %d\n", attr + idx, idx, tag, max_idx, tag); return -1; } printf("info: GetASNTag(%p, %d, %d, %d): found tag: 0x%0x\n", attr + idx, idx, tag, max_idx, tag); len = GetLength(attr + idx, &idx, &len, max_idx); if (len <= 0) { printf("error: GetLength(%p, %d, %d, %d) returned: %d\n", attr + idx, idx, len, max_idx, len); return -1; } printf("info: GetLength(%p, %d, %d, %d) returned: %d\n", attr + idx, idx, len, max_idx, len); return rc; } #define BOLDRED "\033[1m\033[31m" #define BOLDGREEN "\033[1m\033[32m" #define BOLDWHITE "\033[1m\033[37m" #define BOLDBLUE "\033[1m\033[34m" #define BOLDYELLOW "\033[1m\033[33m" #define RESET "\033[0m" static void acert_print_data(const byte * data, size_t i, size_t j) { if (isprint(data[i + 2 + j])) { printf("%c", data[i + 2 + j]); } else { printf("."); } return; } /* Dump data as hex, with some pretty color coding. * Kind of a silly work in progress, for debugging use. * */ static void acert_dump_hex(const char * what, const byte * data, size_t len) { uint8_t seq_list[1024]; uint16_t n_seq = 0; uint8_t str_list[1024]; uint16_t n_str = 0; memset(str_list, 0, sizeof(str_list)); if (!dump) { return; } printf("\ninfo: %s\n", what); for (size_t i = 0; i < len; ++i) { if (i % 8 == 0) { /* indent first element */ printf(" "); } if (data[i] == (ASN_SEQUENCE | ASN_CONSTRUCTED)) { seq_list[n_seq] = i; n_seq++; printf(BOLDRED "0x%02x " RESET, data[i]); if ((i + 1) % 8 == 0) { printf("\n"); } ++i; printf(BOLDGREEN "0x%02x " RESET, data[i]); } else if (data[i] == ASN_PRINTABLE_STRING) { str_list[n_str] = i; n_str++; printf(BOLDBLUE "0x%02x " RESET, data[i]); if ((i + 1) % 8 == 0) { printf("\n"); } ++i; printf(BOLDYELLOW "0x%02x " RESET, data[i]); } else { printf("0x%02x ", data[i]); } if ((i + 1) % 8 == 0) { printf("\n"); } } printf("\n\n"); if (n_seq) { printf("constructed sequences\n"); for (size_t n = 0; n < n_seq; ++n) { size_t i = seq_list[n]; uint8_t seq_len = data[i + 1]; printf(BOLDRED " 0x%02x " RESET, data[i]); printf(BOLDGREEN "0x%02x " RESET, data[i + 1]); for (size_t j = 0; j < seq_len; ++j) { acert_print_data(data, i, j); } printf("\n"); } } printf("\n"); if (n_str) { printf("printable strings\n"); for (size_t n = 0; n < n_str; ++n) { size_t i = str_list[n]; uint8_t str_len = data[i + 1]; printf(BOLDBLUE " 0x%02x " RESET, data[i]); printf(BOLDYELLOW "0x%02x " RESET, data[i + 1]); for (size_t j = 0; j < str_len; ++j) { printf("%c", data[i + 2 + j]); acert_print_data(data, i, j); } printf("\n"); } } printf("\n"); return; } #endif /* if USE_WOLFSSL */ /* Reads and print pubkey certificate. * */ static EVP_PKEY * acert_read_x509_pubkey(const char * cert) { BIO * bp = NULL; BIO * bout = NULL; X509 * x509 = NULL; EVP_PKEY * pkey = NULL; int rc = -1; bp = BIO_new_file(cert, "r"); if (bp == NULL) { printf("error: BIO_new_file returned: NULL\n"); goto end_cert_read; } bout = BIO_new_fp(stderr, BIO_NOCLOSE); if (bout == NULL) { printf("error: BIO_new_fp returned: NULL\n"); goto end_cert_read; } x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL); if (x509 == NULL) { printf("error: PEM_read_bio_X509 returned: NULL\n"); goto end_cert_read; } printf("info: PEM_read_bio_X509: good\n"); if (print) { rc = X509_print(bout, x509); if (rc != 1) { printf("error: X509_print returned: %d\n", rc); } } pkey = X509_get_pubkey(x509); if (pkey == NULL) { printf("error: X509_get_pubkey(%p) returned: NULL\n", x509); goto end_cert_read; } end_cert_read: if (bp != NULL) { BIO_free(bp); bp = NULL; } if (bout != NULL) { BIO_free(bout); bout = NULL; } if (x509 != NULL) { X509_free(x509); x509 = NULL; } return pkey; } /* Reads an x509 acert from file. * * Returns: X509_ACERT * on success. * Returns: NULL on failure. * */ static X509_ACERT * acert_read(const char * file) { BIO * bp = NULL; X509_ACERT * x509 = NULL; bp = BIO_new_file(file, "r"); if (bp == NULL) { printf("error: BIO_new_file returned: NULL\n"); return NULL; } x509 = PEM_read_bio_X509_ACERT(bp, NULL, NULL, NULL); BIO_free(bp); bp = NULL; if (x509 == NULL) { printf("error: PEM_read_bio_X509_ACERT returned: NULL\n"); return NULL; } printf("info: PEM_read_bio_X509_ACERT: good\n"); return x509; } #if !defined(USE_WOLFSSL) /* Writes an x509 acert to file. * * Not supported in wolfSSL yet. * */ static int acert_write(const char * file, const X509_ACERT * acert) { BIO * bp = NULL; int ret = 0; bp = BIO_new_file(file, "w"); if (bp == NULL) { printf("error: BIO_new_file returned: NULL\n"); return -1; } ret = PEM_write_bio_X509_ACERT(bp, acert); BIO_free(bp); bp = NULL; if (ret != 1) { printf("error: PEM_write_bio_X509_ACERT: %d\n", ret); return -1; } printf("info: wrote acert to file: %s\n", file); return 0; } static int acert_write_pubkey(const char * file, EVP_PKEY * pkey) { BIO * bp = NULL; int ret = 0; bp = BIO_new_file(file, "w"); if (bp == NULL) { printf("error: BIO_new_file returned: NULL\n"); return -1; } ret = PEM_write_bio_PUBKEY(bp, pkey); BIO_free(bp); bp = NULL; if (ret != 1) { printf("error: PEM_write_bio_PUBKEY: %d\n", ret); return -1; } printf("info: wrote pubkey to file: %s\n", file); return 0; } #endif /* if !USE_WOLFSSL */ static EVP_PKEY * acert_read_pubkey(const char * file) { BIO * bp = NULL; EVP_PKEY * pkey = NULL; bp = BIO_new_file(file, "r"); if (bp == NULL) { printf("error: BIO_new_file returned: NULL\n"); return NULL; } pkey = PEM_read_bio_PUBKEY(bp, &pkey, NULL, NULL); BIO_free(bp); bp = NULL; if (pkey == NULL) { printf("error: PEM_read_bio_PUBKEY returned: NULL\n"); return NULL; } printf("info: PEM_read_bio_PUBKEY: good\n"); return pkey; } static int acert_print(X509_ACERT * x509) { BIO * bout = NULL; int print_rc = 0; if (!print) { /* Nothing to do. */ return 0; } bout = BIO_new_fp(stderr, BIO_NOCLOSE); if (bout == NULL) { printf("error: BIO_new_fp returned: NULL\n"); return -1; } print_rc = X509_ACERT_print(bout, x509); BIO_free(bout); bout = NULL; if (print_rc == 1) { printf("info: X509_ACERT_print: good\n"); } else { printf("error: X509_ACERT_print returned: %d\n", print_rc); return -1; } return 0; } static void acert_err(const char * what, int rc) { unsigned long err = ERR_get_error(); printf("error: %s returned: %d: %lu, %s\n", what, rc, err, ERR_error_string(err, NULL)); return; } static int acert_print_usage_and_die(void) { printf("options:\n"); printf(" -c \n"); printf(" -f \n"); printf(" -m (rsa_pss only)\n"); printf(" -l (rsa_pss only)\n"); printf(" -r (use rsa_pss)\n"); printf(" -s (resign and verify)\n"); printf(" -v (verbose)\n"); printf(" -w (write signed acert to file)\n"); printf("\n"); printf("usage:\n"); printf(" verifying acert with pub key file:\n"); printf(" $./test/test_acert -f certs/signed/acert.pem -k certs/signed/acert_pubkey.pem\n"); printf(" info: using acert file: certs/signed/acert.pem\n"); printf(" info: using pubkey file: certs/signed/acert_pubkey.pem\n"); printf(" info: PEM_read_bio_X509_ACERT: good\n"); printf(" info: acert version: 1\n"); printf(" info: PEM_read_bio_PUBKEY: good\n"); printf(" info: X509_ACERT_verify: good\n"); printf(" info: acert_do_test: good\n"); printf(" success\n"); exit(EXIT_FAILURE); }