[tsvwg] DTLS in SCTP: my concern and proposal

tuexen@fh-muenster.de Sun, 16 March 2025 07:09 UTC

Return-Path: <tuexen@fh-muenster.de>
X-Original-To: tsvwg@mail2.ietf.org
Delivered-To: tsvwg@mail2.ietf.org
Received: from localhost (localhost [127.0.0.1]) by mail2.ietf.org (Postfix) with ESMTP id 266C8BFDE41 for <tsvwg@mail2.ietf.org>; Sun, 16 Mar 2025 00:09:24 -0700 (PDT)
X-Virus-Scanned: amavisd-new at ietf.org
X-Spam-Flag: NO
X-Spam-Score: -4.198
X-Spam-Level:
X-Spam-Status: No, score=-4.198 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Received: from mail2.ietf.org ([166.84.6.31]) by localhost (mail2.ietf.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 0YS3FuVg-c80 for <tsvwg@mail2.ietf.org>; Sun, 16 Mar 2025 00:09:23 -0700 (PDT)
Received: from mx-out-02.fh-muenster.de (mx-out-02.fh-muenster.de [212.201.120.206]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256) (No client certificate requested) by mail2.ietf.org (Postfix) with ESMTPS id DFBECBFDE3A for <tsvwg@ietf.org>; Sun, 16 Mar 2025 00:09:22 -0700 (PDT)
Received: from mail-director-01.fh-muenster.de (mail-director-01.fh-muenster.de [185.149.215.227]) by mx-out-02.fh-muenster.de (Postfix) with ESMTPS id 565A7E0487 for <tsvwg@ietf.org>; Sun, 16 Mar 2025 08:04:21 +0100 (CET)
Received: from smtpclient.apple (ip1f100e7a.dynamic.kabel-deutschland.de [31.16.14.122]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: tuexen) by mail-director-01.fh-muenster.de (Postfix) with ESMTPSA id 34B371A0048 for <tsvwg@ietf.org>; Sun, 16 Mar 2025 08:04:20 +0100 (CET)
From: tuexen@fh-muenster.de
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.400.131.1.6\))
Message-Id: <311D3632-F539-4109-BDF8-BE8A064EB5AE@fh-muenster.de>
Date: Sun, 16 Mar 2025 08:04:20 +0100
To: tsvwg <tsvwg@ietf.org>
X-Mailer: Apple Mail (2.3826.400.131.1.6)
Message-ID-Hash: UX2QVITNETOFKYSNZVXP2W776SFOAC3J
X-Message-ID-Hash: UX2QVITNETOFKYSNZVXP2W776SFOAC3J
X-MailFrom: tuexen@fh-muenster.de
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-tsvwg.ietf.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
X-Mailman-Version: 3.3.9rc6
Precedence: list
Subject: [tsvwg] DTLS in SCTP: my concern and proposal
List-Id: Transport Area Working Group <tsvwg.ietf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/tsvwg/VaqRo1mMuK38DHHiJUjPwpNTDlY>
List-Archive: <https://mailarchive.ietf.org/arch/browse/tsvwg>
List-Help: <mailto:tsvwg-request@ietf.org?subject=help>
List-Owner: <mailto:tsvwg-owner@ietf.org>
List-Post: <mailto:tsvwg@ietf.org>
List-Subscribe: <mailto:tsvwg-join@ietf.org>
List-Unsubscribe: <mailto:tsvwg-leave@ietf.org>

Dear all,

This email was originally sent to the members of the former design team.
It was suggested to send it also the TSVWG mailing list.
I covers only technical aspects.

You all know that we had three alternate solutions. Last IETF there was the
consensus to go for a fourth solution, which is a compromise.
Part of the compromise was that we do the protection at the packet
level, not at the payload level.
So the starting point of a discussing was
https://datatracker.ietf.org/doc/draft-westerlund-tsvwg-sctp-dtls-chunk/

I suggested some modifications, but they seem to be unacceptable to the
authors of the ID. So I am seeking out to you all for further input for
this discussion, since there is an agreement that we need to move forward.

