| Internet-Draft | GoE2EE | July 2026 |
| Justo | Expires 21 January 2027 | [Page] |
This document specifies GoE2EE, an application-layer protocol that provides end-to-end confidentiality and integrity between a client and a server using an ephemeral Elliptic Curve Diffie-Hellman (ECDH) key exchange and an Authenticated Encryption with Associated Data (AEAD) cipher. The protocol aims to provide a security level comparable to TLS 1.3 while allowing a shared secret to be reused across multiple connections originating from the same host, reducing handshake overhead. GoE2EE runs over either TCP or UDP and offers several strategies for a client to retrieve and authenticate the server's long-term public key, including a strategy based on the DNSSEC chain of trust.¶
This note is to be removed before publishing as an RFC.¶
Status information for this document may be found at https://datatracker.ietf.org/doc/draft-justo-goe2ee/.¶
Source for this draft and an issue tracker can be found at https://github.com/rafaeljusto/goe2ee.¶
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 21 January 2027.¶
Copyright (c) 2026 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.¶
Transport Layer Security (TLS) [RFC8446] is the dominant protocol for securing communication on the Internet. It couples confidentiality and integrity with an authentication model that, in its most common deployment, relies on the Web Public Key Infrastructure (PKI) and X.509 certificates [RFC5280].¶
GoE2EE is an application-layer protocol that provides similar cryptographic guarantees to TLS 1.3 but decouples two concerns that TLS binds tightly together:¶
how peers agree on a shared secret and encrypt traffic; and¶
how the client obtains and authenticates the server's long-term public key.¶
For the first concern, GoE2EE performs an ephemeral ECDH key exchange so that the shared secret is never transmitted over the network, derives a symmetric key from that secret, and protects every subsequent message with an AEAD cipher.¶
For the second concern, GoE2EE deliberately leaves the trust model pluggable. A client MAY authenticate the server's public key through the Web PKI, through the DNSSEC chain of trust, through a pre-provisioned key, or, when no authentication is acceptable, by retrieving the key directly from the server in-band.¶
A distinguishing feature of GoE2EE is that a shared secret is bound to a client-chosen identifier rather than to a single transport connection. Consequently, several connections from the same host MAY reuse a previously negotiated secret and skip the handshake entirely, which reduces latency and allows the protocol to operate efficiently over connectionless transports such as UDP.¶
This document specifies the wire format, the cryptographic constructions, the handshake and message-exchange procedures, and the security considerations of the protocol. It corresponds to protocol version 1.¶
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. These words may also appear in this document in lower case as plain English words, absent their normative meanings.¶
The following terms are used throughout this document:¶
The peer that initiates a session and originates request messages.¶
The peer that answers requests and holds a long-term signing key pair.¶
The server's long-term ("global") signing key pair. The server's public global key is used by the client to authenticate the handshake.¶
A 16-octet identifier chosen by the client and associated with a shared secret. It is carried in requests so that the server can locate the corresponding secret.¶
The secret produced by the ECDH exchange, from which the symmetric key is derived.¶
A per-secret, strictly increasing 64-bit value used to derive AEAD nonces and to detect replays.¶
A GoE2EE session has two logical phases: a handshake phase and a message-exchange phase.¶
During the handshake, the client generates an ephemeral ECDH key pair, chooses a session identifier, and sends its public key to the server (the Setup action, Section 5.2). The server generates its own ephemeral ECDH key pair, computes the shared secret, and returns its public key together with a signature over the handshake transcript produced with the server's long-term global key. The client verifies the signature using the server's public global key, which it obtained beforehand through one of the retrieval strategies in Section 6, and then computes the same shared secret.¶
The full handshake is illustrated below.¶
client server | | |-- Setup: id + client ECDH public key ----------->| | (compute shared | | secret, store by id) | |<-- Setup response: server ECDH public key + ------| | hash-type + signature over transcript | | (verify signature, | | compute shared secret) | | | |== Process: id + counter + encrypted message ====>| |<===== Process response: counter + encrypted ======| | |
Because the shared secret is indexed by the session identifier and not by the transport connection, a different connection from the same host that already knows a valid secret and identifier MAY skip the handshake and issue Process requests (Section 5.3) directly.¶
client server | | |== Process: id + counter + encrypted message ====>| |<===== Process response: counter + encrypted ======| | |
The server MUST maintain the mapping from session identifier to shared secret (and the associated replay state, Section 9.4) for as long as it is willing to accept messages for that session, which MAY outlive any individual transport connection. The mechanism and lifetime of this state are implementation choices.¶
All messages are binary. Multi-octet integers are encoded in network byte order (big-endian) unless stated otherwise.¶
Every request begins with a single octet whose two 4-bit fields are the protocol version and the action:¶
0 1 0 1 2 3 4 5 6 7 8 9 ... +-------+-------+---------------------------+ |version|action | message (variable) | +-------+-------+---------------------------+
The protocol version. This document specifies version 1 (0x1). Up to 16
versions can be expressed.¶
An identifier that selects the request semantics. Up to 16 actions can be expressed. This document defines the actions in Table 1.¶
Action-specific content. It MAY be empty.¶
The fixed one-octet header, together with session identifiers carried inside Process requests, is what allows a client to issue actions in any order and to skip the handshake when a secret is already established.¶
The defined actions are:¶
| Action | Name | Section |
|---|---|---|
0x1
|
Hello | Section 5.1 |
0x2
|
Setup | Section 5.2 |
0x3
|
Process | Section 5.3 |
0x4
|
Fetch Key | Section 5.4 |
A server that receives a request whose version it does not support MUST reply
with an error response (Section 3.3) carrying error code
0x04 (Unsupported version).¶
When a request is processed successfully, the response begins with a single octet whose most significant bit is set to 1:¶
0 1 0 1 2 3 4 5 6 7 8 9 ... +-+-------------+---------------------------+ |1| reserved | message (variable) | +-+-------------+---------------------------+
Any request MAY produce an error response. An error response begins with a single octet whose most significant bit is cleared to 0, followed by an error code and an optional human-readable message:¶
+---+----------+--------------+--------------+---------------+ | 0 | resv (7) | err-code (4) | err-size (8) | err-msg (var) | +---+----------+--------------+--------------+---------------+
Cleared to 0 to indicate failure.¶
Reserved for future use; set to 0 on transmit and ignored on receipt.¶
An unsigned 32-bit integer identifying the error. Defined values appear in Table 2.¶
An unsigned 64-bit integer giving the length in octets of the error message.¶
A UTF-8 diagnostic string of the indicated length. It MAY be empty and MUST NOT be relied upon for automated decision-making.¶
The defined error codes are:¶
| Error Code | Description |
|---|---|
0x00000001
|
Malformed request |
0x00000002
|
Server error |
0x00000003
|
Unknown client |
0x00000004
|
Unsupported version |
0x00000005
|
Replay detected |
A receiver MUST treat an error message whose declared size would exceed the receiver's configured limits as a malformed response and abort the session. See Section 9.6.¶
The ephemeral key exchange uses ECDH with the X25519 function [RFC7748]. Public keys are exchanged encoded as a SubjectPublicKeyInfo structure in PKIX, ASN.1 DER form (Section 4.1 of [RFC5280]).¶
Each peer generates a fresh X25519 key pair for every handshake. The shared secret is the X25519 output computed from one peer's private key and the other peer's public key.¶
The raw ECDH output MUST NOT be used directly as a symmetric key, as it is not uniformly distributed. The symmetric key is derived using HKDF [RFC5869] with SHA-256 [FIPS.180-4] as follows:¶
key = HKDF-SHA256(salt = "" (empty),
IKM = ECDH shared secret,
info = "goe2ee/v1 aes-256-gcm",
L = 32)
¶
The info string provides domain separation and MUST be exactly the ASCII
string goe2ee/v1 aes-256-gcm for this version. The salt is empty (a string of
zero length). The output length L is 32 octets, producing a 256-bit key.¶
Messages are protected with AES-256-GCM [FIPS.197] [NIST.SP.800-38D], an AEAD algorithm [RFC5116], keyed with the 32-octet key from the previous section. The authentication tag is 16 octets and is appended to the ciphertext. The Associated Data is empty in this version.¶
AES-GCM requires that a (key, nonce) pair never repeat. GoE2EE never transmits nonces; instead each peer derives the 12-octet GCM nonce deterministically from a direction octet and the message counter:¶
nonce[0] = direction nonce[1..3] = 0x00 0x00 0x00 nonce[4..11] = counter (64-bit, big-endian)¶
where the direction octet is:¶
| Value | Direction |
|---|---|
0x00
|
client to server |
0x01
|
server to client |
The direction octet ensures that the two halves of a conversation never derive the same nonce from the same counter under the shared secret. The counter MUST strictly increase for every message a peer sends under a given secret (see Section 5.3.1). Together these guarantee nonce uniqueness, which is required for the security of AES-GCM.¶
The server authenticates the handshake by signing a transcript that binds both ephemeral public keys and the session identifier. The transcript is the concatenation:¶
+----------+------------+----------+------------+---------+ | clen (4) | client-key | slen (4) | server-key | id (16) | +----------+------------+----------+------------+---------+
where client-key and server-key are the DER-encoded SubjectPublicKeyInfo
representations of the client and server ephemeral public keys, each prefixed by
its 32-bit big-endian length, and id is the 16-octet session identifier.¶
Signing the full transcript rather than the server key alone binds the signature to both parties and to the specific session, so a valid signature cannot be transplanted onto a different key exchange.¶
The signature is produced with the server's long-term global key. The signature algorithm depends on the type of the global key (Table 5):¶
RSA keys: RSASSA-PKCS1-v1_5 [RFC8017] over the hash of the transcript.¶
ECDSA keys: ECDSA over the hash of the transcript, with the signature encoded as an ASN.1 DER sequence.¶
Ed25519 keys: Ed25519 [RFC8032] over the transcript. Note that for Ed25519 the hash type carried in the response (Section 5.2) selects the digest applied to the transcript before signing; the pure Ed25519 algorithm is used (not Ed25519ph).¶
The hash function used to build the digest is signalled by the hash-type field of the Setup response:¶
| Type | Hash |
|---|---|
0x2
|
SHA-256 |
0x3
|
SHA-384 |
0x4
|
SHA-512 |
SHA-1 is intentionally omitted because it is cryptographically broken. A client MUST reject a Setup response that specifies a hash type it does not support.¶
The Hello action is a liveness check. It carries no message body.¶
Request:¶
+-------+-------+ | 0x1 | 0x1 | +-------+-------+¶
Response: a success response (Section 3.3 defines the failure form) with an empty message body.¶
The Setup action performs the key exchange. The client sends its session identifier and ephemeral public key; the server replies with its ephemeral public key and a signature over the transcript (Section 4.5).¶
Request:¶
+-----+-----+---------+-----------+------------+ | 0x1 | 0x2 | id (16) | pklen (4) | public-key | +-----+-----+---------+-----------+------------+
A client-chosen identifier for the secret, formatted as a UUID [RFC4122]. It MUST be unique per host so that the server can distinguish concurrent sessions from the same host, including those traversing intermediaries such as proxies. The client SHOULD choose it so that it is unpredictable to other parties.¶
The length in octets of the following public key.¶
The client's ephemeral X25519 public key, DER-encoded SubjectPublicKeyInfo.¶
Response:¶
+---+------+-----------+--------+-----------+------------+-----+ | 1 | resv | pklen (4) | pubkey | htype (1) | siglen (8) | sig | +---+------+-----------+--------+-----------+------------+-----+
The length in octets of the server's public key.¶
The server's ephemeral X25519 public key, DER-encoded SubjectPublicKeyInfo.¶
The length in octets of the signature.¶
The signature over the handshake transcript (Section 4.5), produced with the server's global key.¶
On receiving the Setup response, the client MUST rebuild the transcript from its own public key, the server's public key, and the session identifier, and MUST verify the signature using the server's public global key. If verification fails, the client MUST abort the session and MUST NOT send Process requests for that identifier. On success, the client computes the shared secret from its private key and the server's public key.¶
The Process action exchanges application data protected by the shared secret.¶
Request:¶
+-----+-----+---+------+---------+---------+----------+---------+ | 0x1 | 0x3 | E | r(7) | id (16) | ctr (8) | mlen (8) | enc-msg | +-----+-----+---+------+---------+---------+----------+---------+
When set to 1, the client expects a response. When cleared to 0, the request is fire-and-forget and the server SHOULD NOT send a response. This is useful over UDP to avoid a return trip.¶
Reserved for future use; set to 0 on transmit and ignored on receipt.¶
The session identifier chosen during Setup, identifying the shared secret.¶
The per-secret message counter (see Section 5.3.1), used both to derive the nonce (Section 4.4) and to detect replays (Section 9.4).¶
The length in octets of the ciphertext (including the AEAD tag).¶
The application payload encrypted with AES-256-GCM under the derived key,
using the nonce derived from direction 0x00 and this counter.¶
If the server does not recognise the identifier, it MUST reply with error code
0x03 (Unknown client). If the counter is a replay or falls before the replay
window, the server MUST reply with error code 0x05 (Replay detected). If
decryption or tag verification fails, the server MUST treat the request as
malformed (0x01).¶
Response (sent only when E is set):¶
+-+----------+-----------+---------------------+---------------+ |1| reserved |counter (8)| enc-msg-size (8) | enc-msg (var) | +-+----------+-----------+---------------------+---------------+
The counter from the request being answered. The server derives the response
nonce from direction 0x01 and this counter.¶
The length in octets of the response ciphertext (including tag).¶
The response payload encrypted under the same shared secret.¶
The counter is a property of the shared secret, not of a transport connection. A client MUST ensure that every Process request it sends under a given secret carries a strictly greater counter than any previous request under that secret. When a secret is shared across several connections from the same host (see Section 7), the counter MUST be incremented atomically so that no value is ever reused across those connections.¶
The server echoes the request counter in the response and derives the response nonce from it with the server-to-client direction octet. Because request and response use different direction octets, the same counter value in both directions does not cause a nonce collision.¶
The Fetch Key action lets a client retrieve the server's public global key in-band. This method provides no authentication of the key and is therefore vulnerable to man-in-the-middle attacks; it MUST NOT be used when an authenticated retrieval strategy (Section 6) is available.¶
Request:¶
+-------+-------+ | 0x1 | 0x4 | +-------+-------+¶
Response:¶
+---+------+------------+-----------+------------+ | 1 | resv | keyalg (1) | pklen (4) | public-key | +---+------+------------+-----------+------------+
The algorithm of the returned key, used to parse it (Table 5).¶
The length in octets of the public key.¶
The server's public global key.¶
The defined key algorithms are:¶
| Algorithm | Description |
|---|---|
0x1
|
RSA |
0x2
|
ECDSA |
0x3
|
Ed25519 |
The security of the handshake rests on the client authenticating the server's public global key. This document does not mandate a single trust model; it defines the following strategies, of which a client MUST use an authenticated one unless the deployment explicitly accepts the risk of an unauthenticated key:¶
The client retrieves the key from a DNSKEY resource record of the server's domain name and validates it through the DNSSEC chain of trust [RFC4033]. To protect the resolution path between the client and the recursive resolver, the client SHOULD use DNS over HTTPS [RFC8484].¶
The client obtains an X.509 certificate [RFC5280] for the server, for example over an HTTPS connection to the server's domain, and validates it against a trusted certification authority.¶
The key is provided to the client out of band, for example as a PEM file.¶
The client retrieves the key using the Fetch Key action (Section 5.4). This strategy is unauthenticated and is NOT RECOMMENDED.¶
The choice of strategy is a local deployment decision and does not affect the wire format of the handshake.¶
Because a shared secret is bound to a session identifier rather than to a transport connection, a client MAY reuse an established secret for multiple connections to the same host and MAY skip the handshake on those connections, issuing Process requests directly. This reduces both round trips and cryptographic work.¶
A client that reuses a secret across connections MUST coordinate the counter as described in Section 5.3.1 so that nonces are never reused. Implementations that pool connections SHOULD share a single counter, incremented atomically, per secret.¶
The server retains the association between identifier, shared secret, and replay state independently of any connection, so it can serve Process requests for a known identifier that arrive on a fresh connection.¶
GoE2EE operates over either TCP or UDP. Over TCP, the transport provides ordering and delivery guarantees. Over UDP, the protocol imposes none: messages may be lost, reordered, or duplicated.¶
The replay window (Section 9.4) tolerates limited reordering, and the deterministic nonce derivation (Section 4.4) does not require in-order delivery. The fire-and-forget mode of the Process action (Section 5.3) is particularly suited to UDP, where avoiding a return trip can significantly reduce overhead for small payloads.¶
Applications that require reliable, ordered delivery SHOULD use TCP or provide their own reliability layer above GoE2EE.¶
The confidentiality of a GoE2EE session depends entirely on the client authenticating the server's public global key before trusting the handshake signature. If a client accepts an unauthenticated key -- notably one obtained through the Fetch Key action (Section 5.4) -- an active attacker can mount a man-in-the-middle attack, presenting its own key and relaying or reading all traffic. Deployments SHOULD use an authenticated retrieval strategy (Section 6).¶
The server signs a transcript (Section 4.5) that includes both ephemeral public keys and the session identifier. This binds the signature to the specific key exchange and session, preventing a signature captured from one handshake from being replayed into another. The client MUST verify this signature and MUST abort on failure.¶
The protocol as specified does not authenticate the client to the server at the cryptographic layer; any client that can present a valid identifier and correctly encrypted messages will be served. Deployments that require client authentication MUST provide it at the application layer or by another mechanism.¶
AES-GCM catastrophically fails if a (key, nonce) pair is ever reused: an attacker who observes two messages encrypted with the same key and nonce can recover the authentication subkey and forge messages [NIST.SP.800-38D]. GoE2EE relies on the strictly increasing per-secret counter and the direction octet (Section 4.4) to guarantee uniqueness. Implementations MUST ensure the counter never repeats or goes backwards for a given secret, including across pooled connections (Section 5.3.1), and MUST stop using a secret before the 64-bit counter would wrap.¶
The server maintains, per secret, a sliding-window replay filter in the style of
the IPsec anti-replay algorithm [RFC6479]. The window tracks the highest
counter seen and a bitmap of the 64 counter values immediately below it. A
message is accepted only if its counter has not been seen before and is not older
than the window; otherwise the server rejects it with error code 0x05 (Replay
detected). This bounds the reordering the server tolerates over unreliable
transports while preventing an attacker from replaying captured Process
requests.¶
Because replay state is per secret and outlives connections, an implementation that reuses a secret across connections shares one replay window for all of them, consistent with the shared counter requirement.¶
Each handshake uses fresh ephemeral X25519 key pairs, so the compromise of a server's long-term global key does not by itself reveal the shared secrets of past sessions. However, reusing a shared secret across many connections (Section 7) enlarges the amount of data protected by a single key and the impact of that secret's compromise. Deployments SHOULD bound the lifetime and volume of data associated with any single secret and re-run the handshake periodically.¶
Several fields are length-prefixed with sizes up to 64 bits. A malicious peer could advertise an enormous size to force large allocations. Implementations MUST enforce sane upper bounds on the sizes of public keys, signatures, and encrypted messages before allocating, and MUST reject messages that exceed those bounds as malformed. The server also holds per-secret state indexed by client-chosen identifiers; an implementation SHOULD bound the number of concurrent sessions and expire idle state to limit memory exhaustion.¶
The version field (Table 1) allows future evolution. A server MUST
reject unsupported versions (0x04). Because the version and action occupy a
single unauthenticated header octet at the start of each request, deployments
concerned with downgrade across versions SHOULD ensure that any future version
that changes the cryptographic constructions also changes the HKDF info label
so that keys are not shared across versions.¶
This document has no IANA actions.¶
The actions (Table 1), error codes (Table 2), hash types (Table 4), and key algorithms (Table 5) defined here are namespaces internal to the protocol and are not requested for registration in any IANA registry by this document. Should this protocol be standardized, the creation of IANA registries for these code points would be appropriate.¶
The design of GoE2EE draws on the architecture of TLS 1.3 [RFC8446] and on established constructions for key derivation [RFC5869], authenticated encryption [NIST.SP.800-38D], and anti-replay [RFC6479].¶