FreeRDP/winpr/libwinpr/crypto/cipher.c
2023-06-28 09:45:09 +02:00

863 lines
18 KiB
C

/**
* WinPR: Windows Portable Runtime
*
* Copyright 2015 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/crypto.h>
#include "../log.h"
#define TAG WINPR_TAG("crypto.cipher")
#if defined(WITH_INTERNAL_RC4)
#include "rc4.h"
#endif
#ifdef WITH_OPENSSL
#include <openssl/aes.h>
#include <openssl/rc4.h>
#include <openssl/des.h>
#include <openssl/evp.h>
#endif
#ifdef WITH_MBEDTLS
#include <mbedtls/md.h>
#include <mbedtls/aes.h>
#include <mbedtls/arc4.h>
#include <mbedtls/des.h>
#include <mbedtls/cipher.h>
#endif
/**
* RC4
*/
struct winpr_rc4_ctx_private_st
{
#if defined(WITH_INTERNAL_RC4)
winpr_int_RC4_CTX* ictx;
#else
#if defined(WITH_OPENSSL)
EVP_CIPHER_CTX* ctx;
#endif
#if defined(WITH_MBEDTLS) && defined(MBEDTLS_ARC4_C)
mbedtls_arc4_context* mctx;
#endif
#endif
};
static WINPR_RC4_CTX* winpr_RC4_New_Internal(const BYTE* key, size_t keylen, BOOL override_fips)
{
WINPR_RC4_CTX* ctx = NULL;
if (!key || (keylen == 0))
return NULL;
ctx = calloc(1, sizeof(WINPR_RC4_CTX));
if (!ctx)
return NULL;
#if defined(WITH_INTERNAL_RC4)
WINPR_UNUSED(override_fips);
ctx->ictx = winpr_int_rc4_new(key, keylen);
if (!ctx->ictx)
goto fail;
#elif defined(WITH_OPENSSL)
const EVP_CIPHER* evp = NULL;
if (keylen > INT_MAX)
goto fail;
ctx->ctx = EVP_CIPHER_CTX_new();
if (!ctx->ctx)
goto fail;
evp = EVP_rc4();
if (!evp)
goto fail;
EVP_CIPHER_CTX_init(ctx->ctx);
if (EVP_EncryptInit_ex(ctx->ctx, evp, NULL, NULL, NULL) != 1)
goto fail;
/* EVP_CIPH_FLAG_NON_FIPS_ALLOW does not exist before openssl 1.0.1 */
#if !(OPENSSL_VERSION_NUMBER < 0x10001000L)
if (override_fips == TRUE)
EVP_CIPHER_CTX_set_flags(ctx->ctx, EVP_CIPH_FLAG_NON_FIPS_ALLOW);
#endif
EVP_CIPHER_CTX_set_key_length(ctx->ctx, (int)keylen);
if (EVP_EncryptInit_ex(ctx->ctx, NULL, NULL, key, NULL) != 1)
goto fail;
#elif defined(WITH_MBEDTLS) && defined(MBEDTLS_ARC4_C)
ctx->mctx = calloc(1, sizeof(mbedtls_arc4_context));
if (!ctx->mctx)
goto fail;
mbedtls_arc4_init(ctx->mctx);
mbedtls_arc4_setup(ctx->mctx, key, (unsigned int)keylen);
#endif
return ctx;
fail:
winpr_RC4_Free(ctx);
return NULL;
}
WINPR_RC4_CTX* winpr_RC4_New_Allow_FIPS(const void* key, size_t keylen)
{
return winpr_RC4_New_Internal(key, keylen, TRUE);
}
WINPR_RC4_CTX* winpr_RC4_New(const void* key, size_t keylen)
{
return winpr_RC4_New_Internal(key, keylen, FALSE);
}
BOOL winpr_RC4_Update(WINPR_RC4_CTX* ctx, size_t length, const void* input, void* output)
{
WINPR_ASSERT(ctx);
#if defined(WITH_INTERNAL_RC4)
return winpr_int_rc4_update(ctx->ictx, length, input, output);
#elif defined(WITH_OPENSSL)
WINPR_ASSERT(ctx->ctx);
int outputLength;
if (length > INT_MAX)
return FALSE;
WINPR_ASSERT(ctx);
if (EVP_CipherUpdate(ctx->ctx, output, &outputLength, input, (int)length) != 1)
return FALSE;
return TRUE;
#elif defined(WITH_MBEDTLS) && defined(MBEDTLS_ARC4_C)
WINPR_ASSERT(ctx->mctx);
if (mbedtls_arc4_crypt(ctx->mctx, length, input, output) == 0)
return TRUE;
#endif
return FALSE;
}
void winpr_RC4_Free(WINPR_RC4_CTX* ctx)
{
if (!ctx)
return;
#if defined(WITH_INTERNAL_RC4)
winpr_int_rc4_free(ctx->ictx);
#elif defined(WITH_OPENSSL)
EVP_CIPHER_CTX_free(ctx->ctx);
#elif defined(WITH_MBEDTLS) && defined(MBEDTLS_ARC4_C)
mbedtls_arc4_free(ctx->mctx);
#endif
free(ctx);
}
/**
* Generic Cipher API
*/
#ifdef WITH_OPENSSL
extern const EVP_MD* winpr_openssl_get_evp_md(WINPR_MD_TYPE md);
#endif
#ifdef WITH_MBEDTLS
extern mbedtls_md_type_t winpr_mbedtls_get_md_type(int md);
#endif
#if defined(WITH_OPENSSL)
static const EVP_CIPHER* winpr_openssl_get_evp_cipher(int cipher)
{
const EVP_CIPHER* evp = NULL;
switch (cipher)
{
case WINPR_CIPHER_NULL:
evp = EVP_enc_null();
break;
case WINPR_CIPHER_AES_128_ECB:
evp = EVP_get_cipherbyname("aes-128-ecb");
break;
case WINPR_CIPHER_AES_192_ECB:
evp = EVP_get_cipherbyname("aes-192-ecb");
break;
case WINPR_CIPHER_AES_256_ECB:
evp = EVP_get_cipherbyname("aes-256-ecb");
break;
case WINPR_CIPHER_AES_128_CBC:
evp = EVP_get_cipherbyname("aes-128-cbc");
break;
case WINPR_CIPHER_AES_192_CBC:
evp = EVP_get_cipherbyname("aes-192-cbc");
break;
case WINPR_CIPHER_AES_256_CBC:
evp = EVP_get_cipherbyname("aes-256-cbc");
break;
case WINPR_CIPHER_AES_128_CFB128:
evp = EVP_get_cipherbyname("aes-128-cfb128");
break;
case WINPR_CIPHER_AES_192_CFB128:
evp = EVP_get_cipherbyname("aes-192-cfb128");
break;
case WINPR_CIPHER_AES_256_CFB128:
evp = EVP_get_cipherbyname("aes-256-cfb128");
break;
case WINPR_CIPHER_AES_128_CTR:
evp = EVP_get_cipherbyname("aes-128-ctr");
break;
case WINPR_CIPHER_AES_192_CTR:
evp = EVP_get_cipherbyname("aes-192-ctr");
break;
case WINPR_CIPHER_AES_256_CTR:
evp = EVP_get_cipherbyname("aes-256-ctr");
break;
case WINPR_CIPHER_AES_128_GCM:
evp = EVP_get_cipherbyname("aes-128-gcm");
break;
case WINPR_CIPHER_AES_192_GCM:
evp = EVP_get_cipherbyname("aes-192-gcm");
break;
case WINPR_CIPHER_AES_256_GCM:
evp = EVP_get_cipherbyname("aes-256-gcm");
break;
case WINPR_CIPHER_AES_128_CCM:
evp = EVP_get_cipherbyname("aes-128-ccm");
break;
case WINPR_CIPHER_AES_192_CCM:
evp = EVP_get_cipherbyname("aes-192-ccm");
break;
case WINPR_CIPHER_AES_256_CCM:
evp = EVP_get_cipherbyname("aes-256-ccm");
break;
case WINPR_CIPHER_CAMELLIA_128_ECB:
evp = EVP_get_cipherbyname("camellia-128-ecb");
break;
case WINPR_CIPHER_CAMELLIA_192_ECB:
evp = EVP_get_cipherbyname("camellia-192-ecb");
break;
case WINPR_CIPHER_CAMELLIA_256_ECB:
evp = EVP_get_cipherbyname("camellia-256-ecb");
break;
case WINPR_CIPHER_CAMELLIA_128_CBC:
evp = EVP_get_cipherbyname("camellia-128-cbc");
break;
case WINPR_CIPHER_CAMELLIA_192_CBC:
evp = EVP_get_cipherbyname("camellia-192-cbc");
break;
case WINPR_CIPHER_CAMELLIA_256_CBC:
evp = EVP_get_cipherbyname("camellia-256-cbc");
break;
case WINPR_CIPHER_CAMELLIA_128_CFB128:
evp = EVP_get_cipherbyname("camellia-128-cfb128");
break;
case WINPR_CIPHER_CAMELLIA_192_CFB128:
evp = EVP_get_cipherbyname("camellia-192-cfb128");
break;
case WINPR_CIPHER_CAMELLIA_256_CFB128:
evp = EVP_get_cipherbyname("camellia-256-cfb128");
break;
case WINPR_CIPHER_CAMELLIA_128_CTR:
evp = EVP_get_cipherbyname("camellia-128-ctr");
break;
case WINPR_CIPHER_CAMELLIA_192_CTR:
evp = EVP_get_cipherbyname("camellia-192-ctr");
break;
case WINPR_CIPHER_CAMELLIA_256_CTR:
evp = EVP_get_cipherbyname("camellia-256-ctr");
break;
case WINPR_CIPHER_CAMELLIA_128_GCM:
evp = EVP_get_cipherbyname("camellia-128-gcm");
break;
case WINPR_CIPHER_CAMELLIA_192_GCM:
evp = EVP_get_cipherbyname("camellia-192-gcm");
break;
case WINPR_CIPHER_CAMELLIA_256_GCM:
evp = EVP_get_cipherbyname("camellia-256-gcm");
break;
case WINPR_CIPHER_CAMELLIA_128_CCM:
evp = EVP_get_cipherbyname("camellia-128-ccm");
break;
case WINPR_CIPHER_CAMELLIA_192_CCM:
evp = EVP_get_cipherbyname("camellia-192-gcm");
break;
case WINPR_CIPHER_CAMELLIA_256_CCM:
evp = EVP_get_cipherbyname("camellia-256-gcm");
break;
case WINPR_CIPHER_DES_ECB:
evp = EVP_get_cipherbyname("des-ecb");
break;
case WINPR_CIPHER_DES_CBC:
evp = EVP_get_cipherbyname("des-cbc");
break;
case WINPR_CIPHER_DES_EDE_ECB:
evp = EVP_get_cipherbyname("des-ede-ecb");
break;
case WINPR_CIPHER_DES_EDE_CBC:
evp = EVP_get_cipherbyname("des-ede-cbc");
break;
case WINPR_CIPHER_DES_EDE3_ECB:
evp = EVP_get_cipherbyname("des-ede3-ecb");
break;
case WINPR_CIPHER_DES_EDE3_CBC:
evp = EVP_get_cipherbyname("des-ede3-cbc");
break;
case WINPR_CIPHER_ARC4_128:
evp = EVP_get_cipherbyname("rc4");
break;
case WINPR_CIPHER_BLOWFISH_ECB:
evp = EVP_get_cipherbyname("blowfish-ecb");
break;
case WINPR_CIPHER_BLOWFISH_CBC:
evp = EVP_get_cipherbyname("blowfish-cbc");
break;
case WINPR_CIPHER_BLOWFISH_CFB64:
evp = EVP_get_cipherbyname("blowfish-cfb64");
break;
case WINPR_CIPHER_BLOWFISH_CTR:
evp = EVP_get_cipherbyname("blowfish-ctr");
break;
}
return evp;
}
#elif defined(WITH_MBEDTLS)
mbedtls_cipher_type_t winpr_mbedtls_get_cipher_type(int cipher)
{
mbedtls_cipher_type_t type = MBEDTLS_CIPHER_NONE;
switch (cipher)
{
case WINPR_CIPHER_NONE:
type = MBEDTLS_CIPHER_NONE;
break;
case WINPR_CIPHER_NULL:
type = MBEDTLS_CIPHER_NULL;
break;
case WINPR_CIPHER_AES_128_ECB:
type = MBEDTLS_CIPHER_AES_128_ECB;
break;
case WINPR_CIPHER_AES_192_ECB:
type = MBEDTLS_CIPHER_AES_192_ECB;
break;
case WINPR_CIPHER_AES_256_ECB:
type = MBEDTLS_CIPHER_AES_256_ECB;
break;
case WINPR_CIPHER_AES_128_CBC:
type = MBEDTLS_CIPHER_AES_128_CBC;
break;
case WINPR_CIPHER_AES_192_CBC:
type = MBEDTLS_CIPHER_AES_192_CBC;
break;
case WINPR_CIPHER_AES_256_CBC:
type = MBEDTLS_CIPHER_AES_256_CBC;
break;
case WINPR_CIPHER_AES_128_CFB128:
type = MBEDTLS_CIPHER_AES_128_CFB128;
break;
case WINPR_CIPHER_AES_192_CFB128:
type = MBEDTLS_CIPHER_AES_192_CFB128;
break;
case WINPR_CIPHER_AES_256_CFB128:
type = MBEDTLS_CIPHER_AES_256_CFB128;
break;
case WINPR_CIPHER_AES_128_CTR:
type = MBEDTLS_CIPHER_AES_128_CTR;
break;
case WINPR_CIPHER_AES_192_CTR:
type = MBEDTLS_CIPHER_AES_192_CTR;
break;
case WINPR_CIPHER_AES_256_CTR:
type = MBEDTLS_CIPHER_AES_256_CTR;
break;
case WINPR_CIPHER_AES_128_GCM:
type = MBEDTLS_CIPHER_AES_128_GCM;
break;
case WINPR_CIPHER_AES_192_GCM:
type = MBEDTLS_CIPHER_AES_192_GCM;
break;
case WINPR_CIPHER_AES_256_GCM:
type = MBEDTLS_CIPHER_AES_256_GCM;
break;
case WINPR_CIPHER_CAMELLIA_128_ECB:
type = MBEDTLS_CIPHER_CAMELLIA_128_ECB;
break;
case WINPR_CIPHER_CAMELLIA_192_ECB:
type = MBEDTLS_CIPHER_CAMELLIA_192_ECB;
break;
case WINPR_CIPHER_CAMELLIA_256_ECB:
type = MBEDTLS_CIPHER_CAMELLIA_256_ECB;
break;
case WINPR_CIPHER_CAMELLIA_128_CBC:
type = MBEDTLS_CIPHER_CAMELLIA_128_CBC;
break;
case WINPR_CIPHER_CAMELLIA_192_CBC:
type = MBEDTLS_CIPHER_CAMELLIA_192_CBC;
break;
case WINPR_CIPHER_CAMELLIA_256_CBC:
type = MBEDTLS_CIPHER_CAMELLIA_256_CBC;
break;
case WINPR_CIPHER_CAMELLIA_128_CFB128:
type = MBEDTLS_CIPHER_CAMELLIA_128_CFB128;
break;
case WINPR_CIPHER_CAMELLIA_192_CFB128:
type = MBEDTLS_CIPHER_CAMELLIA_192_CFB128;
break;
case WINPR_CIPHER_CAMELLIA_256_CFB128:
type = MBEDTLS_CIPHER_CAMELLIA_256_CFB128;
break;
case WINPR_CIPHER_CAMELLIA_128_CTR:
type = MBEDTLS_CIPHER_CAMELLIA_128_CTR;
break;
case WINPR_CIPHER_CAMELLIA_192_CTR:
type = MBEDTLS_CIPHER_CAMELLIA_192_CTR;
break;
case WINPR_CIPHER_CAMELLIA_256_CTR:
type = MBEDTLS_CIPHER_CAMELLIA_256_CTR;
break;
case WINPR_CIPHER_CAMELLIA_128_GCM:
type = MBEDTLS_CIPHER_CAMELLIA_128_GCM;
break;
case WINPR_CIPHER_CAMELLIA_192_GCM:
type = MBEDTLS_CIPHER_CAMELLIA_192_GCM;
break;
case WINPR_CIPHER_CAMELLIA_256_GCM:
type = MBEDTLS_CIPHER_CAMELLIA_256_GCM;
break;
case WINPR_CIPHER_DES_ECB:
type = MBEDTLS_CIPHER_DES_ECB;
break;
case WINPR_CIPHER_DES_CBC:
type = MBEDTLS_CIPHER_DES_CBC;
break;
case WINPR_CIPHER_DES_EDE_ECB:
type = MBEDTLS_CIPHER_DES_EDE_ECB;
break;
case WINPR_CIPHER_DES_EDE_CBC:
type = MBEDTLS_CIPHER_DES_EDE_CBC;
break;
case WINPR_CIPHER_DES_EDE3_ECB:
type = MBEDTLS_CIPHER_DES_EDE3_ECB;
break;
case WINPR_CIPHER_DES_EDE3_CBC:
type = MBEDTLS_CIPHER_DES_EDE3_CBC;
break;
case WINPR_CIPHER_BLOWFISH_ECB:
type = MBEDTLS_CIPHER_BLOWFISH_ECB;
break;
case WINPR_CIPHER_BLOWFISH_CBC:
type = MBEDTLS_CIPHER_BLOWFISH_CBC;
break;
case WINPR_CIPHER_BLOWFISH_CFB64:
type = MBEDTLS_CIPHER_BLOWFISH_CFB64;
break;
case WINPR_CIPHER_BLOWFISH_CTR:
type = MBEDTLS_CIPHER_BLOWFISH_CTR;
break;
case WINPR_CIPHER_ARC4_128:
type = MBEDTLS_CIPHER_ARC4_128;
break;
case WINPR_CIPHER_AES_128_CCM:
type = MBEDTLS_CIPHER_AES_128_CCM;
break;
case WINPR_CIPHER_AES_192_CCM:
type = MBEDTLS_CIPHER_AES_192_CCM;
break;
case WINPR_CIPHER_AES_256_CCM:
type = MBEDTLS_CIPHER_AES_256_CCM;
break;
case WINPR_CIPHER_CAMELLIA_128_CCM:
type = MBEDTLS_CIPHER_CAMELLIA_128_CCM;
break;
case WINPR_CIPHER_CAMELLIA_192_CCM:
type = MBEDTLS_CIPHER_CAMELLIA_192_CCM;
break;
case WINPR_CIPHER_CAMELLIA_256_CCM:
type = MBEDTLS_CIPHER_CAMELLIA_256_CCM;
break;
}
return type;
}
#endif
WINPR_CIPHER_CTX* winpr_Cipher_New(int cipher, int op, const void* key, const void* iv)
{
WINPR_CIPHER_CTX* ctx = NULL;
#if defined(WITH_OPENSSL)
int operation;
const EVP_CIPHER* evp;
EVP_CIPHER_CTX* octx;
if (!(evp = winpr_openssl_get_evp_cipher(cipher)))
return NULL;
if (!(octx = EVP_CIPHER_CTX_new()))
return NULL;
operation = (op == WINPR_ENCRYPT) ? 1 : 0;
if (EVP_CipherInit_ex(octx, evp, NULL, key, iv, operation) != 1)
{
EVP_CIPHER_CTX_free(octx);
return NULL;
}
EVP_CIPHER_CTX_set_padding(octx, 0);
ctx = (WINPR_CIPHER_CTX*)octx;
#elif defined(WITH_MBEDTLS)
int key_bitlen;
mbedtls_operation_t operation;
mbedtls_cipher_context_t* mctx;
mbedtls_cipher_type_t cipher_type = winpr_mbedtls_get_cipher_type(cipher);
const mbedtls_cipher_info_t* cipher_info = mbedtls_cipher_info_from_type(cipher_type);
if (!cipher_info)
return NULL;
if (!(mctx = (mbedtls_cipher_context_t*)calloc(1, sizeof(mbedtls_cipher_context_t))))
return NULL;
operation = (op == WINPR_ENCRYPT) ? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT;
mbedtls_cipher_init(mctx);
if (mbedtls_cipher_setup(mctx, cipher_info) != 0)
{
free(mctx);
return NULL;
}
key_bitlen = mbedtls_cipher_get_key_bitlen(mctx);
if (mbedtls_cipher_setkey(mctx, key, key_bitlen, operation) != 0)
{
mbedtls_cipher_free(mctx);
free(mctx);
return NULL;
}
if (mbedtls_cipher_set_padding_mode(mctx, MBEDTLS_PADDING_NONE) != 0)
{
mbedtls_cipher_free(mctx);
free(mctx);
return NULL;
}
ctx = (WINPR_CIPHER_CTX*)mctx;
#endif
return ctx;
}
BOOL winpr_Cipher_Update(WINPR_CIPHER_CTX* ctx, const void* input, size_t ilen, void* output,
size_t* olen)
{
#if defined(WITH_OPENSSL)
int outl = (int)*olen;
if (ilen > INT_MAX)
{
WLog_ERR(TAG, "input length %" PRIuz " > %d, abort", ilen, INT_MAX);
return FALSE;
}
WINPR_ASSERT(ctx);
if (EVP_CipherUpdate((EVP_CIPHER_CTX*)ctx, output, &outl, input, (int)ilen) == 1)
{
*olen = (size_t)outl;
return TRUE;
}
#elif defined(WITH_MBEDTLS)
if (mbedtls_cipher_update((mbedtls_cipher_context_t*)ctx, input, ilen, output, olen) == 0)
return TRUE;
#endif
WLog_ERR(TAG, "Failed to update the data");
return FALSE;
}
BOOL winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, void* output, size_t* olen)
{
#if defined(WITH_OPENSSL)
int outl = (int)*olen;
if (EVP_CipherFinal_ex((EVP_CIPHER_CTX*)ctx, output, &outl) == 1)
{
*olen = (size_t)outl;
return TRUE;
}
#elif defined(WITH_MBEDTLS)
if (mbedtls_cipher_finish((mbedtls_cipher_context_t*)ctx, output, olen) == 0)
return TRUE;
#endif
return FALSE;
}
void winpr_Cipher_Free(WINPR_CIPHER_CTX* ctx)
{
if (!ctx)
return;
#if defined(WITH_OPENSSL)
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)ctx);
#elif defined(WITH_MBEDTLS)
mbedtls_cipher_free((mbedtls_cipher_context_t*)ctx);
free(ctx);
#endif
}
/**
* Key Generation
*/
int winpr_Cipher_BytesToKey(int cipher, WINPR_MD_TYPE md, const void* salt, const void* data,
size_t datal, size_t count, void* key, void* iv)
{
/**
* Key and IV generation compatible with OpenSSL EVP_BytesToKey():
* https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html
*/
#if defined(WITH_OPENSSL)
const EVP_MD* evp_md;
const EVP_CIPHER* evp_cipher;
evp_md = winpr_openssl_get_evp_md((WINPR_MD_TYPE)md);
evp_cipher = winpr_openssl_get_evp_cipher(cipher);
return EVP_BytesToKey(evp_cipher, evp_md, salt, data, datal, count, key, iv);
#elif defined(WITH_MBEDTLS)
int rv = 0;
BYTE md_buf[64];
int niv, nkey, addmd = 0;
unsigned int mds = 0, i;
mbedtls_md_context_t ctx;
const mbedtls_md_info_t* md_info;
mbedtls_cipher_type_t cipher_type;
const mbedtls_cipher_info_t* cipher_info;
mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md);
md_info = mbedtls_md_info_from_type(md_type);
cipher_type = winpr_mbedtls_get_cipher_type(cipher);
cipher_info = mbedtls_cipher_info_from_type(cipher_type);
nkey = cipher_info->key_bitlen / 8;
niv = cipher_info->iv_size;
if ((nkey > 64) || (niv > 64))
return 0;
if (!data)
return nkey;
mbedtls_md_init(&ctx);
if (mbedtls_md_setup(&ctx, md_info, 0) != 0)
goto err;
while (1)
{
if (mbedtls_md_starts(&ctx) != 0)
goto err;
if (addmd++)
{
if (mbedtls_md_update(&ctx, md_buf, mds) != 0)
goto err;
}
if (mbedtls_md_update(&ctx, data, datal) != 0)
goto err;
if (salt)
{
if (mbedtls_md_update(&ctx, salt, 8) != 0)
goto err;
}
if (mbedtls_md_finish(&ctx, md_buf) != 0)
goto err;
mds = mbedtls_md_get_size(md_info);
for (i = 1; i < (unsigned int)count; i++)
{
if (mbedtls_md_starts(&ctx) != 0)
goto err;
if (mbedtls_md_update(&ctx, md_buf, mds) != 0)
goto err;
if (mbedtls_md_finish(&ctx, md_buf) != 0)
goto err;
}
i = 0;
if (nkey)
{
while (1)
{
if (nkey == 0)
break;
if (i == mds)
break;
if (key)
*(key++) = md_buf[i];
nkey--;
i++;
}
}
if (niv && (i != mds))
{
while (1)
{
if (niv == 0)
break;
if (i == mds)
break;
if (iv)
*(iv++) = md_buf[i];
niv--;
i++;
}
}
if ((nkey == 0) && (niv == 0))
break;
}
rv = cipher_info->key_bitlen / 8;
err:
mbedtls_md_free(&ctx);
SecureZeroMemory(md_buf, 64);
return rv;
#else
return 0;
#endif
}