diff --git a/platform/linux-generic/odp_crypto.c b/platform/linux-generic/odp_crypto.c
index 574671e07..7423864ff 100644
--- a/platform/linux-generic/odp_crypto.c
+++ b/platform/linux-generic/odp_crypto.c
@@ -62,6 +62,14 @@ static const odp_crypto_cipher_capability_t cipher_capa_aes_gcm[] = {
 {.key_len = 24, .iv_len = 12},
 {.key_len = 32, .iv_len = 12} };
 
+static const odp_crypto_cipher_capability_t cipher_capa_aes_ccm[] = {
+{.key_len = 16, .iv_len = 11},
+{.key_len = 16, .iv_len = 13},
+{.key_len = 24, .iv_len = 11},
+{.key_len = 24, .iv_len = 13},
+{.key_len = 32, .iv_len = 11},
+{.key_len = 32, .iv_len = 13} };
+
 #if _ODP_HAVE_CHACHA20_POLY1305
 static const odp_crypto_cipher_capability_t cipher_capa_chacha20_poly1305[] = {
 {.key_len = 32, .iv_len = 12} };
@@ -94,6 +102,9 @@ static const odp_crypto_auth_capability_t auth_capa_sha512_hmac[] = {
 static const odp_crypto_auth_capability_t auth_capa_aes_gcm[] = {
 {.digest_len = 16, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
 
+static const odp_crypto_auth_capability_t auth_capa_aes_ccm[] = {
+{.digest_len = 8, .key_len = 0, .aad_len = {.min = 8, .max = 12, .inc = 4} } };
+
 static const odp_crypto_auth_capability_t auth_capa_aes_gmac[] = {
 {.digest_len = 16, .key_len = 16, .aad_len = {.min = 0, .max = 0, .inc = 0},
 	.iv_len = 12 } };
@@ -749,6 +760,153 @@ static int process_aes_gmac_param(odp_crypto_generic_session_t *session,
 	return 0;
 }
 
+static
+odp_crypto_alg_err_t aes_ccm_encrypt(odp_packet_t pkt,
+				     const odp_crypto_packet_op_param_t *param,
+				     odp_crypto_generic_session_t *session)
+{
+	EVP_CIPHER_CTX *ctx;
+	const uint8_t *aad_head = param->aad_ptr;
+	uint32_t aad_len = session->p.auth_aad_len;
+	void *iv_ptr;
+	int dummy_len = 0;
+	int cipher_len;
+	uint32_t in_len = param->cipher_range.length;
+	uint8_t data[in_len];
+	uint8_t block[EVP_MAX_MD_SIZE];
+	int ret;
+
+	if (param->cipher_iv_ptr)
+		iv_ptr = param->cipher_iv_ptr;
+	else if (session->p.cipher_iv.data)
+		iv_ptr = session->cipher.iv_data;
+	else
+		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
+
+	/* Encrypt it */
+	ctx = EVP_CIPHER_CTX_new();
+	EVP_EncryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+			   NULL, NULL);
+	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN,
+			    session->p.cipher_iv.length, NULL);
+	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG,
+			    session->p.auth_digest_len, NULL);
+	EVP_EncryptInit_ex(ctx, NULL, NULL, session->cipher.key_data, iv_ptr);
+	EVP_CIPHER_CTX_set_padding(ctx, 0);
+
+	/* Set len */
+	EVP_EncryptUpdate(ctx, NULL, &dummy_len, NULL, in_len);
+
+	/* Authenticate header data (if any) without encrypting them */
+	if (aad_len > 0)
+		EVP_EncryptUpdate(ctx, NULL, &dummy_len,
+				  aad_head, aad_len);
+
+	odp_packet_copy_to_mem(pkt, param->cipher_range.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);
+
+	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG,
+			    session->p.auth_digest_len, block);
+	odp_packet_copy_from_mem(pkt, param->hash_result_offset,
+				 session->p.auth_digest_len, block);
+
+	EVP_CIPHER_CTX_free(ctx);
+
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_DATA_SIZE :
+			  ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+odp_crypto_alg_err_t aes_ccm_decrypt(odp_packet_t pkt,
+				     const odp_crypto_packet_op_param_t *param,
+				     odp_crypto_generic_session_t *session)
+{
+	EVP_CIPHER_CTX *ctx;
+	const uint8_t *aad_head = param->aad_ptr;
+	uint32_t aad_len = session->p.auth_aad_len;
+	void *iv_ptr;
+	int dummy_len = 0;
+	int cipher_len;
+	uint32_t in_len = param->cipher_range.length;
+	uint8_t data[in_len];
+	uint8_t block[EVP_MAX_MD_SIZE];
+	int ret;
+
+	if (param->cipher_iv_ptr)
+		iv_ptr = param->cipher_iv_ptr;
+	else if (session->p.cipher_iv.data)
+		iv_ptr = session->cipher.iv_data;
+	else
+		return ODP_CRYPTO_ALG_ERR_IV_INVALID;
+
+	/* Decrypt it */
+	ctx = EVP_CIPHER_CTX_new();
+	EVP_DecryptInit_ex(ctx, session->cipher.evp_cipher, NULL,
+			   NULL, NULL);
+	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN,
+			    session->p.cipher_iv.length, NULL);
+	odp_packet_copy_to_mem(pkt, param->hash_result_offset,
+			       session->p.auth_digest_len, block);
+	EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG,
+			    session->p.auth_digest_len, block);
+	EVP_DecryptInit_ex(ctx, NULL, NULL, session->cipher.key_data, iv_ptr);
+	EVP_CIPHER_CTX_set_padding(ctx, 0);
+
+	/* Set len */
+	EVP_DecryptUpdate(ctx, NULL, &dummy_len, NULL, in_len);
+
+	/* Authenticate header data (if any) without encrypting them */
+	if (aad_len > 0)
+		EVP_DecryptUpdate(ctx, NULL, &dummy_len,
+				  aad_head, aad_len);
+
+	odp_packet_copy_to_mem(pkt, param->cipher_range.offset, in_len,
+			       data);
+
+	ret = EVP_DecryptUpdate(ctx, data, &cipher_len, data, in_len);
+
+	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);
+
+	EVP_CIPHER_CTX_free(ctx);
+
+	return ret <= 0 ? ODP_CRYPTO_ALG_ERR_ICV_CHECK :
+			  ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static int process_aes_ccm_param(odp_crypto_generic_session_t *session,
+				 const EVP_CIPHER *cipher)
+{
+	/* Verify Key len is valid */
+	if ((uint32_t)EVP_CIPHER_key_length(cipher) !=
+	    session->p.cipher_key.length)
+		return -1;
+
+	memcpy(session->cipher.key_data, session->p.cipher_key.data,
+	       session->p.cipher_key.length);
+
+	session->cipher.evp_cipher = cipher;
+
+	/* Set function */
+	if (ODP_CRYPTO_OP_ENCODE == session->p.op)
+		session->cipher.func = aes_ccm_encrypt;
+	else
+		session->cipher.func = aes_ccm_decrypt;
+
+	return 0;
+}
+
 static int process_auth_param(odp_crypto_generic_session_t *session,
 			      uint32_t key_length,
 			      const EVP_MD *evp_md)
