diff --git a/README.md b/README.md index 3db632e3..8454af1a 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,12 @@ from a named group or existing parameters accepts a generator only when the parameters already have it. A non-zero `priv_len` is refused because wolfSSL takes the private key length from the group. +Key exchange refuses the X9.42 KDF (`X942KDF-ASN1`) because wolfCrypt has no +X9.42 KDF, so CMS with DH recipients is not supported. + ### ECC * ECDSA (signing, verification) -* ECDH (key exchange) +* ECDH (key exchange, optional X9.63 KDF as used by CMS) * Key generation * Curves: P-192, P-224, P-256, P-384, P-521 diff --git a/src/wp_aes_wrap.c b/src/wp_aes_wrap.c index 26a8e5ea..56f7e4f9 100644 --- a/src/wp_aes_wrap.c +++ b/src/wp_aes_wrap.c @@ -331,6 +331,7 @@ static int wp_aes_wrap_dinit(wp_AesWrapCtx *ctx, const unsigned char *key, * * @param [in] ctx AES wrap context object. * @param [out] out Buffer to hold encrypted/decrypted result. + * May be NULL to get the output length. * @param [out] outLen Length of encrypted/decrypted data in bytes. * @param [in] outSize Size of output buffer in bytes. * @param [in] in Data to encrypt/decrypt. @@ -355,6 +356,20 @@ static int wp_aes_wrap_update(wp_AesWrapCtx *ctx, unsigned char *out, if (ok && (inLen == 0)) { *outLen = 0; } + else if (ok && (out == NULL)) { + /* Size query. */ + if ((inLen < 2 * KEYWRAP_BLOCK_SIZE) || + ((inLen % KEYWRAP_BLOCK_SIZE) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH); + ok = 0; + } + else if (ctx->wrap) { + *outLen = inLen + KEYWRAP_BLOCK_SIZE; + } + else { + *outLen = inLen - KEYWRAP_BLOCK_SIZE; + } + } else if (ok) { int rc; word32 outSz = (word32)outSize; diff --git a/src/wp_dh_exch.c b/src/wp_dh_exch.c index cc655f32..6b11c474 100644 --- a/src/wp_dh_exch.c +++ b/src/wp_dh_exch.c @@ -31,12 +31,6 @@ #ifdef WP_HAVE_DH -/** No KDF applied to derived secret. */ -#define WP_KDF_NONE 0 -/** X9.63 KDF applied to derived secret. */ -#define WP_KDF_X963 1 - - /** * DH key exchange context. */ @@ -52,8 +46,7 @@ typedef struct wp_DhCtx { /** Pad secret output with zeros. */ int pad; - /** KDF type to apply to calculated secret. */ - int kdfType; + /* KDF parameters are kept for get/set parity; no DH KDF is applied. */ /** Digest to use with KDF. */ enum wc_HashType kdfMd; /** Name of digest to use with KDF. */ @@ -150,7 +143,6 @@ static wp_DhCtx* wp_dh_dupctx(wp_DhCtx* src) if (ok) { /* Copy remaining fields across. */ dst->pad = src->pad; - dst->kdfType = src->kdfType; dst->kdfMd = src->kdfMd; XMEMCPY(dst->kdfMdName, src->kdfMdName, sizeof(dst->kdfMdName)); dst->ukmLen = src->ukmLen; @@ -206,54 +198,6 @@ static int wp_dh_init(wp_DhCtx* ctx, wp_Dh* dh, const OSSL_PARAM params[]) return ok; } -/** - * Derive key from secret KDF to produce output. - * - * @param [in] ctx DH key exchange context object. - * @param [in] key Buffer to hold key derived from secret. - * @param [out] keyLen Length of derived key in bytes. - * @param [in] keySize Length of buffer in bytes. - * @param [in] sec Secret data. - * @param [in] secLen Length of secret data in bytes. - * @return 1 on success. - * @return 0 on failure. - */ -static int wp_dh_kdf_derive(wp_DhCtx* ctx, unsigned char* key, - size_t* keyLen, size_t keySize, unsigned char* sec, size_t secLen) -{ - int ok = 1; - - WOLFPROV_ENTER(WP_LOG_COMP_DH, "wp_dh_kdf_derive"); - - if (keySize < ctx->keyLen) { - ok = 0; - } - if (ok) { -#ifdef HAVE_X963_KDF - int rc; - /* TODO: support X9.42 KDF that includes ASN.1 encoding. */ - rc = wc_X963_KDF(ctx->kdfMd, sec, (word32)secLen, ctx->ukm, - (word32)ctx->ukmLen, key, (word32)ctx->keyLen); - if (rc != 0) { - WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_X963_KDF", rc); - ok = 0; - } - else { - *keyLen = ctx->keyLen; - } -#else - (void)key; - (void)keyLen; - (void)sec; - (void)secLen; - ok = 0; -#endif - } - - WOLFPROV_LEAVE(WP_LOG_COMP_DH, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); - return ok; -} - #if (LIBWOLFSSL_VERSION_HEX < 0x05009002) || defined(WOLFSSL_KCAPI_DH) || \ defined(HAVE_FIPS) /** @@ -398,8 +342,6 @@ static int wp_dh_derive_secret(wp_DhCtx* ctx, unsigned char* secret, /** * Derive a secret/key using DH key exchange. * - * Can put the DH secret through a KDF. - * * @param [in] ctx DH key exchange context object. * @param [out] secret Buffer to hold secret/key. * @param [out] secLen Length of secret/key data in bytes. @@ -411,10 +353,7 @@ static int wp_dh_derive(wp_DhCtx* ctx, unsigned char* secret, size_t* secLen, size_t secSize) { int ok = 1; - int done = 0; - unsigned char* out = NULL; size_t outLen = 0; - unsigned char* tmp = NULL; size_t maxLen = 0; WOLFPROV_ENTER(WP_LOG_COMP_DH, "wp_dh_derive"); @@ -425,59 +364,26 @@ static int wp_dh_derive(wp_DhCtx* ctx, unsigned char* secret, /* No output buffer, return maximum size only. */ if (ok && (secret == NULL)) { - if (ctx->kdfType == WP_KDF_NONE) { - *secLen = wp_dh_get_size(ctx->key); - } - else { - *secLen = ctx->keyLen; - } - done = 1; + *secLen = wp_dh_get_size(ctx->key); } - - if ((!done) && ok) { + else if (ok) { /* Calculate maximum secret length. */ maxLen = wp_dh_get_size(ctx->key); - if (ctx->kdfType == WP_KDF_NONE) { - /* Output of DH key exchange directly into caller's buffer. */ - if (secSize < maxLen) { - ok = 0; - } - else { - out = secret; - outLen = secSize; - } - } - else if (ctx->kdfType == WP_KDF_X963) { - /* Output of DH key exchange goes into temporary buffer. */ - tmp = OPENSSL_malloc(maxLen); - if (tmp == NULL) { - ok = 0; - } - if (ok) { - out = tmp; - outLen = maxLen; - } - } - } - - if ((!done) && ok && (out != NULL)) { - /* DH key exchange derivation using wolfSSL. */ - ok = wp_dh_derive_secret(ctx, out, &outLen, maxLen); - } - - if ((!done) && ok) { - if (ctx->kdfType == WP_KDF_X963) { - /* Put output through KDF using wolfSSL. */ - ok = wp_dh_kdf_derive(ctx, secret, secLen, secSize, out, outLen); + /* Output of DH key exchange directly into caller's buffer. */ + if (secSize < maxLen) { + ok = 0; } else { + outLen = secSize; + /* DH key exchange derivation using wolfSSL. */ + ok = wp_dh_derive_secret(ctx, secret, &outLen, maxLen); + } + if (ok) { *secLen = outLen; } } - OPENSSL_clear_free(tmp, maxLen); - WOLFPROV_LEAVE(WP_LOG_COMP_DH, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } @@ -547,9 +453,9 @@ static int wp_dh_set_peer(wp_DhCtx* ctx, wp_Dh* peer) } /** - * Set the KDF algorithm from the parameters. + * Check the KDF algorithm from the parameters. Only none is supported. * - * @param [in, out] ctx DH key exchange context object. + * @param [in] ctx DH key exchange context object. Unused. * @param [in] params Array of parameters and values. * @return 1 on success. * @return 0 on failure. @@ -566,18 +472,14 @@ static int wp_dh_set_param_kdf(wp_DhCtx* ctx, const OSSL_PARAM params[]) ok = 0; } if (ok && (kdf != NULL)) { - if (kdf[0] == '\0') { - ctx->kdfType = WP_KDF_NONE; - } - else if (XSTRCMP(kdf, OSSL_KDF_NAME_X942KDF_ASN1) == 0) { - /* Only support the non ASN1 variant. */ - ctx->kdfType = WP_KDF_X963; - } - else { + /* wolfCrypt has no X9.42 KDF. */ + if (kdf[0] != '\0') { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED); ok = 0; } } + (void)ctx; WOLFPROV_LEAVE(WP_LOG_COMP_DH, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } @@ -692,7 +594,7 @@ static const OSSL_PARAM* wp_dh_gettable_ctx_params(wp_DhCtx* ctx, /** * Get the KDF algorithm from the context and put into parameters. * - * @param [in] ctx DH key exchange context object. + * @param [in] ctx DH key exchange context object. Unused. * @param [in, out] params Array of parameters and values. * @return 1 on success. * @return 0 on failure. @@ -706,15 +608,12 @@ static int wp_dh_get_params_kdf(wp_DhCtx* ctx, OSSL_PARAM params[]) p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); if (p != NULL) { - const char* type = ""; - if (ctx->kdfType == WP_KDF_X963) { - type = OSSL_KDF_NAME_X942KDF_ASN1; - } - if (ok && (!OSSL_PARAM_set_utf8_string(p, type))) { + if (!OSSL_PARAM_set_utf8_string(p, "")) { ok = 0; } } + (void)ctx; WOLFPROV_LEAVE(WP_LOG_COMP_DH, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok); return ok; } diff --git a/src/wp_ecdh_exch.c b/src/wp_ecdh_exch.c index b4bb0188..ee0d6b0c 100644 --- a/src/wp_ecdh_exch.c +++ b/src/wp_ecdh_exch.c @@ -234,7 +234,7 @@ static int wp_ecdh_kdf_derive(wp_EcdhCtx* ctx, unsigned char* key, return 0; } - if (keySize < ctx->keyLen) { + if ((ctx->keyLen == 0) || (keySize < ctx->keyLen)) { ok = 0; } if (ok) { @@ -460,10 +460,11 @@ static int wp_ecdh_set_param_kdf(wp_EcdhCtx* ctx, const OSSL_PARAM params[]) if (kdf[0] == '\0') { ctx->kdfType = WP_KDF_NONE; } - else if (XSTRCMP(kdf, OSSL_KDF_NAME_X942KDF_ASN1) == 0) { - /* Only support the non ASN1 variant. */ +#ifdef HAVE_X963_KDF + else if (XSTRCMP(kdf, OSSL_KDF_NAME_X963KDF) == 0) { ctx->kdfType = WP_KDF_X963; } +#endif else { ok = 0; } @@ -604,7 +605,7 @@ static int wp_ecdh_get_params_kdf(wp_EcdhCtx* ctx, OSSL_PARAM params[]) if (p != NULL) { const char* type = ""; if (ctx->kdfType == WP_KDF_X963) { - type = OSSL_KDF_NAME_X942KDF_ASN1; + type = OSSL_KDF_NAME_X963KDF; } if (ok && (!OSSL_PARAM_set_utf8_string(p, type))) { ok = 0; diff --git a/test/test_dh.c b/test/test_dh.c index c851d3ea..955b940e 100644 --- a/test/test_dh.c +++ b/test/test_dh.c @@ -552,7 +552,10 @@ int test_dh_invalid_kdf_strings(void *data) (char *)"X", (char *)"X942", (char *)"X942KDF", - (char *)"X942KDF-AS" + (char *)"X942KDF-AS", + /* wolfCrypt has no X9.42 KDF. */ + (char *)OSSL_KDF_NAME_X942KDF_ASN1, + (char *)OSSL_KDF_NAME_X963KDF }; size_t i; @@ -1312,306 +1315,6 @@ int test_dh_derive_small_buffer(void *data) return err; } -#if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256) -/* Apply X9.63 KDF using OpenSSL's reference implementation. */ -static int test_dh_x963_kdf_ref(const unsigned char* secret, size_t secLen, - const char* mdName, const unsigned char* ukm, size_t ukmLen, - unsigned char* out, size_t outLen) -{ - int err = 0; - EVP_KDF *kdf = NULL; - EVP_KDF_CTX *kctx = NULL; - OSSL_PARAM params[4]; - OSSL_PARAM *p = params; - - kdf = EVP_KDF_fetch(osslLibCtx, OSSL_KDF_NAME_X963KDF, NULL); - err = kdf == NULL; - if (err == 0) { - kctx = EVP_KDF_CTX_new(kdf); - err = kctx == NULL; - } - if (err == 0) { - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, - (char*)mdName, 0); - *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, - (unsigned char*)secret, secLen); - if (ukm != NULL && ukmLen > 0) { - *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, - (unsigned char*)ukm, ukmLen); - } - *p = OSSL_PARAM_construct_end(); - - err = EVP_KDF_derive(kctx, out, outLen, params) <= 0; - } - - EVP_KDF_CTX_free(kctx); - EVP_KDF_free(kdf); - return err; -} - -/* Derive raw DH shared secret (no KDF) using wolfProvider. Caller frees. */ -static int test_dh_derive_raw(EVP_PKEY *key, EVP_PKEY *peerKey, - unsigned char **pSecret, size_t *pSecretLen) -{ - int err = 0; - EVP_PKEY_CTX *ctx = NULL; - unsigned char *secret = NULL; - size_t len = 0; - - ctx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, key, NULL); - err = ctx == NULL; - if (err == 0) { - err = EVP_PKEY_derive_init(ctx) <= 0; - } - if (err == 0) { - err = EVP_PKEY_derive_set_peer(ctx, peerKey) <= 0; - } - if (err == 0) { - err = EVP_PKEY_derive(ctx, NULL, &len) <= 0; - } - if (err == 0) { - secret = (unsigned char*)OPENSSL_malloc(len); - err = secret == NULL; - } - if (err == 0) { - err = EVP_PKEY_derive(ctx, secret, &len) <= 0; - } - if (err == 0) { - *pSecret = secret; - *pSecretLen = len; - secret = NULL; - } - - OPENSSL_free(secret); - EVP_PKEY_CTX_free(ctx); - return err; -} - -static int test_dh_derive_x963_raw(EVP_PKEY *key, EVP_PKEY *peerKey, - const char* mdName, size_t outLen, const unsigned char* ukm, size_t ukmLen, - unsigned char *out, size_t outBufLen, int *deriveRet, size_t *derivedLen) -{ - int err = 0; - EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[5]; - OSSL_PARAM *p = params; - - *deriveRet = 0; - *derivedLen = outBufLen; - - ctx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, key, NULL); - err = ctx == NULL; - if (err == 0) { - err = EVP_PKEY_derive_init(ctx) <= 0; - } - if (err == 0) { - err = EVP_PKEY_derive_set_peer(ctx, peerKey) <= 0; - } - if (err == 0) { - /* wolfProvider maps the X942KDF-ASN1 type string to its internal X963 - * KDF implementation (wc_X963_KDF). */ - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, - (char*)OSSL_KDF_NAME_X942KDF_ASN1, 0); - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, - (char*)mdName, 0); - *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, - &outLen); - if (ukm != NULL && ukmLen > 0) { - *p++ = OSSL_PARAM_construct_octet_string( - OSSL_EXCHANGE_PARAM_KDF_UKM, (unsigned char*)ukm, ukmLen); - } - *p = OSSL_PARAM_construct_end(); - - err = EVP_PKEY_CTX_set_params(ctx, params) != 1; - } - if (err == 0) { - *deriveRet = EVP_PKEY_derive(ctx, out, derivedLen); - } - - EVP_PKEY_CTX_free(ctx); - return err; -} - -/* Derive via wolfProvider with X9.63 KDF parameters set, requiring success and - * exactly outLen bytes of output. */ -static int test_dh_derive_with_x963(EVP_PKEY *key, EVP_PKEY *peerKey, - const char* mdName, size_t outLen, const unsigned char* ukm, size_t ukmLen, - unsigned char *out, size_t outBufLen) -{ - int err = 0; - int deriveRet = 0; - size_t derivedLen = 0; - - err = test_dh_derive_x963_raw(key, peerKey, mdName, outLen, ukm, ukmLen, - out, outBufLen, &deriveRet, &derivedLen); - if (err == 0 && deriveRet <= 0) { - err = 1; - } - if (err == 0 && derivedLen != outLen) { - PRINT_ERR_MSG("KDF output length %zu != requested %zu", derivedLen, - outLen); - err = 1; - } - - return err; -} - -/** - * Test DH key derivation through the X9.63 KDF path. - * - * The provider's WP_KDF_X963 branch in wp_dh_derive (1) allocates a temporary - * buffer sized to the prime length, (2) runs the raw DH agreement into it, - * (3) feeds the result through wc_X963_KDF, and (4) securely frees the - * temporary. We validate by computing the same KDF output independently via - * OpenSSL's X963KDF and comparing. - */ -int test_dh_x963_kdf(void *data) -{ - int err = 0; - DH *dh = NULL; - EVP_PKEY *params = NULL; - BIGNUM *p = NULL; - BIGNUM *g = NULL; - EVP_PKEY_CTX *kgCtx = NULL; - EVP_PKEY *keyA = NULL; - EVP_PKEY *keyB = NULL; - unsigned char *raw = NULL; - size_t rawLen = 0; - unsigned char wpOut[96]; - unsigned char refOut[96]; - unsigned char tooSmallBuf[8]; - size_t tooSmallLen; - static const unsigned char ukm[] = { - 0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18, - 0x29, 0x3a, 0x4b, 0x5c, 0x6d, 0x7e, 0x8f, 0x90 - }; - static const struct { - const char* md; - size_t outLen; - int withUkm; - } cases[] = { - { "SHA256", 16, 0 }, - { "SHA256", 32, 0 }, - { "SHA256", 48, 0 }, - { "SHA256", 64, 0 }, - { "SHA256", 32, 1 }, - }; - size_t i; - - (void)data; - - PRINT_MSG("DH X9.63 KDF derivation"); - - dh = DH_new(); - err = dh == NULL; - if (err == 0) { - p = BN_bin2bn(dh_p, sizeof(dh_p), NULL); - err = p == NULL; - } - if (err == 0) { - g = BN_bin2bn(dh_g, sizeof(dh_g), NULL); - err = g == NULL; - } - if (err == 0) { - err = DH_set0_pqg(dh, p, NULL, g) == 0; - if (err == 0) { - p = NULL; - g = NULL; - } - } - if (err == 0) { - params = EVP_PKEY_new(); - err = params == NULL; - } - if (err == 0) { - err = EVP_PKEY_set1_DH(params, dh) != 1; - } - - /* Generate one fresh key pair for both ends. */ - if (err == 0) { - kgCtx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, params, NULL); - err = kgCtx == NULL; - } - if (err == 0) { - err = EVP_PKEY_keygen_init(kgCtx) != 1; - } - if (err == 0) { - err = EVP_PKEY_keygen(kgCtx, &keyA) != 1; - } - if (err == 0) { - err = EVP_PKEY_keygen(kgCtx, &keyB) != 1; - } - - /* Snapshot the raw DH shared secret once - it's the same input the KDF - * branch feeds to wc_X963_KDF regardless of requested output length. */ - if (err == 0) { - err = test_dh_derive_raw(keyA, keyB, &raw, &rawLen); - } - - for (i = 0; (err == 0) && (i < sizeof(cases) / sizeof(cases[0])); ++i) { - const unsigned char *ukmPtr = cases[i].withUkm ? ukm : NULL; - size_t ukmLen = cases[i].withUkm ? sizeof(ukm) : 0; - - memset(wpOut, 0, sizeof(wpOut)); - memset(refOut, 0, sizeof(refOut)); - - err = test_dh_derive_with_x963(keyA, keyB, cases[i].md, - cases[i].outLen, ukmPtr, ukmLen, wpOut, sizeof(wpOut)); - if (err == 0) { - err = test_dh_x963_kdf_ref(raw, rawLen, cases[i].md, ukmPtr, - ukmLen, refOut, cases[i].outLen); - } - if (err == 0 && memcmp(wpOut, refOut, cases[i].outLen) != 0) { - PRINT_ERR_MSG("X9.63 KDF output mismatch (md=%s outLen=%zu ukm=%d)", - cases[i].md, cases[i].outLen, cases[i].withUkm); - PRINT_BUFFER("wolfProvider", wpOut, cases[i].outLen); - PRINT_BUFFER("OpenSSL X963KDF", refOut, cases[i].outLen); - err = 1; - } - /* No bytes beyond the requested length should have been written. */ - if (err == 0) { - size_t j; - for (j = cases[i].outLen; j < sizeof(wpOut); ++j) { - if (wpOut[j] != 0) { - PRINT_ERR_MSG("KDF wrote past requested length at byte %zu", - j); - err = 1; - break; - } - } - } - } - - /* Failure mode: caller's buffer smaller than the requested KDF output. - * The derive itself must fail rather than truncate or overflow, so assert - * on the raw EVP_PKEY_derive() result. */ - if (err == 0) { - int deriveRet = 1; - size_t derivedLen = 0; - - tooSmallLen = sizeof(tooSmallBuf); - err = test_dh_derive_x963_raw(keyA, keyB, "SHA256", 32, NULL, 0, - tooSmallBuf, tooSmallLen, &deriveRet, &derivedLen); - if (err == 0 && deriveRet > 0) { - PRINT_ERR_MSG("DH X963 KDF derive accepted under-sized buffer " - "(ret=%d, len=%zu)", deriveRet, derivedLen); - err = 1; - } - } - - OPENSSL_free(raw); - EVP_PKEY_CTX_free(kgCtx); - EVP_PKEY_free(keyA); - EVP_PKEY_free(keyB); - EVP_PKEY_free(params); - BN_free(p); - BN_free(g); - DH_free(dh); - - return err; -} -#endif /* HAVE_X963_KDF && WP_HAVE_SHA256 */ - /* Pass an oversize OSSL_PARAM (>1024 bytes) for the FFC P component to * EVP_PKEY_fromdata so wp_mp_read_unsigned_bin_le (wp_params.c) hits its * size-guard branch via wp_dh_import_group. Expect failure rather than the diff --git a/test/test_ecc.c b/test/test_ecc.c index 87003a73..31eaf28c 100644 --- a/test/test_ecc.c +++ b/test/test_ecc.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include @@ -1064,7 +1066,11 @@ int test_ecdh_invalid_kdf_strings(void *data) (char *)"X", (char *)"X942", (char *)"X942KDF", - (char *)"X942KDF-AS" + (char *)"X942KDF-AS", + (char *)OSSL_KDF_NAME_X942KDF_ASN1, +#ifndef HAVE_X963_KDF + (char *)OSSL_KDF_NAME_X963KDF, +#endif }; size_t i; @@ -1264,56 +1270,18 @@ int test_ecdh_invalid_curve_peer(void *data) #endif /* USE_ECC_B_PARAM */ #if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256) -/* Apply X9.63 KDF using OpenSSL's reference implementation so we can compare - * against wolfProvider's output. */ -static int test_ecdh_x963_kdf_ref(const unsigned char* secret, size_t secLen, - const char* mdName, const unsigned char* ukm, size_t ukmLen, - unsigned char* out, size_t outLen) -{ - int err = 0; - EVP_KDF *kdf = NULL; - EVP_KDF_CTX *kctx = NULL; - OSSL_PARAM params[4]; - OSSL_PARAM *p = params; - - kdf = EVP_KDF_fetch(osslLibCtx, OSSL_KDF_NAME_X963KDF, NULL); - err = kdf == NULL; - if (err == 0) { - kctx = EVP_KDF_CTX_new(kdf); - err = kctx == NULL; - } - if (err == 0) { - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, - (char*)mdName, 0); - *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, - (unsigned char*)secret, secLen); - if (ukm != NULL && ukmLen > 0) { - *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, - (unsigned char*)ukm, ukmLen); - } - *p = OSSL_PARAM_construct_end(); - - err = EVP_KDF_derive(kctx, out, outLen, params) <= 0; - } - - EVP_KDF_CTX_free(kctx); - EVP_KDF_free(kdf); - return err; -} - -/* Run wolfProvider's ECDH derive with X9.63 KDF parameters set, returning the - * KDF output. */ -static int test_ecdh_derive_with_x963(EVP_PKEY *key, EVP_PKEY *peerKey, - const char* mdName, size_t outLen, const unsigned char* ukm, size_t ukmLen, - unsigned char *out) +/* ECDH derive with X9.63 KDF set the way CMS does (cms_ec.c). */ +static int test_ecdh_derive_with_x963(OSSL_LIB_CTX* libCtx, EVP_PKEY *key, + EVP_PKEY *peerKey, const char* mdName, size_t outLen, + const unsigned char* ukm, size_t ukmLen, unsigned char *out) { int err = 0; EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[5]; + OSSL_PARAM params[4]; OSSL_PARAM *p = params; size_t derivedLen = outLen; - ctx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, key, NULL); + ctx = EVP_PKEY_CTX_new_from_pkey(libCtx, key, NULL); err = ctx == NULL; if (err == 0) { err = EVP_PKEY_derive_init(ctx) != 1; @@ -1322,10 +1290,10 @@ static int test_ecdh_derive_with_x963(EVP_PKEY *key, EVP_PKEY *peerKey, err = EVP_PKEY_derive_set_peer(ctx, peerKey) != 1; } if (err == 0) { - /* wolfProvider currently maps the X942KDF-ASN1 type string to its - * internal X963 KDF implementation (wc_X963_KDF). */ - *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, - (char*)OSSL_KDF_NAME_X942KDF_ASN1, 0); + err = EVP_PKEY_CTX_set_ecdh_kdf_type(ctx, + EVP_PKEY_ECDH_KDF_X9_63) != 1; + } + if (err == 0) { *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, (char*)mdName, 0); *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, @@ -1338,6 +1306,13 @@ static int test_ecdh_derive_with_x963(EVP_PKEY *key, EVP_PKEY *peerKey, err = EVP_PKEY_CTX_set_params(ctx, params) != 1; } + if (err == 0) { + err = EVP_PKEY_CTX_get_ecdh_kdf_type(ctx) != EVP_PKEY_ECDH_KDF_X9_63; + } + if (err == 0) { + err = (EVP_PKEY_derive(ctx, NULL, &derivedLen) != 1) || + (derivedLen != outLen); + } if (err == 0) { err = EVP_PKEY_derive(ctx, out, &derivedLen) != 1; } @@ -1356,6 +1331,8 @@ int test_ecdh_x963_kdf(void *data) int err = 0; EVP_PKEY *keyA = NULL; EVP_PKEY *keyB = NULL; + EVP_PKEY *osslKeyA = NULL; + EVP_PKEY *osslKeyB = NULL; const unsigned char *p; unsigned char wpOut[64]; unsigned char refOut[64]; @@ -1392,6 +1369,18 @@ int test_ecdh_x963_kdf(void *data) sizeof(ecc_peerkey_der_256), wpLibCtx, NULL); err = keyB == NULL; } + if (err == 0) { + p = ecc_key_der_256; + osslKeyA = d2i_PrivateKey_ex(EVP_PKEY_EC, NULL, &p, + sizeof(ecc_key_der_256), osslLibCtx, NULL); + err = osslKeyA == NULL; + } + if (err == 0) { + p = ecc_peerkey_der_256; + osslKeyB = d2i_PrivateKey_ex(EVP_PKEY_EC, NULL, &p, + sizeof(ecc_peerkey_der_256), osslLibCtx, NULL); + err = osslKeyB == NULL; + } for (i = 0; (err == 0) && (i < sizeof(cases) / sizeof(cases[0])); ++i) { const unsigned char *ukmPtr = cases[i].withUkm ? ukm : NULL; @@ -1400,18 +1389,18 @@ int test_ecdh_x963_kdf(void *data) memset(wpOut, 0, sizeof(wpOut)); memset(refOut, 0, sizeof(refOut)); - err = test_ecdh_derive_with_x963(keyA, keyB, cases[i].md, + err = test_ecdh_derive_with_x963(wpLibCtx, keyA, keyB, cases[i].md, cases[i].outLen, ukmPtr, ukmLen, wpOut); if (err == 0) { - err = test_ecdh_x963_kdf_ref(ecc_derived_256, - sizeof(ecc_derived_256), cases[i].md, ukmPtr, ukmLen, refOut, - cases[i].outLen); + err = test_ecdh_derive_with_x963(osslLibCtx, osslKeyA, + osslKeyB, cases[i].md, cases[i].outLen, ukmPtr, ukmLen, + refOut); } if (err == 0 && memcmp(wpOut, refOut, cases[i].outLen) != 0) { PRINT_ERR_MSG("X9.63 KDF output mismatch (md=%s outLen=%zu ukm=%d)", cases[i].md, cases[i].outLen, cases[i].withUkm); PRINT_BUFFER("wolfProvider", wpOut, cases[i].outLen); - PRINT_BUFFER("OpenSSL X963KDF", refOut, cases[i].outLen); + PRINT_BUFFER("OpenSSL", refOut, cases[i].outLen); err = 1; } /* Any tail bytes beyond outLen must remain untouched - guards against @@ -1431,10 +1420,98 @@ int test_ecdh_x963_kdf(void *data) EVP_PKEY_free(keyA); EVP_PKEY_free(keyB); + EVP_PKEY_free(osslKeyA); + EVP_PKEY_free(osslKeyB); return err; } #endif /* HAVE_X963_KDF && WP_HAVE_SHA256 */ + +#if defined(HAVE_X963_KDF) && defined(HAVE_AES_KEYWRAP) && \ + defined(WP_HAVE_AESCBC) && defined(WP_HAVE_SHA1) && !defined(OPENSSL_NO_CMS) +/* CMS EC recipient (cms_kari.c): encrypt in encCtx, decrypt in decCtx. */ +static int test_ecdh_cms_kari_dir(OSSL_LIB_CTX *encCtx, OSSL_LIB_CTX *decCtx) +{ + static const char msg[] = "CMS EC key agreement"; + int err; + const unsigned char *p = ecc_key_der_256; + X509 *cert = NULL; + EVP_PKEY *key = NULL; + STACK_OF(X509) *recips = NULL; + EVP_CIPHER *cipher = NULL; + CMS_ContentInfo *cms = NULL; + CMS_ContentInfo *dec = NULL; + BIO *in = NULL; + BIO *der = NULL; + BIO *out = NULL; + char *outData = NULL; + + in = BIO_new_file(CERTS_DIR "/server-ecc.pem", "r"); + cert = X509_new_ex(encCtx, NULL); + err = (in == NULL) || (cert == NULL) || + (PEM_read_bio_X509(in, &cert, NULL, NULL) == NULL); + BIO_free(in); + in = NULL; + if (err == 0) { + recips = sk_X509_new_null(); + err = (recips == NULL) || (sk_X509_push(recips, cert) != 1); + } + if (err == 0) { + cipher = EVP_CIPHER_fetch(encCtx, "AES-128-CBC", NULL); + in = BIO_new_mem_buf(msg, sizeof(msg) - 1); + der = BIO_new(BIO_s_mem()); + out = BIO_new(BIO_s_mem()); + err = (cipher == NULL) || (in == NULL) || (der == NULL) || + (out == NULL); + } + if (err == 0) { + cms = CMS_encrypt_ex(recips, in, cipher, CMS_BINARY, encCtx, NULL); + err = (cms == NULL) || (i2d_CMS_bio(der, cms) != 1); + } + if (err == 0) { + key = d2i_PrivateKey_ex(EVP_PKEY_EC, NULL, &p, + sizeof(ecc_key_der_256), decCtx, NULL); + dec = CMS_ContentInfo_new_ex(decCtx, NULL); + err = (key == NULL) || (dec == NULL) || + (d2i_CMS_bio(der, &dec) == NULL); + } + if (err == 0) { + err = CMS_decrypt(dec, key, NULL, NULL, out, CMS_BINARY) != 1; + } + if (err == 0) { + err = (BIO_get_mem_data(out, &outData) != (long)(sizeof(msg) - 1)) || + (memcmp(outData, msg, sizeof(msg) - 1) != 0); + } + + BIO_free(out); + BIO_free(der); + BIO_free(in); + CMS_ContentInfo_free(dec); + CMS_ContentInfo_free(cms); + EVP_CIPHER_free(cipher); + sk_X509_free(recips); + EVP_PKEY_free(key); + X509_free(cert); + + return err; +} + +int test_ecdh_cms_kari(void *data) +{ + int err; + + (void)data; + + PRINT_MSG("CMS EC recipient: wolfProvider encrypt, OpenSSL decrypt"); + err = test_ecdh_cms_kari_dir(wpLibCtx, osslLibCtx); + if (err == 0) { + PRINT_MSG("CMS EC recipient: OpenSSL encrypt, wolfProvider decrypt"); + err = test_ecdh_cms_kari_dir(osslLibCtx, wpLibCtx); + } + + return err; +} +#endif /* HAVE_X963_KDF && HAVE_AES_KEYWRAP && WP_HAVE_AESCBC ... */ #endif /* WP_HAVE_EC_P256 */ #ifdef WP_HAVE_EC_P192 diff --git a/test/unit.c b/test/unit.c index be7f72a2..e6ec3457 100644 --- a/test/unit.c +++ b/test/unit.c @@ -374,9 +374,6 @@ TEST_CASE test_case[] = { TEST_DECL(test_dh_krb5_keygen, NULL), TEST_DECL(test_dh_pad, NULL), TEST_DECL(test_dh_derive_small_buffer, NULL), -#if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256) - TEST_DECL(test_dh_x963_kdf, NULL), -#endif TEST_DECL(test_dh_fromdata_oversize, NULL), TEST_DECL(test_dh_param_check_explicit, NULL), TEST_DECL(test_dh_import_group_no_nul, NULL), @@ -485,6 +482,11 @@ TEST_CASE test_case[] = { #if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256) TEST_DECL(test_ecdh_x963_kdf, NULL), #endif + #if defined(HAVE_X963_KDF) && defined(HAVE_AES_KEYWRAP) && \ + defined(WP_HAVE_AESCBC) && defined(WP_HAVE_SHA1) && \ + !defined(OPENSSL_NO_CMS) + TEST_DECL(test_ecdh_cms_kari, NULL), + #endif #endif #ifdef WP_HAVE_ECDSA TEST_DECL(test_ecdsa_p256_pkey, NULL), diff --git a/test/unit.h b/test/unit.h index 35bc245b..e25ccce4 100644 --- a/test/unit.h +++ b/test/unit.h @@ -437,9 +437,6 @@ int test_dh_encoder_import_object(void *data); int test_dh_krb5_keygen(void *data); int test_dh_pad(void *data); int test_dh_derive_small_buffer(void *data); -#if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256) -int test_dh_x963_kdf(void *data); -#endif int test_dh_fromdata_oversize(void *data); int test_dh_param_check_explicit(void *data); int test_dh_import_group_no_nul(void *data); @@ -545,6 +542,10 @@ int test_ecdh_p256(void *data); #if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256) int test_ecdh_x963_kdf(void *data); #endif +#if defined(HAVE_X963_KDF) && defined(HAVE_AES_KEYWRAP) && \ + defined(WP_HAVE_AESCBC) && defined(WP_HAVE_SHA1) && !defined(OPENSSL_NO_CMS) +int test_ecdh_cms_kari(void *data); +#endif #endif /* WP_HAVE_EC_P256 */ #ifdef WP_HAVE_EC_P384 int test_ecdh_p384(void *data);