[tcpm] RFC 5925 SNE algorithm concern

juhamatk@gmail.com Thu, 30 April 2020 07:31 UTC

Return-Path: <juhamatk@gmail.com>
X-Original-To: tcpm@ietfa.amsl.com
Delivered-To: tcpm@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 4B1EA3A0A53 for <tcpm@ietfa.amsl.com>; Thu, 30 Apr 2020 00:31:48 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2.099
X-Spam-Level:
X-Spam-Status: No, score=-2.099 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Sj087CF84puX for <tcpm@ietfa.amsl.com>; Thu, 30 Apr 2020 00:31:46 -0700 (PDT)
Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 82EBB3A0A51 for <tcpm@ietf.org>; Thu, 30 Apr 2020 00:31:46 -0700 (PDT)
Received: by mail-wm1-x333.google.com with SMTP id v8so6739980wma.0 for <tcpm@ietf.org>; Thu, 30 Apr 2020 00:31:46 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=TAnCH2lJjnSeUmZqzXPvYSyKLMV5YIEiioXv5VjcflE=; b=dH7IetMgOjDJ5Yb5AiqNAQlMiE4egfD8SbiNhH5YIDrrpColokk3Qjq9IOgyErr/0e caFcrjOpk/yKkonSojYC4GhPKjzS4knGviwSgkAPMZU5g3OEmbgd27mWHjiqDJOZu9CI 499O4cyGUehweEJ7fojlcUQ6IdaCJtie7j923KZ5HgUMlb+WALwkx3z7JtYxcnjhtXry 3PO4sDOLA+9TeH8JSdNVkENFRqWJAcIapnGY27eTMcU2MvKXASa+DCE3mymyg2/ifbhA 0CzWXAMc69dIG7vF0wDiAs5i2I6MTOvBQo1Z5NVkA/KREGVqff3PmA5/SHrM9uJ2liaW hw/Q==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=TAnCH2lJjnSeUmZqzXPvYSyKLMV5YIEiioXv5VjcflE=; b=qWYJdRY06g2suRBZZkV0Aul+OEFOaSQTFE2jOJmucynl/npLnvZ+eO3CT7hOdmuSc8 j8pgjl/iIJYvMdzqgQwwPdosIWNvr8ealjShXDC2nCsfu96yHNiYcLamKjVRWgA+8yYK 6Bxq/XjCAzKauJRpieFsSi7uTADCq3mbFhQr2DW7Fv+ledjNwS7X5YawgJg+0mkZQPdH pQ/MKL0aOzgXv6w7aLeGoNl3QesnQhGxMH1iUZcq15zO8ALjNsoqkiBehEftDoHzn7SF PrriOC969rqzB/vjwW9LMFD2OudfHdqj1hGen/lUrfjgTrQc9I9OpDBqaVCG1B9CG+zP n0cg==
X-Gm-Message-State: AGi0Pube1vCCP/5kiJW25j9FGsG1+jwfUgAGKvy6FWxJwAviyMZpc+S5 YD370GzhduFtpugWjot7A2hLlOtWxGdj+UYuMJT3mktIt+Y=
X-Google-Smtp-Source: APiQypL4D1KyjyTkF9W6NfWU35OkALEhmZxKxnykLdm+gEBOBl+OsqHzsWhUzRBPcgGyVqHKYOxfo4LznQEnIOZkLyM=
X-Received: by 2002:a05:600c:225a:: with SMTP id a26mr1441955wmm.104.1588231904409; Thu, 30 Apr 2020 00:31:44 -0700 (PDT)
MIME-Version: 1.0
From: juhamatk@gmail.com
Date: Thu, 30 Apr 2020 10:31:32 +0300
Message-ID: <CACS3ZpBLAO2=O6Vf2Vx766qAGqmY2yD_yiLag-K7C3sYFjH9hw@mail.gmail.com>
To: tcpm@ietf.org
Content-Type: text/plain; charset="UTF-8"
Archived-At: <https://mailarchive.ietf.org/arch/msg/tcpm/cNlPjIs2Ql-__QpSrq8M2G-Irjw>
Subject: [tcpm] RFC 5925 SNE algorithm concern
X-BeenThere: tcpm@ietf.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: TCP Maintenance and Minor Extensions Working Group <tcpm.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/tcpm>, <mailto:tcpm-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/tcpm/>
List-Post: <mailto:tcpm@ietf.org>
List-Help: <mailto:tcpm-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/tcpm>, <mailto:tcpm-request@ietf.org?subject=subscribe>
X-List-Received-Date: Thu, 30 Apr 2020 07:31:49 -0000

