Remove "release-" from displayed git version.
[tinc] / src / sptps.c
index 0989b14..4a9683f 100644 (file)
@@ -1,6 +1,6 @@
 /*
     sptps.c -- Simple Peer-to-Peer Security
-    Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>,
+    Copyright (C) 2011-2014 Guus Sliepen <guus@tinc-vpn.org>,
                   2010      Brandon L. Black <blblack@gmail.com>
 
     This program is free software; you can redistribute it and/or modify
@@ -39,7 +39,7 @@ unsigned int sptps_replaywin = 16;
 
    Sign all handshake messages up to ECDHE kex with long-term public keys. (done)
 
-   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?)
+   HMACed KEX finished message to prevent downgrade attacks and prove you have the right key material (done by virtue of Ed25519 over the whole ECDHE exchange?)
 
    Explicit close message needs to be added.
 
@@ -81,7 +81,7 @@ static void warning(sptps_t *s, const char *format, ...) {
 }
 
 // Send a record (datagram version, accepts all record types, handles encryption and authentication).
-static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
        char buffer[len + 21UL];
 
        // Create header with sequence number, length and record type
@@ -102,7 +102,7 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data
        }
 }
 // Send a record (private version, accepts all record types, handles encryption and authentication).
-static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
        if(s->datagram)
                return send_record_priv_datagram(s, type, data, len);
 
@@ -127,7 +127,7 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_
 }
 
 // Send an application record.
-bool sptps_send_record(sptps_t *s, uint8_t type, const char *data, uint16_t len) {
+bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len) {
        // Sanity checks: application cannot send data before handshake is finished,
        // and only record types 0..127 are allowed.
        if(!s->outstate)
@@ -163,7 +163,7 @@ static bool send_kex(sptps_t *s) {
        return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
 }
 
-// Send a SIGnature record, containing an ECDSA signature over both KEX records.
+// Send a SIGnature record, containing an Ed25519 signature over both KEX records.
 static bool send_sig(sptps_t *s) {
        size_t keylen = ECDH_SIZE;
        size_t siglen = ecdsa_size(s->mykey);
@@ -370,14 +370,73 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) {
        }
 }
 
+static bool sptps_check_seqno(sptps_t *s, uint32_t seqno, bool update_state) {
+       // Replay protection using a sliding window of configurable size.
+       // s->inseqno is expected sequence number
+       // seqno is received sequence number
+       // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet
+       // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno.
+       if(s->replaywin) {
+               if(seqno != s->inseqno) {
+                       if(seqno >= s->inseqno + s->replaywin * 8) {
+                               // Prevent packets that jump far ahead of the queue from causing many others to be dropped.
+                               bool farfuture = s->farfuture < s->replaywin >> 2;
+                               if (update_state)
+                                       s->farfuture++;
+                               if(farfuture)
+                                       return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture);
+
+                               // Unless we have seen lots of them, in which case we consider the others lost.
+                               warning(s, "Lost %d packets\n", seqno - s->inseqno);
+                               if (update_state) {
+                                       // Mark all packets in the replay window as being late.
+                                       memset(s->late, 255, s->replaywin);
+                               }
+                       } else if (seqno < s->inseqno) {
+                               // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it.
+                               if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8)))
+                                       return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno);
+                       } else if (update_state) {
+                               // We missed some packets. Mark them in the bitmap as being late.
+                               for(int i = s->inseqno; i < seqno; i++)
+                                       s->late[(i / 8) % s->replaywin] |= 1 << i % 8;
+                       }
+               }
+
+               if (update_state) {
+                       // Mark the current packet as not being late.
+                       s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
+                       s->farfuture = 0;
+               }
+       }
+
+       if (update_state) {
+               if(seqno >= s->inseqno)
+                       s->inseqno = seqno + 1;
+
+               if(!s->inseqno)
+                       s->received = 0;
+               else
+                       s->received++;
+       }
+
+       return true;
+}
+
 // Check datagram for valid HMAC
-bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) {
+bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len) {
        if(!s->instate || len < 21)
                return error(s, EIO, "Received short packet");
 
-       // TODO: just decrypt without updating the replay window
+       uint32_t seqno;
+       memcpy(&seqno, data, 4);
+       seqno = ntohl(seqno);
+       if (!sptps_check_seqno(s, seqno, false))
+               return false;
 
-       return true;
+       char buffer[len];
+       size_t outlen;
+       return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
 }
 
 // Receive incoming data, datagram version.
@@ -412,45 +471,8 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
        if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen))
                return error(s, EIO, "Failed to decrypt and verify packet");
 
-       // Replay protection using a sliding window of configurable size.
-       // s->inseqno is expected sequence number
-       // seqno is received sequence number
-       // s->late[] is a circular buffer, a 1 bit means a packet has not been received yet
-       // The circular buffer contains bits for sequence numbers from s->inseqno - s->replaywin * 8 to (but excluding) s->inseqno.
-       if(s->replaywin) {
-               if(seqno != s->inseqno) {
-                       if(seqno >= s->inseqno + s->replaywin * 8) {
-                               // Prevent packets that jump far ahead of the queue from causing many others to be dropped.
-                               if(s->farfuture++ < s->replaywin >> 2)
-                                       return error(s, EIO, "Packet is %d seqs in the future, dropped (%u)\n", seqno - s->inseqno, s->farfuture);
-
-                               // Unless we have seen lots of them, in which case we consider the others lost.
-                               warning(s, "Lost %d packets\n", seqno - s->inseqno);
-                               // Mark all packets in the replay window as being late.
-                               memset(s->late, 255, s->replaywin);
-                       } else if (seqno < s->inseqno) {
-                               // If the sequence number is farther in the past than the bitmap goes, or if the packet was already received, drop it.
-                               if((s->inseqno >= s->replaywin * 8 && seqno < s->inseqno - s->replaywin * 8) || !(s->late[(seqno / 8) % s->replaywin] & (1 << seqno % 8)))
-                                       return error(s, EIO, "Received late or replayed packet, seqno %d, last received %d\n", seqno, s->inseqno);
-                       } else {
-                               // We missed some packets. Mark them in the bitmap as being late.
-                               for(int i = s->inseqno; i < seqno; i++)
-                                       s->late[(i / 8) % s->replaywin] |= 1 << i % 8;
-                       }
-               }
-
-               // Mark the current packet as not being late.
-               s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8);
-               s->farfuture = 0;
-       }
-
-       if(seqno >= s->inseqno)
-               s->inseqno = seqno + 1;
-
-       if(!s->inseqno)
-               s->received = 0;
-       else
-               s->received++;
+       if(!sptps_check_seqno(s, seqno, true))
+               return false;
 
        // Append a NULL byte for safety.
        buffer[len - 20] = 0;
@@ -461,10 +483,10 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
                if(!s->instate)
                        return error(s, EIO, "Application record received before handshake finished");
                if(!s->receive_record(s->handle, type, buffer + 1, len - 21))
-                       abort();
+                       return false;
        } else if(type == SPTPS_HANDSHAKE) {
                if(!receive_handshake(s, buffer + 1, len - 21))
-                       abort();
+                       return false;
        } else {
                return error(s, EIO, "Invalid record type %d", type);
        }
@@ -473,7 +495,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len
 }
 
 // Receive incoming data. Check if it contains a complete record, if so, handle it.
-bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
+bool sptps_receive_data(sptps_t *s, const void *data, size_t len) {
        if(!s->state)
                return error(s, EIO, "Invalid session state zero");
 
@@ -560,7 +582,7 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) {
 }
 
 // Start a SPTPS session.
-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) {
+bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const void *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
        // Initialise struct sptps
        memset(s, 0, sizeof *s);