diff mbox series

[API-NEXT,v5,15/23] linux-generic: crypto: make en/decryption work across packet segments

Message ID 20170601090339.9313-16-dmitry.ereminsolenikov@linaro.org
State Accepted
Commit b93c17b30d051b114a099ed5219fb2a82f5a864c
Headers show
Series Major cryptography code rework | expand

Commit Message

Dmitry Eremin-Solenikov June 1, 2017, 9:03 a.m. UTC
Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsolenikov@linaro.org>

---
 platform/linux-generic/odp_crypto.c | 184 +++++++++++++++++++++++++++---------
 1 file changed, 138 insertions(+), 46 deletions(-)

-- 
2.11.0
diff mbox series

Patch

diff --git a/platform/linux-generic/odp_crypto.c b/platform/linux-generic/odp_crypto.c
index 7ea90fb92b48..ea7c702650f6 100644
--- a/platform/linux-generic/odp_crypto.c
+++ b/platform/linux-generic/odp_crypto.c
@@ -190,14 +190,122 @@  odp_crypto_alg_err_t auth_check(odp_crypto_op_param_t *param,
 }
 
 static
+int internal_encrypt(EVP_CIPHER_CTX *ctx, odp_crypto_op_param_t *param)
+{
+	odp_packet_t pkt = param->out_pkt;
+	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;
+
+	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);
+
+	return ret;
+}
+
+static
+int internal_decrypt(EVP_CIPHER_CTX *ctx, odp_crypto_op_param_t *param)
+{
+	odp_packet_t pkt = param->out_pkt;
+	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;
+
+	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;
+		}
+
+		/* 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;
+		}
+	}
+
+	ret = EVP_DecryptFinal_ex(ctx, block, &cipher_len);
+	odp_packet_copy_from_mem(pkt, out_pos, cipher_len, block);
+
+	return ret;
+}
+
+static
 odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
 				    odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t plain_len   = param->cipher_range.length;
 	void *iv_ptr;
-	int cipher_len = 0;
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -206,9 +314,6 @@  odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Encrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -216,13 +321,12 @@  odp_crypto_alg_err_t cipher_encrypt(odp_crypto_op_param_t *param,
 	EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
 
-	EVP_EncryptUpdate(ctx, data, &cipher_len, data, plain_len);
-
-	EVP_EncryptFinal_ex(ctx, data + cipher_len, &cipher_len);
+	ret = internal_encrypt(ctx, param);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static
@@ -230,10 +334,8 @@  odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
 				    odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t cipher_len   = param->cipher_range.length;
-	int plain_len = 0;
 	void *iv_ptr;
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -242,9 +344,6 @@  odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Decrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -252,13 +351,12 @@  odp_crypto_alg_err_t cipher_decrypt(odp_crypto_op_param_t *param,
 	EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
 
-	EVP_DecryptUpdate(ctx, data, &plain_len, data, cipher_len);
-
-	EVP_DecryptFinal_ex(ctx, data + plain_len, &plain_len);
+	ret = internal_decrypt(ctx, param);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static int process_cipher_param(odp_crypto_generic_session_t *session,
@@ -293,13 +391,12 @@  odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
 				     odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t plain_len   = param->cipher_range.length;
 	const uint8_t *aad_head = param->aad.ptr;
 	uint32_t aad_len = param->aad.length;
 	void *iv_ptr;
-	int cipher_len = 0;
-	uint8_t *tag = data + param->hash_result_offset;
+	int dummy_len = 0;
+	uint8_t block[EVP_MAX_MD_SIZE];
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -308,9 +405,6 @@  odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Encrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -322,18 +416,20 @@  odp_crypto_alg_err_t aes_gcm_encrypt(odp_crypto_op_param_t *param,
 
 	/* Authenticate header data (if any) without encrypting them */
 	if (aad_len > 0)
-		EVP_EncryptUpdate(ctx, NULL, &cipher_len,
+		EVP_EncryptUpdate(ctx, NULL, &dummy_len,
 				  aad_head, aad_len);
 
-	EVP_EncryptUpdate(ctx, data, &cipher_len, data, plain_len);
+	ret = internal_encrypt(ctx, param);
 
-	EVP_EncryptFinal_ex(ctx, data + cipher_len, &cipher_len);
 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG,
-			    session->p.auth_digest_len, tag);
+			    session->p.auth_digest_len, block);
+	odp_packet_copy_from_mem(param->out_pkt, param->hash_result_offset,
+				 session->p.auth_digest_len, block);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static
@@ -341,13 +437,12 @@  odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
 				     odp_crypto_generic_session_t *session)
 {
 	EVP_CIPHER_CTX *ctx;
-	uint8_t *data  = odp_packet_data(param->out_pkt);
-	uint32_t cipher_len   = param->cipher_range.length;
 	const uint8_t *aad_head = param->aad.ptr;
 	uint32_t aad_len = param->aad.length;
-	int plain_len = 0;
+	int dummy_len = 0;
 	void *iv_ptr;
-	uint8_t *tag   = data + param->hash_result_offset;
+	uint8_t block[EVP_MAX_MD_SIZE];
+	int ret;
 
 	if (param->override_iv_ptr)
 		iv_ptr = param->override_iv_ptr;
@@ -356,9 +451,6 @@  odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
 	else
 		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
 
-	/* Adjust pointer for beginning of area to cipher/auth */
-	data += param->cipher_range.offset;
-
 	/* Decrypt it */
 	ctx = EVP_CIPHER_CTX_new();
 	EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
@@ -368,22 +460,22 @@  odp_crypto_alg_err_t aes_gcm_decrypt(odp_crypto_op_param_t *param,
 	EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, iv_ptr);
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
 
+	odp_packet_copy_to_mem(param->out_pkt, param->hash_result_offset,
+			       session->p.auth_digest_len, block);
 	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG,
-			    session->p.auth_digest_len, tag);
+			    session->p.auth_digest_len, block);
 
 	/* Authenticate header data (if any) without encrypting them */
 	if (aad_len > 0)
-		EVP_DecryptUpdate(ctx, NULL, &plain_len,
+		EVP_DecryptUpdate(ctx, NULL, &dummy_len,
 				  aad_head, aad_len);
 
-	EVP_DecryptUpdate(ctx, data, &plain_len, data, cipher_len);
-
-	if (EVP_DecryptFinal_ex(ctx, data + plain_len, &plain_len) <= 0)
-		return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+	ret = internal_decrypt(ctx, param);
 
 	EVP_CIPHER_CTX_free(ctx);
 
-	return ODP_CRYPTO_ALG_ERR_NONE;
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK :
+			  ODP_CRYPTO_ALG_ERR_NONE;
 }
 
 static int process_aes_gcm_param(odp_crypto_generic_session_t *session,