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) {