strongswan/src/libstrongswan/plugins/wolfssl/wolfssl_aead.c

467 lines
9.9 KiB
C

/*
* Copyright (C) 2019 Sean Parkinson, wolfSSL Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "wolfssl_common.h"
#if (!defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AESCCM))) || \
(defined(HAVE_CHACHA) && defined(HAVE_POLY1305))
#include "wolfssl_aead.h"
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/chacha.h>
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
#include <crypto/iv/iv_gen_seq.h>
/** as defined in RFC 4106 */
#define IV_LEN 8
#define GCM_SALT_LEN 4
#define GCM_NONCE_LEN (GCM_SALT_LEN + IV_LEN)
#define CCM_SALT_LEN 3
#define CCM_NONCE_LEN (CCM_SALT_LEN + IV_LEN)
typedef struct private_aead_t private_aead_t;
/**
* Private data of aead_t
*/
struct private_aead_t {
/**
* Public interface
*/
aead_t public;
/**
* The encryption key
*/
chunk_t key;
/**
* Salt value
*/
chunk_t salt;
/**
* Size of the integrity check value
*/
size_t icv_size;
/**
* IV generator
*/
iv_gen_t *iv_gen;
/**
* The cipher to use
*/
union {
#if !defined(NO_AES) && (defined(HAVE_AESGCM) || defined(HAVE_AESCCM))
Aes aes;
#endif
} cipher;
/**
* The cipher to use
*/
encryption_algorithm_t alg;
};
METHOD(aead_t, encrypt, bool,
private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
chunk_t *encrypted)
{
chunk_t nonce;
u_char *out;
bool success = FALSE;
int ret;
out = plain.ptr;
if (encrypted)
{
*encrypted = chunk_alloc(plain.len + this->icv_size);
out = encrypted->ptr;
}
nonce = chunk_cata("cc", this->salt, iv);
switch (this->alg)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
ret = wc_AesGcmSetKey(&this->cipher.aes, this->key.ptr,
this->key.len);
if (ret == 0)
{
ret = wc_AesGcmEncrypt(&this->cipher.aes, out, plain.ptr,
plain.len, nonce.ptr, GCM_NONCE_LEN, out + plain.len,
this->icv_size, assoc.ptr, assoc.len);
}
success = (ret == 0);
break;
#endif
#if !defined(NO_AES) && defined(HAVE_AESCCM)
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
/* wc_AesCcmEncrypt fails if the pointer is NULL */
if (!plain.ptr && !plain.len)
{
plain.ptr = nonce.ptr;
}
ret = wc_AesCcmSetKey(&this->cipher.aes, this->key.ptr,
this->key.len);
if (ret == 0)
{
ret = wc_AesCcmEncrypt(&this->cipher.aes, out, plain.ptr,
plain.len, nonce.ptr, CCM_NONCE_LEN, out + plain.len,
this->icv_size, assoc.ptr, assoc.len);
}
success = (ret == 0);
break;
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
case ENCR_CHACHA20_POLY1305:
ret = wc_ChaCha20Poly1305_Encrypt(this->key.ptr, nonce.ptr,
assoc.ptr, assoc.len, plain.ptr, plain.len, out,
out + plain.len);
success = (ret == 0);
break;
#endif
default:
break;
}
memwipe(nonce.ptr, nonce.len);
return success;
}
METHOD(aead_t, decrypt, bool,
private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv,
chunk_t *plain)
{
chunk_t nonce;
u_char *out;
bool success = FALSE;
int ret = 0;
if (encrypted.len < this->icv_size)
{
return FALSE;
}
encrypted.len -= this->icv_size;
out = encrypted.ptr;
if (plain)
{
*plain = chunk_alloc(encrypted.len);
out = plain->ptr;
}
nonce = chunk_cata("cc", this->salt, iv);
switch (this->alg)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
ret = wc_AesGcmSetKey(&this->cipher.aes, this->key.ptr,
this->key.len);
if (ret == 0)
{
ret = wc_AesGcmDecrypt(&this->cipher.aes, out, encrypted.ptr,
encrypted.len, nonce.ptr, GCM_NONCE_LEN,
encrypted.ptr + encrypted.len, this->icv_size,
assoc.ptr, assoc.len);
}
success = (ret == 0);
break;
#endif
#if !defined(NO_AES) && defined(HAVE_AESCCM)
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
/* wc_AesCcmDecrypt() fails if the pointers are NULL */
if (!encrypted.ptr && !encrypted.len)
{
encrypted.ptr = nonce.ptr;
}
if (!out && !encrypted.len)
{
out = nonce.ptr;
}
ret = wc_AesCcmSetKey(&this->cipher.aes, this->key.ptr,
this->key.len);
if (ret == 0)
{
ret = wc_AesCcmDecrypt(&this->cipher.aes, out, encrypted.ptr,
encrypted.len, nonce.ptr, CCM_NONCE_LEN,
encrypted.ptr + encrypted.len, this->icv_size,
assoc.ptr, assoc.len);
}
success = (ret == 0);
break;
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
case ENCR_CHACHA20_POLY1305:
ret = wc_ChaCha20Poly1305_Decrypt(this->key.ptr, nonce.ptr,
assoc.ptr, assoc.len, encrypted.ptr, encrypted.len,
encrypted.ptr + encrypted.len, out);
success = (ret == 0);
break;
#endif
default:
break;
}
memwipe(nonce.ptr, nonce.len);
return success;
}
METHOD(aead_t, get_block_size, size_t,
private_aead_t *this)
{
/* all AEAD algorithms are streaming */
return 1;
}
METHOD(aead_t, get_icv_size, size_t,
private_aead_t *this)
{
return this->icv_size;
}
METHOD(aead_t, get_iv_size, size_t,
private_aead_t *this)
{
return IV_LEN;
}
METHOD(aead_t, get_iv_gen, iv_gen_t*,
private_aead_t *this)
{
return this->iv_gen;
}
METHOD(aead_t, get_key_size, size_t,
private_aead_t *this)
{
return this->key.len + this->salt.len;
}
METHOD(aead_t, set_key, bool,
private_aead_t *this, chunk_t key)
{
if (key.len != get_key_size(this))
{
return FALSE;
}
memcpy(this->salt.ptr, key.ptr + key.len - this->salt.len, this->salt.len);
memcpy(this->key.ptr, key.ptr, this->key.len);
return TRUE;
}
METHOD(aead_t, destroy, void,
private_aead_t *this)
{
chunk_clear(&this->key);
chunk_clear(&this->salt);
switch (this->alg)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
wc_AesFree(&this->cipher.aes);
break;
#endif
#if !defined(NO_AES) && defined(HAVE_AESCCM)
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
wc_AesFree(&this->cipher.aes);
break;
#endif
default:
break;
}
this->iv_gen->destroy(this->iv_gen);
free(this);
}
/*
* Described in header
*/
aead_t *wolfssl_aead_create(encryption_algorithm_t algo,
size_t key_size, size_t salt_size)
{
private_aead_t *this;
size_t expected_salt_size;
INIT(this,
.public = {
.encrypt = _encrypt,
.decrypt = _decrypt,
.get_block_size = _get_block_size,
.get_icv_size = _get_icv_size,
.get_iv_size = _get_iv_size,
.get_iv_gen = _get_iv_gen,
.get_key_size = _get_key_size,
.set_key = _set_key,
.destroy = _destroy,
},
.alg = algo,
);
switch (algo)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
#if WOLFSSL_MIN_AUTH_TAG_SZ <= 8
case ENCR_AES_GCM_ICV8:
this->icv_size = 8;
break;
#endif
#if WOLFSSL_MIN_AUTH_TAG_SZ <= 12
case ENCR_AES_GCM_ICV12:
this->icv_size = 12;
break;
#endif
case ENCR_AES_GCM_ICV16:
this->icv_size = 16;
break;
#endif
#if !defined(NO_AES) && defined(HAVE_AESCCM)
case ENCR_AES_CCM_ICV8:
this->icv_size = 8;
break;
case ENCR_AES_CCM_ICV12:
this->icv_size = 12;
break;
case ENCR_AES_CCM_ICV16:
this->icv_size = 16;
break;
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
case ENCR_CHACHA20_POLY1305:
this->icv_size = 16;
break;
#endif
default:
free(this);
return NULL;
}
switch (algo)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
case ENCR_AES_GCM_ICV8:
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
switch (key_size)
{
case 0:
key_size = 16;
/* FALL */
case 16:
case 24:
case 32:
expected_salt_size = GCM_SALT_LEN;
if (wc_AesInit(&this->cipher.aes, NULL, INVALID_DEVID) != 0)
{
DBG1(DBG_LIB, "AES Init failed, aead create failed");
free(this);
return NULL;
}
break;
default:
free(this);
return NULL;
}
break;
#endif
#if !defined(NO_AES) && defined(HAVE_AESCCM)
case ENCR_AES_CCM_ICV8:
case ENCR_AES_CCM_ICV12:
case ENCR_AES_CCM_ICV16:
switch (key_size)
{
case 0:
key_size = 16;
/* FALL */
case 16:
case 24:
case 32:
expected_salt_size = CCM_SALT_LEN;
if (wc_AesInit(&this->cipher.aes, NULL, INVALID_DEVID) != 0)
{
DBG1(DBG_LIB, "AES Init failed, aead create failed");
free(this);
return NULL;
}
break;
default:
free(this);
return NULL;
}
break;
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
case ENCR_CHACHA20_POLY1305:
switch (key_size)
{
case 0:
key_size = 32;
/* FALL */
case 32:
expected_salt_size = 4;
break;
default:
free(this);
return NULL;
}
break;
#endif
default:
free(this);
return NULL;
}
if (salt_size && salt_size != expected_salt_size)
{
/* currently not supported */
free(this);
return NULL;
}
this->key = chunk_alloc(key_size);
this->salt = chunk_alloc(expected_salt_size);
this->iv_gen = iv_gen_seq_create();
return &this->public;
}
#endif