diff --git a/tests/api.c b/tests/api.c index 84416e576..3841913d1 100644 --- a/tests/api.c +++ b/tests/api.c @@ -45185,27 +45185,279 @@ static void test_wolfSSL_EVP_BytesToKey(void) printf(resultFmt, passed); #endif } -static void test_IncCtr(void) +static void test_evp_cipher_aes_gcm(void) { -#if defined(HAVE_AESGCM) && !defined(HAVE_FIPS) - byte key[AES_128_KEY_SIZE] = {0}; - byte iv[GCM_NONCE_MID_SZ] = {0}; - int type = EVP_CTRL_GCM_IV_GEN; - int arg = 0; - void *ptr = NULL; +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && \ + !defined(HAVE_SELFTEST)) || (defined(HAVE_FIPS_VERSION) && \ + (HAVE_FIPS_VERSION >= 2))) + /* + * This test checks data at various points in the encrypt/decrypt process + * against known values produced using the same test with OpenSSL. This + * interop testing is critical for verifying the correctness of our + * EVP_Cipher implementation with AES-GCM. Specifically, this test exercises + * a flow supported by OpenSSL that uses the control command + * EVP_CTRL_GCM_IV_GEN to increment the IV between cipher operations without + * the need to call EVP_CipherInit. OpenSSH uses this flow, for example. We + * had a bug with OpenSSH where wolfSSL OpenSSH servers could only talk to + * wolfSSL OpenSSH clients because there was a bug in this flow that + * happened to "cancel out" if both sides of the connection had the bug. + */ + enum { + NUM_ENCRYPTIONS = 3, + AAD_SIZE = 4 + }; + byte plainText1[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 + }; + byte plainText2[] = { + 0x42, 0x49, 0x3b, 0x27, 0x03, 0x35, 0x59, 0x14, 0x41, 0x47, 0x37, 0x14, + 0x0e, 0x34, 0x0d, 0x28, 0x63, 0x09, 0x0a, 0x5b, 0x22, 0x57, 0x42, 0x22, + 0x0f, 0x5c, 0x1e, 0x53, 0x45, 0x15, 0x62, 0x08, 0x60, 0x43, 0x50, 0x2c + }; + byte plainText3[] = { + 0x36, 0x0d, 0x2b, 0x09, 0x4a, 0x56, 0x3b, 0x4c, 0x21, 0x22, 0x58, 0x0e, + 0x5b, 0x57, 0x10 + }; + byte* plainTexts[NUM_ENCRYPTIONS] = { + plainText1, + plainText2, + plainText3 + }; + const int plainTextSzs[NUM_ENCRYPTIONS] = { + sizeof(plainText1), + sizeof(plainText2), + sizeof(plainText3) + }; + byte aad1[AAD_SIZE] = { + 0x00, 0x00, 0x00, 0x01 + }; + byte aad2[AAD_SIZE] = { + 0x00, 0x00, 0x00, 0x10 + }; + byte aad3[AAD_SIZE] = { + 0x00, 0x00, 0x01, 0x00 + }; + byte* aads[NUM_ENCRYPTIONS] = { + aad1, + aad2, + aad3 + }; + const byte iv[GCM_NONCE_MID_SZ] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF + }; + byte currentIv[GCM_NONCE_MID_SZ]; + const byte key[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f + }; + const byte expIvs[NUM_ENCRYPTIONS][GCM_NONCE_MID_SZ] = { + { + 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, + 0xEF + }, + { + 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, + 0xF0 + }, + { + 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, + 0xF1 + } + }; + const byte expTags[NUM_ENCRYPTIONS][AES_BLOCK_SIZE] = { + { + 0x65, 0x4F, 0xF7, 0xA0, 0xBB, 0x7B, 0x90, 0xB7, 0x9C, 0xC8, 0x14, + 0x3D, 0x32, 0x18, 0x34, 0xA9 + }, + { + 0x50, 0x3A, 0x13, 0x8D, 0x91, 0x1D, 0xEC, 0xBB, 0xBA, 0x5B, 0x57, + 0xA2, 0xFD, 0x2D, 0x6B, 0x7F + }, + { + 0x3B, 0xED, 0x18, 0x9C, 0xB3, 0xE3, 0x61, 0x1E, 0x11, 0xEB, 0x13, + 0x5B, 0xEC, 0x52, 0x49, 0x32, + } + }; - EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); - const EVP_CIPHER *init = EVP_aes_128_gcm(); + const byte expCipherText1[] = { + 0xCB, 0x93, 0x4F, 0xC8, 0x22, 0xE2, 0xC0, 0x35, 0xAA, 0x6B, 0x41, 0x15, + 0x17, 0x30, 0x2F, 0x97, 0x20, 0x74, 0x39, 0x28, 0xF8, 0xEB, 0xC5, 0x51, + 0x7B, 0xD9, 0x8A, 0x36, 0xB8, 0xDA, 0x24, 0x80, 0xE7, 0x9E, 0x09, 0xDE + }; + const byte expCipherText2[] = { + 0xF9, 0x32, 0xE1, 0x87, 0x37, 0x0F, 0x04, 0xC1, 0xB5, 0x59, 0xF0, 0x45, + 0x3A, 0x0D, 0xA0, 0x26, 0xFF, 0xA6, 0x8D, 0x38, 0xFE, 0xB8, 0xE5, 0xC2, + 0x2A, 0x98, 0x4A, 0x54, 0x8F, 0x1F, 0xD6, 0x13, 0x03, 0xB2, 0x1B, 0xC0 + }; + const byte expCipherText3[] = { + 0xD0, 0x37, 0x59, 0x1C, 0x2F, 0x85, 0x39, 0x4D, 0xED, 0xC2, 0x32, 0x5B, + 0x80, 0x5E, 0x6B, + }; + const byte* expCipherTexts[NUM_ENCRYPTIONS] = { + expCipherText1, + expCipherText2, + expCipherText3 + }; + byte* cipherText; + byte* calcPlainText; + byte tag[AES_BLOCK_SIZE]; + EVP_CIPHER_CTX* encCtx = NULL; + EVP_CIPHER_CTX* decCtx = NULL; + int i, j, outl; - printf(testingFmt, "IncCtr"); + printf(testingFmt, "test_evp_cipher_aes_gcm"); - AssertNotNull(ctx); - wolfSSL_EVP_CIPHER_CTX_init(ctx); - AssertIntEQ(EVP_CipherInit(ctx, init, key, iv, 1), WOLFSSL_SUCCESS); + /****************************************************/ + for (i = 0; i < 3; ++i) { + AssertNotNull(encCtx = EVP_CIPHER_CTX_new()); + AssertNotNull(decCtx = EVP_CIPHER_CTX_new()); - AssertIntEQ(wolfSSL_EVP_CIPHER_CTX_ctrl(ctx, type, arg, ptr), WOLFSSL_SUCCESS); + /* First iteration, set key before IV. */ + if (i == 0) { + AssertIntEQ(EVP_CipherInit(encCtx, EVP_aes_256_gcm(), key, NULL, 1), + SSL_SUCCESS); + AssertIntEQ(EVP_CipherInit(encCtx, NULL, NULL, iv, 1), + SSL_SUCCESS); + AssertIntEQ(EVP_CipherInit(decCtx, EVP_aes_256_gcm(), key, NULL, 0), + SSL_SUCCESS); + AssertIntEQ(EVP_CipherInit(decCtx, NULL, NULL, iv, 0), + SSL_SUCCESS); + } + /* Second iteration, IV before key. */ + else { + AssertIntEQ(EVP_CipherInit(encCtx, EVP_aes_256_gcm(), NULL, iv, 1), + SSL_SUCCESS); + AssertIntEQ(EVP_CipherInit(encCtx, NULL, key, NULL, 1), + SSL_SUCCESS); + AssertIntEQ(EVP_CipherInit(decCtx, EVP_aes_256_gcm(), NULL, iv, 0), + SSL_SUCCESS); + AssertIntEQ(EVP_CipherInit(decCtx, NULL, key, NULL, 0), + SSL_SUCCESS); + } - EVP_CIPHER_CTX_free(ctx); + /* + * EVP_CTRL_GCM_IV_GEN should fail if EVP_CTRL_GCM_SET_IV_FIXED hasn't + * been issued first. + */ + AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_IV_GEN, -1, + currentIv), SSL_FAILURE); + + AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_SET_IV_FIXED, -1, + (void*)iv), SSL_SUCCESS); + AssertIntEQ(EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_SET_IV_FIXED, -1, + (void*)iv), SSL_SUCCESS); + + for (j = 0; j < NUM_ENCRYPTIONS; ++j) { + /*************** Encrypt ***************/ + AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_IV_GEN, -1, + currentIv), SSL_SUCCESS); + /* Check current IV against expected. */ + AssertIntEQ(XMEMCMP(currentIv, expIvs[j], GCM_NONCE_MID_SZ), 0); + + /* Add AAD. */ + if (i == 2) { + /* Test streaming API. */ + AssertIntEQ(EVP_CipherUpdate(encCtx, NULL, &outl, aads[j], + AAD_SIZE), SSL_SUCCESS); + } + else { + AssertIntEQ(EVP_Cipher(encCtx, NULL, aads[j], AAD_SIZE), + AAD_SIZE); + } + + AssertNotNull(cipherText = (byte*)XMALLOC(plainTextSzs[j], NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + + /* Encrypt plaintext. */ + if (i == 2){ + AssertIntEQ(EVP_CipherUpdate(encCtx, cipherText, &outl, + plainTexts[j], plainTextSzs[j]), + SSL_SUCCESS); + } + else { + AssertIntEQ(EVP_Cipher(encCtx, cipherText, plainTexts[j], + plainTextSzs[j]), plainTextSzs[j]); + } + + if (i == 2) { + AssertIntEQ(EVP_CipherFinal(encCtx, cipherText, &outl), + SSL_SUCCESS); + } + else { + /* + * Calling EVP_Cipher with NULL input and output for AES-GCM is + * akin to calling EVP_CipherFinal. + */ + AssertIntGE(EVP_Cipher(encCtx, NULL, NULL, 0), 0); + } + + /* Check ciphertext against expected. */ + AssertIntEQ(XMEMCMP(cipherText, expCipherTexts[j], plainTextSzs[j]), + 0); + + /* Get and check tag against expected. */ + AssertIntEQ(EVP_CIPHER_CTX_ctrl(encCtx, EVP_CTRL_GCM_GET_TAG, + sizeof(tag), tag), SSL_SUCCESS); + AssertIntEQ(XMEMCMP(tag, expTags[j], sizeof(tag)), 0); + + /*************** Decrypt ***************/ + AssertIntEQ(EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_IV_GEN, -1, + currentIv), SSL_SUCCESS); + /* Check current IV against expected. */ + AssertIntEQ(XMEMCMP(currentIv, expIvs[j], GCM_NONCE_MID_SZ), 0); + + /* Add AAD. */ + if (i == 2) { + /* Test streaming API. */ + AssertIntEQ(EVP_CipherUpdate(decCtx, NULL, &outl, aads[j], + AAD_SIZE), SSL_SUCCESS); + } + else { + AssertIntEQ(EVP_Cipher(decCtx, NULL, aads[j], AAD_SIZE), + AAD_SIZE); + } + + /* Set expected tag. */ + AssertIntEQ(EVP_CIPHER_CTX_ctrl(decCtx, EVP_CTRL_GCM_SET_TAG, + sizeof(tag), tag), SSL_SUCCESS); + + /* Decrypt ciphertext. */ + AssertNotNull(calcPlainText = (byte*)XMALLOC(plainTextSzs[j], NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + if (i == 2){ + AssertIntEQ(EVP_CipherUpdate(decCtx, calcPlainText, &outl, + cipherText, plainTextSzs[j]), + SSL_SUCCESS); + } + else { + /* This first EVP_Cipher call will check the tag, too. */ + AssertIntEQ(EVP_Cipher(decCtx, calcPlainText, cipherText, + plainTextSzs[j]), plainTextSzs[j]); + } + + if (i == 2) { + AssertIntEQ(EVP_CipherFinal(decCtx, calcPlainText, &outl), + SSL_SUCCESS); + } + else { + AssertIntGE(EVP_Cipher(decCtx, NULL, NULL, 0), 0); + } + + /* Check plaintext against expected. */ + AssertIntEQ(XMEMCMP(calcPlainText, plainTexts[j], plainTextSzs[j]), + 0); + + XFREE(cipherText, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(calcPlainText, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + EVP_CIPHER_CTX_free(encCtx); + EVP_CIPHER_CTX_free(decCtx); + } + printf(resultFmt, passed); #endif } @@ -54485,7 +54737,7 @@ void ApiTest(void) test_wolfSSL_EVP_BytesToKey(); test_wolfSSL_EVP_PKEY_param_check(); test_wolfSSL_QT_EVP_PKEY_CTX_free(); - test_IncCtr(); + test_evp_cipher_aes_gcm(); test_wolfSSL_OBJ_ln(); test_wolfSSL_OBJ_sn(); test_wolfSSL_TXT_DB(); diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 25994e15e..23f9b8e26 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -824,17 +824,36 @@ static int checkPad(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *buff) return ctx->block_size - n; } -int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, - unsigned char *out, int *outl) +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) +static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) +{ + int i; + for (i = ctrSz-1; i >= 0; i--) { + if (++ctr[i]) + break; + } +} +#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) || + * HAVE_FIPS_VERSION >= 2 */ + +int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out, + int *outl) { int fl; int ret = WOLFSSL_SUCCESS; +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) + byte tmp = 0; +#endif + if (!ctx || !outl) return WOLFSSL_FAILURE; WOLFSSL_ENTER("wolfSSL_EVP_CipherFinal"); switch (ctx->cipherType) { -#if !defined(NO_AES) && defined(HAVE_AESGCM) +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) case AES_128_GCM_TYPE: case AES_192_GCM_TYPE: case AES_256_GCM_TYPE: @@ -864,6 +883,12 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, XFREE(ctx->gcmBuffer, NULL, DYNAMIC_TYPE_OPENSSL); ctx->gcmBuffer = NULL; ctx->gcmBufferLen = 0; + + if (ctx->gcmIncIv) { + IncCtr((byte*)ctx->cipher.aes.reg, + ctx->cipher.aes.nonceSz); + ctx->gcmIncIv = 0; + } } else { *outl = 0; @@ -878,18 +903,41 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, else { ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes, ctx->authTag, ctx->authTagSz); + if (ctx->gcmIncIv) { + IncCtr((byte*)ctx->cipher.aes.reg, ctx->cipher.aes.nonceSz); + } } if (ret == 0) { - ret = WOLFSSL_SUCCESS; + /* Reinitialize for subsequent wolfSSL_EVP_Cipher calls. */ + if (wc_AesGcmInit(&ctx->cipher.aes, NULL, 0, + (byte*)ctx->cipher.aes.reg, + ctx->ivSz) != 0) { + WOLFSSL_MSG("wc_AesGcmInit failed"); + ret = WOLFSSL_FAILURE; + } + else { + ret = WOLFSSL_SUCCESS; + } } else { ret = WOLFSSL_FAILURE; } #endif /* WOLFSSL_AESGCM_STREAM */ - /* Clear IV, since IV reuse is not recommended for AES GCM. */ - XMEMSET(ctx->iv, 0, AES_BLOCK_SIZE); + if (ret == WOLFSSL_SUCCESS) { + if (ctx->gcmIncIv) { + ctx->gcmIncIv = 0; + } + else { + /* Clear IV, since IV reuse is not recommended for AES GCM. */ + XMEMSET(ctx->iv, 0, AES_BLOCK_SIZE); + } + if (wolfSSL_StoreExternalIV(ctx) != WOLFSSL_SUCCESS) { + ret = WOLFSSL_FAILURE; + } + } break; -#endif /* !NO_AES && HAVE_AESGCM */ +#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) || + * HAVE_FIPS_VERSION >= 2 */ #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) case CHACHA20_POLY1305_TYPE: if (wc_ChaCha20Poly1305_Final(&ctx->cipher.chachaPoly, @@ -962,9 +1010,33 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, } if (ret == WOLFSSL_SUCCESS) { +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) + /* + * This flag needs to retain its value between wolfSSL_EVP_CipherFinal + * calls. wolfSSL_EVP_CipherInit will clear it, so we save and restore + * it here. + */ + if (ctx->cipherType == AES_128_GCM_TYPE || + ctx->cipherType == AES_192_GCM_TYPE || + ctx->cipherType == AES_256_GCM_TYPE) { + tmp = ctx->gcmIvGenEnable; + } +#endif + /* reset cipher state after final */ ret = wolfSSL_EVP_CipherInit(ctx, NULL, NULL, NULL, -1); + +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) + if (ctx->cipherType == AES_128_GCM_TYPE || + ctx->cipherType == AES_192_GCM_TYPE || + ctx->cipherType == AES_256_GCM_TYPE) { + ctx->gcmIvGenEnable = tmp; + } +#endif } + return ret; } @@ -5287,17 +5359,6 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) } } -#if defined(HAVE_AESGCM) && !defined(HAVE_SELFTEST) - static WC_INLINE void IncCtr(byte* ctr, word32 ctrSz) - { - int i; - for (i = ctrSz-1; i >= 0; i--) { - if (++ctr[i]) - break; - } - } -#endif - /* This function allows cipher specific parameters to be determined and set. */ int wolfSSL_EVP_CIPHER_CTX_ctrl(WOLFSSL_EVP_CIPHER_CTX *ctx, int type, \ @@ -5381,35 +5442,58 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) break; } } + #ifdef HAVE_AESGCM + if (ret == WOLFSSL_SUCCESS) { + /* + * OpenSSL requires that a EVP_CTRL_AEAD_SET_IV_FIXED + * command be issued before a EVP_CTRL_GCM_IV_GEN command. + * This flag is used to enforce that. + */ + ctx->gcmIvGenEnable = 1; + } + #endif #endif /* !WC_NO_RNG */ break; #if defined(HAVE_AESGCM) && !defined(_WIN32) && !defined(HAVE_SELFTEST) && \ (!defined(HAVE_FIPS) || FIPS_VERSION_GE(2,0)) + /* + * Using EVP_CTRL_GCM_IV_GEN is a way to do AES-GCM encrypt/decrypt + * multiple times with EVP_Cipher without having to call + * EVP_CipherInit between each iteration. The IV is incremented for + * each subsequent EVP_Cipher call to prevent IV reuse. + */ case EVP_CTRL_GCM_IV_GEN: if ((ctx->flags & WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER) == 0) break; + if (!ctx->gcmIvGenEnable) { + WOLFSSL_MSG("Must use EVP_CTRL_AEAD_SET_IV_FIXED before " + "EVP_CTRL_GCM_IV_GEN"); + break; + } if (ctx->cipher.aes.keylen == 0 || ctx->ivSz == 0) { WOLFSSL_MSG("Key or IV not set"); break; } - if (wc_AesGcmSetExtIV(&ctx->cipher.aes, ctx->iv, - ctx->ivSz) != 0) { - WOLFSSL_MSG("wc_AesGcmSetIV failed"); + if (ptr == NULL) { + WOLFSSL_MSG("Destination buffer for IV bytes NULL."); break; } -#ifdef WOLFSSL_AESGCM_STREAM - /* Initialize using IV cached in Aes object. */ - if (wc_AesGcmInit(&ctx->cipher.aes, NULL, 0, NULL, 0) != 0) { - WOLFSSL_MSG("wc_AesGcmInit failed"); - break; + if (arg <= 0 || arg > ctx->ivSz) { + XMEMCPY(ptr, ctx->iv, ctx->ivSz); } -#endif /* WOLFSSL_AESGCM_STREAM */ - /* OpenSSL increments the IV. Not sure why */ - IncCtr(ctx->iv, ctx->ivSz); - /* Clear any leftover AAD. */ - if (ctx->gcmAuthIn != NULL) - XMEMSET(ctx->gcmAuthIn, 0, ctx->gcmAuthInSz); - ctx->gcmAuthInSz = 0; + else { + /* + * Copy the last "arg" bytes of ctx->iv into the buffer at + * "ptr." Not sure why OpenSSL does this, but it does. + */ + XMEMCPY(ptr, ctx->iv + ctx->ivSz - arg, arg); + } + + /* + * The gcmIncIV flag indicates that the IV should be incremented + * after the next cipher operation. + */ + ctx->gcmIncIv = 1; ret = WOLFSSL_SUCCESS; break; #endif /* HAVE_AESGCM && !_WIN32 && !HAVE_SELFTEST && (!HAVE_FIPS || @@ -5498,6 +5582,8 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) ctx->gcmAuthIn = NULL; } ctx->gcmAuthInSz = 0; + ctx->gcmIvGenEnable = 0; + ctx->gcmIncIv = 0; #endif } @@ -5608,8 +5694,190 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) #endif /* AES_ANY_SIZE && AES_SET_KEY */ #endif /* NO_AES */ +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) + static int EvpCipherInitAesGCM(WOLFSSL_EVP_CIPHER_CTX* ctx, + const WOLFSSL_EVP_CIPHER* type, + const byte* key, const byte* iv, int enc) + { + int ret = WOLFSSL_SUCCESS; + + ctx->block_size = AES_BLOCK_SIZE; + ctx->authTagSz = AES_BLOCK_SIZE; + if (ctx->ivSz == 0) { + ctx->ivSz = GCM_NONCE_MID_SZ; + } + ctx->flags &= ~WOLFSSL_EVP_CIPH_MODE; + ctx->flags |= WOLFSSL_EVP_CIPH_GCM_MODE | + WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER; + if (enc == 0 || enc == 1) { + ctx->enc = enc ? 1 : 0; + } + + #ifdef WOLFSSL_AES_128 + if (ctx->cipherType == AES_128_GCM_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_GCM))) { + WOLFSSL_MSG("EVP_AES_128_GCM"); + ctx->cipherType = AES_128_GCM_TYPE; + ctx->keyLen = AES_128_KEY_SIZE; + } + #endif + #ifdef WOLFSSL_AES_192 + if (ctx->cipherType == AES_192_GCM_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_GCM))) { + WOLFSSL_MSG("EVP_AES_192_GCM"); + ctx->cipherType = AES_192_GCM_TYPE; + ctx->keyLen = AES_192_KEY_SIZE; + } + #endif + #ifdef WOLFSSL_AES_256 + if (ctx->cipherType == AES_256_GCM_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_GCM))) { + WOLFSSL_MSG("EVP_AES_256_GCM"); + ctx->cipherType = AES_256_GCM_TYPE; + ctx->keyLen = AES_256_KEY_SIZE; + } + #endif + + #ifndef WOLFSSL_AESGCM_STREAM + if (ret == WOLFSSL_SUCCESS && key && + wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) { + WOLFSSL_MSG("wc_AesGcmSetKey() failed"); + ret = WOLFSSL_FAILURE; + } + #endif /* !WOLFSSL_AESGCM_STREAM */ + if (ret == WOLFSSL_SUCCESS && iv && + wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) { + WOLFSSL_MSG("wc_AesGcmSetExtIV() failed"); + ret = WOLFSSL_FAILURE; + } + #ifdef WOLFSSL_AESGCM_STREAM + /* + * Initialize with key and IV if available. wc_AesGcmInit will fail + * if called with IV only and no key has been set. + */ + if (ret == WOLFSSL_SUCCESS && + (key || (iv && ctx->cipher.aes.gcmKeySet)) && + wc_AesGcmInit(&ctx->cipher.aes, key, + (key == NULL) ? 0 : ctx->keyLen, iv, + (iv == NULL) ? 0 : ctx->ivSz) != 0) { + WOLFSSL_MSG("wc_AesGcmInit() failed"); + ret = WOLFSSL_FAILURE; + } + #endif /* WOLFSSL_AESGCM_STREAM */ + + return ret; + } + + static int EvpCipherAesGCM(WOLFSSL_EVP_CIPHER_CTX* ctx, byte* dst, + byte* src, word32 len) + { + int ret = WOLFSSL_FAILURE; + + #ifndef WOLFSSL_AESGCM_STREAM + /* No destination means only AAD. */ + if (src != NULL && dst == NULL) { + ret = wolfSSL_EVP_CipherUpdate_GCM_AAD(ctx, src, len); + } + else if (src != NULL && dst != NULL) { + if (ctx->enc) { + ret = wc_AesGcmEncrypt(&ctx->cipher.aes, dst, src, + len, ctx->iv, ctx->ivSz, ctx->authTag, + ctx->authTagSz, ctx->gcmAuthIn, + ctx->gcmAuthInSz); + } + else { + ret = wc_AesGcmDecrypt(&ctx->cipher.aes, dst, src, + len, ctx->iv, ctx->ivSz, ctx->authTag, + ctx->authTagSz, ctx->gcmAuthIn, + ctx->gcmAuthInSz); + } + if (ctx->gcmIncIv) { + IncCtr((byte*)ctx->cipher.aes.reg, + ctx->cipher.aes.nonceSz); + ctx->gcmIncIv = 0; + } + } + #else + /* + * No need to call wc_AesGcmInit. Should have been called by + * wolfSSL_EVP_CipherInit. + */ + /* NULL dst and non-NULL src means only AAD. */ + if (src != NULL && dst == NULL) { + if (ctx->enc) { + ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, NULL, + NULL, 0, src, len); + } + else { + ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, NULL, + NULL, 0, src, len); + } + } + /* Only plain/cipher text. */ + else if (src != NULL && dst != NULL) { + if (ctx->enc) { + ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, dst, src, + len, NULL, 0); + } + else { + ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, dst, src, + len, NULL, 0); + } + } + /* + * src == NULL is analogous to other "final"-type functions + * (e.g. EVP_CipherFinal). Calculates tag on encrypt + * and checks tag on decrypt. + */ + else { + if (ctx->enc) { + /* Calculate authentication tag. */ + ret = wc_AesGcmEncryptFinal(&ctx->cipher.aes, + ctx->authTag, ctx->authTagSz); + /* + * wc_AesGcmEncryptFinal increments the IV in + * ctx->cipher.aes.reg, so we don't call IncCtr here. + */ + } + else { + /* Calculate authentication tag and compare. */ + ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes, + ctx->authTag, ctx->authTagSz); + if (ctx->gcmIncIv) { + IncCtr((byte*)ctx->cipher.aes.reg, + ctx->cipher.aes.nonceSz); + } + } + /* Reinitialize for subsequent wolfSSL_EVP_Cipher calls. */ + if (wc_AesGcmInit(&ctx->cipher.aes, NULL, 0, + (byte*)ctx->cipher.aes.reg, + ctx->ivSz) != 0) { + WOLFSSL_MSG("wc_AesGcmInit failed"); + return WOLFSSL_FATAL_ERROR; + } + ctx->gcmIncIv = 0; + } + #endif /* WOLFSSL_AESGCM_STREAM */ + if (src == NULL) { + /* + * Clear any leftover AAD on final (final is when src is + * NULL). + */ + XMEMSET(ctx->gcmAuthIn, 0, ctx->gcmAuthInSz); + ctx->gcmAuthInSz = 0; + } + if (ret == 0) { + ret = len; + } + + return ret; + } +#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) || + * HAVE_FIPS_VERSION >= 2 */ + /* return WOLFSSL_SUCCESS on ok, 0 on failure to match API compatibility */ - int wolfSSL_EVP_CipherInit(WOLFSSL_EVP_CIPHER_CTX* ctx, + int wolfSSL_EVP_CipherInit(WOLFSSL_EVP_CIPHER_CTX* ctx, const WOLFSSL_EVP_CIPHER* type, const byte* key, const byte* iv, int enc) { @@ -5648,6 +5916,8 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) ctx->gcmAuthIn = NULL; } ctx->gcmAuthInSz = 0; + ctx->gcmIvGenEnable = 0; + ctx->gcmIncIv = 0; #endif #ifndef NO_AES @@ -5732,125 +6002,29 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) } #endif /* WOLFSSL_AES_256 */ #endif /* HAVE_AES_CBC || WOLFSSL_AES_DIRECT */ -#if (!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \ - (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)) - #ifdef HAVE_AESGCM + #if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) + if ( #ifdef WOLFSSL_AES_128 - if (ctx->cipherType == AES_128_GCM_TYPE || - (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_GCM))) { - WOLFSSL_MSG("EVP_AES_128_GCM"); - ctx->cipherType = AES_128_GCM_TYPE; - ctx->flags &= ~WOLFSSL_EVP_CIPH_MODE; - ctx->flags |= WOLFSSL_EVP_CIPH_GCM_MODE | - WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER; - ctx->keyLen = 16; - ctx->block_size = AES_BLOCK_SIZE; - ctx->authTagSz = AES_BLOCK_SIZE; - if (ctx->ivSz == 0) { - ctx->ivSz = GCM_NONCE_MID_SZ; - } - -#ifndef WOLFSSL_AESGCM_STREAM - if (key && wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) { - WOLFSSL_MSG("wc_AesGcmSetKey() failed"); - return WOLFSSL_FAILURE; - } -#endif /* !WOLFSSL_AESGCM_STREAM */ - if (iv && wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) { - WOLFSSL_MSG("wc_AesGcmSetExtIV() failed"); - return WOLFSSL_FAILURE; - } -#ifdef WOLFSSL_AESGCM_STREAM - /* Initialize with key and IV if available. */ - if (wc_AesGcmInit(&ctx->cipher.aes, key, - (key == NULL) ? 0 : ctx->keyLen, iv, - (iv == NULL) ? 0 : ctx->ivSz) != 0) { - WOLFSSL_MSG("wc_AesGcmInit() failed"); - return WOLFSSL_FAILURE; - } -#endif /* WOLFSSL_AESGCM_STREAM */ - if (enc == 0 || enc == 1) - ctx->enc = enc ? 1 : 0; - } - #endif /* WOLFSSL_AES_128 */ + ctx->cipherType == AES_128_GCM_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_GCM)) + #endif #ifdef WOLFSSL_AES_192 - if (ctx->cipherType == AES_192_GCM_TYPE || - (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_GCM))) { - WOLFSSL_MSG("EVP_AES_192_GCM"); - ctx->cipherType = AES_192_GCM_TYPE; - ctx->flags &= ~WOLFSSL_EVP_CIPH_MODE; - ctx->flags |= WOLFSSL_EVP_CIPH_GCM_MODE | - WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER; - ctx->keyLen = 24; - ctx->block_size = AES_BLOCK_SIZE; - ctx->authTagSz = AES_BLOCK_SIZE; - if (ctx->ivSz == 0) { - ctx->ivSz = GCM_NONCE_MID_SZ; - } - -#ifndef WOLFSSL_AESGCM_STREAM - if (key && wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) { - WOLFSSL_MSG("wc_AesGcmSetKey() failed"); - return WOLFSSL_FAILURE; - } -#endif /* !WOLFSSL_AESGCM_STREAM */ - if (iv && wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) { - WOLFSSL_MSG("wc_AesGcmSetExtIV() failed"); - return WOLFSSL_FAILURE; - } -#ifdef WOLFSSL_AESGCM_STREAM - /* Initialize with key and IV if available. */ - if (wc_AesGcmInit(&ctx->cipher.aes, key, - (key == NULL) ? 0 : ctx->keyLen, iv, - (iv == NULL) ? 0 : ctx->ivSz) != 0) { - WOLFSSL_MSG("wc_AesGcmInit() failed"); - return WOLFSSL_FAILURE; - } -#endif /* WOLFSSL_AESGCM_STREAM */ - if (enc == 0 || enc == 1) - ctx->enc = enc ? 1 : 0; - } - #endif /* WOLFSSL_AES_192 */ + || ctx->cipherType == AES_192_GCM_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_GCM)) + #endif #ifdef WOLFSSL_AES_256 - if (ctx->cipherType == AES_256_GCM_TYPE || - (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_GCM))) { - WOLFSSL_MSG("EVP_AES_256_GCM"); - ctx->cipherType = AES_256_GCM_TYPE; - ctx->flags &= ~WOLFSSL_EVP_CIPH_MODE; - ctx->flags |= WOLFSSL_EVP_CIPH_GCM_MODE | - WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER; - ctx->keyLen = 32; - ctx->block_size = AES_BLOCK_SIZE; - ctx->authTagSz = AES_BLOCK_SIZE; - if (ctx->ivSz == 0) { - ctx->ivSz = GCM_NONCE_MID_SZ; - } - -#ifndef WOLFSSL_AESGCM_STREAM - if (key && wc_AesGcmSetKey(&ctx->cipher.aes, key, ctx->keyLen)) { - WOLFSSL_MSG("wc_AesGcmSetKey() failed"); + || ctx->cipherType == AES_256_GCM_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_GCM)) + #endif + ) { + if (EvpCipherInitAesGCM(ctx, type, key, iv, enc) + != WOLFSSL_SUCCESS) { return WOLFSSL_FAILURE; } -#endif /* !WOLFSSL_AESGCM_STREAM */ - if (iv && wc_AesGcmSetExtIV(&ctx->cipher.aes, iv, ctx->ivSz)) { - WOLFSSL_MSG("wc_AesGcmSetExtIV() failed"); - return WOLFSSL_FAILURE; - } -#ifdef WOLFSSL_AESGCM_STREAM - /* Initialize with key and IV if available. */ - if (wc_AesGcmInit(&ctx->cipher.aes, - key, (key == NULL) ? 0 : ctx->keyLen, - iv, (iv == NULL) ? 0 : ctx->ivSz) != 0) { - WOLFSSL_MSG("wc_AesGcmInit() failed"); - return WOLFSSL_FAILURE; - } -#endif /* WOLFSSL_AESGCM_STREAM */ - if (enc == 0 || enc == 1) - ctx->enc = enc ? 1 : 0; } - #endif /* WOLFSSL_AES_256 */ - #endif /* HAVE_AESGCM */ -#endif /* (!HAVE_FIPS && !HAVE_SELFTEST) || HAVE_FIPS_VERSION >= 2 */ + #endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) || + * HAVE_FIPS_VERSION >= 2 */ #ifdef WOLFSSL_AES_COUNTER #ifdef WOLFSSL_AES_128 if (ctx->cipherType == AES_128_CTR_TYPE || @@ -6596,31 +6770,23 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) /* Return length on ok */ int wolfSSL_EVP_Cipher(WOLFSSL_EVP_CIPHER_CTX* ctx, byte* dst, byte* src, - word32 len) + word32 len) { - int ret = 0; + int ret = WOLFSSL_FAILURE; + WOLFSSL_ENTER("wolfSSL_EVP_Cipher"); - if (ctx == NULL) { - WOLFSSL_MSG("Bad function argument"); + if (ctx == NULL || ((src == NULL || dst == NULL) && + (ctx->cipherType != AES_128_GCM_TYPE && + ctx->cipherType != AES_192_GCM_TYPE && + ctx->cipherType != AES_256_GCM_TYPE))) { + WOLFSSL_MSG("Bad argument."); return WOLFSSL_FATAL_ERROR; } - if (src == NULL || dst == NULL) { - if (src != NULL && dst == NULL && - (ctx->cipherType == AES_128_GCM_TYPE || - ctx->cipherType == AES_192_GCM_TYPE || - ctx->cipherType == AES_256_GCM_TYPE)) { - WOLFSSL_MSG("Setting GCM AAD."); - } - else { - WOLFSSL_MSG("Bad function argument"); - return WOLFSSL_FATAL_ERROR; - } - } - - if (ctx->cipherType == 0xff) { - WOLFSSL_MSG("no init"); + if (ctx->cipherType == WOLFSSL_EVP_CIPH_TYPE_INIT) { + WOLFSSL_MSG("Cipher operation not initialized. Call " + "wolfSSL_EVP_CipherInit."); return WOLFSSL_FATAL_ERROR; } @@ -6706,74 +6872,16 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) break; #endif /* WOLFSSL_AES_XTS */ -#ifdef HAVE_AESGCM +#if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ + || FIPS_VERSION_GE(2,0)) case AES_128_GCM_TYPE : case AES_192_GCM_TYPE : case AES_256_GCM_TYPE : WOLFSSL_MSG("AES GCM"); -#ifndef WOLFSSL_AESGCM_STREAM - /* No destination means only AAD. */ - if (!dst) { - ret = wolfSSL_EVP_CipherUpdate_GCM_AAD(ctx, src, len); - } - else { - if (ctx->enc) - ret = wc_AesGcmEncrypt(&ctx->cipher.aes, dst, src, - len, ctx->iv, ctx->ivSz, ctx->authTag, - ctx->authTagSz, ctx->gcmAuthIn, ctx->gcmAuthInSz); - else - ret = wc_AesGcmDecrypt(&ctx->cipher.aes, dst, src, - len, ctx->iv, ctx->ivSz, ctx->authTag, - ctx->authTagSz, ctx->gcmAuthIn, ctx->gcmAuthInSz); - } -#else - /* Do one shot operation with streaming API as other - * initialization set up for streaming. */ - ret = wc_AesGcmInit(&ctx->cipher.aes, NULL, 0, ctx->iv, - ctx->ivSz); - /* No destination means only AAD. */ - if ((ret == 0) && (dst == NULL)) { - if (ctx->enc) { - ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, NULL, - NULL, 0, src, len); - } - else { - ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, NULL, - NULL, 0, src, len); - } - } - /* Only plaintext/cipher text. */ - else if (ret == 0) { - if (ctx->enc) { - ret = wc_AesGcmEncryptUpdate(&ctx->cipher.aes, dst, src, - len, NULL, 0); - } - else { - ret = wc_AesGcmDecryptUpdate(&ctx->cipher.aes, dst, src, - len, NULL, 0); - if (ret == 0) { - ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes, - ctx->authTag, ctx->authTagSz); - } - } - } - if (ret == 0) { - if (ctx->enc) { - /* Calculate authentication tag. */ - ret = wc_AesGcmEncryptFinal(&ctx->cipher.aes, - ctx->authTag, ctx->authTagSz); - } - else { - /* Calculate authentication tag and compare. */ - ret = wc_AesGcmDecryptFinal(&ctx->cipher.aes, - ctx->authTag, ctx->authTagSz); - } - } -#endif /* WOLFSSL_AESGCM_STREAM */ - if (ret == 0) - ret = len; + ret = EvpCipherAesGCM(ctx, dst, src, len); break; -#endif /* HAVE_AESGCM */ +#endif /* HAVE_AESGCM && ((!HAVE_FIPS && !HAVE_SELFTEST) || + * HAVE_FIPS_VERSION >= 2 */ #ifdef HAVE_AES_ECB case AES_128_ECB_TYPE : case AES_192_ECB_TYPE : diff --git a/wolfssl/openssl/evp.h b/wolfssl/openssl/evp.h index d96a88281..7a612f838 100644 --- a/wolfssl/openssl/evp.h +++ b/wolfssl/openssl/evp.h @@ -438,6 +438,10 @@ struct WOLFSSL_EVP_CIPHER_CTX { #endif int authTagSz; #endif +#ifdef HAVE_AESGCM + byte gcmIvGenEnable:1; + byte gcmIncIv:1; +#endif #endif };