Use the ChaCha-Poly1305 cipher for the SPTPS protocol.
The main reason to switch from AES-256-GCM to ChaCha-Poly1305 is to remove a dependency on OpenSSL, whose behaviour of the AES-256-GCM decryption function changes between versions. The source code for ChaCha-Pol1305 is small and in the public domain, and can therefore be easily included in tinc itself. Moreover, it is very fast even without using any optimized assembler, easily outperforming AES-256-GCM on platforms that don't have special AES instructions in hardware.
This commit is contained in:
parent
49e3baec20
commit
2980173ee7
9 changed files with 614 additions and 68 deletions
84
src/sptps.c
84
src/sptps.c
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "system.h"
|
||||
|
||||
#include "cipher.h"
|
||||
#include "chacha-poly1305/chacha-poly1305.h"
|
||||
#include "crypto.h"
|
||||
#include "ecdh.h"
|
||||
#include "ecdsa.h"
|
||||
|
|
@ -85,26 +85,19 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -116,27 +109,19 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -204,14 +189,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 +232,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 +309,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;
|
||||
|
|
@ -422,11 +407,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.
|
||||
|
|
@ -514,22 +497,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 +526,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 +602,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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue