X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=src%2Fsptps.c;h=e03b18a8b12e498182ee16b04deb37f337d90be1;hb=refs%2Fheads%2Fchannels;hp=370883fd0715f579bd4edec4a8c76a44b374648d;hpb=a86faaf34711d6b0f278b670d70a229a3cf0d479;p=meshlink diff --git a/src/sptps.c b/src/sptps.c index 370883fd..e03b18a8 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -1,7 +1,6 @@ /* sptps.c -- Simple Peer-to-Peer Security - Copyright (C) 2011-2013 Guus Sliepen , - 2010 Brandon L. Black + Copyright (C) 2014 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +19,7 @@ #include "system.h" -#include "cipher.h" +#include "chacha-poly1305/chacha-poly1305.h" #include "crypto.h" #include "ecdh.h" #include "ecdsa.h" @@ -81,68 +80,53 @@ 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 - uint32_t seqno = htonl(s->outseqno++); + uint32_t seqno = s->outseqno++; + uint32_t netseqno = ntohl(seqno); - memcpy(buffer, &seqno, 4); + memcpy(buffer, &netseqno, 4); buffer[4] = type; + memcpy(buffer + 5, data, len); if(s->outstate) { // If first handshake has finished, encrypt and HMAC - if(!cipher_set_counter(s->outcipher, &seqno, sizeof seqno)) - return error(s, EINVAL, "Failed to set counter"); - - if(!cipher_gcm_encrypt_start(s->outcipher, buffer + 4, 1, buffer + 4, NULL)) - return error(s, EINVAL, "Error encrypting record"); - - if(!cipher_gcm_encrypt_finish(s->outcipher, data, len, buffer + 5, NULL)) - return error(s, EINVAL, "Error encrypting record"); - + chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 4, len + 1, buffer + 4, NULL); return s->send_data(s->handle, type, buffer, len + 21UL); } else { // Otherwise send as plaintext - memcpy(buffer + 5, data, len); return s->send_data(s->handle, type, buffer, len + 5UL); } } // 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); char buffer[len + 19UL]; // Create header with sequence number, length and record type - uint32_t seqno = htonl(s->outseqno++); + uint32_t seqno = s->outseqno++; uint16_t netlen = htons(len); memcpy(buffer, &netlen, 2); buffer[2] = type; + memcpy(buffer + 3, data, len); if(s->outstate) { // If first handshake has finished, encrypt and HMAC - if(!cipher_set_counter(s->outcipher, &seqno, 4)) - return error(s, EINVAL, "Failed to set counter"); - - if(!cipher_gcm_encrypt_start(s->outcipher, buffer, 3, buffer, NULL)) - return error(s, EINVAL, "Error encrypting record"); - - if(!cipher_gcm_encrypt_finish(s->outcipher, data, len, buffer + 3, NULL)) - return error(s, EINVAL, "Error encrypting record"); - + chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 2, len + 1, buffer + 2, NULL); return s->send_data(s->handle, type, buffer, len + 19UL); } else { // Otherwise send as plaintext - memcpy(buffer + 3, data, len); return s->send_data(s->handle, type, buffer, len + 3UL); } } // 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) @@ -204,14 +188,14 @@ static bool send_sig(sptps_t *s) { static bool generate_key_material(sptps_t *s, const char *shared, size_t len) { // Initialise cipher and digest structures if necessary if(!s->outstate) { - s->incipher = cipher_open_by_name("aes-256-gcm"); - s->outcipher = cipher_open_by_name("aes-256-gcm"); + s->incipher = chacha_poly1305_init(); + s->outcipher = chacha_poly1305_init(); if(!s->incipher || !s->outcipher) return error(s, EINVAL, "Failed to open cipher"); } // Allocate memory for key material - size_t keylen = cipher_keylength(s->incipher) + cipher_keylength(s->outcipher); + size_t keylen = 2 * CHACHA_POLY1305_KEYLEN; s->key = realloc(s->key, keylen); if(!s->key) @@ -247,10 +231,10 @@ static bool receive_ack(sptps_t *s, const char *data, uint16_t len) { return error(s, EIO, "Invalid ACK record length"); if(s->initiator) { - if(!cipher_set_counter_key(s->incipher, s->key)) + if(!chacha_poly1305_set_key(s->incipher, s->key)) return error(s, EINVAL, "Failed to set counter"); } else { - if(!cipher_set_counter_key(s->incipher, s->key + cipher_keylength(s->outcipher))) + if(!chacha_poly1305_set_key(s->incipher, s->key + CHACHA_POLY1305_KEYLEN)) return error(s, EINVAL, "Failed to set counter"); } @@ -324,11 +308,11 @@ static bool receive_sig(sptps_t *s, const char *data, uint16_t len) { // TODO: only set new keys after ACK has been set/received if(s->initiator) { - if(!cipher_set_counter_key(s->outcipher, s->key + cipher_keylength(s->incipher))) - return error(s, EINVAL, "Failed to set counter"); + if(!chacha_poly1305_set_key(s->outcipher, s->key + CHACHA_POLY1305_KEYLEN)) + return error(s, EINVAL, "Failed to set key"); } else { - if(!cipher_set_counter_key(s->outcipher, s->key)) - return error(s, EINVAL, "Failed to set counter"); + if(!chacha_poly1305_set_key(s->outcipher, s->key)) + return error(s, EINVAL, "Failed to set key"); } return true; @@ -386,7 +370,7 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) { } // 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"); @@ -396,7 +380,9 @@ bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) { } // Receive incoming data, datagram version. -static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len) { +static bool sptps_receive_data_datagram(sptps_t *s, const void *vdata, size_t len) { + const char *data = vdata; + if(len < (s->instate ? 21 : 5)) return error(s, EIO, "Received short packet"); @@ -422,11 +408,9 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len char buffer[len]; - if(!cipher_set_counter(s->incipher, data, sizeof seqno)) - return error(s, EINVAL, "Failed to set counter"); size_t outlen; - if(!cipher_gcm_decrypt(s->incipher, data + 4, len - 4, buffer, &outlen)) + 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. @@ -437,11 +421,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len 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. + // TODO: Prevent packets that jump far ahead of the queue from causing many others to be dropped. 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); @@ -458,7 +438,6 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len // Mark the current packet as not being late. s->late[(seqno / 8) % s->replaywin] &= ~(1 << seqno % 8); - s->farfuture = 0; } if(seqno >= s->inseqno) @@ -490,7 +469,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"); @@ -514,22 +493,9 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { if(s->buflen < 2) return true; - // Update sequence number. - - uint32_t seqno = htonl(s->inseqno++); - - // Decrypt the length bytes - - if(s->instate) { - if(!cipher_set_counter(s->incipher, &seqno, 4)) - return error(s, EINVAL, "Failed to set counter"); - - if(!cipher_gcm_decrypt_start(s->incipher, s->inbuf, 2, &s->reclen, NULL)) - return error(s, EINVAL, "Failed to decrypt record"); - } else { - memcpy(&s->reclen, s->inbuf, 2); - } + // Get the length bytes + 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. @@ -556,9 +522,13 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) return true; + // Update sequence number. + + uint32_t seqno = s->inseqno++; + // Check HMAC and decrypt. if(s->instate) { - if(!cipher_gcm_decrypt_finish(s->incipher, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) + 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"); } @@ -628,10 +598,8 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_ // Stop a SPTPS session. bool sptps_stop(sptps_t *s) { // Clean up any resources. - cipher_close(s->incipher); - cipher_close(s->outcipher); - digest_close(s->indigest); - digest_close(s->outdigest); + chacha_poly1305_exit(s->incipher); + chacha_poly1305_exit(s->outcipher); ecdh_free(s->ecdh); free(s->inbuf); free(s->mykex);