Here is a figure describing the architecture, which I'll use in this email.

  +---------------+ +------------------------------+
  |      ULP      | |           DTLS 1.3           |
  |               | |   +---------------------+    |
  |               | | +-+    Key Management   +--+ |
  |               | | | +---------------------+  | |
  |               | | |     +---+ +---+          | |
  |               | | |     | H | |   |          | |
  |               | | |     | a | |   |          | |
  |               | | | k   | n | | A |        k | |
  |               | | | e   | d | | l |        e | |
  |               | | | y   | s | | e |   ...  y | |
  |               | | | s   | h | | r |        s | |
  |               | | |     | a | | t |          | |
  |               | | |     | k | |   |          | |
  |               | | |     | e | |   |          | |
  |               | | |     +-+-+ +-+-+          | |
  |               | | |       |     |            | |
  |               | | |       +-----+---...      | |
  |               | | | ContentType |            | |
  |               | | |  +----------+----------+ | |
  |               | | +->|        Record       | | |
  |               | |    | Protection Operator | | |
  +               | |    +----------+----------+ | |
  +-------+-------+ +----------------------------+-+
          |                          |           |
          +--+-----------------------+           | keys
        PPID |                                   V
  +----------------------------------------------+-+
  |                    +---------------------+   | |
  |        SCTP        |         Chunk       |<--+ |
  |                    | Protection Operator |     |
  |                    +---------------------+     |
  +------------------------------------------------+

The figure is not perfect and it does intentionally not show
kernel/userland boundaries.

The Record Protection Operator transform an DTLSInnerPlaintext, which
contains a message (handshake, alert, ...) and a ContentType and
computes the DTLSCiphertext, which contains the unified_hdr and
the encrypted_record and vice versa.

The Chunk Protection Operator transforms an DTLSInnerPlaintext, which
contains a message (list of SCTP chunks) and uses application_data
as the ContentType and computes the DTLSCiphertext, which contains
the unified_hdr and the encrypted_record and vice versa.
The DTLSCiphertext is the chunk value of the DTLS chunk.

From a functional point of view, the Record Protection Operator and
the Record Protection Operator are very similar. Both also provide
replay protection.

The proposal from Ericsson is that the Record Protection Operator and
the Record Protection Operator are identical. This means they share
the same code and the same state.
Since sharing the state is hard, a solution is to have only one
protection layer which does both. This is simple to implement,
if SCTP and DTLS are both implemented in user space or kernel space.
However, doing full DTLS in kernel space is not acceptable for FreeBSD
or Linux kernel developers at the moment.
But this is not necessary. For a kernel SCTP implementation using the
socket APU one can do the following:

  +---------------+ +------------------------------+
  |      ULP      | |           DTLS 1.3           |
  |               | |   +---------------------+    |
  |               | |   +    Key Management   +--+ |
  |               | |   +---------------------+  | |
  |               | |       +---+ +---+          | |
  |               | |       | H | |   |          | |
  |               | |       | a | |   |          | |
  |               | |       | n | | A |        k | |
  |               | |       | d | | l |        e | |
  |               | |       | s | | e |   ...  y | |
  |               | |       | h | | r |        s | |
  |               | |       | a | | t |          | |
  |               | |       | k | |   |          | |
  |               | |       | e | |   |          | |
  |               | |       +-+-+ +-+-+          | |
  |               | |         |     |            | |
  |               | |         +-----+---...      | |
  |               | |               |            | |
  +-------+-------+ +----------------------------+-+
          |                         |            |
          +--+----------------------+            | keys
        cmsg |        cmsg_data=ContentType      V
  +----------------------------------------------+-+
  |                    +---------------------+   | |
  |        SCTP        |    Chunk/Record     |<--+ |
  |                    | Protection Operator |     |
  |                    +---------------------+     |
  +------------------------------------------------+

So when the DTLS stack sends a message needing to be protected,
it calls sendmsg() with the message (handshake, alert, ...) and
a cmsg providing the ContentType. Then the kernel know that
the Record Protection Operator should be applied to the message
before putting it into the send buffer.
If packet level protection is enabled, the SCTP stack will use
the Chunk Protection Operator to construct the DTLS chunk just
before sending the SCTP packet.

If a packet is received and contains a DTLS chunk, the Chunk
Protection Operator will decrypt the chunks and performs the
replay protection.
When a user message is put into the receive buffer, the PPID
is inspected to figure out, if the Record Protection Operator
needs to be applied. If that is the case, the content type
will be provided as a cmsg.

