From 79f214f73c28c9b56c13f9f6cf498aa2091846d2 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Fri, 9 May 2025 11:29:19 -0600 Subject: [PATCH 1/3] add new X509 API: wc_Exportx509PubKeyWithSpki --- tests/api.c | 142 +++++++++++++++++++++++++++++++++ wolfcrypt/src/asn.c | 78 +++++++++++++++++- wolfssl/wolfcrypt/asn_public.h | 2 + 3 files changed, 221 insertions(+), 1 deletion(-) diff --git a/tests/api.c b/tests/api.c index e67f38f7d..bcfb2216f 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22755,6 +22755,147 @@ static int test_wc_GetPubKeyDerFromCert(void) return EXPECT_RESULT(); } +static int test_wc_ExportX509PubKeyWithSpki(void) +{ + EXPECT_DECLS; +#if !defined(NO_RSA) || defined(HAVE_ECC) + int ret; + word32 idx = 0; + byte keyDer[TWOK_BUF]; /* large enough for up to RSA 2048 */ + word32 keyDerSz = (word32)sizeof(keyDer); +#if !defined(NO_RSA) && defined(WOLFSSL_CERT_REQ) && !defined(NO_FILESYSTEM) + byte certBuf[6000]; /* for PEM and CSR, client-cert.pem is 5-6kB */ + word32 certBufSz = sizeof(certBuf); +#endif +#if ((!defined(USE_CERT_BUFFERS_2048) && !defined(USE_CERT_BUFFERS_1024)) || \ + defined(WOLFSSL_CERT_REQ)) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) + XFILE fp = XBADFILE; +#endif +#ifndef NO_RSA + RsaKey rsaKey; + #if defined(USE_CERT_BUFFERS_2048) + byte* rsaCertDer = (byte*)client_cert_der_2048; + word32 rsaCertDerSz = sizeof_client_cert_der_2048; + #elif defined(USE_CERT_BUFFERS_1024) + byte* rsaCertDer = (byte*)client_cert_der_1024; + word32 rsaCertDerSz = sizeof_client_cert_der_1024; + #else + unsigned char rsaCertDer[TWOK_BUF]; + word32 rsaCertDerSz; + #endif +#endif +#ifdef HAVE_ECC + ecc_key eccKey; + #if defined(USE_CERT_BUFFERS_256) + byte* eccCert = (byte*)cliecc_cert_der_256; + word32 eccCertSz = sizeof_cliecc_cert_der_256; + #else + unsigned char eccCert[ONEK_BUF]; + word32 eccCertSz; + XFILE fp2 = XBADFILE; + #endif +#endif + +#ifndef NO_RSA + +#if !defined(USE_CERT_BUFFERS_1024) && !defined(USE_CERT_BUFFERS_2048) + ExpectTrue((fp = XFOPEN("./certs/1024/client-cert.der", "rb")) != XBADFILE); + ExpectIntGT(rsaCertDerSz = (word32)XFREAD(rsaCertDer, 1, sizeof(rsaCertDer), + fp), 0); + if (fp != XBADFILE) { + XFCLOSE(fp); + fp = XBADFILE; + } +#endif + + /* good test case - RSA DER cert */ + ExpectIntEQ(wc_ExportX509PubKeyWithSpki(rsaCertDer, rsaCertDerSz, keyDer, + &keyDerSz), 0); + ExpectIntGT(keyDerSz, 0); + + /* sanity check, verify we can import DER public key */ + ret = wc_InitRsaKey(&rsaKey, HEAP_HINT); + ExpectIntEQ(ret, 0); + ExpectIntEQ(wc_RsaPublicKeyDecode(keyDer, &idx, &rsaKey, keyDerSz), 0); + if (ret == 0) { + wc_FreeRsaKey(&rsaKey); + } + + /* bad args: certDer */ + keyDerSz = (word32)sizeof(keyDer); + ExpectIntEQ(wc_ExportX509PubKeyWithSpki(NULL, rsaCertDerSz, keyDer, + &keyDerSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* bad args: 0 sized certSz */ + keyDerSz = (word32)sizeof(keyDer); + ExpectIntEQ(wc_ExportX509PubKeyWithSpki(rsaCertDer, 0, keyDer, &keyDerSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* bad args: NULL inout size */ + ExpectIntEQ(ret = wc_ExportX509PubKeyWithSpki(rsaCertDer, rsaCertDerSz, + keyDer, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Certificate Request Tests */ + #if defined(WOLFSSL_CERT_REQ) && !defined(NO_FILESYSTEM) + { + XMEMSET(certBuf, 0, sizeof(certBuf)); + ExpectTrue((fp = XFOPEN("./certs/csr.signed.der", "rb")) != XBADFILE); + ExpectIntGT(certBufSz = (word32)XFREAD(certBuf, 1, certBufSz, fp), 0); + if (fp != XBADFILE) { + XFCLOSE(fp); + } + + /* good test case - RSA DER certificate request */ + keyDerSz = sizeof(keyDer); + ExpectIntEQ(ret = wc_ExportX509PubKeyWithSpki(rsaCertDer, rsaCertDerSz, + keyDer, &keyDerSz), 0); + ExpectIntGT(keyDerSz, 0); + + /* sanity check, verify we can import DER public key */ + ret = wc_InitRsaKey(&rsaKey, HEAP_HINT); + ExpectIntEQ(ret, 0); + idx = 0; + ExpectIntEQ(wc_RsaPublicKeyDecode(keyDer, &idx, &rsaKey, keyDerSz), 0); + if (ret == 0) { + wc_FreeRsaKey(&rsaKey); + } + } + #endif /* WOLFSSL_CERT_REQ */ +#endif /* NO_RSA */ + +#ifdef HAVE_ECC + #ifndef USE_CERT_BUFFERS_256 + ExpectTrue((fp2 = XFOPEN("./certs/client-ecc-cert.der", "rb")) != + XBADFILE); + ExpectIntGT(eccCertSz = (word32)XFREAD(eccCert, 1, ONEK_BUF, fp2), 0); + if (fp2 != XBADFILE) { + XFCLOSE(fp2); + } + #endif + + /* good test case - ECC */ + XMEMSET(keyDer, 0, sizeof(keyDer)); + keyDerSz = sizeof(keyDer); + ExpectIntEQ(wc_ExportX509PubKeyWithSpki(eccCert, eccCertSz, keyDer, + &keyDerSz), 0); + ExpectIntGT(keyDerSz, 0); + + /* sanity check, verify we can import DER public key */ + ret = wc_ecc_init(&eccKey); + ExpectIntEQ(ret, 0); + idx = 0; /* reset idx to 0, used above in RSA case */ + ExpectIntEQ(wc_EccPublicKeyDecode(keyDer, &idx, &eccKey, keyDerSz), 0); + if (ret == 0) { + wc_ecc_free(&eccKey); + } + +#endif +#endif /* !NO_RSA || HAVE_ECC */ + return EXPECT_RESULT(); +} + static int test_wc_CheckCertSigPubKey(void) { EXPECT_DECLS; @@ -66846,6 +66987,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wc_PubKeyPemToDer), TEST_DECL(test_wc_PemPubKeyToDer), TEST_DECL(test_wc_GetPubKeyDerFromCert), + TEST_DECL(test_wc_ExportX509PubKeyWithSpki), TEST_DECL(test_wc_CheckCertSigPubKey), /* wolfCrypt ASN tests */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 66e574b26..6384f1eef 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -22981,7 +22981,8 @@ static int CheckDate(ASNGetData *dataASN, int dateType) * @param [out] badDateRet Bad date return code. * @param [in] stopAtPubKey Stop parsing before subjectPublicKeyInfo. * @param [in] stopAfterPubKey Stop parsing after subjectPublicKeyInfo. - * @return 0 on success. + * @return 0 on success if of the stop arguments is not set, otherwise set to + * the corresponding byte offset at which the parsing stopped. * @return ASN_CRIT_EXT_E when a critical extension was not recognized. * @return ASN_TIME_E when date BER tag is nor UTC or GENERALIZED time. * @return ASN_DATE_SZ_E when time data is not supported. @@ -24608,6 +24609,81 @@ int wc_CertGetPubKey(const byte* cert, word32 certSz, return ret; } #endif + +/* + * @brief Export the SubjectPublicKeyInfo from an X.509 certificate + * + * This function extracts the SubjectPublicKeyInfo (SPKI) section from an X.509 + * certificate in DER format. The SPKI contains the public key algorithm and + * the public key itself. + * + * @param certDer [in] Pointer to the DER encoded certificate + * @param certSz [in] Size of the DER encoded certificate + * @param pubKeyDer [out] Buffer to hold the extracted SPKI (can be NULL to + * get size) + * @param pubKeyDerSz [in,out] On input, size of pubKeyDer buffer + * On output, actual size of the SPKI + * + * @return 0 on success, negative on error + * @return BAD_FUNC_ARG if certDer is NULL, certSz is 0, or pubKeyDerSz is NULL + * @return BUFFER_E if the provided buffer is too small + */ +WOLFSSL_API int wc_ExportX509PubKeyWithSpki(const byte* certDer, word32 certSz, + byte* pubKeyDer, + word32* pubKeyDerSz) +{ + DecodedCert cert; + int ret; + word32 startIdx; + word32 idx; + word32 length; + int badDate = 0; + + if (certDer == NULL || certSz == 0 || pubKeyDerSz == NULL) { + return BAD_FUNC_ARG; + } + + /* Initialize decoded cert structure */ + wc_InitDecodedCert(&cert, certDer, certSz, NULL); + + /* Parse up to the SubjectPublicKeyInfo */ + ret = wc_GetPubX509(&cert, 0, &badDate); + if (ret < 0) { + wc_FreeDecodedCert(&cert); + return ret; + } + + /* Save the starting index of SubjectPublicKeyInfo */ + startIdx = cert.srcIdx; + + /* Get the length of the SubjectPublicKeyInfo sequence */ + idx = startIdx; + ret = GetSequence(certDer, &idx, (int*)&length, certSz); + if (ret < 0) { + wc_FreeDecodedCert(&cert); + return ret; + } + + /* Calculate total length including sequence header */ + length += (idx - startIdx); + + /* Copy the SubjectPublicKeyInfo if buffer provided */ + if (pubKeyDer != NULL) { + if (*pubKeyDerSz < (word32)length) { + wc_FreeDecodedCert(&cert); + return BUFFER_E; + } + XMEMCPY(pubKeyDer, &certDer[startIdx], length); + } + + /* Return the size */ + *pubKeyDerSz = length; + + wc_FreeDecodedCert(&cert); + return 0; +} + + #ifdef HAVE_OCSP Signer* findSignerByKeyHash(Signer *list, byte *hash) { diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 00aa08183..2f95d5194 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -879,6 +879,8 @@ WOLFSSL_API int wc_ParseCert( WOLFSSL_API int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, byte* derKey, word32* derKeySz); +WOLFSSL_API int wc_ExportX509PubKeyWithSpki(const byte* cert, word32 certSz, + byte* pubKey, word32* pubKeySz); #ifdef WOLFSSL_FPKI WOLFSSL_API int wc_GetUUIDFromCert(struct DecodedCert* cert, From 2151a1b8a1f8f81c4dba985429d50b76db7307e5 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 12 May 2025 11:43:56 -0600 Subject: [PATCH 2/3] review comments --- tests/api.c | 30 +++++++++------- wolfcrypt/src/asn.c | 63 +++++++++++++++++----------------- wolfssl/wolfcrypt/asn_public.h | 5 +-- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/tests/api.c b/tests/api.c index bcfb2216f..60db1b4fb 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22755,7 +22755,7 @@ static int test_wc_GetPubKeyDerFromCert(void) return EXPECT_RESULT(); } -static int test_wc_ExportX509PubKeyWithSpki(void) +static int test_wc_GetSubjectPubKeyInfoDerFromCert(void) { EXPECT_DECLS; #if !defined(NO_RSA) || defined(HAVE_ECC) @@ -22809,8 +22809,8 @@ static int test_wc_ExportX509PubKeyWithSpki(void) #endif /* good test case - RSA DER cert */ - ExpectIntEQ(wc_ExportX509PubKeyWithSpki(rsaCertDer, rsaCertDerSz, keyDer, - &keyDerSz), 0); + ExpectIntEQ(wc_GetSubjectPubKeyInfoDerFromCert(rsaCertDer, rsaCertDerSz, + keyDer, &keyDerSz), 0); ExpectIntGT(keyDerSz, 0); /* sanity check, verify we can import DER public key */ @@ -22823,18 +22823,20 @@ static int test_wc_ExportX509PubKeyWithSpki(void) /* bad args: certDer */ keyDerSz = (word32)sizeof(keyDer); - ExpectIntEQ(wc_ExportX509PubKeyWithSpki(NULL, rsaCertDerSz, keyDer, - &keyDerSz), + ExpectIntEQ(wc_GetSubjectPubKeyInfoDerFromCert(NULL, rsaCertDerSz, keyDer, + &keyDerSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* bad args: 0 sized certSz */ keyDerSz = (word32)sizeof(keyDer); - ExpectIntEQ(wc_ExportX509PubKeyWithSpki(rsaCertDer, 0, keyDer, &keyDerSz), + ExpectIntEQ(wc_GetSubjectPubKeyInfoDerFromCert(rsaCertDer, 0, keyDer, + &keyDerSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* bad args: NULL inout size */ - ExpectIntEQ(ret = wc_ExportX509PubKeyWithSpki(rsaCertDer, rsaCertDerSz, - keyDer, NULL), + ExpectIntEQ(ret = wc_GetSubjectPubKeyInfoDerFromCert(rsaCertDer, + rsaCertDerSz, keyDer, + NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* Certificate Request Tests */ @@ -22849,8 +22851,10 @@ static int test_wc_ExportX509PubKeyWithSpki(void) /* good test case - RSA DER certificate request */ keyDerSz = sizeof(keyDer); - ExpectIntEQ(ret = wc_ExportX509PubKeyWithSpki(rsaCertDer, rsaCertDerSz, - keyDer, &keyDerSz), 0); + ExpectIntEQ(ret = wc_GetSubjectPubKeyInfoDerFromCert(rsaCertDer, + rsaCertDerSz, + keyDer, + &keyDerSz), 0); ExpectIntGT(keyDerSz, 0); /* sanity check, verify we can import DER public key */ @@ -22878,8 +22882,8 @@ static int test_wc_ExportX509PubKeyWithSpki(void) /* good test case - ECC */ XMEMSET(keyDer, 0, sizeof(keyDer)); keyDerSz = sizeof(keyDer); - ExpectIntEQ(wc_ExportX509PubKeyWithSpki(eccCert, eccCertSz, keyDer, - &keyDerSz), 0); + ExpectIntEQ(wc_GetSubjectPubKeyInfoDerFromCert(eccCert, eccCertSz, keyDer, + &keyDerSz), 0); ExpectIntGT(keyDerSz, 0); /* sanity check, verify we can import DER public key */ @@ -66987,7 +66991,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wc_PubKeyPemToDer), TEST_DECL(test_wc_PemPubKeyToDer), TEST_DECL(test_wc_GetPubKeyDerFromCert), - TEST_DECL(test_wc_ExportX509PubKeyWithSpki), + TEST_DECL(test_wc_GetSubjectPubKeyInfoDerFromCert), TEST_DECL(test_wc_CheckCertSigPubKey), /* wolfCrypt ASN tests */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 6384f1eef..e201e5bbe 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -24628,59 +24628,60 @@ int wc_CertGetPubKey(const byte* cert, word32 certSz, * @return BAD_FUNC_ARG if certDer is NULL, certSz is 0, or pubKeyDerSz is NULL * @return BUFFER_E if the provided buffer is too small */ -WOLFSSL_API int wc_ExportX509PubKeyWithSpki(const byte* certDer, word32 certSz, - byte* pubKeyDer, - word32* pubKeyDerSz) +WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer, + word32 certSz, + byte* pubKeyDer, + word32* pubKeyDerSz) { DecodedCert cert; int ret; word32 startIdx; word32 idx; word32 length; - int badDate = 0; + int badDate; if (certDer == NULL || certSz == 0 || pubKeyDerSz == NULL) { return BAD_FUNC_ARG; } - /* Initialize decoded cert structure */ + length = 0; + badDate = 0; + wc_InitDecodedCert(&cert, certDer, certSz, NULL); /* Parse up to the SubjectPublicKeyInfo */ ret = wc_GetPubX509(&cert, 0, &badDate); - if (ret < 0) { - wc_FreeDecodedCert(&cert); - return ret; - } + if (ret >= 0) { + /* Save the starting index of SubjectPublicKeyInfo */ + startIdx = cert.srcIdx; - /* Save the starting index of SubjectPublicKeyInfo */ - startIdx = cert.srcIdx; + /* Get the length of the SubjectPublicKeyInfo sequence */ + idx = startIdx; + ret = GetSequence(certDer, &idx, (int*)&length, certSz); + if (ret >= 0) { + /* Calculate total length including sequence header */ + length += (idx - startIdx); - /* Get the length of the SubjectPublicKeyInfo sequence */ - idx = startIdx; - ret = GetSequence(certDer, &idx, (int*)&length, certSz); - if (ret < 0) { - wc_FreeDecodedCert(&cert); - return ret; - } - - /* Calculate total length including sequence header */ - length += (idx - startIdx); - - /* Copy the SubjectPublicKeyInfo if buffer provided */ - if (pubKeyDer != NULL) { - if (*pubKeyDerSz < (word32)length) { - wc_FreeDecodedCert(&cert); - return BUFFER_E; + /* Copy the SubjectPublicKeyInfo if buffer provided */ + if (pubKeyDer != NULL) { + if (*pubKeyDerSz < (word32)length) { + ret = BUFFER_E; + } + else { + XMEMCPY(pubKeyDer, &certDer[startIdx], length); + } + } } - XMEMCPY(pubKeyDer, &certDer[startIdx], length); } - /* Return the size */ - *pubKeyDerSz = length; + if (ret >= 0) { + ret = 0; + } + *pubKeyDerSz = length; wc_FreeDecodedCert(&cert); - return 0; + + return ret; } diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 2f95d5194..b2b067a41 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -879,8 +879,9 @@ WOLFSSL_API int wc_ParseCert( WOLFSSL_API int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, byte* derKey, word32* derKeySz); -WOLFSSL_API int wc_ExportX509PubKeyWithSpki(const byte* cert, word32 certSz, - byte* pubKey, word32* pubKeySz); +WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* cert, + word32 certSz, byte* pubKey, + word32* pubKeySz); #ifdef WOLFSSL_FPKI WOLFSSL_API int wc_GetUUIDFromCert(struct DecodedCert* cert, From 40c9a03ebe501aaa9cf7f3d6fcbfb4c99fb4a79c Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Wed, 14 May 2025 11:31:36 -0600 Subject: [PATCH 3/3] rename function args to match implementation --- wolfcrypt/src/asn.c | 8 ++++---- wolfssl/wolfcrypt/asn_public.h | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index e201e5bbe..2d33563a9 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -24629,7 +24629,7 @@ int wc_CertGetPubKey(const byte* cert, word32 certSz, * @return BUFFER_E if the provided buffer is too small */ WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer, - word32 certSz, + word32 certDerSz, byte* pubKeyDer, word32* pubKeyDerSz) { @@ -24640,14 +24640,14 @@ WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer, word32 length; int badDate; - if (certDer == NULL || certSz == 0 || pubKeyDerSz == NULL) { + if (certDer == NULL || certDerSz == 0 || pubKeyDerSz == NULL) { return BAD_FUNC_ARG; } length = 0; badDate = 0; - wc_InitDecodedCert(&cert, certDer, certSz, NULL); + wc_InitDecodedCert(&cert, certDer, certDerSz, NULL); /* Parse up to the SubjectPublicKeyInfo */ ret = wc_GetPubX509(&cert, 0, &badDate); @@ -24657,7 +24657,7 @@ WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer, /* Get the length of the SubjectPublicKeyInfo sequence */ idx = startIdx; - ret = GetSequence(certDer, &idx, (int*)&length, certSz); + ret = GetSequence(certDer, &idx, (int*)&length, certDerSz); if (ret >= 0) { /* Calculate total length including sequence header */ length += (idx - startIdx); diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index b2b067a41..fff6a5ac8 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -879,9 +879,10 @@ WOLFSSL_API int wc_ParseCert( WOLFSSL_API int wc_GetPubKeyDerFromCert(struct DecodedCert* cert, byte* derKey, word32* derKeySz); -WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* cert, - word32 certSz, byte* pubKey, - word32* pubKeySz); +WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer, + word32 certDerSz, + byte* pubKeyDer, + word32* pubKeyDerSz); #ifdef WOLFSSL_FPKI WOLFSSL_API int wc_GetUUIDFromCert(struct DecodedCert* cert,