From 585780c00d291e90dbe9062e05fee8139e2aacf1 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 4 Feb 2022 11:11:39 -0500 Subject: [PATCH] TT#14008 perform ROC guessing also for AEAD All crypto suites except AEAD have an explicit packet authentication stage. If authentication fails for a packet, we take some guesses about a ROC mismatch and see if authentication can succeed with a different ROC. If a working ROC is found, our tracked ROC is updated and decryption proceeds. AEAD doesn't have an explicit authentication stage and authentication is performed implicitly by the decryption engine, which simply returns a decryption error if the authentication fails. We must therefore add the same ROC guessing logic at this step for AEAD. Change-Id: Ic1a70daa667e23976b74d2303c823b8d8c7bcb2b (cherry picked from commit 550bc06e854f14ff9a301ae33ea2b602ae6a6712) --- daemon/rtp.c | 35 ++++++++++- kernel-module/xt_RTPENGINE.c | 112 ++++++++++++++++++++++++----------- 2 files changed, 111 insertions(+), 36 deletions(-) diff --git a/daemon/rtp.c b/daemon/rtp.c index d6c1950bf..de93d5c84 100644 --- a/daemon/rtp.c +++ b/daemon/rtp.c @@ -217,8 +217,39 @@ decrypt_idx: ssrc_ctx->srtp_index = index; decrypt:; int prev_len = to_decrypt.len; - if (!c->params.session_params.unencrypted_srtp && crypto_decrypt_rtp(c, rtp, &to_decrypt, index)) - return -1; + if (c->params.session_params.unencrypted_srtp) + { } // nothing to do + else { + int ret; + int guess = 0; + while (true) { + // make backup in case of failed decryption clobbers the buffer + // XXX only needed for AEAD ciphers + char backup[to_decrypt.len]; + memcpy(backup, to_decrypt.s, to_decrypt.len); + + ret = crypto_decrypt_rtp(c, rtp, &to_decrypt, index); + if (ret != 1) + break; + // AEAD failed: try ROC guessing as above. restore backup buffer first + memcpy(to_decrypt.s, backup, to_decrypt.len); + if (guess == 0) + index += 0x10000; + else if (guess == 1) + index -= 0x20000; + else if (guess == 2) + index &= 0xffff; + else + break; + guess++; + }; + if (ret) { + ilog(LOG_WARNING | LOG_FLAG_LIMIT, "Discarded SRTP packet: decryption failed"); + return -1; + } + if (guess != 0) + ssrc_ctx->srtp_index = index; + } crypto_debug_printf(", dec pl: "); crypto_debug_dump(&to_decrypt); diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c index 780092192..23743ddbf 100644 --- a/kernel-module/xt_RTPENGINE.c +++ b/kernel-module/xt_RTPENGINE.c @@ -253,13 +253,13 @@ static int is_valid_address(const struct re_address *rea); static int aes_f8_session_key_init(struct re_crypto_context *, struct rtpengine_srtp *); static int srtp_encrypt_aes_cm(struct re_crypto_context *, struct rtpengine_srtp *, - struct rtp_parsed *, uint64_t); + struct rtp_parsed *, uint64_t *); static int srtp_encrypt_aes_f8(struct re_crypto_context *, struct rtpengine_srtp *, - struct rtp_parsed *, uint64_t); + struct rtp_parsed *, uint64_t *); static int srtp_encrypt_aes_gcm(struct re_crypto_context *, struct rtpengine_srtp *, - struct rtp_parsed *, uint64_t); + struct rtp_parsed *, uint64_t *); static int srtp_decrypt_aes_gcm(struct re_crypto_context *, struct rtpengine_srtp *, - struct rtp_parsed *, uint64_t); + struct rtp_parsed *, uint64_t *); static void call_put(struct re_call *call); static void del_stream(struct re_stream *stream, struct rtpengine_table *); @@ -428,9 +428,9 @@ struct re_cipher { const char *tfm_name; const char *aead_name; int (*decrypt)(struct re_crypto_context *, struct rtpengine_srtp *, - struct rtp_parsed *, uint64_t); + struct rtp_parsed *, uint64_t *); int (*encrypt)(struct re_crypto_context *, struct rtpengine_srtp *, - struct rtp_parsed *, uint64_t); + struct rtp_parsed *, uint64_t *); int (*session_key_init)(struct re_crypto_context *, struct rtpengine_srtp *); }; @@ -4051,8 +4051,9 @@ ok: /* XXX shared code */ static int srtp_encrypt_aes_cm(struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, - uint64_t pkt_idx) + uint64_t *pkt_idxp) { + uint64_t pkt_idx = *pkt_idxp; unsigned char iv[16]; uint32_t *ivi; uint32_t idxh, idxl; @@ -4075,8 +4076,9 @@ static int srtp_encrypt_aes_cm(struct re_crypto_context *c, static int srtp_encrypt_aes_f8(struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, - uint64_t pkt_idx) + uint64_t *pkt_idxp) { + uint64_t pkt_idx = *pkt_idxp; unsigned char iv[16]; uint32_t roc; @@ -4092,9 +4094,10 @@ static int srtp_encrypt_aes_f8(struct re_crypto_context *c, static int srtp_encrypt_aes_gcm(struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, - uint64_t pkt_idx) + uint64_t *pkt_idxp) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) + uint64_t pkt_idx = *pkt_idxp; unsigned char iv[12]; struct aead_request *req; struct scatterlist sg[2]; @@ -4141,49 +4144,87 @@ static int srtp_encrypt_aes_gcm(struct re_crypto_context *c, } static int srtp_decrypt_aes_gcm(struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, - uint64_t pkt_idx) + uint64_t *pkt_idxp) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) + uint64_t pkt_idx = *pkt_idxp; unsigned char iv[12]; struct aead_request *req; struct scatterlist sg[2]; int ret; + int guess = 0; + char *copy = NULL; if (s->session_salt_len != 12) return -EINVAL; if (r->payload_len < 16) return -EINVAL; - memcpy(iv, c->session_salt, 12); + do { + memcpy(iv, c->session_salt, 12); - *(uint32_t*)(iv+2) ^= r->header->ssrc; - *(uint32_t*)(iv+6) ^= htonl((pkt_idx & 0x00ffffffff0000ULL) >> 16); - *(uint16_t*)(iv+10) ^= htons(pkt_idx & 0x00ffffULL); + *(uint32_t*)(iv+2) ^= r->header->ssrc; + *(uint32_t*)(iv+6) ^= htonl((pkt_idx & 0x00ffffffff0000ULL) >> 16); + *(uint16_t*)(iv+10) ^= htons(pkt_idx & 0x00ffffULL); - req = aead_request_alloc(c->aead, GFP_ATOMIC); - if (!req) - return -ENOMEM; - if (IS_ERR(req)) - return PTR_ERR(req); + req = aead_request_alloc(c->aead, GFP_ATOMIC); + if (!req) + return -ENOMEM; + if (IS_ERR(req)) + return PTR_ERR(req); - sg_init_table(sg, ARRAY_SIZE(sg)); - sg_set_buf(&sg[0], r->header, r->header_len); - sg_set_buf(&sg[1], r->payload, r->payload_len); + sg_init_table(sg, ARRAY_SIZE(sg)); + sg_set_buf(&sg[0], r->header, r->header_len); + sg_set_buf(&sg[1], r->payload, r->payload_len); - aead_request_set_callback(req, 0, NULL, NULL); + // make copy of payload in case the decyption clobbers it + copy = kmalloc(r->payload_len, GFP_ATOMIC); + if (copy) + memcpy(copy, r->payload, r->payload_len); + + aead_request_set_callback(req, 0, NULL, NULL); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0) - aead_request_set_ad(req, r->header_len); - aead_request_set_crypt(req, sg, sg, r->payload_len, iv); + aead_request_set_ad(req, r->header_len); + aead_request_set_crypt(req, sg, sg, r->payload_len, iv); #else - aead_request_set_assoc(req, &sg[0], r->header_len); - aead_request_set_crypt(req, &sg[1], &sg[1], r->payload_len, iv); + aead_request_set_assoc(req, &sg[0], r->header_len); + aead_request_set_crypt(req, &sg[1], &sg[1], r->payload_len, iv); #endif - ret = crypto_aead_decrypt(req); - aead_request_free(req); + ret = crypto_aead_decrypt(req); + aead_request_free(req); - if (ret == 0) - r->payload_len -= 16; + if (ret == 0) { + r->payload_len -= 16; + break; + } + if (ret != -EBADMSG) + break; + + // authentication failed: restore payload and do some ROC guessing + if (!copy) + break; + memcpy(r->payload, copy, r->payload_len); + + if (guess == 0) + pkt_idx += 0x10000; + else if (guess == 1) + pkt_idx -= 0x20000; + else if (guess == 2) + pkt_idx &= 0xffff; + else + break; + + guess++; + } while (1); + + if (copy) + kfree(copy); + + if (ret == 0 && guess != 0) { + *pkt_idxp = pkt_idx; + ret = 1; + } return ret; #else @@ -4199,12 +4240,12 @@ static inline int srtp_encrypt(struct re_crypto_context *c, return 0; if (!c->cipher->encrypt) return 0; - return c->cipher->encrypt(c, s, r, pkt_idx); + return c->cipher->encrypt(c, s, r, &pkt_idx); } static inline int srtp_decrypt(struct re_crypto_context *c, struct rtpengine_srtp *s, struct rtp_parsed *r, - uint64_t pkt_idx) + uint64_t *pkt_idx) { if (!c->cipher->decrypt) return 0; @@ -4539,8 +4580,11 @@ found_ssrc:; goto skip1; errstr = "SRTP decryption failed"; - if (srtp_decrypt(&g->decrypt, &g->target.decrypt, &rtp, pkt_idx)) + err = srtp_decrypt(&g->decrypt, &g->target.decrypt, &rtp, &pkt_idx); + if (err < 0) goto skip_error; + if (err == 1) + update_packet_index(&g->decrypt, &g->target.decrypt, pkt_idx, ssrc_idx); skb_trim(skb, rtp.header_len + rtp.payload_len);