So the proposal of Ericsson an be implemented also for SCTP kernel
stacks.
What is needed is:
* a DTLS implementation which provides the DTLSInnerPlaintext.
 This looks similar to what TLS implementation might do for QUIC,
 but this time it is DTLS, not TLS.
* an implementation of the DTLS record layer in the kernel
I think we all agree up to here. If not, please let me know.

Here is my concern:

In my view, Record Protection Operator and Chunk Protection Operator
are conceptually different things. I agree that they are functionally
very similar. In particular I would have no problem is using the same
code. But sharing the state between these two operators it what concerns
me. I have fixed a couple of security issues where sharing the state
between unrelated/independent functions was the root cause.
Let me illustrate this by an example.
Since DTLS messages are transmitted via SCTP reliably, it makes no
sense to me to retransmit them at the DTLS layer.
If both protection operators share the same state, they perform the
replay protection the same data (most likely an array and the sequence
number of the left edge). Let us assume the replay window is W.
Now the sender sends a key_update and uses sequence number S.
Assume this SCTP packet containing this message is lost.
If the SCTP sender sends a lot more packets and they all
arrive at the receiver and the retransmission is W packets later,
the Chunk Protection Operator will process the packet successfully,
but the Record Protection Operator will drop the valid packet.
This should not happen.
This is an example, and we can fix it. But it keeps the door open for
attacks/problems related to this state sharing (including side channel
attacks).

Here is my proposal:

  +---------------+ +------------------------------+
  |      ULP      | |           DTLS 1.3           |
  |               | |   +---------------------+    |
  |               | | +>+    Key Exporter     +--+ |
  |               | | | +---------------------+  | |
  |               | | | +---------------------+  | |
  |               | | +-+    Key Management   +  | |
  |               | | | +---------------------+  | |
  |               | | |     +---+ +---+          | |
  |               | | |     | H | |   |          | |
  |               | | |     | a | |   |          | |
  |               | | | k   | n | | A |        k | |
  |               | | | e   | d | | l |        e | |
  |               | | | y   | s | | e |   ...  y | |
  |               | | | s   | h | | r |        s | |
  |               | | |     | a | | t |          | |
  |               | | |     | k | |   |          | |
  |               | | |     | e | |   |          | |
  |               | | |     +-+-+ +-+-+          | |
  |               | | |       |     |            | |
  |               | | |       +-----+---...      | |
  |               | | | ContentType |            | |
  |               | | |  +----------+----------+ | |
  |               | | +->|        Record       | | |
  |               | |    | Protection Operator | | |
  +               | |    +----------+----------+ | |
  +-------+-------+ +----------------------------+-+
          |                          |           |
          +--+-----------------------+           | keys
        PPID |                                   V
  +----------------------------------------------+-+
  |                    +---------------------+   | |
  |        SCTP        |         Chunk       |<--+ |
  |                    | Protection Operator |     |
  |                    +---------------------+     |
  +------------------------------------------------+

Let the Chunk Protection Operator use keys from the Key Exporter
and its own replay protection (its own sequence number and array).

As the solution above, an implementation of the DTLS record layer
is needed in the SCTP stack.
However, the DTLS stack does not need any modifications. One could
even use a TLS 1.3 implementation, since SCTP can provide a reliable
in-sequence transmission.
This solution also avoids that the SCTP stack looks into the PPID,
which is needed by the first solution. The SCTP stack is not intended
to look into the SCTP stack.

I have also two comments about the DTLS chunk:
1. I am not sure we need more than one DTLS connection in general.
  I do understand that the key management proposed by Ericsson needs
  it. So the R and DCI bits might be specified in that ID.
  But this depends how we specify (multiple versions of) key management
2. I think we can optimize the chunk layout:
  One could use the chunk flags to encode the flags needed for the unified_hdr.
  I think there is not use for the length field in the unified_hdr, so the L
  bit can go away. We could make the sequence number part of the chunk value
  with a fixed size. So the S bit can go away. Not sure, if the Connection ID
  is really needed.
But the first one is something to move around in specifications, the second
one is an optimization.

It would be great, if you can share any opinions or suggestions or questions.
If there are other people you know that can provide input in the discussion,
please forward the mail to them.

Best regards
Michael