diff --git a/.gitignore b/.gitignore index 0ef9644175..591b9d4230 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,10 @@ ecc-key.der ecc-key.pem certreq.der certreq.pem +crlRsaOut.pem +crlRsaOut.der +crlEccOut.pem +crlEccOut.der pkcs7cert.der pkcs7authEnvelopedDataAES128GCM.der pkcs7authEnvelopedDataAES128GCM_ECDH_SHA1KDF.der @@ -470,3 +474,6 @@ wolfssl/debug-trace-error-codes.h wolfssl/debug-untrace-error-codes.h AGENTS.md + +# Code navigation files +compile_commands.json diff --git a/src/crl.c b/src/crl.c index 17f1fb30e3..27d4f05a9b 100644 --- a/src/crl.c +++ b/src/crl.c @@ -816,6 +816,227 @@ int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type, return ret ? ret : WOLFSSL_SUCCESS; /* convert 0 to WOLFSSL_SUCCESS */ } +/* Store CRL into a buffer in DER or PEM format. + * If buff is NULL, updates inOutSz with required size and returns success. + * Returns WOLFSSL_SUCCESS on success, negative on failure. + */ +int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) +{ + CRL_Entry* ent = NULL; + const byte* tbs = NULL; + word32 tbsSz = 0; + const byte* sig = NULL; + word32 sigSz = 0; + word32 sigOID = 0; +#ifdef WC_RSA_PSS + const byte* sigParams = NULL; + word32 sigParamsSz = 0; +#endif + word32 algoLen = 0; + word32 bitHdrLen = 0; + word32 totalContentLen = 0; + word32 outerHdrLen = 0; + word32 derNeeded = 0; + long outSz = 0; + + WOLFSSL_ENTER("BufferStoreCRL"); + + if (crl == NULL || inOutSz == NULL) { + return BAD_FUNC_ARG; + } + + outSz = *inOutSz; + + /* Access the first CRL entry. */ + if (wc_LockRwLock_Rd(&crl->crlLock) != 0) { + WOLFSSL_MSG("wc_LockRwLock_Rd failed"); + return BAD_MUTEX_E; + } + ent = crl->crlList; + if (ent != NULL) { + tbs = ent->toBeSigned; + tbsSz = ent->tbsSz; + sig = ent->signature; + sigSz = ent->signatureSz; + sigOID = ent->signatureOID; +#ifdef WC_RSA_PSS + sigParams = ent->sigParams; + 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; + } + + /* Calculate encoded lengths for AlgorithmIdentifier. */ +#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; + } + /* OBJECT IDENTIFIER header */ + idLen = (word32)SetObjectId((int)oidSz, NULL); + algoLen = SetSequence(idLen + oidSz + sigParamsSz, NULL) + + idLen + oidSz + sigParamsSz; + } + else +#endif + { + algoLen = SetAlgoID((int)sigOID, NULL, oidSigType, 0); + if (algoLen == 0) { + WOLFSSL_MSG("SetAlgoID failed"); + return WOLFSSL_FATAL_ERROR; + } + } + + /* 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; + + if (type == WOLFSSL_FILETYPE_ASN1) { + if (buff == NULL) { + *inOutSz = (long)derNeeded; + return WOLFSSL_SUCCESS; + } + if ((long)derNeeded > outSz) { + WOLFSSL_MSG("Output buffer too small for DER CRL"); + return BUFFER_E; + } + + /* Encode DER CRL directly into caller buffer. */ + { + word32 pos = 0; +#ifdef WC_RSA_PSS + word32 oidSz = 0; + const byte* oid = NULL; +#endif + /* Outer SEQUENCE header */ + pos += SetSequence(totalContentLen, buff + pos); + /* tbsCertList */ + XMEMCPY(buff + pos, tbs, tbsSz); + pos += tbsSz; + + /* signatureAlgorithm AlgorithmIdentifier */ +#ifdef WC_RSA_PSS + if (sigParams != NULL && sigParamsSz > 0) { + /* Lookup OID bytes for signature algorithm. */ + oid = OidFromId(sigOID, oidSigType, &oidSz); + if (oid == NULL) { + WOLFSSL_MSG("Unknown signature OID for CRL"); + return WOLFSSL_FATAL_ERROR; + } + /* 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 + { + pos += SetAlgoID((int)sigOID, buff + pos, oidSigType, 0); + } + + /* signature BIT STRING and bytes */ + pos += SetBitString(sigSz, 0, buff + pos); + XMEMCPY(buff + pos, sig, sigSz); + (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) { + 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; + } + /* Encode DER CRL into temporary buffer. */ + { + word32 pos = 0; +#ifdef WC_RSA_PSS + word32 oidSz = 0; + const byte* oid = NULL; +#endif + pos += SetSequence(totalContentLen, derTmp + pos); + XMEMCPY(derTmp + pos, tbs, tbsSz); + pos += tbsSz; +#ifdef WC_RSA_PSS + 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; + } + 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); + (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; + } + *inOutSz = pemSz; + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return WOLFSSL_SUCCESS; + } +#endif /* WOLFSSL_DER_TO_PEM */ + else { + return BAD_FUNC_ARG; + } +} #ifdef HAVE_CRL_UPDATE_CB /* Fill out CRL info structure, WOLFSSL_SUCCESS on ok */ @@ -1893,5 +2114,532 @@ int LoadCRL(WOLFSSL_CRL* crl, const char* path, int type, int monitor) } #endif /* !NO_FILESYSTEM && !NO_WOLFSSL_DIR */ +#ifndef NO_FILESYSTEM +/* Store CRL to a file in DER or PEM format. + * Returns WOLFSSL_SUCCESS on success, negative on failure. + * @param [in] crl CRL object. + * @param [in] path Path to the file to store the CRL. + * @param [in] type Format of encoding. Valid values: + * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. + * @return WOLFSSL_SUCCESS on success, or negative on failure. + */ +int StoreCRL(WOLFSSL_CRL* crl, const char* path, int type) +{ + XFILE fp = XBADFILE; + int ret = WOLFSSL_SUCCESS; + long sz = 0; + byte* mem = NULL; + + WOLFSSL_ENTER("StoreCRL"); + + if (crl == NULL || path == NULL) + return BAD_FUNC_ARG; + + /* Determine required size. */ + ret = BufferStoreCRL(crl, NULL, &sz, type); + if (ret != WOLFSSL_SUCCESS) { + return ret; + } + + /* Allocate temporary buffer. */ + mem = (byte*)XMALLOC((size_t)sz, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (mem == NULL) { + return MEMORY_E; + } + + /* Encode CRL into buffer. */ + ret = BufferStoreCRL(crl, mem, &sz, type); + if (ret == WOLFSSL_SUCCESS) { + /* Open destination file for writing. */ + fp = XFOPEN(path, "wb"); + if (fp == XBADFILE) { + ret = WOLFSSL_BAD_FILE; + } + else { + size_t wrote = XFWRITE(mem, 1, (size_t)sz, fp); + if (wrote != (size_t)sz) { + WOLFSSL_MSG("CRL file write failed"); + ret = FWRITE_ERROR; + } + XFCLOSE(fp); + } + } + + XFREE(mem, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} +#else +int StoreCRL(WOLFSSL_CRL* crl, const char* file, int type) +{ + (void)crl; + (void)file; + (void)type; + return NOT_COMPILED_IN; +} +#endif /* NO_FILESYSTEM */ + +#if defined(OPENSSL_EXTRA) +/* Create a new empty CRL object for generation. + * Version is set to 2 by default. Use wolfSSL_X509_CRL_set_version() to change it. + * lastUpdate set to current time, nextUpdate set to 500 days from now. + * Returns a new CRL or NULL on failure. + */ +WOLFSSL_X509_CRL* wolfSSL_X509_CRL_new(void) +{ + WOLFSSL_X509_CRL* crl; + CRL_Entry* entry; + WOLFSSL_ASN1_TIME asnTime; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_new"); + + crl = (WOLFSSL_X509_CRL*)XMALLOC(sizeof(WOLFSSL_X509_CRL), NULL, + DYNAMIC_TYPE_CRL); + if (crl == NULL) { + WOLFSSL_MSG("Memory allocation failed for CRL"); + return NULL; + } + + if (InitCRL(crl, NULL) < 0) { + WOLFSSL_MSG("Init CRL failed"); + XFREE(crl, NULL, DYNAMIC_TYPE_CRL); + return NULL; + } + + /* Allocate empty CRL entry for setting fields */ + entry = (CRL_Entry*)XMALLOC(sizeof(CRL_Entry), NULL, DYNAMIC_TYPE_CRL_ENTRY); + if (entry == NULL) { + WOLFSSL_MSG("Memory allocation failed for CRL entry"); + FreeCRL(crl, 1); + return NULL; + } + XMEMSET(entry, 0, sizeof(CRL_Entry)); + + if (wc_InitMutex(&entry->verifyMutex) != 0) { + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + + crl->crlList = entry; + + /* Set thisUpdate to current time */ + if (wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0) == NULL) { + WOLFSSL_MSG("Failed to get current time"); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + if (wolfSSL_X509_CRL_set_lastUpdate(crl, &asnTime) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Failed to set last update"); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + + /* Set next update date to 500 days from now, + * following convention from wc_InitCert() */ + if (wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 500, 0) == NULL) { + WOLFSSL_MSG("Failed to get next update time"); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + if (wolfSSL_X509_CRL_set_nextUpdate(crl, &asnTime) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Failed to set next update"); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + + /* Set default version to v2 (required for extensions) */ + entry->version = 2; + + return crl; +} + +#ifdef WOLFSSL_CERT_GEN +/* Add a revoked certificate entry to CRL. + * crl: target CRL + * serial: serial number of revoked cert + * serialSz: size of serial number + * revDate: revocation date (ASN.1 format without tag/length), or NULL for + * current time + * revDateFmt: date format (ASN_UTC_TIME or ASN_GENERALIZED_TIME), ignored if + * revDate is NULL + * Returns WOLFSSL_SUCCESS on success. + */ +int wolfSSL_X509_CRL_add_revoked(WOLFSSL_X509_CRL* crl, + const byte* serial, int serialSz, + const byte* revDate, byte revDateFmt) +{ + CRL_Entry* entry; + RevokedCert* rc; + RevokedCert* curr; + WOLFSSL_ASN1_TIME asnTime; + byte revDateBuf[MAX_DATE_SIZE]; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_add_revoked"); + + if (crl == NULL || serial == NULL || serialSz <= 0 || + serialSz > EXTERNAL_SERIAL_SIZE) { + return BAD_FUNC_ARG; + } + + entry = crl->crlList; + if (entry == NULL) { + return BAD_FUNC_ARG; + } + + /* If revDate is NULL, use current time */ + if (revDate == NULL) { + XMEMSET(&asnTime, 0, sizeof(asnTime)); + if (wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0) == NULL) { + WOLFSSL_MSG("Failed to get current time"); + return BAD_STATE_E; + } + XMEMCPY(revDateBuf, asnTime.data, asnTime.length); + revDate = revDateBuf; + revDateFmt = (byte)asnTime.type; + } + + rc = (RevokedCert*)XMALLOC(sizeof(RevokedCert), crl->heap, + DYNAMIC_TYPE_REVOKED); + if (rc == NULL) { + return MEMORY_E; + } + XMEMSET(rc, 0, sizeof(RevokedCert)); + + XMEMCPY(rc->serialNumber, serial, (size_t)serialSz); + rc->serialSz = serialSz; + XMEMCPY(rc->revDate, revDate, MAX_DATE_SIZE); + rc->revDateFormat = revDateFmt; + rc->next = NULL; + + /* Add to end of list */ + if (entry->certs == NULL) { + entry->certs = rc; + } + else { + for (curr = entry->certs; curr->next != NULL; curr = curr->next) + ; + curr->next = rc; + } + entry->totalCerts++; + + WOLFSSL_LEAVE("wolfSSL_X509_CRL_add_revoked", WOLFSSL_SUCCESS); + return WOLFSSL_SUCCESS; +} + +/* Add a revoked certificate entry to CRL by parsing a certificate buffer. + * crl: target CRL + * certBuf: DER-encoded certificate buffer + * certSz: size of certificate buffer + * revDate: revocation date (ASN.1 format without tag/length), or NULL for + * current time + * revDateFmt: date format (ASN_UTC_TIME or ASN_GENERALIZED_TIME), ignored if + * revDate is NULL + * Returns WOLFSSL_SUCCESS on success. + */ +int wolfSSL_X509_CRL_add_revoked_cert(WOLFSSL_X509_CRL* crl, + const byte* certBuf, int certSz, + const byte* revDate, byte revDateFmt) +{ + int ret; + DecodedCert cert[1]; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_add_revoked_cert"); + + if (crl == NULL || certBuf == NULL || certSz <= 0) { + return BAD_FUNC_ARG; + } + + /* Initialize and parse the certificate */ + InitDecodedCert(cert, certBuf, (word32)certSz, NULL); + ret = ParseCertRelative(cert, CERT_TYPE, NO_VERIFY, NULL, NULL); + if (ret != 0) { + WOLFSSL_MSG("Failed to parse certificate"); + FreeDecodedCert(cert); + return ret; + } + + /* Add the revoked certificate entry */ + ret = wolfSSL_X509_CRL_add_revoked(crl, cert->serial, cert->serialSz, + revDate, revDateFmt); + + FreeDecodedCert(cert); + + return ret; +} + +/* Sign a CRL with a private key, rebuilding TBS from fields. + * crl: CRL with fields set via setter functions + * pkey: private key for signing + * md: digest algorithm (e.g., EVP_sha256()) + * Note: only one entry is supported in the CRL list. + * Returns WOLFSSL_SUCCESS on success. + */ +int wolfSSL_X509_CRL_sign(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* pkey, + const WOLFSSL_EVP_MD* md) +{ + int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); + CRL_Entry* entry; + byte* issuerDer = NULL; + int issuerSz = 0; + int sigType; + int tbsSz; + int totalSz; + byte* buf = NULL; + word32 bufSz = 4096; /* Working buffer for TBS + signature */ + RsaKey* rsaKey = NULL; + ecc_key* eccKey = NULL; + WC_RNG rng; + int rngInit = 0; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_sign"); + + if (crl == NULL || pkey == NULL || md == NULL) { + return BAD_FUNC_ARG; + } + + /* Fetch only the first entry in the CRL list */ + entry = crl->crlList; + if (entry == NULL) { + WOLFSSL_MSG("CRL has no entry"); + return BAD_FUNC_ARG; + } + + /* Determine signature type from digest and key type */ +#ifndef NO_RSA + if (pkey->type == WC_EVP_PKEY_RSA) { + if (md == wolfSSL_EVP_sha256()) { + sigType = CTC_SHA256wRSA; + } + #ifdef WOLFSSL_SHA384 + else if (md == wolfSSL_EVP_sha384()) { + sigType = CTC_SHA384wRSA; + } + #endif + #ifdef WOLFSSL_SHA512 + else if (md == wolfSSL_EVP_sha512()) { + sigType = CTC_SHA512wRSA; + } + #endif + else if (md == wolfSSL_EVP_sha1()) { + sigType = CTC_SHAwRSA; + } + else { + WOLFSSL_MSG("Unsupported digest for RSA"); + return BAD_FUNC_ARG; + } + rsaKey = (RsaKey*)pkey->rsa->internal; + } + else +#endif +#ifdef HAVE_ECC + if (pkey->type == WC_EVP_PKEY_EC) { + if (md == wolfSSL_EVP_sha256()) { + sigType = CTC_SHA256wECDSA; + } + #ifdef WOLFSSL_SHA384 + else if (md == wolfSSL_EVP_sha384()) { + sigType = CTC_SHA384wECDSA; + } + #endif + #ifdef WOLFSSL_SHA512 + else if (md == wolfSSL_EVP_sha512()) { + sigType = CTC_SHA512wECDSA; + } + #endif + else if (md == wolfSSL_EVP_sha1()) { + sigType = CTC_SHAwECDSA; + } + else { + WOLFSSL_MSG("Unsupported digest for ECDSA"); + return BAD_FUNC_ARG; + } + eccKey = (ecc_key*)pkey->ecc->internal; + } + else +#endif + { + WOLFSSL_MSG("Unsupported key type"); + return BAD_FUNC_ARG; + } + + /* Get issuer name DER */ + if (entry->issuer != NULL) { + issuerSz = wolfSSL_i2d_X509_NAME(entry->issuer, &issuerDer); + if (issuerSz <= 0) { + WOLFSSL_MSG("Failed to encode issuer name"); + return WOLFSSL_FAILURE; + } + } + else { + WOLFSSL_MSG("CRL has no issuer set"); + return BAD_FUNC_ARG; + } + + /* Copy dates from ASN1 time structures to raw fields if needed */ + if (entry->lastDateAsn1.length > 0 && entry->lastDateFormat == 0) { + XMEMCPY(entry->lastDate, entry->lastDateAsn1.data, + (size_t)entry->lastDateAsn1.length); + entry->lastDateFormat = (byte)entry->lastDateAsn1.type; + } + if (entry->nextDateAsn1.length > 0 && entry->nextDateFormat == 0) { + XMEMCPY(entry->nextDate, entry->nextDateAsn1.data, + (size_t)entry->nextDateAsn1.length); + entry->nextDateFormat = (byte)entry->nextDateAsn1.type; + } + + /* Verify we have valid dates */ + if (entry->lastDateFormat == 0) { + WOLFSSL_MSG("CRL has no lastUpdate date set"); + XFREE(issuerDer, NULL, DYNAMIC_TYPE_OPENSSL); + return BAD_FUNC_ARG; + } + + /* Initialize RNG */ + if (wc_InitRng(&rng) != 0) { + WOLFSSL_MSG("RNG init failed"); + XFREE(issuerDer, NULL, DYNAMIC_TYPE_OPENSSL); + return WOLFSSL_FAILURE; + } + rngInit = 1; + + /* Allocate working buffer */ + buf = (byte*)XMALLOC(bufSz, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (buf == NULL) { + ret = MEMORY_E; + goto cleanup; + } + + /* Build to-be-signed (TBS) portion of the CRL buffer. + * Note that we pass the fields rather than the CRL_entry struct so + * wolfcrypt need not know about the openSSL-compatible CRL_entry struct. */ + tbsSz = wc_MakeCRL_ex(issuerDer, (word32)issuerSz, + entry->lastDate, entry->lastDateFormat, + entry->nextDate, entry->nextDateFormat, + entry->certs, entry->totalCerts, + entry->crlNumberSet ? (const byte*)entry->crlNumber : NULL, + entry->crlNumberSet ? CRL_MAX_NUM_SZ : 0, + sigType, entry->version, + buf, bufSz); + if (tbsSz < 0) { + WOLFSSL_MSG("wc_MakeCRL_ex failed"); + ret = tbsSz; + goto cleanup; + } + + /* Sign and complete CRL. Note that the output buffer is the same as the input buffer. + * The signature is added to the end of the buffer. */ + totalSz = wc_SignCRL_ex(buf, tbsSz, sigType, buf, bufSz, + rsaKey, eccKey, &rng); + if (totalSz < 0) { + WOLFSSL_MSG("wc_SignCRL_ex failed"); + ret = totalSz; + goto cleanup; + } + + /* Update CRL entry with new toBeSigned and signature */ + /* Free old buffers if present */ + if (entry->toBeSigned != NULL) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + } + if (entry->signature != NULL) { + XFREE(entry->signature, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + } + + /* Extract TBS and signature from the complete CRL buffer. + * This bookkeeping ensures the CRL_entry is updated with the results + * from the complete CRL buffer. + * After AddSignature, the buffer layout is: + * [outer SEQUENCE header][TBS][AlgorithmIdentifier][BIT STRING signature] + */ + { + word32 idx = 0; + int len; + word32 tbsStart, tbsLen; + int sigLen; + + /* Parse outer SEQUENCE */ + if (GetSequence(buf, &idx, &len, (word32)totalSz) < 0) { + ret = ASN_PARSE_E; + goto cleanup; + } + + /* TBS starts here */ + tbsStart = idx; + + /* Parse TBS SEQUENCE to get its length */ + if (GetSequence(buf, &idx, &len, (word32)totalSz) < 0) { + ret = ASN_PARSE_E; + goto cleanup; + } + tbsLen = idx + (word32)len - tbsStart; + idx = tbsStart + tbsLen; /* Move past TBS */ + + /* Allocate and copy TBS */ + entry->toBeSigned = (byte*)XMALLOC(tbsLen, crl->heap, + DYNAMIC_TYPE_CRL_ENTRY); + if (entry->toBeSigned == NULL) { + ret = MEMORY_E; + goto cleanup; + } + XMEMCPY(entry->toBeSigned, buf + tbsStart, tbsLen); + entry->tbsSz = tbsLen; + + /* Skip AlgorithmIdentifier */ + if (GetAlgoId(buf, &idx, (word32*)&len, oidSigType, (word32)totalSz) < 0) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + ret = ASN_PARSE_E; + goto cleanup; + } + + /* Get BIT STRING */ + if (GetASNHeader(buf, ASN_BIT_STRING, &idx, &sigLen, (word32)totalSz) < 0) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + ret = ASN_PARSE_E; + goto cleanup; + } + + /* Skip unused bits byte */ + if (buf[idx] != 0) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + ret = ASN_PARSE_E; + goto cleanup; + } + idx++; + sigLen--; + + entry->signature = (byte*)XMALLOC((word32)sigLen, crl->heap, + DYNAMIC_TYPE_CRL_ENTRY); + if (entry->signature == NULL) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + ret = MEMORY_E; + goto cleanup; + } + XMEMCPY(entry->signature, buf + idx, (size_t)sigLen); + entry->signatureSz = (word32)sigLen; + entry->signatureOID = (word32)sigType; + } + + /* Mark the CRL as verified/signed for future reference. */ + entry->verified = 1; + ret = WOLFSSL_SUCCESS; + +cleanup: + if (buf != NULL) + XFREE(buf, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (issuerDer != NULL) + XFREE(issuerDer, NULL, DYNAMIC_TYPE_OPENSSL); + if (rngInit) + wc_FreeRng(&rng); + + return ret; +} +#endif /* WOLFSSL_CERT_GEN */ + +#endif /* OPENSSL_EXTRA */ + #endif /* HAVE_CRL */ #endif /* !WOLFCRYPT_ONLY */ diff --git a/src/x509.c b/src/x509.c index 7594b0dae8..2ab09236f2 100644 --- a/src/x509.c +++ b/src/x509.c @@ -8718,6 +8718,30 @@ WOLFSSL_X509_NAME* wolfSSL_X509_CRL_get_issuer_name(WOLFSSL_X509_CRL* crl) return crl->crlList->issuer; } +/* Set issuer name of CRL + * return WOLFSSL_SUCCESS on success + * return WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_CRL_set_issuer_name(WOLFSSL_X509_CRL* crl, WOLFSSL_X509_NAME* name) +{ + WOLFSSL_X509_NAME* newName; + + if (crl == NULL || crl->crlList == NULL || name == NULL) + return WOLFSSL_FAILURE; + + newName = wolfSSL_X509_NAME_dup(name); + if (newName == NULL) + return WOLFSSL_FAILURE; + + if (crl->crlList->issuer != NULL) { + FreeX509Name(crl->crlList->issuer); + XFREE(crl->crlList->issuer, crl->heap, DYNAMIC_TYPE_X509); + } + crl->crlList->issuer = newName; + + return WOLFSSL_SUCCESS; +} + /* Retrieve version from CRL * return version on success * return 0 on failure @@ -8730,6 +8754,20 @@ int wolfSSL_X509_CRL_version(WOLFSSL_X509_CRL* crl) return crl->crlList->version; } +/* Set version of CRL + * RFC 5280: 5.2.1. CRL version is 0 for v1, 1 for v2, etc + * return WOLFSSL_SUCCESS on success + * return WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_CRL_set_version(WOLFSSL_X509_CRL* crl, int version) +{ + if (crl == NULL || crl->crlList == NULL) + return WOLFSSL_FAILURE; + + crl->crlList->version = version; + return WOLFSSL_SUCCESS; +} + /* Retrieve sig OID from CRL * return OID on success * return 0 on failure @@ -8742,6 +8780,19 @@ int wolfSSL_X509_CRL_get_signature_type(WOLFSSL_X509_CRL* crl) return crl->crlList->signatureOID; } +/* Set signature type of CRL + * return WOLFSSL_SUCCESS on success + * return WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_CRL_set_signature_type(WOLFSSL_X509_CRL* crl, int signatureType) +{ + if (crl == NULL || crl->crlList == NULL) + return WOLFSSL_FAILURE; + + crl->crlList->signatureOID = signatureType; + return WOLFSSL_SUCCESS; +} + /* Retrieve sig NID from CRL * return NID on success * return 0 on failure @@ -8754,6 +8805,19 @@ int wolfSSL_X509_CRL_get_signature_nid(const WOLFSSL_X509_CRL* crl) return oid2nid(crl->crlList->signatureOID, oidSigType); } +/* Set signature NID of CRL + * return OID 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; + + crl->crlList->signatureOID = nid2oid(nid, oidSigType); + return WOLFSSL_SUCCESS; +} + /* Retrieve signature from CRL * return WOLFSSL_SUCCESS on success and negative values on failure */ @@ -8780,6 +8844,23 @@ int wolfSSL_X509_CRL_get_signature(WOLFSSL_X509_CRL* crl, return WOLFSSL_SUCCESS; } +int wolfSSL_X509_CRL_set_signature(WOLFSSL_X509_CRL* crl, + unsigned char* buf, int bufSz) +{ + if (crl == NULL || crl->crlList == NULL || + crl->crlList->signature == NULL) + return BAD_FUNC_ARG; + + if (bufSz < (int)sizeof(crl->crlList->signature)) { + WOLFSSL_MSG("Signature buffer too small"); + return BAD_FUNC_ARG; + } + + XMEMCPY(crl->crlList->signature, buf, bufSz); + crl->crlList->signatureSz = bufSz; + return WOLFSSL_SUCCESS; +} + /* Retrieve serial number from RevokedCert * return WOLFSSL_SUCCESS on success and negative values on failure */ @@ -9261,8 +9342,16 @@ WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_lastUpdate(WOLFSSL_X509_CRL* crl) (crl->crlList->lastDateAsn1.data[0] != 0)) { return &crl->crlList->lastDateAsn1; } - else - return NULL; + return NULL; +} + +int wolfSSL_X509_CRL_set_lastUpdate(WOLFSSL_X509_CRL* crl, WOLFSSL_ASN1_TIME* time) +{ + if (crl != NULL && crl->crlList != NULL && time != NULL) { + crl->crlList->lastDateAsn1 = *time; + return WOLFSSL_SUCCESS; + } + return WOLFSSL_FAILURE; } WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_nextUpdate(WOLFSSL_X509_CRL* crl) @@ -9271,8 +9360,16 @@ WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_nextUpdate(WOLFSSL_X509_CRL* crl) (crl->crlList->nextDateAsn1.data[0] != 0)) { return &crl->crlList->nextDateAsn1; } - else - return NULL; + return NULL; +} + +int wolfSSL_X509_CRL_set_nextUpdate(WOLFSSL_X509_CRL* crl, WOLFSSL_ASN1_TIME* time) +{ + if (crl != NULL && crl->crlList != NULL && time != NULL) { + crl->crlList->nextDateAsn1 = *time; + return WOLFSSL_SUCCESS; + } + return WOLFSSL_FAILURE; } #ifndef NO_WOLFSSL_STUB @@ -9284,6 +9381,70 @@ int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* key) return 0; } #endif + +/* Encode CRL to DER format in memory. + * + * If *out is NULL, allocates memory and returns it via *out. + * If *out is not NULL, writes DER data starting at *out. + * + * @param crl CRL to encode + * @param out Pointer to output buffer pointer + * @return Size of DER encoding on success, negative on failure + */ +int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, unsigned char** out) +{ + int ret; + long derSz = 0; + byte* der = NULL; + int alloced = 0; + + WOLFSSL_ENTER("wolfSSL_i2d_X509_CRL"); + + if (crl == NULL) { + return BAD_FUNC_ARG; + } + + /* Get required size */ + ret = BufferStoreCRL(crl, NULL, &derSz, WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS || derSz <= 0) { + WOLFSSL_MSG("BufferStoreCRL failed to get size"); + return WOLFSSL_FAILURE; + } + + if (out == NULL) { + /* Just return size */ + return (int)derSz; + } + + if (*out == NULL) { + /* Allocate output buffer */ + der = (byte*)XMALLOC((size_t)derSz, NULL, DYNAMIC_TYPE_OPENSSL); + if (der == NULL) { + WOLFSSL_MSG("Memory allocation failed"); + return MEMORY_E; + } + alloced = 1; + } + else { + der = *out; + } + + /* Encode CRL to DER */ + ret = BufferStoreCRL(crl, der, &derSz, WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("BufferStoreCRL failed to encode"); + if (alloced) { + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + } + return WOLFSSL_FAILURE; + } + + if (alloced) { + *out = der; + } + + return (int)derSz; +} #endif /* HAVE_CRL && OPENSSL_EXTRA */ #ifdef OPENSSL_EXTRA @@ -12327,6 +12488,19 @@ WOLFSSL_X509_CRL* wolfSSL_PEM_read_X509_CRL(XFILE fp, return (WOLFSSL_X509_CRL* )wolfSSL_PEM_read_X509_ex(fp, (void **)crl, cb, u, CRL_TYPE); } + +/* Convert CRL to 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) +{ + int ret; + WOLFSSL_ENTER("wolfSSL_write_X509_CRL"); + ret = StoreCRL(crl, path, type); + WOLFSSL_LEAVE("wolfSSL_write_X509_CRL", ret); + return ret; +} #endif #ifdef WOLFSSL_CERT_GEN diff --git a/tests/api.c b/tests/api.c index 4f257c5dc6..9a2ca8abea 100644 --- a/tests/api.c +++ b/tests/api.c @@ -19837,10 +19837,10 @@ static int test_sk_X509(void) return EXPECT_RESULT(); } -static int test_sk_X509_CRL(void) +static int test_sk_X509_CRL_decode(void) { EXPECT_DECLS; -#if defined(OPENSSL_ALL) && !defined(NO_CERTS) && defined(HAVE_CRL) && \ +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && defined(HAVE_CRL) && \ !defined(NO_RSA) X509_CRL* crl = NULL; XFILE fp = XBADFILE; @@ -20032,6 +20032,222 @@ static int test_sk_X509_CRL(void) return EXPECT_RESULT(); } +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && \ + defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_CERT_GEN) +/* Helper function to create, sign, and write a CRL */ +static int generate_crl_test(const char* keyFile, const char* certFile, + const char* derFile, const char* pemFile) +{ + EXPECT_DECLS; + X509_NAME* issuer = NULL; + X509_NAME* decodedIssuer = NULL; + WOLFSSL_X509* cert = NULL; + WOLFSSL_ASN1_TIME asnTime = {0}; + XFILE fp = XBADFILE; + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_X509_CRL* decodedCrl = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + int i = 0; + int revokedCount = 0; + const byte serialsToRevoke[] = { 0x02, 0x03, 0x04 }; + const int numSerials = (int)(sizeof(serialsToRevoke) / sizeof(serialsToRevoke[0])); + static const char* certToRevokePath = "./certs/server-cert.pem"; + WOLFSSL_X509* certToRevoke = NULL; + + /* Load certificate to get issuer name (CRL issuer = cert subject) */ + ExpectTrue((fp = XFOPEN(certFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(cert = PEM_read_X509(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Create a new empty CRL */ + ExpectNotNull(crl = wolfSSL_X509_CRL_new()); + ExpectIntEQ(wolfSSL_X509_CRL_version(crl), 2); + + /* Set issuer name, must match certificate subject for verification */ + ExpectNotNull(issuer = X509_get_subject_name(cert)); + ExpectIntEQ(wolfSSL_X509_CRL_set_issuer_name(crl, issuer), WOLFSSL_SUCCESS); + + /* Set thisUpdate to current time */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_lastUpdate(crl, &asnTime), WOLFSSL_SUCCESS); + + /* Set nextUpdate to 30 days from now */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 30, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_nextUpdate(crl, &asnTime), WOLFSSL_SUCCESS); + + /* Add revoked certificates based on serial numbers */ + for (i = 0; i < numSerials; i++) { + byte revDate[MAX_DATE_SIZE]; + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0)); + XMEMCPY(revDate, asnTime.data, asnTime.length); + + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, &serialsToRevoke[i], + sizeof(serialsToRevoke[i]), revDate, (byte)asnTime.type), + WOLFSSL_SUCCESS); + } + + /* Add a revoked certificate entry to CRL by parsing a different certificate buffer */ + ExpectTrue((fp = XFOPEN(certToRevokePath, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(certToRevoke = PEM_read_X509(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + { + const byte* certDer = NULL; + int certDerSz = 0; + + ExpectNotNull(certDer = wolfSSL_X509_get_der(certToRevoke, &certDerSz)); + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked_cert(crl, certDer, certDerSz, + NULL, 0), WOLFSSL_SUCCESS); + } + wolfSSL_X509_free(certToRevoke); + certToRevoke = NULL; + + /* Count revoked certificates - should be 'numSerials' + 1 */ + revokedCount = 0; + if (EXPECT_SUCCESS() && crl != NULL && crl->crlList != NULL) { + RevokedCert* rev = crl->crlList->certs; + while (rev != NULL) { + revokedCount++; + rev = rev->next; + } + } + ExpectIntEQ(revokedCount, numSerials + 1); + + /* Load private key for signing */ + ExpectTrue((fp = XFOPEN(keyFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Sign the CRL */ + ExpectIntEQ(wolfSSL_X509_CRL_sign(crl, pkey, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + + /* Encode CRL to DER */ + ExpectIntEQ(wolfSSL_write_X509_CRL(crl, derFile, WOLFSSL_FILETYPE_ASN1), + WOLFSSL_SUCCESS); + + /* Encode CRL to PEM */ + ExpectIntEQ(wolfSSL_write_X509_CRL(crl, pemFile, WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + + /* ===== Validate encoded DER CRL by decoding and checking contents ===== */ + ExpectTrue((fp = XFOPEN(derFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(decodedCrl = d2i_X509_CRL_fp(fp, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Verify CRL version is v2 */ + ExpectIntEQ(wolfSSL_X509_CRL_version(decodedCrl), 2); + + /* Verify issuer name matches */ + ExpectNotNull(decodedIssuer = wolfSSL_X509_CRL_get_issuer_name(decodedCrl)); + ExpectIntEQ(X509_NAME_cmp(issuer, decodedIssuer), 0); + + /* Verify signature type is SHA256 with RSA or ECDSA */ + ExpectIntNE(wolfSSL_X509_CRL_get_signature_type(decodedCrl), 0); + + /* Verify lastUpdate and nextUpdate are set */ + ExpectNotNull(wolfSSL_X509_CRL_get_lastUpdate(decodedCrl)); + ExpectNotNull(wolfSSL_X509_CRL_get_nextUpdate(decodedCrl)); + + /* Count revoked certificates - should be 'numSerials' */ + revokedCount = 0; + if (EXPECT_SUCCESS() && decodedCrl != NULL && decodedCrl->crlList != NULL) { + RevokedCert* rev = decodedCrl->crlList->certs; + while (rev != NULL) { + revokedCount++; + rev = rev->next; + } + } + ExpectIntEQ(revokedCount, numSerials + 1); + + wolfSSL_X509_CRL_free(decodedCrl); + decodedCrl = NULL; + + /* ===== Validate encoded PEM CRL by decoding and checking contents ===== */ + ExpectTrue((fp = XFOPEN(pemFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(decodedCrl = (X509_CRL*)PEM_read_X509_CRL(fp, NULL, + NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Verify CRL version is v2 */ + ExpectIntEQ(wolfSSL_X509_CRL_version(decodedCrl), 2); + + /* Verify issuer name matches */ + ExpectNotNull(decodedIssuer = wolfSSL_X509_CRL_get_issuer_name(decodedCrl)); + ExpectIntEQ(X509_NAME_cmp(issuer, decodedIssuer), 0); + + /* Count revoked certificates from PEM - should also be 'numSerials' */ + revokedCount = 0; + if (EXPECT_SUCCESS() && decodedCrl != NULL && decodedCrl->crlList != NULL) { + RevokedCert* rev = decodedCrl->crlList->certs; + while (rev != NULL) { + revokedCount++; + rev = rev->next; + } + } + ExpectIntEQ(revokedCount, numSerials + 1); + + wolfSSL_X509_CRL_free(decodedCrl); + + wolfSSL_EVP_PKEY_free(pkey); + wolfSSL_X509_CRL_free(crl); + wolfSSL_X509_free(cert); + + return EXPECT_RESULT(); +} +#endif + +static int test_sk_X509_CRL_encode(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && \ + defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_CERT_GEN) +#ifndef NO_RSA + static const char* crlRsaPemFile = "./certs/crl/crlRsaOut.pem"; + static const char* crlRsaDerFile = "./certs/crl/crlRsaOut.der"; + static const char* testRsaKeyFile = "./certs/ca-key.pem"; + /* Use ca-cert.pem to match ca-key.pem for proper CRL verification */ + static const char* testRsaCertFile = "./certs/ca-cert.pem"; +#endif +#ifdef HAVE_ECC + static const char* crlEccPemFile = "./certs/crl/crlEccOut.pem"; + static const char* crlEccDerFile = "./certs/crl/crlEccOut.der"; + static const char* testEccKeyFile = "./certs/ecc-key.pem"; + /* Use server-ecc.pem to match ecc-key.pem for proper CRL verification */ + static const char* testEccCertFile = "./certs/server-ecc.pem"; +#endif + +#ifndef NO_RSA + /* Generate RSA-signed CRL (PEM and DER) */ + ExpectIntEQ(generate_crl_test(testRsaKeyFile, testRsaCertFile, + crlRsaDerFile, crlRsaPemFile), TEST_SUCCESS); +#endif + +#ifdef HAVE_ECC + /* Generate ECC-signed CRL (PEM and DER) */ + ExpectIntEQ(generate_crl_test(testEccKeyFile, testEccCertFile, + crlEccDerFile, crlEccPemFile), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + static int test_X509_REQ(void) { EXPECT_DECLS; @@ -31316,7 +31532,8 @@ TEST_CASE testCases[] = { /* OpenSSL sk_X509 API test */ TEST_DECL(test_sk_X509), /* OpenSSL sk_X509_CRL API test */ - TEST_DECL(test_sk_X509_CRL), + TEST_DECL(test_sk_X509_CRL_decode), + TEST_DECL(test_sk_X509_CRL_encode), /* OpenSSL X509 REQ API test */ TEST_DECL(test_wolfSSL_d2i_X509_REQ), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 31466ca846..e32a9d7165 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -631,6 +631,7 @@ static word32 SizeASNLength(word32 length) #define ASNIntMSBSet(asn, data_a, i) \ (((asn)[i].tag == ASN_INTEGER) && \ ((data_a)[i].data.buffer.data != NULL && \ + ((data_a)[i].data.buffer.length > 0) && \ ((data_a)[i].data.buffer.data[0] & 0x80) == 0x80)) @@ -3131,11 +3132,10 @@ const char* GetSigName(int oid) { #if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS7) || \ - defined(OPENSSL_EXTRA) + defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN) #if !defined(NO_DSA) || defined(HAVE_ECC) || !defined(NO_CERTS) || \ - (!defined(NO_RSA) && \ - (defined(WOLFSSL_CERT_GEN) || \ - ((defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))))) + defined(WOLFSSL_CERT_GEN) || \ + (!defined(NO_RSA) && (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))) /* Set the DER/BER encoding of the ASN.1 INTEGER header. * * When output is NULL, calculate the header length only. @@ -41397,6 +41397,401 @@ int ParseCRL(RevokedCert* rcert, DecodedCRL* dcrl, const byte* buff, word32 sz, #endif /* WOLFSSL_ASN_TEMPLATE */ } +#ifdef WOLFSSL_CERT_GEN +/* Encode a date as ASN.1 (UTC or GeneralizedTime). + * Returns length written to output (including tag and length bytes). + * If output is NULL, just returns the required size. + */ +static word32 EncodeCrlDate(byte* output, const byte* date, byte format) +{ + word32 idx = 0; + word32 dateLen; + + /* Determine date length based on format */ + if (format == ASN_UTC_TIME) { + dateLen = ASN_UTC_TIME_SIZE - 1; /* exclude null terminator */ + } + else if (format == ASN_GENERALIZED_TIME) { + dateLen = ASN_GENERALIZED_TIME_SIZE - 1; + } + else { + return 0; /* unsupported format */ + } + + if (output != NULL) { + output[idx] = format; + } + idx++; + + if (output != NULL) { + idx += SetLength(dateLen, output + idx); + XMEMCPY(output + idx, date, dateLen); + } + else { + idx += SetLength(dateLen, NULL); + } + idx += dateLen; + + return idx; +} + +/* Encode a serial number as ASN.1 INTEGER. + * Similar to SetSerialNumber but always available. + */ +static int EncodeCrlSerial(const byte* sn, word32 snSz, byte* output, + word32 outputSz) +{ + int i; + int snSzInt = (int)snSz; + const byte* snPtr = sn; + + if (sn == NULL || snSzInt < 0) + return BAD_FUNC_ARG; + + /* remove leading zeros */ + while (snSzInt > 0 && snPtr[0] == 0) { + snSzInt--; + snPtr++; + } + /* Serial numbers must be a positive value (and not zero) */ + if (snSzInt == 0) { + return BAD_FUNC_ARG; + } + + i = SetASNInt(snSzInt, snPtr[0], NULL); + /* sanity check number of bytes to copy */ + if (snSzInt > (int)outputSz - i || snSzInt <= 0) { + return BUFFER_E; + } + + if (output != NULL) { + /* write out ASN.1 Integer */ + (void)SetASNInt(snSzInt, snPtr[0], output); + XMEMCPY(output + i, snPtr, (size_t)snSzInt); + } + + return i + snSzInt; +} + +/* Encode a single revoked certificate entry. + * Returns length written to output. + */ +static word32 EncodeRevokedCert(byte* output, const RevokedCert* rc) +{ + word32 idx = 0; + word32 snSz, dateSz, seqSz; + byte snBuf[MAX_SN_SZ]; + byte dateBuf[MAX_DATE_SIZE + 2]; /* tag + length + data */ + byte seqBuf[MAX_SEQ_SZ]; + + /* Encode serial number */ + snSz = (word32)EncodeCrlSerial(rc->serialNumber, (word32)rc->serialSz, + snBuf, sizeof(snBuf)); + if ((int)snSz < 0) + return 0; + + /* Encode revocation date */ + dateSz = EncodeCrlDate(dateBuf, rc->revDate, rc->revDateFormat); + if (dateSz == 0) + return 0; + + /* Wrap in SEQUENCE */ + seqSz = SetSequence(snSz + dateSz, seqBuf); + + if (output != NULL) { + XMEMCPY(output + idx, seqBuf, seqSz); + idx += seqSz; + XMEMCPY(output + idx, snBuf, snSz); + idx += snSz; + XMEMCPY(output + idx, dateBuf, dateSz); + idx += dateSz; + } + else { + idx = seqSz + snSz + dateSz; + } + + return idx; +} + +/* Encode the CRL Number extension. + * Returns length written to output. + */ +static word32 EncodeCrlNumberExt(byte* output, const byte* crlNum, + word32 crlNumSz) +{ + word32 idx = 0; + word32 oidSz, intSz, octetSz, seqSz; + byte seqBuf[MAX_SEQ_SZ]; + byte octetBuf[MAX_OCTET_STR_SZ]; + byte intBuf[MAX_SN_SZ]; + + /* CRL Number OID: 2.5.29.20 */ + static const byte crlNumOid[] = { 0x06, 0x03, 0x55, 0x1d, 0x14 }; + oidSz = sizeof(crlNumOid); + + /* Encode the INTEGER for CRL number */ + intSz = (word32)EncodeCrlSerial(crlNum, crlNumSz, intBuf, sizeof(intBuf)); + if ((int)intSz < 0) + return 0; + + /* Wrap INTEGER in OCTET STRING */ + octetSz = SetOctetString(intSz, octetBuf); + + /* Wrap in extension SEQUENCE */ + seqSz = SetSequence(oidSz + octetSz + intSz, seqBuf); + + if (output != NULL) { + XMEMCPY(output + idx, seqBuf, seqSz); + idx += seqSz; + XMEMCPY(output + idx, crlNumOid, oidSz); + idx += oidSz; + XMEMCPY(output + idx, octetBuf, octetSz); + idx += octetSz; + XMEMCPY(output + idx, intBuf, intSz); + idx += intSz; + } + else { + idx = seqSz + oidSz + octetSz + intSz; + } + + return idx; +} + +/* Build CRL TBSCertList from fields. + * issuerDer: DER-encoded issuer Name + * issuerSz: size of issuer DER + * lastDate/lastDateFmt: thisUpdate time and format + * nextDate/nextDateFmt: nextUpdate time and format + * certs: linked list of revoked certificates (may be NULL) + * crlNumber/crlNumberSz: CRL number extension data (may be NULL) + * sigType: signature algorithm type (e.g., CTC_SHA256wRSA) + * version: CRL version (1 or 2; 2 required for extensions) + * output: buffer to write TBS (NULL to calculate size) + * outputSz: size of output buffer + * + * Returns: size of TBS on success, negative error code on failure + */ +int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz, + const byte* lastDate, byte lastDateFmt, + const byte* nextDate, byte nextDateFmt, + RevokedCert* certs, int totalCerts, + const byte* crlNumber, word32 crlNumberSz, + int sigType, int version, + byte* output, word32 outputSz) +{ + word32 idx = 0; + word32 tbsContentSz = 0; + word32 versionSz = 0, algoSz = 0, lastDateSz = 0, nextDateSz = 0; + word32 revokedSz = 0, extSz = 0, extSeqSz = 0, extCtxSz = 0; + word32 tbsSeqSz; + byte tbsSeqBuf[MAX_SEQ_SZ]; + byte versionBuf[MAX_VERSION_SZ]; + byte algoBuf[MAX_ALGO_SZ]; + byte lastDateBuf[MAX_DATE_SIZE + 2]; + byte nextDateBuf[MAX_DATE_SIZE + 2]; + byte revokedSeqBuf[MAX_SEQ_SZ]; + byte extSeqBuf[MAX_SEQ_SZ]; + byte extCtxBuf[MAX_SEQ_SZ + 1]; /* context tag + length */ + RevokedCert* rc; + int i; + + (void)totalCerts; + + if (issuerDer == NULL || issuerSz == 0 || lastDate == NULL) + return BAD_FUNC_ARG; + + /* Version: only include if v2 (version = 2 means value 1 in ASN.1) */ + if (version >= 2) { + versionSz = (word32)SetMyVersion(version - 1, versionBuf, FALSE); + } + + /* Signature AlgorithmIdentifier */ + algoSz = SetAlgoID(sigType, algoBuf, oidSigType, 0); + if (algoSz == 0) + return ALGO_ID_E; + + /* thisUpdate */ + lastDateSz = EncodeCrlDate(lastDateBuf, lastDate, lastDateFmt); + if (lastDateSz == 0) + return ASN_DATE_SZ_E; + + /* nextUpdate (optional) */ + if (nextDate != NULL && nextDateFmt != 0) { + nextDateSz = EncodeCrlDate(nextDateBuf, nextDate, nextDateFmt); + } + + /* revokedCertificates (optional) */ + if (certs != NULL) { + word32 contentSz = 0; + /* First pass: calculate size */ + for (rc = certs; rc != NULL; rc = rc->next) { + word32 entrySz = EncodeRevokedCert(NULL, rc); + if (entrySz == 0) + return ASN_PARSE_E; + contentSz += entrySz; + } + revokedSz = SetSequence(contentSz, revokedSeqBuf) + contentSz; + } + + /* crlExtensions (optional) - CRL Number */ + if (crlNumber != NULL && crlNumberSz > 0 && version >= 2) { + word32 crlNumExtSz = EncodeCrlNumberExt(NULL, crlNumber, crlNumberSz); + if (crlNumExtSz > 0) { + extSeqSz = SetSequence(crlNumExtSz, extSeqBuf); + /* Context tag [0] EXPLICIT */ + extCtxBuf[0] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0); + extCtxSz = 1 + SetLength(extSeqSz + crlNumExtSz, extCtxBuf + 1); + extSz = extCtxSz + extSeqSz + crlNumExtSz; + } + } + + /* Calculate total TBS content size */ + tbsContentSz = versionSz + algoSz + issuerSz + lastDateSz + nextDateSz + + revokedSz + extSz; + + /* TBS SEQUENCE header */ + tbsSeqSz = SetSequence(tbsContentSz, tbsSeqBuf); + + /* Check buffer size */ + if (output != NULL && (tbsSeqSz + tbsContentSz > outputSz)) + return BUFFER_E; + + /* If output is NULL, just return required size */ + if (output == NULL) + return (int)(tbsSeqSz + tbsContentSz); + + /* Encode TBS */ + idx = 0; + + /* TBS SEQUENCE header */ + XMEMCPY(output + idx, tbsSeqBuf, tbsSeqSz); + idx += tbsSeqSz; + + /* Version (optional) */ + if (versionSz > 0) { + XMEMCPY(output + idx, versionBuf, versionSz); + idx += versionSz; + } + + /* Signature AlgorithmIdentifier */ + XMEMCPY(output + idx, algoBuf, algoSz); + idx += algoSz; + + /* Issuer Name */ + XMEMCPY(output + idx, issuerDer, issuerSz); + idx += issuerSz; + + /* thisUpdate */ + XMEMCPY(output + idx, lastDateBuf, lastDateSz); + idx += lastDateSz; + + /* nextUpdate (optional) */ + if (nextDateSz > 0) { + XMEMCPY(output + idx, nextDateBuf, nextDateSz); + idx += nextDateSz; + } + + /* revokedCertificates (optional) */ + if (revokedSz > 0) { + word32 contentSz = 0; + for (rc = certs; rc != NULL; rc = rc->next) { + contentSz += EncodeRevokedCert(NULL, rc); + } + idx += SetSequence(contentSz, output + idx); + for (rc = certs, i = 0; rc != NULL; rc = rc->next, i++) { + idx += EncodeRevokedCert(output + idx, rc); + } + } + + /* crlExtensions (optional) */ + if (extSz > 0) { + word32 crlNumExtSz = EncodeCrlNumberExt(NULL, crlNumber, crlNumberSz); + /* Context tag [0] */ + output[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0); + idx += SetLength(extSeqSz + crlNumExtSz, output + idx); + /* Extensions SEQUENCE */ + idx += SetSequence(crlNumExtSz, output + idx); + /* CRL Number extension */ + idx += EncodeCrlNumberExt(output + idx, crlNumber, crlNumberSz); + } + + return (int)idx; +} + +/* Sign a CRL TBS and produce complete CRL DER. + * tbsBuf: contains the TBS at the beginning + * tbsSz: size of TBS in tbsBuf + * sType: signature type (e.g., CTC_SHA256wRSA) + * buf: output buffer for complete CRL + * bufSz: size of output buffer + * rsaKey/eccKey: signing key (one must be non-NULL) + * rng: random number generator + * + * Returns: size of complete CRL on success, negative error on failure + */ +int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, + byte* buf, word32 bufSz, + RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng) +{ + int ret; + int sigSz; + CertSignCtx certSignCtx_lcl; + CertSignCtx* certSignCtx = &certSignCtx_lcl; + void* heap = NULL; + + if (tbsBuf == NULL || tbsSz <= 0 || buf == NULL || rng == NULL) + return BAD_FUNC_ARG; + if (rsaKey == NULL && eccKey == NULL) + return BAD_FUNC_ARG; + + XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); + + if (rsaKey != NULL) { + heap = rsaKey->heap; + } +#ifdef HAVE_ECC + else if (eccKey != NULL) { + heap = eccKey->heap; + } +#endif + + /* Copy TBS to output buffer first */ + if ((word32)tbsSz > bufSz) + return BUFFER_E; + XMEMCPY(buf, tbsBuf, (size_t)tbsSz); + +#ifndef WOLFSSL_NO_MALLOC + certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (certSignCtx->sig == NULL) + return MEMORY_E; + /* Initialize first byte to avoid static analysis warnings about using + * uninitialized memory if MakeSignature fails before writing sig. */ + certSignCtx->sig[0] = 0; +#endif + + /* Create signature */ + sigSz = MakeSignature(certSignCtx, buf, (word32)tbsSz, certSignCtx->sig, + MAX_ENCODED_SIG_SZ, rsaKey, eccKey, NULL, NULL, NULL, + NULL, NULL, rng, (word32)sType, heap); + if (sigSz < 0) { +#ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return sigSz; + } + + /* Add signature algorithm and signature to buffer */ + ret = AddSignature(buf, tbsSz, certSignCtx->sig, sigSz, sType); + +#ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} +#endif /* WOLFSSL_CERT_GEN */ + #endif /* HAVE_CRL */ diff --git a/wolfssl/crl.h b/wolfssl/crl.h index 059edeef84..774f9361c2 100644 --- a/wolfssl/crl.h +++ b/wolfssl/crl.h @@ -39,8 +39,11 @@ WOLFSSL_LOCAL void FreeCRL(WOLFSSL_CRL* crl, int dynamic); WOLFSSL_LOCAL int LoadCRL(WOLFSSL_CRL* crl, const char* path, int type, int monitor); +WOLFSSL_LOCAL int StoreCRL(WOLFSSL_CRL* crl, const char* file, int type); WOLFSSL_LOCAL int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type, int verify); +WOLFSSL_LOCAL int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, + int type); WOLFSSL_LOCAL int CheckCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert); WOLFSSL_LOCAL int CheckCertCRL_ex(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, int serialSz, byte* serialHash, const byte* extCrlInfo, diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 5b8175564e..3ae8c63168 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -806,8 +806,10 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define d2i_X509_CRL_fp wolfSSL_d2i_X509_CRL_fp #define PEM_read_X509_CRL wolfSSL_PEM_read_X509_CRL +#define X509_CRL_new wolfSSL_X509_CRL_new #define X509_CRL_dup wolfSSL_X509_CRL_dup #define X509_CRL_free wolfSSL_X509_CRL_free +#define X509_CRL_sign wolfSSL_X509_CRL_sign #define X509_CRL_get_lastUpdate wolfSSL_X509_CRL_get_lastUpdate #define X509_CRL_get0_lastUpdate wolfSSL_X509_CRL_get_lastUpdate #define X509_CRL_get_nextUpdate wolfSSL_X509_CRL_get_nextUpdate @@ -817,6 +819,17 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #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 +#define X509_CRL_set_nextUpdate wolfSSL_X509_CRL_set_nextUpdate +#define X509_CRL_set1_nextUpdate wolfSSL_X509_CRL_set_nextUpdate +#define X509_CRL_set_issuer_name wolfSSL_X509_CRL_set_issuer_name +#define X509_CRL_set_version wolfSSL_X509_CRL_set_version +#define X509_CRL_set_signature_type wolfSSL_X509_CRL_set_signature_type +#define X509_CRL_set_signature_nid wolfSSL_X509_CRL_set_signature_nid +#define X509_CRL_set_signature wolfSSL_X509_CRL_set_signature +#define X509_CRL_add_revoked wolfSSL_X509_CRL_add_revoked +#define X509_CRL_add_revoked_cert wolfSSL_X509_CRL_add_revoked_cert #define X509_load_crl_file wolfSSL_X509_load_crl_file #define X509_ACERT_new wolfSSL_X509_ACERT_new diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 7b5b76d062..6007b9c976 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2347,8 +2347,9 @@ WOLFSSL_API void wolfSSL_X509_STORE_CTX_trusted_stack(WOLFSSL_X509_STORE_CTX *ct WOLF_STACK_OF(WOLFSSL_X509) *sk); WOLFSSL_API WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_lastUpdate(WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_lastUpdate(WOLFSSL_X509_CRL* crl, WOLFSSL_ASN1_TIME* time); WOLFSSL_API WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_nextUpdate(WOLFSSL_X509_CRL* crl); - +WOLFSSL_API int wolfSSL_X509_CRL_set_nextUpdate(WOLFSSL_X509_CRL* crl, WOLFSSL_ASN1_TIME* time); WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509); WOLFSSL_API int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* pkey); WOLFSSL_API void wolfSSL_X509_OBJECT_free_contents(WOLFSSL_X509_OBJECT* obj); @@ -3443,15 +3444,21 @@ WOLFSSL_API WOLFSSL_X509_CRL *wolfSSL_d2i_X509_CRL_fp(XFILE file, WOLFSSL_X509_C WOLFSSL_API WOLFSSL_X509_CRL *wolfSSL_d2i_X509_CRL_bio(WOLFSSL_BIO *bp, WOLFSSL_X509_CRL **crl); WOLFSSL_API int wolfSSL_X509_CRL_version(WOLFSSL_X509_CRL *crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_version(WOLFSSL_X509_CRL* crl, int version); WOLFSSL_API int wolfSSL_X509_CRL_get_signature_type(WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_signature_type(WOLFSSL_X509_CRL* crl, int signatureType); WOLFSSL_API int wolfSSL_X509_CRL_get_signature_nid( const WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_signature_nid(WOLFSSL_X509_CRL* crl, int nid); WOLFSSL_API int wolfSSL_X509_CRL_get_signature(WOLFSSL_X509_CRL* crl, unsigned char* buf, int* bufSz); +WOLFSSL_API int wolfSSL_X509_CRL_set_signature(WOLFSSL_X509_CRL* crl, + unsigned char* buf, int bufSz); WOLFSSL_API int wolfSSL_X509_CRL_print(WOLFSSL_BIO* bio, WOLFSSL_X509_CRL* crl); WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_CRL_get_issuer_name( WOLFSSL_X509_CRL *crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_issuer_name(WOLFSSL_X509_CRL* crl, WOLFSSL_X509_NAME* name); WOLFSSL_API int wolfSSL_X509_REVOKED_get_serial_number(RevokedCert* rev, byte* in, int* inOutSz); #endif @@ -3459,6 +3466,23 @@ WOLFSSL_API int wolfSSL_X509_REVOKED_get_serial_number(RevokedCert* rev, WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl); WOLFSSL_API void wolfSSL_X509_CRL_free(WOLFSSL_X509_CRL *crl); #endif +#if defined(HAVE_CRL) && defined(OPENSSL_EXTRA) +WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_new(void); +#ifdef WOLFSSL_CERT_GEN +WOLFSSL_API int wolfSSL_X509_CRL_add_revoked(WOLFSSL_X509_CRL* crl, + const unsigned char* serial, int serialSz, + const unsigned char* revDate, + unsigned char revDateFmt); +WOLFSSL_API int wolfSSL_X509_CRL_add_revoked_cert(WOLFSSL_X509_CRL* crl, + const unsigned char* certBuf, int certSz, + const unsigned char* revDate, + unsigned char revDateFmt); +WOLFSSL_API int wolfSSL_X509_CRL_sign(WOLFSSL_X509_CRL* crl, + WOLFSSL_EVP_PKEY* pkey, + const WOLFSSL_EVP_MD* md); +#endif /* WOLFSSL_CERT_GEN */ +WOLFSSL_API int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, unsigned char** out); +#endif /* HAVE_CRL && OPENSSL_EXTRA */ #if defined(WOLFSSL_ACERT) && \ (defined(OPENSSL_EXTRA_X509_SMALL) || defined(OPENSSL_EXTRA)) @@ -5199,6 +5223,7 @@ WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_INFO)* wolfSSL_PEM_X509_INFO_read_bio( #ifndef NO_FILESYSTEM WOLFSSL_API WOLFSSL_X509_CRL *wolfSSL_PEM_read_X509_CRL(XFILE fp, WOLFSSL_X509_CRL **x, wc_pem_password_cb *cb, void *u); +WOLFSSL_API int wolfSSL_write_X509_CRL(WOLFSSL_X509_CRL* crl, const char* path, int type); #endif WOLFSSL_API int wolfSSL_PEM_get_EVP_CIPHER_INFO(const char* header, EncryptedInfo* cipher); diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 685f806996..3c6ab6da9c 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -593,6 +593,21 @@ WOLFSSL_API int wc_SetCustomExtension(Cert *cert, int critical, const char *oid, #endif /* WOLFSSL_CERT_EXT */ +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) +/* CRL Generation functions */ +struct RevokedCert; /* forward declaration */ +WOLFSSL_API int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz, + const byte* lastDate, byte lastDateFmt, + const byte* nextDate, byte nextDateFmt, + struct RevokedCert* certs, int totalCerts, + const byte* crlNumber, word32 crlNumberSz, + int sigType, int version, + byte* output, word32 outputSz); +WOLFSSL_API int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, + byte* buf, word32 bufSz, + RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng); +#endif /* WOLFSSL_CERT_GEN && HAVE_CRL */ + WOLFSSL_API int wc_GetDateInfo(const byte* certDate, int certDateSz, const byte** date, byte* format, int* length); #ifndef NO_ASN_TIME