aboutsummaryrefslogtreecommitdiff
path: root/protocol_transport.md
blob: 7915e29badf1033c88590f3cad6560b9bd8a9439 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# tomsg transport protocol

This is the specification for the lower-level transport protocol that underlies
the application-level protocol described in `protocol.md`. This transport is
used for communication between a client and the server.

If at any point one of the parties breaks the protocol, the socket and transport
should be closed.

## Size assumptions

The protocol makes a few assumptions about lengths of certain values in the
libsodium API. An implementation of the protocol should verify that these
assumptions still hold when the application is finally run.

- `crypto_secretstream_xchacha20poly1305_KEYBYTES` = 32
- `crypto_secretstream_xchacha20poly1305_HEADERBYTES` = 24
- `crypto_kx_SESSIONKEYBYTES` >= 32
- `crypto_kx_PUBLICKEYBYTES` = 32
- `crypto_kx_SECRETKEYBYTES` = 32

## Message types

- ClientHello:
  - 16 bytes magic: `tomsg v01 client`
  - 32 bytes client public key
- ServerHello:
  - 16 bytes magic: `tomsg v01 server`
  - 32 bytes server public key
  - 24 bytes server->client libsodium secretstream header
- ClientHello2:
  - 8 bytes magic: `cheader0`
  - 24 bytes client->server libsodium secretstream header
- DataMessage:
  - 8 bytes magic: `datamsg0`
  - 8 bytes (unsigned little-endian) message length = N
  - N bytes data

## Initialisation sequence

### Client initialisation sequence

The following handshake is performed in order to set up two libsodium
secretstreams, one for client->server communication (a push stream) and one for
server->client communication (a pull stream).

1. Generate a keypair using `crypto_kx_keypair`. These keys are referred to as
   the client public and secret keys.
2. Send a ClientHello message.
3. Receive a ServerHello message.
   - Check that the magic is correct for this version of the transport protocol.
   - Use `crypto_kx_client_session_keys` to compute the client->server and
     server->client encryption keys.
   - Use `crypto_secretstream_xchacha20poly1305_init_push` to create the
     client->server libsodium push secretstream using the client->server
     encryption key.
   - Use `crypto_secretstream_xchacha20poly1305_init_pull` to create the
     server->client libsodium pull secretstream using the server->client
     encryption key and header.
4. Send a ClientHello2 message.

### Server initialisation sequence

The following handshake is performed in order to set up two libsodium
secretstreams, one for server->client communication (a push stream) and one for
client->server communication (a pull stream).

1. Generate a keypair using `crypto_kx_keypair`. These keys are referred to as
   the server public and secret keys.
2. Receive a ClientHello message.
   - Check that the magic is correct for this version of the transport protocol.
   - Use `crypto_kx_server_session_keys` to compute the client->server and
     server->client symmetric keys.
   - Use `crypto_secretstream_xchacha20poly1305_init_push` to create the
     server->client libsodium push secretstream using the server->client
     encryption key.
3. Send a ServerHello message.
4. Receive a ClientHello2 message.
   - Check that the magic is correct for this version of the transport protocol.
   - Use `crypto_secretstream_xchacha20poly1305_init_pull` to create the
     client->server libsodium pull secretstream using the client->server
     encryption key and header.

## Data exchange

At this point, we can reconcile the two halves of the protocol: both parties now
have a push secretstream for transmission to the other party, and a pull
secretstream for reception from the other party.

### Receiving a message

At all times, the current party can receive a DataMessage message. To handle the
message, use `crypto_secretstream_xchacha20poly1305_pull` to decrypt the
encrypted data in the message, and observe the tag.
- If the tag is 0:
  - Return the decrypted data to the application.
- If the tag is `crypto_secretstream_xchacha20poly1305_TAG_FINAL`:
  - Close the socket and consider the transport closed. Report this to the
    application.

### Sending a message

When the current party wants to send a message, use
`crypto_secretstream_xchacha20poly1305_push`, with no additional data and
`tag` = 0, to encrypt the message in the push secretstream. Then send the
encrypted result in a DataMessage message to the other party.

### Ending the transport

When the current party wants to end the transport and close the socket, one has
a choice between a graceful shutdown and an improper shutdown (just closing the
socket and forgetting about the connection). Sometimes an improper shutdown
cannot be avoided, e.g. if the application is killed by the operating system,
but a graceful shutdown is always preferred.

To perform a graceful shutdown, use `crypto_secretstream_xchacha20poly1305_push`
on the push secretstream to encrypt the empty string with no additional data and
`tag` = `crypto_secretstream_xchacha20poly1305_TAG_FINAL`. Then send the
encrypted result in a DataMessage message to the other party.

After the DataMessage message is successfully sent, the socket can be closed and
the transport considered ended.


# SSH notes

- Use libssh, not libssh2
- `ssh_message.type` has type `enum ssh_requests_e`, not `int`