diff --git a/IDE/WINVS/unit-test/unit-test.vcxproj b/IDE/WINVS/unit-test/unit-test.vcxproj index 0b7bf991..2466d7c8 100644 --- a/IDE/WINVS/unit-test/unit-test.vcxproj +++ b/IDE/WINVS/unit-test/unit-test.vcxproj @@ -31,6 +31,7 @@ + diff --git a/docs/FIPS_INTEGRATION_GUIDE.md b/docs/FIPS_INTEGRATION_GUIDE.md index b1818aa1..2613379e 100644 --- a/docs/FIPS_INTEGRATION_GUIDE.md +++ b/docs/FIPS_INTEGRATION_GUIDE.md @@ -295,12 +295,32 @@ Always use `--distclean` when switching between production and testing builds: | Category | Algorithms | |----------|------------| -| Digests | MD5, SHA-1 (signing), BLAKE2, SM3 | +| Digests | MD5, MD5-SHA1, SHA-1 (signing), BLAKE2, SM3 | | Symmetric | DES, 3DES, ChaCha20, RC4 | | Asymmetric | RSA < 2048, DSA, Ed25519/Ed448 | | Key Exchange | X25519/X448, DH (MODP) | | Curves | P-192 | +In a default FIPS build, wolfProvider registers MD5, MD5-SHA1, and +DES-EDE3-CBC (3DES), including their aliases, with `fips=yes`. Fetches and +metadata queries succeed, but cryptographic use raises an error. These entries +claim the names for rejection; they do not provide approved cryptographic +services. The MD5 restriction also applies to wolfProvider KDF paths. + +Encrypted-key import is restricted to PBES2/PBKDF2 with AES-CBC and a SHA-based +PRF. Keys encrypted with DES/3DES, MD5-based password schemes, and traditional +encrypted PEM are rejected before decryption. Traditional encrypted PEM uses +MD5 for key derivation even when its cipher is AES. + +A successful fetch is therefore not a capability check for these algorithms. +Configure applications for TLS 1.2 or later and exclude 3DES cipher suites. +Legacy TLS availability checks can accept the metadata and fail later when +the handshake attempts to use a denied algorithm. + +Defining `WP_ALLOW_NON_FIPS` when building wolfProvider explicitly opts out of +these restrictions, provided the underlying wolfCrypt algorithms are compiled +in. This override is disabled by default and permits non-FIPS operations. + --- ## Troubleshooting diff --git a/include/wolfprovider/internal.h b/include/wolfprovider/internal.h index 0b10515c..f1d00821 100644 --- a/include/wolfprovider/internal.h +++ b/include/wolfprovider/internal.h @@ -262,6 +262,9 @@ int wp_encrypt_key_pkcs8(WOLFPROV_CTX* provCtx, int cipher, OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg); int wp_decrypt_key_pkcs8(unsigned char* data, word32* len, OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg); +#ifdef WP_FIPS_DENY_LEGACY +int wp_check_fips_pkcs8(const unsigned char* data, word32 len); +#endif int wp_read_der_bio(WOLFPROV_CTX* provCtx, OSSL_CORE_BIO *coreBio, unsigned char** data, word32* len); int wp_read_pem_bio(WOLFPROV_CTX *provctx, OSSL_CORE_BIO *coreBio, diff --git a/include/wolfprovider/settings.h b/include/wolfprovider/settings.h index cdda021c..c1d85529 100644 --- a/include/wolfprovider/settings.h +++ b/include/wolfprovider/settings.h @@ -52,6 +52,15 @@ #define WP_HAVE_PKCS8_ENC #endif +/* This is the intended FIPS design: claim MD5, MD5-SHA1 and 3DES with + * fips=yes, return valid metadata, and fail loudly on attempts to use them. + * Do not drop legacy algorithm registration in FIPS builds, even when + * wolfCrypt compiles them out. WP_ALLOW_NON_FIPS is the explicit opt-in + * override. */ +#if defined(HAVE_FIPS) && !defined(WP_ALLOW_NON_FIPS) + #define WP_FIPS_DENY_LEGACY +#endif + #define WP_HAVE_DIGEST #if !defined(NO_MD5) #define WP_HAVE_MD5 diff --git a/src/wp_dec_epki2pki.c b/src/wp_dec_epki2pki.c index 9f204ba4..71759771 100644 --- a/src/wp_dec_epki2pki.c +++ b/src/wp_dec_epki2pki.c @@ -280,6 +280,11 @@ static int wp_epki2pki_decode(wp_Epki2Pki* ctx, OSSL_CORE_BIO* coreBio, if ((!done) && ok && !wp_epki2pki_is_epki(data, len)) { done = 1; } +#ifdef WP_FIPS_DENY_LEGACY + if ((!done) && ok && !wp_check_fips_pkcs8(data, len)) { + ok = 0; + } +#endif if ((!done) && ok && ((pwCb == NULL) || (!pwCb(password, sizeof(password), &passwordLen, NULL, pwCbArg)))) { diff --git a/src/wp_dec_pem2der.c b/src/wp_dec_pem2der.c index 0d93d4d7..e368af11 100644 --- a/src/wp_dec_pem2der.c +++ b/src/wp_dec_pem2der.c @@ -156,6 +156,7 @@ static int wp_pem2der_convert(const char* data, word32 len, DerBuffer** pDer, const char* footer; const char* base64Data; size_t base64Len; + size_t i; WOLFPROV_ENTER(WP_LOG_COMP_PK, "wp_pem2der_convert"); @@ -167,7 +168,14 @@ static int wp_pem2der_convert(const char* data, word32 len, DerBuffer** pDer, if (ok) { base64Data = data + 16 + nameLen + 1; base64Len = len - (16 + nameLen + 1); - footer = XSTRSTR(base64Data, "-----END "); + /* Older wolfSSL builds do not export the XSTRNSTR helper. */ + footer = NULL; + for (i = 0; (base64Len >= 9) && (i <= base64Len - 9); i++) { + if (XMEMCMP(base64Data + i, "-----END ", 9) == 0) { + footer = base64Data + i; + break; + } + } if (footer == NULL) { info->consumed = len; ok = 0; @@ -325,7 +333,7 @@ static int wp_pem2der_decode_data(const unsigned char* data, word32 len, dataType = NULL; obj = OSSL_OBJECT_PKEY; -#ifdef WOLFSSL_ENCRYPTED_KEYS +#if defined(WOLFSSL_ENCRYPTED_KEYS) && !defined(WP_FIPS_DENY_LEGACY) /* wc_PemToDer decrypts in place through the password callback. */ dataFormat = "PrivateKeyInfo"; info.passwd_cb = wp_pem_password_cb; @@ -344,6 +352,22 @@ static int wp_pem2der_decode_data(const unsigned char* data, word32 len, ok = 0; } +#ifdef WP_FIPS_DENY_LEGACY + /* Traditional encrypted PEM derives its key with MD5, even for AES. + * Reject its encryption headers before wc_PemToDer can decrypt it. */ + if (ok && (obj == OSSL_OBJECT_PKEY)) { + word32 i; + for (i = 0; i <= len - 8; i++) { + if (XMEMCMP(data + i, "DEK-Info", 8) == 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED, + "Traditional encrypted PEM is disabled in FIPS mode"); + ok = 0; + break; + } + } + } +#endif + if (ok && !done) { /* Decode the PEM to DER using wolfSSL. */ rc = wc_PemToDer(data, len, type, &der, NULL, &info, &algoId); diff --git a/src/wp_des.c b/src/wp_des.c index be9f8d37..a9afb89b 100644 --- a/src/wp_des.c +++ b/src/wp_des.c @@ -30,8 +30,77 @@ #include -#if defined(WP_HAVE_DES3CBC) -#if !defined(HAVE_FIPS) || defined(WP_ALLOW_NON_FIPS) +#if defined(WP_HAVE_DES3CBC) || defined(WP_FIPS_DENY_LEGACY) +/** + * Returns the parameters that can be retrieved. + * + * @param [in] provCtx wolfProvider context object. Unused. + * @return Array of parameters. + */ +static const OSSL_PARAM *wp_cipher_gettable_params(void *provCtx) +{ + /** + * Parameters able to be retrieved for a cipher. + */ + static const OSSL_PARAM cipher_supported_gettable_params[] = { + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_MODE, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL), + OSSL_PARAM_END + }; + (void)provCtx; + return cipher_supported_gettable_params; +} + +/** + * Get the values from the DES3 block context for the parameters. + * + * @param [in, out] params Array of parameters to retrieve. + * @param [in] mode DES3 cipher mode. + * @param [in] kBits Number of bits in key. + * @param [in] ivBits Number of bits in IV. + * @return 1 on success. + * @return 0 on failure. + */ +static int wp_des3_block_get_params(OSSL_PARAM params[], unsigned int mode, + size_t kBits, size_t ivBits) +{ + int ok = 1; + OSSL_PARAM *p; + + WOLFPROV_ENTER(WP_LOG_COMP_DES, "wp_des3_block_get_params"); + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE); + if ((p != NULL) && (!OSSL_PARAM_set_uint(p, mode))) { + ok = 0; + } + if (ok) { + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if ((p != NULL) && (!OSSL_PARAM_set_size_t(p, kBits / 8))) { + ok = 0; + } + } + if (ok) { + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE); + /* This metadata is also needed when wolfCrypt has NO_DES3 set. */ + if ((p != NULL) && (!OSSL_PARAM_set_size_t(p, 8))) { + ok = 0; + } + } + if (ok) { + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if ((p != NULL) && (!OSSL_PARAM_set_size_t(p, ivBits / 8))) { + ok = 0; + } + } + + WOLFPROV_LEAVE(WP_LOG_COMP_DES, + __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); + return ok; +} + +#ifndef WP_FIPS_DENY_LEGACY /** * Data structure for DES3 ciphers that are block based. */ @@ -105,74 +174,6 @@ static void *wp_des3_block_dupctx(wp_Des3BlockCtx *src) return dst; } -/** - * Returns the parameters that can be retrieved. - * - * @param [in] provCtx wolfProvider context object. Unused. - * @return Array of parameters. - */ -static const OSSL_PARAM *wp_cipher_gettable_params( - WOLFPROV_CTX *provCtx) -{ - /** - * Parameters able to be retrieved for a cipher. - */ - static const OSSL_PARAM cipher_supported_gettable_params[] = { - OSSL_PARAM_uint(OSSL_CIPHER_PARAM_MODE, NULL), - OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), - OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), - OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL), - OSSL_PARAM_END - }; - (void)provCtx; - return cipher_supported_gettable_params; -} - -/** - * Get the values from the DES3 block context for the parameters. - * - * @param [in, out] params Array of parameters to retrieve. - * @param [in] mode DES3 cipher mode. - * @param [in] kBits Number of bits in key. - * @param [in] ivBits Number of bits in IV. - * @return 1 on success. - * @return 0 on failure. - */ -static int wp_des3_block_get_params(OSSL_PARAM params[], unsigned int mode, - size_t kBits, size_t ivBits) -{ - int ok = 1; - OSSL_PARAM *p; - - WOLFPROV_ENTER(WP_LOG_COMP_DES, "wp_des3_block_get_params"); - - p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE); - if ((p != NULL) && (!OSSL_PARAM_set_uint(p, mode))) { - ok = 0; - } - if (ok) { - p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); - if ((p != NULL) && (!OSSL_PARAM_set_size_t(p, kBits / 8))) { - ok = 0; - } - } - if (ok) { - p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE); - if ((p != NULL) && (!OSSL_PARAM_set_size_t(p, DES_BLOCK_SIZE))) { - ok = 0; - } - } - if (ok) { - p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); - if ((p != NULL) && (!OSSL_PARAM_set_size_t(p, ivBits / 8))) { - ok = 0; - } - } - - WOLFPROV_LEAVE(WP_LOG_COMP_DES, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); - return ok; -} - /** * Returns the parameters of a cipher context that can be retrieved. * @@ -914,32 +915,84 @@ IMPLEMENT_DES3_BLOCK_DISPATCH(lcmode, kBits, ivBits) /** wp_des3cbc_functions_functions */ IMPLEMENT_DES3_BLOCK(cbc, CBC, 192, 64) -#else /* defined(HAVE_FIPS) && !defined(WP_ALLOW_NON_FIPS */ +#else /* WP_FIPS_DENY_LEGACY */ -#define IMPLEMENT_DES3_BLOCK_NULL(mode) \ -const OSSL_DISPATCH wp_des3##mode##_functions[] = { \ - { OSSL_FUNC_CIPHER_NEWCTX, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_FREECTX, (DFUNC)wp_des3_void }, \ - { OSSL_FUNC_CIPHER_DUPCTX, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_DECRYPT_INIT, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_UPDATE, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_FINAL, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_CIPHER, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_GET_PARAMS, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ - (DFUNC)wp_des3_null }, \ - { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ - (DFUNC)wp_des3_null }, \ - { 0, NULL } \ -}; -static int wp_des3_null(void) { return 0; } -static void wp_des3_void(void) {} +static int wp_des3cbc_get_params(OSSL_PARAM params[]) +{ + return wp_des3_block_get_params(params, EVP_CIPH_CBC_MODE, 192, 64); +} + +static int wp_des3_denied(void) +{ + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED, + "3DES is disabled in FIPS mode"); + return 0; +} + +static void* wp_des3_denied_newctx(void* provCtx) +{ + (void)provCtx; + wp_des3_denied(); + return NULL; +} -IMPLEMENT_DES3_BLOCK_NULL(cbc) +static void wp_des3_denied_freectx(void* ctx) +{ + (void)ctx; +} + +static int wp_des3_denied_init(void* ctx, const unsigned char* key, + size_t keyLen, const unsigned char* iv, size_t ivLen, + const OSSL_PARAM params[]) +{ + (void)ctx; + (void)key; + (void)keyLen; + (void)iv; + (void)ivLen; + (void)params; + return wp_des3_denied(); +} + +static int wp_des3_denied_update(void* ctx, unsigned char* out, size_t* outLen, + size_t outSize, const unsigned char* in, size_t inLen) +{ + (void)ctx; + (void)out; + (void)outSize; + (void)in; + (void)inLen; + if (outLen != NULL) { + *outLen = 0; + } + return wp_des3_denied(); +} + +static int wp_des3_denied_final(void* ctx, unsigned char* out, size_t* outLen, + size_t outSize) +{ + (void)ctx; + (void)out; + (void)outSize; + if (outLen != NULL) { + *outLen = 0; + } + return wp_des3_denied(); +} + +const OSSL_DISPATCH wp_des3cbc_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (DFUNC)wp_des3_denied_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (DFUNC)wp_des3_denied_freectx }, + { OSSL_FUNC_CIPHER_DUPCTX, (DFUNC)wp_des3_denied_newctx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (DFUNC)wp_des3_denied_init }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (DFUNC)wp_des3_denied_init }, + { OSSL_FUNC_CIPHER_UPDATE, (DFUNC)wp_des3_denied_update }, + { OSSL_FUNC_CIPHER_FINAL, (DFUNC)wp_des3_denied_final }, + { OSSL_FUNC_CIPHER_CIPHER, (DFUNC)wp_des3_denied_update }, + { OSSL_FUNC_CIPHER_GET_PARAMS, (DFUNC)wp_des3cbc_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (DFUNC)wp_cipher_gettable_params }, + { 0, NULL } +}; #endif -#endif /* WP_HAVE_DES3CBC */ +#endif /* WP_HAVE_DES3CBC || WP_FIPS_DENY_LEGACY */ diff --git a/src/wp_digests.c b/src/wp_digests.c index fb027688..7451e0cb 100644 --- a/src/wp_digests.c +++ b/src/wp_digests.c @@ -231,21 +231,69 @@ const OSSL_DISPATCH name##_functions[] = { \ { 0, NULL } \ }; -#define IMPLEMENT_DIGEST_NULL(name) \ -/** Dispatch table for digest_null algorithms. */ \ -static int name##_digest_null(void) { return 0; } \ -static void name##_digest_void(void) {} \ -const OSSL_DISPATCH name##_functions[] = { \ - { OSSL_FUNC_DIGEST_NEWCTX, (DFUNC)name##_digest_null }, \ - { OSSL_FUNC_DIGEST_INIT, (DFUNC)name##_digest_null }, \ - { OSSL_FUNC_DIGEST_UPDATE, (DFUNC)name##_digest_null }, \ - { OSSL_FUNC_DIGEST_FINAL, (DFUNC)name##_digest_null }, \ - { OSSL_FUNC_DIGEST_FREECTX, (DFUNC)name##_digest_void }, \ - { OSSL_FUNC_DIGEST_DUPCTX, (DFUNC)name##_digest_null }, \ - { OSSL_FUNC_DIGEST_GET_PARAMS, (DFUNC)name##_digest_null }, \ - { OSSL_FUNC_DIGEST_GETTABLE_PARAMS, (DFUNC)name##_digest_null }, \ - { 0, NULL } \ +#ifdef WP_FIPS_DENY_LEGACY +static int wp_digest_denied(void) +{ + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED, + "MD5-based digests are disabled in FIPS mode"); + return 0; +} + +static void* wp_digest_denied_newctx(void* provCtx) +{ + (void)provCtx; + wp_digest_denied(); + return NULL; +} + +static void wp_digest_denied_freectx(void* ctx) +{ + (void)ctx; +} + +static int wp_digest_denied_init(void* ctx, const OSSL_PARAM params[]) +{ + (void)ctx; + (void)params; + return wp_digest_denied(); +} + +static int wp_digest_denied_update(void* ctx, const unsigned char* in, + size_t inLen) +{ + (void)ctx; + (void)in; + (void)inLen; + return wp_digest_denied(); +} + +static int wp_digest_denied_final(void* ctx, unsigned char* out, + size_t* outLen, size_t outSize) +{ + (void)ctx; + (void)out; + (void)outSize; + if (outLen != NULL) { + *outLen = 0; + } + return wp_digest_denied(); +} + +/* Fetching describes the method; creating or using a context is forbidden. */ +#define IMPLEMENT_DIGEST_DENIED(name, blkSize, dgstSize) \ +IMPLEMENT_DIGEST_GET_PARAM(name, blkSize, dgstSize, 0) \ +const OSSL_DISPATCH name##_functions[] = { \ + { OSSL_FUNC_DIGEST_NEWCTX, (DFUNC)wp_digest_denied_newctx }, \ + { OSSL_FUNC_DIGEST_INIT, (DFUNC)wp_digest_denied_init }, \ + { OSSL_FUNC_DIGEST_UPDATE, (DFUNC)wp_digest_denied_update }, \ + { OSSL_FUNC_DIGEST_FINAL, (DFUNC)wp_digest_denied_final }, \ + { OSSL_FUNC_DIGEST_FREECTX, (DFUNC)wp_digest_denied_freectx }, \ + { OSSL_FUNC_DIGEST_DUPCTX, (DFUNC)wp_digest_denied_newctx }, \ + { OSSL_FUNC_DIGEST_GET_PARAMS, (DFUNC)name##_get_params }, \ + { OSSL_FUNC_DIGEST_GETTABLE_PARAMS, (DFUNC)wp_digest_gettable_params }, \ + { 0, NULL } \ }; +#endif /* WP_FIPS_DENY_LEGACY */ /** * Get parameters of a digest algorithm. @@ -319,9 +367,10 @@ static const OSSL_PARAM* wp_digest_gettable_params(void* provCtx) * MD5 ******************************************************************************/ -#ifdef WP_HAVE_MD5 -#if defined(HAVE_FIPS) && !defined(WP_ALLOW_NON_FIPS) -IMPLEMENT_DIGEST_NULL(wp_md5) +#if defined(WP_HAVE_MD5) || defined(WP_FIPS_DENY_LEGACY) +#ifdef WP_FIPS_DENY_LEGACY +/* Metadata must not depend on wolfCrypt's MD5 implementation being present. */ +IMPLEMENT_DIGEST_DENIED(wp_md5, 64, 16) #else IMPLEMENT_DIGEST(wp_md5, wc_Md5, WC_MD5_BLOCK_SIZE, WC_MD5_DIGEST_SIZE, @@ -335,8 +384,8 @@ IMPLEMENT_DIGEST(wp_md5, wc_Md5, * SHA1-MD5 ******************************************************************************/ -#ifdef WP_HAVE_MD5_SHA1 -#if !defined(HAVE_FIPS) || defined(WP_ALLOW_NON_FIPS) +#if defined(WP_HAVE_MD5_SHA1) || defined(WP_FIPS_DENY_LEGACY) +#ifndef WP_FIPS_DENY_LEGACY /** * Combined MD5 and SHA-1 digest. */ @@ -472,8 +521,8 @@ IMPLEMENT_DIGEST(wp_md5_sha1, wp_Md5Sha, 0, wp_InitMd5Sha_ex, wp_Md5ShaUpdate, wp_Md5ShaFinal, wp_Md5ShaCopy, wp_Md5ShaFree) -#else /* defined(HAVE_FIPS) && !defined(WP_ALLOW_NON_FIPS) */ -IMPLEMENT_DIGEST_NULL(wp_md5_sha1) +#else /* WP_FIPS_DENY_LEGACY */ +IMPLEMENT_DIGEST_DENIED(wp_md5_sha1, 64, 36) #endif #endif diff --git a/src/wp_internal.c b/src/wp_internal.c index d4bb7e3c..d6f376ed 100644 --- a/src/wp_internal.c +++ b/src/wp_internal.c @@ -19,10 +19,12 @@ */ +#include #include #include #include #include +#include #include #include @@ -578,6 +580,16 @@ enum wc_HashType wp_nid_to_wc_hash_type(int nid) { enum wc_HashType hashType = WC_HASH_TYPE_NONE; +#ifdef WP_FIPS_DENY_LEGACY + /* A fetch can succeed for a denial method. KDFs and MACs calling wolfCrypt + * directly must not treat that metadata as permission to use it. */ + if ((nid == NID_md5) || (nid == NID_md5_sha1)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED, + "MD5-based digests are disabled in FIPS mode"); + nid = NID_undef; + } +#endif + switch (nid) { case NID_md5: hashType = WC_HASH_TYPE_MD5; @@ -1217,6 +1229,70 @@ int wp_encrypt_key_pkcs8(WOLFPROV_CTX* provCtx, int cipher, #endif } +#ifdef WP_FIPS_DENY_LEGACY +/** + * Check encryption metadata before wolfCrypt can decrypt a key directly. + * Allow PBES2/PBKDF2 with AES-CBC and a SHA-based PRF. + * + * @param [in] data DER-encoded EncryptedPrivateKeyInfo. + * @param [in] len Length of the DER encoding in bytes. + * @return 1 when the encryption scheme is allowed, or 0 when rejected. + */ +int wp_check_fips_pkcs8(const unsigned char* data, word32 len) +{ + const unsigned char* p = data; + const X509_ALGOR* alg = NULL; + X509_SIG* epki = NULL; + PBE2PARAM* pbe = NULL; + PBKDF2PARAM* kdf = NULL; + int nid, ok = 0; + + WOLFPROV_ENTER(WP_LOG_COMP_PROVIDER, "wp_check_fips_pkcs8"); + + if ((data != NULL) && (len <= INT_MAX)) { + epki = d2i_X509_SIG(NULL, &p, (long)len); + } + if ((epki != NULL) && (p == data + len)) { + X509_SIG_get0(epki, &alg, NULL); + if ((OBJ_obj2nid(alg->algorithm) == NID_pbes2) && + (alg->parameter != NULL)) { + pbe = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBE2PARAM), + alg->parameter); + } + } + if ((pbe != NULL) && + (OBJ_obj2nid(pbe->keyfunc->algorithm) == NID_id_pbkdf2) && + (pbe->keyfunc->parameter != NULL)) { + nid = OBJ_obj2nid(pbe->encryption->algorithm); + if ((nid == NID_aes_128_cbc) || (nid == NID_aes_192_cbc) || + (nid == NID_aes_256_cbc)) { + kdf = ASN1_TYPE_unpack_sequence(ASN1_ITEM_rptr(PBKDF2PARAM), + pbe->keyfunc->parameter); + } + } + if (kdf != NULL) { + /* Omitted PBKDF2 PRF means HMAC-SHA1. */ + nid = (kdf->prf == NULL) ? NID_hmacWithSHA1 : + OBJ_obj2nid(kdf->prf->algorithm); + ok = (nid == NID_hmacWithSHA1) || (nid == NID_hmacWithSHA224) || + (nid == NID_hmacWithSHA256) || (nid == NID_hmacWithSHA384) || + (nid == NID_hmacWithSHA512) || (nid == NID_hmacWithSHA512_224) || + (nid == NID_hmacWithSHA512_256); + } + PBKDF2PARAM_free(kdf); + PBE2PARAM_free(pbe); + X509_SIG_free(epki); + if (!ok) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED, + "Encrypted keys require PBES2/PBKDF2 with AES-CBC and a SHA PRF " + "in FIPS mode"); + } + WOLFPROV_LEAVE(WP_LOG_COMP_PROVIDER, + __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); + return ok; +} +#endif + #ifdef WP_HAVE_PKCS8_ENC /* DER encoding of the PBKDF2 OID (1.2.840.113549.1.5.12). */ static const unsigned char wp_pbkdf2_oid[] = { @@ -1275,6 +1351,11 @@ int wp_decrypt_key_pkcs8(unsigned char* data, word32* len, if ((data == NULL) || (!wp_is_pbkdf2_encrypted(data, *len))) { ok = 0; } +#ifdef WP_FIPS_DENY_LEGACY + if (ok && !wp_check_fips_pkcs8(data, *len)) { + ok = 0; + } +#endif #ifdef WOLFSSL_SMALL_STACK if (ok) { password = (char*)XMALLOC(WP_EPKI_PASSWORD_MAX, NULL, diff --git a/src/wp_wolfprov.c b/src/wp_wolfprov.c index 69446838..12d898ad 100644 --- a/src/wp_wolfprov.c +++ b/src/wp_wolfprov.c @@ -404,12 +404,12 @@ static int wolfprov_get_params(void* provCtx, OSSL_PARAM params[]) /* List of digest algorithm implementations available in wolfSSL provider. */ static const OSSL_ALGORITHM wolfprov_digests[] = { -#ifdef WP_HAVE_MD5 +#if defined(WP_HAVE_MD5) || defined(WP_FIPS_DENY_LEGACY) /* MD5 */ { WP_NAMES_MD5, WOLFPROV_PROPERTIES, wp_md5_functions, "" }, #endif -#ifdef WP_HAVE_MD5_SHA1 +#if defined(WP_HAVE_MD5_SHA1) || defined(WP_FIPS_DENY_LEGACY) /* MD5-SHA1 */ { WP_NAMES_MD5_SHA1, WOLFPROV_PROPERTIES, wp_md5_sha1_functions, "" }, @@ -551,7 +551,7 @@ static const OSSL_ALGORITHM wolfprov_ciphers[] = { "" }, #endif -#ifdef WP_HAVE_DES3CBC +#if defined(WP_HAVE_DES3CBC) || defined(WP_FIPS_DENY_LEGACY) { WP_NAMES_DES_EDE3_CBC, WOLFPROV_PROPERTIES, wp_des3cbc_functions, "" }, #endif diff --git a/test/include.am b/test/include.am index cab0700f..bfedbddc 100644 --- a/test/include.am +++ b/test/include.am @@ -21,6 +21,7 @@ test_unit_test_SOURCES = \ test/test_rand_seed.c \ test/test_ecc.c \ test/test_ecx.c \ + test/test_fips_policy.c \ test/test_gmac.c \ test/test_krb5kdf.c \ test/test_sshkdf.c \ diff --git a/test/test_ecc.c b/test/test_ecc.c index 87003a73..7607859c 100644 --- a/test/test_ecc.c +++ b/test/test_ecc.c @@ -3661,36 +3661,40 @@ static int test_ec_derive_off_curve_peer(void) #endif /* WP_HAVE_ECDH */ /* - * A truncated PEM (header only) must be rejected without an out-of-bounds read - * in wp_pem2der_convert (base64Data past the buffer / base64Len underflow). + * Reject truncated PEM without reading past the input. The longer cases + * reach the footer search in wp_pem2der_convert rather than the header guard. */ int test_ec_decode_short_pem(void *data) { int err = 0; - OSSL_DECODER_CTX *dctx = NULL; - EVP_PKEY *pkey = NULL; - static const char shortPem[] = "-----BEGIN EC PARAMETERS-----\n"; - const unsigned char *p = (const unsigned char *)shortPem; - size_t pLen = sizeof(shortPem) - 1; + static const char* shortPem[] = { + "-----BEGIN EC PARAMETERS-----\n", + "-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n", + "-----BEGIN EC PARAMETERS-----\nBggqhkjOPQMBBw==\n-----END" + }; + size_t i; (void)data; - dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "EC", - EVP_PKEY_KEY_PARAMETERS, wpLibCtx, NULL); - if (dctx == NULL) { - err = 1; - } - if (err == 0) { - /* Truncated PEM simply fails to decode; must not read out of bounds. */ - (void)OSSL_DECODER_from_data(dctx, &p, &pLen); - if (pkey != NULL) { - PRINT_ERR_MSG("Truncated PEM unexpectedly produced a key"); + for (i = 0; !err && (i < sizeof(shortPem) / sizeof(shortPem[0])); i++) { + OSSL_DECODER_CTX* dctx; + EVP_PKEY* pkey = NULL; + const unsigned char* p = (const unsigned char*)shortPem[i]; + size_t pLen = XSTRLEN(shortPem[i]); + + ERR_clear_error(); + dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, "EC", + EVP_PKEY_KEY_PARAMETERS, wpLibCtx, "provider=wolfprov"); + err = (dctx == NULL); + if (!err && ((OSSL_DECODER_from_data(dctx, &p, &pLen) != 0) || + (pkey != NULL))) { + PRINT_ERR_MSG("Truncated PEM unexpectedly decoded"); err = 1; } + OSSL_DECODER_CTX_free(dctx); + EVP_PKEY_free(pkey); + ERR_clear_error(); } - - OSSL_DECODER_CTX_free(dctx); - EVP_PKEY_free(pkey); return err; } diff --git a/test/test_fips_policy.c b/test/test_fips_policy.c new file mode 100644 index 00000000..17d0862b --- /dev/null +++ b/test/test_fips_policy.c @@ -0,0 +1,349 @@ +/* test_fips_policy.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfProvider. + * + * wolfProvider is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfProvider is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfProvider. If not, see . + */ + +#include "unit.h" +#include +#include +#include +#include +#include + +#ifdef WP_FIPS_DENY_LEGACY + +/* Run first: a successful fetch must not leave errors from other algorithms + * being constructed, and denial methods must survive NO_MD5/NO_DES3 builds. */ +int test_fips_legacy_algorithms(void* data) +{ + static const char* digests[] = { + "MD5", "SSL3-MD5", "1.2.840.113549.2.5", "MD5-SHA1" + }; + static const char* ciphers[] = { + "DES-EDE3-CBC", "DES3", "1.2.840.113549.3.7" + }; + EVP_MD* md; + EVP_CIPHER* cipher; + unsigned char out[EVP_MAX_MD_SIZE], key[24], iv[8] = {0}; + unsigned int outLen; + size_t i; + int enc, err = 0; + + (void)data; + ERR_clear_error(); + md = EVP_MD_fetch(wpLibCtx, "SHA256", "provider=wolfprov"); + if ((md == NULL) || (ERR_peek_error() != 0)) { + PRINT_ERR_MSG("FIPS digest discovery left an error"); + err = 1; + } + EVP_MD_free(md); +#ifdef WP_HAVE_AESCBC + cipher = EVP_CIPHER_fetch(wpLibCtx, "AES-128-CBC", "provider=wolfprov"); + if ((cipher == NULL) || (ERR_peek_error() != 0)) { + PRINT_ERR_MSG("FIPS cipher discovery left an error"); + err = 1; + } + EVP_CIPHER_free(cipher); +#endif + + for (i = 0; !err && (i < sizeof(digests) / sizeof(digests[0])); i++) { + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + md = EVP_MD_fetch(wpLibCtx, digests[i], "provider=wolfprov"); + if ((ctx == NULL) || (md == NULL) || (ERR_peek_error() != 0) || + (EVP_MD_get0_provider(md) != wpProv) || + (EVP_MD_get_block_size(md) != 64) || + (EVP_MD_get_size(md) != ((i == 3) ? 36 : 16)) || + (EVP_MD_gettable_params(md) == NULL)) { + PRINT_ERR_MSG("Invalid denial metadata: %s", digests[i]); + err = 1; + } + if (!err && ((EVP_DigestInit_ex2(ctx, md, NULL) != 0) || + (ERR_peek_error() == 0))) { + PRINT_ERR_MSG("FIPS digest initialization was not rejected: %s", + digests[i]); + err = 1; + } + ERR_clear_error(); + if (!err && ((EVP_Digest("abc", 3, out, &outLen, md, NULL) != 0) || + (ERR_peek_error() == 0))) { + PRINT_ERR_MSG("FIPS digest operation was not rejected: %s", + digests[i]); + err = 1; + } + ERR_clear_error(); + EVP_MD_CTX_free(ctx); + EVP_MD_free(md); + } + + for (i = 0; i < sizeof(key); i++) { + key[i] = (unsigned char)(i + 1); + } + for (i = 0; !err && (i < sizeof(ciphers) / sizeof(ciphers[0])); i++) { + cipher = EVP_CIPHER_fetch(wpLibCtx, ciphers[i], "provider=wolfprov"); + if ((cipher == NULL) || (ERR_peek_error() != 0) || + (EVP_CIPHER_get0_provider(cipher) != wpProv) || + (EVP_CIPHER_get_key_length(cipher) != 24) || + (EVP_CIPHER_get_iv_length(cipher) != 8) || + (EVP_CIPHER_get_block_size(cipher) != 8) || + (EVP_CIPHER_gettable_params(cipher) == NULL)) { + PRINT_ERR_MSG("Invalid denial metadata: %s", ciphers[i]); + err = 1; + } + for (enc = 0; !err && (enc <= 1); enc++) { + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + if ((ctx == NULL) || + (EVP_CipherInit_ex2(ctx, cipher, key, iv, enc, NULL) != 0) || + (ERR_peek_error() == 0)) { + PRINT_ERR_MSG("FIPS cipher initialization was not rejected: %s", + ciphers[i]); + err = 1; + } + ERR_clear_error(); + EVP_CIPHER_CTX_free(ctx); + } + EVP_CIPHER_free(cipher); + } + ERR_clear_error(); + return err; +} + +#if defined(WP_HAVE_HKDF) || defined(WP_HAVE_PBE) || defined(WP_HAVE_TLS1_PRF) +/* These KDFs call wolfCrypt directly after resolving a digest name. A denial + * method's successful fetch must never enable the underlying MD5 operation. */ +static int test_fips_kdf_digest(const char* name, const char* deniedDigest) +{ + EVP_KDF* kdf = EVP_KDF_fetch(wpLibCtx, name, "provider=wolfprov"); + const char* digests[] = { "SHA256", NULL }; + char properties[] = "provider=wolfprov"; + unsigned char key[64] = {0}, salt[16] = {0}, out[32]; + uint64_t iterations = 1000; + int i, err = 0; + + digests[1] = deniedDigest; + if ((kdf == NULL) || (EVP_KDF_get0_provider(kdf) != wpProv)) { + PRINT_ERR_MSG("Failed to fetch FIPS KDF: %s", name); + err = 1; + } + for (i = 0; !err && (i < 2); i++) { + EVP_KDF_CTX* ctx = EVP_KDF_CTX_new(kdf); + OSSL_PARAM params[6]; + size_t n = 0; + params[n++] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char*)digests[i], 0); + params[n++] = OSSL_PARAM_construct_utf8_string( + OSSL_KDF_PARAM_PROPERTIES, properties, 0); + if (strcmp(name, "PBKDF2") == 0) { + params[n++] = OSSL_PARAM_construct_octet_string( + OSSL_KDF_PARAM_PASSWORD, key, sizeof(key)); + params[n++] = OSSL_PARAM_construct_uint64(OSSL_KDF_PARAM_ITER, + &iterations); + } + else { + params[n++] = OSSL_PARAM_construct_octet_string( + (strcmp(name, "TLS1-PRF") == 0) ? OSSL_KDF_PARAM_SECRET : + OSSL_KDF_PARAM_KEY, key, sizeof(key)); + } + params[n++] = OSSL_PARAM_construct_octet_string( + (strcmp(name, "TLS1-PRF") == 0) ? OSSL_KDF_PARAM_SEED : + OSSL_KDF_PARAM_SALT, salt, sizeof(salt)); + params[n] = OSSL_PARAM_construct_end(); + ERR_clear_error(); + if ((ctx == NULL) || + (EVP_KDF_derive(ctx, out, sizeof(out), params) != (i == 0)) || + ((i == 1) && (ERR_peek_error() == 0))) { + PRINT_ERR_MSG("Incorrect FIPS KDF policy: %s / %s", name, + digests[i]); + err = 1; + } + ERR_clear_error(); + EVP_KDF_CTX_free(ctx); + } + EVP_KDF_free(kdf); + return err; +} +#endif + +int test_fips_legacy_kdfs(void* data) +{ + int err = 0; + (void)data; +#ifdef WP_HAVE_HKDF + err = test_fips_kdf_digest("HKDF", "MD5"); +#endif +#ifdef WP_HAVE_PBE + if (!err) { + err = test_fips_kdf_digest("PBKDF2", "MD5"); + } +#endif +#ifdef WP_HAVE_TLS1_PRF + if (!err) { + err = test_fips_kdf_digest("TLS1-PRF", "MD5-SHA1"); + } +#endif + return err; +} + +/* Use real encrypted keys to exercise the cipher, PRF and PEM restrictions. */ +int test_fips_legacy_key_decrypt(void* data) +{ + int err = 0; +#ifdef WP_HAVE_RSA + static const struct { + const char* cipher; + int pbe, prf, allowed, traditional; + } cases[] = { + { "AES-128-CBC", -1, NID_hmacWithSHA256, 1, 0 }, + { "AES-192-CBC", -1, NID_hmacWithSHA256, 1, 0 }, + { "AES-256-CBC", -1, NID_hmacWithSHA256, 1, 0 }, + /* SHA1 is the default PBKDF2 PRF and is omitted from the DER. */ + { "AES-256-CBC", -1, NID_hmacWithSHA1, 1, 0 }, +#ifndef OPENSSL_NO_MD5 + { "AES-256-CBC", -1, NID_hmacWithMD5, 0, 0 }, + { "AES-256-CBC", -1, NID_undef, 0, 1 }, +#endif +#ifndef OPENSSL_NO_DES + { "DES-EDE3-CBC", -1, NID_hmacWithSHA256, 0, 0 }, + { "DES-EDE3-CBC", NID_pbe_WithSHA1And3_Key_TripleDES_CBC, + NID_undef, 0, 0 }, +#endif + }; + BIO* input = BIO_new_file(CERTS_DIR "/server-key.pem", "r"); + EVP_PKEY* key = NULL; + PKCS8_PRIV_KEY_INFO* p8 = NULL; + const char* pass = "wolfprov-test-pass"; + unsigned char salt[16] = {0}; + int scheme, format; + + if (input != NULL) { + key = PEM_read_bio_PrivateKey_ex(input, NULL, NULL, NULL, + osslLibCtx, NULL); + } + if (key != NULL) { + p8 = EVP_PKEY2PKCS8(key); + } + err = (p8 == NULL); + for (scheme = 0; !err && + (scheme < (int)(sizeof(cases) / sizeof(cases[0]))); scheme++) { + EVP_CIPHER* cipher = EVP_CIPHER_fetch(osslLibCtx, + cases[scheme].cipher, NULL); + X509_SIG* epki = NULL; +#ifndef WP_HAVE_PKCS8_ENC + /* Keep rejection coverage when this build cannot import AES keys. */ + if (cases[scheme].allowed) { + EVP_CIPHER_free(cipher); + continue; + } +#endif + err = (cipher == NULL); + if (!err && !cases[scheme].traditional) { + if (cases[scheme].pbe == -1) { + X509_ALGOR* alg = PKCS5_pbe2_set_iv_ex(cipher, 2048, salt, + sizeof(salt), NULL, cases[scheme].prf, osslLibCtx); + if (alg != NULL) { + epki = PKCS8_set0_pbe_ex(pass, (int)XSTRLEN(pass), p8, + alg, osslLibCtx, NULL); + if (epki == NULL) { + X509_ALGOR_free(alg); + } + } + } + else { + epki = PKCS8_encrypt_ex(cases[scheme].pbe, cipher, + pass, (int)XSTRLEN(pass), salt, sizeof(salt), 2048, p8, + osslLibCtx, NULL); + } + err = (epki == NULL); + } + for (format = cases[scheme].traditional; !err && (format < 2); + format++) { + BIO* encoded = BIO_new(BIO_s_mem()); + EVP_PKEY* decoded = NULL; + OSSL_DECODER_CTX* ctx = NULL; + char* bytes = NULL; + unsigned long code; + int rc = 0, denied = 0; + + if (encoded != NULL) { + if (cases[scheme].traditional) { + /* This writer has no libctx argument. Create the legacy + * fixture with OpenSSL, even under a global FIPS config. */ + OSSL_LIB_CTX* saved = OSSL_LIB_CTX_set0_default(osslLibCtx); + if (saved != NULL) { + rc = PEM_write_bio_PrivateKey_traditional(encoded, key, + cipher, (const unsigned char*)pass, + (int)XSTRLEN(pass), NULL, NULL); + OSSL_LIB_CTX_set0_default(saved); + } + } + else { + rc = (format == 0) ? i2d_PKCS8_bio(encoded, epki) : + PEM_write_bio_PKCS8(encoded, epki); + } + } + err = (rc != 1); + if (!err) { + ctx = OSSL_DECODER_CTX_new_for_pkey(&decoded, + (format == 0) ? "DER" : "PEM", NULL, "RSA", + EVP_PKEY_KEYPAIR, wpLibCtx, "provider=wolfprov"); + err = (ctx == NULL) || + (OSSL_DECODER_CTX_set_passphrase(ctx, + (const unsigned char*)pass, XSTRLEN(pass)) != 1); + } + ERR_clear_error(); + if (!err) { + size_t len = (size_t)BIO_get_mem_data(encoded, &bytes); + const unsigned char* p = (const unsigned char*)bytes; + rc = OSSL_DECODER_from_data(ctx, &p, &len); + while ((code = ERR_get_error()) != 0) { + denied |= (ERR_GET_LIB(code) == ERR_LIB_PROV) && + (ERR_GET_REASON(code) == PROV_R_NOT_SUPPORTED); + } + /* Some bundles lack AES-192 PKCS#8 support. The policy must + * allow it; verify the key when the decoder supports it. */ + if (cases[scheme].allowed && (rc == 0) && + (strcmp(cases[scheme].cipher, "AES-192-CBC") == 0)) { + err = denied || (decoded != NULL); + } + else if (cases[scheme].allowed) { + err = (rc != 1) || (decoded == NULL) || + (EVP_PKEY_eq(key, decoded) != 1); + } + else { + err = (rc != 0) || (decoded != NULL) || !denied; + } + } + if (err) { + PRINT_ERR_MSG("Incorrect FIPS encrypted-key policy: %d / %d", + scheme, format); + } + OSSL_DECODER_CTX_free(ctx); + EVP_PKEY_free(decoded); + BIO_free(encoded); + } + X509_SIG_free(epki); + EVP_CIPHER_free(cipher); + } + PKCS8_PRIV_KEY_INFO_free(p8); + EVP_PKEY_free(key); + BIO_free(input); +#endif + (void)data; + ERR_clear_error(); + return err; +} +#endif /* WP_FIPS_DENY_LEGACY */ diff --git a/test/test_tls1_prf.c b/test/test_tls1_prf.c index 65ee8842..16c966e6 100644 --- a/test/test_tls1_prf.c +++ b/test/test_tls1_prf.c @@ -19,6 +19,7 @@ */ #include "unit.h" +#include #ifdef WP_HAVE_TLS1_PRF @@ -531,7 +532,23 @@ int test_tls1_prf(void *data) (void)data; -#ifdef WP_HAVE_MD5_SHA1 +#ifdef WP_FIPS_DENY_LEGACY + { + unsigned char key[32]; + EVP_MD* md; + + /* MD5-SHA1 must remain unusable after metadata discovery succeeds. */ + ERR_clear_error(); + md = EVP_MD_fetch(wpLibCtx, "MD5-SHA1", "provider=wolfprov"); + err = (md == NULL) || (ERR_peek_error() != 0); + if (err == 0) { + err = (test_tls1_prf_calc(wpLibCtx, key, sizeof(key), md) == 0) || + (ERR_peek_error() == 0); + } + ERR_clear_error(); + EVP_MD_free(md); + } +#elif defined(WP_HAVE_MD5_SHA1) err = test_tls1_prf_md(EVP_md5_sha1()); #endif #ifdef WP_HAVE_SHA256 diff --git a/test/unit.c b/test/unit.c index be7f72a2..fa0dceb3 100644 --- a/test/unit.c +++ b/test/unit.c @@ -180,6 +180,12 @@ static int debug = 1; static unsigned long flags = 0; TEST_CASE test_case[] = { +#ifdef WP_FIPS_DENY_LEGACY + /* Exercise the first fetch before another test constructs the methods. */ + TEST_DECL(test_fips_legacy_algorithms, NULL), + TEST_DECL(test_fips_legacy_kdfs, NULL), + TEST_DECL(test_fips_legacy_key_decrypt, NULL), +#endif TEST_DECL(test_logging, &debug), TEST_DECL(test_ct_masks, NULL), #ifdef WP_HAVE_SHA1 diff --git a/test/unit.h b/test/unit.h index 35bc245b..f822ee2a 100644 --- a/test/unit.h +++ b/test/unit.h @@ -78,8 +78,8 @@ #ifdef TEST_MULTITHREADED -#define PRINT_MSG(str) -#define PRINT_ERR_MSG(str) +#define PRINT_MSG(...) +#define PRINT_ERR_MSG(...) #else #define PRINT_MSG(str, ...) printf("MSG: " str "\n", ##__VA_ARGS__) #define PRINT_ERR_MSG(str, ...) printf("ERR: " str "\n", ##__VA_ARGS__) @@ -117,6 +117,9 @@ typedef struct TEST_CASE { int test_logging(void *data); int test_ct_masks(void *data); +int test_fips_legacy_algorithms(void *data); +int test_fips_legacy_kdfs(void *data); +int test_fips_legacy_key_decrypt(void *data); #define WP_VALGRIND_TEST 0x1