2 sptps.c -- Simple Peer-to-Peer Security
3 Copyright (C) 2011 Guus Sliepen <guus@tinc-vpn.org>,
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 Nonce MUST be exchanged first (done)
32 Signatures MUST be done over both nonces, to guarantee the signature is fresh
33 Otherwise: if ECDHE key of one side is compromised, it can be reused!
35 Add explicit tag to beginning of structure to distinguish the client and server when signing. (done)
37 Sign all handshake messages up to ECDHE kex with long-term public keys. (done)
39 HMACed KEX finished message to prevent downgrade attacks and prove you have the right key material (done by virtue of ECDSA over the whole ECDHE exchange?)
41 Explicit close message needs to be added.
43 Maybe do add some alert messages to give helpful error messages? Not more than TLS sends.
45 Use counter mode instead of OFB. (done)
47 Make sure ECC operations are fixed time (aka prevent side-channel attacks).
50 // Log an error message.
51 static bool error(sptps_t *s, int s_errno, const char *msg) {
52 fprintf(stderr, "SPTPS error: %s\n", msg);
57 // Send a record (private version, accepts all record types, handles encryption and authentication).
58 static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
59 char buffer[len + 23UL];
61 // Create header with sequence number, length and record type
62 uint32_t seqno = htonl(s->outseqno++);
63 uint16_t netlen = htons(len);
65 memcpy(buffer, &seqno, 4);
66 memcpy(buffer + 4, &netlen, 2);
69 // Add plaintext (TODO: avoid unnecessary copy)
70 memcpy(buffer + 7, data, len);
73 // If first handshake has finished, encrypt and HMAC
74 if(!cipher_counter_xor(&s->outcipher, buffer + 4, len + 3UL, buffer + 4))
77 if(!digest_create(&s->outdigest, buffer, len + 7UL, buffer + 7UL + len))
80 return s->send_data(s->handle, buffer + 4, len + 19UL);
82 // Otherwise send as plaintext
83 return s->send_data(s->handle, buffer + 4, len + 3UL);
87 // Send an application record.
88 bool send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
89 // Sanity checks: application cannot send data before handshake is finished,
90 // and only record types 0..127 are allowed.
92 return error(s, EINVAL, "Handshake phase not finished yet");
94 if(type >= SPTPS_HANDSHAKE)
95 return error(s, EINVAL, "Invalid application record type");
97 return send_record_priv(s, type, data, len);
100 // Send a Key EXchange record, containing a random nonce and an ECDHE public key.
101 static bool send_kex(sptps_t *s) {
102 size_t keylen = ECDH_SIZE;
104 // Make room for our KEX message, which we will keep around since send_sig() needs it.
105 s->mykex = realloc(s->mykex, 1 + 32 + keylen);
107 return error(s, errno, strerror(errno));
109 // Set version byte to zero.
110 s->mykex[0] = SPTPS_VERSION;
112 // Create a random nonce.
113 randomize(s->mykex + 1, 32);
115 // Create a new ECDH public key.
116 if(!ecdh_generate_public(&s->ecdh, s->mykex + 1 + 32))
119 return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
122 // Send a SIGnature record, containing an ECDSA signature over both KEX records.
123 static bool send_sig(sptps_t *s) {
124 size_t keylen = ECDH_SIZE;
125 size_t siglen = ecdsa_size(&s->mykey);
127 // Concatenate both KEX messages, plus tag indicating if it is from the connection originator
128 char msg[(1 + 32 + keylen) * 2 + 1];
131 msg[0] = s->initiator;
132 memcpy(msg + 1, s->mykex, 1 + 32 + keylen);
133 memcpy(msg + 2 + 32 + keylen, s->hiskex, 1 + 32 + keylen);
136 if(!ecdsa_sign(&s->mykey, msg, sizeof msg, sig))
139 // Send the SIG exchange record.
140 return send_record_priv(s, SPTPS_HANDSHAKE, sig, sizeof sig);
143 // Generate key material from the shared secret created from the ECDHE key exchange.
144 static bool generate_key_material(sptps_t *s, const char *shared, size_t len) {
145 // Initialise cipher and digest structures if necessary
148 = cipher_open_by_name(&s->incipher, "aes-256-ecb")
149 && cipher_open_by_name(&s->outcipher, "aes-256-ecb")
150 && digest_open_by_name(&s->indigest, "sha256", 16)
151 && digest_open_by_name(&s->outdigest, "sha256", 16);
156 // Allocate memory for key material
157 size_t keylen = digest_keylength(&s->indigest) + digest_keylength(&s->outdigest) + cipher_keylength(&s->incipher) + cipher_keylength(&s->outcipher);
159 s->key = realloc(s->key, keylen);
161 return error(s, errno, strerror(errno));
163 // Create the HMAC seed, which is "key expansion" + session label + server nonce + client nonce
164 char seed[s->labellen + 64 + 13];
165 strcpy(seed, "key expansion");
167 memcpy(seed + 13, s->mykex + 1, 32);
168 memcpy(seed + 45, s->hiskex + 1, 32);
170 memcpy(seed + 13, s->hiskex + 1, 32);
171 memcpy(seed + 45, s->mykex + 1, 32);
173 memcpy(seed + 78, s->label, s->labellen);
175 // Use PRF to generate the key material
176 if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen))
182 // Send an ACKnowledgement record.
183 static bool send_ack(sptps_t *s) {
184 return send_record_priv(s, SPTPS_HANDSHAKE, "", 0);
187 // Receive an ACKnowledgement record.
188 static bool receive_ack(sptps_t *s, const char *data, uint16_t len) {
190 return error(s, EIO, "Invalid ACK record length");
194 = cipher_set_counter_key(&s->incipher, s->key)
195 && digest_set_key(&s->indigest, s->key + cipher_keylength(&s->incipher), digest_keylength(&s->indigest));
200 = cipher_set_counter_key(&s->incipher, s->key + cipher_keylength(&s->outcipher) + digest_keylength(&s->outdigest))
201 && digest_set_key(&s->indigest, s->key + cipher_keylength(&s->outcipher) + digest_keylength(&s->outdigest) + cipher_keylength(&s->incipher), digest_keylength(&s->indigest));
213 // Receive a Key EXchange record, respond by sending a SIG record.
214 static bool receive_kex(sptps_t *s, const char *data, uint16_t len) {
215 // Verify length of the HELLO record
216 if(len != 1 + 32 + ECDH_SIZE)
217 return error(s, EIO, "Invalid KEX record length");
219 // Ignore version number for now.
221 // Make a copy of the KEX message, send_sig() and receive_sig() need it
222 s->hiskex = realloc(s->hiskex, len);
224 return error(s, errno, strerror(errno));
226 memcpy(s->hiskex, data, len);
231 // Receive a SIGnature record, verify it, if it passed, compute the shared secret and calculate the session keys.
232 static bool receive_sig(sptps_t *s, const char *data, uint16_t len) {
233 size_t keylen = ECDH_SIZE;
234 size_t siglen = ecdsa_size(&s->hiskey);
236 // Verify length of KEX record.
238 return error(s, EIO, "Invalid KEX record length");
240 // Concatenate both KEX messages, plus tag indicating if it is from the connection originator
241 char msg[(1 + 32 + keylen) * 2 + 1];
243 msg[0] = !s->initiator;
244 memcpy(msg + 1, s->hiskex, 1 + 32 + keylen);
245 memcpy(msg + 2 + 32 + keylen, s->mykex, 1 + 32 + keylen);
248 if(!ecdsa_verify(&s->hiskey, msg, sizeof msg, data))
251 // Compute shared secret.
252 char shared[ECDH_SHARED_SIZE];
253 if(!ecdh_compute_shared(&s->ecdh, s->hiskex + 1 + 32, shared))
256 // Generate key material from shared secret.
257 if(!generate_key_material(s, shared, sizeof shared))
266 // Send cipher change record
270 // TODO: only set new keys after ACK has been set/received
273 = cipher_set_counter_key(&s->outcipher, s->key + cipher_keylength(&s->incipher) + digest_keylength(&s->indigest))
274 && digest_set_key(&s->outdigest, s->key + cipher_keylength(&s->incipher) + digest_keylength(&s->indigest) + cipher_keylength(&s->outcipher), digest_keylength(&s->outdigest));
279 = cipher_set_counter_key(&s->outcipher, s->key)
280 && digest_set_key(&s->outdigest, s->key + cipher_keylength(&s->outcipher), digest_keylength(&s->outdigest));
290 // Force another Key EXchange (for testing purposes).
291 bool force_kex(sptps_t *s) {
292 if(!s->outstate || s->state != SPTPS_SECONDARY_KEX)
293 return error(s, EINVAL, "Cannot force KEX in current state");
295 s->state = SPTPS_KEX;
299 // Receive a handshake record.
300 static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
301 // Only a few states to deal with handshaking.
302 fprintf(stderr, "Received handshake message, current state %d\n", s->state);
304 case SPTPS_SECONDARY_KEX:
305 // We receive a secondary KEX request, first respond by sending our own.
309 // We have sent our KEX request, we expect our peer to sent one as well.
310 if(!receive_kex(s, data, len))
312 s->state = SPTPS_SIG;
315 // If we already sent our secondary public ECDH key, we expect the peer to send his.
316 if(!receive_sig(s, data, len))
318 // s->state = SPTPS_ACK;
319 s->state = SPTPS_ACK;
322 // We expect a handshake message to indicate transition to the new keys.
323 if(!receive_ack(s, data, len))
325 s->receive_record(s->handle, SPTPS_HANDSHAKE, NULL, 0);
326 s->state = SPTPS_SECONDARY_KEX;
328 // TODO: split ACK into a VERify and ACK?
330 return error(s, EIO, "Invalid session state");
334 // Receive incoming data. Check if it contains a complete record, if so, handle it.
335 bool receive_data(sptps_t *s, const char *data, size_t len) {
337 // First read the 2 length bytes.
339 size_t toread = 6 - s->buflen;
343 memcpy(s->inbuf + s->buflen, data, toread);
349 // Exit early if we don't have the full length.
353 // Decrypt the length bytes
356 if(!cipher_counter_xor(&s->incipher, s->inbuf + 4, 2, &s->reclen))
359 memcpy(&s->reclen, s->inbuf + 4, 2);
362 s->reclen = ntohs(s->reclen);
364 // If we have the length bytes, ensure our buffer can hold the whole request.
365 s->inbuf = realloc(s->inbuf, s->reclen + 23UL);
367 return error(s, errno, strerror(errno));
369 // Add sequence number.
370 uint32_t seqno = htonl(s->inseqno++);
371 memcpy(s->inbuf, &seqno, 4);
373 // Exit early if we have no more data to process.
378 // Read up to the end of the record.
379 size_t toread = s->reclen + (s->instate ? 23UL : 7UL) - s->buflen;
383 memcpy(s->inbuf + s->buflen, data, toread);
388 // If we don't have a whole record, exit.
389 if(s->buflen < s->reclen + (s->instate ? 23UL : 7UL))
392 // Check HMAC and decrypt.
394 if(!digest_verify(&s->indigest, s->inbuf, s->reclen + 7UL, s->inbuf + s->reclen + 7UL))
395 return error(s, EIO, "Invalid HMAC");
397 if(!cipher_counter_xor(&s->incipher, s->inbuf + 6UL, s->reclen + 1UL, s->inbuf + 6UL))
401 // Append a NULL byte for safety.
402 s->inbuf[s->reclen + 7UL] = 0;
404 uint8_t type = s->inbuf[6];
406 if(type < SPTPS_HANDSHAKE) {
408 return error(s, EIO, "Application record received before handshake finished");
409 if(!s->receive_record(s->handle, type, s->inbuf + 7, s->reclen))
411 } else if(type == SPTPS_HANDSHAKE) {
412 if(!receive_handshake(s, s->inbuf + 7, s->reclen))
415 return error(s, EIO, "Invalid record type");
424 // Start a SPTPS session.
425 bool start_sptps(sptps_t *s, void *handle, bool initiator, ecdsa_t mykey, ecdsa_t hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
426 // Initialise struct sptps
427 memset(s, 0, sizeof *s);
430 s->initiator = initiator;
434 s->label = malloc(labellen);
436 return error(s, errno, strerror(errno));
438 s->inbuf = malloc(7);
440 return error(s, errno, strerror(errno));
442 memset(s->inbuf, 0, 4);
444 memcpy(s->label, label, labellen);
445 s->labellen = labellen;
447 s->send_data = send_data;
448 s->receive_record = receive_record;
450 // Do first KEX immediately
451 s->state = SPTPS_KEX;
455 // Stop a SPTPS session.
456 bool stop_sptps(sptps_t *s) {
457 // Clean up any resources.