/*
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
}
// 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
}
}
// 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);
}
// 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)
}
// 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");
uint32_t seqno;
memcpy(&seqno, data, 4);
seqno = ntohl(seqno);
+ data += 4; len -= 4;
if(!s->instate) {
if(seqno != s->inseqno)
s->inseqno = seqno + 1;
- uint8_t type = data[4];
+ uint8_t type = *(data++); len--;
if(type != SPTPS_HANDSHAKE)
return error(s, EIO, "Application record received before handshake finished");
- return receive_handshake(s, data + 5, len - 5);
+ return receive_handshake(s, data, len);
}
// Decrypt
char buffer[len];
-
size_t outlen;
-
- if(!chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen))
+ if(!chacha_poly1305_decrypt(s->incipher, seqno, data, len, buffer, &outlen))
return error(s, EIO, "Failed to decrypt and verify packet");
if(!sptps_check_seqno(s, seqno, true))
return false;
// Append a NULL byte for safety.
- buffer[len - 20] = 0;
+ buffer[outlen] = 0;
+
+ data = buffer;
+ len = outlen;
- uint8_t type = buffer[0];
+ uint8_t type = *(data++); len--;
if(type < SPTPS_HANDSHAKE) {
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();
+ if(!s->receive_record(s->handle, type, data, len))
+ return false;
} else if(type == SPTPS_HANDSHAKE) {
- if(!receive_handshake(s, buffer + 1, len - 21))
- abort();
+ if(!receive_handshake(s, data, len))
+ return false;
} else {
return error(s, EIO, "Invalid record type %d", type);
}
}
// 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) {
+size_t sptps_receive_data(sptps_t *s, const void *data, size_t len) {
+ size_t total_read = 0;
+
if(!s->state)
return error(s, EIO, "Invalid session state zero");
if(s->datagram)
- return sptps_receive_data_datagram(s, data, len);
+ return sptps_receive_data_datagram(s, data, len) ? len : false;
- while(len) {
- // First read the 2 length bytes.
- if(s->buflen < 2) {
- size_t toread = 2 - s->buflen;
- if(toread > len)
- toread = len;
+ // First read the 2 length bytes.
+ if(s->buflen < 2) {
+ size_t toread = 2 - s->buflen;
+ if(toread > len)
+ toread = len;
- memcpy(s->inbuf + s->buflen, data, toread);
+ memcpy(s->inbuf + s->buflen, data, toread);
- s->buflen += toread;
- len -= toread;
- data += toread;
+ total_read += toread;
+ s->buflen += toread;
+ len -= toread;
+ data += toread;
- // Exit early if we don't have the full length.
- if(s->buflen < 2)
- return true;
+ // Exit early if we don't have the full length.
+ if(s->buflen < 2)
+ return total_read;
- // Get the length bytes
+ // Get the length bytes
- memcpy(&s->reclen, s->inbuf, 2);
- s->reclen = ntohs(s->reclen);
+ memcpy(&s->reclen, s->inbuf, 2);
+ s->reclen = ntohs(s->reclen);
- // If we have the length bytes, ensure our buffer can hold the whole request.
- s->inbuf = realloc(s->inbuf, s->reclen + 19UL);
- if(!s->inbuf)
- return error(s, errno, strerror(errno));
+ // If we have the length bytes, ensure our buffer can hold the whole request.
+ s->inbuf = realloc(s->inbuf, s->reclen + 19UL);
+ if(!s->inbuf)
+ return error(s, errno, strerror(errno));
- // Exit early if we have no more data to process.
- if(!len)
- return true;
- }
+ // Exit early if we have no more data to process.
+ if(!len)
+ return total_read;
+ }
- // Read up to the end of the record.
- size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen;
- if(toread > len)
- toread = len;
+ // Read up to the end of the record.
+ size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen;
+ if(toread > len)
+ toread = len;
- memcpy(s->inbuf + s->buflen, data, toread);
- s->buflen += toread;
- len -= toread;
- data += toread;
+ memcpy(s->inbuf + s->buflen, data, toread);
+ total_read += toread;
+ s->buflen += toread;
+ len -= toread;
+ data += toread;
- // If we don't have a whole record, exit.
- if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL))
- return true;
+ // If we don't have a whole record, exit.
+ if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL))
+ return total_read;
- // Update sequence number.
+ // Update sequence number.
- uint32_t seqno = s->inseqno++;
+ uint32_t seqno = s->inseqno++;
- // Check HMAC and decrypt.
- if(s->instate) {
- if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL))
- return error(s, EINVAL, "Failed to decrypt and verify record");
- }
-
- // Append a NULL byte for safety.
- s->inbuf[s->reclen + 3UL] = 0;
+ // Check HMAC and decrypt.
+ if(s->instate) {
+ if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL))
+ return error(s, EINVAL, "Failed to decrypt and verify record");
+ }
- uint8_t type = s->inbuf[2];
+ // Append a NULL byte for safety.
+ s->inbuf[s->reclen + 3UL] = 0;
- if(type < SPTPS_HANDSHAKE) {
- if(!s->instate)
- return error(s, EIO, "Application record received before handshake finished");
- if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen))
- return false;
- } else if(type == SPTPS_HANDSHAKE) {
- if(!receive_handshake(s, s->inbuf + 3, s->reclen))
- return false;
- } else {
- return error(s, EIO, "Invalid record type %d", type);
- }
+ uint8_t type = s->inbuf[2];
- s->buflen = 0;
+ if(type < SPTPS_HANDSHAKE) {
+ if(!s->instate)
+ return error(s, EIO, "Application record received before handshake finished");
+ if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen))
+ return false;
+ } else if(type == SPTPS_HANDSHAKE) {
+ if(!receive_handshake(s, s->inbuf + 3, s->reclen))
+ return false;
+ } else {
+ return error(s, EIO, "Invalid record type %d", type);
}
- return true;
+ s->buflen = 0;
+
+ return total_read;
}
// 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);