From aafc876759533c3c73b1a6463ff86b43d1ece55d Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Fri, 23 Jan 2026 18:35:34 -0800 Subject: [PATCH] Add cert/CRL capabilities: skid, akid, dist point, netscape --- scripts/crl-gen-openssl.test | 8 +- src/crl.c | 198 +++++++++--------- src/ssl.c | 1 - src/x509.c | 378 ++++++++++++++++++++++++++++++++++- tests/api.c | 83 ++++++++ tests/api/test_certman.c | 8 +- wolfssl/openssl/ssl.h | 16 +- wolfssl/ssl.h | 25 +++ wolfssl/wolfio.h | 4 + 9 files changed, 612 insertions(+), 109 deletions(-) diff --git a/scripts/crl-gen-openssl.test b/scripts/crl-gen-openssl.test index 22a91267520..ff6b1e90155 100755 --- a/scripts/crl-gen-openssl.test +++ b/scripts/crl-gen-openssl.test @@ -95,12 +95,8 @@ check_crl() { -CRLfile "$crl" \ "$revoked_cert" 2>&1) || verify_rc=$? verify_rc=${verify_rc:-0} - # Normalize to avoid case/CR differences and accept common OpenSSL error - # formats (some builds report only an error code). - verify_out_norm=$(printf '%s' "$verify_out" | tr '[:upper:]' '[:lower:]' | \ - tr -d '\r') - if ! echo "$verify_out_norm" | grep -q "revoked" && \ - ! echo "$verify_out_norm" | grep -q "error 23"; then + # Avoid pipefail/SIGPIPE false negatives with `grep -q` in a pipeline. + if ! grep -qi "revoked" <<< "$verify_out"; then echo "expected revoked verification failure for $label CRL" echo "$verify_out" return 1 diff --git a/src/crl.c b/src/crl.c index 83f58dae47e..ee8f8391705 100644 --- a/src/crl.c +++ b/src/crl.c @@ -825,6 +825,7 @@ int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type, */ int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) { + int ret = 0; CRL_Entry* ent = NULL; const byte* tbs = NULL; word32 tbsSz = 0; @@ -850,7 +851,8 @@ int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) outSz = *inOutSz; - /* Access the first CRL entry. */ + /* Access the first CRL entry. Lock is held until encoding is complete + * to prevent the entry from being freed by another thread. */ if (wc_LockRwLock_Rd(&crl->crlLock) != 0) { WOLFSSL_MSG("wc_LockRwLock_Rd failed"); return BAD_MUTEX_E; @@ -867,59 +869,63 @@ int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) sigParamsSz = ent->sigParamsSz; #endif } - wc_UnLockRwLock(&crl->crlLock); if (ent == NULL || tbs == NULL || tbsSz == 0 || sig == NULL || sigSz == 0) { WOLFSSL_MSG("CRL entry missing toBeSigned/signature data"); - return BAD_FUNC_ARG; + ret = BAD_FUNC_ARG; } /* Calculate encoded lengths for AlgorithmIdentifier. */ + if (ret == 0) { #ifdef WC_RSA_PSS - if (sigParams != NULL && sigParamsSz > 0) { - /* OID + explicit parameters inside SEQUENCE */ - word32 oidSz = 0; - word32 idLen; - const byte* oid = OidFromId(sigOID, oidSigType, &oidSz); - if (oid == NULL) { - WOLFSSL_MSG("Unknown signature OID for CRL"); - return WOLFSSL_FATAL_ERROR; + if (sigParams != NULL && sigParamsSz > 0) { + /* OID + explicit parameters inside SEQUENCE */ + word32 oidSz = 0; + word32 idLen; + const byte* oid = OidFromId(sigOID, oidSigType, &oidSz); + if (oid == NULL) { + WOLFSSL_MSG("Unknown signature OID for CRL"); + ret = WOLFSSL_FATAL_ERROR; + } + else { + /* OBJECT IDENTIFIER header */ + idLen = (word32)SetObjectId((int)oidSz, NULL); + algoLen = SetSequence(idLen + oidSz + sigParamsSz, NULL) + + idLen + oidSz + sigParamsSz; + } } - /* OBJECT IDENTIFIER header */ - idLen = (word32)SetObjectId((int)oidSz, NULL); - algoLen = SetSequence(idLen + oidSz + sigParamsSz, NULL) - + idLen + oidSz + sigParamsSz; - } - else + else #endif - { - algoLen = SetAlgoID((int)sigOID, NULL, oidSigType, 0); - if (algoLen == 0) { - WOLFSSL_MSG("SetAlgoID failed"); - return WOLFSSL_FATAL_ERROR; + { + algoLen = SetAlgoID((int)sigOID, NULL, oidSigType, 0); + if (algoLen == 0) { + WOLFSSL_MSG("SetAlgoID failed"); + ret = WOLFSSL_FATAL_ERROR; + } } } - /* BIT STRING header for signature */ - bitHdrLen = SetBitString(sigSz, 0, NULL); + if (ret == 0) { + /* BIT STRING header for signature */ + bitHdrLen = SetBitString(sigSz, 0, NULL); - /* Compute total DER size. */ - totalContentLen = tbsSz + algoLen + bitHdrLen + sigSz; - outerHdrLen = SetSequence(totalContentLen, NULL); - derNeeded = outerHdrLen + totalContentLen; + /* Compute total DER size. */ + totalContentLen = tbsSz + algoLen + bitHdrLen + sigSz; + outerHdrLen = SetSequence(totalContentLen, NULL); + derNeeded = outerHdrLen + totalContentLen; + } - if (type == WOLFSSL_FILETYPE_ASN1) { + if (ret == 0 && type == WOLFSSL_FILETYPE_ASN1) { if (buff == NULL) { *inOutSz = (long)derNeeded; - return WOLFSSL_SUCCESS; + ret = WOLFSSL_SUCCESS; } - if ((long)derNeeded > outSz) { + else if ((long)derNeeded > outSz) { WOLFSSL_MSG("Output buffer too small for DER CRL"); - return BUFFER_E; + ret = BUFFER_E; } - - /* Encode DER CRL directly into caller buffer. */ - { + else { + /* Encode DER CRL directly into caller buffer. */ word32 pos = 0; #ifdef WC_RSA_PSS word32 oidSz = 0; @@ -938,18 +944,20 @@ int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) oid = OidFromId(sigOID, oidSigType, &oidSz); if (oid == NULL) { WOLFSSL_MSG("Unknown signature OID for CRL"); - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; + } + else { + /* SEQUENCE header for AlgorithmIdentifier */ + pos += SetSequence((word32)SetObjectId((int)oidSz, NULL) + + oidSz + sigParamsSz, buff + pos); + /* OBJECT IDENTIFIER header and content */ + pos += (word32)SetObjectId((int)oidSz, buff + pos); + XMEMCPY(buff + pos, oid, oidSz); + pos += oidSz; + /* Parameters as captured (already DER encoded) */ + XMEMCPY(buff + pos, sigParams, sigParamsSz); + pos += sigParamsSz; } - /* SEQUENCE header for AlgorithmIdentifier */ - pos += SetSequence((word32)SetObjectId((int)oidSz, NULL) + - oidSz + sigParamsSz, buff + pos); - /* OBJECT IDENTIFIER header and content */ - pos += (word32)SetObjectId((int)oidSz, buff + pos); - XMEMCPY(buff + pos, oid, oidSz); - pos += oidSz; - /* Parameters as captured (already DER encoded) */ - XMEMCPY(buff + pos, sigParams, sigParamsSz); - pos += sigParamsSz; } else #endif @@ -957,26 +965,29 @@ int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) pos += SetAlgoID((int)sigOID, buff + pos, oidSigType, 0); } - /* signature BIT STRING and bytes */ - pos += SetBitString(sigSz, 0, buff + pos); - XMEMCPY(buff + pos, sig, sigSz); + if (ret == 0) { + /* signature BIT STRING and bytes */ + pos += SetBitString(sigSz, 0, buff + pos); + XMEMCPY(buff + pos, sig, sigSz); + + *inOutSz = (long)derNeeded; + ret = WOLFSSL_SUCCESS; + } (void)pos; /* pos not used after this point */ } - - *inOutSz = (long)derNeeded; - return WOLFSSL_SUCCESS; } #ifdef WOLFSSL_DER_TO_PEM - else if (type == WOLFSSL_FILETYPE_PEM) { + else if (ret == 0 && type == WOLFSSL_FILETYPE_PEM) { byte* derTmp = NULL; int pemSz; /* Build DER first in a temporary buffer. */ derTmp = (byte*)XMALLOC(derNeeded, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); if (derTmp == NULL) { - return MEMORY_E; + ret = MEMORY_E; } - /* Encode DER CRL into temporary buffer. */ - { + + if (ret == 0) { + /* Encode DER CRL into temporary buffer. */ word32 pos = 0; #ifdef WC_RSA_PSS word32 oidSz = 0; @@ -989,56 +1000,63 @@ int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) if (sigParams != NULL && sigParamsSz > 0) { oid = OidFromId(sigOID, oidSigType, &oidSz); if (oid == NULL) { - XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); - return WOLFSSL_FATAL_ERROR; + ret = WOLFSSL_FATAL_ERROR; + } + else { + pos += SetSequence((word32)SetObjectId((int)oidSz, NULL) + + oidSz + sigParamsSz, derTmp + pos); + pos += (word32)SetObjectId((int)oidSz, derTmp + pos); + XMEMCPY(derTmp + pos, oid, oidSz); + pos += oidSz; + XMEMCPY(derTmp + pos, sigParams, sigParamsSz); + pos += sigParamsSz; } - pos += SetSequence((word32)SetObjectId((int)oidSz, NULL) + - oidSz + sigParamsSz, derTmp + pos); - pos += (word32)SetObjectId((int)oidSz, derTmp + pos); - XMEMCPY(derTmp + pos, oid, oidSz); - pos += oidSz; - XMEMCPY(derTmp + pos, sigParams, sigParamsSz); - pos += sigParamsSz; } else #endif { pos += SetAlgoID((int)sigOID, derTmp + pos, oidSigType, 0); } - pos += SetBitString(sigSz, 0, derTmp + pos); - XMEMCPY(derTmp + pos, sig, sigSz); + if (ret == 0) { + pos += SetBitString(sigSz, 0, derTmp + pos); + XMEMCPY(derTmp + pos, sig, sigSz); + } (void)pos; /* pos not used after this point */ } /* Determine required PEM size. */ - pemSz = wc_DerToPemEx(derTmp, derNeeded, NULL, 0, NULL, CRL_TYPE); - if (pemSz < 0) { - XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); - return WOLFSSL_FATAL_ERROR; - } - if (buff == NULL) { - *inOutSz = pemSz; - XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); - return WOLFSSL_SUCCESS; - } - if (outSz < pemSz) { - XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); - WOLFSSL_MSG("Output buffer too small for PEM CRL"); - return BUFFER_E; - } - if (wc_DerToPemEx(derTmp, derNeeded, buff, (word32)pemSz, NULL, - CRL_TYPE) < 0) { - XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); - return WOLFSSL_FATAL_ERROR; + if (ret == 0) { + pemSz = wc_DerToPemEx(derTmp, derNeeded, NULL, 0, NULL, CRL_TYPE); + if (pemSz < 0) { + ret = WOLFSSL_FATAL_ERROR; + } + else if (buff == NULL) { + *inOutSz = pemSz; + ret = WOLFSSL_SUCCESS; + } + else if (outSz < pemSz) { + WOLFSSL_MSG("Output buffer too small for PEM CRL"); + ret = BUFFER_E; + } + else if (wc_DerToPemEx(derTmp, derNeeded, buff, (word32)pemSz, + NULL, CRL_TYPE) < 0) { + ret = WOLFSSL_FATAL_ERROR; + } + else { + *inOutSz = pemSz; + ret = WOLFSSL_SUCCESS; + } } - *inOutSz = pemSz; + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); - return WOLFSSL_SUCCESS; } #endif /* WOLFSSL_DER_TO_PEM */ - else { - return BAD_FUNC_ARG; + else if (ret == 0) { + ret = BAD_FUNC_ARG; } + + wc_UnLockRwLock(&crl->crlLock); + return ret; } #ifdef HAVE_CRL_UPDATE_CB diff --git a/src/ssl.c b/src/ssl.c index b449c61b6df..9972e9f7cd9 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -11509,7 +11509,6 @@ void wolfSSL_set_dynlock_destroy_callback( (void)f; } - /* Sets the DNS hostname to name. * Hostname is cleared if name is NULL or empty. */ int wolfSSL_set1_host(WOLFSSL * ssl, const char* name) diff --git a/src/x509.c b/src/x509.c index 65089dc76e8..ab2f14d9438 100644 --- a/src/x509.c +++ b/src/x509.c @@ -34,6 +34,9 @@ #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) #include #endif +#ifdef OPENSSL_EXTRA + #include +#endif #if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) unsigned int wolfSSL_X509_get_extension_flags(WOLFSSL_X509* x509) @@ -3053,8 +3056,31 @@ int wolfSSL_X509_add_altname(WOLFSSL_X509* x509, const char* name, int type) return WOLFSSL_SUCCESS; if (type == ASN_IP_TYPE) { - WOLFSSL_MSG("Type not supported, use wolfSSL_X509_add_altname_ex"); +#ifdef WOLFSSL_IP_ALT_NAME + byte ip4[4]; + byte ip6[16]; + int ptonRet; + + /* Check if this is an ip4 address */ + ptonRet = XINET_PTON(WOLFSSL_IP4, name, ip4); + if (ptonRet == 1) { + return wolfSSL_X509_add_altname_ex(x509, (const char*)ip4, 4, + type); + } + + /* Check for ip6 */ + ptonRet = XINET_PTON(WOLFSSL_IP6, name, ip6); + if (ptonRet == 1) { + return wolfSSL_X509_add_altname_ex(x509, (const char*)ip6, 16, + type); + } + + WOLFSSL_MSG("IP address parse failed"); return WOLFSSL_FAILURE; +#else + WOLFSSL_MSG("WOLFSSL_IP_ALT_NAME not enabled"); + return WOLFSSL_FAILURE; +#endif } return wolfSSL_X509_add_altname_ex(x509, name, nameSz, type); @@ -9397,16 +9423,29 @@ int wolfSSL_X509_CRL_get_signature_nid(const WOLFSSL_X509_CRL* crl) } /* Set signature NID of CRL - * return OID on success + * return WOLFSSL_SUCCESS on success * return negative value on failure */ int wolfSSL_X509_CRL_set_signature_nid(WOLFSSL_X509_CRL* crl, int nid) { - if (crl == NULL || crl->crlList == NULL || nid <= 0) - return BAD_FUNC_ARG; + int ret = WOLFSSL_SUCCESS; + word32 oid; - crl->crlList->signatureOID = nid2oid(nid, oidSigType); - return WOLFSSL_SUCCESS; + if (crl == NULL || crl->crlList == NULL || nid <= 0) { + ret = BAD_FUNC_ARG; + } + + if (ret == WOLFSSL_SUCCESS) { + oid = nid2oid(nid, oidSigType); + if (oid == (word32)-1 || oid == (word32)WOLFSSL_FATAL_ERROR) { + ret = WOLFSSL_FATAL_ERROR; + } + else { + crl->crlList->signatureOID = oid; + } + } + + return ret; } /* Retrieve signature from CRL @@ -10004,7 +10043,7 @@ int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* key) * * @param crl CRL to encode * @param out Pointer to output buffer pointer - * @return Size of DER encoding on success, negative on failure + * @return Size of DER encoding on success, WOLFSSL_FAILURE on failure */ int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, unsigned char** out) { @@ -10057,11 +10096,156 @@ int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, unsigned char** out) if (alloced) { *out = der; } + else { + *out += derSz; + } return (int)derSz; } #endif /* HAVE_CRL && OPENSSL_EXTRA */ +#if defined(WOLFSSL_CERT_EXT) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) +/* Set CRL Distribution Points from pre-encoded DER. + * + * x509 - Certificate to modify + * der - Pre-encoded CRLDistributionPoints DER + * derSz - Size of DER in bytes + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_CRL_set_dist_points(WOLFSSL_X509* x509, + const unsigned char* der, int derSz) +{ + WOLFSSL_ENTER("wolfSSL_X509_CRL_set_dist_points"); + + if (x509 == NULL || der == NULL || derSz <= 0) { + return WOLFSSL_FAILURE; + } + + if (x509->rawCRLInfo != NULL) { + XFREE(x509->rawCRLInfo, x509->heap, DYNAMIC_TYPE_X509_EXT); + } + x509->rawCRLInfo = (byte*)XMALLOC((word32)derSz, x509->heap, + DYNAMIC_TYPE_X509_EXT); + if (x509->rawCRLInfo == NULL) { + return WOLFSSL_FAILURE; + } + + XMEMCPY(x509->rawCRLInfo, der, (word32)derSz); + x509->rawCRLInfoSz = derSz; + x509->CRLdistSet = 1; + + return WOLFSSL_SUCCESS; +} + +/* Add CRL Distribution Point URI. + * Encodes URI into proper CRLDistributionPoints DER format. + * + * x509 - Certificate to modify + * uri - URI string (e.g., "http://crl.example.com/ca.crl") + * critical - Whether extension is critical + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_CRL_add_dist_point(WOLFSSL_X509* x509, + const char* uri, int critical) +{ + word32 uriLen; + byte* derBuf = NULL; + word32 derSz; + word32 idx; + word32 uriTagLen; /* [6] tag + length + URI */ + word32 genNamesLen; /* [0] IMPLICIT GeneralNames wrapper */ + word32 distPtNmLen; /* [0] EXPLICIT distributionPoint wrapper */ + word32 distPtSeqLen; /* SEQUENCE for DistributionPoint */ + word32 outerSeqLen; /* SEQUENCE for CRLDistributionPoints */ + int ret = WOLFSSL_SUCCESS; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_add_dist_point"); + + if (x509 == NULL || uri == NULL) { + return WOLFSSL_FAILURE; + } + + uriLen = (word32)XSTRLEN(uri); + if (uriLen == 0) { + WOLFSSL_MSG("URI empty"); + return WOLFSSL_FAILURE; + } + + /* + * Encode CRL Distribution Points in DER format: + * CRLDistributionPoints ::= SEQUENCE OF DistributionPoint + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] EXPLICIT DistributionPointName OPTIONAL + * } + * DistributionPointName ::= CHOICE { + * fullName [0] IMPLICIT GeneralNames + * } + * GeneralNames ::= SEQUENCE OF GeneralName + * GeneralName ::= [6] IMPLICIT IA5String (uniformResourceIdentifier) + */ + + /* Calculate sizes from innermost to outermost */ + /* [6] tag (1 byte) + length encoding + URI data */ + uriTagLen = ASN_TAG_SZ + SetLength(uriLen, NULL) + uriLen; + /* [0] CONSTRUCTED tag (1 byte) + length encoding + uriTagLen */ + genNamesLen = ASN_TAG_SZ + SetLength(uriTagLen, NULL) + uriTagLen; + /* [0] CONSTRUCTED tag (1 byte) + length encoding + genNamesLen */ + distPtNmLen = ASN_TAG_SZ + SetLength(genNamesLen, NULL) + genNamesLen; + /* SEQUENCE header + distPtNmLen */ + distPtSeqLen = SetSequence(distPtNmLen, NULL) + distPtNmLen; + /* Outer SEQUENCE header + distPtSeqLen */ + outerSeqLen = SetSequence(distPtSeqLen, NULL) + distPtSeqLen; + + derSz = outerSeqLen; + + /* Allocate buffer for DER encoding */ + derBuf = (byte*)XMALLOC(derSz, x509->heap, DYNAMIC_TYPE_X509_EXT); + if (derBuf == NULL) { + return WOLFSSL_FAILURE; + } + + /* Build forward using SetSequence/SetHeader/SetLength */ + idx = 0; + + /* SEQUENCE for CRLDistributionPoints (outer) */ + idx += SetSequence(distPtSeqLen, derBuf + idx); + + /* SEQUENCE for DistributionPoint */ + idx += SetSequence(distPtNmLen, derBuf + idx); + + /* [0] EXPLICIT wrapper for distributionPoint */ + derBuf[idx++] = (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED); + idx += SetLength(genNamesLen, derBuf + idx); + + /* [0] IMPLICIT wrapper for GeneralNames (constructed) */ + derBuf[idx++] = (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED); + idx += SetLength(uriTagLen, derBuf + idx); + + /* [6] IMPLICIT IA5String tag for URI (context-specific, primitive) */ + derBuf[idx++] = (ASN_CONTEXT_SPECIFIC | 6); /* [6] tag */ + idx += SetLength(uriLen, derBuf + idx); + + /* Copy URI string */ + XMEMCPY(derBuf + idx, uri, uriLen); + idx += uriLen; + + /* Store the encoded CRL info in x509 */ + { + ret = wolfSSL_X509_CRL_set_dist_points(x509, derBuf, (int)idx); + if (ret == WOLFSSL_SUCCESS && critical) { + x509->CRLdistCrit = 1; + } + } + + XFREE(derBuf, x509->heap, DYNAMIC_TYPE_X509_EXT); + + return ret; +} +#endif /* WOLFSSL_CERT_EXT && (OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL) */ + #ifdef OPENSSL_EXTRA @@ -13106,9 +13290,8 @@ WOLFSSL_X509_CRL* wolfSSL_PEM_read_X509_CRL(XFILE fp, CRL_TYPE); } -/* Convert CRL to DER or PEM format. +/* Store CRL to file in DER or PEM format. * Returns WOLFSSL_SUCCESS on success, negative on failure. - * The caller is responsible for freeing the buffer using XFREE. */ int wolfSSL_write_X509_CRL(WOLFSSL_X509_CRL* crl, const char* path, int type) { @@ -15720,6 +15903,183 @@ int wolfSSL_X509_set_version(WOLFSSL_X509* x509, long v) return WOLFSSL_SUCCESS; } +#ifdef WOLFSSL_CERT_EXT +/* Set Subject Key Identifier from raw bytes. + * + * x509 - Certificate to modify + * skid - Raw SKID bytes + * skidSz - Size of SKID in bytes + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_set_subject_key_id(WOLFSSL_X509* x509, + const unsigned char* skid, int skidSz) +{ + WOLFSSL_ENTER("wolfSSL_X509_set_subject_key_id"); + + if (x509 == NULL || skid == NULL || skidSz <= 0) { + return WOLFSSL_FAILURE; + } + + /* Allocate/reallocate memory for subjKeyId */ + if (x509->subjKeyId == NULL || (int)x509->subjKeyIdSz < skidSz) { + if (x509->subjKeyId != NULL) { + XFREE(x509->subjKeyId, x509->heap, DYNAMIC_TYPE_X509_EXT); + } + x509->subjKeyId = (byte*)XMALLOC((word32)skidSz, x509->heap, + DYNAMIC_TYPE_X509_EXT); + if (x509->subjKeyId == NULL) { + return WOLFSSL_FAILURE; + } + } + + XMEMCPY(x509->subjKeyId, skid, (word32)skidSz); + x509->subjKeyIdSz = (word32)skidSz; + x509->subjKeyIdSet = 1; + + return WOLFSSL_SUCCESS; +} + +#ifndef NO_SHA +/* Set Subject Key Identifier by computing SHA-1 hash of the public key. + * + * x509 - Certificate to modify (must have public key set) + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_set_subject_key_id_ex(WOLFSSL_X509* x509) +{ + byte hash[WC_SHA_DIGEST_SIZE]; + int ret; + + WOLFSSL_ENTER("wolfSSL_X509_set_subject_key_id_ex"); + + if (x509 == NULL) { + return WOLFSSL_FAILURE; + } + + /* Check if public key has been set */ + if (x509->pubKey.buffer == NULL || x509->pubKey.length == 0) { + WOLFSSL_MSG("Public key not set"); + return WOLFSSL_FAILURE; + } + + /* Compute SHA-1 hash of the public key */ + ret = wc_ShaHash(x509->pubKey.buffer, x509->pubKey.length, hash); + if (ret != 0) { + WOLFSSL_MSG("wc_ShaHash failed"); + return WOLFSSL_FAILURE; + } + + return wolfSSL_X509_set_subject_key_id(x509, hash, WC_SHA_DIGEST_SIZE); +} +#endif /* !NO_SHA */ + +/* Set Authority Key Identifier from raw bytes. + * + * x509 - Certificate to modify + * akid - Raw AKID bytes + * akidSz - Size of AKID in bytes + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_set_authority_key_id(WOLFSSL_X509* x509, + const unsigned char* akid, int akidSz) +{ + WOLFSSL_ENTER("wolfSSL_X509_set_authority_key_id"); + + if (x509 == NULL || akid == NULL || akidSz <= 0) { + return WOLFSSL_FAILURE; + } + + /* Allocate/reallocate memory for authKeyIdSrc */ + if (x509->authKeyIdSrc == NULL || (int)x509->authKeyIdSrcSz < akidSz) { + if (x509->authKeyIdSrc != NULL) { + XFREE(x509->authKeyIdSrc, x509->heap, DYNAMIC_TYPE_X509_EXT); + } + x509->authKeyIdSrc = (byte*)XMALLOC((word32)akidSz, x509->heap, + DYNAMIC_TYPE_X509_EXT); + if (x509->authKeyIdSrc == NULL) { + return WOLFSSL_FAILURE; + } + } + + XMEMCPY(x509->authKeyIdSrc, akid, (word32)akidSz); + x509->authKeyIdSrcSz = (word32)akidSz; + x509->authKeyId = x509->authKeyIdSrc; + x509->authKeyIdSz = (word32)akidSz; + x509->authKeyIdSet = 1; + + return WOLFSSL_SUCCESS; +} + +#ifndef NO_SHA +/* Set Authority Key Identifier from issuer certificate. + * Extracts SKID from issuer (or computes from issuer's public key). + * + * x509 - Certificate to modify + * issuer - Issuer certificate + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_set_authority_key_id_ex(WOLFSSL_X509* x509, + WOLFSSL_X509* issuer) +{ + byte hash[WC_SHA_DIGEST_SIZE]; + int ret; + + WOLFSSL_ENTER("wolfSSL_X509_set_authority_key_id_ex"); + + if (x509 == NULL || issuer == NULL) { + return WOLFSSL_FAILURE; + } + + /* First try to use issuer's SKID if it's set */ + if (issuer->subjKeyIdSet && issuer->subjKeyId != NULL && + issuer->subjKeyIdSz > 0) { + return wolfSSL_X509_set_authority_key_id(x509, issuer->subjKeyId, + (int)issuer->subjKeyIdSz); + } + + /* Otherwise compute from issuer's public key */ + if (issuer->pubKey.buffer == NULL || issuer->pubKey.length == 0) { + WOLFSSL_MSG("Issuer public key not available"); + return WOLFSSL_FAILURE; + } + + ret = wc_ShaHash(issuer->pubKey.buffer, issuer->pubKey.length, hash); + if (ret != 0) { + WOLFSSL_MSG("wc_ShaHash failed"); + return WOLFSSL_FAILURE; + } + + return wolfSSL_X509_set_authority_key_id(x509, hash, WC_SHA_DIGEST_SIZE); +} +#endif /* !NO_SHA */ +#endif /* WOLFSSL_CERT_EXT */ + +#ifndef IGNORE_NETSCAPE_CERT_TYPE +/* Set Netscape Certificate Type extension. + * + * x509 - Certificate to modify + * nsCertType - Bitwise OR of NS_SSL_CLIENT, NS_SSL_SERVER, etc. + * + * Returns WOLFSSL_SUCCESS or WOLFSSL_FAILURE + */ +int wolfSSL_X509_set_ns_cert_type(WOLFSSL_X509* x509, int nsCertType) +{ + WOLFSSL_ENTER("wolfSSL_X509_set_ns_cert_type"); + + if (x509 == NULL) { + return WOLFSSL_FAILURE; + } + + x509->nsCertType = (byte)nsCertType; + + return WOLFSSL_SUCCESS; +} +#endif /* !IGNORE_NETSCAPE_CERT_TYPE */ + #endif /* (OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL) && WOLFSSL_CERT_GEN */ #if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && \ diff --git a/tests/api.c b/tests/api.c index 0e09becb7e6..111fef6dc77 100644 --- a/tests/api.c +++ b/tests/api.c @@ -17959,6 +17959,88 @@ static int test_wolfSSL_X509_SEP(void) return EXPECT_RESULT(); } +/* Test wolfSSL_X509_set_* extension functions */ +static int test_wolfSSL_X509_set_extensions(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) + WOLFSSL_X509* x509 = NULL; +#ifdef WOLFSSL_CERT_EXT + byte skid[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; + byte akid[20] = {20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; +#endif +#ifndef IGNORE_NETSCAPE_CERT_TYPE + int nsCertType = 0; +#endif + + ExpectNotNull(x509 = wolfSSL_X509_new()); + +#ifdef WOLFSSL_CERT_EXT + /* Test wolfSSL_X509_set_subject_key_id */ + ExpectIntEQ(wolfSSL_X509_set_subject_key_id(NULL, skid, sizeof(skid)), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_subject_key_id(x509, NULL, sizeof(skid)), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_subject_key_id(x509, skid, 0), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_subject_key_id(x509, skid, sizeof(skid)), + WOLFSSL_SUCCESS); + + /* Test wolfSSL_X509_set_authority_key_id */ + ExpectIntEQ(wolfSSL_X509_set_authority_key_id(NULL, akid, sizeof(akid)), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_authority_key_id(x509, NULL, sizeof(akid)), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_authority_key_id(x509, akid, 0), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_authority_key_id(x509, akid, sizeof(akid)), + WOLFSSL_SUCCESS); + + /* Test wolfSSL_X509_CRL_add_dist_point */ + ExpectIntEQ(wolfSSL_X509_CRL_add_dist_point(NULL, + "http://crl.example.com/ca.crl", 0), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_CRL_add_dist_point(x509, NULL, 0), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_CRL_add_dist_point(x509, + "http://crl.example.com/ca.crl", 0), WOLFSSL_SUCCESS); + + /* Test wolfSSL_X509_CRL_set_dist_points with raw DER */ + { + /* Simple CRL DP DER encoding for "http://example.com/crl" */ + byte crlDer[] = { + 0x30, 0x1d, /* SEQUENCE (outer) */ + 0x30, 0x1b, /* SEQUENCE (DistributionPoint) */ + 0xa0, 0x19, /* [0] EXPLICIT */ + 0xa0, 0x17, /* [0] IMPLICIT GeneralNames */ + 0x86, 0x15, /* [6] URI */ + 'h','t','t','p',':','/','/','e','x','a','m','p','l','e','.','c', + 'o','m','/','c','r','l' + }; + ExpectIntEQ(wolfSSL_X509_CRL_set_dist_points(NULL, crlDer, + sizeof(crlDer)), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_CRL_set_dist_points(x509, NULL, + sizeof(crlDer)), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_CRL_set_dist_points(x509, crlDer, 0), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_CRL_set_dist_points(x509, crlDer, + sizeof(crlDer)), WOLFSSL_SUCCESS); + } +#endif /* WOLFSSL_CERT_EXT */ + +#ifndef IGNORE_NETSCAPE_CERT_TYPE + /* Test wolfSSL_X509_set_ns_cert_type */ + nsCertType = WC_NS_SSL_CLIENT | WC_NS_SSL_SERVER; + ExpectIntEQ(wolfSSL_X509_set_ns_cert_type(NULL, nsCertType), + WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_X509_set_ns_cert_type(x509, nsCertType), + WOLFSSL_SUCCESS); +#endif + + wolfSSL_X509_free(x509); +#endif /* OPENSSL_EXTRA && !NO_CERTS */ + return EXPECT_RESULT(); +} + static int test_wolfSSL_OpenSSL_add_all_algorithms(void) { EXPECT_DECLS; @@ -32307,6 +32389,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_X509_ALGOR_get0), TEST_DECL(test_wolfSSL_X509_SEP), + TEST_DECL(test_wolfSSL_X509_set_extensions), TEST_DECL(test_wolfSSL_X509_CRL), #ifndef NO_BIO TEST_DECL(test_wolfSSL_X509_print), diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c index 4e5d17e0f02..19ea8bc1201 100644 --- a/tests/api/test_certman.c +++ b/tests/api/test_certman.c @@ -976,9 +976,14 @@ int test_wolfSSL_CertManagerNameConstraint2(void) WOLFSSL_SUCCESS); ExpectIntEQ(wolfSSL_X509_add_altname(x509, "", ASN_DIR_TYPE), WOLFSSL_SUCCESS); - /* IP not supported. */ + /* IP not supported unless WOLFSSL_IP_ALT_NAME is enabled. */ +#ifdef WOLFSSL_IP_ALT_NAME + ExpectIntEQ(wolfSSL_X509_add_altname(x509, "127.0.0.1", ASN_IP_TYPE), + WOLFSSL_SUCCESS); +#else ExpectIntEQ(wolfSSL_X509_add_altname(x509, "127.0.0.1", ASN_IP_TYPE), WOLFSSL_FAILURE); +#endif /* add in matching DIR alt name and resign */ wolfSSL_X509_add_altname_ex(x509, altName, sizeof(altName), ASN_DIR_TYPE); @@ -2397,4 +2402,3 @@ int test_various_pathlen_chains(void) #endif return EXPECT_RESULT(); } - diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 4a2dd2dd278..115a5289f5f 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -576,6 +576,21 @@ typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; #define X509_set1_notBefore wolfSSL_X509_set1_notBefore #define X509_set_serialNumber wolfSSL_X509_set_serialNumber #define X509_set_version wolfSSL_X509_set_version +#ifdef WOLFSSL_CERT_EXT +#define X509_set_subject_key_id wolfSSL_X509_set_subject_key_id +#ifndef NO_SHA +#define X509_set_subject_key_id_ex wolfSSL_X509_set_subject_key_id_ex +#endif +#define X509_set_authority_key_id wolfSSL_X509_set_authority_key_id +#ifndef NO_SHA +#define X509_set_authority_key_id_ex wolfSSL_X509_set_authority_key_id_ex +#endif +#define X509_CRL_set_dist_points wolfSSL_X509_CRL_set_dist_points +#define X509_CRL_add_dist_point wolfSSL_X509_CRL_add_dist_point +#endif +#ifndef IGNORE_NETSCAPE_CERT_TYPE +#define X509_set_ns_cert_type wolfSSL_X509_set_ns_cert_type +#endif #define X509_REQ_set_version wolfSSL_X509_REQ_set_version #define X509_REQ_get_version wolfSSL_X509_REQ_get_version #define X509_sign wolfSSL_X509_sign @@ -818,7 +833,6 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_CRL_verify wolfSSL_X509_CRL_verify #define X509_CRL_get_REVOKED wolfSSL_X509_CRL_get_REVOKED #define X509_CRL_get_issuer wolfSSL_X509_CRL_get_issuer_name -#define X509_CRL_get_signature_nid wolfSSL_X509_CRL_get_signature_nid #define X509_CRL_get_version wolfSSL_X509_CRL_version #define X509_CRL_set_lastUpdate wolfSSL_X509_CRL_set_lastUpdate #define X509_CRL_set1_lastUpdate wolfSSL_X509_CRL_set_lastUpdate diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index ebe06f3e337..86ed5644942 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2307,6 +2307,24 @@ WOLFSSL_API WOLFSSL_ASN1_TIME* wolfSSL_X509_get_notAfter(const WOLFSSL_X509* x50 WOLFSSL_API int wolfSSL_X509_set_serialNumber(WOLFSSL_X509* x509, WOLFSSL_ASN1_INTEGER* s); WOLFSSL_API int wolfSSL_X509_set_version(WOLFSSL_X509* x509, long v); +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) +#ifdef WOLFSSL_CERT_EXT +WOLFSSL_API int wolfSSL_X509_set_subject_key_id(WOLFSSL_X509* x509, + const unsigned char* skid, int skidSz); +#ifndef NO_SHA +WOLFSSL_API int wolfSSL_X509_set_subject_key_id_ex(WOLFSSL_X509* x509); +#endif +WOLFSSL_API int wolfSSL_X509_set_authority_key_id(WOLFSSL_X509* x509, + const unsigned char* akid, int akidSz); +#ifndef NO_SHA +WOLFSSL_API int wolfSSL_X509_set_authority_key_id_ex(WOLFSSL_X509* x509, + WOLFSSL_X509* issuer); +#endif +#endif /* WOLFSSL_CERT_EXT */ +#ifndef IGNORE_NETSCAPE_CERT_TYPE +WOLFSSL_API int wolfSSL_X509_set_ns_cert_type(WOLFSSL_X509* x509, int nsCertType); +#endif +#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ WOLFSSL_API int wolfSSL_X509_sign(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, const WOLFSSL_EVP_MD* md); WOLFSSL_API int wolfSSL_X509_sign_ctx(WOLFSSL_X509 *x509, WOLFSSL_EVP_MD_CTX *ctx); @@ -3515,6 +3533,13 @@ WOLFSSL_API int wolfSSL_X509_CRL_sign(WOLFSSL_X509_CRL* crl, WOLFSSL_API int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, unsigned char** out); #endif /* HAVE_CRL && OPENSSL_EXTRA */ +#if defined(WOLFSSL_CERT_EXT) && \ + (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) +WOLFSSL_API int wolfSSL_X509_CRL_set_dist_points(WOLFSSL_X509* x509, + const unsigned char* der, int derSz); +WOLFSSL_API int wolfSSL_X509_CRL_add_dist_point(WOLFSSL_X509* x509, + const char* uri, int critical); +#endif /* WOLFSSL_CERT_EXT && (OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL) */ #if defined(WOLFSSL_ACERT) && \ (defined(OPENSSL_EXTRA_X509_SMALL) || defined(OPENSSL_EXTRA)) diff --git a/wolfssl/wolfio.h b/wolfssl/wolfio.h index abfdbe55346..6a6e5dd38d4 100644 --- a/wolfssl/wolfio.h +++ b/wolfssl/wolfio.h @@ -1012,6 +1012,10 @@ WOLFSSL_API void wolfSSL_SetIOWriteFlags(WOLFSSL* ssl, int flags); #define XINET_PTON(a,b,c,d) inet_pton((a),(b),(c),(d)) #elif defined(WOLFSSL_ZEPHYR) #define XINET_PTON(a,b,c) zsock_inet_pton((a),(b),(c)) + #elif defined(WOLFSSL_LINUXKM) + #define XINET_PTON(a,b,c) \ + (((a) == WOLFSSL_IP4) ? in4_pton((b), -1, (u8*)(c), -1, NULL) : \ + ((a) == WOLFSSL_IP6) ? in6_pton((b), -1, (u8*)(c), -1, NULL) : 0) #else #define XINET_PTON(a,b,c) inet_pton((a),(b),(c)) #endif