]> git.meshlink.io Git - meshlink/blob - src/sptps.c
Fix compiling with -Wall -W.
[meshlink] / src / sptps.c
1 /*
2     sptps.c -- Simple Peer-to-Peer Security
3     Copyright (C) 2014-2017 Guus Sliepen <guus@meshlink.io>
4
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.
9
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.
14
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.
18 */
19
20 #include "system.h"
21
22 #include "chacha-poly1305/chacha-poly1305.h"
23 #include "crypto.h"
24 #include "ecdh.h"
25 #include "ecdsa.h"
26 #include "logger.h"
27 #include "prf.h"
28 #include "sptps.h"
29
30 unsigned int sptps_replaywin = 32;
31
32 /*
33    Nonce MUST be exchanged first (done)
34    Signatures MUST be done over both nonces, to guarantee the signature is fresh
35    Otherwise: if ECDHE key of one side is compromised, it can be reused!
36
37    Add explicit tag to beginning of structure to distinguish the client and server when signing. (done)
38
39    Sign all handshake messages up to ECDHE kex with long-term public keys. (done)
40
41    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?)
42
43    Explicit close message needs to be added.
44
45    Maybe do add some alert messages to give helpful error messages? Not more than TLS sends.
46
47    Use counter mode instead of OFB. (done)
48
49    Make sure ECC operations are fixed time (aka prevent side-channel attacks).
50 */
51
52 void sptps_log_quiet(sptps_t *s, int s_errno, const char *format, va_list ap) {
53         (void)s;
54         (void)s_errno;
55         (void)format;
56         (void)ap;
57 }
58
59 void sptps_log_stderr(sptps_t *s, int s_errno, const char *format, va_list ap) {
60         (void)s;
61         (void)s_errno;
62         vfprintf(stderr, format, ap);
63         fputc('\n', stderr);
64 }
65
66 void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap) = sptps_log_stderr;
67
68 // Log an error message.
69 static bool error(sptps_t *s, int s_errno, const char *format, ...) {
70         if(format) {
71                 va_list ap;
72                 va_start(ap, format);
73                 sptps_log(s, s_errno, format, ap);
74                 va_end(ap);
75         }
76
77         errno = s_errno;
78         return false;
79 }
80
81 static void warning(sptps_t *s, const char *format, ...) {
82         va_list ap;
83         va_start(ap, format);
84         sptps_log(s, 0, format, ap);
85         va_end(ap);
86 }
87
88 // Send a record (datagram version, accepts all record types, handles encryption and authentication).
89 static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
90         char buffer[len + 21UL];
91
92         // Create header with sequence number, length and record type
93         uint32_t seqno = s->outseqno++;
94         uint32_t netseqno = ntohl(seqno);
95
96         memcpy(buffer, &netseqno, 4);
97         buffer[4] = type;
98         memcpy(buffer + 5, data, len);
99
100         if(s->outstate) {
101                 // If first handshake has finished, encrypt and HMAC
102                 chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 4, len + 1, buffer + 4, NULL);
103                 return s->send_data(s->handle, type, buffer, len + 21UL);
104         } else {
105                 // Otherwise send as plaintext
106                 return s->send_data(s->handle, type, buffer, len + 5UL);
107         }
108 }
109 // Send a record (private version, accepts all record types, handles encryption and authentication).
110 static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
111         if(s->datagram)
112                 return send_record_priv_datagram(s, type, data, len);
113
114         char buffer[len + 19UL];
115
116         // Create header with sequence number, length and record type
117         uint32_t seqno = s->outseqno++;
118         uint16_t netlen = htons(len);
119
120         memcpy(buffer, &netlen, 2);
121         buffer[2] = type;
122         memcpy(buffer + 3, data, len);
123
124         if(s->outstate) {
125                 // If first handshake has finished, encrypt and HMAC
126                 chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 2, len + 1, buffer + 2, NULL);
127                 return s->send_data(s->handle, type, buffer, len + 19UL);
128         } else {
129                 // Otherwise send as plaintext
130                 return s->send_data(s->handle, type, buffer, len + 3UL);
131         }
132 }
133
134 // Send an application record.
135 bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
136         // Sanity checks: application cannot send data before handshake is finished,
137         // and only record types 0..127 are allowed.
138         if(!s->outstate)
139                 return error(s, EINVAL, "Handshake phase not finished yet");
140
141         if(type >= SPTPS_HANDSHAKE)
142                 return error(s, EINVAL, "Invalid application record type");
143
144         return send_record_priv(s, type, data, len);
145 }
146
147 // Send a Key EXchange record, containing a random nonce and an ECDHE public key.
148 static bool send_kex(sptps_t *s) {
149         size_t keylen = ECDH_SIZE;
150
151         // Make room for our KEX message, which we will keep around since send_sig() needs it.
152         if(s->mykex)
153                 return false;
154         s->mykex = realloc(s->mykex, 1 + 32 + keylen);
155         if(!s->mykex)
156                 return error(s, errno, strerror(errno));
157
158         // Set version byte to zero.
159         s->mykex[0] = SPTPS_VERSION;
160
161         // Create a random nonce.
162         randomize(s->mykex + 1, 32);
163
164         // Create a new ECDH public key.
165         if(!(s->ecdh = ecdh_generate_public(s->mykex + 1 + 32)))
166                 return error(s, EINVAL, "Failed to generate ECDH public key");
167
168         return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
169 }
170
171 // Send a SIGnature record, containing an ECDSA signature over both KEX records.
172 static bool send_sig(sptps_t *s) {
173         size_t keylen = ECDH_SIZE;
174         size_t siglen = ecdsa_size(s->mykey);
175
176         // Concatenate both KEX messages, plus tag indicating if it is from the connection originator, plus label
177         char msg[(1 + 32 + keylen) * 2 + 1 + s->labellen];
178         char sig[siglen];
179
180         msg[0] = s->initiator;
181         memcpy(msg + 1, s->mykex, 1 + 32 + keylen);
182         memcpy(msg + 1 + 33 + keylen, s->hiskex, 1 + 32 + keylen);
183         memcpy(msg + 1 + 2 * (33 + keylen), s->label, s->labellen);
184
185         // Sign the result.
186         if(!ecdsa_sign(s->mykey, msg, sizeof(msg), sig))
187                 return error(s, EINVAL, "Failed to sign SIG record");
188
189         // Send the SIG exchange record.
190         return send_record_priv(s, SPTPS_HANDSHAKE, sig, sizeof(sig));
191 }
192
193 // Generate key material from the shared secret created from the ECDHE key exchange.
194 static bool generate_key_material(sptps_t *s, const char *shared, size_t len) {
195         // Initialise cipher and digest structures if necessary
196         if(!s->outstate) {
197                 s->incipher = chacha_poly1305_init();
198                 s->outcipher = chacha_poly1305_init();
199                 if(!s->incipher || !s->outcipher)
200                         return error(s, EINVAL, "Failed to open cipher");
201         }
202
203         // Allocate memory for key material
204         size_t keylen = 2 * CHACHA_POLY1305_KEYLEN;
205
206         s->key = realloc(s->key, keylen);
207         if(!s->key)
208                 return error(s, errno, strerror(errno));
209
210         // Create the HMAC seed, which is "key expansion" + session label + server nonce + client nonce
211         char seed[s->labellen + 64 + 13];
212         strcpy(seed, "key expansion");
213         if(s->initiator) {
214                 memcpy(seed + 13, s->mykex + 1, 32);
215                 memcpy(seed + 45, s->hiskex + 1, 32);
216         } else {
217                 memcpy(seed + 13, s->hiskex + 1, 32);
218                 memcpy(seed + 45, s->mykex + 1, 32);
219         }
220         memcpy(seed + 77, s->label, s->labellen);
221
222         // Use PRF to generate the key material
223         if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen))
224                 return error(s, EINVAL, "Failed to generate key material");
225
226         return true;
227 }
228
229 // Send an ACKnowledgement record.
230 static bool send_ack(sptps_t *s) {
231         return send_record_priv(s, SPTPS_HANDSHAKE, "", 0);
232 }
233
234 // Receive an ACKnowledgement record.
235 static bool receive_ack(sptps_t *s, const char *data, uint16_t len) {
236         (void)data;
237         if(len)
238                 return error(s, EIO, "Invalid ACK record length");
239
240         if(s->initiator) {
241                 if(!chacha_poly1305_set_key(s->incipher, s->key))
242                         return error(s, EINVAL, "Failed to set counter");
243         } else {
244                 if(!chacha_poly1305_set_key(s->incipher, s->key + CHACHA_POLY1305_KEYLEN))
245                         return error(s, EINVAL, "Failed to set counter");
246         }
247
248         free(s->key);
249         s->key = NULL;
250         s->instate = true;
251
252         return true;
253 }
254
255 // Receive a Key EXchange record, respond by sending a SIG record.
256 static bool receive_kex(sptps_t *s, const char *data, uint16_t len) {
257         // Verify length of the HELLO record
258         if(len != 1 + 32 + ECDH_SIZE)
259                 return error(s, EIO, "Invalid KEX record length");
260
261         // Ignore version number for now.
262
263         // Make a copy of the KEX message, send_sig() and receive_sig() need it
264         if(s->hiskex)
265                 return error(s, EINVAL, "Received a second KEX message before first has been processed");
266         s->hiskex = realloc(s->hiskex, len);
267         if(!s->hiskex)
268                 return error(s, errno, strerror(errno));
269
270         memcpy(s->hiskex, data, len);
271
272         return send_sig(s);
273 }
274
275 // Receive a SIGnature record, verify it, if it passed, compute the shared secret and calculate the session keys.
276 static bool receive_sig(sptps_t *s, const char *data, uint16_t len) {
277         size_t keylen = ECDH_SIZE;
278         size_t siglen = ecdsa_size(s->hiskey);
279
280         // Verify length of KEX record.
281         if(len != siglen)
282                 return error(s, EIO, "Invalid KEX record length");
283
284         // Concatenate both KEX messages, plus tag indicating if it is from the connection originator
285         char msg[(1 + 32 + keylen) * 2 + 1 + s->labellen];
286
287         msg[0] = !s->initiator;
288         memcpy(msg + 1, s->hiskex, 1 + 32 + keylen);
289         memcpy(msg + 1 + 33 + keylen, s->mykex, 1 + 32 + keylen);
290         memcpy(msg + 1 + 2 * (33 + keylen), s->label, s->labellen);
291
292         // Verify signature.
293         if(!ecdsa_verify(s->hiskey, msg, sizeof(msg), data))
294                 return error(s, EIO, "Failed to verify SIG record");
295
296         // Compute shared secret.
297         char shared[ECDH_SHARED_SIZE];
298         if(!ecdh_compute_shared(s->ecdh, s->hiskex + 1 + 32, shared))
299                 return error(s, EINVAL, "Failed to compute ECDH shared secret");
300         s->ecdh = NULL;
301
302         // Generate key material from shared secret.
303         if(!generate_key_material(s, shared, sizeof(shared)))
304                 return false;
305
306         free(s->mykex);
307         free(s->hiskex);
308
309         s->mykex = NULL;
310         s->hiskex = NULL;
311
312         // Send cipher change record
313         if(s->outstate && !send_ack(s))
314                 return false;
315
316         // TODO: only set new keys after ACK has been set/received
317         if(s->initiator) {
318                 if(!chacha_poly1305_set_key(s->outcipher, s->key + CHACHA_POLY1305_KEYLEN))
319                         return error(s, EINVAL, "Failed to set key");
320         } else {
321                 if(!chacha_poly1305_set_key(s->outcipher, s->key))
322                         return error(s, EINVAL, "Failed to set key");
323         }
324
325         return true;
326 }
327
328 // Force another Key EXchange (for testing purposes).
329 bool sptps_force_kex(sptps_t *s) {
330         if(!s->outstate || s->state != SPTPS_SECONDARY_KEX)
331                 return error(s, EINVAL, "Cannot force KEX in current state");
332
333         s->state = SPTPS_KEX;
334         return send_kex(s);
335 }
336
337 // Receive a handshake record.
338 static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
339         // Only a few states to deal with handshaking.
340         switch(s->state) {
341         case SPTPS_SECONDARY_KEX:
342                 // We receive a secondary KEX request, first respond by sending our own.
343                 if(!send_kex(s))
344                         return false;
345                 // fallthrough
346         case SPTPS_KEX:
347                 // We have sent our KEX request, we expect our peer to sent one as well.
348                 if(!receive_kex(s, data, len))
349                         return false;
350                 s->state = SPTPS_SIG;
351                 return true;
352         case SPTPS_SIG:
353                 // If we already sent our secondary public ECDH key, we expect the peer to send his.
354                 if(!receive_sig(s, data, len))
355                         return false;
356                 if(s->outstate)
357                         s->state = SPTPS_ACK;
358                 else {
359                         s->outstate = true;
360                         if(!receive_ack(s, NULL, 0))
361                                 return false;
362                         s->receive_record(s->handle, SPTPS_HANDSHAKE, NULL, 0);
363                         s->state = SPTPS_SECONDARY_KEX;
364                 }
365
366                 return true;
367         case SPTPS_ACK:
368                 // We expect a handshake message to indicate transition to the new keys.
369                 if(!receive_ack(s, data, len))
370                         return false;
371                 s->receive_record(s->handle, SPTPS_HANDSHAKE, NULL, 0);
372                 s->state = SPTPS_SECONDARY_KEX;
373                 return true;
374         // TODO: split ACK into a VERify and ACK?
375         default:
376                 return error(s, EIO, "Invalid session state %d", s->state);
377         }
378 }
379
380 // Check datagram for valid HMAC
381 bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) {
382         if(!s->instate)
383                 return error(s, EIO, "SPTPS state not ready to verify this datagram");
384
385         if(len < 21)
386                 return error(s, EIO, "Received short packet in sptps_verify_datagram");
387
388         uint32_t seqno;
389         memcpy(&seqno, data, 4);
390         seqno = ntohl(seqno);
391         // TODO: check whether seqno makes sense, to avoid CPU intensive decrypt
392
393         char buffer[len];
394         size_t outlen;
395         return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
396 }
397
398 // Receive incoming data, datagram version.
399 static bool sptps_receive_data_datagram(sptps_t *s, const void *vdata, size_t len) {
400         const char *data = vdata;
401
402         if(len < (s->instate ? 21 : 5))
403                 return error(s, EIO, "Received short packet in sptps_receive_data_datagram");
404
405         uint32_t seqno;
406         memcpy(&seqno, data, 4);
407         seqno = ntohl(seqno);
408
409         if(!s->instate) {
410                 if(seqno != s->inseqno)
411                         return error(s, EIO, "Invalid packet seqno: %d != %d", seqno, s->inseqno);
412
413                 s->inseqno = seqno + 1;
414
415                 uint8_t type = data[4];
416
417                 if(type != SPTPS_HANDSHAKE)
418                         return error(s, EIO, "Application record received before handshake finished");
419
420                 return receive_handshake(s, data + 5, len - 5);
421         }
422
423         // Decrypt
424
425         char buffer[len];
426
427         size_t outlen;
428
429         if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen))
430                 return error(s, EIO, "Failed to decrypt and verify packet");
431
432         // Replay protection using a sliding window of configurable size.
433         // s->inseqno is expected sequence number
434         // seqno is received sequence number
435         // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet
436         // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno.
437         if(s->replaywin) {
438                 if(seqno != s->inseqno) {
439                         if(seqno >= s->inseqno + s->replaywin * 8) {
440                                 // TODO: Prevent packets that jump far ahead of the queue from causing many others to be dropped.
441                                 warning(s, "Lost %d packets\n", seqno - s->inseqno);
442                                 // Mark all packets in the replay window as being late.
443                                 memset(s->late, 255, s->replaywin);
444                         } else if(seqno < s->inseqno) {
445                                 // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it.
446                                 if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8)))
447                                         return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno);
448                         } else {
449                                 // We missed some packets. Mark them in the bitmap as being late.
450                                 for(uint32_t i = s->inseqno; i < seqno; i++)
451                                         s->late[(i / 8) % s->replaywin] |= 1 << i % 8;
452                         }
453                 }
454
455                 // Mark the current packet as not being late.
456                 s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
457         }
458
459         if(seqno >= s->inseqno)
460                 s->inseqno = seqno + 1;
461
462         if(!s->inseqno)
463                 s->received = 0;
464         else
465                 s->received++;
466
467         // Append a NULL byte for safety.
468         buffer[len - 20] = 0;
469
470         uint8_t type = buffer[0];
471
472         if(type < SPTPS_HANDSHAKE) {
473                 if(!s->instate)
474                         return error(s, EIO, "Application record received before handshake finished");
475                 if(!s->receive_record(s->handle, type, buffer + 1, len - 21))
476                         abort();
477         } else if(type == SPTPS_HANDSHAKE) {
478                 if(!receive_handshake(s, buffer + 1, len - 21))
479                         abort();
480         } else
481                 return error(s, EIO, "Invalid record type %d", type);
482
483         return true;
484 }
485
486 // Receive incoming data. Check if it contains a complete record, if so, handle it.
487 bool sptps_receive_data(sptps_t *s, const void *data, size_t len) {
488         if(!s->state)
489                 return error(s, EIO, "Invalid session state zero");
490
491         if(s->datagram)
492                 return sptps_receive_data_datagram(s, data, len);
493
494         while(len) {
495                 // First read the 2 length bytes.
496                 if(s->buflen < 2) {
497                         size_t toread = 2 - s->buflen;
498                         if(toread > len)
499                                 toread = len;
500
501                         memcpy(s->inbuf + s->buflen, data, toread);
502
503                         s->buflen += toread;
504                         len -= toread;
505                         data += toread;
506
507                         // Exit early if we don't have the full length.
508                         if(s->buflen < 2)
509                                 return true;
510
511                         // Get the length bytes
512
513                         memcpy(&s->reclen, s->inbuf, 2);
514                         s->reclen = ntohs(s->reclen);
515
516                         // If we have the length bytes, ensure our buffer can hold the whole request.
517                         s->inbuf = realloc(s->inbuf, s->reclen + 19UL);
518                         if(!s->inbuf)
519                                 return error(s, errno, strerror(errno));
520
521                         // Exit early if we have no more data to process.
522                         if(!len)
523                                 return true;
524                 }
525
526                 // Read up to the end of the record.
527                 size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen;
528                 if(toread > len)
529                         toread = len;
530
531                 memcpy(s->inbuf + s->buflen, data, toread);
532                 s->buflen += toread;
533                 len -= toread;
534                 data += toread;
535
536                 // If we don't have a whole record, exit.
537                 if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL))
538                         return true;
539
540                 // Update sequence number.
541
542                 uint32_t seqno = s->inseqno++;
543
544                 // Check HMAC and decrypt.
545                 if(s->instate) {
546                         if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL))
547                                 return error(s, EINVAL, "Failed to decrypt and verify record");
548                 }
549
550                 // Append a NULL byte for safety.
551                 s->inbuf[s->reclen + 3UL] = 0;
552
553                 uint8_t type = s->inbuf[2];
554
555                 if(type < SPTPS_HANDSHAKE) {
556                         if(!s->instate)
557                                 return error(s, EIO, "Application record received before handshake finished");
558                         if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen))
559                                 return false;
560                 } else if(type == SPTPS_HANDSHAKE) {
561                         if(!receive_handshake(s, s->inbuf + 3, s->reclen))
562                                 return false;
563                 } else
564                         return error(s, EIO, "Invalid record type %d", type);
565
566                 s->buflen = 0;
567         }
568
569         return true;
570 }
571
572 // Start a SPTPS session.
573 bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
574         if(!s || !mykey || !hiskey || !label || !labellen || !send_data || !receive_record)
575                 return error(s, EINVAL, "Invalid argument to sptps_start()");
576
577         // Initialise struct sptps
578         memset(s, 0, sizeof(*s));
579
580         s->handle = handle;
581         s->initiator = initiator;
582         s->datagram = datagram;
583         s->mykey = mykey;
584         s->hiskey = hiskey;
585         s->replaywin = sptps_replaywin;
586         if(s->replaywin) {
587                 s->late = malloc(s->replaywin);
588                 if(!s->late)
589                         return error(s, errno, strerror(errno));
590                 memset(s->late, 0, s->replaywin);
591         }
592
593         s->label = malloc(labellen);
594         if(!s->label)
595                 return error(s, errno, strerror(errno));
596
597         if(!datagram) {
598                 s->inbuf = malloc(7);
599                 if(!s->inbuf)
600                         return error(s, errno, strerror(errno));
601                 s->buflen = 0;
602         }
603
604         memcpy(s->label, label, labellen);
605         s->labellen = labellen;
606
607         s->send_data = send_data;
608         s->receive_record = receive_record;
609
610         // Do first KEX immediately
611         s->state = SPTPS_KEX;
612         return send_kex(s);
613 }
614
615 // Stop a SPTPS session.
616 bool sptps_stop(sptps_t *s) {
617         // Clean up any resources.
618         chacha_poly1305_exit(s->incipher);
619         chacha_poly1305_exit(s->outcipher);
620         ecdh_free(s->ecdh);
621         free(s->inbuf);
622         free(s->mykex);
623         free(s->hiskex);
624         free(s->key);
625         free(s->label);
626         free(s->late);
627         memset(s, 0, sizeof(*s));
628         return true;
629 }