Hello all,

While working with RFC 5925, I have been checking the details of
example SNE algorithm. I have a slight concern with the example
algorithm proposed by the RFC.

The idea of SNE is to simulate 64-bit sequence number with 32-bit
sequences to prevent replay attacks. RFC 5925 proposes the following
algorithm to implement 64-bit sequence numbering:

Original algorithm pseudo-code in the RFC 5925 + errata:
     /* set the flag when the SEG.SEQ first rolls over */
     if ((RCV.SNE_FLAG == 0)
        && (RCV.PREV_SEQ > 0x7fffffff) && (SEG.SEQ < 0x7fffffff)) {
           RCV.SNE = RCV.SNE + 1;
           RCV.SNE_FLAG = 1;
     }
     /* decide which SNE to use after incremented */
     if ((RCV.SNE_FLAG == 1) && (SEG.SEQ > 0x7fffffff)) {
        SNE = RCV.SNE - 1; # use the pre-increment value
     } else {
        SNE = RCV.SNE; # use the current value
     }
     /* reset the flag in the *middle* of the window */
     if ((RCV.PREV_SEQ < 0x7fffffff) && (SEG.SEQ > 0x7fffffff)) {
        RCV.SNE_FLAG = 0;
     }
     /* save the current SEQ for the next time through the code */
     RCV.PREV_SEQ = SEG.SEQ;

When looking into this in detail, to me it looks like there might be
an issue when 32-bit sequence wraps back to the beginning and a
retransmission occurs with the high sequence range. Then SNE is
increased unexpectedly and does not follow proper sequencing. A test
case below:

State:
PREV_SEQ = 0xffffffe
RCV.SNE = 0
RCV.SNE_FLAG = 0

New frame:
SEG.SEQ = 0x0
RCV.SNE => 1
RCV.SNE_FLAG => 1
PREV_SEQ => 0

Retransmit:
SEG.SEQ = 0xfffffffd
RCV.SNE_FLAG => 0 - incorrectly updated
PREV_SEQ => 0xfffffffd - incorrectly updated

New frame:
SEG.SEQ = 0x2
RCV.SNE => 2 - incorrect value, should be 1 based on sequencing
RCV.SNE_FLAG => 1
PREV_SEQ => 0x2

I could not figure out any reason why the above sequence would not be
valid. Even though it is mentioned in the RFC that the algorithm is
just an example, the sequencing mismatch is unfortunate as mismatching
SNEs on different sides will bring the authenticated TCP session down
and cause data loss through timeout. Thus, for interoperability, it is
crucial that correct sequencing is used on both ends.

If above is really a real problem, then perhaps an improved algorithm
could be as follows:

Improved algorithm suggestion pseudo-code:
     PRE = 0;
     // set the flag when the SEG.SEQ first rolls over
     if ((RCV.SNE_FLAG == 0)
        && (RCV.PREV_SEQ > 0x7fffffff) && (SEG.SEQ < 0x7fffffff)) {
          RCV.SNE = RCV.SNE + 1;
          RCV.SNE_FLAG = 1;
     }
     // decide which SNE to use after incremented,
     // pre-increment is used for retransmits
     if ((RCV.SNE_FLAG == 1) && (SEG.SEQ > 0x7fffffff)) {
        SNE = RCV.SNE - 1; # use the pre-increment value
        PRE = 1;
     } else {
        SNE = RCV.SNE; # use the current value
     }
     // reset the flag in the *middle* of the window,
     // include SEG.SEQ equals half so that we do not miss
     // that edge case
     if (PRE == 0) {
        if(RCV.SNE_FLAG == 1 &&
           RCV.PREV_SEQ < 0x7fffffff) && (SEG.SEQ >= 0x7fffffff)) {
           RCV.SNE_FLAG = 0;
        }
        // save the current SEQ for the next time through the code,
        // if we are not retransmitting
        RCV.PREV_SEQ = SEG.SEQ;
     }

This passes the problematic case (and bunch of others I have tested).
This also includes a small additional fix in the middle window
handling where SEG.SEQ was missing one edge value (SEG.SEQ >
0x7fffffff => SEG.SEQ >= 0x7fffffff). I hope I have not missed
anything else, but it is certainly possible, so comments are welcome.

So, what do you think, is this a real concern? If it is a real issue,
what would be the best way to handle this, perhaps an errata with an
improved algorithm after throughout review of the new algorithm?

Thank you,
--
 Juhamatti