diff --git a/certs/test/cert-max-name-entries.cfg b/certs/test/cert-max-name-entries.cfg new file mode 100644 index 00000000000..f526cb52af1 --- /dev/null +++ b/certs/test/cert-max-name-entries.cfg @@ -0,0 +1,25 @@ +[ req ] +distinguished_name = req_distinguished_name +prompt = no +x509_extensions = v3_ca + +[ req_distinguished_name ] +0.OU = Unit 1 +1.OU = Unit 2 +2.OU = Unit 3 +3.OU = Unit 4 +4.OU = Unit 5 +5.OU = Unit 6 +6.OU = Unit 7 +7.OU = Unit 8 +8.OU = Unit 9 +9.OU = Unit 10 +10.OU = Unit 11 +11.OU = Unit 12 +12.OU = Unit 13 +13.OU = Unit 14 +14.OU = Unit 15 +CN = www.wolfssl.com + +[ v3_ca ] +basicConstraints = critical, CA:TRUE diff --git a/certs/test/cert-max-name-entries.der b/certs/test/cert-max-name-entries.der new file mode 100644 index 00000000000..04f66dbb92c Binary files /dev/null and b/certs/test/cert-max-name-entries.der differ diff --git a/certs/test/cert-max-name-entries.pem b/certs/test/cert-max-name-entries.pem new file mode 100644 index 00000000000..13f79fcd21d --- /dev/null +++ b/certs/test/cert-max-name-entries.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE4zCCA8ugAwIBAgIUNsLW2RNP4CtaY3EwJupxH/8NMHUwDQYJKoZIhvcNAQEL +BQAwggEfMQ8wDQYDVQQLDAZVbml0IDExDzANBgNVBAsMBlVuaXQgMjEPMA0GA1UE +CwwGVW5pdCAzMQ8wDQYDVQQLDAZVbml0IDQxDzANBgNVBAsMBlVuaXQgNTEPMA0G +A1UECwwGVW5pdCA2MQ8wDQYDVQQLDAZVbml0IDcxDzANBgNVBAsMBlVuaXQgODEP +MA0GA1UECwwGVW5pdCA5MRAwDgYDVQQLDAdVbml0IDEwMRAwDgYDVQQLDAdVbml0 +IDExMRAwDgYDVQQLDAdVbml0IDEyMRAwDgYDVQQLDAdVbml0IDEzMRAwDgYDVQQL +DAdVbml0IDE0MRAwDgYDVQQLDAdVbml0IDE1MRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20wHhcNMjYxMDI4MTM0NTQzWhcNMjkwNzI0MTM0NTQzWjCCAR8xDzANBgNV +BAsMBlVuaXQgMTEPMA0GA1UECwwGVW5pdCAyMQ8wDQYDVQQLDAZVbml0IDMxDzAN +BgNVBAsMBlVuaXQgNDEPMA0GA1UECwwGVW5pdCA1MQ8wDQYDVQQLDAZVbml0IDYx +DzANBgNVBAsMBlVuaXQgNzEPMA0GA1UECwwGVW5pdCA4MQ8wDQYDVQQLDAZVbml0 +IDkxEDAOBgNVBAsMB1VuaXQgMTAxEDAOBgNVBAsMB1VuaXQgMTExEDAOBgNVBAsM +B1VuaXQgMTIxEDAOBgNVBAsMB1VuaXQgMTMxEDAOBgNVBAsMB1VuaXQgMTQxEDAO +BgNVBAsMB1VuaXQgMTUxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCVCOFXQfJxbbfSRUEnAWXGRa7yvCQw +uJXOL07W9hyIvHyf+6hnf/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVy +Lm8uhtiVc9qsG1O5Xz/XGQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOm +jBipAm2vwxkBLrgQ48bMQLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1f +HLwjc9EJA4kU0hC5ZMMq0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3Pi +XSXJag3DOWCktLBpQkIJ6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaMT +MBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAmTfaakUVMMDE +vIafwGdq5qxOGFGECFhKS8yRFiuzIUpddrzcrn4V1H9gVtD0ND2ke4sevNCV8e23 +XHgcsrjahzx09HP6vrcrgpuwGawzd1S0jGuXtE9bq7/QnJo2co4wK6/DWEIzE752 +Gzj8iu4e0K7WHBokz1mqwBwB7FBFVrMC1e9rJS40kOaIvvY9jPVEb7wT1ajOj7Wt +e++8E7IPyLqh59K5g2rthdEaj6q1YCHIYGe+xa6IVUk/XnOP3lw+ltjaN28a161G ++wdw5wxWYIN+3utOLmGr8euZu5pyvL4HXxYEKlcZ8u/M9MqJY7Qzq752Kf7rs4Ms +8nRLzUmiDQ== +-----END CERTIFICATE----- diff --git a/certs/test/cert-over-max-name-entries.cfg b/certs/test/cert-over-max-name-entries.cfg new file mode 100644 index 00000000000..d1a7e593be5 --- /dev/null +++ b/certs/test/cert-over-max-name-entries.cfg @@ -0,0 +1,26 @@ +[ req ] +distinguished_name = req_distinguished_name +prompt = no +x509_extensions = v3_ca + +[ req_distinguished_name ] +0.OU = Unit 1 +1.OU = Unit 2 +2.OU = Unit 3 +3.OU = Unit 4 +4.OU = Unit 5 +5.OU = Unit 6 +6.OU = Unit 7 +7.OU = Unit 8 +8.OU = Unit 9 +9.OU = Unit 10 +10.OU = Unit 11 +11.OU = Unit 12 +12.OU = Unit 13 +13.OU = Unit 14 +14.OU = Unit 15 +15.OU = Unit 16 +CN = www.wolfssl.com + +[ v3_ca ] +basicConstraints = critical, CA:TRUE diff --git a/certs/test/cert-over-max-name-entries.der b/certs/test/cert-over-max-name-entries.der new file mode 100644 index 00000000000..cc757aecf40 Binary files /dev/null and b/certs/test/cert-over-max-name-entries.der differ diff --git a/certs/test/cert-over-max-name-entries.pem b/certs/test/cert-over-max-name-entries.pem new file mode 100644 index 00000000000..b15ed6cef21 --- /dev/null +++ b/certs/test/cert-over-max-name-entries.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFBzCCA++gAwIBAgIUdexUP+aDlmBpXXrWAPj7Q0naxUIwDQYJKoZIhvcNAQEL +BQAwggExMQ8wDQYDVQQLDAZVbml0IDExDzANBgNVBAsMBlVuaXQgMjEPMA0GA1UE +CwwGVW5pdCAzMQ8wDQYDVQQLDAZVbml0IDQxDzANBgNVBAsMBlVuaXQgNTEPMA0G +A1UECwwGVW5pdCA2MQ8wDQYDVQQLDAZVbml0IDcxDzANBgNVBAsMBlVuaXQgODEP +MA0GA1UECwwGVW5pdCA5MRAwDgYDVQQLDAdVbml0IDEwMRAwDgYDVQQLDAdVbml0 +IDExMRAwDgYDVQQLDAdVbml0IDEyMRAwDgYDVQQLDAdVbml0IDEzMRAwDgYDVQQL +DAdVbml0IDE0MRAwDgYDVQQLDAdVbml0IDE1MRAwDgYDVQQLDAdVbml0IDE2MRgw +FgYDVQQDDA93d3cud29sZnNzbC5jb20wHhcNMjYxMDI4MTM0NTQzWhcNMjkwNzI0 +MTM0NTQzWjCCATExDzANBgNVBAsMBlVuaXQgMTEPMA0GA1UECwwGVW5pdCAyMQ8w +DQYDVQQLDAZVbml0IDMxDzANBgNVBAsMBlVuaXQgNDEPMA0GA1UECwwGVW5pdCA1 +MQ8wDQYDVQQLDAZVbml0IDYxDzANBgNVBAsMBlVuaXQgNzEPMA0GA1UECwwGVW5p +dCA4MQ8wDQYDVQQLDAZVbml0IDkxEDAOBgNVBAsMB1VuaXQgMTAxEDAOBgNVBAsM +B1VuaXQgMTExEDAOBgNVBAsMB1VuaXQgMTIxEDAOBgNVBAsMB1VuaXQgMTMxEDAO +BgNVBAsMB1VuaXQgMTQxEDAOBgNVBAsMB1VuaXQgMTUxEDAOBgNVBAsMB1VuaXQg +MTYxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMCVCOFXQfJxbbfSRUEnAWXGRa7yvCQwuJXOL07W9hyIvHyf ++6hnf/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVyLm8uhtiVc9qsG1O5 +Xz/XGQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOmjBipAm2vwxkBLrgQ +48bMQLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1fHLwjc9EJA4kU0hC5 +ZMMq0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3PiXSXJag3DOWCktLBp +QkIJ6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaMTMBEwDwYDVR0TAQH/ +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAvoraEaAYuFdQBu0teN4CbKmz7ADh ++pNY18LYO9EH7YUNqVElkeUB74ti+plPIY4KMHja/5GyojHnhay8MsL8AUvc0Ngk +4sy+tkbBS6CmsrCl+Cxqb2fwmGQ4OznKosbjkfngSigKb6kyD/cBK5CUKAewqnBu +v4vmx6GYZ8ppqgZyT19SZvUv4KvhEpwARPjicsaAe+rLs72VrqgfSZOBCFonCc0p +RLCecYCunYQItaDIc6dgQu09QEwPgcR4yp503B4jI2h4MlkfLkhmQT+aotU81Z9U +r/zDrwlGcFNjda0VajrlAyqux+23RAY5OnY8c3x0EOEyWdoTFyVhf0ax1Q== +-----END CERTIFICATE----- diff --git a/certs/test/gen-ext-certs.sh b/certs/test/gen-ext-certs.sh index 0d88a7048cd..b05a1300e5a 100755 --- a/certs/test/gen-ext-certs.sh +++ b/certs/test/gen-ext-certs.sh @@ -1541,3 +1541,69 @@ DNS.1050 = example1050.com EOF gen_cert +# Subject and issuer with 16 attributes: the most an X509_NAME holds +# with OPENSSL_EXTRA +OUT=certs/test/cert-max-name-entries +CONFIG=certs/test/cert-max-name-entries.cfg +tee >$CONFIG <$CONFIG <cm, 1); } -static int CheckDecodedCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert, - WOLFSSL_CERT_MANAGER* cm, int canRetry) +/* Get the issuer X509_NAME of cert to pass to CheckCertCRLCm(). */ +static void* CertCrlIssuerName(DecodedCert* cert) { -#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - void* issuerName = cert->issuerName; +#if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \ + (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \ + !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR) && \ + !defined(NO_STDIO_FILESYSTEM) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)) + /* Used to load a CRL from the hash directory. The cert may have been + * parsed without its X509_NAMEs. */ + (void)GetCertX509Name(cert, ASN_ISSUER, NULL); + return cert->issuerName; +#elif defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + return cert->issuerName; #else - void* issuerName = NULL; + (void)cert; + return NULL; #endif +} + +static int CheckDecodedCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert, + WOLFSSL_CERT_MANAGER* cm, int canRetry) +{ + void* issuerName = CertCrlIssuerName(cert); + return CheckCertCRLCm(crl, cert->issuerHash, cert->serial, cert->serialSz, NULL, cert->extCrlInfo, cert->extCrlInfoSz, issuerName, cm, canRetry); diff --git a/src/internal.c b/src/internal.c index b7749f7ae81..902e02aa3fa 100644 --- a/src/internal.c +++ b/src/internal.c @@ -15659,6 +15659,68 @@ void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType) } } +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) +/* Set the issuer or subject X509_NAME of the X509 from the decoded + * certificate. + * + * When parsing skipped building the X509_NAME, the entries are added straight + * to the X509's name rather than building one and copying it. + * + * @param [in, out] x509 X509 object. + * @param [in, out] name X509's issuer or subject name. + * @param [in, out] dCert Decoded certificate. + * @param [in] nameType ASN_ISSUER or ASN_SUBJECT. + * @return 0 on success. + * @return MEMORY_E when building the name fails. + */ +static int CopyDecodedX509Name(WOLFSSL_X509* x509, WOLFSSL_X509_NAME* name, + DecodedCert* dCert, int nameType) +{ + int ret = 0; + WOLFSSL_X509_NAME* decName; + const byte* der; + + if (nameType == ASN_ISSUER) { + decName = (WOLFSSL_X509_NAME*)dCert->issuerName; + der = dCert->issuerNameDer; + } + else { + decName = (WOLFSSL_X509_NAME*)dCert->subjectName; + der = dCert->subjectNameDer; + } + + if (decName != NULL) { + if (nameType == ASN_ISSUER) { + wolfSSL_X509_set_issuer_name(x509, decName); + } + else { + wolfSSL_X509_set_subject_name(x509, decName); + } + name->x509 = x509; + } + else if (der != NULL) { + /* Same reset as wolfSSL_X509_set_issuer/subject_name(). */ + FreeX509Name(name); + InitX509Name(name, 0, x509->heap); + ret = GetCertX509Name(dCert, nameType, name); + if (ret == 0) { + name->x509 = x509; + if (nameType == ASN_ISSUER) { + x509->issuerSet = 1; + } + } + else { + FreeX509Name(name); + /* The name was checked when the certificate was parsed, so not + * being able to build it now is a resource failure. */ + ret = MEMORY_E; + } + } + + return ret; +} +#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ + #ifdef WOLFSSL_CERT_REQ static int CopyREQAttributes(WOLFSSL_X509* x509, DecodedCert* dCert) { @@ -15818,6 +15880,11 @@ static int CopyDecodedPubKey(WOLFSSL_X509* x509, DecodedCert* dCert, int ret) dCert->pubKeySize, x509->heap, DYNAMIC_TYPE_PUBLIC_KEY); if (x509->pubKey.buffer != NULL) { x509->pubKeyOID = (int)dCert->keyOID; + #ifdef OPENSSL_EXTRA_X509_SMALL + /* Set now as the getters that cache objects made from the key + * may run in parallel. */ + x509->key.pubKeyOID = (int)dCert->keyOID; + #endif x509->pubKey.length = dCert->pubKeySize; XMEMCPY(x509->pubKey.buffer, dCert->publicKey, dCert->pubKeySize); } @@ -15825,66 +15892,10 @@ static int CopyDecodedPubKey(WOLFSSL_X509* x509, DecodedCert* dCert, int ret) ret = MEMORY_E; #if defined(OPENSSL_ALL) if (ret == 0) { - x509->key.pubKeyOID = (int)dCert->keyOID; - - if (!x509->key.algor) { - x509->key.algor = wolfSSL_X509_ALGOR_new(); - } else { - wolfSSL_ASN1_OBJECT_free(x509->key.algor->algorithm); - } - if (!x509->key.algor) { - ret = MEMORY_E; - } else { - if (!(x509->key.algor->algorithm = - wolfSSL_OBJ_nid2obj(oid2nid(dCert->keyOID, oidKeyType)))) { - ret = PUBLIC_KEY_E; - WOLFSSL_ERROR_VERBOSE(ret); - } - } - - switch (dCert->keyOID) { - #ifdef HAVE_ED25519 - case ED25519k: - x509->key.pkey = wolfSSL_EVP_PKEY_new_raw_public_key( - WC_EVP_PKEY_ED25519, NULL, dCert->publicKey, - dCert->pubKeySize); - break; - #endif - #ifdef HAVE_ED448 - case ED448k: - x509->key.pkey = wolfSSL_EVP_PKEY_new_raw_public_key( - WC_EVP_PKEY_ED448, NULL, dCert->publicKey, - dCert->pubKeySize); - break; - #endif - #if defined(WOLFSSL_HAVE_MLDSA) && \ - defined(WOLFSSL_MLDSA_PUBLIC_KEY) && \ - !defined(WOLFSSL_MLDSA_NO_ASN1) && \ - defined(WC_ENABLE_ASYM_KEY_EXPORT) - #ifdef WOLFSSL_MLDSA_FIPS204_DRAFT - case DILITHIUM_LEVEL2k: - case DILITHIUM_LEVEL3k: - case DILITHIUM_LEVEL5k: - #endif - case ML_DSA_44k: - case ML_DSA_65k: - case ML_DSA_87k: - /* Raw key size does not distinguish draft from final, so - * encode with the certificate's key OID. This fills - * the x509->key.pkey cache without a new reference. */ - (void)wolfSSL_X509_get0_pubkey(x509); - break; - #endif - default: - x509->key.pkey = wolfSSL_d2i_PUBKEY(NULL, - &dCert->publicKey, dCert->pubKeySize); - break; - } - - if (x509->key.pkey == NULL) { - ret = PUBLIC_KEY_E; - WOLFSSL_ERROR_VERBOSE(ret); - } + /* The EVP_PKEY is decoded on first use by X509CachedPubKey() and + * the algorithm object by wolfSSL_X509_PUBKEY_get0_param(). */ + wolfSSL_X509_ALGOR_free(x509->key.algor); + x509->key.algor = NULL; } #endif } @@ -16118,18 +16129,16 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) CopyDecodedName(&x509->issuer, dCert, ASN_ISSUER); #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - if (dCert->issuerName != NULL) { - wolfSSL_X509_set_issuer_name(x509, - (WOLFSSL_X509_NAME*)dCert->issuerName); - x509->issuer.x509 = x509; + ret = CopyDecodedX509Name(x509, &x509->issuer, dCert, ASN_ISSUER); + if (ret != 0) { + return ret; } #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ CopyDecodedName(&x509->subject, dCert, ASN_SUBJECT); #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) - if (dCert->subjectName != NULL) { - wolfSSL_X509_set_subject_name(x509, - (WOLFSSL_X509_NAME*)dCert->subjectName); - x509->subject.x509 = x509; + ret = CopyDecodedX509Name(x509, &x509->subject, dCert, ASN_SUBJECT); + if (ret != 0) { + return ret; } #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ @@ -17652,6 +17661,11 @@ PRAGMA_GCC_DIAG_POP #endif InitDecodedCert(args->dCert, cert->buffer, cert->length, ssl->heap); + #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* The X509_NAMEs are only needed when the certificate is copied into + * a WOLFSSL_X509, which adds the entries to its names directly. */ + args->dCert->noX509Name = 1; + #endif args->dCertInit = 1; args->dCert->sigCtx.devId = ssl->devId; @@ -18151,6 +18165,10 @@ static int ProcessPeerCertAddPendingCA(WOLFSSL* ssl, buffer* cert) }); InitDecodedCert(dCertAdd, cert->buffer, cert->length, ssl->heap); +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* A signer doesn't keep the X509_NAMEs, don't build them. */ + dCertAdd->noX509Name = 1; +#endif dCertAdd_inited = 1; ret = ParseCert(dCertAdd, CA_TYPE, NO_VERIFY, SSL_CM(ssl)); @@ -19571,6 +19589,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, if (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) || ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) { WOLFSSL_MSG("try to load certificate if hash dir is set"); + /* Peer certs are parsed without their X509_NAMEs */ + (void)GetCertX509Name(args->dCert, ASN_ISSUER, NULL); ret = LoadCertByIssuer(SSL_STORE(ssl), (WOLFSSL_X509_NAME*)args->dCert->issuerName, X509_LU_X509); @@ -20052,6 +20072,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) { int lastErr = ret; /* save error from last time */ WOLFSSL_MSG("try to load certificate if hash dir is set"); + /* Peer certs are parsed without their X509_NAMEs */ + (void)GetCertX509Name(args->dCert, ASN_ISSUER, NULL); ret = LoadCertByIssuer(SSL_STORE(ssl), (WOLFSSL_X509_NAME*)args->dCert->issuerName, X509_LU_X509); diff --git a/src/ssl_certman.c b/src/ssl_certman.c index ac01a8343b9..0946251320b 100644 --- a/src/ssl_certman.c +++ b/src/ssl_certman.c @@ -3249,6 +3249,10 @@ int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify) #endif InitDecodedCert(cert, der->buffer, der->length, cm->heap); +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* A signer doesn't keep the X509_NAMEs, don't build them. */ + cert->noX509Name = 1; +#endif #ifdef WC_ASN_UNKNOWN_EXT_CB if (cm->unknownExtCallback32 != NULL) { diff --git a/src/x509.c b/src/x509.c index 5eb3cfd0136..b443b5834a5 100644 --- a/src/x509.c +++ b/src/x509.c @@ -4625,6 +4625,10 @@ static WOLFSSL_X509* d2i_X509orX509REQ(WOLFSSL_X509** x509, InitDecodedCert(cert, (byte*)in, (word32)len, heap); #ifdef WOLFSSL_CERT_REQ cert->isCSR = (byte)req; + #endif + #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* CopyDecodedToX509() adds the name entries to the X509 directly. */ + cert->noX509Name = 1; #endif if (ParseCertRelative(cert, type, 0, NULL, NULL) == 0) { newX509 = wolfSSL_X509_new_ex(heap); @@ -6609,6 +6613,11 @@ static WOLFSSL_X509* loadX509orX509REQFromBuffer( if (WC_VAR_OK(cert)) { InitDecodedCert(cert, der->buffer, der->length, NULL); + #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* CopyDecodedToX509() adds the name entries to the X509 + * directly. */ + cert->noX509Name = 1; + #endif /* For TRUSTED_CERT_TYPE the DER buffer holds the certificate * followed by auxiliary trust info. ParseCertRelative() recognizes * the type: it parses only the certificate, permits the trailing @@ -6865,6 +6874,16 @@ static WOLFSSL_EVP_PKEY* X509DecodePubKey(WOLFSSL_X509* x509) else if (x509->pubKeyOID == ED448k) { key->type = WC_EVP_PKEY_ED448; } + #endif + #ifdef OPENSSL_EXTRA + else if (x509->pubKeyOID != ECDSAk && x509->pubKeyOID != SM2k) { + /* Other key types are decoded from the encoded key. */ + const unsigned char* der = x509->pubKey.buffer; + + wolfSSL_EVP_PKEY_free(key); + return wolfSSL_d2i_PUBKEY(NULL, &der, + (long)x509->pubKey.length); + } #endif else { key->type = WC_EVP_PKEY_EC; @@ -6980,6 +6999,7 @@ static WOLFSSL_EVP_PKEY* X509DecodePubKey(WOLFSSL_X509* x509) } #endif /* HAVE_ED25519 */ + /* decode Ed448 key */ #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) && \ (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) if (key->type == WC_EVP_PKEY_ED448) { @@ -6990,6 +7010,7 @@ static WOLFSSL_EVP_PKEY* X509DecodePubKey(WOLFSSL_X509* x509) } key->ownEd448 = 1; + /* The X.509 public key buffer holds the raw Ed448 key. */ if (wc_ed448_import_public( (const unsigned char*)key->pkey.ptr, (word32)key->pkey_sz, key->ed448) != 0) { @@ -7029,6 +7050,26 @@ static WOLFSSL_EVP_PKEY* X509DecodePubKey(WOLFSSL_X509* x509) return key; } +#ifndef SINGLE_THREADED +/* Reads a pointer that another thread may store with + * wolfSSL_Atomic_Ptr_CompareExchange(). */ +static void* X509CacheLoad(void* volatile* ptr) +{ +#if defined(__GNUC__) && defined(__ATOMIC_SEQ_CST) + return __atomic_load_n(ptr, __ATOMIC_ACQUIRE); +#else + void* current = NULL; + + /* Only replaces NULL with NULL, so nothing is changed. */ + (void)wolfSSL_Atomic_Ptr_CompareExchange(ptr, ¤t, NULL); + return current; +#endif +} +#define X509_CACHE_LOAD(ptr) X509CacheLoad((void* volatile*)&(ptr)) +#else +#define X509_CACHE_LOAD(ptr) (ptr) +#endif + /* Returns the public key cached in x509, decoding it on first use. * The key is owned by x509 and freed with it. */ static WOLFSSL_EVP_PKEY* X509CachedPubKey(WOLFSSL_X509* x509) @@ -7037,12 +7078,24 @@ static WOLFSSL_EVP_PKEY* X509CachedPubKey(WOLFSSL_X509* x509) if (x509 == NULL) return NULL; - key = x509->key.pkey; + key = (WOLFSSL_EVP_PKEY*)X509_CACHE_LOAD(x509->key.pkey); if (key == NULL) { key = X509DecodePubKey(x509); if (key != NULL) { - x509->key.pubKeyOID = x509->pubKeyOID; + #ifndef SINGLE_THREADED + void* current = NULL; + + /* Threads may decode the key of a shared X509 at the same time. + * Only the first key is cached, so a key handed out is never + * replaced. */ + if (!wolfSSL_Atomic_Ptr_CompareExchange( + (void* volatile*)&x509->key.pkey, ¤t, key)) { + wolfSSL_EVP_PKEY_free(key); + key = (WOLFSSL_EVP_PKEY*)current; + } + #else x509->key.pkey = key; + #endif } } return key; @@ -12395,6 +12448,8 @@ int wolfSSL_X509_PUBKEY_get0_param(WOLFSSL_ASN1_OBJECT **ppkalg, { const unsigned char* bitStr; int bitStrSz; + WOLFSSL_EVP_PKEY* pkey; + WOLFSSL_X509_ALGOR* algor; WOLFSSL_ENTER("wolfSSL_X509_PUBKEY_get0_param"); @@ -12402,15 +12457,23 @@ int wolfSSL_X509_PUBKEY_get0_param(WOLFSSL_ASN1_OBJECT **ppkalg, WOLFSSL_MSG("X509_PUBKEY struct not populated"); return WOLFSSL_FAILURE; } - if ((pk || ppklen) && !pub->pkey) { + /* The key and algorithm are cached and read once, as other threads may + * be storing them. */ + pkey = (WOLFSSL_EVP_PKEY*)X509_CACHE_LOAD(pub->pkey); + if ((pk || ppklen) && (pkey == NULL)) { WOLFSSL_MSG("X509_PUBKEY has no decoded key"); return WOLFSSL_FAILURE; } - if (!pub->algor) { + algor = (WOLFSSL_X509_ALGOR*)X509_CACHE_LOAD(pub->algor); + if (algor == NULL) { + #ifndef SINGLE_THREADED + void* current = NULL; + #endif + /* Build the algorithm fully before storing it, so a failure * never leaves a half built one behind. */ - WOLFSSL_X509_ALGOR* algor = wolfSSL_X509_ALGOR_new(); + algor = wolfSSL_X509_ALGOR_new(); if (algor == NULL) { return WOLFSSL_FAILURE; } @@ -12421,17 +12484,27 @@ int wolfSSL_X509_PUBKEY_get0_param(WOLFSSL_ASN1_OBJECT **ppkalg, wolfSSL_X509_ALGOR_free(algor); return WOLFSSL_FAILURE; } + #ifndef SINGLE_THREADED + /* Threads may build the algorithm of a shared X509 at the same + * time. Only the first is stored. */ + if (!wolfSSL_Atomic_Ptr_CompareExchange( + (void* volatile*)&pub->algor, ¤t, algor)) { + wolfSSL_X509_ALGOR_free(algor); + algor = (WOLFSSL_X509_ALGOR*)current; + } + #else pub->algor = algor; + #endif } if (pa) - *pa = pub->algor; + *pa = algor; if (ppkalg) - *ppkalg = pub->algor->algorithm; + *ppkalg = algor->algorithm; if ((pk != NULL) || (ppklen != NULL)) { - bitStr = (const unsigned char*)pub->pkey->pkey.ptr; - bitStrSz = pub->pkey->pkey_sz; + bitStr = (const unsigned char*)pkey->pkey.ptr; + bitStrSz = pkey->pkey_sz; /* try to parse BIT STRING from SPKI, otherwise return the * pointer as is */ @@ -12451,17 +12524,22 @@ int wolfSSL_X509_PUBKEY_get0_param(WOLFSSL_ASN1_OBJECT **ppkalg, /* Returns a pointer to the pkey when passed a key */ WOLFSSL_EVP_PKEY* wolfSSL_X509_PUBKEY_get(WOLFSSL_X509_PUBKEY* key) { + WOLFSSL_EVP_PKEY* pkey = NULL; + WOLFSSL_ENTER("wolfSSL_X509_PUBKEY_get"); - if (key == NULL || key->pkey == NULL) { + if (key != NULL) { + pkey = (WOLFSSL_EVP_PKEY*)X509_CACHE_LOAD(key->pkey); + } + if (pkey == NULL) { WOLFSSL_LEAVE("wolfSSL_X509_PUBKEY_get", BAD_FUNC_ARG); return NULL; } - if (wolfSSL_EVP_PKEY_up_ref(key->pkey) != WOLFSSL_SUCCESS) { + if (wolfSSL_EVP_PKEY_up_ref(pkey) != WOLFSSL_SUCCESS) { WOLFSSL_LEAVE("wolfSSL_X509_PUBKEY_get", BAD_MUTEX_E); return NULL; } WOLFSSL_LEAVE("wolfSSL_X509_PUBKEY_get", WOLFSSL_SUCCESS); - return key->pkey; + return pkey; } /* Returns the pkey without a new reference. */ @@ -12470,7 +12548,8 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_PUBKEY_get0(const WOLFSSL_X509_PUBKEY* key) WOLFSSL_ENTER("wolfSSL_X509_PUBKEY_get0"); if (key == NULL) return NULL; - return key->pkey; + return (WOLFSSL_EVP_PKEY*)X509_CACHE_LOAD( + ((WOLFSSL_X509_PUBKEY*)key)->pkey); } int wolfSSL_X509_PUBKEY_set(WOLFSSL_X509_PUBKEY **x, WOLFSSL_EVP_PKEY *key) @@ -15609,6 +15688,18 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( * HAVE_STUNNEL || WOLFSSL_NGINX || HAVE_POCO_LIB || WOLFSSL_HAPROXY */ #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + /* Get the object of an entry in a name. + * The object set with the entry is used when it is for the entry's NID, + * rather than filling it in from the OID table again. + */ + static WOLFSSL_ASN1_OBJECT* X509NameEntryObject(WOLFSSL_X509_NAME_ENTRY* e) + { + if ((e->object != NULL) && (e->object->nid == e->nid)) { + return e->object; + } + return wolfSSL_X509_NAME_ENTRY_get_object(e); + } + /* add all entry of type "nid" to the buffer "fullName" and advance "idx" * since number of entries is small, a brute force search is used here * returns the number of entries added @@ -15628,7 +15719,7 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( unsigned char* data; e = &name->entry[i]; - obj = wolfSSL_X509_NAME_ENTRY_get_object(e); + obj = X509NameEntryObject(e); if (obj == NULL) { return BAD_FUNC_ARG; } @@ -15669,7 +15760,7 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( WOLFSSL_ASN1_OBJECT* obj; e = &name->entry[i]; - obj = wolfSSL_X509_NAME_ENTRY_get_object(e); + obj = X509NameEntryObject(e); if (obj == NULL) return BAD_FUNC_ARG; @@ -15704,22 +15795,15 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( return 0; } - /* Copies entry into name. With it being copied freeing entry becomes the - * callers responsibility. + /* Set an entry in name from the NID, string type and data. + * idx is the index to place the entry at or -1 for the first unset one. * returns 1 for success and 0 for error */ - int wolfSSL_X509_NAME_add_entry(WOLFSSL_X509_NAME* name, - WOLFSSL_X509_NAME_ENTRY* entry, int idx, int set) + static int X509NameAddEntry(WOLFSSL_X509_NAME* name, int nid, int type, + const unsigned char* data, int dataSz, int idx) { WOLFSSL_X509_NAME_ENTRY* current = NULL; int ret, i; - WOLFSSL_ENTER_VERBOSE("wolfSSL_X509_NAME_add_entry"); - - if (name == NULL || entry == NULL || entry->value == NULL) { - WOLFSSL_MSG("NULL argument passed in"); - return WOLFSSL_FAILURE; - } - if (idx >= 0) { /* place in specific index */ @@ -15748,11 +15832,8 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( if (current->set == 0) name->entrySz++; - if (wolfSSL_X509_NAME_ENTRY_create_by_NID(¤t, - entry->nid, - wolfSSL_ASN1_STRING_type(entry->value), - wolfSSL_ASN1_STRING_data(entry->value), - wolfSSL_ASN1_STRING_length(entry->value)) != NULL) + if ((wolfSSL_X509_NAME_ENTRY_create_by_NID(¤t, nid, type, data, + dataSz) != NULL) && (current->value != NULL)) { ret = WOLFSSL_SUCCESS; #ifdef OPENSSL_ALL @@ -15787,10 +15868,29 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( if (RebuildFullName(name) != 0) return WOLFSSL_FAILURE; - (void)set; return WOLFSSL_SUCCESS; } + /* Copies entry into name. With it being copied freeing entry becomes the + * callers responsibility. + * returns 1 for success and 0 for error */ + int wolfSSL_X509_NAME_add_entry(WOLFSSL_X509_NAME* name, + WOLFSSL_X509_NAME_ENTRY* entry, int idx, int set) + { + WOLFSSL_ENTER_VERBOSE("wolfSSL_X509_NAME_add_entry"); + + if (name == NULL || entry == NULL || entry->value == NULL) { + WOLFSSL_MSG("NULL argument passed in"); + return WOLFSSL_FAILURE; + } + + (void)set; + return X509NameAddEntry(name, entry->nid, + wolfSSL_ASN1_STRING_type(entry->value), + wolfSSL_ASN1_STRING_data(entry->value), + wolfSSL_ASN1_STRING_length(entry->value), idx); + } + int wolfSSL_X509_NAME_add_entry_by_txt(WOLFSSL_X509_NAME *name, const char *field, int type, const unsigned char *bytes, int len, @@ -15826,16 +15926,14 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( int type, const unsigned char *bytes, int len, int loc, int set) { - int ret; - WOLFSSL_X509_NAME_ENTRY* entry; WOLFSSL_ENTER_VERBOSE("wolfSSL_X509_NAME_add_entry_by_NID"); - entry = wolfSSL_X509_NAME_ENTRY_create_by_NID(NULL, nid, type, bytes, - len); - if (entry == NULL) + if (name == NULL || bytes == NULL) { + WOLFSSL_MSG("NULL argument passed in"); return WOLFSSL_FAILURE; - ret = wolfSSL_X509_NAME_add_entry(name, entry, loc, set); - wolfSSL_X509_NAME_ENTRY_free(entry); - return ret; + } + (void)set; + /* Set the entry in place rather than copying a temporary one. */ + return X509NameAddEntry(name, nid, type, bytes, len, loc); } WOLFSSL_X509_NAME_ENTRY *wolfSSL_X509_NAME_delete_entry( diff --git a/tests/api/test_ossl_x509.c b/tests/api/test_ossl_x509.c index 6dde5a998c1..11953ab9d0e 100644 --- a/tests/api/test_ossl_x509.c +++ b/tests/api/test_ossl_x509.c @@ -2130,6 +2130,48 @@ int test_wolfSSL_X509_max_name_constraints(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509_max_name_entries(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \ + !defined(NO_TLS) && !defined(NO_RSA) + + /* Only test if max name entries has not been modified */ +#if WC_MAX_NAME_ENTRIES == 16 + + WOLFSSL_X509* x509 = NULL; + WOLFSSL_CERT_MANAGER* cm = NULL; + /* Self-signed CAs with 16 and 17 attributes in the subject and issuer */ + const char* max_entries = "./certs/test/cert-max-name-entries.pem"; + const char* over_max_entries = "./certs/test/cert-over-max-name-entries.pem"; + + /* The X509_NAME entries are added straight to the X509. */ + ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(max_entries, + WOLFSSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_X509_NAME_entry_count( + wolfSSL_X509_get_subject_name(x509)), WC_MAX_NAME_ENTRIES); + ExpectIntEQ(wolfSSL_X509_NAME_entry_count( + wolfSSL_X509_get_issuer_name(x509)), WC_MAX_NAME_ENTRIES); + wolfSSL_X509_free(x509); + x509 = NULL; + ExpectNull(x509 = wolfSSL_X509_load_certificate_file(over_max_entries, + WOLFSSL_FILETYPE_PEM)); + wolfSSL_X509_free(x509); + + /* Loading a CA doesn't build the X509_NAMEs, but still rejects names + * that they can't hold. */ + ExpectNotNull(cm = wolfSSL_CertManagerNew()); + ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, max_entries, NULL), + WOLFSSL_SUCCESS); + ExpectIntNE(wolfSSL_CertManagerLoadCA(cm, over_max_entries, NULL), + WOLFSSL_SUCCESS); + wolfSSL_CertManagerFree(cm); +#endif + +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_check_ca(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509.h b/tests/api/test_ossl_x509.h index 1d3771ca5e5..cb8f9184a3e 100644 --- a/tests/api/test_ossl_x509.h +++ b/tests/api/test_ossl_x509.h @@ -55,6 +55,7 @@ int test_wolfSSL_X509_check_host_embedded_nul_san(void); int test_wolfSSL_MatchDomainName_wildcard(void); int test_wolfSSL_X509_max_altnames(void); int test_wolfSSL_X509_max_name_constraints(void); +int test_wolfSSL_X509_max_name_entries(void); int test_wolfSSL_X509_check_ca(void); int test_X509_get_signature_nid(void); int test_wolfSSL_X509_cmp(void); @@ -91,6 +92,7 @@ int test_wolfSSL_X509_cmp(void); TEST_DECL_GROUP("ossl_x509", test_wolfSSL_MatchDomainName_wildcard), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_max_altnames), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_max_name_constraints), \ + TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_max_name_entries), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_ca), \ TEST_DECL_GROUP("ossl_x509", test_X509_get_signature_nid), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_cmp) diff --git a/tests/api/test_ossl_x509_pk.c b/tests/api/test_ossl_x509_pk.c index 72455ad551f..e4b7302d7c2 100644 --- a/tests/api/test_ossl_x509_pk.c +++ b/tests/api/test_ossl_x509_pk.c @@ -867,6 +867,126 @@ int test_wolfSSL_X509_get0_pubkey(void) return EXPECT_RESULT(); } +/* The public key of a certificate is decoded on first use for every key type, + * not only those decoded when the certificate is parsed. */ +int test_wolfSSL_X509_get_pubkey_key_types(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) && \ + defined(HAVE_ED448_KEY_EXPORT) + { + X509* x509 = NULL; + EVP_PKEY* key = NULL; + ASN1_OBJECT* obj = NULL; + const unsigned char* pk = NULL; + int pkLen = 0; + byte pub[ED448_PUB_KEY_SIZE]; + word32 pubSz = (word32)sizeof(pub); + + ExpectNotNull(x509 = X509_load_certificate_file( + "./certs/ed448/ca-ed448.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(key = X509_get_pubkey(x509)); + ExpectPtrEq(X509_get0_pubkey(x509), key); + ExpectIntEQ(EVP_PKEY_id(key), EVP_PKEY_ED448); + /* The decoded key is the key in the certificate. */ + ExpectIntEQ(X509_PUBKEY_get0_param(&obj, &pk, &pkLen, NULL, + X509_get_X509_PUBKEY(x509)), 1); + ExpectIntEQ(pkLen, ED448_PUB_KEY_SIZE); + ExpectNotNull(key == NULL ? NULL : key->ed448); + ExpectIntEQ(wc_ed448_export_public(key == NULL ? NULL : key->ed448, + pub, &pubSz), 0); + ExpectIntEQ(pubSz, ED448_PUB_KEY_SIZE); + if ((pk != NULL) && (pkLen == ED448_PUB_KEY_SIZE)) { + ExpectBufEQ(pub, pk, ED448_PUB_KEY_SIZE); + } + EVP_PKEY_free(key); + X509_free(x509); + } +#endif +#if !defined(NO_RSA) && defined(WC_RSA_PSS) + { + X509* x509 = NULL; + EVP_PKEY* key = NULL; + + ExpectNotNull(x509 = X509_load_certificate_file( + "./certs/rsapss/ca-rsapss.pem", SSL_FILETYPE_PEM)); + ExpectNotNull(key = X509_get0_pubkey(x509)); + ExpectIntEQ(EVP_PKEY_id(key), EVP_PKEY_RSA); + ExpectIntEQ(EVP_PKEY_bits(key), 2048); + X509_free(x509); + } +#endif +#endif + return EXPECT_RESULT(); +} + +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) && !defined(SINGLE_THREADED) +#define X509_PUBKEY_THREADS 4 + +typedef struct X509PubKeyThreadData { + X509* x509; + EVP_PKEY* key; + X509_ALGOR* algor; +} X509PubKeyThreadData; + +static THREAD_RETURN WOLFSSL_THREAD test_X509_pubkey_thread(void* arg) +{ + X509PubKeyThreadData* data = (X509PubKeyThreadData*)arg; + + data->key = X509_get0_pubkey(data->x509); + if (X509_PUBKEY_get0_param(NULL, NULL, NULL, &data->algor, + X509_get_X509_PUBKEY(data->x509)) != 1) { + data->algor = NULL; + } + WOLFSSL_RETURN_FROM_THREAD(0); +} +#endif + +/* Threads getting the public key of a shared X509 for the first time all get + * the one key and algorithm that are cached. */ +int test_wolfSSL_X509_get0_pubkey_threads(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_CERTS) && !defined(SINGLE_THREADED) + X509PubKeyThreadData data[X509_PUBKEY_THREADS]; + THREAD_TYPE threads[X509_PUBKEY_THREADS]; + X509* x509 = NULL; + int started; + int round; + int i; + + for (round = 0; (round < 16) && EXPECT_SUCCESS(); round++) { + ExpectNotNull(x509 = X509_load_certificate_file(caCertFile, + SSL_FILETYPE_PEM)); + XMEMSET(data, 0, sizeof(data)); + started = 0; + while ((started < X509_PUBKEY_THREADS) && EXPECT_SUCCESS()) { + data[started].x509 = x509; + ExpectIntEQ(wolfSSL_NewThread(&threads[started], + test_X509_pubkey_thread, &data[started]), 0); + if (EXPECT_SUCCESS()) { + started++; + } + } + for (i = 0; i < started; i++) { + ExpectIntEQ(wolfSSL_JoinThread(threads[i]), 0); + } + for (i = 0; i < started; i++) { + ExpectNotNull(data[i].key); + ExpectPtrEq(data[i].key, X509_get0_pubkey(x509)); + ExpectNotNull(data[i].algor); + ExpectPtrEq(data[i].algor, data[0].algor); + } + X509_free(x509); + x509 = NULL; + } +#endif + return EXPECT_RESULT(); +} + /* A failed X509_PUBKEY_get0_param() must not leave a half built algorithm * that later calls report as success. */ int test_wolfSSL_X509_PUBKEY_get0_param_fail(void) diff --git a/tests/api/test_ossl_x509_pk.h b/tests/api/test_ossl_x509_pk.h index a095da75780..c7afb032848 100644 --- a/tests/api/test_ossl_x509_pk.h +++ b/tests/api/test_ossl_x509_pk.h @@ -32,6 +32,8 @@ int test_wolfSSL_X509_PUBKEY_MLDSA(void); int test_wolfSSL_X509_PUBKEY_get(void); int test_wolfSSL_X509_set_pubkey(void); int test_wolfSSL_X509_get0_pubkey(void); +int test_wolfSSL_X509_get_pubkey_key_types(void); +int test_wolfSSL_X509_get0_pubkey_threads(void); int test_wolfSSL_X509_PUBKEY_get0_param_fail(void); #define TEST_OSSL_X509_PK_DECLS \ @@ -43,6 +45,8 @@ int test_wolfSSL_X509_PUBKEY_get0_param_fail(void); TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_PUBKEY_get), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_set_pubkey), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey), \ + TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get_pubkey_key_types), \ + TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_get0_pubkey_threads), \ TEST_DECL_GROUP("ossl_x509_pk", test_wolfSSL_X509_PUBKEY_get0_param_fail) #endif /* WOLFCRYPT_TEST_OSSL_X509_PK_H */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index a1d8bae76dc..79f4769b824 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -16076,16 +16076,20 @@ static int GetRDN(DecodedCert* cert, char* full, word32* idx, int* nid, /* Add the attribute value last parsed into an X509_NAME. * - * @param [in, out] dName X509_NAME to add to. NULL is ignored. - * @param [in] nid OpenSSL NID of the attribute type. 0 when unknown, - * in which case nothing is added. - * @param [in] val Parsed attribute value (rdnChoice data). + * @param [in, out] dName X509_NAME to add to. NULL when the X509_NAME is + * not being built. + * @param [in] nid OpenSSL NID of the attribute type. 0 when unknown, + * in which case nothing is added. + * @param [in] val Parsed attribute value (rdnChoice data). + * @param [in, out] entries Number of entries the X509_NAME would hold. Used + * when dName is NULL. * @return 0 on success. * @return ASN_PARSE_E when the string is empty or the entry could not be * added. */ #ifdef WOLFSSL_X509_NAME_AVAILABLE -static int AddRDNToName(WOLFSSL_X509_NAME* dName, int nid, ASNGetData* val) +static int AddRDNToName(WOLFSSL_X509_NAME* dName, int nid, ASNGetData* val, + int* entries) { int ret = 0; int enc; @@ -16137,6 +16141,14 @@ static int AddRDNToName(WOLFSSL_X509_NAME* dName, int nid, ASNGetData* val) ret = ASN_PARSE_E; } } + else if (nid != 0) { + /* Fail when the X509_NAME, built later by GetCertX509Name(), can't + * hold all the entries - as when it is built now. */ + if (++(*entries) > MAX_NAME_ENTRIES) { + WOLFSSL_MSG("Too many entries for X509_NAME"); + ret = ASN_PARSE_E; + } + } return ret; } #endif /* WOLFSSL_X509_NAME_AVAILABLE */ @@ -16171,6 +16183,7 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, word32 srcIdx = *inOutIdx; #ifdef WOLFSSL_X509_NAME_AVAILABLE WOLFSSL_X509_NAME* dName = NULL; + int nameEntries = 0; #endif /* WOLFSSL_X509_NAME_AVAILABLE */ WOLFSSL_MSG_VERBOSE("Getting Cert Name"); @@ -16193,7 +16206,21 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, CALLOC_ASNGETDATA(dataASN, rdnASN_Length, ret, cert->heap); #ifdef WOLFSSL_X509_NAME_AVAILABLE - if (ret == 0) { + /* Keep where the encoded name is so GetCertX509Name() can build the + * X509_NAME later. */ + if (nameType == ASN_ISSUER) { + cert->issuerNameDer = input + srcIdx; + cert->issuerNameDerSz = maxIdx - srcIdx; + } + else { + cert->subjectNameDer = input + srcIdx; + cert->subjectNameDerSz = maxIdx - srcIdx; + } + if (cert->x509NameTarget != NULL) { + /* Fill in the X509_NAME passed in. */ + dName = (WOLFSSL_X509_NAME*)cert->x509NameTarget; + } + else if ((ret == 0) && (!cert->noX509Name)) { /* Create an X509_NAME to hold data for OpenSSL compatibility APIs. */ dName = wolfSSL_X509_NAME_new_ex(cert->heap); if (dName == NULL) { @@ -16255,10 +16282,12 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, dataASN); } #ifdef WOLFSSL_X509_NAME_AVAILABLE - /* TODO: push this back up to ssl.c - * (do parsing for WOLFSSL_X509_NAME on demand) */ + /* The entry is only added when the X509_NAME is being built. + * GetCertX509Name() builds it on demand when parsing skipped it, + * but the checks are always done. */ if (ret == 0) { - ret = AddRDNToName(dName, nid, &dataASN[RDNASN_IDX_ATTR_VAL]); + ret = AddRDNToName(dName, nid, &dataASN[RDNASN_IDX_ATTR_VAL], + &nameEntries); } #endif @@ -16282,7 +16311,8 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, #ifdef WOLFSSL_X509_NAME_AVAILABLE if (ret == 0) { ret = AddRDNToName(dName, nid, - &dataASN[RDNASN_IDX_ATTR_VAL]); + &dataASN[RDNASN_IDX_ATTR_VAL], + &nameEntries); } #endif } @@ -16295,8 +16325,9 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, *inOutIdx = srcIdx; #ifdef WOLFSSL_X509_NAME_AVAILABLE - /* Store X509_NAME in certificate. */ - if (nameType == ASN_ISSUER) { + /* Store X509_NAME in certificate, when it was built and not passed + * in. */ + if ((dName != NULL) && (nameType == ASN_ISSUER)) { #if (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || \ defined(HAVE_LIGHTY)) && \ (defined(HAVE_PKCS7) || defined(WOLFSSL_CERT_EXT)) @@ -16304,18 +16335,22 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, WC_ASN_NAME_MAX); XMEMCPY(dName->raw, cert->issuerRaw, (size_t)dName->rawLen); #endif - cert->issuerName = dName; + if (dName != cert->x509NameTarget) { + cert->issuerName = dName; + } } - else { + else if (dName != NULL) { #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) dName->rawLen = (int)min((word32)cert->subjectRawLen, WC_ASN_NAME_MAX); XMEMCPY(dName->raw, cert->subjectRaw, (size_t)dName->rawLen); #endif - cert->subjectName = dName; + if (dName != cert->x509NameTarget) { + cert->subjectName = dName; + } } } - else { + else if (dName != cert->x509NameTarget) { /* Dispose of unused X509_NAME. */ wolfSSL_X509_NAME_free(dName); #endif @@ -16394,6 +16429,79 @@ int GetName(DecodedCert* cert, int nameType, int maxIdx) return ret; } #endif /* WOLFSSL_ASN_TEMPLATE */ + +#ifdef WOLFSSL_X509_NAME_AVAILABLE +/* Build the WOLFSSL_X509_NAME for the issuer or subject of a parsed + * certificate when parsing skipped it (noX509Name set). + * + * The encoded name is parsed again in a temporary DecodedCert so that the + * fields of cert are left as they are. + * + * @param [in, out] cert Decoded certificate object. + * @param [in] nameType ASN_ISSUER or ASN_SUBJECT. + * @param [in, out] target Empty WOLFSSL_X509_NAME to add the entries to. + * NULL to store a new one in cert. + * @return 0 on success, including when the name in cert was already built. + * @return BAD_FUNC_ARG when cert is NULL or the encoded name is not known. + * @return MEMORY_E when dynamic memory allocation fails. + * @return Other negative values when parsing the name fails. + */ +int GetCertX509Name(DecodedCert* cert, int nameType, void* target) +{ + int ret = 0; + void** name; + const byte* der; + word32 derSz; + WC_DECLARE_VAR(tmp, DecodedCert, 1, 0); + + if (cert == NULL) { + return BAD_FUNC_ARG; + } + if (nameType == ASN_ISSUER) { + name = &cert->issuerName; + der = cert->issuerNameDer; + derSz = cert->issuerNameDerSz; + } + else { + name = &cert->subjectName; + der = cert->subjectNameDer; + derSz = cert->subjectNameDerSz; + } + if ((target == NULL) && (*name != NULL)) { + return 0; + } + if ((der == NULL) || (derSz == 0)) { + return BAD_FUNC_ARG; + } + + WC_ALLOC_VAR_EX(tmp, DecodedCert, 1, cert->heap, DYNAMIC_TYPE_DCERT, + return MEMORY_E); + InitDecodedCert(tmp, der, derSz, cert->heap); + /* The name hash uses the hash of the signature algorithm (SM3 for SM2). */ + tmp->signatureOID = cert->signatureOID; + tmp->x509NameTarget = target; + ret = GetName(tmp, nameType, (int)derSz); + if ((ret == 0) && (target == NULL)) { + /* Take ownership of the X509_NAME from the temporary object. */ + if (nameType == ASN_ISSUER) { + *name = tmp->issuerName; + tmp->issuerName = NULL; + } + else { + *name = tmp->subjectName; + tmp->subjectName = NULL; + } + if (*name == NULL) { + ret = ASN_PARSE_E; + } + } + FreeDecodedCert(tmp); + WC_FREE_VAR_EX(tmp, cert->heap, DYNAMIC_TYPE_DCERT); + + return ret; +} +#endif /* WOLFSSL_X509_NAME_AVAILABLE */ + #ifndef NO_ASN_TIME /* two byte date/time, add to value */ diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 6bb3427a208..5eb722f00da 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2170,6 +2170,14 @@ struct DecodedCert { /* WOLFSSL_X509_NAME structures (used void* to avoid including ssl.h) */ void* issuerName; void* subjectName; + /* Encoded issuer and subject Names in source, for GetCertX509Name() */ + const byte* issuerNameDer; + const byte* subjectNameDer; + word32 issuerNameDerSz; + word32 subjectNameDerSz; + /* WOLFSSL_X509_NAME to fill in when parsing a name instead of + * allocating one. Set by GetCertX509Name(). */ + void* x509NameTarget; #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ #ifdef WOLFSSL_SEP int deviceTypeSz; @@ -2303,6 +2311,12 @@ struct DecodedCert { * TRUSTED CERTIFICATE auxiliary trust * info. */ WC_BITFIELD selfIssued:1; /* Issuer name matches subject name */ +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + WC_BITFIELD noX509Name:1; /* don't build issuerName and + * subjectName when parsing. Use + * GetCertX509Name() to build them + * when needed. */ +#endif #ifdef WC_ASN_UNKNOWN_EXT_CB wc_UnknownExtCallback unknownExtCallback; wc_UnknownExtCallback32 unknownExtCallback32; @@ -2547,6 +2561,11 @@ WOLFSSL_LOCAL int CalcHashId_ex(const byte* data, word32 len, byte* hash, WOLFSSL_LOCAL int GetHashId(const byte* id, int length, byte* hash, int hashAlg); WOLFSSL_LOCAL int GetName(DecodedCert* cert, int nameType, int maxIdx); +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \ + !defined(WOLFCRYPT_ONLY) +WOLFSSL_LOCAL int GetCertX509Name(DecodedCert* cert, int nameType, + void* target); +#endif #ifdef ASN_BER_TO_DER WOLFSSL_API int wc_BerToDer(const byte* ber, word32 berSz, byte* der,