Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 59 additions & 74 deletions wolfssl-gnutls-wrapper/src/cipher.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <wolfssl/options.h>
#include "gnutls_compat.h"
#include "logging.h"
#include <stdlib.h>
#include <sys/random.h>
#include "mac.h"
#include <wolfssl/wolfcrypt/aes.h>

Expand Down Expand Up @@ -112,8 +114,6 @@ struct wolfssl_cipher_ctx {
size_t data_size;
/** Tag has been set. */
unsigned int tag_set:1;
/** Tag has been set from external source. */
unsigned int tag_set_ext:1;
};

/** Array of supported ciphers. */
Expand Down Expand Up @@ -654,7 +654,6 @@ int wolfssl_cipher_setiv(void *_ctx, const void *iv, size_t iv_size)
/* IV stored and used in encrypt/decrypt/tag. */
/* No tag set, auth data or plaintext now we have a new IV. */
ctx->tag_set = 0;
ctx->tag_set_ext = 0;
ctx->auth_data_size = 0;
ctx->data_size = 0;
break;
Expand Down Expand Up @@ -1042,31 +1041,28 @@ int wolfssl_cipher_decrypt(void *_ctx, const void *src, size_t src_size,
unsigned char *aad = ctx->auth_data_heap ?
ctx->auth_data_heap : ctx->auth_data_static;

/* If caller hasn't set tag then we are creating it. */
if (!ctx->tag_set_ext) {
/* Encrypt the ciphertext to get the plaintext.
* Tag will have been created on plaintext which is of no use.
*/
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, decr, ctx->data,
ctx->data_size, ctx->iv, ctx->iv_size,
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
if (ret != 0) {
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
gnutls_free(decr);
return GNUTLS_E_ENCRYPTION_FAILED;
}
/* Encrypt the plaintext to create the tag. */
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, decr, decr,
ctx->data_size, ctx->iv, ctx->iv_size,
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
if (ret != 0) {
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
gnutls_free(decr);
return GNUTLS_E_ENCRYPTION_FAILED;
}
/* A tag is now available. */
ctx->tag_set = 1;
/* Encrypt the ciphertext to get the plaintext.
* Tag will have been created on plaintext which is of no use.
*/
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, decr, ctx->data,
ctx->data_size, ctx->iv, ctx->iv_size,
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
if (ret != 0) {
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
gnutls_free(decr);
return GNUTLS_E_ENCRYPTION_FAILED;
}
/* Encrypt the plaintext to create the tag. */
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, decr, decr,
ctx->data_size, ctx->iv, ctx->iv_size,
ctx->tag, ctx->tag_size, aad, ctx->auth_data_size);
if (ret != 0) {
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
gnutls_free(decr);
return GNUTLS_E_ENCRYPTION_FAILED;
}
/* A tag is now available. */
ctx->tag_set = 1;
/* Do decryption with cipehtext, IV, authentication data and tag. */
ret = wc_AesGcmDecrypt(&ctx->cipher.aes_ctx, decr,
ctx->data, ctx->data_size, ctx->iv, ctx->iv_size,
Expand Down Expand Up @@ -1132,68 +1128,59 @@ void wolfssl_cipher_tag(void *_ctx, void *tag, size_t tag_size)
WGW_LOG("tag_size %zu", tag_size);

struct wolfssl_cipher_ctx *ctx = _ctx;
int ret = -1;

/* The tag is output only: gnutls compares it with the received tag after
* a decrypt, so it must always be written. When it cannot be computed it
* is filled with random bytes, which no received tag will match. */
Comment on lines +1133 to +1135
if (!ctx->initialized) {
WGW_LOG("cipher context not initialized");
return;
}

/* Make sure copied tag size is no larger than that generated. */
if (tag_size > ctx->tag_size) {
tag_size = ctx->tag_size;
}

/* Check if tag available. */
if (ctx->tag_set) {
if (ctx->mode == GCM) {
XMEMCPY(tag, ctx->tag, tag_size);
/* Authentication data used - reset count. */
ctx->auth_data_size = 0;
/* Dispose of cached data. */
gnutls_free(ctx->data);
ctx->data = NULL;
ctx->data_size = 0;
WGW_LOG("tag returned successfully");
} else {
WGW_LOG("AES mode not supported: %d", ctx->mode);
} else if (ctx->mode != GCM) {
WGW_LOG("AES mode not supported: %d", ctx->mode);
} else {
/* Make sure copied tag size is no larger than that generated. */
if (tag_size > ctx->tag_size) {
tag_size = ctx->tag_size;
}
} else if (ctx->enc) {
int ret = -1;

/* Encrypting and no tag set means we don't have plaintext. */
if (ctx->mode == GCM) {
WGW_LOG("wc_AesGcmEncrypt");

if (ctx->tag_set) {
/* Tag of the data encrypted or decrypted. */
ret = 0;
} else {
unsigned char *aad = ctx->auth_data_heap ?
ctx->auth_data_heap : ctx->auth_data_static;

/* Do authentication with no plaintext. */
/* No data: the tag is over the authentication data alone, the
* same for encryption and decryption. */
WGW_LOG("wc_AesGcmEncrypt");
ret = wc_AesGcmEncrypt(&ctx->cipher.aes_ctx, NULL, NULL, 0, ctx->iv,
ctx->iv_size, ctx->tag, ctx->tag_size, aad,
ctx->auth_data_size);
if (ret != 0) {
WGW_WOLFSSL_ERROR("wc_AesGcmEncrypt", ret);
} else {
/* Copy out tag. */
ctx->tag_set = 1;
XMEMCPY(tag, ctx->tag, tag_size);
WGW_LOG("tag stored successfully");
}
/* Authentication data used - reset count. */
ctx->auth_data_size = 0;
/* Dispose of cached plaintext. */
gnutls_free(ctx->data);
ctx->data = NULL;
ctx->data_size = 0;
} else {
WGW_LOG("AES mode not supported: %d", ctx->mode);
}
} else {
/* Decrypting and we need to set tag for decrypt operation. */
XMEMCPY(ctx->tag, tag, tag_size);
ctx->tag_set = 1;
ctx->tag_set_ext = 1;
WGW_LOG("tag provided successfully");
/* The tag ends this message: a handle reused without a new IV must
* not return it again. */
ctx->tag_set = 0;
/* Authentication data used - reset count. */
ctx->auth_data_size = 0;
/* Dispose of cached data. */
gnutls_free(ctx->data);
ctx->data = NULL;
ctx->data_size = 0;
}

if (ret == 0) {
XMEMCPY(tag, ctx->tag, tag_size);
WGW_LOG("tag returned successfully");
} else if ((gnutls_rnd(GNUTLS_RND_NONCE, tag, tag_size) != 0) &&
(getrandom(tag, tag_size, 0) != (ssize_t)tag_size)) {
/* No random tag possible: never return one a sender could match. */
WGW_ERROR("no random bytes for the tag");
abort();
}
}

Expand Down Expand Up @@ -1632,8 +1619,6 @@ int wolfssl_cipher_aead_decrypt(void *_ctx, const void *nonce,
return GNUTLS_E_SHORT_MEMORY_BUFFER;
}

ctx->enc = 0;

/* Encrypted size includes tag. */
encr_size -= tag_size;

Expand Down
145 changes: 97 additions & 48 deletions wolfssl-gnutls-wrapper/src/pk.c
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,37 @@ static int wolfssl_pk_encrypt(gnutls_pk_algorithm_t algo,
return ret;
}

/**
* Finish an RSA decrypt2 (caller-sized output buffer).
*
* The caller's buffer is written only when the decrypted length equals its
* size, by a constant-time masked select; on any failure (padding or length)
* it is left unchanged. TLS RSA key exchange prefills it with a random
* premaster and ignores the error, so a failure must not change it.
*
* @param [in, out] plaintext Caller's buffer and its size.
* @param [in] scratch Decrypted data, at least plaintext->size bytes.
* @param [in] ret Result of the wolfCrypt decryption.
* @return 0 on success.
* @return GNUTLS_E_DECRYPTION_FAILED otherwise.
*/
static int rsa_decrypt2_select(gnutls_datum_t *plaintext,
const unsigned char *scratch, int ret)
{
unsigned int diff = (unsigned int)ret ^ plaintext->size;
/* All ones when ret equals the size, else zero. */
unsigned int ok = ((diff | (0U - diff)) >> (sizeof(diff) * 8 - 1)) - 1U;
unsigned char mask = (unsigned char)ok;
unsigned int i;

for (i = 0; i < plaintext->size; i++) {
plaintext->data[i] = (unsigned char)((scratch[i] & mask) |
(plaintext->data[i] & (unsigned char)~mask));
}

return ok ? 0 : GNUTLS_E_DECRYPTION_FAILED;
}

/**
* Decrypt ciphertext using RSA PKCS#1 v1.5 with private key.
*
Expand All @@ -909,6 +940,7 @@ static int wolfssl_pk_decrypt_rsa(gnutls_datum_t *plaintext,
unsigned char out[1024];
unsigned char *plain;
word32 plain_size;
word32 scratch_size = 0;

WGW_FUNC_ENTER();

Expand Down Expand Up @@ -950,13 +982,24 @@ static int wolfssl_pk_decrypt_rsa(gnutls_datum_t *plaintext,
wc_FreeRsaKey(&rsa);
return GNUTLS_E_MEMORY_ERROR;
}
}
/* Set plain to valid buffer. */
if ((!alloc_plaintext) &&
(plaintext->size < (unsigned int)wc_RsaEncryptSize(&rsa))) {
plain = out;
} else {
plain = plaintext->data;
} else {
/* decrypt2: never decrypt into the caller's buffer (see
* rsa_decrypt2_select()); scratch holds the key size and the
* caller's size. */
scratch_size = plain_size > plaintext->size ? plain_size :
plaintext->size;
if (scratch_size <= sizeof(out)) {
plain = out;
} else {
plain = gnutls_malloc(scratch_size);
if (plain == NULL) {
WGW_ERROR("Allocating memory for plaintext");
wc_FreeRsaKey(&rsa);
return GNUTLS_E_MEMORY_ERROR;
}
}
XMEMSET(plain, 0, scratch_size);
}

PRIVATE_KEY_UNLOCK();
Expand All @@ -969,29 +1012,26 @@ static int wolfssl_pk_decrypt_rsa(gnutls_datum_t *plaintext,

/* No longer need RSA key. */
wc_FreeRsaKey(&rsa);

if (!alloc_plaintext) {
ret = rsa_decrypt2_select(plaintext, plain, ret);
gnutls_memset(plain, 0, scratch_size);
if (plain != out) {
gnutls_free(plain);
}
return ret;
}

if (ret < 0) {
WGW_WOLFSSL_ERROR("wc_RsaPrivateDecrypt", ret);
if (alloc_plaintext) {
/* Dispose of allocated buffer for plaintext. */
gnutls_free(plaintext->data);
/* Ensure output datum is empty on error. */
plaintext->data = NULL;
plaintext->size = 0;
}
/* Dispose of allocated buffer for plaintext. */
gnutls_free(plaintext->data);
/* Ensure output datum is empty on error. */
plaintext->data = NULL;
plaintext->size = 0;
return GNUTLS_E_DECRYPTION_FAILED;
}

/* Check if returning through another buffer. */
if (plain != plaintext->data) {
/* Ensure the output buffer is big enough. */
if ((unsigned int)ret > plaintext->size) {
WGW_ERROR("Decrypted data too big for plaintext buffer: %d > %d",
ret, plaintext->size);
return GNUTLS_E_DECRYPTION_FAILED;
}
/* Copy the decrypted data into output buffer. */
XMEMCPY(plaintext->data, plain, ret);
}
/* Set the actual size into output datum. */
plaintext->size = ret;

Expand Down Expand Up @@ -1021,6 +1061,7 @@ static int wolfssl_pk_decrypt_rsa_oaep(gnutls_datum_t *plaintext,
unsigned char out[1024];
unsigned char *plain;
word32 plain_size;
word32 scratch_size = 0;

WGW_FUNC_ENTER();

Expand Down Expand Up @@ -1064,13 +1105,24 @@ static int wolfssl_pk_decrypt_rsa_oaep(gnutls_datum_t *plaintext,
wc_FreeRsaKey(&rsa);
return GNUTLS_E_MEMORY_ERROR;
}
}
/* Set plain to valid buffer. */
if ((!alloc_plaintext) &&
(plaintext->size < (unsigned int)wc_RsaEncryptSize(&rsa))) {
plain = out;
} else {
plain = plaintext->data;
} else {
/* decrypt2: never decrypt into the caller's buffer (see
* rsa_decrypt2_select()); scratch holds the key size and the
* caller's size. */
scratch_size = plain_size > plaintext->size ? plain_size :
plaintext->size;
if (scratch_size <= sizeof(out)) {
plain = out;
} else {
plain = gnutls_malloc(scratch_size);
if (plain == NULL) {
WGW_ERROR("Allocating memory for plaintext");
wc_FreeRsaKey(&rsa);
return GNUTLS_E_MEMORY_ERROR;
}
}
XMEMSET(plain, 0, scratch_size);
}

PRIVATE_KEY_UNLOCK();
Expand All @@ -1084,29 +1136,26 @@ static int wolfssl_pk_decrypt_rsa_oaep(gnutls_datum_t *plaintext,

/* No longer need RSA key. */
wc_FreeRsaKey(&rsa);

if (!alloc_plaintext) {
ret = rsa_decrypt2_select(plaintext, plain, ret);
gnutls_memset(plain, 0, scratch_size);
Comment on lines +1140 to +1142
if (plain != out) {
gnutls_free(plain);
}
return ret;
}

if (ret < 0) {
WGW_WOLFSSL_ERROR("wc_RsaPublicDecrypt_ex", ret);
if (alloc_plaintext) {
/* Dispose of allocated buffer for plaintext. */
gnutls_free(plaintext->data);
/* Ensure output datum is empty on error. */
plaintext->data = NULL;
plaintext->size = 0;
}
/* Dispose of allocated buffer for plaintext. */
gnutls_free(plaintext->data);
/* Ensure output datum is empty on error. */
plaintext->data = NULL;
plaintext->size = 0;
return GNUTLS_E_DECRYPTION_FAILED;
}

/* Check if returning through another buffer. */
if (plain != plaintext->data) {
/* Ensure the output buffer is big enough. */
if ((unsigned int)ret > plaintext->size) {
WGW_ERROR("Decrypted data too big for plaintext buffer: %d > %d",
ret, plaintext->size);
return GNUTLS_E_DECRYPTION_FAILED;
}
/* Copy the decrypted data into output buffer. */
XMEMCPY(plaintext->data, plain, ret);
}
/* Set the actual size into output datum. */
plaintext->size = ret;

Expand Down
Loading
Loading