| /* |
| * Copyright (C) 2019 Sean Parkinson, wolfSSL Inc. |
| * Copyright (C) 2021 Andreas Steffen, strongSec GmbH |
| * |
| * 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_crypter.h" |
| |
| #include "wolfssl_common.h" |
| #include <wolfssl/wolfcrypt/aes.h> |
| #include <wolfssl/wolfcrypt/camellia.h> |
| #include <wolfssl/wolfcrypt/des3.h> |
| |
| #include <utils/debug.h> |
| |
| typedef struct private_wolfssl_crypter_t private_wolfssl_crypter_t; |
| |
| #define CTR_SALT_LEN 4 |
| |
| /** |
| * Private data of wolfssl_crypter_t |
| */ |
| struct private_wolfssl_crypter_t { |
| |
| /** |
| * Public part of this class. |
| */ |
| wolfssl_crypter_t public; |
| |
| /** |
| * wolfSSL cipher |
| */ |
| union { |
| #if !defined(NO_AES) && (!defined(NO_AES_CBC) || defined(HAVE_AES_ECB) || defined(WOLFSSL_AES_COUNTER)) |
| Aes aes; |
| #endif |
| #ifdef HAVE_CAMELLIA |
| Camellia camellia; |
| #endif |
| #ifndef NO_DES3 |
| Des des; |
| Des3 des3; |
| #endif |
| } cipher; |
| |
| /** |
| * Encryption algorithm identifier |
| */ |
| encryption_algorithm_t alg; |
| |
| /** |
| * Private key |
| */ |
| chunk_t key; |
| |
| /** |
| * Salt value |
| */ |
| chunk_t salt; |
| |
| /** |
| * Size of block |
| */ |
| size_t block_size; |
| |
| /** |
| * Size of IV |
| */ |
| size_t iv_size; |
| }; |
| |
| METHOD(crypter_t, decrypt, bool, |
| private_wolfssl_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) |
| { |
| u_char *out; |
| bool success = FALSE; |
| int ret; |
| u_char nonce[AES_BLOCK_SIZE] = {0}; |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| chunk_t d = chunk_empty; |
| #endif |
| |
| out = data.ptr; |
| if (dst) |
| { |
| *dst = chunk_alloc(data.len); |
| out = dst->ptr; |
| } |
| |
| if (this->salt.len > 0) |
| { |
| memcpy(nonce, this->salt.ptr, this->salt.len); |
| memcpy(nonce + this->salt.len, iv.ptr, this->iv_size); |
| nonce[AES_BLOCK_SIZE - 1] = 1; |
| } |
| |
| switch (this->alg) |
| { |
| case ENCR_NULL: |
| memcpy(out, data.ptr, data.len); |
| success = TRUE; |
| break; |
| #if !defined(NO_AES) && !defined(NO_AES_CBC) |
| case ENCR_AES_CBC: |
| ret = wc_AesSetKey(&this->cipher.aes, this->key.ptr, this->key.len, |
| iv.ptr, AES_DECRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_AesCbcDecrypt(&this->cipher.aes, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(HAVE_AES_ECB) |
| case ENCR_AES_ECB: |
| ret = wc_AesSetKey(&this->cipher.aes, this->key.ptr, this->key.len, |
| iv.ptr, AES_DECRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_AesEcbDecrypt(&this->cipher.aes, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| case ENCR_AES_CTR: |
| if (out == data.ptr) |
| { |
| d = chunk_alloc(data.len); |
| out = d.ptr; |
| } |
| ret = wc_AesSetKeyDirect(&this->cipher.aes, this->key.ptr, |
| this->key.len, nonce, AES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_AesCtrEncrypt(&this->cipher.aes, out, data.ptr, |
| data.len); |
| } |
| if (ret == 0 && out == d.ptr) |
| { |
| memcpy(data.ptr, out, data.len); |
| } |
| chunk_free(&d); |
| success = (ret == 0); |
| break; |
| #endif |
| #ifdef HAVE_CAMELLIA |
| case ENCR_CAMELLIA_CBC: |
| ret = wc_CamelliaSetKey(&this->cipher.camellia, this->key.ptr, |
| this->key.len, iv.ptr); |
| if (ret == 0) |
| { |
| ret = wc_CamelliaCbcDecrypt(&this->cipher.camellia, out, |
| data.ptr, data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #ifndef NO_DES3 |
| case ENCR_3DES: |
| ret = wc_Des3_SetKey(&this->cipher.des3, this->key.ptr, iv.ptr, |
| DES_DECRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_Des3_CbcDecrypt(&this->cipher.des3, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| case ENCR_DES: |
| ret = wc_Des_SetKey(&this->cipher.des, this->key.ptr, iv.ptr, |
| DES_DECRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_Des_CbcDecrypt(&this->cipher.des, out, data.ptr, |
| data.len); |
| } |
| if (ret == 0) |
| success = TRUE; |
| break; |
| #ifdef WOLFSSL_DES_ECB |
| case ENCR_DES_ECB: |
| ret = wc_Des_SetKey(&this->cipher.des, this->key.ptr, iv.ptr, |
| DES_DECRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_Des_EcbDecrypt(&this->cipher.des, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #endif |
| default: |
| break; |
| } |
| |
| memwipe(nonce, sizeof(nonce)); |
| return success; |
| } |
| |
| METHOD(crypter_t, encrypt, bool, |
| private_wolfssl_crypter_t *this, chunk_t data, chunk_t iv, chunk_t *dst) |
| { |
| u_char *out; |
| bool success = FALSE; |
| int ret; |
| u_char nonce[AES_BLOCK_SIZE] = {0}; |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| chunk_t d = chunk_empty; |
| #endif |
| |
| out = data.ptr; |
| if (dst) |
| { |
| *dst = chunk_alloc(data.len); |
| out = dst->ptr; |
| } |
| |
| if (this->salt.len > 0) |
| { |
| memcpy(nonce, this->salt.ptr, this->salt.len); |
| memcpy(nonce + this->salt.len, iv.ptr, this->iv_size); |
| nonce[AES_BLOCK_SIZE - 1] = 1; |
| } |
| |
| switch (this->alg) |
| { |
| case ENCR_NULL: |
| memcpy(out, data.ptr, data.len); |
| success = TRUE; |
| break; |
| #if !defined(NO_AES) && !defined(NO_AES_CBC) |
| case ENCR_AES_CBC: |
| ret = wc_AesSetKey(&this->cipher.aes, this->key.ptr, this->key.len, |
| iv.ptr, AES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_AesCbcEncrypt(&this->cipher.aes, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(HAVE_AES_ECB) |
| case ENCR_AES_ECB: |
| ret = wc_AesSetKey(&this->cipher.aes, this->key.ptr, this->key.len, |
| iv.ptr, AES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_AesEcbEncrypt(&this->cipher.aes, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| case ENCR_AES_CTR: |
| if (out == data.ptr) |
| { |
| d = chunk_alloc(data.len); |
| out = d.ptr; |
| } |
| ret = wc_AesSetKeyDirect(&this->cipher.aes, this->key.ptr, |
| this->key.len, nonce, AES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_AesCtrEncrypt(&this->cipher.aes, out, data.ptr, |
| data.len); |
| } |
| if (ret == 0 && out == d.ptr) |
| { |
| memcpy(data.ptr, out, data.len); |
| } |
| chunk_free(&d); |
| success = (ret == 0); |
| break; |
| #endif |
| #ifdef HAVE_CAMELLIA |
| case ENCR_CAMELLIA_CBC: |
| ret = wc_CamelliaSetKey(&this->cipher.camellia, this->key.ptr, |
| this->key.len, iv.ptr); |
| if (ret == 0) |
| { |
| ret = wc_CamelliaCbcEncrypt(&this->cipher.camellia, out, |
| data.ptr, data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #ifndef NO_DES3 |
| case ENCR_3DES: |
| ret = wc_Des3_SetKey(&this->cipher.des3, this->key.ptr, iv.ptr, |
| DES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_Des3_CbcEncrypt(&this->cipher.des3, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| case ENCR_DES: |
| ret = wc_Des_SetKey(&this->cipher.des, this->key.ptr, iv.ptr, |
| DES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_Des_CbcEncrypt(&this->cipher.des, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #ifdef WOLFSSL_DES_ECB |
| case ENCR_DES_ECB: |
| ret = wc_Des_SetKey(&this->cipher.des, this->key.ptr, iv.ptr, |
| DES_ENCRYPTION); |
| if (ret == 0) |
| { |
| ret = wc_Des_EcbEncrypt(&this->cipher.des, out, data.ptr, |
| data.len); |
| } |
| success = (ret == 0); |
| break; |
| #endif |
| #endif |
| default: |
| break; |
| } |
| |
| return success; |
| } |
| |
| METHOD(crypter_t, get_block_size, size_t, |
| private_wolfssl_crypter_t *this) |
| { |
| return this->block_size; |
| } |
| |
| METHOD(crypter_t, get_iv_size, size_t, |
| private_wolfssl_crypter_t *this) |
| { |
| return this->iv_size; |
| } |
| |
| METHOD(crypter_t, get_key_size, size_t, |
| private_wolfssl_crypter_t *this) |
| { |
| return this->key.len + this->salt.len; |
| } |
| |
| METHOD(crypter_t, set_key, bool, |
| private_wolfssl_crypter_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(crypter_t, destroy, void, |
| private_wolfssl_crypter_t *this) |
| { |
| chunk_clear(&this->key); |
| chunk_clear(&this->salt); |
| switch (this->alg) |
| { |
| #if !defined(NO_AES) && !defined(NO_AES_CBC) |
| case ENCR_AES_CBC: |
| wc_AesFree(&this->cipher.aes); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(HAVE_AES_ECB) |
| case ENCR_AES_ECB: |
| wc_AesFree(&this->cipher.aes); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| case ENCR_AES_CTR: |
| wc_AesFree(&this->cipher.aes); |
| break; |
| #endif |
| #ifndef NO_DES3 |
| case ENCR_3DES: |
| wc_Des3Free(&this->cipher.des3); |
| break; |
| #endif |
| default: |
| break; |
| } |
| free(this); |
| } |
| |
| /* |
| * Described in header |
| */ |
| wolfssl_crypter_t *wolfssl_crypter_create(encryption_algorithm_t algo, |
| size_t key_size) |
| { |
| private_wolfssl_crypter_t *this; |
| size_t block_size; |
| size_t iv_size; |
| size_t salt_len = 0; |
| int ret = 0; |
| |
| switch (algo) |
| { |
| case ENCR_NULL: |
| key_size = 0; |
| block_size = 1; |
| iv_size = 0; |
| break; |
| #if !defined(NO_AES) && !defined(NO_AES_CBC) |
| case ENCR_AES_CBC: |
| switch (key_size) |
| { |
| case 0: |
| key_size = 16; |
| /* fall-through */ |
| case 16: |
| case 24: |
| case 32: |
| block_size = AES_BLOCK_SIZE; |
| iv_size = AES_IV_SIZE; |
| break; |
| default: |
| return NULL; |
| } |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(HAVE_AES_ECB) |
| case ENCR_AES_ECB: |
| switch (key_size) |
| { |
| case 0: |
| key_size = 16; |
| /* fall-through */ |
| case 16: |
| case 24: |
| case 32: |
| block_size = AES_BLOCK_SIZE; |
| iv_size = AES_IV_SIZE; |
| break; |
| default: |
| return NULL; |
| } |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| case ENCR_AES_CTR: |
| switch (key_size) |
| { |
| case 0: |
| key_size = 16; |
| /* fall-through */ |
| case 16: |
| case 24: |
| case 32: |
| block_size = 1; |
| iv_size = 8; |
| salt_len = CTR_SALT_LEN; |
| break; |
| default: |
| return NULL; |
| } |
| break; |
| #endif |
| #ifdef HAVE_CAMELLIA |
| case ENCR_CAMELLIA_CBC: |
| switch (key_size) |
| { |
| case 0: |
| key_size = 16; |
| /* fall-through */ |
| case 16: |
| case 24: |
| case 32: |
| block_size = CAMELLIA_BLOCK_SIZE; |
| iv_size = CAMELLIA_BLOCK_SIZE; |
| break; |
| default: |
| return NULL; |
| } |
| break; |
| #endif |
| #ifndef NO_DES3 |
| case ENCR_3DES: |
| if (key_size != 24) |
| { |
| return NULL; |
| } |
| block_size = DES_BLOCK_SIZE; |
| iv_size = DES_BLOCK_SIZE; |
| break; |
| case ENCR_DES: |
| #ifdef WOLFSSL_DES_ECB |
| case ENCR_DES_ECB: |
| #endif |
| if (key_size != 8) |
| { |
| return NULL; |
| } |
| block_size = DES_BLOCK_SIZE; |
| iv_size = DES_BLOCK_SIZE; |
| break; |
| #endif |
| default: |
| return NULL; |
| } |
| |
| INIT(this, |
| .public = { |
| .crypter = { |
| .encrypt = _encrypt, |
| .decrypt = _decrypt, |
| .get_block_size = _get_block_size, |
| .get_iv_size = _get_iv_size, |
| .get_key_size = _get_key_size, |
| .set_key = _set_key, |
| .destroy = _destroy, |
| }, |
| }, |
| .alg = algo, |
| .block_size = block_size, |
| .iv_size = iv_size, |
| ); |
| |
| switch (algo) |
| { |
| #if !defined(NO_AES) && !defined(NO_AES_CBC) |
| case ENCR_AES_CBC: |
| ret = wc_AesInit(&this->cipher.aes, NULL, INVALID_DEVID); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(HAVE_AES_ECB) |
| case ENCR_AES_ECB: |
| ret = wc_AesInit(&this->cipher.aes, NULL, INVALID_DEVID); |
| break; |
| #endif |
| #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER) |
| case ENCR_AES_CTR: |
| ret = wc_AesInit(&this->cipher.aes, NULL, INVALID_DEVID); |
| break; |
| #endif |
| #ifndef NO_DES3 |
| case ENCR_3DES: |
| ret = wc_Des3Init(&this->cipher.des3, NULL, INVALID_DEVID); |
| break; |
| #endif |
| default: |
| break; |
| } |
| if (ret != 0) |
| { |
| free(this); |
| return NULL; |
| } |
| |
| this->key = chunk_alloc(key_size); |
| this->salt = chunk_alloc(salt_len); |
| |
| return &this->public; |
| } |