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 243 (lumag:ipsec-packet-impl-3)
 ** https://github.com/Linaro/odp/pull/243
 ** Patch: https://github.com/Linaro/odp/pull/243.patch
 ** Base sha: e3108af2f0b58c2ceca422b418439bba5de04b11
 ** Merge commit sha: 0b3c6fecf5adbd74ef6dd0c08d541051854b91a4
 **/
 platform/linux-generic/include/odp_ipsec_internal.h | 16 +++++++++++++---
 platform/linux-generic/odp_ipsec.c                  | 19 ++++++++++++++++++-
 platform/linux-generic/odp_ipsec_sad.c              |  6 ++++++
 3 files changed, 37 insertions(+), 4 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..1aa437b8e 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -676,7 +676,24 @@ 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);
+
+                       param.override_iv_ptr = iv;
+               } else if (ipsec_sa->esp_iv_len) {
                        uint32_t len;
 
                        len = odp_random_data(iv + ipsec_sa->salt_length,
diff --git a/platform/linux-generic/odp_ipsec_sad.c 
b/platform/linux-generic/odp_ipsec_sad.c
index f0b5b9e4a..5d20bb66c 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)
@@ -315,6 +316,7 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const 
odp_ipsec_sa_param_t *param)
        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 +325,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