Add patches for alternative ciphersuite
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
lagertonne 2022-04-25 13:11:44 +02:00
parent 56d25e7bd6
commit d787d2479a
11 changed files with 3514 additions and 0 deletions

View file

@ -0,0 +1,879 @@
From cc521f3d5f3a0c758163c871e75f5e533e86771b Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Mon, 2 Aug 2021 23:53:13 +0200
Subject: [PATCH 01/10] Add AES-256-GCM support to SPTPS.
This also adds a simple cipher suite negotiation, where peers announce the
ciphers they support, and their preferred cipher. Since we need to bump the
SPTPS version anyway, also prefer little endian over network byte order.
---
doc/SPTPS | 45 +++++--
src/invitation.c | 11 +-
src/protocol_auth.c | 25 +++-
src/protocol_key.c | 30 ++++-
src/sptps.c | 310 +++++++++++++++++++++++++++++++++-----------
src/sptps.h | 44 ++++++-
src/sptps_test.c | 13 +-
7 files changed, 382 insertions(+), 96 deletions(-)
diff --git a/doc/SPTPS b/doc/SPTPS
index 2d8fee5b..2da27604 100644
--- a/doc/SPTPS
+++ b/doc/SPTPS
@@ -18,8 +18,8 @@ Stream record layer
A record consists of these fields:
-- uint32_t seqno (network byte order)
-- uint16_t length (network byte order)
+- uint32_t seqno (little endian)
+- uint16_t length (little endian)
- uint8_t type
- opaque data[length]
- opaque hmac[HMAC_SIZE] (HMAC over all preceding fields)
@@ -45,8 +45,8 @@ Datagram record layer
A record consists of these fields:
-- uint16_t length (network byte order)
-- uint32_t seqno (network byte order)
+- uint16_t length (little endian)
+- uint32_t seqno (little endian)
- uint8_t type
- opaque data[length]
- opaque hmac[HMAC_SIZE] (HMAC over all preceding fields)
@@ -75,7 +75,7 @@ SIG ->
...encrypt and HMAC using session keys from now on...
App ->
- <- App
+ <- App
...
...
@@ -91,7 +91,7 @@ ACK ->
...encrypt and HMAC using new session keys from now on...
App ->
- <- App
+ <- App
...
...
---------------------
@@ -102,7 +102,11 @@ connection.
Key EXchange message:
-- uint8_t kex_version (always 0 in this version of SPTPS)
+- uint8_t kex_version (always 1 in this version of SPTPS)
+- uint8_t
+ - high 4 bits: public key algorithm
+ - low 4 bits: preferred cipher suite
+- uint16_t bitmask of cipher suites supported
- opaque nonce[32] (random number)
- opaque ecdh_key[ECDH_SIZE]
@@ -162,9 +166,34 @@ The expanded key is used as follows:
Where initiator_cipher_key is the key used by session initiator to encrypt
messages sent to the responder.
+Public key suites
+-----------------
+
+0: Ed25519 + SHA512
+1: Ed448 + SHAKE256?
+
+Symmetric cipher suites
+-----------------------
+
+Value in parentheses is the static priority used to break ties in cipher suite
+negotiation. We favor those algorithms that run faster without hardware
+acceleration.
+
+0: Chacha20-Poly1305 (1)
+1: AES256-GCM (0)
+
+Cipher suite selection
+----------------------
+
+Public key suites are required to match on both sides. The symmetric suite is chosen as follows:
+
+1. AND the supported cipher suite bitmasks
+2. If both preferred cipher suites are possible, choose the one with the highest static priority.
+3. If only one is possible, choose that one.
+4. If none is possible, choose the suite from the resulting bitmask that has the highest static priority.
+
TODO:
-----
- Document format of ECDH public key, ECDSA signature
-- Document how CTR mode is used
- Refer to TLS RFCs where appropriate
diff --git a/src/invitation.c b/src/invitation.c
index cff9e727..6c49af48 100644
--- a/src/invitation.c
+++ b/src/invitation.c
@@ -1399,7 +1399,16 @@ next:
}
// Start an SPTPS session
- if(!sptps_start(&sptps, NULL, true, false, key, hiskey, "tinc invitation", 15, invitation_send, invitation_receive)) {
+ sptps_params_t params = {
+ .initiator = true,
+ .mykey = key,
+ .hiskey = hiskey,
+ .label = "tinc invitation",
+ .send_data = invitation_send,
+ .receive_record = invitation_receive,
+ };
+
+ if(!sptps_start(&sptps, &params)) {
ecdsa_free(hiskey);
ecdsa_free(key);
return 1;
diff --git a/src/protocol_auth.c b/src/protocol_auth.c
index 22254575..050b266c 100644
--- a/src/protocol_auth.c
+++ b/src/protocol_auth.c
@@ -412,7 +412,17 @@ bool id_h(connection_t *c, const char *request) {
c->protocol_minor = 2;
- return sptps_start(&c->sptps, c, false, false, invitation_key, c->ecdsa, "tinc invitation", 15, send_meta_sptps, receive_invitation_sptps);
+ sptps_params_t params = {
+ .handle = c,
+ .initiator = false,
+ .mykey = invitation_key,
+ .hiskey = c->ecdsa,
+ .label = "tinc invitation",
+ .send_data = send_meta_sptps,
+ .receive_record = receive_invitation_sptps,
+ };
+
+ return sptps_start(&c->sptps, &params);
}
/* Check if identity is a valid name */
@@ -507,7 +517,18 @@ bool id_h(connection_t *c, const char *request) {
snprintf(label, labellen, "tinc TCP key expansion %s %s", c->name, myself->name);
}
- return sptps_start(&c->sptps, c, c->outgoing, false, myself->connection->ecdsa, c->ecdsa, label, labellen, send_meta_sptps, receive_meta_sptps);
+ sptps_params_t params = {
+ .handle = c,
+ .initiator = c->outgoing,
+ .mykey = myself->connection->ecdsa,
+ .hiskey = c->ecdsa,
+ .label = label,
+ .labellen = sizeof(label),
+ .send_data = send_meta_sptps,
+ .receive_record = receive_meta_sptps,
+ };
+
+ return sptps_start(&c->sptps, &params);
} else {
return send_metakey(c);
}
diff --git a/src/protocol_key.c b/src/protocol_key.c
index 740d2fb4..da53c16c 100644
--- a/src/protocol_key.c
+++ b/src/protocol_key.c
@@ -128,7 +128,20 @@ bool send_req_key(node_t *to) {
to->status.waitingforkey = true;
to->last_req_key = now.tv_sec;
to->incompression = myself->incompression;
- return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, labellen, send_initial_sptps_data, receive_sptps_record);
+
+ sptps_params_t params = {
+ .handle = to,
+ .initiator = true,
+ .datagram = true,
+ .mykey = myself->connection->ecdsa,
+ .hiskey = to->ecdsa,
+ .label = label,
+ .labellen = sizeof(label),
+ .send_data = send_initial_sptps_data,
+ .receive_record = receive_sptps_record,
+ };
+
+ return sptps_start(&to->sptps, &params);
}
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
@@ -249,7 +262,20 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, no
from->status.validkey = false;
from->status.waitingforkey = true;
from->last_req_key = now.tv_sec;
- sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, labellen, send_sptps_data_myself, receive_sptps_record);
+
+ sptps_params_t params = {
+ .handle = from,
+ .initiator = false,
+ .datagram = true,
+ .mykey = myself->connection->ecdsa,
+ .hiskey = from->ecdsa,
+ .label = label,
+ .labellen = sizeof(label),
+ .send_data = send_sptps_data_myself,
+ .receive_record = receive_sptps_record,
+ };
+
+ sptps_start(&from->sptps, &params);
sptps_receive_data(&from->sptps, buf, len);
send_mtu_info(myself, from, MTU);
return true;
diff --git a/src/sptps.c b/src/sptps.c
index a0483c34..33c41424 100644
--- a/src/sptps.c
+++ b/src/sptps.c
@@ -28,6 +28,10 @@
#include "sptps.h"
#include "random.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#endif
+
unsigned int sptps_replaywin = 16;
/*
@@ -90,25 +94,159 @@ static void warning(sptps_t *s, const char *format, ...) {
va_end(ap);
}
+static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_half) {
+ switch(suite) {
+ case SPTPS_CHACHA_POLY1305:
+ *ctx = chacha_poly1305_init();
+ return ctx && chacha_poly1305_set_key(*ctx, key + (key_half ? CHACHA_POLY1305_KEYLEN : 0));
+
+ case SPTPS_AES256_GCM:
+#ifdef HAVE_OPENSSL
+ *ctx = EVP_CIPHER_CTX_new();
+
+ if(!ctx) {
+ return false;
+ }
+
+ return EVP_EncryptInit_ex(*ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 4, NULL)
+ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? 32 : 0), key);
+#endif
+
+ default:
+ return false;
+ }
+}
+
+static void cipher_exit(uint8_t suite, void *ctx) {
+ switch(suite) {
+ case SPTPS_CHACHA_POLY1305:
+ chacha_poly1305_exit(ctx);
+ break;
+
+ case SPTPS_AES256_GCM:
+#ifdef HAVE_OPENSSL
+ EVP_CIPHER_CTX_free(ctx);
+ break;
+#endif
+
+ default:
+ break;
+ }
+}
+
+static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) {
+ switch(suite) {
+ case SPTPS_CHACHA_POLY1305:
+ chacha_poly1305_encrypt(ctx, seqno, in, inlen, out, outlen);
+ return true;
+
+ case SPTPS_AES256_GCM:
+#ifdef HAVE_OPENSSL
+ {
+ if(!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, (uint8_t *)&seqno)) {
+ return false;
+ }
+
+ int outlen1 = 0, outlen2 = 0;
+
+ if(!EVP_EncryptUpdate(ctx, out, &outlen1, in, (int)inlen)) {
+ return false;
+ }
+
+ if(!EVP_EncryptFinal_ex(ctx, out + outlen1, &outlen2)) {
+ return false;
+ }
+
+ outlen1 += outlen2;
+
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out + outlen1)) {
+ return false;
+ }
+
+ outlen1 += 16;
+
+ if(outlen) {
+ *outlen = outlen1;
+ }
+
+ return true;
+ }
+
+#endif
+
+ default:
+ return false;
+ }
+}
+
+static bool cipher_decrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) {
+ switch(suite) {
+ case SPTPS_CHACHA_POLY1305:
+ return chacha_poly1305_decrypt(ctx, seqno, in, inlen, out, outlen);
+
+ case SPTPS_AES256_GCM:
+#ifdef HAVE_OPENSSL
+ {
+ if(inlen < 16) {
+ return false;
+ }
+
+ inlen -= 16;
+
+ if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, (uint8_t *)&seqno)) {
+ return false;
+ }
+
+ int outlen1 = 0, outlen2 = 0;
+
+ if(!EVP_DecryptUpdate(ctx, out, &outlen1, in, (int)inlen)) {
+ return false;
+ }
+
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void *)(in + inlen))) {
+ return false;
+ }
+
+ if(!EVP_DecryptFinal_ex(ctx, out + outlen1, &outlen2)) {
+ return false;
+ }
+
+ if(outlen) {
+ *outlen = outlen1 + outlen2;
+ }
+
+ return true;
+ }
+
+#endif
+
+ default:
+ return false;
+ }
+}
+
+
// 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 void *data, uint16_t len) {
- uint8_t *buffer = alloca(len + 21UL);
-
+ uint8_t *buffer = alloca(len + SPTPS_DATAGRAM_OVERHEAD);
// Create header with sequence number, length and record type
uint32_t seqno = s->outseqno++;
- uint32_t netseqno = ntohl(seqno);
- memcpy(buffer, &netseqno, 4);
+ memcpy(buffer, &seqno, 4);
buffer[4] = type;
memcpy(buffer + 5, data, len);
if(s->outstate) {
// If first handshake has finished, encrypt and HMAC
- chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 4, len + 1, buffer + 4, NULL);
- return s->send_data(s->handle, type, buffer, len + 21UL);
+ if(!cipher_encrypt(s->cipher_suite, s->outcipher, seqno, buffer + 4, len + 1, buffer + 4, NULL)) {
+ return error(s, EINVAL, "Failed to encrypt message");
+ }
+
+ return s->send_data(s->handle, type, buffer, len + SPTPS_DATAGRAM_OVERHEAD);
} else {
// Otherwise send as plaintext
- return s->send_data(s->handle, type, buffer, len + 5UL);
+ return s->send_data(s->handle, type, buffer, len + SPTPS_DATAGRAM_HEADER);
}
}
// Send a record (private version, accepts all record types, handles encryption and authentication).
@@ -117,11 +255,11 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_
return send_record_priv_datagram(s, type, data, len);
}
- uint8_t *buffer = alloca(len + 19UL);
+ uint8_t *buffer = alloca(len + SPTPS_OVERHEAD);
// Create header with sequence number, length and record type
uint32_t seqno = s->outseqno++;
- uint16_t netlen = htons(len);
+ uint16_t netlen = len;
memcpy(buffer, &netlen, 2);
buffer[2] = type;
@@ -129,11 +267,14 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_
if(s->outstate) {
// If first handshake has finished, encrypt and HMAC
- chacha_poly1305_encrypt(s->outcipher, seqno, buffer + 2, len + 1, buffer + 2, NULL);
- return s->send_data(s->handle, type, buffer, len + 19UL);
+ if(!cipher_encrypt(s->cipher_suite, s->outcipher, seqno, buffer + 2, len + 1, buffer + 2, NULL)) {
+ return error(s, EINVAL, "Failed to encrypt message");
+ }
+
+ return s->send_data(s->handle, type, buffer, len + SPTPS_OVERHEAD);
} else {
// Otherwise send as plaintext
- return s->send_data(s->handle, type, buffer, len + 3UL);
+ return s->send_data(s->handle, type, buffer, len + SPTPS_HEADER);
}
}
@@ -161,7 +302,7 @@ static bool send_kex(sptps_t *s) {
return false;
}
- s->mykex = realloc(s->mykex, 1 + 32 + keylen);
+ s->mykex = realloc(s->mykex, 4 + 32 + keylen);
if(!s->mykex) {
return error(s, errno, strerror(errno));
@@ -169,16 +310,18 @@ static bool send_kex(sptps_t *s) {
// Set version byte to zero.
s->mykex[0] = SPTPS_VERSION;
+ s->mykex[1] = s->preferred_suite;
+ memcpy(s->mykex + 2, &s->cipher_suites, 2);
// Create a random nonce.
- randomize(s->mykex + 1, 32);
+ randomize(s->mykex + 4, 32);
// Create a new ECDH public key.
- if(!(s->ecdh = ecdh_generate_public(s->mykex + 1 + 32))) {
+ if(!(s->ecdh = ecdh_generate_public(s->mykex + 4 + 32))) {
return error(s, EINVAL, "Failed to generate ECDH public key");
}
- return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen);
+ return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 4 + 32 + keylen);
}
// Send a SIGnature record, containing an Ed25519 signature over both KEX records.
@@ -192,9 +335,9 @@ static bool send_sig(sptps_t *s) {
uint8_t *sig = alloca(siglen);
msg[0] = s->initiator;
- memcpy(msg + 1, s->mykex, 1 + 32 + keylen);
- memcpy(msg + 1 + 33 + keylen, s->hiskex, 1 + 32 + keylen);
- memcpy(msg + 1 + 2 * (33 + keylen), s->label, s->labellen);
+ memcpy(msg + 1, s->mykex, 4 + 32 + keylen);
+ memcpy(msg + 1 + (4 + 32 + keylen), s->hiskex, 4 + 32 + keylen);
+ memcpy(msg + 1 + 2 * (4 + 32 + keylen), s->label, s->labellen);
// Sign the result.
if(!ecdsa_sign(s->mykey, msg, msglen, sig)) {
@@ -207,16 +350,6 @@ static bool send_sig(sptps_t *s) {
// Generate key material from the shared secret created from the ECDHE key exchange.
static bool generate_key_material(sptps_t *s, const uint8_t *shared, size_t len) {
- // Initialise cipher and digest structures if necessary
- if(!s->outstate) {
- 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 = 2 * CHACHA_POLY1305_KEYLEN;
@@ -261,14 +394,8 @@ static bool receive_ack(sptps_t *s, const uint8_t *data, uint16_t len) {
return error(s, EIO, "Invalid ACK record length");
}
- if(s->initiator) {
- if(!chacha_poly1305_set_key(s->incipher, s->key)) {
- return error(s, EINVAL, "Failed to set counter");
- }
- } else {
- if(!chacha_poly1305_set_key(s->incipher, s->key + CHACHA_POLY1305_KEYLEN)) {
- return error(s, EINVAL, "Failed to set counter");
- }
+ if(!cipher_init(s->cipher_suite, &s->incipher, s->key, s->initiator)) {
+ return error(s, EINVAL, "Failed to initialize cipher");
}
free(s->key);
@@ -278,14 +405,51 @@ static bool receive_ack(sptps_t *s, const uint8_t *data, uint16_t len) {
return true;
}
+static uint8_t select_cipher_suite(uint16_t mask, uint8_t pref1, uint8_t pref2) {
+ // Check if there is a viable preference, if so select the lowest one
+ uint8_t selection = 255;
+
+ if(mask & (1U << pref1)) {
+ selection = pref1;
+ }
+
+ if(pref2 < selection && (mask & (1U << pref2))) {
+ selection = pref2;
+ }
+
+ // Otherwise, select the lowest cipher suite both sides support
+ if(selection == 255) {
+ selection = 0;
+
+ while(!(mask & 1U)) {
+ selection++;
+ mask >>= 1;
+ }
+ }
+
+ return selection;
+}
+
// Receive a Key EXchange record, respond by sending a SIG record.
static bool receive_kex(sptps_t *s, const uint8_t *data, uint16_t len) {
// Verify length of the HELLO record
- if(len != 1 + 32 + ECDH_SIZE) {
+ if(len != 4 + 32 + ECDH_SIZE) {
return error(s, EIO, "Invalid KEX record length");
}
- // Ignore version number for now.
+ if(data[0] != SPTPS_VERSION) {
+ return error(s, EIO, "Incompatible SPTPS version");
+ }
+
+ uint16_t suites;
+ memcpy(&suites, data + 2, 2);
+ suites &= s->cipher_suites;
+
+ if(!suites) {
+ return error(s, EIO, "No matching cipher suites");
+ }
+
+ s->cipher_suite = select_cipher_suite(suites, s->preferred_suite, data[1] & 0xf);
// Make a copy of the KEX message, send_sig() and receive_sig() need it
if(s->hiskex) {
@@ -322,9 +486,9 @@ static bool receive_sig(sptps_t *s, const uint8_t *data, uint16_t len) {
uint8_t *msg = alloca(msglen);
msg[0] = !s->initiator;
- memcpy(msg + 1, s->hiskex, 1 + 32 + keylen);
- memcpy(msg + 1 + 33 + keylen, s->mykex, 1 + 32 + keylen);
- memcpy(msg + 1 + 2 * (33 + keylen), s->label, s->labellen);
+ memcpy(msg + 1, s->hiskex, 4 + 32 + keylen);
+ memcpy(msg + 1 + (4 + 32 + keylen), s->mykex, 4 + 32 + keylen);
+ memcpy(msg + 1 + 2 * (4 + 32 + keylen), s->label, s->labellen);
// Verify signature.
if(!ecdsa_verify(s->hiskey, msg, msglen, data)) {
@@ -334,7 +498,7 @@ static bool receive_sig(sptps_t *s, const uint8_t *data, uint16_t len) {
// Compute shared secret.
uint8_t shared[ECDH_SHARED_SIZE];
- if(!ecdh_compute_shared(s->ecdh, s->hiskex + 1 + 32, shared)) {
+ if(!ecdh_compute_shared(s->ecdh, s->hiskex + 4 + 32, shared)) {
return error(s, EINVAL, "Failed to compute ECDH shared secret");
}
@@ -360,15 +524,8 @@ static bool receive_sig(sptps_t *s, const uint8_t *data, uint16_t len) {
return false;
}
- // TODO: only set new keys after ACK has been set/received
- if(s->initiator) {
- if(!chacha_poly1305_set_key(s->outcipher, s->key + CHACHA_POLY1305_KEYLEN)) {
- return error(s, EINVAL, "Failed to set key");
- }
- } else {
- if(!chacha_poly1305_set_key(s->outcipher, s->key)) {
- return error(s, EINVAL, "Failed to set key");
- }
+ if(!cipher_init(s->cipher_suite, &s->outcipher, s->key, !s->initiator)) {
+ return error(s, EINVAL, "Failed to initialize cipher");
}
return true;
@@ -518,15 +675,13 @@ bool sptps_verify_datagram(sptps_t *s, const void *vdata, size_t len) {
const uint8_t *data = vdata;
uint32_t seqno;
memcpy(&seqno, data, 4);
- seqno = ntohl(seqno);
if(!sptps_check_seqno(s, seqno, false)) {
return false;
}
uint8_t *buffer = alloca(len);
- size_t outlen;
- return chacha_poly1305_decrypt(s->incipher, seqno, data + 4, len - 4, buffer, &outlen);
+ return cipher_decrypt(s->cipher_suite, s->incipher, seqno, data + 4, len - 4, buffer, NULL);
}
// Receive incoming data, datagram version.
@@ -537,7 +692,6 @@ static bool sptps_receive_data_datagram(sptps_t *s, const uint8_t *data, size_t
uint32_t seqno;
memcpy(&seqno, data, 4);
- seqno = ntohl(seqno);
data += 4;
len -= 4;
@@ -563,7 +717,7 @@ static bool sptps_receive_data_datagram(sptps_t *s, const uint8_t *data, size_t
uint8_t *buffer = alloca(len);
size_t outlen;
- if(!chacha_poly1305_decrypt(s->incipher, seqno, data, len, buffer, &outlen)) {
+ if(!cipher_decrypt(s->cipher_suite, s->incipher, seqno, data, len, buffer, &outlen)) {
return error(s, EIO, "Failed to decrypt and verify packet");
}
@@ -635,10 +789,9 @@ size_t sptps_receive_data(sptps_t *s, const void *vdata, size_t len) {
// 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.
- s->inbuf = realloc(s->inbuf, s->reclen + 19UL);
+ s->inbuf = realloc(s->inbuf, s->reclen + SPTPS_OVERHEAD);
if(!s->inbuf) {
return error(s, errno, strerror(errno));
@@ -651,7 +804,7 @@ size_t sptps_receive_data(sptps_t *s, const void *vdata, size_t len) {
}
// Read up to the end of the record.
- size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen;
+ size_t toread = s->reclen + (s->instate ? SPTPS_OVERHEAD : SPTPS_HEADER) - s->buflen;
if(toread > len) {
toread = len;
@@ -662,7 +815,7 @@ size_t sptps_receive_data(sptps_t *s, const void *vdata, size_t len) {
s->buflen += toread;
// If we don't have a whole record, exit.
- if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) {
+ if(s->buflen < s->reclen + (s->instate ? SPTPS_OVERHEAD : SPTPS_HEADER)) {
return total_read;
}
@@ -672,13 +825,13 @@ size_t sptps_receive_data(sptps_t *s, const void *vdata, size_t len) {
// Check HMAC and decrypt.
if(s->instate) {
- if(!chacha_poly1305_decrypt(s->incipher, seqno, s->inbuf + 2UL, s->reclen + 17UL, s->inbuf + 2UL, NULL)) {
+ if(!cipher_decrypt(s->cipher_suite, 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;
+ s->inbuf[s->reclen + SPTPS_HEADER] = 0;
uint8_t type = s->inbuf[2];
@@ -704,16 +857,18 @@ size_t sptps_receive_data(sptps_t *s, const void *vdata, 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 void *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) {
+bool sptps_start(sptps_t *s, const sptps_params_t *params) {
// Initialise struct sptps
memset(s, 0, sizeof(*s));
- s->handle = handle;
- s->initiator = initiator;
- s->datagram = datagram;
- s->mykey = mykey;
- s->hiskey = hiskey;
+ s->handle = params->handle;
+ s->initiator = params->initiator;
+ s->datagram = params->datagram;
+ s->mykey = params->mykey;
+ s->hiskey = params->hiskey;
s->replaywin = sptps_replaywin;
+ s->cipher_suites = params->cipher_suites ? params->cipher_suites & SPTPS_ALL_CIPHER_SUITES : SPTPS_ALL_CIPHER_SUITES;
+ s->preferred_suite = params->preferred_suite;
if(s->replaywin) {
s->late = malloc(s->replaywin);
@@ -725,13 +880,16 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_
memset(s->late, 0, s->replaywin);
}
- s->label = malloc(labellen);
+ s->labellen = params->labellen ? params->labellen : strlen(params->label);
+ s->label = malloc(s->labellen);
if(!s->label) {
return error(s, errno, strerror(errno));
}
- if(!datagram) {
+ memcpy(s->label, params->label, s->labellen);
+
+ if(!s->datagram) {
s->inbuf = malloc(7);
if(!s->inbuf) {
@@ -741,11 +899,9 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_
s->buflen = 0;
}
- memcpy(s->label, label, labellen);
- s->labellen = labellen;
- s->send_data = send_data;
- s->receive_record = receive_record;
+ s->send_data = params->send_data;
+ s->receive_record = params->receive_record;
// Do first KEX immediately
s->state = SPTPS_KEX;
@@ -755,8 +911,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.
- chacha_poly1305_exit(s->incipher);
- chacha_poly1305_exit(s->outcipher);
+ cipher_exit(s->cipher_suite, s->incipher);
+ cipher_exit(s->cipher_suite, s->outcipher);
ecdh_free(s->ecdh);
free(s->inbuf);
free(s->mykex);
diff --git a/src/sptps.h b/src/sptps.h
index 96edc366..b9ec11fd 100644
--- a/src/sptps.h
+++ b/src/sptps.h
@@ -26,7 +26,7 @@
#include "ecdh.h"
#include "ecdsa.h"
-#define SPTPS_VERSION 0
+#define SPTPS_VERSION 1
// Record types
#define SPTPS_HANDSHAKE 128 // Key exchange and authentication
@@ -34,7 +34,10 @@
#define SPTPS_CLOSE 130 // Application closed the connection
// Overhead for datagrams
-#define SPTPS_DATAGRAM_OVERHEAD 21
+static const size_t SPTPS_OVERHEAD = 19;
+static const size_t SPTPS_HEADER = 3;
+static const size_t SPTPS_DATAGRAM_OVERHEAD = 21;
+static const size_t SPTPS_DATAGRAM_HEADER = 5;
typedef bool (*send_data_t)(void *handle, uint8_t type, const void *data, size_t len);
typedef bool (*receive_record_t)(void *handle, uint8_t type, const void *data, uint16_t len);
@@ -47,9 +50,40 @@ typedef enum sptps_state_t {
SPTPS_ACK = 4, // Waiting for an ACKnowledgement record
} sptps_state_t;
+// Public key suites
+enum {
+ SPTPS_ED25519 = 0,
+};
+
+// Cipher suites
+enum {
+ SPTPS_CHACHA_POLY1305 = 0,
+ SPTPS_AES256_GCM = 1,
+ SPTPS_ALL_CIPHER_SUITES = 0x3,
+};
+
+typedef struct sptps_params {
+ void *handle;
+ bool initiator;
+ bool datagram;
+ uint8_t preferred_suite;
+ uint16_t cipher_suites;
+ ecdsa_t *mykey;
+ ecdsa_t *hiskey;
+ const void *label;
+ size_t labellen;
+ send_data_t send_data;
+ receive_record_t receive_record;
+} sptps_params_t;
+
typedef struct sptps {
bool initiator;
bool datagram;
+ uint8_t preferred_suite;
+ uint16_t cipher_suites;
+
+ uint8_t pk_suite;
+ uint8_t cipher_suite;
sptps_state_t state;
uint8_t *inbuf;
@@ -57,7 +91,7 @@ typedef struct sptps {
uint16_t reclen;
bool instate;
- chacha_poly1305_ctx_t *incipher;
+ void *incipher;
uint32_t inseqno;
uint32_t received;
unsigned int replaywin;
@@ -65,7 +99,7 @@ typedef struct sptps {
uint8_t *late;
bool outstate;
- chacha_poly1305_ctx_t *outcipher;
+ void *outcipher;
uint32_t outseqno;
ecdsa_t *mykey;
@@ -87,7 +121,7 @@ extern unsigned int sptps_replaywin;
extern void sptps_log_quiet(sptps_t *s, int s_errno, const char *format, va_list ap);
extern void sptps_log_stderr(sptps_t *s, int s_errno, const char *format, va_list ap);
extern void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap);
-extern 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);
+extern bool sptps_start(sptps_t *s, const struct sptps_params *params);
extern bool sptps_stop(sptps_t *s);
extern bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len);
extern size_t sptps_receive_data(sptps_t *s, const void *data, size_t len);
diff --git a/src/sptps_test.c b/src/sptps_test.c
index 249f2e4f..e77ab9c7 100644
--- a/src/sptps_test.c
+++ b/src/sptps_test.c
@@ -562,7 +562,18 @@ static int run_test(int argc, char *argv[]) {
sptps_t s;
- if(!sptps_start(&s, &sock, initiator, datagram, mykey, hiskey, "sptps_test", 10, send_data, receive_record)) {
+ sptps_params_t params = {
+ .handle = &sock,
+ .initiator = initiator,
+ .datagram = datagram,
+ .mykey = mykey,
+ .hiskey = hiskey,
+ .label = "sptps_test",
+ .send_data = send_data,
+ .receive_record = receive_record,
+ };
+
+ if(!sptps_start(&s, &params)) {
free(mykey);
free(hiskey);
return 1;
--
2.36.0

View file

@ -0,0 +1,91 @@
From 1d0eea4899f9642a3945c07b9266e660b9f9ce71 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Tue, 3 Aug 2021 00:38:37 +0200
Subject: [PATCH 02/10] Add cipher suite selection options to sptps_test.
---
src/sptps_test.c | 38 +++++++++++++++++++++++++++-----------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/src/sptps_test.c b/src/sptps_test.c
index e77ab9c7..32ed62d3 100644
--- a/src/sptps_test.c
+++ b/src/sptps_test.c
@@ -127,6 +127,8 @@ static struct option const long_options[] = {
{"replay-window", required_argument, NULL, 'W'},
{"special", no_argument, NULL, 's'},
{"verbose", required_argument, NULL, 'v'},
+ {"cipher-suites", required_argument, NULL, 'M'},
+ {"preferred-cipher", required_argument, NULL, 'P'},
{"help", no_argument, NULL, 1},
{NULL, 0, NULL, 0}
};
@@ -136,19 +138,21 @@ static void usage(void) {
"Usage: %s [options] my_ed25519_key_file his_ed25519_key_file [host] port\n"
"\n"
"Valid options are:\n"
- " -d, --datagram Enable datagram mode.\n"
- " -q, --quit Quit when EOF occurs on stdin.\n"
- " -r, --readonly Only send data from the socket to stdout.\n"
+ " -d, --datagram Enable datagram mode.\n"
+ " -q, --quit Quit when EOF occurs on stdin.\n"
+ " -r, --readonly Only send data from the socket to stdout.\n"
#ifdef HAVE_LINUX
- " -t, --tun Use a tun device instead of stdio.\n"
+ " -t, --tun Use a tun device instead of stdio.\n"
#endif
- " -w, --writeonly Only send data from stdin to the socket.\n"
- " -L, --packet-loss RATE Fake packet loss of RATE percent.\n"
- " -R, --replay-window N Set replay window to N bytes.\n"
- " -s, --special Enable special handling of lines starting with #, ^ and $.\n"
- " -v, --verbose Display debug messages.\n"
- " -4 Use IPv4.\n"
- " -6 Use IPv6.\n"
+ " -w, --writeonly Only send data from stdin to the socket.\n"
+ " -L, --packet-loss RATE Fake packet loss of RATE percent.\n"
+ " -R, --replay-window N Set replay window to N bytes.\n"
+ " -M, --cipher-suites MASK Set the mask of allowed cipher suites.\n"
+ " -P, --preferred-suite N Set the preferred cipher suite.\n"
+ " -s, --special Enable special handling of lines starting with #, ^ and $.\n"
+ " -v, --verbose Display debug messages.\n"
+ " -4 Use IPv4.\n"
+ " -6 Use IPv6.\n"
"\n"
"Report bugs to tinc@tinc-vpn.org.\n";
@@ -326,6 +330,8 @@ static int run_test(int argc, char *argv[]) {
int r;
int option_index = 0;
bool quit = false;
+ unsigned long cipher_suites = SPTPS_ALL_CIPHER_SUITES;
+ unsigned long preferred_suite = 0;
while((r = getopt_long(argc, argv, "dqrstwL:W:v46", long_options, &option_index)) != EOF) {
switch(r) {
@@ -366,6 +372,14 @@ static int run_test(int argc, char *argv[]) {
sptps_replaywin = atoi(optarg);
break;
+ case 'M': /* cipher suites */
+ cipher_suites = strtoul(optarg, NULL, 0);
+ break;
+
+ case 'P': /* preferred cipher */
+ preferred_suite = strtoul(optarg, NULL, 0);
+ break;
+
case 'v': /* be verbose */
verbose = true;
break;
@@ -571,6 +585,8 @@ static int run_test(int argc, char *argv[]) {
.label = "sptps_test",
.send_data = send_data,
.receive_record = receive_record,
+ .cipher_suites = cipher_suites,
+ .preferred_suite = preferred_suite,
};
if(!sptps_start(&s, &params)) {
--
2.36.0

View file

@ -0,0 +1,285 @@
From c0f0610037847ea2abae1e7a826d36a55f9dfa36 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Tue, 3 Aug 2021 00:39:05 +0200
Subject: [PATCH 03/10] Let sptps_speed benchmark all cipher suites.
---
src/sptps_speed.c | 212 +++++++++++++++++++++++++++-------------------
1 file changed, 125 insertions(+), 87 deletions(-)
diff --git a/src/sptps_speed.c b/src/sptps_speed.c
index c7c6e546..45bbeb5c 100644
--- a/src/sptps_speed.c
+++ b/src/sptps_speed.c
@@ -168,22 +168,76 @@ static int run_benchmark(int argc, char *argv[]) {
fprintf(stderr, "%28.2lf op/s\n", rate);
ecdh_free(ecdh1);
- // SPTPS authentication phase
-
int fd[2];
+ struct pollfd pfd[2] = {{fd[0], POLLIN}, {fd[1], POLLIN}};
+
+ sptps_params_t params1 = {
+ .handle = fd + 0,
+ .initiator = true,
+ .datagram = false,
+ .mykey = key1,
+ .hiskey = key2,
+ .label = "sptps_speed",
+ .send_data = send_data,
+ .receive_record = receive_record,
+ };
+
+ sptps_params_t params2 = {
+ .handle = fd + 1,
+ .initiator = false,
+ .datagram = false,
+ .mykey = key2,
+ .hiskey = key1,
+ .label = "sptps_speed",
+ .send_data = send_data,
+ .receive_record = receive_record,
+ };
+
+ static const char *suite_names[] = {
+ "Chacha20-Poly1305",
+ "AES-256-GCM",
+ };
+
+ for(uint8_t suite = 0; suite < 2; suite++) {
+ fprintf(stderr, "\nCipher suite %u (%s):\n", suite, suite_names[suite]);
+ params1.preferred_suite = params2.preferred_suite = suite;
+
+ // SPTPS authentication phase
+
+ fprintf(stderr, "SPTPS/TCP authenticate for %lg seconds: ", duration);
+
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
+ fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
+ return 1;
+ }
- if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
- fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
- return 1;
- }
+ pfd[0].fd = fd[0], pfd[1].fd = fd[1];
+ params1.datagram = params2.datagram = false;
- struct pollfd pfd[2] = {{fd[0], POLLIN, 0}, {fd[1], POLLIN, 0}};
+ for(clock_start(); clock_countto(duration);) {
+ sptps_start(&sptps1, &params1);
+ sptps_start(&sptps2, &params2);
- fprintf(stderr, "SPTPS/TCP authenticate for %lg seconds: ", duration);
+ while(poll(pfd, 2, 0)) {
+ if(pfd[0].revents) {
+ receive_data(&sptps1);
+ }
- for(clock_start(); clock_countto(duration);) {
- sptps_start(&sptps1, fd + 0, true, false, key1, key2, "sptps_speed", 11, send_data, receive_record);
- sptps_start(&sptps2, fd + 1, false, false, key2, key1, "sptps_speed", 11, send_data, receive_record);
+ if(pfd[1].revents) {
+ receive_data(&sptps2);
+ }
+ }
+
+ sptps_stop(&sptps1);
+ sptps_stop(&sptps2);
+ }
+
+ fprintf(stderr, "%10.2lf op/s\n", rate * 2);
+
+ // SPTPS data
+
+ sptps_start(&sptps1, &params1);
+ sptps_start(&sptps2, &params2);
while(poll(pfd, 2, 0)) {
if(pfd[0].revents) {
@@ -195,65 +249,68 @@ static int run_benchmark(int argc, char *argv[]) {
}
}
- sptps_stop(&sptps1);
- sptps_stop(&sptps2);
- }
+ fprintf(stderr, "SPTPS/TCP transmit for %lg seconds: ", duration);
- fprintf(stderr, "%10.2lf op/s\n", rate * 2);
+ for(clock_start(); clock_countto(duration);) {
+ if(!sptps_send_record(&sptps1, 0, buf1, 1451)) {
+ abort();
+ }
- // SPTPS data
+ receive_data(&sptps2);
+ }
- sptps_start(&sptps1, fd + 0, true, false, key1, key2, "sptps_speed", 11, send_data, receive_record);
- sptps_start(&sptps2, fd + 1, false, false, key2, key1, "sptps_speed", 11, send_data, receive_record);
+ rate *= 2 * 1451 * 8;
- while(poll(pfd, 2, 0)) {
- if(pfd[0].revents) {
- receive_data(&sptps1);
+ if(rate > 1e9) {
+ fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
+ } else if(rate > 1e6) {
+ fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
+ } else if(rate > 1e3) {
+ fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
}
- if(pfd[1].revents) {
- receive_data(&sptps2);
- }
- }
+ sptps_stop(&sptps1);
+ sptps_stop(&sptps2);
- fprintf(stderr, "SPTPS/TCP transmit for %lg seconds: ", duration);
+ close(fd[0]);
+ close(fd[1]);
- for(clock_start(); clock_countto(duration);) {
- if(!sptps_send_record(&sptps1, 0, buf1, 1451)) {
- abort();
+ // SPTPS datagram authentication phase
+
+ if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) {
+ fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
+ return 1;
}
- receive_data(&sptps2);
- }
+ pfd[0].fd = fd[0], pfd[1].fd = fd[1];
+ params1.datagram = params2.datagram = true;
- rate *= 2 * 1451 * 8;
+ fprintf(stderr, "SPTPS/UDP authenticate for %lg seconds: ", duration);
- if(rate > 1e9) {
- fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
- } else if(rate > 1e6) {
- fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
- } else if(rate > 1e3) {
- fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
- }
+ for(clock_start(); clock_countto(duration);) {
+ sptps_start(&sptps1, &params1);
+ sptps_start(&sptps2, &params2);
- sptps_stop(&sptps1);
- sptps_stop(&sptps2);
+ while(poll(pfd, 2, 0)) {
+ if(pfd[0].revents) {
+ receive_data(&sptps1);
+ }
- // SPTPS datagram authentication phase
+ if(pfd[1].revents) {
+ receive_data(&sptps2);
+ }
+ }
- close(fd[0]);
- close(fd[1]);
+ sptps_stop(&sptps1);
+ sptps_stop(&sptps2);
+ }
- if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) {
- fprintf(stderr, "Could not create a UNIX socket pair: %s\n", sockstrerror(sockerrno));
- return 1;
- }
+ fprintf(stderr, "%10.2lf op/s\n", rate * 2);
- fprintf(stderr, "SPTPS/UDP authenticate for %lg seconds: ", duration);
+ // SPTPS datagram data
- for(clock_start(); clock_countto(duration);) {
- sptps_start(&sptps1, fd + 0, true, true, key1, key2, "sptps_speed", 11, send_data, receive_record);
- sptps_start(&sptps2, fd + 1, false, true, key2, key1, "sptps_speed", 11, send_data, receive_record);
+ sptps_start(&sptps1, &params1);
+ sptps_start(&sptps2, &params2);
while(poll(pfd, 2, 0)) {
if(pfd[0].revents) {
@@ -265,54 +322,35 @@ static int run_benchmark(int argc, char *argv[]) {
}
}
- sptps_stop(&sptps1);
- sptps_stop(&sptps2);
- }
-
- fprintf(stderr, "%10.2lf op/s\n", rate * 2);
+ fprintf(stderr, "SPTPS/UDP transmit for %lg seconds: ", duration);
- // SPTPS datagram data
-
- sptps_start(&sptps1, fd + 0, true, true, key1, key2, "sptps_speed", 11, send_data, receive_record);
- sptps_start(&sptps2, fd + 1, false, true, key2, key1, "sptps_speed", 11, send_data, receive_record);
-
- while(poll(pfd, 2, 0)) {
- if(pfd[0].revents) {
- receive_data(&sptps1);
- }
+ for(clock_start(); clock_countto(duration);) {
+ if(!sptps_send_record(&sptps1, 0, buf1, 1451)) {
+ abort();
+ }
- if(pfd[1].revents) {
receive_data(&sptps2);
}
- }
- fprintf(stderr, "SPTPS/UDP transmit for %lg seconds: ", duration);
+ rate *= 2 * 1451 * 8;
- for(clock_start(); clock_countto(duration);) {
- if(!sptps_send_record(&sptps1, 0, buf1, 1451)) {
- abort();
+ if(rate > 1e9) {
+ fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
+ } else if(rate > 1e6) {
+ fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
+ } else if(rate > 1e3) {
+ fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
}
- receive_data(&sptps2);
- }
-
- rate *= 2 * 1451 * 8;
+ sptps_stop(&sptps1);
+ sptps_stop(&sptps2);
- if(rate > 1e9) {
- fprintf(stderr, "%14.2lf Gbit/s\n", rate / 1e9);
- } else if(rate > 1e6) {
- fprintf(stderr, "%14.2lf Mbit/s\n", rate / 1e6);
- } else if(rate > 1e3) {
- fprintf(stderr, "%14.2lf kbit/s\n", rate / 1e3);
+ close(fd[0]);
+ close(fd[1]);
}
- sptps_stop(&sptps1);
- sptps_stop(&sptps2);
-
// Clean up
- close(fd[0]);
- close(fd[1]);
ecdsa_free(key1);
ecdsa_free(key2);
--
2.36.0

View file

@ -0,0 +1,219 @@
From 9d423c31024e37655aac014662cb5bee82c26464 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Mon, 9 Aug 2021 21:55:09 +0200
Subject: [PATCH 04/10] If we link with OpenSSL, use it for Chacha20-Poly1305
as well.
---
src/sptps.c | 128 ++++++++++++++++++++++++++++++++--------------------
1 file changed, 78 insertions(+), 50 deletions(-)
diff --git a/src/sptps.c b/src/sptps.c
index 33c41424..55b9e5ca 100644
--- a/src/sptps.c
+++ b/src/sptps.c
@@ -96,12 +96,26 @@ static void warning(sptps_t *s, const char *format, ...) {
static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_half) {
switch(suite) {
+#ifndef HAVE_OPENSSL
+
case SPTPS_CHACHA_POLY1305:
*ctx = chacha_poly1305_init();
return ctx && chacha_poly1305_set_key(*ctx, key + (key_half ? CHACHA_POLY1305_KEYLEN : 0));
+#else
+
+ case SPTPS_CHACHA_POLY1305:
+ *ctx = EVP_CIPHER_CTX_new();
+
+ if(!ctx) {
+ return false;
+ }
+
+ return EVP_EncryptInit_ex(*ctx, EVP_chacha20_poly1305(), NULL, NULL, NULL)
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL)
+ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? CHACHA_POLY1305_KEYLEN : 0), key);
+
case SPTPS_AES256_GCM:
-#ifdef HAVE_OPENSSL
*ctx = EVP_CIPHER_CTX_new();
if(!ctx) {
@@ -109,8 +123,8 @@ static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_
}
return EVP_EncryptInit_ex(*ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)
- && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 4, NULL)
- && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? 32 : 0), key);
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL)
+ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? 64 : 0), key);
#endif
default:
@@ -120,12 +134,16 @@ static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_
static void cipher_exit(uint8_t suite, void *ctx) {
switch(suite) {
+#ifndef HAVE_OPENSSL
+
case SPTPS_CHACHA_POLY1305:
chacha_poly1305_exit(ctx);
break;
+#else
+
+ case SPTPS_CHACHA_POLY1305:
case SPTPS_AES256_GCM:
-#ifdef HAVE_OPENSSL
EVP_CIPHER_CTX_free(ctx);
break;
#endif
@@ -136,43 +154,48 @@ static void cipher_exit(uint8_t suite, void *ctx) {
}
static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) {
+ uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24};
+
switch(suite) {
+#ifndef HAVE_OPENSSL
+
case SPTPS_CHACHA_POLY1305:
chacha_poly1305_encrypt(ctx, seqno, in, inlen, out, outlen);
return true;
- case SPTPS_AES256_GCM:
-#ifdef HAVE_OPENSSL
- {
- if(!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, (uint8_t *)&seqno)) {
- return false;
- }
+#else
- int outlen1 = 0, outlen2 = 0;
+ case SPTPS_CHACHA_POLY1305:
+ case SPTPS_AES256_GCM: {
+ if(!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce)) {
+ return false;
+ }
- if(!EVP_EncryptUpdate(ctx, out, &outlen1, in, (int)inlen)) {
- return false;
- }
+ int outlen1 = 0, outlen2 = 0;
- if(!EVP_EncryptFinal_ex(ctx, out + outlen1, &outlen2)) {
- return false;
- }
+ if(!EVP_EncryptUpdate(ctx, out, &outlen1, in, (int)inlen)) {
+ return false;
+ }
- outlen1 += outlen2;
+ if(!EVP_EncryptFinal_ex(ctx, out + outlen1, &outlen2)) {
+ return false;
+ }
- if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out + outlen1)) {
- return false;
- }
+ outlen1 += outlen2;
- outlen1 += 16;
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out + outlen1)) {
+ return false;
+ }
- if(outlen) {
- *outlen = outlen1;
- }
+ outlen1 += 16;
- return true;
+ if(outlen) {
+ *outlen = outlen1;
}
+ return true;
+ }
+
#endif
default:
@@ -181,44 +204,49 @@ static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8
}
static bool cipher_decrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) {
+ uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24};
+
switch(suite) {
+#ifndef HAVE_OPENSSL
+
case SPTPS_CHACHA_POLY1305:
return chacha_poly1305_decrypt(ctx, seqno, in, inlen, out, outlen);
- case SPTPS_AES256_GCM:
-#ifdef HAVE_OPENSSL
- {
- if(inlen < 16) {
- return false;
- }
+#else
- inlen -= 16;
+ case SPTPS_CHACHA_POLY1305:
+ case SPTPS_AES256_GCM: {
+ if(inlen < 16) {
+ return false;
+ }
- if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, (uint8_t *)&seqno)) {
- return false;
- }
+ inlen -= 16;
- int outlen1 = 0, outlen2 = 0;
+ if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce)) {
+ return false;
+ }
- if(!EVP_DecryptUpdate(ctx, out, &outlen1, in, (int)inlen)) {
- return false;
- }
+ int outlen1 = 0, outlen2 = 0;
- if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void *)(in + inlen))) {
- return false;
- }
+ if(!EVP_DecryptUpdate(ctx, out, &outlen1, in, (int)inlen)) {
+ return false;
+ }
- if(!EVP_DecryptFinal_ex(ctx, out + outlen1, &outlen2)) {
- return false;
- }
+ if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void *)(in + inlen))) {
+ return false;
+ }
- if(outlen) {
- *outlen = outlen1 + outlen2;
- }
+ if(!EVP_DecryptFinal_ex(ctx, out + outlen1, &outlen2)) {
+ return false;
+ }
- return true;
+ if(outlen) {
+ *outlen = outlen1 + outlen2;
}
+ return true;
+ }
+
#endif
default:
--
2.36.0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
From d64b9c4a2f48ce7533e9f7a8f5f6e890764515ab Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Tue, 10 Aug 2021 23:08:04 +0200
Subject: [PATCH 06/10] Ensure we are compatible with LibreSSL.
---
src/sptps.c | 66 ++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 53 insertions(+), 13 deletions(-)
diff --git a/src/sptps.c b/src/sptps.c
index 33e88ed9..7c8d20b7 100644
--- a/src/sptps.c
+++ b/src/sptps.c
@@ -107,26 +107,26 @@ static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_
#else
case SPTPS_CHACHA_POLY1305:
- *ctx = EVP_CIPHER_CTX_new();
+#ifdef EVP_F_EVP_AEAD_CTX_INIT
+ *ctx = malloc(sizeof(EVP_AEAD_CTX));
- if(!ctx) {
- return false;
- }
+ return *ctx && EVP_AEAD_CTX_init(*ctx, EVP_aead_chacha20_poly1305(), key + (key_half ? CIPHER_KEYLEN : 0), 32, 16, NULL);
+#else
+ *ctx = EVP_CIPHER_CTX_new();
- return EVP_EncryptInit_ex(*ctx, EVP_chacha20_poly1305(), NULL, NULL, NULL)
- && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL)
+ return *ctx
+ && EVP_EncryptInit_ex(*ctx, EVP_chacha20_poly1305(), NULL, NULL, NULL)
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)
&& EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? CIPHER_KEYLEN : 0), key);
+#endif
case SPTPS_AES256_GCM:
*ctx = EVP_CIPHER_CTX_new();
- if(!ctx) {
- return false;
- }
-
- return EVP_EncryptInit_ex(*ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)
- && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL)
- && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? 64 : 0), key);
+ return *ctx
+ && EVP_EncryptInit_ex(*ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)
+ && EVP_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL)
+ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? CIPHER_KEYLEN : 0), key);
#endif
default:
@@ -145,6 +145,12 @@ static void cipher_exit(uint8_t suite, void *ctx) {
#else
case SPTPS_CHACHA_POLY1305:
+#ifdef EVP_F_EVP_AEAD_CTX_INIT
+ EVP_AEAD_CTX_cleanup(ctx);
+ free(ctx);
+ break;
+#endif
+
case SPTPS_AES256_GCM:
EVP_CIPHER_CTX_free(ctx);
break;
@@ -176,6 +182,23 @@ static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8
#else
case SPTPS_CHACHA_POLY1305:
+#ifdef EVP_F_EVP_AEAD_CTX_INIT
+ {
+ size_t outlen1;
+
+ if(!EVP_AEAD_CTX_seal(ctx, out, &outlen1, inlen + 16, nonce, sizeof(nonce), in, inlen, NULL, 0)) {
+ return false;
+ }
+
+ if(outlen) {
+ *outlen = outlen1;
+ }
+
+ return true;
+ }
+
+#endif
+
case SPTPS_AES256_GCM: {
if(!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce)) {
return false;
@@ -239,6 +262,23 @@ static bool cipher_decrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8
#else
case SPTPS_CHACHA_POLY1305:
+#ifdef EVP_F_EVP_AEAD_CTX_INIT
+ {
+ size_t outlen1;
+
+ if(!EVP_AEAD_CTX_open(ctx, out, &outlen1, inlen, nonce, sizeof(nonce), in, inlen + 16, NULL, 0)) {
+ return false;
+ }
+
+ if(outlen) {
+ *outlen = outlen1;
+ }
+
+ return true;
+ }
+
+#endif
+
case SPTPS_AES256_GCM: {
if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce)) {
return false;
--
2.36.0

View file

@ -0,0 +1,26 @@
From 948be5b4a813e814e36be23a63817df283e8db91 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Tue, 10 Aug 2021 23:08:52 +0200
Subject: [PATCH 07/10] Fix infinite loop on SPTPS errors when running
sptps_test in datagram mode.
---
src/sptps_test.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/sptps_test.c b/src/sptps_test.c
index 32ed62d3..7e5977ed 100644
--- a/src/sptps_test.c
+++ b/src/sptps_test.c
@@ -721,6 +721,8 @@ static int run_test(int argc, char *argv[]) {
free(mykey);
free(hiskey);
return 1;
+ } else {
+ break;
}
}
--
2.36.0

View file

@ -0,0 +1,40 @@
From 440bf1e9e484ac9800308dafbb5089e400df3522 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Sun, 22 Aug 2021 22:16:42 +0200
Subject: [PATCH 08/10] Fix documentation of default cipher algorithm used for
the legacy protocol.
---
doc/tinc.conf.5.in | 2 +-
doc/tinc.texi | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in
index d7aa7d99..0cfdd089 100644
--- a/doc/tinc.conf.5.in
+++ b/doc/tinc.conf.5.in
@@ -562,7 +562,7 @@ Multiple
.Va Address
variables can be specified, in which case each address will be tried until a working
connection has been established.
-.It Va Cipher Li = Ar cipher Pq blowfish
+.It Va Cipher Li = Ar cipher Pq aes-256-cbc
The symmetric cipher algorithm used to encrypt UDP packets.
Any cipher supported by LibreSSL or OpenSSL is recognised.
Furthermore, specifying
diff --git a/doc/tinc.texi b/doc/tinc.texi
index 2e519d1c..ab3dca23 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -1328,7 +1328,7 @@ Multiple Address variables can be specified, in which case each address will be
tried until a working connection has been established.
@cindex Cipher
-@item Cipher = <@var{cipher}> (blowfish)
+@item Cipher = <@var{cipher}> (aes-256-cbc)
The symmetric cipher algorithm used to encrypt UDP packets using the legacy protocol.
Any cipher supported by LibreSSL or OpenSSL is recognized.
Furthermore, specifying @samp{none} will turn off packet encryption.
--
2.36.0

View file

@ -0,0 +1,230 @@
From 4e64f72feb99b7933e907fb0fab93368749db779 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Sun, 22 Aug 2021 22:44:04 +0200
Subject: [PATCH 09/10] Make the ExperimentalProtocol option obsolete.
Remove mentions of it from the documentation, but keep supporting the
option for now, as this makes it easier to test compatibility with the
legacy protocol.
---
README.md | 8 ++++----
doc/tinc.conf.5.in | 18 +++---------------
doc/tinc.texi | 21 ++++++---------------
src/tincctl.c | 2 +-
test/integration/algorithms.py | 4 ++--
test/integration/legacy_protocol.py | 4 ++--
test/integration/splice.py | 4 ++--
7 files changed, 20 insertions(+), 41 deletions(-)
diff --git a/README.md b/README.md
index 11129f79..9e3a64a4 100644
--- a/README.md
+++ b/README.md
@@ -55,12 +55,12 @@ versions, the security might only be as good as that of the oldest version.
## Compatibility
-Version 1.1pre18 is compatible with 1.0pre8, 1.0 and later, but not with older
+Version 1.1pre18 is compatible with 1.0 and later, but not with older
versions of tinc.
-When the ExperimentalProtocol option is used, tinc is still compatible with
-1.0.X, 1.1pre11 and later, but not with any version between 1.1pre1 and
-1.1pre10.
+Note that this pre-release version of tinc 1.1 might be incompatible with older
+pre-release versions as the new cryptographic protocol might still undergo
+changes.
## Requirements
diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in
index 0cfdd089..a5a56ed5 100644
--- a/doc/tinc.conf.5.in
+++ b/doc/tinc.conf.5.in
@@ -287,15 +287,6 @@ When combined with the IndirectData option,
packets for nodes for which we do not have a meta connection with are also dropped.
.It Va Ed25519PrivateKeyFile Li = Ar filename Po Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa /ed25519_key.priv Pc
The file in which the private Ed25519 key of this tinc daemon resides.
-This is only used if
-.Va ExperimentalProtocol
-is enabled.
-.It Va ExperimentalProtocol Li = yes | no Pq yes
-When this option is enabled, the SPTPS protocol will be used when connecting to nodes that also support it.
-Ephemeral ECDH will be used for key exchanges,
-and Ed25519 will be used instead of RSA for authentication.
-When enabled, an Ed25519 key must have been generated before with
-.Nm tinc generate-ed25519-keys .
.It Va Forwarding Li = off | internal | kernel Po internal Pc Bq experimental
This option selects the way indirect packets are forwarded.
.Bl -tag -width indent
@@ -569,8 +560,7 @@ Furthermore, specifying
.Qq none
will turn off packet encryption.
It is best to use only those ciphers which support CBC mode.
-This option has no effect for connections between nodes using
-.Va ExperimentalProtocol .
+This option only affects communication using the legacy protocol.
.It Va ClampMSS Li = yes | no Pq yes
This option specifies whether tinc should clamp the maximum segment size (MSS)
of TCP packets to the path MTU. This helps in situations where ICMP
@@ -585,8 +575,7 @@ Any digest supported by LibreSSL or OpenSSL is recognised.
Furthermore, specifying
.Qq none
will turn off packet authentication.
-This option has no effect for connections between nodes using
-.Va ExperimentalProtocol .
+This option only affects communication using the legacy protocol.
.It Va IndirectData Li = yes | no Pq no
When set to yes, only nodes which already have a meta connection to you
will try to establish direct communication with you.
@@ -596,8 +585,7 @@ The length of the message authentication code used to authenticate UDP packets.
Can be anything from
.Qq 0
up to the length of the digest produced by the digest algorithm.
-This option has no effect for connections between nodes using
-.Va ExperimentalProtocol .
+This option only affects communication using the legacy protocol.
.It Va PMTU Li = Ar mtu Po 1514 Pc
This option controls the initial path MTU to this node.
.It Va PMTUDiscovery Li = yes | no Po yes Pc
diff --git a/doc/tinc.texi b/doc/tinc.texi
index ab3dca23..c1e62a52 100644
--- a/doc/tinc.texi
+++ b/doc/tinc.texi
@@ -1025,15 +1025,6 @@ packets for nodes for which we do not have a meta connection with are also dropp
@cindex Ed25519PrivateKeyFile
@item Ed25519PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/ed25519_key.priv})
The file in which the private Ed25519 key of this tinc daemon resides.
-This is only used if ExperimentalProtocol is enabled.
-
-@cindex ExperimentalProtocol
-@item ExperimentalProtocol = <yes|no> (yes)
-When this option is enabled, the SPTPS protocol will be used when connecting to nodes that also support it.
-Ephemeral ECDH will be used for key exchanges,
-and Ed25519 will be used instead of RSA for authentication.
-When enabled, an Ed25519 key must have been generated before with
-@command{tinc generate-ed25519-keys}.
@cindex Forwarding
@item Forwarding = <off|internal|kernel> (internal) [experimental]
@@ -1333,7 +1324,7 @@ The symmetric cipher algorithm used to encrypt UDP packets using the legacy prot
Any cipher supported by LibreSSL or OpenSSL is recognized.
Furthermore, specifying @samp{none} will turn off packet encryption.
It is best to use only those ciphers which support CBC mode.
-This option has no effect for connections using the SPTPS protocol, which always use AES-256-CTR.
+This option only affects communication using the legacy protocol.
@cindex ClampMSS
@item ClampMSS = <yes|no> (yes)
@@ -1352,7 +1343,7 @@ Possible values are 0 (off), 1 (fast zlib) and any integer up to 9 (best zlib),
The digest algorithm used to authenticate UDP packets using the legacy protocol.
Any digest supported by LibreSSL or OpenSSL is recognized.
Furthermore, specifying @samp{none} will turn off packet authentication.
-This option has no effect for connections using the SPTPS protocol, which always use HMAC-SHA-256.
+This option only affects communication using the legacy protocol.
@cindex IndirectData
@item IndirectData = <yes|no> (no)
@@ -1365,7 +1356,7 @@ It is best to leave this option out or set it to no.
The length of the message authentication code used to authenticate UDP packets using the legacy protocol.
Can be anything from 0
up to the length of the digest produced by the digest algorithm.
-This option has no effect for connections using the SPTPS protocol, which never truncate MACs.
+This option only affects communication using the legacy protocol.
@cindex PMTU
@item PMTU = <@var{mtu}> (1514)
@@ -3030,9 +3021,9 @@ Therefore, tinc also authenticates the data.
Finally, tinc uses sequence numbers (which themselves are also authenticated) to prevent an attacker from replaying valid packets.
Since version 1.1pre3, tinc has two protocols used to protect your data; the legacy protocol, and the new Simple Peer-to-Peer Security (SPTPS) protocol.
-The SPTPS protocol is designed to address some weaknesses in the legacy protocol.
-The new authentication protocol is used when two nodes connect to each other that both have the ExperimentalProtocol option set to yes,
-otherwise the legacy protocol will be used.
+The SPTPS protocol is designed to address some weaknesses in the legacy protocol,
+and is used automatically if both sides support it.
+Once two nodes have connected with the new protocol, rollback to the legacy protocol is not allowed.
@menu
* Legacy authentication protocol::
diff --git a/src/tincctl.c b/src/tincctl.c
index 9b39f2ce..2032b33a 100644
--- a/src/tincctl.c
+++ b/src/tincctl.c
@@ -1651,7 +1651,7 @@ const var_t variables[] = {
{"DeviceType", VAR_SERVER},
{"DirectOnly", VAR_SERVER | VAR_SAFE},
{"Ed25519PrivateKeyFile", VAR_SERVER},
- {"ExperimentalProtocol", VAR_SERVER},
+ {"ExperimentalProtocol", VAR_SERVER | VAR_OBSOLETE},
{"Forwarding", VAR_SERVER},
{"FWMark", VAR_SERVER},
{"GraphDumpFile", VAR_SERVER | VAR_OBSOLETE},
diff --git a/test/integration/algorithms.py b/test/integration/algorithms.py
index b056c7d5..52e0f820 100755
--- a/test/integration/algorithms.py
+++ b/test/integration/algorithms.py
@@ -23,7 +23,7 @@ def init(ctx: Test, digest: str, cipher: str) -> T.Tuple[Tinc, Tinc]:
set Digest {digest}
set Cipher {cipher}
"""
- foo.cmd(stdin=stdin)
+ foo.cmd("--force", stdin=stdin)
foo.start()
stdin = f"""
@@ -35,7 +35,7 @@ def init(ctx: Test, digest: str, cipher: str) -> T.Tuple[Tinc, Tinc]:
set Digest {digest}
set Cipher {cipher}
"""
- bar.cmd(stdin=stdin)
+ bar.cmd("--force", stdin=stdin)
foo.add_script(bar.script_up)
bar.add_script(foo.script_up)
diff --git a/test/integration/legacy_protocol.py b/test/integration/legacy_protocol.py
index 845ac345..f7ab1bd2 100755
--- a/test/integration/legacy_protocol.py
+++ b/test/integration/legacy_protocol.py
@@ -73,14 +73,14 @@ with Test("foo 1.1, bar 1.1") as context:
with Test("foo 1.1, bar 1.0") as context:
foo_node, bar_node = init(context)
- bar_node.cmd("set", "ExperimentalProtocol", "no")
+ bar_node.cmd("--force", "set", "ExperimentalProtocol", "no")
foo_node.cmd("del", f"{bar_node}.Ed25519PublicKey")
bar_node.cmd("del", f"{foo_node}.Ed25519PublicKey")
run_keys_test(foo_node, bar_node, empty=True)
with Test("bar 1.0 must not be allowed to connect") as context:
foo_node, bar_node = init(context)
- bar_node.cmd("set", "ExperimentalProtocol", "no")
+ bar_node.cmd("--force", "set", "ExperimentalProtocol", "no")
bar_up = bar_node.add_script(Script.SUBNET_UP)
bar_node.cmd("start")
diff --git a/test/integration/splice.py b/test/integration/splice.py
index 578845fb..868ffbc3 100755
--- a/test/integration/splice.py
+++ b/test/integration/splice.py
@@ -28,7 +28,7 @@ def init(ctx: Test, *options: str) -> T.Tuple[Tinc, Tinc]:
set Subnet 10.96.96.1
{custom}
"""
- foo.cmd(stdin=stdin)
+ foo.cmd("--force", stdin=stdin)
stdin = f"""
init {bar}
@@ -39,7 +39,7 @@ def init(ctx: Test, *options: str) -> T.Tuple[Tinc, Tinc]:
set Subnet 10.96.96.2
{custom}
"""
- bar.cmd(stdin=stdin)
+ bar.cmd("--force", stdin=stdin)
foo.add_script(Script.SUBNET_UP)
bar.add_script(Script.SUBNET_UP)
--
2.36.0

View file

@ -0,0 +1,322 @@
From f4db140a8ffc63b575181299c3964e4634606280 Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Tue, 31 Aug 2021 16:27:47 +0200
Subject: [PATCH 10/10] Move poly1305_get_tag() into poly1305.c, hide
poly1305_init().
The crypto library on Windows exposes a symbol named poly1305_init(),
which clashes with ours. We can avoid this by moving poly1305_get_tag()
to poly1305.[ch], where it belongs better, and this allows us to make
all the lower-level Poly1305 functions static.
Also remove the support for associated data while we are at it, since we
are never using it.
---
src/chacha-poly1305/chacha.h | 1 -
src/chacha-poly1305/chachapoly.c | 58 ++++----------------------------
src/chacha-poly1305/chachapoly.h | 6 ++--
src/chacha-poly1305/poly1305.c | 54 +++++++++++++++++++++++++----
src/chacha-poly1305/poly1305.h | 20 +----------
src/sptps.c | 4 +--
6 files changed, 58 insertions(+), 85 deletions(-)
diff --git a/src/chacha-poly1305/chacha.h b/src/chacha-poly1305/chacha.h
index a137ab6b..d4784f49 100644
--- a/src/chacha-poly1305/chacha.h
+++ b/src/chacha-poly1305/chacha.h
@@ -31,4 +31,3 @@ void chacha_encrypt_bytes(struct chacha_ctx *x, const unsigned char *m,
unsigned char *c, uint32_t bytes);
#endif /* CHACHA_H */
-
diff --git a/src/chacha-poly1305/chachapoly.c b/src/chacha-poly1305/chachapoly.c
index 9a6620ce..68f04edd 100644
--- a/src/chacha-poly1305/chachapoly.c
+++ b/src/chacha-poly1305/chachapoly.c
@@ -53,52 +53,6 @@ static int memcmp_eq(const void *av, const void *bv, int n) {
return res;
}
-/**
- * Poly1305 tag generation. This concatenates a string according to the rules
- * outlined in RFC 7539 and calculates the tag.
- *
- * \param poly_key 32 byte secret one-time key for poly1305
- * \param ad associated data
- * \param ad_len associated data length in bytes
- * \param ct ciphertext
- * \param ct_len ciphertext length in bytes
- * \param tag pointer to 16 bytes for tag storage
- */
-static void poly1305_get_tag(unsigned char *poly_key, const void *ad,
- int ad_len, const void *ct, int ct_len, unsigned char *tag) {
- struct poly1305_context poly;
- unsigned left_over;
- uint64_t len;
- unsigned char pad[16];
-
- poly1305_init(&poly, poly_key);
- memset(&pad, 0, sizeof(pad));
-
- /* associated data and padding */
- poly1305_update(&poly, ad, ad_len);
- left_over = ad_len % 16;
-
- if(left_over) {
- poly1305_update(&poly, pad, 16 - left_over);
- }
-
- /* payload and padding */
- poly1305_update(&poly, ct, ct_len);
- left_over = ct_len % 16;
-
- if(left_over) {
- poly1305_update(&poly, pad, 16 - left_over);
- }
-
- /* lengths */
- len = ad_len;
- poly1305_update(&poly, (unsigned char *)&len, 8);
- len = ct_len;
- poly1305_update(&poly, (unsigned char *)&len, 8);
-
- poly1305_finish(&poly, tag);
-}
-
int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len) {
assert(key_len == 128 || key_len == 256);
@@ -108,7 +62,7 @@ int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len) {
}
int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
- const void *ad, int ad_len, void *input, int input_len,
+ void *input, int input_len,
void *output, void *tag, int tag_len, int encrypt) {
unsigned char poly_key[CHACHA_BLOCKLEN];
unsigned char calc_tag[POLY1305_TAGLEN];
@@ -121,7 +75,7 @@ int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
/* check tag if decrypting */
if(encrypt == 0 && tag_len) {
- poly1305_get_tag(poly_key, ad, ad_len, input, input_len, calc_tag);
+ poly1305_get_tag(poly_key, input, input_len, calc_tag);
if(memcmp_eq(calc_tag, tag, tag_len) != 0) {
return CHACHAPOLY_INVALID_MAC;
@@ -135,7 +89,7 @@ int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
/* add tag if encrypting */
if(encrypt && tag_len) {
- poly1305_get_tag(poly_key, ad, ad_len, output, input_len, calc_tag);
+ poly1305_get_tag(poly_key, output, input_len, calc_tag);
memcpy(tag, calc_tag, tag_len);
}
@@ -143,7 +97,7 @@ int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
}
int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce,
- const void *ad, int ad_len, void *input, int input_len,
+ void *input, int input_len,
void *output, void *tag, int tag_len, int encrypt) {
unsigned char keystream[CHACHA_BLOCKLEN];
unsigned char calc_tag[POLY1305_TAGLEN];
@@ -159,7 +113,7 @@ int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce,
/* check tag if decrypting */
if(encrypt == 0 && tag_len) {
- poly1305_get_tag(keystream, ad, ad_len, input, input_len, calc_tag);
+ poly1305_get_tag(keystream, input, input_len, calc_tag);
if(memcmp_eq(calc_tag, tag, tag_len) != 0) {
return CHACHAPOLY_INVALID_MAC;
@@ -174,7 +128,7 @@ int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce,
/* add tag if encrypting */
if(encrypt && tag_len) {
- poly1305_get_tag(keystream, ad, ad_len, output, input_len, calc_tag);
+ poly1305_get_tag(keystream, output, input_len, calc_tag);
memcpy(tag, calc_tag, tag_len);
}
diff --git a/src/chacha-poly1305/chachapoly.h b/src/chacha-poly1305/chachapoly.h
index ffc9576d..5d01f525 100644
--- a/src/chacha-poly1305/chachapoly.h
+++ b/src/chacha-poly1305/chachapoly.h
@@ -52,8 +52,6 @@ int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len);
*
* \param ctx context data
* \param nonce nonce (12 bytes)
- * \param ad associated data
- * \param ad_len associated data length in bytes
* \param input plaintext/ciphertext input
* \param input_len input length in bytes;
* \param output plaintext/ciphertext output
@@ -65,7 +63,7 @@ int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len);
* failed when decrypting
*/
int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
- const void *ad, int ad_len, void *input, int input_len,
+ void *input, int input_len,
void *output, void *tag, int tag_len, int encrypt);
/**
@@ -76,7 +74,7 @@ int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce,
* chachapoly_crypt.
*/
int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce,
- const void *ad, int ad_len, void *input, int input_len,
+ void *input, int input_len,
void *output, void *tag, int tag_len, int encrypt);
#endif
diff --git a/src/chacha-poly1305/poly1305.c b/src/chacha-poly1305/poly1305.c
index 0c90564c..b25435a7 100644
--- a/src/chacha-poly1305/poly1305.c
+++ b/src/chacha-poly1305/poly1305.c
@@ -5,6 +5,20 @@ public domain
#include "poly1305.h"
+/* use memcpy() to copy blocks of memory (typically faster) */
+#define USE_MEMCPY 1
+/* use unaligned little-endian load/store (can be faster) */
+#define USE_UNALIGNED 0
+
+struct poly1305_context {
+ uint32_t r[5];
+ uint32_t h[5];
+ uint32_t pad[4];
+ size_t leftover;
+ unsigned char buffer[POLY1305_BLOCK_SIZE];
+ unsigned char final;
+};
+
#if (USE_UNALIGNED == 1)
#define U8TO32(p) \
(*((uint32_t *)(p)))
@@ -33,7 +47,7 @@ U32TO8(unsigned char *p, uint32_t v) {
}
#endif
-void
+static void
poly1305_init(struct poly1305_context *st, const unsigned char key[32]) {
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
st->r[0] = (U8TO32(&key[ 0])) & 0x3ffffff;
@@ -131,7 +145,7 @@ poly1305_blocks(struct poly1305_context *st, const unsigned char *m, size_t byte
st->h[4] = h4;
}
-void
+static void
poly1305_finish(struct poly1305_context *st, unsigned char mac[16]) {
uint32_t h0, h1, h2, h3, h4, c;
uint32_t g0, g1, g2, g3, g4;
@@ -241,8 +255,7 @@ poly1305_finish(struct poly1305_context *st, unsigned char mac[16]) {
st->pad[3] = 0;
}
-
-void
+static void
poly1305_update(struct poly1305_context *st, const unsigned char *m, size_t bytes) {
size_t i;
@@ -293,10 +306,37 @@ poly1305_update(struct poly1305_context *st, const unsigned char *m, size_t byte
}
}
+/**
+ * Poly1305 tag generation. This concatenates a string according to the rules
+ * outlined in RFC 7539 and calculates the tag.
+ *
+ * \param key 32 byte secret one-time key for poly1305
+ * \param ct ciphertext
+ * \param ct_len ciphertext length in bytes
+ * \param tag pointer to 16 bytes for tag storage
+ */
void
-poly1305_auth(unsigned char mac[16], const unsigned char *m, size_t bytes, const unsigned char key[32]) {
+poly1305_get_tag(const unsigned char key[32], const void *ct, int ct_len, unsigned char tag[16]) {
struct poly1305_context ctx;
+ unsigned left_over;
+ uint64_t len;
+ unsigned char pad[16];
+
poly1305_init(&ctx, key);
- poly1305_update(&ctx, m, bytes);
- poly1305_finish(&ctx, mac);
+ memset(&pad, 0, sizeof(pad));
+
+ /* payload and padding */
+ poly1305_update(&ctx, ct, ct_len);
+ left_over = ct_len % 16;
+
+ if(left_over) {
+ poly1305_update(&ctx, pad, 16 - left_over);
+ }
+
+ /* lengths */
+ len = 0;
+ poly1305_update(&ctx, (unsigned char *)&len, 8);
+ len = ct_len;
+ poly1305_update(&ctx, (unsigned char *)&len, 8);
+ poly1305_finish(&ctx, tag);
}
diff --git a/src/chacha-poly1305/poly1305.h b/src/chacha-poly1305/poly1305.h
index 624a19a9..5fc3b903 100644
--- a/src/chacha-poly1305/poly1305.h
+++ b/src/chacha-poly1305/poly1305.h
@@ -9,24 +9,6 @@
#define POLY1305_TAGLEN 16
#define POLY1305_BLOCK_SIZE 16
-/* use memcpy() to copy blocks of memory (typically faster) */
-#define USE_MEMCPY 1
-/* use unaligned little-endian load/store (can be faster) */
-#define USE_UNALIGNED 0
-
-struct poly1305_context {
- uint32_t r[5];
- uint32_t h[5];
- uint32_t pad[4];
- size_t leftover;
- unsigned char buffer[POLY1305_BLOCK_SIZE];
- unsigned char final;
-};
-
-void poly1305_init(struct poly1305_context *ctx, const unsigned char key[32]);
-void poly1305_update(struct poly1305_context *ctx, const unsigned char *m, size_t bytes);
-void poly1305_finish(struct poly1305_context *ctx, unsigned char mac[16]);
-void poly1305_auth(unsigned char mac[16], const unsigned char *m, size_t bytes, const unsigned char key[32]);
+void poly1305_get_tag(const unsigned char key[32], const void *ct, int ct_len, unsigned char tag[16]);
#endif /* POLY1305_H */
-
diff --git a/src/sptps.c b/src/sptps.c
index 7c8d20b7..8f713fe6 100644
--- a/src/sptps.c
+++ b/src/sptps.c
@@ -168,7 +168,7 @@ static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8
#ifndef HAVE_OPENSSL
case SPTPS_CHACHA_POLY1305: {
- if(chachapoly_crypt(ctx, nonce, NULL, 0, (void *)in, inlen, out, out + inlen, 16, 1) != CHACHAPOLY_OK) {
+ if(chachapoly_crypt(ctx, nonce, (void *)in, inlen, out, out + inlen, 16, 1) != CHACHAPOLY_OK) {
return false;
}
@@ -249,7 +249,7 @@ static bool cipher_decrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8
#ifndef HAVE_OPENSSL
case SPTPS_CHACHA_POLY1305:
- if(chachapoly_crypt(ctx, nonce, NULL, 0, (void *)in, inlen, out, (void *)(in + inlen), 16, 0) != CHACHAPOLY_OK) {
+ if(chachapoly_crypt(ctx, nonce, (void *)in, inlen, out, (void *)(in + inlen), 16, 0) != CHACHAPOLY_OK) {
return false;
}
--
2.36.0

10
debian/patches/series vendored
View file

@ -1 +1,11 @@
alt-ciphersuite/0001-Add-AES-256-GCM-support-to-SPTPS.patch
alt-ciphersuite/0002-Add-cipher-suite-selection-options-to-sptps_test.patch
alt-ciphersuite/0003-Let-sptps_speed-benchmark-all-cipher-suites.patch
alt-ciphersuite/0004-If-we-link-with-OpenSSL-use-it-for-Chacha20-Poly1305.patch
alt-ciphersuite/0005-Update-the-built-in-Chacha20-Poly1305-code-to-an-RFC.patch
alt-ciphersuite/0006-Ensure-we-are-compatible-with-LibreSSL.patch
alt-ciphersuite/0007-Fix-infinite-loop-on-SPTPS-errors-when-running-sptps.patch
alt-ciphersuite/0008-Fix-documentation-of-default-cipher-algorithm-used-f.patch
alt-ciphersuite/0009-Make-the-ExperimentalProtocol-option-obsolete.patch
alt-ciphersuite/0010-Move-poly1305_get_tag-into-poly1305.c-hide-poly1305_.patch
fix-version-number