Internet-Draft | CORE Authenticated Key Exchange (CAKE) | August 2025 |
Schanzenbach & ch3 | Expires 5 February 2026 | [Page] |
This document contains the GNUnet CORE AKE (CAKE).¶
This document defines the normative wire format of the protocol, cryptographic routines and security considerations for use by implementers.¶
This specification was developed outside the IETF and does not have IETF consensus. It is published here to inform readers about the function of GNUnet communicators, guide future implementations, and ensure interoperability including with the pre-existing GNUnet implementation.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 5 February 2026.¶
Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
This specification was developed outside the IETF and does not have IETF consensus. It is published here to guide implementers of GNS and to ensure interoperability among implementations.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
While some of the terminology is explicitly re-defined here, the reader is expected to be familiar with TLS 1.3 ([RFC8446]), DTLS 1.3 ([RFC9147]) and HPKE ([RFC9180]).¶
The design rationale for CAKE is similar to DTLS 1.3 (cf. Section 3 of [RFC9147]). Except that CAKE does not consider Fragmentation as this is expected to be provided by the transport underlay layer of GNUnet.¶
This protocol is heavily inspired by [KEMTLS].¶
We assume that the peers have semi-static (as opposed to ephemeral) key pairs. Let (pkA,skA) be the key pair of peer PIDA and (pkB,skB) the key pair of peer PIDB.¶
For any secure handshake protocol, we have to dermine an initiator and a responder in the protocol. We use GNUNET_CRYPTO_hash_cmp
to determine which peer is the responder R and which peer the initiator I:¶
if (GNUNET_CRYPTO_hash_cmp (pk_A, pk_B)) { pk_I = pk_A pk_R = pk_B } else { pk_I = pk_B pk_R = pk_A }¶
It is possible that the designated initiator does not initiate the handshake. After a pre-determined timeout, the respective other peer may initiate.
We assume that the initiator knows pkR (pre-distributed through HELLO
, for example).¶
Below is a swimlane of the protocol messages. On the left and right side of the swimlanes the secrets known to the Initiator and Responder are shown respectively. If a private key of a key pair is known it is implied that the public key is also known. Messages in brackets are optional. Messages in braces are encrypted.¶
Initiator Responder sk_I | | sk_R sk_e | | r_I | | pk_R | | c_R | | ss_e | | ss_R | | | | | InitiatorHello: | | r_I | | pk_e | | c_R | | H(pk_R) | | {pk_I,pc_I,svcinfo_I}ETS | +---------------------------------------------->| | | r_R | | r_I | | pk_I | | c_R | | c_I | | c_e | | ss_R | | ss_I | | ss_e | ResponderHello: | | r_R | | c_e | | {c_I,pc_R,svcinfo_R}RHTS | | {finished_R}RHTS | r_R | | c_I | | c_e | | ss_I | | ss_e | | | InitiatorDone: | | {finished_I}IHTS | | [ACK]IATS | +---------------------------------------------->| | | | | | [ACK]RATS | |<----------------------------------------------+ | | | | | [Application Payload] | |<--------------------------------------------->| | | v v
Notice how we do not need any acknowledgement messages until after finishedR (after 1 RTT). We use the ACKs in the handshake as explicit key confirmations. The InitiatorHello message is a single flight that is implicitly ack'ed with ResponderHello. ResponderHello is a single flight that is implicitly ack'ed with finishedI. The reason why this works is because CAKE groups the messages in row 3 of Table 1 in Section 5.7 of [RFC9147] into a single message (ResponderHello). Hence the only message that is sent without any expected response (and consequently requiring an explicit ACK) is finishedI (and Heartbeats). pcX are 16 bit fields that indicate the peer class (FIXME peer class section). NI is a nonce generated by the initiator. NR is a nonce generated by the responder.¶
The Initiator creates the InitiatorHello message which includes the encrypted tuple (pkI,svcinfo_I). The fields are encrypted using a key derived from the ETS according to Figure 1 and Figure 2. The so-called Responder KEM Challenge cR and the nonce rI are computed as:¶
R processes the InitiatorHello as follows:¶
The ETS, the Handshake and Master Secrets are generated according to Figure 2. Note that IATS cannot be derived (yet) at this point. R may now generate its ResponderHello message:¶
I processes the message received by R:¶
At this point we have a secure channel.¶
Note that for the handshake we do not use epochs or sequence numbers. The reason for this is simple: DTLS uses epoch 0 for plaintext messages. Epoch 1 is reserved for payload encrypted with a key derived from ETS. However, we only have a single message that contains such a payload: InitiatorHello. Epoch 2 is reserved for payload encrypted with a key derived from *HTS. But we only have a single message that contains a payload encrypted with a key derived from RHTS: ResponderHello. We also only have a single message that contains a payload encrypted with a key derived from IHTS: finishedI. Consequently, we do not need any signalling of Epochs until we encrypt data using *ATS secrets. The optional application data that may already be sent by the responder after its first handshake message or by the initiator after its second handshake message, are already wrapped inside an EncryptedMessage and have both Epoch and sequence numbers set.¶
The key schedule is very similar to [RFC8446] Section 7.1:¶
HKDF-Extract(ss_R,0) = Early Secret (ES) | +-----> HKDF-Expand-Label(., | "early data", | H(T(H(pk_R)))) | = Early Transport Secret (ETS) | v HKDF-Expand-Label(., | "derived", | "") = derived Early Secret (dES) | v HKDF-Extract(ss_e,.) = Handshake Secret (HS) | +-----> HKDF-Expand-Label(., | "i hs traffic", | H(T(r_R))) | = IHTS | +-----> HKDF-Expand-Label(., | "r hs traffic", | H(T(r_R))) | = RHTS v HKDF-Expand-Label(., | "derived", | "") = derived Handshake Secret (dHS) | v HKDF-Extract(ss_I,.) = Master Secret (MS) | +-----> HKDF-Expand-Label(., | "i ap traffic", | H(T({finished_I}))) | = IATS_0 | +-----> HKDF-Expand-Label(., "r ap traffic", H(T({finished_R}))) = RATS_0
"." shows the argument position of the input variable from the incoming arrow.¶
In general the transcript hashes are part of the HKDF-Expand calls. The transcript hash is defined as the hash over the message parts sent (or to be sent) and received on the wire up until that point.¶
Notice that from the very beginning ssR is required for the key schedule. This means that R must be able to solve the Responder KEM Challenge cR. Similarly, the master secret (MS) requires knowledge of ssI. This means that I must be able to solve the Initiator KEM Challenge cI. The KEM Challenges provide the underlying public key authentication mechanism.¶
When a traffic secret ([I,R][A,H]TS or ETS) is used to encrypt data, the respective encryption key and starting nonce is generated as follows:¶
key = HKDF-Expand-Label [I,R][A,H]TS, "key", 32) nonce = HKDF-Expand-Label ([I,R][A,H]TS, "iv", 24)
Notice that the per-message nonce is generated from the nonce above as defined in Section 5.3 of [RFC8446] from the sequence number.¶
After a successful initial handshake, both initiator and responder may update the application traffic secrets ([A,I]ATS) and generate new keys. Let [I,R]ATS0 be the initial secrets with index 0. The next secret is derived as:¶
[I,R]ATS_N+1 = HKDF-Expand-Label ([I,R]ATS_N, "traffic_upd", secret_len)
When a peer wants to update keys, it sends a key update message Section 6.10. Implementations SHOULD delete old traffic secrets and their derived keys.¶
This section is named and structured to mimic Section 5 of [RFC9147].¶
CAKE reuses the logic for timeout and retransmission from Section 5.8 of [RFC9147]. It differs in that large flight sizes are not of concern for CAKE. Similarly, the only Post-Handshake message relevant for CAKE is the KeyUpdate message.¶
Section 7.1 of [RFC8446] specifies that HKDF-Expand-Label uses a label prefix of "tls13 ". For CAKE, that label SHALL be "cake10". This ensures key separation between CAKE, DTLS 1.3 and TLS 1.3.¶
The svcinfo field is a string consisting of key-value pairs separated by a separator indicating supported services and their versions. E.g. "dht:1.1;cadet:0.4". The field is zero terminated.¶
The HandshakeFinished field contains either finishedI or finishedR value:¶
Any sent message starts with a MessageHeader
:¶
0 8 16 24 32 40 48 56 +-----+-----+-----+-----+-----+-----+-----+-----+ | Size | Type (0xXX) | +-----+-----+-----+-----+-----+-----+-----+-----+
The possible types of messages are:¶
An encrypted message also always starts with a MessageHeader and the allowed types are:¶
The InitiatorHello:¶
0 8 16 24 32 40 48 56 +-----+-----+-----+-----+-----+-----+-----+-----+ | r_I | +-----+-----+-----+-----+-----+-----+-----+-----+ | pk_e | | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ | c_R | | | | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ | H(pk_R) (512 bit) | / / | | +-----+-----+-----+-----+-----+-----+-----+-----+ / {pk_I,pc_I,svcinfo_I} /
The initiator kem challenge cR is generated according to Figure 2 using:¶
The pkI and ServiceInfo
are encrypted using XChaCha20-Poly1305 [RFC8439]
with key and IV derived from the ETS.¶
The ResponderHello:¶
0 8 16 24 32 40 48 56 +-----+-----+-----+-----+-----+-----+-----+-----+ | r_R | +-----+-----+-----+-----+-----+-----+-----+-----+ | c_e | | | | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ / {c_I,pc_I,svcinfo_R}{finished_R} /
The protected fields after the nonce are encrypted using a key derived from AHTS. The finishedR is encrypted individually. This is because the transcript of the ResponderHello to generate the finishedR must end before it.¶
The InitiatorDone message contains the finishedI field encrypted with a key derived from the IHTS. The message type MUST be CORE_INITIATOR_DONE.¶
The EncryptedMessage follows a message header with type CORE_ENCRYPTED_MESSAGE
:¶
0 8 16 24 32 40 48 56 +-----+-----+-----+-----+-----+-----+-----+-----+ | Epoch | +-----+-----+-----+-----+-----+-----+-----+-----+ | Sequence Number | +-----+-----+-----+-----+-----+-----+-----+-----+ | Tag | | | +-----+-----+-----+-----+-----+-----+-----+-----+
The epoch starts at 0 after the handshake with the first *ATS secret. Any peer may at any time update to a new epoch. Peers may keep a history of secrets for respective epochs at their own discretion in order to handle out-of-order message deliveries. Unlike DTLS1.3 in CAKE does not truncate the epoch and no reconstruction is necessary, hence the epoch may be bumped by peers at their own discretion without explicit key update mechanisms. A peer may consider the epoch too old or too far in the future and reject description. For this purpose, a peer may manage a sliding window of epochs that can be used by the other peer.¶
The sequence number is encrypted as defined in Section 4.2.3 of [RFC9147] for ChaCha20-based AEAD schemes. For clarity, the XOR-based encryption using the 64 byte output of ChaCha20 is as follows: The Tag is divided into a 32-bit counter and 96-bit nonce for use with ChaCha20. The key is derived from the *ats as follows:¶
sn_secret = HKDF-Expand-Label [I,R]ATS_N, "sn", 32)
Where [I,R]ATS_N is the respective ATS secret of epoch N. The leading 8 bytes of the 64 byte output of ChaCha20 are then XORed with the sequence number in network byte order:¶
sn_key = HKDF-Expand-Label(Secret, "sn", "", 8) mask = ChaCha20(sn_key, Ciphertext[0..3], Ciphertext[4..15]) sn_enc = mask[0..8] XOR sn_nbo
Notice how this requires a ciphertext of at least 16 bytes. But our ciphertexts are always at least 16 bytes due to the Poly1305 atuthentication tag. In fact, since the authentication tag is considered part of the ciphertext, and is prepended in front of the encrypted plaintext, the mask is always computed on the authentication tag only.¶
The Tag is followed by encrypted application data. The length of the data is included in the size field of the MessageHeader preceeding the EncryptedMessage header.¶
The per-message nonce is not transmitted and instead generated as defined in Section 5.3 of [RFC8446].¶
The HEARTBEAT message is a simple MessageHeader inside an EncryptedMessage with type CORE_HEARTBEAT
followed by an UpdateRequested indicator.
This means that for every received EncryptedMessage
the peer MUST check if this is a Heartbeat.
A Heartbeat message may implicitlyindicate that the sender has switched its
traffic secrets according to the key schedule in Section 5.
If any bit in the UpdateRequested field is set, this means that the responder
of the Heartbeat SHOULD increment its epoch.
Any bytes following the UpdateRequested field are updated services info
svcinfo (Section 6.3).
Services info updates are optional.¶
0 8 16 24 32 +-----+-----+-----+-----+ | UpdateRequested | +-----+-----+-----+-----+ | svcinfo / / /
The ACK message is a simple MessageHeader inside an EncryptedMessage with
type CORE_ACK
.
This means that for every received EncryptedMessage
the peer MUST check if this is an ACK message after decryption.
The peer may use the ACK in order to ensure liveliness of connected peers in
combination with Heartbeats.¶
We must discuss EdDSA vs X25519 KEM usage. Maybe see Communicator draft for this. Basically, since we also use the EdDSA peer ID key for signing (HELLOs), we offer any attacker a signing oracle against our X25519 public keys. This should be safe, but it is not clean. To solve this, we would have to separate HELLO signing keys and PID KX keys. This issue did not arise before, because we used signed semi-statix DH keys.¶
The IETF ChaCha20 version is basically only recommended for use when a specification or compatibility specifically requires it. With ChaCha20, we would have to increment the nonce as it cannot be chosen securely at random (not long enough). XChaCha20 is the generally recommended cipher for any use case and we use it. The only downside seems to be that XChaCha20 is practically not specified anywhere (although it can be trivially defined in this document based on HChaCha) and only really implemented in libsodium.¶
We must define which KEM is to be used. We may want to use our HPKE Elligator KEM [LSD0011]. Using Elligator on this level may not be useful unless we also get rid of the plaintext message headers since those definitely do not look random. Ergo, the use of elligator probably makes more sense on the communicator level.¶
The Initiator/Responder selection logic may require a timed fallback: The designates Initiator may never initiate (NAT, already has sufficient connections, learns about responder later than responder about initiator etc.). This may result in edge cases where the Initiator initiates a handshake and the Responder also initiates a handshake at the same time switching roles. In such cases we may simply do both key exchanges. If both succeed, we drop the key exchange that was not initiated by the designated initiator on both peers. Otherwise we use the successful key exchange and the roles are swapped.¶
-¶
The CAKE handshake is currently implemented in a branch. Open tasks include:¶