diff --git a/configure.ac b/configure.ac index 42918ab71..2cc0525e7 100644 --- a/configure.ac +++ b/configure.ac @@ -2075,19 +2075,21 @@ then AS_IF([test "$enable_kcapi_hash" != "no"], [ENABLED_KCAPI_HASH=yes]) AS_IF([test "$enable_kcapi_hmac" != "no"], [ENABLED_KCAPI_HMAC=yes]) AS_IF([test "$enable_kcapi_aes" != "no"], [ENABLED_KCAPI_AES=yes]) -# currently the PK alg KCAPI options run into build failures, so disabling here for now. -# AS_IF([test "$enable_kcapi_rsa" != "no"], [ENABLED_KCAPI_RSA=yes]) -# AS_IF([test "$enable_kcapi_dh" != "no"], [ENABLED_KCAPI_DH=yes]) -# AS_IF([test "$enable_kcapi_ecc" != "no"], [ENABLED_KCAPI_ECC=yes]) + AS_IF([test "$enable_kcapi_rsa" != "no"], [ENABLED_KCAPI_RSA=yes]) + AS_IF([test "$enable_kcapi_dh" != "no"], [ENABLED_KCAPI_DH=yes]) + AS_IF([test "$enable_kcapi_ecc" != "no"], [ENABLED_KCAPI_ECC=yes]) fi -AS_IF([test "$ENABLED_KCAPI_HASH" != "no" || - test "$ENABLED_KCAPI_HMAC" != "no" || - test "$ENABLED_KCAPI_AES" != "no" || - test "$ENABLED_KCAPI_RSA" != "no" || - test "$ENABLED_KCAPI_DH" != "no" || - test "$ENABLED_KCAPI_ECC" != "no"], - [LIBS="$LIBS -lkcapi"]) +if test "$ENABLED_KCAPI_HASH" != "no" || + test "$ENABLED_KCAPI_HMAC" != "no" || + test "$ENABLED_KCAPI_AES" != "no" || + test "$ENABLED_KCAPI_RSA" != "no" || + test "$ENABLED_KCAPI_DH" != "no" || + test "$ENABLED_KCAPI_ECC" != "no" +then + LIBS="$LIBS -lkcapi" + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KCAPI" +fi if test "$ENABLED_KCAPI_HASH" = "yes" then @@ -3594,16 +3596,10 @@ AS_CASE([$FIPS_VERSION], -DHAVE_FIPS_VERSION_MINOR=$HAVE_FIPS_VERSION_MINOR \ -DHAVE_ECC_CDH \ -DWC_RSA_NO_PADDING \ - -DWOLFSSL_ECDSA_SET_K \ -DWOLFSSL_VALIDATE_ECC_IMPORT \ -DECC_USER_CURVES \ - -DHAVE_ECC192 \ - -DHAVE_ECC224 \ - -DHAVE_ECC256 \ -DHAVE_ECC384 \ -DHAVE_ECC521 \ - -DWOLFSSL_ECDSA_SET_K \ - -DWC_RNG_SEED_CB \ -DWOLFSSL_VALIDATE_FFC_IMPORT \ -DHAVE_FFDHE_Q \ -DHAVE_FFDHE_3072 \ @@ -3611,6 +3607,15 @@ AS_CASE([$FIPS_VERSION], -DHAVE_FFDHE_6144 \ -DHAVE_FFDHE_8192" + # KCAPI API does not support custom k for sign, don't force enable ECC key sizes and do not use seed callback + AS_IF([test "x$ENABLED_KCAPI_ECC" = "xno"], + [AM_CFLAGS="$AM_CFLAGS \ + -DWC_RNG_SEED_CB \ + -DWOLFSSL_ECDSA_SET_K \ + -DHAVE_ECC192 \ + -DHAVE_ECC224 \ + -DHAVE_ECC256"]) + DEFAULT_MAX_CLASSIC_ASYM_KEY_BITS=8192 # force various features to FIPS 140-3 defaults, unless overridden with v5-dev: @@ -3883,8 +3888,8 @@ if test "x$ENABLED_HASHDRBG" = "xyes" then AM_CFLAGS="$AM_CFLAGS -DHAVE_HASHDRBG" else - # turn on Hash DRBG if FIPS is on - if test "x$ENABLED_FIPS" = "xyes" + # turn on Hash DRBG if FIPS is on (don't force on for KCAPI) + if test "x$ENABLED_FIPS" = "xyes" && test "x$ENABLED_KCAPI" = "xno" then AM_CFLAGS="$AM_CFLAGS -DHAVE_HASHDRBG" ENABLED_HASHDRBG=yes diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index fe44ca04c..d772cbec3 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -1250,12 +1250,14 @@ static int wc_ecc_export_x963_compressed(ecc_key* key, byte* out, word32* outLen #if !defined(WOLFSSL_SP_MATH) && \ !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \ - !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SE050) + !defined(WOLFSSL_CRYPTOCELL) && !defined(WOLFSSL_SILABS_SE_ACCEL) && \ + !defined(WOLFSSL_SE050) static int ecc_check_pubkey_order(ecc_key* key, ecc_point* pubkey, mp_int* a, mp_int* prime, mp_int* order); #endif static int _ecc_validate_public_key(ecc_key* key, int partial, int priv); -#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN) +#if (FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN)) && \ + !defined(WOLFSSL_KCAPI_ECC) static int _ecc_pairwise_consistency_test(ecc_key* key, WC_RNG* rng); #endif @@ -5198,7 +5200,8 @@ int wc_ecc_make_key_ex2(WC_RNG* rng, int keysize, ecc_key* key, int curve_id, err = _ecc_make_key_ex(rng, keysize, key, curve_id, flags); -#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN) +#if (FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN)) && \ + !defined(WOLFSSL_KCAPI_ECC) if (err == MP_OKAY) { err = _ecc_validate_public_key(key, 0, 0); } @@ -5572,7 +5575,7 @@ static int wc_ecc_sign_hash_hw(const byte* in, word32 inlen, return err; } #elif defined(WOLFSSL_KCAPI_ECC) - err = KcapiEcc_Sign(key, in, inlen, out, outlen); + err = KcapiEcc_Sign(key, in, inlen, out, *outlen); if (err != MP_OKAY) { return err; } @@ -8604,10 +8607,6 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime) int err; ecc_point* base = NULL; ecc_point* res = NULL; -#ifdef WOLFSSL_KCAPI_ECC - ecc_key pubKey; - word32 len; -#endif #ifdef WOLFSSL_NO_MALLOC ecc_point lcl_base; ecc_point lcl_res; @@ -8672,7 +8671,29 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime) if (err == MP_OKAY) err = mp_set(base->z, 1); -#ifndef WOLFSSL_KCAPI_ECC +#ifdef WOLFSSL_KCAPI_ECC + if (err == MP_OKAY) { + word32 pubkey_sz = (word32)key->dp->size*2; + if (key->handle == NULL) { + /* if handle loaded, then pubkey_raw already populated */ + err = KcapiEcc_LoadKey(key, key->pubkey_raw, &pubkey_sz, 1); + } + if (err == 0) { + err = mp_read_unsigned_bin(res->x, key->pubkey_raw, + pubkey_sz/2); + } + if (err == MP_OKAY) { + err = mp_read_unsigned_bin(res->y, + key->pubkey_raw + pubkey_sz/2, + pubkey_sz/2); + } + if (err == MP_OKAY) { + err = mp_set(res->z, 1); + } + } + (void)a; + (void)prime; +#else #ifdef ECC_TIMING_RESISTANT if (err == MP_OKAY) err = wc_ecc_mulmod_ex2(&key->k, base, res, a, prime, curve->order, @@ -8682,29 +8703,10 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime) err = wc_ecc_mulmod_ex2(&key->k, base, res, a, prime, curve->order, NULL, 1, key->heap); #endif -#else - /* Using Shared Secret to perform scalar multiplication - * Calculates the x ordinidate only. - */ - if (err == MP_OKAY) { - err = wc_ecc_init(&pubKey); - if (err == MP_OKAY) { - wc_ecc_copy_point(base, &pubKey.pubkey); - } - if (err == MP_OKAY) { - err = KcapiEcc_SetPubKey(&pubKey); - } - if (err == MP_OKAY) { - len = key->dp->size; - err = KcapiEcc_SharedSecret(key, &pubKey, pubKey.pubkey_raw, - &len); - } - } -#endif /* !WOLFSSL_KCAPI_ECC */ +#endif /* WOLFSSL_KCAPI_ECC */ } if (err == MP_OKAY) { -#ifndef WOLFSSL_KCAPI_ECC /* compare result to public key */ if (mp_cmp(res->x, key->pubkey.x) != MP_EQ || mp_cmp(res->y, key->pubkey.y) != MP_EQ || @@ -8712,17 +8714,6 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime) /* didn't match */ err = ECC_PRIV_KEY_E; } -#else - err = mp_read_unsigned_bin(pubKey.pubkey.x, pubKey.pubkey_raw, len); - /* compare result to public key - y can be negative! */ - if (err == MP_OKAY && ((!mp_isone(res->z)) || - mp_cmp(pubKey.pubkey.x, key->pubkey.x) != MP_EQ)) { - /* didn't match */ - err = ECC_PRIV_KEY_E; - } - (void)a; - (void)prime; -#endif } wc_ecc_curve_free(curve); @@ -8735,7 +8726,8 @@ static int ecc_check_privkey_gen(ecc_key* key, mp_int* a, mp_int* prime) #endif /* FIPS_VERSION_GE(5,0) || WOLFSSL_VALIDATE_ECC_KEYGEN || * (!WOLFSSL_SP_MATH && WOLFSSL_VALIDATE_ECC_IMPORT) */ -#if FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN) +#if (FIPS_VERSION_GE(5,0) || defined(WOLFSSL_VALIDATE_ECC_KEYGEN)) && \ + !defined(WOLFSSL_KCAPI_ECC) /* check privkey generator helper, creates prime needed */ static int ecc_check_privkey_gen_helper(ecc_key* key) @@ -8754,6 +8746,9 @@ static int ecc_check_privkey_gen_helper(ecc_key* key) #elif defined(WOLFSSL_SILABS_SE_ACCEL) /* Hardware based private key, so this operation is not supported */ err = MP_OKAY; /* just report success */ +#elif defined(WOLFSSL_KCAPI_ECC) + /* Hardware based private key, so this operation is not supported */ + err = MP_OKAY; /* just report success */ #else err = MP_OKAY; ALLOC_CURVE_SPECS(2, err); @@ -8836,7 +8831,7 @@ static int _ecc_pairwise_consistency_test(ecc_key* key, WC_RNG* rng) return err; } -#endif /* FIPS v5 or later || WOLFSSL_VALIDATE_ECC_KEYGEN */ +#endif /* (FIPS v5 or later || WOLFSSL_VALIDATE_ECC_KEYGEN) &&!WOLFSSL_KCAPI_ECC */ #ifndef WOLFSSL_SP_MATH /* validate order * pubkey = point at infinity, 0 on success */ @@ -9096,7 +9091,11 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv) /* private keys must be in the range [1, n-1] */ if ((err == MP_OKAY) && (key->type == ECC_PRIVATEKEY) && (mp_iszero(&key->k) || mp_isneg(&key->k) || - (mp_cmp(&key->k, curve->order) != MP_LT))) { + (mp_cmp(&key->k, curve->order) != MP_LT)) + #ifdef WOLFSSL_KCAPI_ECC + && key->handle == NULL + #endif + ) { err = ECC_PRIV_KEY_E; } @@ -9118,7 +9117,7 @@ static int _ecc_validate_public_key(ecc_key* key, int partial, int priv) #endif FREE_CURVE_SPECS(); -#endif /* WOLFSSL_ATECC508A */ +#endif /* HW Based Crypto */ #else err = WC_KEY_SIZE_E; #endif /* !WOLFSSL_SP_MATH */ @@ -9204,14 +9203,14 @@ int wc_ecc_import_x963_ex(const byte* in, word32 inLen, ecc_key* key, #if defined(WOLFSSL_ATECC508A) || defined(WOLFSSL_ATECC608A) /* For SECP256R1 only save raw public key for hardware */ - if (curve_id == ECC_SECP256R1 && inLen <= sizeof(key->pubkey_raw)) { + if (curve_id == ECC_SECP256R1 && inLen <= (word32)sizeof(key->pubkey_raw)) { #ifdef HAVE_COMP_KEY if (!compressed) #endif XMEMCPY(key->pubkey_raw, (byte*)in, inLen); } #elif defined(WOLFSSL_KCAPI_ECC) - XMEMCPY(key->pubkey_raw + KCAPI_PARAM_SZ, (byte*)in, inLen); + XMEMCPY(key->pubkey_raw, (byte*)in, inLen); #endif if (err == MP_OKAY) { @@ -9884,11 +9883,11 @@ static int wc_ecc_import_raw_private(ecc_key* key, const char* qx, #elif defined(WOLFSSL_KCAPI_ECC) if (err == MP_OKAY) { word32 keySz = key->dp->size; - err = wc_export_int(key->pubkey.x, key->pubkey_raw + KCAPI_PARAM_SZ, + err = wc_export_int(key->pubkey.x, key->pubkey_raw, &keySz, keySz, WC_TYPE_UNSIGNED_BIN); if (err == MP_OKAY) { err = wc_export_int(key->pubkey.y, - &key->pubkey_raw[KCAPI_PARAM_SZ + keySz], &keySz, keySz, + &key->pubkey_raw[keySz], &keySz, keySz, WC_TYPE_UNSIGNED_BIN); } } diff --git a/wolfcrypt/src/port/kcapi/kcapi_aes.c b/wolfcrypt/src/port/kcapi/kcapi_aes.c index e29099304..4a11a306f 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_aes.c +++ b/wolfcrypt/src/port/kcapi/kcapi_aes.c @@ -234,8 +234,8 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, { int ret = 0; byte* data = NULL; - word32 dataSz = authInSz + sz + authTagSz; - ssize_t rc; + word32 dataSz; + int inbuflen = 0, outbuflen = 0; size_t pageSz = (size_t)sysconf(_SC_PAGESIZE); /* argument checks */ @@ -255,16 +255,22 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, } if (ret == 0) { - ret = posix_memalign((void*)&data, pageSz, dataSz); - if (ret < 0) { - ret = MEMORY_E; + ret = kcapi_aead_init(&aes->handle, WC_NAME_AESGCM, 0); + if (ret != 0) { + WOLFSSL_MSG("Error with first time setup of kcapi"); } } if (ret == 0) { - ret = kcapi_aead_init(&aes->handle, WC_NAME_AESGCM, 0); - if (ret != 0) { - WOLFSSL_MSG("Error with first time setup of kcapi"); + inbuflen = (int)kcapi_aead_inbuflen_enc( aes->handle, sz, authInSz, + authTagSz); + outbuflen = (int)kcapi_aead_outbuflen_enc(aes->handle, sz, authInSz, + authTagSz); + dataSz = (inbuflen > outbuflen) ? inbuflen : outbuflen; + + ret = posix_memalign((void*)&data, pageSz, dataSz); + if (ret < 0) { + ret = MEMORY_E; } } @@ -287,16 +293,18 @@ int wc_AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, XMEMCPY(data, authIn, authInSz); XMEMCPY(data + authInSz, in, sz); - rc = kcapi_aead_encrypt(aes->handle, data, dataSz, iv, data, dataSz, - KCAPI_ACCESS_HEURISTIC); - if (rc < 0) { + ret = (int)kcapi_aead_encrypt(aes->handle, data, inbuflen, iv, data, + outbuflen, KCAPI_ACCESS_HEURISTIC); + if (ret < 0) { WOLFSSL_MSG("GcmEncrypt failed"); - ret = (int)rc; } - else if ((word32)rc != dataSz) { + else if (ret != outbuflen) { WOLFSSL_MSG("GcmEncrypt produced wrong output length"); ret = BAD_FUNC_ARG; } + else { + ret = 0; /* success */ + } } if (ret == 0) { @@ -327,9 +335,8 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, { int ret = 0; byte* data = NULL; - word32 dataSz = authInSz + sz + authTagSz; - word32 outSz = authInSz + sz; - ssize_t rc; + word32 dataSz; + int inbuflen = 0, outbuflen = 0; size_t pageSz = (size_t)sysconf(_SC_PAGESIZE); /* argument checks */ @@ -350,16 +357,22 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, } if (ret == 0) { - ret = posix_memalign((void*)&data, pageSz, dataSz); - if (ret < 0) { - ret = MEMORY_E; + ret = kcapi_aead_init(&aes->handle, WC_NAME_AESGCM, 0); + if (ret != 0) { + WOLFSSL_MSG("Error with first time setup of kcapi"); } } if (ret == 0) { - ret = kcapi_aead_init(&aes->handle, WC_NAME_AESGCM, 0); - if (ret != 0) { - WOLFSSL_MSG("Error with first time setup of kcapi"); + inbuflen = (int)kcapi_aead_inbuflen_dec( aes->handle, sz, authInSz, + authTagSz); + outbuflen = (int)kcapi_aead_outbuflen_dec(aes->handle, sz, authInSz, + authTagSz); + dataSz = (inbuflen > outbuflen) ? inbuflen : outbuflen; + + ret = posix_memalign((void*)&data, pageSz, dataSz); + if (ret < 0) { + ret = MEMORY_E; } } @@ -380,19 +393,20 @@ int wc_AesGcmDecrypt(Aes* aes, byte* out, const byte* in, word32 sz, XMEMCPY(data + authInSz, in, sz); XMEMCPY(data + authInSz + sz, authTag, authTagSz); - rc = kcapi_aead_decrypt(aes->handle, data, dataSz, iv, data, outSz, - KCAPI_ACCESS_HEURISTIC); - if (rc < 0) { + ret = (int)kcapi_aead_decrypt(aes->handle, data, inbuflen, iv, data, + outbuflen, KCAPI_ACCESS_HEURISTIC); + if (ret < 0) { WOLFSSL_MSG("GcmDecrypt failed"); - if (rc == -EBADMSG) + if (ret == -EBADMSG) ret = AES_GCM_AUTH_E; - else - ret = (int)rc; } - else if ((word32)rc != outSz) { + else if (ret != outbuflen) { WOLFSSL_MSG("GcmDecrypt produced wrong output length"); ret = BAD_FUNC_ARG; } + else { + ret = 0; /* success */ + } } if (ret == 0) { diff --git a/wolfcrypt/src/port/kcapi/kcapi_dh.c b/wolfcrypt/src/port/kcapi/kcapi_dh.c index f62f13848..72c65ead5 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_dh.c +++ b/wolfcrypt/src/port/kcapi/kcapi_dh.c @@ -26,7 +26,7 @@ #include -#if defined(WOLFSSL_KCAPI_DH) +#if defined(WOLFSSL_KCAPI_DH) && !defined(NO_DH) #include #include @@ -232,4 +232,4 @@ int KcapiDh_SharedSecret(DhKey* private_key, const byte* pub, word32 pubSz, return ret; } -#endif /* WOLFSSL_KCAPI_DH */ +#endif /* WOLFSSL_KCAPI_DH && !NO_DH */ diff --git a/wolfcrypt/src/port/kcapi/kcapi_ecc.c b/wolfcrypt/src/port/kcapi/kcapi_ecc.c index 54c483495..631e0593b 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_ecc.c +++ b/wolfcrypt/src/port/kcapi/kcapi_ecc.c @@ -26,7 +26,7 @@ #include -#if defined(WOLFSSL_KCAPI_ECC) +#if defined(WOLFSSL_KCAPI_ECC) && defined(HAVE_ECC) #include #include @@ -34,6 +34,9 @@ #include #include +#ifndef ECC_CURVE_NIST_P256 +#define ECC_CURVE_NIST_P256 2 +#endif #ifndef ECC_CURVE_NIST_P384 #define ECC_CURVE_NIST_P384 3 #endif @@ -79,70 +82,119 @@ static int KcapiEcc_CurveId(int curve_id, word32* kcapiCurveId) return ret; } -int KcapiEcc_MakeKey(ecc_key* key, int keysize, int curve_id) +int KcapiEcc_LoadKey(ecc_key* key, byte* pubkey_raw, word32* pubkey_sz, + int release_handle) { int ret = 0; - int sz = 0; - word32 kcapiCurveId; + word32 kcapiCurveId = 0; + word32 keySz; + int handleInit = 0; - if (curve_id == ECC_CURVE_DEF) { - switch (keysize) { - case 32: - curve_id = ECC_SECP256R1; - break; - case 48: - curve_id = ECC_SECP384R1; - break; - case 66: - curve_id = ECC_SECP521R1; - break; - default: - ret = BAD_FUNC_ARG; - break; - } - } - if (ret == 0) { - ret = KcapiEcc_CurveId(curve_id, &kcapiCurveId); + if (key == NULL || key->dp == NULL) { + ret = BAD_FUNC_ARG; } - if (key->handle != NULL) { - kcapi_kpp_destroy(key->handle); - key->handle = NULL; - } if (ret == 0) { + keySz = key->dp->size; + ret = KcapiEcc_CurveId(key->dp->id, &kcapiCurveId); + } + + /* if handle doesn't exist create one */ + if (ret == 0 && key->handle == NULL) { ret = kcapi_kpp_init(&key->handle, WC_NAME_ECDH, 0); - if (ret != 0) { - WOLFSSL_MSG("KcapiEcc_MakeKey: Failed to initialize"); + if (ret == 0) { + handleInit = 1; + ret = kcapi_kpp_ecdh_setcurve(key->handle, kcapiCurveId); + if (ret >= 0) { + ret = 0; + } } } + + /* set the key */ if (ret == 0) { - ret = kcapi_kpp_ecdh_setcurve(key->handle, kcapiCurveId); - } - if (ret == 0) { - ret = kcapi_kpp_setkey(key->handle, NULL, 0); + if (mp_iszero(&key->k) != MP_YES) { + /* if a private key value is set, load and use it */ + byte priv[MAX_ECC_BYTES]; + ret = wc_export_int(&key->k, priv, &keySz, keySz, WC_TYPE_UNSIGNED_BIN); + if (ret == 0) { + ret = kcapi_kpp_setkey(key->handle, priv, keySz); + } + } + else { + /* generate new ephemeral key */ + ret = kcapi_kpp_setkey(key->handle, NULL, 0); + } if (ret >= 0) { ret = 0; } } - if (ret == 0) { - sz = ret = (int)kcapi_kpp_keygen(key->handle, key->pubkey_raw, - sizeof(key->pubkey_raw), KCAPI_ACCESS_HEURISTIC); + + /* optionally export public key */ + if (ret == 0 && pubkey_raw != NULL && pubkey_sz != NULL) { + if (*pubkey_sz < keySz*2) { + ret = BUFFER_E; + } + if (ret == 0) { + ret = (int)kcapi_kpp_keygen(key->handle, pubkey_raw, keySz*2, + KCAPI_ACCESS_HEURISTIC); + if (ret >= 0) { + *pubkey_sz = ret; + ret = 0; + } + } } - if (ret >= 0) { - ret = mp_read_unsigned_bin(key->pubkey.x, key->pubkey_raw, sz / 2); + + if (handleInit && release_handle && key != NULL && key->handle != NULL) { + kcapi_kpp_destroy(key->handle); + key->handle = NULL; + } + + return ret; +} + +int KcapiEcc_MakeKey(ecc_key* key, int keysize, int curve_id) +{ + int ret = 0; + word32 pubkey_sz = (word32)sizeof(key->pubkey_raw); + + /* free existing handle */ + if (key != NULL && key->handle != NULL) { + kcapi_kpp_destroy(key->handle); + key->handle = NULL; + } + + /* check arguments */ + if (key == NULL || key->dp == NULL) { + ret = BAD_FUNC_ARG; + } + + ret = KcapiEcc_LoadKey(key, key->pubkey_raw, &pubkey_sz, 0); + if (ret == 0) { + ret = mp_read_unsigned_bin(key->pubkey.x, + key->pubkey_raw, pubkey_sz / 2); } if (ret == 0) { - ret = mp_read_unsigned_bin(key->pubkey.y, key->pubkey_raw + sz / 2, - sz / 2); + ret = mp_read_unsigned_bin(key->pubkey.y, + key->pubkey_raw + pubkey_sz / 2, pubkey_sz / 2); + } + if (ret == 0) { + ret = mp_set(key->pubkey.z, 1); } if (ret == 0) { key->type = ECC_PRIVATEKEY; } - if ((ret != 0) && (key->handle != NULL)) { + + /* if error release handle now */ + if (ret != 0 && key->handle != NULL) { kcapi_kpp_destroy(key->handle); key->handle = NULL; } + /* These are not used. The key->dp is set */ + (void)keysize; + (void)curve_id; + return ret; } @@ -150,26 +202,37 @@ int KcapiEcc_MakeKey(ecc_key* key, int keysize, int curve_id) int KcapiEcc_SharedSecret(ecc_key* private_key, ecc_key* public_key, byte* out, word32* outlen) { - int ret; + int ret = 0; word32 kcapiCurveId = 0; byte* buf_aligned = NULL; byte* pub_aligned = NULL; byte* out_aligned = NULL; size_t pageSz = (size_t)sysconf(_SC_PAGESIZE); - byte* pub = public_key->pubkey_raw; + byte* pub; + word32 keySz; - ret = KcapiEcc_CurveId(private_key->dp->id, &kcapiCurveId); + if (private_key == NULL || private_key->dp == NULL || public_key == NULL) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + pub = public_key->pubkey_raw; + keySz = private_key->dp->size; + ret = KcapiEcc_CurveId(private_key->dp->id, &kcapiCurveId); + } if (ret == 0 && private_key->handle == NULL) { ret = kcapi_kpp_init(&private_key->handle, WC_NAME_ECDH, 0); if (ret == 0) { ret = kcapi_kpp_ecdh_setcurve(private_key->handle, kcapiCurveId); + if (ret >= 0) { + ret = 0; + } } } /* if a private key value is set, load and use it */ - if (mp_iszero(&private_key->k) != MP_YES) { + if (ret == 0 && mp_iszero(&private_key->k) != MP_YES) { byte priv[MAX_ECC_BYTES]; - word32 keySz = private_key->dp->size; ret = wc_export_int(&private_key->k, priv, &keySz, keySz, WC_TYPE_UNSIGNED_BIN); if (ret == 0) { @@ -196,18 +259,20 @@ int KcapiEcc_SharedSecret(ecc_key* private_key, ecc_key* public_key, byte* out, } else { pub_aligned = buf_aligned + pageSz; - XMEMCPY(pub_aligned, pub, public_key->dp->size * 2); + XMEMCPY(pub_aligned, pub, keySz * 2); } ret = (int)kcapi_kpp_ssgen(private_key->handle, pub_aligned, - public_key->dp->size * 2, out_aligned, - *outlen, KCAPI_ACCESS_HEURISTIC); + keySz * 2, out_aligned, keySz * 2, KCAPI_ACCESS_HEURISTIC); if (ret >= 0) { - *outlen = ret/2; + *outlen = ret / 2; if (out_aligned != out) { + /* don't overflow out */ + if (ret > (int)*outlen) + ret = (int)*outlen; XMEMCPY(out, out_aligned, ret); } - ret = 0; + ret = 0; /* success */ } } @@ -233,13 +298,17 @@ static int KcapiEcc_SetPrivKey(ecc_key* key) if (ret == 0) { priv[0] = ECDSA_KEY_VERSION; priv[1] = kcapiCurveId; - ret = wc_export_int(&key->k, priv + 2, &keySz, keySz, + ret = wc_export_int(&key->k, priv + KCAPI_PARAM_SZ, &keySz, keySz, WC_TYPE_UNSIGNED_BIN); } if (ret == 0) { - ret = kcapi_akcipher_setkey(key->handle, priv, KCAPI_PARAM_SZ + keySz); + /* call with NULL to so KCAPI treats incoming data as hash */ + ret = kcapi_akcipher_setkey(key->handle, NULL, 0); if (ret >= 0) { - ret = 0; + ret = kcapi_akcipher_setkey(key->handle, priv, KCAPI_PARAM_SZ + keySz); + if (ret >= 0) { + ret = 0; + } } } @@ -247,20 +316,27 @@ static int KcapiEcc_SetPrivKey(ecc_key* key) } int KcapiEcc_Sign(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, - word32* sigLen) + word32 sigLen) { int ret = 0; byte* buf_aligned = NULL; byte* hash_aligned = NULL; byte* sig_aligned = NULL; size_t pageSz = (size_t)sysconf(_SC_PAGESIZE); + int handleInit = 0; + word32 keySz; - if (key->handle == NULL) { + if (key == NULL || key->dp == NULL) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0 && key->handle == NULL) { ret = kcapi_akcipher_init(&key->handle, WC_NAME_ECDSA, 0); if (ret != 0) { WOLFSSL_MSG("KcapiEcc_Sign: Failed to initialize"); } if (ret == 0) { + handleInit = 1; ret = KcapiEcc_SetPrivKey(key); } } @@ -272,6 +348,13 @@ int KcapiEcc_Sign(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, } } } + if (ret == 0) { + /* make sure signature output is large enough */ + keySz = key->dp->size; + if (sigLen < keySz*2) { + ret = BUFFER_E; + } + } if (ret == 0) { sig_aligned = ((size_t)sig % pageSz == 0) ? sig : buf_aligned; if ((size_t)hash % pageSz == 0) { @@ -282,42 +365,54 @@ int KcapiEcc_Sign(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, XMEMCPY(hash_aligned, hash, hashLen); } ret = (int)kcapi_akcipher_sign(key->handle, hash_aligned, hashLen, - sig_aligned, *sigLen, - KCAPI_ACCESS_HEURISTIC); + sig_aligned, keySz*2, KCAPI_ACCESS_HEURISTIC); if (ret >= 0) { - *sigLen = ret; - ret = 0; if (sig_aligned != sig) { XMEMCPY(sig, sig_aligned, ret); } + ret = 0; /* mark success */ } } + /* Using free as this is in an environment that will have it * available along with posix_memalign. */ if (buf_aligned != NULL) { free(buf_aligned); } + if (handleInit) { + kcapi_kpp_destroy(key->handle); + key->handle = NULL; + } + return ret; } #endif #ifdef HAVE_ECC_VERIFY -int KcapiEcc_SetPubKey(ecc_key* key) +static int KcapiEcc_SetPubKey(ecc_key* key) { int ret; - int len = KCAPI_PARAM_SZ + key->dp->size * 2; word32 kcapiCurveId; + word32 keySz = key->dp->size; + byte pub[KCAPI_PARAM_SZ + (MAX_ECC_BYTES * 2)]; + int pubLen; ret = KcapiEcc_CurveId(key->dp->id, &kcapiCurveId); if (ret == 0) { - key->pubkey_raw[0] = ECDSA_KEY_VERSION; - key->pubkey_raw[1] = kcapiCurveId; + pub[0] = ECDSA_KEY_VERSION; + pub[1] = kcapiCurveId; + XMEMCPY(&pub[KCAPI_PARAM_SZ], key->pubkey_raw, keySz * 2); + pubLen = KCAPI_PARAM_SZ + (keySz * 2); - ret = kcapi_akcipher_setpubkey(key->handle, key->pubkey_raw, len); + /* call with NULL to so KCAPI treats incoming data as hash */ + ret = kcapi_akcipher_setpubkey(key->handle, NULL, 0); if (ret >= 0) { - ret = 0; + ret = kcapi_akcipher_setpubkey(key->handle, pub, pubLen); + if (ret >= 0) { + ret = 0; + } } } @@ -330,32 +425,48 @@ int KcapiEcc_Verify(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, int ret = 0; byte* sigHash_aligned = NULL; size_t pageSz = (size_t)sysconf(_SC_PAGESIZE); + int handleInit = 0; + word32 keySz = 0; + byte* outbuf = NULL; - if (key->handle == NULL) { + if (key == NULL || key->dp == NULL) { + ret = BAD_FUNC_ARG; + } + + if (ret == 0 && key->handle == NULL) { ret = kcapi_akcipher_init(&key->handle, WC_NAME_ECDSA, 0); if (ret != 0) { WOLFSSL_MSG("KcapiEcc_Verify: Failed to initialize"); } + if (ret == 0) { + handleInit = 1; + ret = KcapiEcc_SetPubKey(key); + } } - if (ret == 0) { - ret = KcapiEcc_SetPubKey(key); - } - if (ret == 0) { ret = posix_memalign((void*)&sigHash_aligned, pageSz, sigLen + hashLen); if (ret < 0) { ret = MEMORY_E; } } + if (ret == 0) { + keySz = key->dp->size; + ret = posix_memalign((void*)&outbuf, pageSz, keySz * 2); + if (ret < 0) { + ret = MEMORY_E; + } + } if (ret == 0) { XMEMCPY(sigHash_aligned, sig, sigLen); XMEMCPY(sigHash_aligned + sigLen, hash, hashLen); ret = (int)kcapi_akcipher_verify(key->handle, sigHash_aligned, - sigLen + hashLen, NULL, hashLen, KCAPI_ACCESS_HEURISTIC); + sigLen + hashLen, outbuf, keySz * 2, + KCAPI_ACCESS_HEURISTIC); if (ret >= 0) { ret = 0; } + (void)outbuf; /* not used */ } /* Using free as this is in an environment that will have it @@ -363,8 +474,16 @@ int KcapiEcc_Verify(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, if (sigHash_aligned != NULL) { free(sigHash_aligned); } + if (outbuf != NULL) { + free(outbuf); + } + + if (handleInit) { + kcapi_kpp_destroy(key->handle); + key->handle = NULL; + } return ret; } #endif -#endif /* WOLFSSL_KCAPI_ECC */ +#endif /* WOLFSSL_KCAPI_ECC && HAVE_ECC */ diff --git a/wolfcrypt/src/port/kcapi/kcapi_hash.c b/wolfcrypt/src/port/kcapi/kcapi_hash.c index 47976e543..a14957b40 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_hash.c +++ b/wolfcrypt/src/port/kcapi/kcapi_hash.c @@ -642,4 +642,3 @@ int wc_Sha512_256Copy(wc_Sha512* src, wc_Sha512* dst) #endif /* WOLFSSL_SHA512 */ #endif /* WOLFSSL_KCAPI_HASH */ - diff --git a/wolfcrypt/src/port/kcapi/kcapi_hmac.c b/wolfcrypt/src/port/kcapi/kcapi_hmac.c index 41d06da0f..525001536 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_hmac.c +++ b/wolfcrypt/src/port/kcapi/kcapi_hmac.c @@ -26,7 +26,7 @@ #include -#if defined(WOLFSSL_KCAPI_HMAC) +#if defined(WOLFSSL_KCAPI_HMAC) && !defined(NO_HMAC) #define FIPS_NO_WRAPPERS @@ -334,5 +334,4 @@ int wc_HmacFinal(Hmac* hmac, byte* hash) return ret; } -#endif /* WOLFSSL_KCAPI_HMAC */ - +#endif /* WOLFSSL_KCAPI_HMAC && !NO_HMAC */ diff --git a/wolfcrypt/src/port/kcapi/kcapi_rsa.c b/wolfcrypt/src/port/kcapi/kcapi_rsa.c index 828c4c1c8..f11ee2fb7 100644 --- a/wolfcrypt/src/port/kcapi/kcapi_rsa.c +++ b/wolfcrypt/src/port/kcapi/kcapi_rsa.c @@ -26,7 +26,7 @@ #include -#if defined(WOLFSSL_KCAPI_RSA) +#if defined(WOLFSSL_KCAPI_RSA) && !defined(NO_RSA) #include #include @@ -356,5 +356,4 @@ int KcapiRsa_Encrypt(RsaKey* key, const byte* in, word32 inLen, byte* out, return ret; } -#endif /* WOLFSSL_KCAPI_RSA */ - +#endif /* WOLFSSL_KCAPI_RSA && !NO_RSA */ diff --git a/wolfcrypt/src/random.c b/wolfcrypt/src/random.c index d2bb66329..14e3ca202 100644 --- a/wolfcrypt/src/random.c +++ b/wolfcrypt/src/random.c @@ -2786,8 +2786,33 @@ int wc_GenerateSeed(OS_Seed* os, byte* output, word32 sz) return 0; } #endif - - /* End wc_GenerateSeed */ + +#if defined(CUSTOM_RAND_GENERATE_BLOCK) && defined(WOLFSSL_KCAPI) +#include +int wc_hwrng_generate_block(byte *output, word32 sz) +{ + int fd; + int len; + int ret = 0; + fd = open("/dev/hwrng", O_RDONLY); + if (fd == -1) + return OPEN_RAN_E; + while(sz) + { + len = (int)read(fd, output, sz); + if (len == -1) + { + ret = READ_RAN_E; + break; + } + sz -= len; + output += len; + } + close(fd); + return ret; +} +#endif + #endif /* WC_NO_RNG */ #endif /* HAVE_FIPS */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 48540cbf2..f87a2e40a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -1120,13 +1120,15 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ return err_sys("AES-GCM test failed!\n", ret); #endif #if !defined(WOLFSSL_AFALG_XILINX_AES) && !defined(WOLFSSL_XILINX_CRYPT) && \ - !(defined(WOLF_CRYPTO_CB) && \ + !defined(WOLFSSL_KCAPI_AES) && !(defined(WOLF_CRYPTO_CB) && \ (defined(HAVE_INTEL_QA_SYNC) || defined(HAVE_CAVIUM_OCTEON_SYNC))) if ((ret = aesgcm_default_test()) != 0) { return err_sys("AES-GCM test failed!\n", ret); } #endif - TEST_PASS("AES-GCM test passed!\n"); + if (ret == 0) { + TEST_PASS("AES-GCM test passed!\n"); + } #endif #if defined(HAVE_AESCCM) && defined(WOLFSSL_AES_128) @@ -21039,7 +21041,8 @@ done: #endif -#if defined(HAVE_ECC_SIGN) && defined(WOLFSSL_ECDSA_SET_K) +#if defined(HAVE_ECC_SIGN) && defined(WOLFSSL_ECDSA_SET_K) && \ + !defined(WOLFSSL_KCAPI_ECC) static int ecc_test_sign_vectors(WC_RNG* rng) { int ret; @@ -21714,7 +21717,7 @@ static int ecc_test_curve_size(WC_RNG* rng, int keySize, int testVerifyCount, !defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) word32 y; #endif -#ifdef HAVE_ECC_SIGN +#if defined(HAVE_ECC_SIGN) && !defined(WOLFSSL_KCAPI_ECC) WC_DECLARE_VAR(sig, byte, ECC_SIG_SIZE, HEAP_HINT); WC_DECLARE_VAR(digest, byte, ECC_DIGEST_SIZE, HEAP_HINT); int i; @@ -21748,7 +21751,7 @@ static int ecc_test_curve_size(WC_RNG* rng, int keySize, int testVerifyCount, ERROR_OUT(-9901, done); #endif -#ifdef HAVE_ECC_SIGN +#if defined(HAVE_ECC_SIGN) && !defined(WOLFSSL_KCAPI_ECC) if (sig == NULL || digest == NULL) ERROR_OUT(-9902, done); #endif @@ -21994,7 +21997,9 @@ static int ecc_test_curve_size(WC_RNG* rng, int keySize, int testVerifyCount, #endif /* HAVE_ECC_KEY_IMPORT */ #endif /* HAVE_ECC_KEY_EXPORT */ -#if !defined(ECC_TIMING_RESISTANT) || (defined(ECC_TIMING_RESISTANT) && !defined(WC_NO_RNG)) + /* For KCAPI cannot sign using generated ECDH key */ +#if !defined(ECC_TIMING_RESISTANT) || (defined(ECC_TIMING_RESISTANT) && \ + !defined(WC_NO_RNG) && !defined(WOLFSSL_KCAPI_ECC)) #ifdef HAVE_ECC_SIGN /* ECC w/out Shamir has issue with all 0 digest */ /* WC_BIGINT doesn't have 0 len well on hardware */ @@ -22076,10 +22081,12 @@ static int ecc_test_curve_size(WC_RNG* rng, int keySize, int testVerifyCount, } #endif /* HAVE_ECC_VERIFY */ #endif /* HAVE_ECC_SIGN */ -#endif /* !ECC_TIMING_RESISTANT || (ECC_TIMING_RESISTANT && !WC_NO_RNG) */ +#endif /* !ECC_TIMING_RESISTANT || (ECC_TIMING_RESISTANT && + * !WC_NO_RNG && !WOLFSSL_KCAPI_ECC) */ #if defined(HAVE_ECC_KEY_EXPORT) && !defined(WC_NO_RNG) && \ - !defined(WOLFSSL_ATECC508) && !defined(WOLFSSL_ATECC608A) + !defined(WOLFSSL_ATECC508) && !defined(WOLFSSL_ATECC608A) && \ + !defined(WOLFSSL_KCAPI_ECC) x = ECC_KEY_EXPORT_BUF_SIZE; ret = wc_ecc_export_private_only(userA, exportBuf, &x); if (ret != 0) @@ -24118,7 +24125,8 @@ WOLFSSL_TEST_SUBROUTINE int ecc_test(void) #endif #endif -#if defined(HAVE_ECC_SIGN) && defined(WOLFSSL_ECDSA_SET_K) +#if defined(HAVE_ECC_SIGN) && defined(WOLFSSL_ECDSA_SET_K) && \ + !defined(WOLFSSL_KCAPI_ECC) ret = ecc_test_sign_vectors(&rng); if (ret != 0) { printf("ecc_test_sign_vectors failed! %d\n", ret); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 4fca5344f..108a5ede2 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1840,7 +1840,7 @@ WOLFSSL_LOCAL int CheckCertSignaturePubKey(const byte* cert, word32 certSz, WOLFSSL_LOCAL int CheckCSRSignaturePubKey(const byte* cert, word32 certSz, void* heap, const byte* pubKey, word32 pubKeySz, int pubKeyOID); #endif /* WOLFSSL_CERT_REQ */ -WOLFSSL_LOCAL int AddSignature(byte* buf, int bodySz, const byte* sig, int sigSz, +WOLFSSL_ASN_API int AddSignature(byte* buf, int bodySz, const byte* sig, int sigSz, int sigAlgoType); WOLFSSL_LOCAL int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm); diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index 772a36015..a7899837b 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -466,7 +466,7 @@ struct ecc_key { #endif #ifdef WOLFSSL_KCAPI_ECC struct kcapi_handle* handle; - byte pubkey_raw[KCAPI_PARAM_SZ + MAX_ECC_BYTES * 2]; + byte pubkey_raw[MAX_ECC_BYTES * 2]; #endif #ifdef WOLFSSL_ASYNC_CRYPT diff --git a/wolfssl/wolfcrypt/port/kcapi/kcapi_ecc.h b/wolfssl/wolfcrypt/port/kcapi/kcapi_ecc.h index de4359144..823cafb21 100644 --- a/wolfssl/wolfcrypt/port/kcapi/kcapi_ecc.h +++ b/wolfssl/wolfcrypt/port/kcapi/kcapi_ecc.h @@ -36,16 +36,16 @@ #define WC_ECCKEY_TYPE_DEFINED #endif -int KcapiEcc_SetPubKey(ecc_key* key); - -void KcapiEcc_Free(ecc_key* key); -int KcapiEcc_MakeKey(ecc_key* key, int keysize, int curve_id); -int KcapiEcc_SharedSecret(ecc_key* private_key, ecc_key* public_key, byte* out, - word32* outlen); -int KcapiEcc_Sign(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, - word32* sigLen); -int KcapiEcc_Verify(ecc_key* key, const byte* hash, word32 hashLen, byte* sig, - word32 sigLen); +WOLFSSL_LOCAL void KcapiEcc_Free(ecc_key* key); +WOLFSSL_LOCAL int KcapiEcc_MakeKey(ecc_key* key, int keysize, int curve_id); +WOLFSSL_LOCAL int KcapiEcc_LoadKey(ecc_key* key, byte* pubkey_raw, + word32* pubkey_sz, int release_handle); +WOLFSSL_LOCAL int KcapiEcc_SharedSecret(ecc_key* private_key, + ecc_key* public_key, byte* out, word32* outlen); +WOLFSSL_LOCAL int KcapiEcc_Sign(ecc_key* key, const byte* hash, word32 hashLen, + byte* sig, word32 sigLen); +WOLFSSL_LOCAL int KcapiEcc_Verify(ecc_key* key, const byte* hash, word32 hashLen, + byte* sig, word32 sigLen); #endif /* WOLF_CRYPT_KCAPI_ECC_H */ diff --git a/wolfssl/wolfcrypt/random.h b/wolfssl/wolfcrypt/random.h index 963596dfb..ff4ef0de5 100644 --- a/wolfssl/wolfcrypt/random.h +++ b/wolfssl/wolfcrypt/random.h @@ -99,6 +99,11 @@ * #define CUSTOM_RAND_GENERATE_BLOCK myRngFunc * extern int myRngFunc(byte* output, word32 sz); */ + #if defined(CUSTOM_RAND_GENERATE_BLOCK) && defined(WOLFSSL_KCAPI) + #undef CUSTOM_RAND_GENERATE_BLOCK + #define CUSTOM_RAND_GENERATE_BLOCK wc_hwrng_generate_block + WOLFSSL_LOCAL int wc_hwrng_generate_block(byte *output, word32 sz); + #endif #elif defined(HAVE_HASHDRBG) #ifdef NO_SHA256 #error "Hash DRBG requires SHA-256." diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 6484387f1..22c09269e 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -1627,9 +1627,12 @@ extern void uITRON4_free(void *p) ; #define WOLFSSL_AES_GCM_FIXED_IV_AAD #endif #ifdef WOLFSSL_KCAPI_ECC + #undef ECC_USER_CURVES #define ECC_USER_CURVES #undef NO_ECC256 + #undef HAVE_ECC384 #define HAVE_ECC384 + #undef HAVE_ECC521 #define HAVE_ECC521 #endif