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