EllipticCP
This document describes a protocol that defines packet exchange between nodes using underlying datagram protocol (e.g. UDP). Nodes participating in exchange are:
- client: node that initiates connection;
- server: node that accepts connection.
Overview
Packet consists of packet metadata (header) and EllipticCP message. EllipticCP messages are encrypted and authenticated with AEAD algorithm instantiated with ChaCha20 and Poly1305 (RFC 8439). AEAD algorithm uses shared secret obtained from Curve25519 Diffie-Hellman function.
Both server and client have long-term key pairs. Server generates its short-term key pair on startup and changes it periodically (every hour or so). Client must obtain server's long term public key separately. Before opening connection, client generates key pair which is client's short-term key for this connection. During life time of a connection, both client and server can change short-term key at any time. After short-term key is changed, previous key remains valid until next change of short-term key. Client can not change its short-term key more than once per hour.
Short-term keys have "parity" property. Initial key (server's first key upon startup or client's key generated before opening a connection) is defined to be of even parity. After each key change, its "parity" is also changed (e.g. if old one was 'even', new one is 'odd').
Packet format
Client -> server | Server -> client |
---|---|
Init request packet:(I,C',0,N,Box[INIT_REQ message](C'->S)) |
|
Init reply packet:(I,N,Box[INIT_REP message](S->C')) |
|
Open request packet:(I,C',K,N,Box[OPEN_REQ message](C'->S')) |
|
Open reply packet:(I,C',N,Box[OPEN_REP message](S'->C')) |
|
Message packet:(I,C',N,Box[message](C'->S')) |
Message packet:(I,C',N,Box[message](S'->C')) |
... | ... |
Key exchange packet:(I,C',N,Box[KEYX_REQ message](C'->S')) |
|
Key exchange packet:(I,C',N,Box[KEYX_REP message](S'->C')) |
|
... | ... |
Where:
- I is key identifier (1 byte);
- C' is client's short-term public key (32 bytes);
- S' is server's short-term public key (32 bytes);
- S is server's long-term public key (32 bytes);
- N is nonce (64-bit integer in big endian form);
- 0 is zero zero-padding (64 bytes: all zero);
- K is a cookie (28 bytes) — see cookie in open request message below;
Box[X](C->S)
is a cryptographic box, encrypting and authenticating X from the public key C to the public key S.
Key identifier (KEYID) contains information about keys used to encrypt and authenticate cryptographic box:
- KEYID[7:4]:
- b1111 if server's long-term key is used (init request/reply packets only);
- b0000 if 'even' servers's short-term key is used;
- b0001 if 'odd' server's short-term key is used;
- KEYID[3:0]:
- b1000 if 'even' client's short-term key is used but client's short-term key is not present in packet header (init reply packet only);
- b0000 if 'even' client's short-term key is used;
- b0001 if 'odd' client's short-term key is used.
Client and server initialize nonce for new connection to randomly generated 64-bit number before sending init request/open reply packet. Nonces are increased by precisely 1 for each subsequent packet. Nonce for init reply packet is initialized to randomly generated 64-bit number on startup and is increased by precisely 1 for every subsequent init reply packet. 96-bit AEAD nonce is constructed in the following way:
- client -> server: ASCII "CLNT" is prepended to nonce present in packet header;
- server -> client: ASCII "SRVR" is prepended to nonce present in packet header.
Upon opening connection, both client and server can send any number of message packets.
Client periodically sends key exchange packet to check if server has changed its short-term key and to keep connection alive. If client generates new short-term key, it must send key exchange packet to inform server about new key. Server will encrypt subsequent packets to client with new key only after it receives packet from client which was encrypted with that new key. Client can use new key immediately after receiving key exchange response. If client does not receive response to key exchange after some number of tries, it should consider connection broken.
Server can purge connections after some time of inactivity.
Messages defined by EllipticCP
Message format is (MT,MD[,MT,MD,...]) where:
- MT is 1 byte message type:
- b10000000:
INIT_REQ
— init request message; - b10000001:
INIT_REP
— init reply message; - b10000010:
OPEN_REQ
— open request message; - b10000011:
OPEN_REP
— open reply message; - b10000100:
KEYX_REQ
— key exchange request message; - b10000101:
KEYX_REP
— key exchange reply message; - b000xxxxx: application specific;
- b10000000:
- MD is message data. Structure and length of message data is defined by message type.
Init request message
- Message type is
INIT_REQ
; - Message data is 64 bytes: all zero.
Init reply message
- Message type is
INIT_REP
; - Message data is (I,S',A) where:
- I is key identifier (1 byte): 0 if server's current key is 'even', 1 otherwise;
- S' is server's current short-term public key (32 bytes);
- A is authentication tag (20 bytes) — obtained by computing
HMAC-SHA1(CK, C' || N)
where:- CK is server's "cookie key" — non-shared secret generated once at server startup;
- C' is client's short-term public key (32 bytes);
- N is is nonce (8 bytes) — the same nonce used to construct this message (nonce present in packet header)
Open request message
K — cookie (present in packet header) is constructed by concatenating nonce and authentication tag from previously received init reply packet/message. Server may reject cookies with nonces that are too distant from current init reply nonce counter, thus preventing replay attack using captured open request packets. Purpose of vouch subpacket is to authenticate client towards the server (optional).
- Message type is
OPEN_REQ
; - Message data is (CT,VP[,V]) where:
- CT is connection type (1 byte):
- b10000001: VCONN connection (TBD);
- b10000010: VLINK connection (TBD);
- b0xxxxxxx: application specific;
- VP is vouch present (1 byte): 1 if vouch subpacket is present, 0 otherwise;
- V is vouch subpacket
(C,N,Box[C',S'](C->S))
where:- C is client's long-term public key (32 bytes);
- N is nonce (8 bytes) — 96-bit AEAD nonce is constructed by prepending ASCII "VBOX" to N;
- C' is client's short-term public key (32 bytes).
- S' is server's short-term public key (32 bytes).
- CT is connection type (1 byte):
Open reply message
- Message type is
OPEN_REP
; - Message data is null.
Key exchange request message
- Message type is
KEYX_REQ
; - Message data is (I,C') where:
- I is key identifier (1 byte): 0 if client's current key is 'even', 1 otherwise;
- C' is client's current short-term public key (32 bytes).
Key exchange reply message
- Message type is
KEYX_REP
; - Message data is (I,S') where:
- I is key identifier (1 byte): 0 if server's current key is 'even', 1 otherwise;
- S' is server's current short-term public key (32 bytes).