From 9db1b60762b4c544c6165cd86024e3979ce95184 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Tue, 6 Oct 2026 19:43:21 -0700 Subject: [PATCH] file_store: decode DER certificates and CRLs The file store has no decoder that takes a raw DER certificate or CRL, so with wolfProvider as the default provider "openssl x509 -inform der" and "openssl crl -inform der" fail with "DECODER routines::unsupported ... Input type: DER, Input structure: Certificate" (CertificateList for CRLs). PEM input works because the PEM to DER decoder passes the DER up as an object. OpenSSL's own file store does the same for DER input with file_store_any2obj.c. Add a DER pass-through decoder under the private name "wolfprov-der2obj", with structure=Certificate (OSSL_OBJECT_CERT) and structure=CertificateList (OSSL_OBJECT_CRL). It checks that the input starts with a complete DER SEQUENCE and passes it up unparsed; other input is left to the other decoders. It does no cryptography. The file store adds it only when the caller expects a certificate or CRL, so key loading is unchanged, and the private name keeps it out of other decoder chains. Add test_rsa_load_cert_der: load the DER of certs/server-cert.pem, produced by OpenSSL's PEM and ASN.1 code, through the file store and compare the re-encoded certificate with it. --- certs/crl.pem | 42 +++++ certs/include.am | 2 + include/wolfprovider/alg_funcs.h | 5 + src/wp_file_store.c | 270 ++++++++++++++++++++++++++++--- src/wp_wolfprov.c | 8 + test/test_rsa.c | 250 ++++++++++++++++++++++++++++ test/unit.c | 3 + test/unit.h | 3 + 8 files changed, 564 insertions(+), 19 deletions(-) create mode 100644 certs/crl.pem diff --git a/certs/crl.pem b/certs/crl.pem new file mode 100644 index 00000000..b1b5f1ba --- /dev/null +++ b/certs/crl.pem @@ -0,0 +1,42 @@ +Certificate Revocation List (CRL): + Version 2 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com, emailAddress=facts@wolfssl.com + Last Update: Jun 11 21:44:40 2026 GMT + Next Update: Mar 7 21:44:40 2029 GMT + CRL extensions: + X509v3 CRL Number: + 2 +Revoked Certificates: + Serial Number: 02 + Revocation Date: Jun 11 21:44:40 2026 GMT + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 53:dc:68:3d:93:47:65:03:ca:a5:07:e8:13:26:72:94:02:3e: + 6f:c2:8d:27:fc:f6:64:c6:06:77:bc:1f:26:e4:6e:87:95:f8: + fe:18:ee:d7:24:f9:b8:ad:74:85:76:ec:55:92:6c:58:4f:59: + 66:68:f1:17:78:dc:39:6c:4c:2b:38:1e:d4:07:ac:b8:2c:f5: + d4:2b:c1:96:09:79:8a:a2:e2:d1:e5:1e:32:92:1f:12:81:a6: + ad:4d:ce:9d:9a:08:6f:56:e4:fe:58:8d:da:b2:88:54:6a:5a: + 19:07:9c:1b:de:4f:7a:62:dd:35:75:25:29:a2:9a:ba:19:a9: + 32:fe:61:ad:bb:70:06:0f:37:62:7c:02:61:3e:43:22:e9:62: + 12:f2:f7:0b:18:3a:a2:fb:0a:e9:2d:0b:68:fc:e1:41:51:64: + 7c:c8:8c:4c:d6:fc:90:0b:24:e7:e3:52:a5:e9:09:f8:85:e9: + 73:ca:b2:00:19:05:d6:5f:99:af:bb:9a:41:0f:48:a7:b4:25: + 78:d0:b6:ce:3b:3c:c2:2d:fa:65:2e:65:5e:67:01:01:aa:f7: + 4d:ad:5a:8f:b4:30:d4:9c:0c:e9:b6:c5:b6:4a:b6:eb:3a:01: + d1:92:b4:94:e8:62:e3:d3:37:ff:aa:38:8d:9f:e3:de:9c:b1: + 70:c5:11:64 +-----BEGIN X509 CRL----- +MIICBTCB7gIBATANBgkqhkiG9w0BAQsFADCBlTELMAkGA1UEBhMCVVMxEDAOBgNV +BAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3Ro +MRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20x +IDAeBgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tFw0yNjA2MTEyMTQ0NDBa +Fw0yOTAzMDcyMTQ0NDBaMBQwEgIBAhcNMjYwNjExMjE0NDQwWqAOMAwwCgYDVR0U +BAMCAQIwDQYJKoZIhvcNAQELBQADggEBAFPcaD2TR2UDyqUH6BMmcpQCPm/CjSf8 +9mTGBne8HybkboeV+P4Y7tck+bitdIV27FWSbFhPWWZo8Rd43DlsTCs4HtQHrLgs +9dQrwZYJeYqi4tHlHjKSHxKBpq1Nzp2aCG9W5P5YjdqyiFRqWhkHnBveT3pi3TV1 +JSmimroZqTL+Ya27cAYPN2J8AmE+QyLpYhLy9wsYOqL7CuktC2j84UFRZHzIjEzW +/JALJOfjUqXpCfiF6XPKsgAZBdZfma+7mkEPSKe0JXjQts47PMIt+mUuZV5nAQGq +902tWo+0MNScDOm2xbZKtus6AdGStJToYuPTN/+qOI2f496csXDFEWQ= +-----END X509 CRL----- diff --git a/certs/include.am b/certs/include.am index 3c30b33c..04e6ba17 100644 --- a/certs/include.am +++ b/certs/include.am @@ -2,6 +2,8 @@ # for test_rsa.c dist_noinst_SCRIPTS += certs/server-key.pem dist_noinst_SCRIPTS += certs/server-cert.pem +dist_noinst_SCRIPTS += certs/ca-cert.pem +dist_noinst_SCRIPTS += certs/crl.pem # for test_ecc.c dist_noinst_SCRIPTS += certs/server-ecc.pem diff --git a/include/wolfprovider/alg_funcs.h b/include/wolfprovider/alg_funcs.h index 1711a2a7..3bf18d19 100644 --- a/include/wolfprovider/alg_funcs.h +++ b/include/wolfprovider/alg_funcs.h @@ -221,6 +221,9 @@ typedef void (*DFUNC)(void); /* Decoder names. */ #define WP_NAMES_DER "DER" +/* Private name: only the file store fetches these, so no other decoder chain + * picks them up. */ +#define WP_NAMES_DER2OBJ "wolfprov-der2obj" /* Store names. */ #define WP_NAMES_FILE "file" @@ -547,6 +550,8 @@ extern const OSSL_DISPATCH wp_mldsa87_spki_decoder_functions[]; extern const OSSL_DISPATCH wp_mldsa87_pki_decoder_functions[]; extern const OSSL_DISPATCH wp_pem_to_der_decoder_functions[]; extern const OSSL_DISPATCH wp_epki_to_pki_decoder_functions[]; +extern const OSSL_DISPATCH wp_der_to_cert_decoder_functions[]; +extern const OSSL_DISPATCH wp_der_to_crl_decoder_functions[]; /* Encode implementations. */ extern const OSSL_DISPATCH wp_rsa_spki_der_encoder_functions[]; extern const OSSL_DISPATCH wp_rsa_spki_pem_encoder_functions[]; diff --git a/src/wp_file_store.c b/src/wp_file_store.c index 32b825ad..e1303580 100644 --- a/src/wp_file_store.c +++ b/src/wp_file_store.c @@ -307,6 +307,210 @@ static int wp_file_decoder_set_input_structure(OSSL_DECODER_CTX* decCtx, return ok; } +/** + * DER certificate/CRL to object decoder context. + */ +typedef struct wp_Der2Obj { + /** Provider context - used to read the core BIO. */ + WOLFPROV_CTX* provCtx; +} wp_Der2Obj; + +/** + * Create a new DER to object decoder context. + * + * @param [in] provCtx Provider context. + * @return Pointer to context. + */ +static wp_Der2Obj* wp_der2obj_newctx(WOLFPROV_CTX* provCtx) +{ + wp_Der2Obj* ctx = NULL; + + if (wolfssl_prov_is_running()) { + ctx = (wp_Der2Obj*)OPENSSL_zalloc(sizeof(*ctx)); + } + if (ctx != NULL) { + ctx->provCtx = provCtx; + } + + return ctx; +} + +/** + * Dispose of DER to object decoder context. + * + * @param [in] ctx DER to object decoder context. + */ +static void wp_der2obj_freectx(wp_Der2Obj* ctx) +{ + OPENSSL_free(ctx); +} + +/** + * Read exactly len bytes from the BIO. + * + * @param [in] bio BIO to read from. + * @param [out] buf Buffer to read into. + * @param [in] len Number of bytes to read. At most INT_MAX. + * @return 1 when all bytes were read. + * @return 0 otherwise. + */ +static int wp_der2obj_read(BIO* bio, unsigned char* buf, word32 len) +{ + int readLen = 1; + + while ((len > 0) && (readLen > 0)) { + readLen = BIO_read(bio, buf, (int)len); + if (readLen > 0) { + buf += readLen; + len -= (word32)readLen; + } + } + + return len == 0; +} + +/** + * Read the header of the DER SEQUENCE at the BIO's position. + * + * @param [in] bio BIO to read from. + * @param [out] hdr Buffer of at least 6 bytes to hold the header. + * @param [out] hdrLen Length of the header in bytes. + * @param [out] objLen Length of the SEQUENCE including its header. + * @return 1 when a DER SEQUENCE header was read. + * @return 0 otherwise. + */ +static int wp_der2obj_read_hdr(BIO* bio, unsigned char* hdr, word32* hdrLen, + word32* objLen) +{ + int ok; + word32 n = 0; + word32 contentLen = 0; + word32 i; + + ok = wp_der2obj_read(bio, hdr, 2) && (hdr[0] == 0x30); + if (ok && (hdr[1] < 0x80)) { + contentLen = hdr[1]; + } + else if (ok) { + n = hdr[1] & 0x7f; + ok = (n >= 1) && (n <= 4) && wp_der2obj_read(bio, hdr + 2, n); + for (i = 0; ok && (i < n); i++) { + contentLen = (contentLen << 8) | hdr[2 + i]; + } + } + /* Keep the whole object within BIO_read's int length. */ + if (ok && (contentLen > (word32)INT_MAX - 2 - n)) { + ok = 0; + } + if (ok) { + *hdrLen = 2 + n; + *objLen = 2 + n + contentLen; + } + + return ok; +} + +/** + * Pass DER data up as a certificate or CRL object, unparsed. + * + * The file store needs this for DER input, as OpenSSL's file store does + * (file_store_any2obj.c): the PEM to DER decoder does the same for PEM input. + * No cryptographic operation is performed. + * + * @param [in] ctx DER to object decoder context. + * @param [in, out] coreBio BIO wrapped for the core. + * @param [in] obj Object type: OSSL_OBJECT_CERT or OSSL_OBJECT_CRL. + * @param [in] dataCb Callback to pass the object to. + * @param [in] dataCbArg Argument to pass to callback. + * @return 1 on success or when the data is not a DER SEQUENCE. + * @return 0 on failure. + */ +static int wp_der2obj_decode(wp_Der2Obj* ctx, OSSL_CORE_BIO* coreBio, int obj, + OSSL_CALLBACK* dataCb, void* dataCbArg) +{ + int ok = 1; + BIO* bio; + unsigned char hdr[6]; + word32 hdrLen = 0; + word32 objLen = 0; + unsigned char* data = NULL; + OSSL_PARAM params[3]; + + WOLFPROV_ENTER(WP_LOG_COMP_PROVIDER, "wp_der2obj_decode"); + + bio = wp_corebio_get_bio(ctx->provCtx, coreBio); + if (bio == NULL) { + ok = 0; + } + /* Read one object only so the next load starts at the next object. + * Not a complete DER SEQUENCE: not for this decoder, let others try. */ + else if (wp_der2obj_read_hdr(bio, hdr, &hdrLen, &objLen)) { + data = (unsigned char*)OPENSSL_malloc(objLen); + if (data == NULL) { + ok = 0; + } + else { + XMEMCPY(data, hdr, hdrLen); + } + if (ok && wp_der2obj_read(bio, data + hdrLen, objLen - hdrLen)) { + params[0] = OSSL_PARAM_construct_octet_string( + OSSL_OBJECT_PARAM_DATA, data, objLen); + params[1] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &obj); + params[2] = OSSL_PARAM_construct_end(); + ok = dataCb(params, dataCbArg); + } + } + OPENSSL_free(data); + BIO_free(bio); + + WOLFPROV_LEAVE(WP_LOG_COMP_PROVIDER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); + return ok; +} + +/** + * Decode DER certificate: pass it up as a certificate object. + */ +static int wp_der2cert_decode(wp_Der2Obj* ctx, OSSL_CORE_BIO* coreBio, + int selection, OSSL_CALLBACK* dataCb, void* dataCbArg, + OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg) +{ + (void)selection; + (void)pwCb; + (void)pwCbArg; + return wp_der2obj_decode(ctx, coreBio, OSSL_OBJECT_CERT, dataCb, + dataCbArg); +} + +/** + * Decode DER CRL: pass it up as a CRL object. + */ +static int wp_der2crl_decode(wp_Der2Obj* ctx, OSSL_CORE_BIO* coreBio, + int selection, OSSL_CALLBACK* dataCb, void* dataCbArg, + OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg) +{ + (void)selection; + (void)pwCb; + (void)pwCbArg; + return wp_der2obj_decode(ctx, coreBio, OSSL_OBJECT_CRL, dataCb, + dataCbArg); +} + +/** Dispatch table for DER certificate to object decoder. */ +const OSSL_DISPATCH wp_der_to_cert_decoder_functions[] = { + { OSSL_FUNC_DECODER_NEWCTX, (DFUNC)wp_der2obj_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (DFUNC)wp_der2obj_freectx }, + { OSSL_FUNC_DECODER_DECODE, (DFUNC)wp_der2cert_decode }, + { 0, NULL } +}; + +/** Dispatch table for DER CRL to object decoder. */ +const OSSL_DISPATCH wp_der_to_crl_decoder_functions[] = { + { OSSL_FUNC_DECODER_NEWCTX, (DFUNC)wp_der2obj_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (DFUNC)wp_der2obj_freectx }, + { OSSL_FUNC_DECODER_DECODE, (DFUNC)wp_der2crl_decode }, + { 0, NULL } +}; + /** * Information about supported decoders from file data. */ @@ -404,6 +608,41 @@ static int wp_file_decoder_prop_query(const char* base, const char* caller, return ok; } +/** + * Fetch a decoder and add it to the decoder context. + * + * @param [in] ctx File system context object. + * @param [in, out] decCtx OpenSSL decoder context. + * @param [in] name Decoder name. + * @param [in] propQuery Structure property query. May be NULL. + * @return 1 on success. + * @return 0 on failure. + */ +static int wp_file_add_decoder(wp_FileCtx* ctx, OSSL_DECODER_CTX* decCtx, + const char* name, const char* propQuery) +{ + int ok = 1; + OSSL_DECODER* decoder = NULL; + char* query = NULL; + + if (!wp_file_decoder_prop_query(propQuery, ctx->propQuery, &query)) { + ok = 0; + } + if (ok) { + decoder = OSSL_DECODER_fetch(ctx->provCtx->libCtx, name, query); + if (decoder == NULL) { + ok = 0; + } + } + if (ok && !OSSL_DECODER_CTX_add_decoder(decCtx, decoder)) { + ok = 0; + } + OSSL_DECODER_free(decoder); + OPENSSL_free(query); + + return ok; +} + /** * Set the decoders into the decoder context. * @@ -416,29 +655,22 @@ static int wp_file_set_decoder(wp_FileCtx* ctx, OSSL_DECODER_CTX* decCtx) { int ok = 1; size_t i; - OSSL_DECODER* decoder; - char* query = NULL; WOLFPROV_ENTER(WP_LOG_COMP_PROVIDER, "wp_file_set_decoder"); for (i = 0; ok && (i < WP_DECODERS_SIZE); i++) { - if (!wp_file_decoder_prop_query(wp_decoders[i].propQuery, - ctx->propQuery, &query)) { - ok = 0; - } - if (ok) { - decoder = OSSL_DECODER_fetch(ctx->provCtx->libCtx, - wp_decoders[i].name, query); - if (decoder == NULL) { - ok = 0; - } - if (ok && !OSSL_DECODER_CTX_add_decoder(decCtx, decoder)) { - ok = 0; - } - OSSL_DECODER_free(decoder); - } - OPENSSL_free(query); - query = NULL; + ok = wp_file_add_decoder(ctx, decCtx, wp_decoders[i].name, + wp_decoders[i].propQuery); + } + /* DER certificates and CRLs are passed up unparsed, as OpenSSL's file + * store does. Only added when one is expected: key loading is unchanged. */ + if (ok && (ctx->type == OSSL_STORE_INFO_CERT)) { + ok = wp_file_add_decoder(ctx, decCtx, WP_NAMES_DER2OBJ, + "structure=Certificate"); + } + else if (ok && (ctx->type == OSSL_STORE_INFO_CRL)) { + ok = wp_file_add_decoder(ctx, decCtx, WP_NAMES_DER2OBJ, + "structure=CertificateList"); } WOLFPROV_LEAVE(WP_LOG_COMP_PROVIDER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); diff --git a/src/wp_wolfprov.c b/src/wp_wolfprov.c index 69446838..b648a13e 100644 --- a/src/wp_wolfprov.c +++ b/src/wp_wolfprov.c @@ -1660,6 +1660,14 @@ static const OSSL_ALGORITHM wolfprov_decoder[] = { wp_epki_to_pki_decoder_functions, "" }, + /* DER certificate and CRL pass-through for the file store. */ + { WP_NAMES_DER2OBJ, WP_DECODER_PROPERTIES(Certificate), + wp_der_to_cert_decoder_functions, + "" }, + { WP_NAMES_DER2OBJ, WP_DECODER_PROPERTIES(CertificateList), + wp_der_to_crl_decoder_functions, + "" }, + { NULL, NULL, NULL, NULL } }; diff --git a/test/test_rsa.c b/test/test_rsa.c index 8886afe6..16708021 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -2110,6 +2110,256 @@ int test_rsa_load_cert(void* data) return err; } +/** + * Load a DER certificate through the file store. The expected DER comes from + * OpenSSL's own PEM and ASN.1 code; the store must return the same bytes. + */ +int test_rsa_load_cert_der(void* data) +{ + int err; + BIO* pemBio = NULL; + BIO* derBio = NULL; + X509* oracle = NULL; + X509* cert = NULL; + unsigned char* oracleDer = NULL; + unsigned char* certDer = NULL; + int oracleLen = 0; + int certLen = 0; + OSSL_STORE_CTX* ctx = NULL; + OSSL_STORE_INFO* info = NULL; + + (void)data; + + PRINT_MSG("Convert certificate to DER with OpenSSL"); + pemBio = BIO_new_file(CERTS_DIR "/server-cert.pem", "r"); + err = pemBio == NULL; + if (err == 0) { + oracle = PEM_read_bio_X509(pemBio, NULL, NULL, NULL); + err = oracle == NULL; + } + if (err == 0) { + oracleLen = i2d_X509(oracle, &oracleDer); + err = oracleLen <= 0; + } + if (err == 0) { + derBio = BIO_new_mem_buf(oracleDer, oracleLen); + err = derBio == NULL; + } + if (err == 0) { + PRINT_MSG("Load DER certificate from the file store"); + ctx = OSSL_STORE_attach(derBio, "file", wpLibCtx, NULL, NULL, NULL, + NULL, NULL, NULL); + err = ctx == NULL; + } + if (err == 0) { + err = OSSL_STORE_expect(ctx, OSSL_STORE_INFO_CERT) != 1; + } + if (err == 0) { + info = OSSL_STORE_load(ctx); + err = info == NULL; + } + if (err == 0) { + cert = OSSL_STORE_INFO_get1_CERT(info); + err = cert == NULL; + } + if (err == 0) { + PRINT_MSG("Compare with OpenSSL's DER"); + certLen = i2d_X509(cert, &certDer); + err = (certLen != oracleLen) || + (memcmp(certDer, oracleDer, oracleLen) != 0); + } + + OPENSSL_free(certDer); + OPENSSL_free(oracleDer); + X509_free(cert); + X509_free(oracle); + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(ctx); + BIO_free(derBio); + BIO_free(pemBio); + return err; +} + +/** + * Convert a PEM certificate file to DER with OpenSSL's PEM and ASN.1 code. + */ +static int test_rsa_cert_file_der(const char* file, unsigned char** der, + int* derLen) +{ + int err; + BIO* bio = NULL; + X509* x509 = NULL; + + bio = BIO_new_file(file, "r"); + err = bio == NULL; + if (err == 0) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + err = x509 == NULL; + } + if (err == 0) { + *derLen = i2d_X509(x509, der); + err = *derLen <= 0; + } + + X509_free(x509); + BIO_free(bio); + return err; +} + +/** + * Load two concatenated DER certificates through the file store. Each load + * must return the next certificate in order, as OpenSSL's file store does. + */ +int test_rsa_load_cert_der_multi(void* data) +{ + int err = 0; + static const char* files[2] = { + CERTS_DIR "/server-cert.pem", + CERTS_DIR "/ca-cert.pem" + }; + unsigned char* oracleDer[2] = { NULL, NULL }; + int oracleLen[2] = { 0, 0 }; + unsigned char* allDer = NULL; + BIO* derBio = NULL; + X509* cert = NULL; + unsigned char* certDer = NULL; + int certLen = 0; + OSSL_STORE_CTX* ctx = NULL; + OSSL_STORE_INFO* info = NULL; + int i; + + (void)data; + + PRINT_MSG("Convert certificates to DER with OpenSSL"); + for (i = 0; (err == 0) && (i < 2); i++) { + err = test_rsa_cert_file_der(files[i], &oracleDer[i], &oracleLen[i]); + } + if (err == 0) { + allDer = (unsigned char*)OPENSSL_malloc(oracleLen[0] + oracleLen[1]); + err = allDer == NULL; + } + if (err == 0) { + memcpy(allDer, oracleDer[0], oracleLen[0]); + memcpy(allDer + oracleLen[0], oracleDer[1], oracleLen[1]); + derBio = BIO_new_mem_buf(allDer, oracleLen[0] + oracleLen[1]); + err = derBio == NULL; + } + if (err == 0) { + PRINT_MSG("Load concatenated DER certificates from the file store"); + ctx = OSSL_STORE_attach(derBio, "file", wpLibCtx, NULL, NULL, NULL, + NULL, NULL, NULL); + err = ctx == NULL; + } + if (err == 0) { + err = OSSL_STORE_expect(ctx, OSSL_STORE_INFO_CERT) != 1; + } + if (err == 0) { + PRINT_MSG("Compare each certificate with OpenSSL's DER, in order"); + } + for (i = 0; (err == 0) && (i < 2); i++) { + info = OSSL_STORE_load(ctx); + err = (info == NULL) || + (OSSL_STORE_INFO_get_type(info) != OSSL_STORE_INFO_CERT); + if (err == 0) { + cert = OSSL_STORE_INFO_get1_CERT(info); + err = cert == NULL; + } + if (err == 0) { + certLen = i2d_X509(cert, &certDer); + err = (certLen != oracleLen[i]) || + (memcmp(certDer, oracleDer[i], oracleLen[i]) != 0); + } + OPENSSL_free(certDer); + certDer = NULL; + X509_free(cert); + cert = NULL; + OSSL_STORE_INFO_free(info); + info = NULL; + } + if (err == 0) { + PRINT_MSG("Check that nothing follows the two certificates"); + err = OSSL_STORE_eof(ctx) != 1; + } + + OSSL_STORE_close(ctx); + BIO_free(derBio); + OPENSSL_free(allDer); + OPENSSL_free(oracleDer[1]); + OPENSSL_free(oracleDer[0]); + return err; +} + +/** + * Load a DER CRL through the file store. The expected DER comes from + * OpenSSL's own PEM and ASN.1 code; the store must return the same bytes. + */ +int test_rsa_load_crl_der(void* data) +{ + int err; + BIO* pemBio = NULL; + BIO* derBio = NULL; + X509_CRL* oracle = NULL; + X509_CRL* crl = NULL; + unsigned char* oracleDer = NULL; + unsigned char* crlDer = NULL; + int oracleLen = 0; + int crlLen = 0; + OSSL_STORE_CTX* ctx = NULL; + OSSL_STORE_INFO* info = NULL; + + (void)data; + + PRINT_MSG("Convert CRL to DER with OpenSSL"); + pemBio = BIO_new_file(CERTS_DIR "/crl.pem", "r"); + err = pemBio == NULL; + if (err == 0) { + oracle = PEM_read_bio_X509_CRL(pemBio, NULL, NULL, NULL); + err = oracle == NULL; + } + if (err == 0) { + oracleLen = i2d_X509_CRL(oracle, &oracleDer); + err = oracleLen <= 0; + } + if (err == 0) { + derBio = BIO_new_mem_buf(oracleDer, oracleLen); + err = derBio == NULL; + } + if (err == 0) { + PRINT_MSG("Load DER CRL from the file store"); + ctx = OSSL_STORE_attach(derBio, "file", wpLibCtx, NULL, NULL, NULL, + NULL, NULL, NULL); + err = ctx == NULL; + } + if (err == 0) { + err = OSSL_STORE_expect(ctx, OSSL_STORE_INFO_CRL) != 1; + } + if (err == 0) { + info = OSSL_STORE_load(ctx); + err = (info == NULL) || + (OSSL_STORE_INFO_get_type(info) != OSSL_STORE_INFO_CRL); + } + if (err == 0) { + crl = OSSL_STORE_INFO_get1_CRL(info); + err = crl == NULL; + } + if (err == 0) { + PRINT_MSG("Compare with OpenSSL's DER"); + crlLen = i2d_X509_CRL(crl, &crlDer); + err = (crlLen != oracleLen) || + (memcmp(crlDer, oracleDer, oracleLen) != 0); + } + + OPENSSL_free(crlDer); + OPENSSL_free(oracleDer); + X509_CRL_free(crl); + X509_CRL_free(oracle); + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(ctx); + BIO_free(derBio); + BIO_free(pemBio); + return err; +} + /** * Load the RSA private key from the file store with a caller property query. * diff --git a/test/unit.c b/test/unit.c index be7f72a2..e4a184f7 100644 --- a/test/unit.c +++ b/test/unit.c @@ -417,6 +417,9 @@ TEST_CASE test_case[] = { TEST_DECL(test_rsa_pss_no_params, NULL), TEST_DECL(test_rsa_load_key, NULL), TEST_DECL(test_rsa_load_cert, NULL), + TEST_DECL(test_rsa_load_cert_der, NULL), + TEST_DECL(test_rsa_load_cert_der_multi, NULL), + TEST_DECL(test_rsa_load_crl_der, NULL), TEST_DECL(test_rsa_load_key_prop_query, NULL), TEST_DECL(test_rsa_fromdata, NULL), TEST_DECL(test_rsa_fromdata_oversize, NULL), diff --git a/test/unit.h b/test/unit.h index 35bc245b..84381bfa 100644 --- a/test/unit.h +++ b/test/unit.h @@ -404,6 +404,9 @@ int test_rsa_pss_no_params(void *data); int test_rsa_load_key(void* data); int test_rsa_load_cert(void* data); +int test_rsa_load_cert_der(void* data); +int test_rsa_load_cert_der_multi(void* data); +int test_rsa_load_crl_der(void* data); int test_rsa_load_key_prop_query(void* data); int test_rsa_fromdata(void* data); int test_rsa_fromdata_oversize(void* data);