aboutsummaryrefslogtreecommitdiff
path: root/platform/linux-generic/odp_crypto_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'platform/linux-generic/odp_crypto_openssl.c')
-rw-r--r--platform/linux-generic/odp_crypto_openssl.c281
1 files changed, 161 insertions, 120 deletions
diff --git a/platform/linux-generic/odp_crypto_openssl.c b/platform/linux-generic/odp_crypto_openssl.c
index 07a91cc46..d8276b4b4 100644
--- a/platform/linux-generic/odp_crypto_openssl.c
+++ b/platform/linux-generic/odp_crypto_openssl.c
@@ -1,4 +1,5 @@
/* Copyright (c) 2014-2018, Linaro Limited
+ * Copyright (c) 2021, Nokia
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
@@ -438,8 +439,12 @@ void packet_aes_xcbc_mac(odp_packet_t pkt,
len -= datalen;
if (eoff != 0) {
if (eoff + datalen > AES_BLOCK_SIZE) {
- memxor(e + eoff, data, AES_BLOCK_SIZE - eoff);
- datalen -= (AES_BLOCK_SIZE - eoff);
+ /* bytes needed to fill the partial block */
+ uint32_t remaining_len = AES_BLOCK_SIZE - eoff;
+
+ memxor(e + eoff, data, remaining_len);
+ datalen -= remaining_len;
+ data += remaining_len;
eoff = 0;
EVP_EncryptUpdate(ctx,
e, &dummy_len, e, sizeof(e));
@@ -695,7 +700,7 @@ int packet_cmac_eia2(odp_packet_t pkt,
{
CMAC_CTX *ctx = local.cmac_ctx[session->idx];
void *iv_ptr;
- uint32_t offset = param->auth_range.offset;
+ uint32_t offset = param->auth_range.offset / 8;
uint32_t len = (param->auth_range.length + 7) / 8;
size_t outlen;
@@ -894,120 +899,146 @@ int internal_aad(EVP_CIPHER_CTX *ctx,
return ret;
}
-static
-int internal_encrypt(EVP_CIPHER_CTX *ctx,
- odp_packet_t pkt,
- const odp_crypto_packet_op_param_t *param)
-{
- unsigned in_pos = param->cipher_range.offset;
- unsigned out_pos = param->cipher_range.offset;
- unsigned in_len = param->cipher_range.length;
- uint8_t block[2 * EVP_MAX_BLOCK_LENGTH];
- unsigned block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
- int cipher_len;
- int ret;
-
- ODP_ASSERT(in_pos + in_len <= odp_packet_len(pkt));
-
- while (in_len > 0) {
- uint32_t seglen = 0; /* GCC */
- uint8_t *insegaddr = odp_packet_offset(pkt, in_pos,
- &seglen, NULL);
- unsigned inseglen = in_len < seglen ? in_len : seglen;
-
- /* There should be at least 1 additional block in out buffer */
- if (inseglen > block_len) {
- unsigned part = inseglen - block_len;
-
- EVP_EncryptUpdate(ctx, insegaddr, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- out_pos += cipher_len;
- }
-
- /* Use temporal storage */
- if (inseglen > 0) {
- unsigned part = inseglen;
-
- EVP_EncryptUpdate(ctx, block, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- odp_packet_copy_from_mem(pkt, out_pos,
- cipher_len, block);
- out_pos += cipher_len;
- }
- }
-
- ret = EVP_EncryptFinal_ex(ctx, block, &cipher_len);
- odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+typedef int (*evp_update_t)(EVP_CIPHER_CTX *, unsigned char *,
+ int *, const unsigned char *, int);
- return ret;
-}
+typedef int (*evp_final_t)(EVP_CIPHER_CTX *, unsigned char *, int *);
-static
-int internal_decrypt(EVP_CIPHER_CTX *ctx,
- odp_packet_t pkt,
- const odp_crypto_packet_op_param_t *param)
-{
- unsigned in_pos = param->cipher_range.offset;
- unsigned out_pos = param->cipher_range.offset;
- unsigned in_len = param->cipher_range.length;
- uint8_t block[2 * EVP_MAX_BLOCK_LENGTH];
- unsigned block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
- int cipher_len;
- int ret;
+static inline int internal_crypt(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param,
+ evp_update_t EVP_update,
+ evp_final_t EVP_final)
+{
+ uint32_t in_pos = param->cipher_range.offset;
+ uint32_t out_pos = in_pos;
+ uint32_t in_len = param->cipher_range.length;
+ uint8_t block[EVP_MAX_BLOCK_LENGTH];
+ uint32_t buffered = 0;
+ uint32_t block_len = EVP_CIPHER_block_size(EVP_CIPHER_CTX_cipher(ctx));
+ int out_len;
+ int rc;
ODP_ASSERT(in_pos + in_len <= odp_packet_len(pkt));
+ /*
+ * In the following loop we process one packet segment per iteration.
+ * We rely on the following properties of the encrypt/decrypt update
+ * function with the algorithms that we use:
+ *
+ * - The function processes (and writes to output) only whole blocks.
+ * - Input data beyond the last full block is buffered inside OpenSSL.
+ * - The amount of buffered data is always less than one block.
+ * - Total amount of output data does not exceed the total amount
+ * of input data at any point.
+ */
while (in_len > 0) {
- uint32_t seglen = 0; /* GCC */
- uint8_t *insegaddr = odp_packet_offset(pkt, in_pos,
- &seglen, NULL);
- unsigned inseglen = in_len < seglen ? in_len : seglen;
-
- /* There should be at least 1 additional block in out buffer */
- if (inseglen > block_len) {
- unsigned part = inseglen - block_len;
-
- EVP_DecryptUpdate(ctx, insegaddr, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- out_pos += cipher_len;
+ uint32_t seglen = 0;
+ uint8_t *in_addr = odp_packet_offset(pkt, in_pos,
+ &seglen, NULL);
+ uint32_t len = in_len < seglen ? in_len : seglen;
+
+ if (odp_unlikely(buffered > 0)) {
+ /*
+ * Leftover data from the previous segment is
+ * in the buffer inside OpenSSL.
+ */
+ uint32_t remaining_len = block_len - buffered;
+
+ if (odp_likely(len >= remaining_len)) {
+ /*
+ * Let's fill the buffered input data to a
+ * full block and get the output block to
+ * a memory buffer. The buffer is then copied
+ * to the packet, crossing segment boundary.
+ */
+ rc = EVP_update(ctx, block, &out_len,
+ in_addr, remaining_len);
+ if (odp_unlikely(rc != 1))
+ goto err;
+ if (odp_unlikely(out_len != (int)block_len))
+ goto err;
+ in_addr += remaining_len;
+ in_pos += remaining_len;
+ len -= remaining_len;
+ in_len -= remaining_len;
+ buffered = 0;
+ rc = odp_packet_copy_from_mem(pkt, out_pos,
+ block_len, block);
+ if (odp_unlikely(rc))
+ goto err;
+ out_pos += block_len;
+ } else {
+ /*
+ * Not enough data in this segment to fill
+ * the buffer to a full block. Fill the buffer
+ * a bit more and go to the next segment.
+ */
+ rc = EVP_update(ctx, block, &out_len,
+ in_addr, len);
+ if (odp_unlikely(rc != 1))
+ goto err;
+ if (odp_unlikely(out_len > 0))
+ goto err;
+ in_pos += len;
+ in_len -= len;
+ buffered += len;
+ continue;
+ }
}
-
- /* Use temporal storage */
- if (inseglen > 0) {
- unsigned part = inseglen;
-
- EVP_DecryptUpdate(ctx, block, &cipher_len,
- insegaddr, part);
- in_pos += part;
- in_len -= part;
- insegaddr += part;
- inseglen -= part;
-
- odp_packet_copy_from_mem(pkt, out_pos,
- cipher_len, block);
- out_pos += cipher_len;
+ ODP_ASSERT(buffered == 0);
+
+ if (in_len > 0) {
+ /*
+ * No input is buffered inside OpenSSL. We pass the
+ * whole remaining segment to OpenSSL and expect to
+ * get a multiple of block size of data processed,
+ * with the rest left in the buffer.
+ */
+ rc = EVP_update(ctx, in_addr, &out_len, in_addr, len);
+ if (odp_unlikely(rc != 1))
+ goto err;
+ ODP_ASSERT(CHECK_IS_POWER2(block_len));
+ buffered = len & (block_len - 1);
+ if (odp_unlikely(out_len + buffered != len))
+ goto err;
+ in_pos += len;
+ in_len -= len;
+ out_pos += len - buffered;
}
}
+ if (odp_unlikely(buffered > 0))
+ goto err;
+ /*
+ * We do not expect any more data out since the cipher range is
+ * supposed to be a multiple of the block size.
+ */
+ rc = EVP_final(ctx, block, &out_len);
+ if (odp_unlikely(out_len != 0))
+ return 0;
+ return rc;
+err:
+ ODP_ERR("internal error\n");
+ (void)EVP_final(ctx, block, &out_len);
+ return 0;
+}
- ret = EVP_DecryptFinal_ex(ctx, block, &cipher_len);
- odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+static int internal_encrypt(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param)
+{
+ return internal_crypt(ctx, pkt, param,
+ EVP_EncryptUpdate,
+ EVP_EncryptFinal_ex);
+}
- return ret;
+static int internal_decrypt(EVP_CIPHER_CTX *ctx,
+ odp_packet_t pkt,
+ const odp_crypto_packet_op_param_t *param)
+{
+ return internal_crypt(ctx, pkt, param,
+ EVP_DecryptUpdate,
+ EVP_DecryptFinal_ex);
}
static void
@@ -1121,6 +1152,10 @@ odp_crypto_alg_err_t cipher_encrypt_bits(odp_packet_t pkt,
uint32_t in_len = (param->cipher_range.length + 7) / 8;
uint8_t data[in_len];
int ret;
+ uint32_t offset;
+
+ /* Range offset is in bits in bit mode but must be divisible by 8. */
+ offset = param->cipher_range.offset / 8;
if (param->cipher_iv_ptr)
iv_ptr = param->cipher_iv_ptr;
@@ -1131,16 +1166,14 @@ odp_crypto_alg_err_t cipher_encrypt_bits(odp_packet_t pkt,
EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
- odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
- data);
+ odp_packet_copy_to_mem(pkt, offset, in_len, data);
EVP_EncryptUpdate(ctx, data, &cipher_len, data, in_len);
ret = EVP_EncryptFinal_ex(ctx, data + cipher_len, &dummy_len);
cipher_len += dummy_len;
- odp_packet_copy_from_mem(pkt, param->cipher_range.offset, in_len,
- data);
+ odp_packet_copy_from_mem(pkt, offset, in_len, data);
return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
ODP_CRYPTO_ALG_ERR_NONE;
@@ -1159,6 +1192,10 @@ odp_crypto_alg_err_t cipher_decrypt_bits(odp_packet_t pkt,
uint32_t in_len = (param->cipher_range.length + 7) / 8;
uint8_t data[in_len];
int ret;
+ uint32_t offset;
+
+ /* Range offset is in bits in bit mode but must be divisible by 8. */
+ offset = param->cipher_range.offset / 8;
if (param->cipher_iv_ptr)
iv_ptr = param->cipher_iv_ptr;
@@ -1169,16 +1206,14 @@ odp_crypto_alg_err_t cipher_decrypt_bits(odp_packet_t pkt,
EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
- odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
- data);
+ odp_packet_copy_to_mem(pkt, offset, in_len, data);
EVP_DecryptUpdate(ctx, data, &cipher_len, data, in_len);
ret = EVP_DecryptFinal_ex(ctx, data + cipher_len, &dummy_len);
cipher_len += dummy_len;
- odp_packet_copy_from_mem(pkt, param->cipher_range.offset, in_len,
- data);
+ odp_packet_copy_from_mem(pkt, offset, in_len, data);
return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
ODP_CRYPTO_ALG_ERR_NONE;
@@ -2461,12 +2496,18 @@ odp_crypto_operation(odp_crypto_op_param_t *param,
packet_param.auth_range = param->auth_range;
rc = odp_crypto_op(&param->pkt, &out_pkt, &packet_param, 1);
- if (rc < 0)
- return rc;
+ if (rc <= 0)
+ return -1;
rc = odp_crypto_result(&packet_result, out_pkt);
- if (rc < 0)
- return rc;
+ if (rc < 0) {
+ /*
+ * We cannot fail since odp_crypto_op() has already processed
+ * the packet. Let's indicate error in the result instead.
+ */
+ packet_hdr(out_pkt)->p.flags.crypto_err = 1;
+ packet_result.ok = false;
+ }
/* Indicate to caller operation was sync */
*posted = 0;
@@ -2524,7 +2565,7 @@ int _odp_crypto_init_global(void)
mem_size += nlocks * sizeof(odp_ticketlock_t);
/* Allocate our globally shared memory */
- shm = odp_shm_reserve("_odp_crypto_pool_ssl", mem_size,
+ shm = odp_shm_reserve("_odp_crypto_ssl_global", mem_size,
ODP_CACHE_LINE_SIZE,
0);
if (ODP_SHM_INVALID == shm) {
@@ -2579,7 +2620,7 @@ int _odp_crypto_term_global(void)
CRYPTO_set_id_callback(NULL);
#endif
- ret = odp_shm_free(odp_shm_lookup("_odp_crypto_pool_ssl"));
+ ret = odp_shm_free(odp_shm_lookup("_odp_crypto_ssl_global"));
if (ret < 0) {
ODP_ERR("shm free failed for crypto_pool\n");
rc = -1;