@@ -789,6 +947,7 @@ int odp_crypto_capability(odp_crypto_capability_t *capa)
 	capa->ciphers.bit.aes_cbc    = 1;
 	capa->ciphers.bit.aes_ctr    = 1;
 	capa->ciphers.bit.aes_gcm    = 1;
+	capa->ciphers.bit.aes_ccm    = 1;
 #if _ODP_HAVE_CHACHA20_POLY1305
 	capa->ciphers.bit.chacha20_poly1305 = 1;
 #endif
@@ -799,6 +958,7 @@ int odp_crypto_capability(odp_crypto_capability_t *capa)
 	capa->auths.bit.sha256_hmac  = 1;
 	capa->auths.bit.sha512_hmac  = 1;
 	capa->auths.bit.aes_gcm      = 1;
+	capa->auths.bit.aes_ccm      = 1;
 	capa->auths.bit.aes_gmac     = 1;
 #if _ODP_HAVE_CHACHA20_POLY1305
 	capa->auths.bit.chacha20_poly1305 = 1;
@@ -846,6 +1006,10 @@ int odp_crypto_cipher_capability(odp_cipher_alg_t cipher,
 		src = cipher_capa_aes_gcm;
 		num = sizeof(cipher_capa_aes_gcm) / size;
 		break;
+	case ODP_CIPHER_ALG_AES_CCM:
+		src = cipher_capa_aes_ccm;
+		num = sizeof(cipher_capa_aes_ccm) / size;
+		break;
 #if _ODP_HAVE_CHACHA20_POLY1305
 	case ODP_CIPHER_ALG_CHACHA20_POLY1305:
 		src = cipher_capa_chacha20_poly1305;
@@ -900,6 +1064,10 @@ int odp_crypto_auth_capability(odp_auth_alg_t auth,
 		src = auth_capa_aes_gmac;
 		num = sizeof(auth_capa_aes_gmac) / size;
 		break;
+	case ODP_AUTH_ALG_AES_CCM:
+		src = auth_capa_aes_ccm;
+		num = sizeof(auth_capa_aes_ccm) / size;
+		break;
 #if _ODP_HAVE_CHACHA20_POLY1305
 	case ODP_AUTH_ALG_CHACHA20_POLY1305:
 		src = auth_capa_chacha20_poly1305;
@@ -1027,6 +1195,20 @@ odp_crypto_session_create(odp_crypto_session_param_t *param,
 		else
 			rc = -1;
 		break;
+	case ODP_CIPHER_ALG_AES_CCM:
+		/* AES-CCM requires to do both auth and
+		 * cipher at the same time */
+		if (param->auth_alg != ODP_AUTH_ALG_AES_CCM)
+			rc = -1;
+		else if (param->cipher_key.length == 16)
+			rc = process_aes_ccm_param(session, EVP_aes_128_ccm());
+		else if (param->cipher_key.length == 24)
+			rc = process_aes_ccm_param(session, EVP_aes_192_ccm());
+		else if (param->cipher_key.length == 32)
+			rc = process_aes_ccm_param(session, EVP_aes_256_ccm());
+		else
+			rc = -1;
+		break;
 #if _ODP_HAVE_CHACHA20_POLY1305
 	case ODP_CIPHER_ALG_CHACHA20_POLY1305:
 		/* ChaCha20_Poly1305 requires to do both auth and
@@ -1108,6 +1290,16 @@ odp_crypto_session_create(odp_crypto_session_param_t *param,
 		else
 			rc = -1;
 		break;
+	case ODP_AUTH_ALG_AES_CCM:
+		/* AES-CCM requires to do both auth and
+		 * cipher at the same time */
+		if (param->cipher_alg == ODP_CIPHER_ALG_AES_CCM) {
+			session->auth.func = null_crypto_routine;
+			rc = 0;
+		} else {
+			rc = -1;
+		}
+		break;
 #if _ODP_HAVE_CHACHA20_POLY1305
 	case ODP_AUTH_ALG_CHACHA20_POLY1305:
 		/* ChaCha20_Poly1305 requires to do both auth and
