From: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org>

Reusing IV block with GCM results in disastrous consequences. Use
counter instead of random-generated IV to remove possibility for IV
reuse.

Signed-off-by: Dmitry Eremin-Solenikov <dmitry.ereminsoleni...@linaro.org>
---
/** Email created from pull request 288 (lumag:gmac)
 ** https://github.com/Linaro/odp/pull/288
 ** Patch: https://github.com/Linaro/odp/pull/288.patch
 ** Base sha: a908a4dead95321e84d6a8a23de060051dcd8969
 ** Merge commit sha: 9fff58cf77d87306efed89f77e84f957850623d5
 **/
 .../linux-generic/include/odp_ipsec_internal.h     | 16 ++++++++++---
 platform/linux-generic/odp_ipsec.c                 | 28 +++++++++++++++-------
 platform/linux-generic/odp_ipsec_sad.c             |  9 +++++++
 3 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/platform/linux-generic/include/odp_ipsec_internal.h 
b/platform/linux-generic/include/odp_ipsec_internal.h
index 1340ca7bd..afc2f686e 100644
--- a/platform/linux-generic/include/odp_ipsec_internal.h
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -118,9 +118,17 @@ struct ipsec_sa_s {
        uint8_t         salt[IPSEC_MAX_SALT_LEN];
        uint32_t        salt_length;
 
-       unsigned        dec_ttl : 1;
-       unsigned        copy_dscp : 1;
-       unsigned        copy_df : 1;
+       union {
+               unsigned flags;
+               struct {
+                       unsigned        dec_ttl : 1;
+                       unsigned        copy_dscp : 1;
+                       unsigned        copy_df : 1;
+
+                       /* Only for outbound */
+                       unsigned        use_counter_iv : 1;
+               };
+       };
 
        union {
                struct {
@@ -136,6 +144,8 @@ struct ipsec_sa_s {
                        odp_atomic_u32_t tun_hdr_id;
                        odp_atomic_u32_t seq;
 
+                       odp_atomic_u64_t counter; /* for CTR/GCM */
+
                        uint8_t         tun_ttl;
                        uint8_t         tun_dscp;
                        uint8_t         tun_df;
diff --git a/platform/linux-generic/odp_ipsec.c 
b/platform/linux-generic/odp_ipsec.c
index e57736c2a..6a731e999 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -676,23 +676,36 @@ static ipsec_sa_t *ipsec_out_single(odp_packet_t pkt,
                               ip_data_len +
                               ipsec_sa->icv_len;
 
-               if (ipsec_sa->esp_iv_len) {
+               if (ipsec_sa->use_counter_iv) {
+                       uint64_t ctr;
+
+                       /* Both GCM and CTR use 8-bit counters */
+                       ODP_ASSERT(sizeof(ctr) == ipsec_sa->esp_iv_len);
+
+                       ctr = odp_atomic_fetch_add_u64(&ipsec_sa->out.counter,
+                                                      1);
+                       /* Check for overrun */
+                       if (ctr == 0)
+                               goto out;
+
+                       memcpy(iv, ipsec_sa->salt, ipsec_sa->salt_length);
+                       memcpy(iv + ipsec_sa->salt_length, &ctr,
+                              ipsec_sa->esp_iv_len);
+
+               } else if (ipsec_sa->esp_iv_len) {
                        uint32_t len;
 
-                       len = odp_random_data(iv + ipsec_sa->salt_length,
-                                             ipsec_sa->esp_iv_len,
+                       len = odp_random_data(iv, ipsec_sa->esp_iv_len,
                                              ODP_RANDOM_CRYPTO);
 
                        if (len != ipsec_sa->esp_iv_len) {
                                status->error.alg = 1;
                                goto out;
                        }
-
-                       memcpy(iv, ipsec_sa->salt, ipsec_sa->salt_length);
-
-                       param.override_iv_ptr = iv;
                }
 
+               param.override_iv_ptr = iv;
+
                if (odp_packet_extend_tail(&pkt, trl_len, NULL, NULL) < 0) {
                        status->error.alg = 1;
                        goto out;
@@ -734,7 +747,6 @@ static ipsec_sa_t *ipsec_out_single(odp_packet_t pkt,
                odp_packet_copy_from_mem(pkt,
                                         ipsec_offset, _ODP_ESPHDR_LEN,
                                         &esp);
-               memcpy(iv, ipsec_sa->salt, ipsec_sa->salt_length);
                odp_packet_copy_from_mem(pkt,
                                         ipsec_offset + _ODP_ESPHDR_LEN,
                                         ipsec_sa->esp_iv_len,
diff --git a/platform/linux-generic/odp_ipsec_sad.c 
b/platform/linux-generic/odp_ipsec_sad.c
index f0b5b9e4a..dc338bfcd 100644
--- a/platform/linux-generic/odp_ipsec_sad.c
+++ b/platform/linux-generic/odp_ipsec_sad.c
@@ -207,6 +207,7 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const 
odp_ipsec_sa_param_t *param)
        ipsec_sa->context = param->context;
        ipsec_sa->queue = param->dest_queue;
        ipsec_sa->mode = param->mode;
+       ipsec_sa->flags = 0;
        if (ODP_IPSEC_DIR_INBOUND == param->dir) {
                ipsec_sa->in.lookup_mode = param->inbound.lookup_mode;
                if (ODP_IPSEC_LOOKUP_DSTADDR_SPI == ipsec_sa->in.lookup_mode)
@@ -298,11 +299,13 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const 
odp_ipsec_sa_param_t *param)
        case ODP_CIPHER_ALG_NULL:
                ipsec_sa->esp_iv_len = 0;
                ipsec_sa->esp_block_len = 1;
+               crypto_param.iv.length = 0;
                break;
        case ODP_CIPHER_ALG_DES:
        case ODP_CIPHER_ALG_3DES_CBC:
                ipsec_sa->esp_iv_len = 8;
                ipsec_sa->esp_block_len = 8;
+               crypto_param.iv.length = 8;
                break;
 #if ODP_DEPRECATED_API
        case ODP_CIPHER_ALG_AES128_CBC:
@@ -310,11 +313,13 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const 
odp_ipsec_sa_param_t *param)
        case ODP_CIPHER_ALG_AES_CBC:
                ipsec_sa->esp_iv_len = 16;
                ipsec_sa->esp_block_len = 16;
+               crypto_param.iv.length = 16;
                break;
 #if ODP_DEPRECATED_API
        case ODP_CIPHER_ALG_AES128_GCM:
 #endif
        case ODP_CIPHER_ALG_AES_GCM:
+               ipsec_sa->use_counter_iv = 1;
                ipsec_sa->esp_iv_len = 8;
                ipsec_sa->esp_block_len = 16;
                crypto_param.iv.length = 12;
@@ -323,6 +328,10 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const 
odp_ipsec_sa_param_t *param)
                return ODP_IPSEC_SA_INVALID;
        }
 
+       if (1 == ipsec_sa->use_counter_iv &&
+           ODP_IPSEC_DIR_OUTBOUND == param->dir)
+               odp_atomic_init_u64(&ipsec_sa->out.counter, 1);
+
        crypto_param.auth_digest_len = ipsec_sa->icv_len;
 
        if (param->crypto.cipher_key_extra.length) {

Reply via email to