diff --git a/debian/patches/alt-ciphersuite/0001-Add-AES-256-GCM-support-to-SPTPS.patch b/debian/patches/alt-ciphersuite/0001-Add-AES-256-GCM-support-to-SPTPS.patch new file mode 100644 index 0000000..c5bf717 --- /dev/null +++ b/debian/patches/alt-ciphersuite/0001-Add-AES-256-GCM-support-to-SPTPS.patch @@ -0,0 +1,879 @@ +From cc521f3d5f3a0c758163c871e75f5e533e86771b Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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, ¶ms)) { + 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, ¶ms); + } + + /* 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, ¶ms); + } 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, ¶ms); + } + + 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, ¶ms); + 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 ++#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, ¶ms)) { + free(mykey); + free(hiskey); + return 1; +-- +2.36.0 + diff --git a/debian/patches/alt-ciphersuite/0002-Add-cipher-suite-selection-options-to-sptps_test.patch b/debian/patches/alt-ciphersuite/0002-Add-cipher-suite-selection-options-to-sptps_test.patch new file mode 100644 index 0000000..6bcb80e --- /dev/null +++ b/debian/patches/alt-ciphersuite/0002-Add-cipher-suite-selection-options-to-sptps_test.patch @@ -0,0 +1,91 @@ +From 1d0eea4899f9642a3945c07b9266e660b9f9ce71 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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, ¶ms)) { +-- +2.36.0 + diff --git a/debian/patches/alt-ciphersuite/0003-Let-sptps_speed-benchmark-all-cipher-suites.patch b/debian/patches/alt-ciphersuite/0003-Let-sptps_speed-benchmark-all-cipher-suites.patch new file mode 100644 index 0000000..c5b8fc6 --- /dev/null +++ b/debian/patches/alt-ciphersuite/0003-Let-sptps_speed-benchmark-all-cipher-suites.patch @@ -0,0 +1,285 @@ +From c0f0610037847ea2abae1e7a826d36a55f9dfa36 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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, ¶ms1); ++ sptps_start(&sptps2, ¶ms2); + +- 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, ¶ms1); ++ sptps_start(&sptps2, ¶ms2); + + 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, ¶ms1); ++ sptps_start(&sptps2, ¶ms2); + +- 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, ¶ms1); ++ sptps_start(&sptps2, ¶ms2); + + 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 + diff --git a/debian/patches/alt-ciphersuite/0004-If-we-link-with-OpenSSL-use-it-for-Chacha20-Poly1305.patch b/debian/patches/alt-ciphersuite/0004-If-we-link-with-OpenSSL-use-it-for-Chacha20-Poly1305.patch new file mode 100644 index 0000000..e6288e5 --- /dev/null +++ b/debian/patches/alt-ciphersuite/0004-If-we-link-with-OpenSSL-use-it-for-Chacha20-Poly1305.patch @@ -0,0 +1,219 @@ +From 9d423c31024e37655aac014662cb5bee82c26464 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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 + diff --git a/debian/patches/alt-ciphersuite/0005-Update-the-built-in-Chacha20-Poly1305-code-to-an-RFC.patch b/debian/patches/alt-ciphersuite/0005-Update-the-built-in-Chacha20-Poly1305-code-to-an-RFC.patch new file mode 100644 index 0000000..ea7574d --- /dev/null +++ b/debian/patches/alt-ciphersuite/0005-Update-the-built-in-Chacha20-Poly1305-code-to-an-RFC.patch @@ -0,0 +1,1295 @@ +From c0f2a9d37077abeaf01c403bf60eb018349d3de4 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +Date: Mon, 9 Aug 2021 22:30:42 +0200 +Subject: [PATCH 05/10] Update the built-in Chacha20-Poly1305 code to an RFC + 7539 complaint version. + +This is necessary so our copy of Chacha20-Poly1305 is compatible with that +of many other crypto libraries. + +This code is made by Grigori Goronz, but is heavily based on the code from +D.J. Bernstein's ref10 implementation used before. +--- + src/chacha-poly1305/chacha-poly1305.c | 104 ------- + src/chacha-poly1305/chacha-poly1305.h | 15 - + src/chacha-poly1305/chacha.c | 50 ++- + src/chacha-poly1305/chacha.h | 28 +- + src/chacha-poly1305/chachapoly.c | 182 +++++++++++ + src/chacha-poly1305/chachapoly.h | 82 +++++ + src/chacha-poly1305/meson.build | 2 +- + src/chacha-poly1305/poly1305.c | 427 ++++++++++++++++---------- + src/chacha-poly1305/poly1305.h | 38 ++- + src/sptps.c | 50 ++- + src/sptps.h | 4 +- + 11 files changed, 643 insertions(+), 339 deletions(-) + delete mode 100644 src/chacha-poly1305/chacha-poly1305.c + delete mode 100644 src/chacha-poly1305/chacha-poly1305.h + create mode 100644 src/chacha-poly1305/chachapoly.c + create mode 100644 src/chacha-poly1305/chachapoly.h + +diff --git a/src/chacha-poly1305/chacha-poly1305.c b/src/chacha-poly1305/chacha-poly1305.c +deleted file mode 100644 +index d13fec41..00000000 +--- a/src/chacha-poly1305/chacha-poly1305.c ++++ /dev/null +@@ -1,104 +0,0 @@ +-#include "../system.h" +-#include "../xalloc.h" +- +-#include "chacha.h" +-#include "chacha-poly1305.h" +-#include "poly1305.h" +- +-struct chacha_poly1305_ctx { +- struct chacha_ctx main_ctx, header_ctx; +-}; +- +-chacha_poly1305_ctx_t *chacha_poly1305_init(void) { +- chacha_poly1305_ctx_t *ctx = xzalloc(sizeof(*ctx)); +- return ctx; +-} +- +-void chacha_poly1305_exit(chacha_poly1305_ctx_t *ctx) { +- free(ctx); +-} +- +-bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *vkey) { +- const uint8_t *key = vkey; +- chacha_keysetup(&ctx->main_ctx, key, 256); +- chacha_keysetup(&ctx->header_ctx, key + 32, 256); +- return true; +-} +- +-static void put_u64(void *vp, uint64_t v) { +- uint8_t *p = (uint8_t *) vp; +- +- p[0] = (uint8_t)(v >> 56) & 0xff; +- p[1] = (uint8_t)(v >> 48) & 0xff; +- p[2] = (uint8_t)(v >> 40) & 0xff; +- p[3] = (uint8_t)(v >> 32) & 0xff; +- p[4] = (uint8_t)(v >> 24) & 0xff; +- p[5] = (uint8_t)(v >> 16) & 0xff; +- p[6] = (uint8_t)(v >> 8) & 0xff; +- p[7] = (uint8_t) v & 0xff; +-} +- +-bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *voutdata, size_t *outlen) { +- uint8_t seqbuf[8]; +- const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ +- uint8_t poly_key[POLY1305_KEYLEN]; +- uint8_t *outdata = voutdata; +- +- /* +- * Run ChaCha20 once to generate the Poly1305 key. The IV is the +- * packet sequence number. +- */ +- memset(poly_key, 0, sizeof(poly_key)); +- put_u64(seqbuf, seqnr); +- chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); +- chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key)); +- +- /* Set Chacha's block counter to 1 */ +- chacha_ivsetup(&ctx->main_ctx, seqbuf, one); +- +- chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen); +- poly1305_auth(outdata + inlen, outdata, inlen, poly_key); +- +- if(outlen) { +- *outlen = inlen + POLY1305_TAGLEN; +- } +- +- return true; +-} +- +-bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *vindata, size_t inlen, void *outdata, size_t *outlen) { +- uint8_t seqbuf[8]; +- const uint8_t one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */ +- uint8_t expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN]; +- const uint8_t *indata = vindata; +- +- /* +- * Run ChaCha20 once to generate the Poly1305 key. The IV is the +- * packet sequence number. +- */ +- memset(poly_key, 0, sizeof(poly_key)); +- put_u64(seqbuf, seqnr); +- chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL); +- chacha_encrypt_bytes(&ctx->main_ctx, poly_key, poly_key, sizeof(poly_key)); +- +- /* Set Chacha's block counter to 1 */ +- chacha_ivsetup(&ctx->main_ctx, seqbuf, one); +- +- /* Check tag before anything else */ +- inlen -= POLY1305_TAGLEN; +- const uint8_t *tag = indata + inlen; +- +- poly1305_auth(expected_tag, indata, inlen, poly_key); +- +- if(memcmp(expected_tag, tag, POLY1305_TAGLEN)) { +- return false; +- } +- +- chacha_encrypt_bytes(&ctx->main_ctx, indata, outdata, inlen); +- +- if(outlen) { +- *outlen = inlen; +- } +- +- return true; +-} +diff --git a/src/chacha-poly1305/chacha-poly1305.h b/src/chacha-poly1305/chacha-poly1305.h +deleted file mode 100644 +index af7eaf5e..00000000 +--- a/src/chacha-poly1305/chacha-poly1305.h ++++ /dev/null +@@ -1,15 +0,0 @@ +-#ifndef CHACHA_POLY1305_H +-#define CHACHA_POLY1305_H +- +-#define CHACHA_POLY1305_KEYLEN 64 +- +-typedef struct chacha_poly1305_ctx chacha_poly1305_ctx_t; +- +-extern chacha_poly1305_ctx_t *chacha_poly1305_init(void); +-extern void chacha_poly1305_exit(chacha_poly1305_ctx_t *); +-extern bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const void *key); +- +-extern bool chacha_poly1305_encrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen); +-extern bool chacha_poly1305_decrypt(chacha_poly1305_ctx_t *ctx, uint64_t seqnr, const void *indata, size_t inlen, void *outdata, size_t *outlen); +- +-#endif //CHACHA_POLY1305_H +diff --git a/src/chacha-poly1305/chacha.c b/src/chacha-poly1305/chacha.c +index 696f44a5..4452aca2 100644 +--- a/src/chacha-poly1305/chacha.c ++++ b/src/chacha-poly1305/chacha.c +@@ -4,27 +4,30 @@ D. J. Bernstein + Public domain. + */ + +-#include "../system.h" +- + #include "chacha.h" + +-typedef struct chacha_ctx chacha_ctx; +- + #define U8C(v) (v##U) + #define U32C(v) (v##U) + +-#define U8V(v) ((uint8_t)(v) & U8C(0xFF)) ++#define U8V(v) ((unsigned char)(v) & U8C(0xFF)) + #define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF)) + + #define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + ++#if (USE_UNALIGNED == 1) ++#define U8TO32_LITTLE(p) \ ++ (*((uint32_t *)(p))) ++#define U32TO8_LITTLE(p, v) \ ++ do { \ ++ *((uint32_t *)(p)) = v; \ ++ } while (0) ++#else + #define U8TO32_LITTLE(p) \ + (((uint32_t)((p)[0]) ) | \ + ((uint32_t)((p)[1]) << 8) | \ + ((uint32_t)((p)[2]) << 16) | \ + ((uint32_t)((p)[3]) << 24)) +- + #define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ +@@ -32,6 +35,7 @@ typedef struct chacha_ctx chacha_ctx; + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) ++#endif + + #define ROTATE(v,c) (ROTL32(v,c)) + #define XOR(v,w) ((v) ^ (w)) +@@ -47,7 +51,8 @@ typedef struct chacha_ctx chacha_ctx; + static const char sigma[16] = "expand 32-byte k"; + static const char tau[16] = "expand 16-byte k"; + +-void chacha_keysetup(chacha_ctx *x, const uint8_t *k, uint32_t kbits) { ++void ++chacha_keysetup(struct chacha_ctx *x, const unsigned char *k, uint32_t kbits) { + const char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); +@@ -55,10 +60,10 @@ void chacha_keysetup(chacha_ctx *x, const uint8_t *k, uint32_t kbits) { + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + +- if(kbits == 256) { /* recommended */ ++ if(kbits == 256) { /* recommended */ + k += 16; + constants = sigma; +- } else { /* kbits == 128 */ ++ } else { /* kbits == 128 */ + constants = tau; + } + +@@ -72,19 +77,21 @@ void chacha_keysetup(chacha_ctx *x, const uint8_t *k, uint32_t kbits) { + x->input[3] = U8TO32_LITTLE(constants + 12); + } + +-void chacha_ivsetup(chacha_ctx *x, const uint8_t *iv, const uint8_t *counter) { ++void ++chacha_ivsetup(struct chacha_ctx *x, const unsigned char *iv, const unsigned char *counter) { + x->input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0); +- x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4); +- x->input[14] = U8TO32_LITTLE(iv + 0); +- x->input[15] = U8TO32_LITTLE(iv + 4); ++ //x->input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4); ++ x->input[13] = U8TO32_LITTLE(iv + 0); ++ x->input[14] = U8TO32_LITTLE(iv + 4); ++ x->input[15] = U8TO32_LITTLE(iv + 8); + } + + void +-chacha_encrypt_bytes(chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes) { ++chacha_encrypt_bytes(struct chacha_ctx *x, const unsigned char *m, unsigned char *c, uint32_t bytes) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; +- uint8_t *ctarget = NULL; +- uint8_t tmp[64]; ++ unsigned char *ctarget = NULL; ++ unsigned char tmp[64]; + uint32_t i; + + if(!bytes) { +@@ -110,10 +117,15 @@ chacha_encrypt_bytes(chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes + + for(;;) { + if(bytes < 64) { ++#if (USE_MEMCPY == 1) ++ memcpy(tmp, m, bytes); ++#else ++ + for(i = 0; i < bytes; ++i) { + tmp[i] = m[i]; + } + ++#endif + m = tmp; + ctarget = c; + c = tmp; +@@ -207,9 +219,15 @@ chacha_encrypt_bytes(chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes + + if(bytes <= 64) { + if(bytes < 64) { ++#if (USE_MEMCPY == 1) ++ memcpy(ctarget, c, bytes); ++#else ++ + for(i = 0; i < bytes; ++i) { + ctarget[i] = c[i]; + } ++ ++#endif + } + + x->input[12] = j12; +diff --git a/src/chacha-poly1305/chacha.h b/src/chacha-poly1305/chacha.h +index 103c3d81..a137ab6b 100644 +--- a/src/chacha-poly1305/chacha.h ++++ b/src/chacha-poly1305/chacha.h +@@ -7,18 +7,28 @@ Public domain. + #ifndef CHACHA_H + #define CHACHA_H + ++#include ++#include ++#include ++#include ++ ++#define CHACHA_BLOCKLEN 64 ++ ++/* 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 chacha_ctx { + uint32_t input[16]; + }; + +-#define CHACHA_MINKEYLEN 16 +-#define CHACHA_NONCELEN 8 +-#define CHACHA_CTRLEN 8 +-#define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN) +-#define CHACHA_BLOCKLEN 64 ++void chacha_keysetup(struct chacha_ctx *x, const unsigned char *k, ++ uint32_t kbits); ++void chacha_ivsetup(struct chacha_ctx *x, const unsigned char *iv, ++ const unsigned char *ctr); ++void chacha_encrypt_bytes(struct chacha_ctx *x, const unsigned char *m, ++ unsigned char *c, uint32_t bytes); + +-void chacha_keysetup(struct chacha_ctx *x, const uint8_t *k, uint32_t kbits); +-void chacha_ivsetup(struct chacha_ctx *x, const uint8_t *iv, const uint8_t *ctr); +-void chacha_encrypt_bytes(struct chacha_ctx *x, const uint8_t *m, uint8_t *c, uint32_t bytes); ++#endif /* CHACHA_H */ + +-#endif /* CHACHA_H */ +diff --git a/src/chacha-poly1305/chachapoly.c b/src/chacha-poly1305/chachapoly.c +new file mode 100644 +index 00000000..9a6620ce +--- /dev/null ++++ b/src/chacha-poly1305/chachapoly.c +@@ -0,0 +1,182 @@ ++/* ++ * The MIT License (MIT) ++ * ++ * Copyright (c) 2015 Grigori Goronzy ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in all ++ * copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "chachapoly.h" ++ ++/** ++ * Constant-time memory compare. This should help to protect against ++ * side-channel attacks. ++ * ++ * \param av input 1 ++ * \param bv input 2 ++ * \param n bytes to compare ++ * \return 0 if inputs are equal ++ */ ++static int memcmp_eq(const void *av, const void *bv, int n) { ++ const unsigned char *a = (const unsigned char *) av; ++ const unsigned char *b = (const unsigned char *) bv; ++ unsigned char res = 0; ++ int i; ++ ++ for(i = 0; i < n; i++) { ++ res |= *a ^ *b; ++ a++; ++ b++; ++ } ++ ++ 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); ++ ++ memset(ctx, 0, sizeof(*ctx)); ++ chacha_keysetup(&ctx->cha_ctx, key, key_len); ++ return CHACHAPOLY_OK; ++} ++ ++int chachapoly_crypt(struct chachapoly_ctx *ctx, const void *nonce, ++ const void *ad, int ad_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]; ++ const unsigned char one[4] = { 1, 0, 0, 0 }; ++ ++ /* initialize keystream and generate poly1305 key */ ++ memset(poly_key, 0, sizeof(poly_key)); ++ chacha_ivsetup(&ctx->cha_ctx, nonce, NULL); ++ chacha_encrypt_bytes(&ctx->cha_ctx, poly_key, poly_key, sizeof(poly_key)); ++ ++ /* check tag if decrypting */ ++ if(encrypt == 0 && tag_len) { ++ poly1305_get_tag(poly_key, ad, ad_len, input, input_len, calc_tag); ++ ++ if(memcmp_eq(calc_tag, tag, tag_len) != 0) { ++ return CHACHAPOLY_INVALID_MAC; ++ } ++ } ++ ++ /* crypt data */ ++ chacha_ivsetup(&ctx->cha_ctx, nonce, one); ++ chacha_encrypt_bytes(&ctx->cha_ctx, (unsigned char *)input, ++ (unsigned char *)output, input_len); ++ ++ /* add tag if encrypting */ ++ if(encrypt && tag_len) { ++ poly1305_get_tag(poly_key, ad, ad_len, output, input_len, calc_tag); ++ memcpy(tag, calc_tag, tag_len); ++ } ++ ++ return CHACHAPOLY_OK; ++} ++ ++int chachapoly_crypt_short(struct chachapoly_ctx *ctx, const void *nonce, ++ const void *ad, int ad_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]; ++ int i; ++ ++ assert(input_len <= 32); ++ ++ /* initialize keystream and generate poly1305 key */ ++ memset(keystream, 0, sizeof(keystream)); ++ chacha_ivsetup(&ctx->cha_ctx, nonce, NULL); ++ chacha_encrypt_bytes(&ctx->cha_ctx, keystream, keystream, ++ sizeof(keystream)); ++ ++ /* check tag if decrypting */ ++ if(encrypt == 0 && tag_len) { ++ poly1305_get_tag(keystream, ad, ad_len, input, input_len, calc_tag); ++ ++ if(memcmp_eq(calc_tag, tag, tag_len) != 0) { ++ return CHACHAPOLY_INVALID_MAC; ++ } ++ } ++ ++ /* crypt data */ ++ for(i = 0; i < input_len; i++) { ++ ((unsigned char *)output)[i] = ++ ((unsigned char *)input)[i] ^ keystream[32 + i]; ++ } ++ ++ /* add tag if encrypting */ ++ if(encrypt && tag_len) { ++ poly1305_get_tag(keystream, ad, ad_len, output, input_len, calc_tag); ++ memcpy(tag, calc_tag, tag_len); ++ } ++ ++ return CHACHAPOLY_OK; ++} +diff --git a/src/chacha-poly1305/chachapoly.h b/src/chacha-poly1305/chachapoly.h +new file mode 100644 +index 00000000..ffc9576d +--- /dev/null ++++ b/src/chacha-poly1305/chachapoly.h +@@ -0,0 +1,82 @@ ++/* ++ * The MIT License (MIT) ++ * ++ * Copyright (c) 2015 Grigori Goronzy ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in all ++ * copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ */ ++ ++#ifndef CHACHAPOLY_H ++#define CHACHAPOLY_H ++ ++#include "chacha.h" ++#include "poly1305.h" ++ ++#define CHACHAPOLY_OK 0 ++#define CHACHAPOLY_INVALID_MAC -1 ++ ++struct chachapoly_ctx { ++ struct chacha_ctx cha_ctx; ++}; ++ ++/** ++ * Initialize ChaCha20-Poly1305 AEAD. ++ * For RFC 7539 conformant AEAD, 256 bit keys must be used. ++ * ++ * \param ctx context data ++ * \param key 16 or 32 bytes of key material ++ * \param key_len key length, 256 or 512 bits ++ * \return success if 0 ++ */ ++int chachapoly_init(struct chachapoly_ctx *ctx, const void *key, int key_len); ++ ++/** ++ * Encrypt or decrypt with ChaCha20-Poly1305. The AEAD construction conforms ++ * to RFC 7539. ++ * ++ * \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 ++ * \param tag tag output ++ * \param tag_len tag length in bytes (0-16); ++ if 0, authentification is skipped ++ * \param encrypt decrypt if 0, else encrypt ++ * \return CHACHAPOLY_OK if no error, CHACHAPOLY_INVALID_MAC if auth ++ * 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 *output, void *tag, int tag_len, int encrypt); ++ ++/** ++ * Encrypt or decrypt with Chacha20-Poly1305 for short messages. ++ * The AEAD construction is different from chachapoly_crypt, but more ++ * efficient for small messages. Up to 32 bytes can be encrypted. The size ++ * of associated data is not restricted. The interface is similar to ++ * 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 *output, void *tag, int tag_len, int encrypt); ++ ++#endif +diff --git a/src/chacha-poly1305/meson.build b/src/chacha-poly1305/meson.build +index d8fd74cc..60a20ab3 100644 +--- a/src/chacha-poly1305/meson.build ++++ b/src/chacha-poly1305/meson.build +@@ -1,5 +1,5 @@ + src_chacha_poly = files( +- 'chacha-poly1305.c', ++ 'chachapoly.c', + 'chacha.c', + 'poly1305.c', + ) +diff --git a/src/chacha-poly1305/poly1305.c b/src/chacha-poly1305/poly1305.c +index 4d99b8c3..0c90564c 100644 +--- a/src/chacha-poly1305/poly1305.c ++++ b/src/chacha-poly1305/poly1305.c +@@ -1,205 +1,302 @@ + /* +- * Public Domain poly1305 from Andrew Moon +- * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna +- */ +- +-#include "../system.h" ++poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition ++public domain ++*/ + + #include "poly1305.h" + +-#define mul32x32_64(a,b) ((uint64_t)(a) * (b)) +- +-#define U8TO32_LE(p) \ +- (((uint32_t)((p)[0])) | \ +- ((uint32_t)((p)[1]) << 8) | \ +- ((uint32_t)((p)[2]) << 16) | \ +- ((uint32_t)((p)[3]) << 24)) +- +-#define U32TO8_LE(p, v) \ ++#if (USE_UNALIGNED == 1) ++#define U8TO32(p) \ ++ (*((uint32_t *)(p))) ++#define U32TO8(p, v) \ + do { \ +- (p)[0] = (uint8_t)((v)); \ +- (p)[1] = (uint8_t)((v) >> 8); \ +- (p)[2] = (uint8_t)((v) >> 16); \ +- (p)[3] = (uint8_t)((v) >> 24); \ ++ *((uint32_t *)(p)) = v; \ + } while (0) ++#else ++/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ ++static uint32_t ++U8TO32(const unsigned char *p) { ++ return ++ (((uint32_t)(p[0] & 0xff)) | ++ ((uint32_t)(p[1] & 0xff) << 8) | ++ ((uint32_t)(p[2] & 0xff) << 16) | ++ ((uint32_t)(p[3] & 0xff) << 24)); ++} ++ ++/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ ++static void ++U32TO8(unsigned char *p, uint32_t v) { ++ p[0] = (v) & 0xff; ++ p[1] = (v >> 8) & 0xff; ++ p[2] = (v >> 16) & 0xff; ++ p[3] = (v >> 24) & 0xff; ++} ++#endif + + void +-poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) { +- uint32_t t0, t1, t2, t3; +- uint32_t h0, h1, h2, h3, h4; ++poly1305_init(struct poly1305_context *st, const unsigned char key[32]) { ++ /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ ++ st->r[0] = (U8TO32(&key[ 0])) & 0x3ffffff; ++ st->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03; ++ st->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff; ++ st->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff; ++ st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff; ++ ++ /* h = 0 */ ++ st->h[0] = 0; ++ st->h[1] = 0; ++ st->h[2] = 0; ++ st->h[3] = 0; ++ st->h[4] = 0; ++ ++ /* save pad for later */ ++ st->pad[0] = U8TO32(&key[16]); ++ st->pad[1] = U8TO32(&key[20]); ++ st->pad[2] = U8TO32(&key[24]); ++ st->pad[3] = U8TO32(&key[28]); ++ ++ st->leftover = 0; ++ st->final = 0; ++} ++ ++static void ++poly1305_blocks(struct poly1305_context *st, const unsigned char *m, size_t bytes) { ++ const uint32_t hibit = (st->final) ? 0 : (1 << 24); /* 1 << 128 */ + uint32_t r0, r1, r2, r3, r4; + uint32_t s1, s2, s3, s4; +- uint32_t b, nb; +- size_t j; +- uint64_t t[5]; +- uint64_t f0, f1, f2, f3; +- uint32_t g0, g1, g2, g3, g4; +- uint64_t c; +- unsigned char mp[16]; +- +- /* clamp key */ +- t0 = U8TO32_LE(key + 0); +- t1 = U8TO32_LE(key + 4); +- t2 = U8TO32_LE(key + 8); +- t3 = U8TO32_LE(key + 12); +- +- /* precompute multipliers */ +- r0 = t0 & 0x3ffffff; +- t0 >>= 26; +- t0 |= t1 << 6; +- r1 = t0 & 0x3ffff03; +- t1 >>= 20; +- t1 |= t2 << 12; +- r2 = t1 & 0x3ffc0ff; +- t2 >>= 14; +- t2 |= t3 << 18; +- r3 = t2 & 0x3f03fff; +- t3 >>= 8; +- r4 = t3 & 0x00fffff; ++ uint32_t h0, h1, h2, h3, h4; ++ uint64_t d0, d1, d2, d3, d4; ++ uint32_t c; ++ ++ r0 = st->r[0]; ++ r1 = st->r[1]; ++ r2 = st->r[2]; ++ r3 = st->r[3]; ++ r4 = st->r[4]; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + +- /* init state */ +- h0 = 0; +- h1 = 0; +- h2 = 0; +- h3 = 0; +- h4 = 0; +- +- /* full blocks */ +- if(inlen < 16) { +- goto poly1305_donna_atmost15bytes; +- } ++ h0 = st->h[0]; ++ h1 = st->h[1]; ++ h2 = st->h[2]; ++ h3 = st->h[3]; ++ h4 = st->h[4]; + +-poly1305_donna_16bytes: +- m += 16; +- inlen -= 16; +- +- t0 = U8TO32_LE(m - 16); +- t1 = U8TO32_LE(m - 12); +- t2 = U8TO32_LE(m - 8); +- t3 = U8TO32_LE(m - 4); +- +- h0 += t0 & 0x3ffffff; +- h1 += ((((uint64_t) t1 << 32) | t0) >> 26) & 0x3ffffff; +- h2 += ((((uint64_t) t2 << 32) | t1) >> 20) & 0x3ffffff; +- h3 += ((((uint64_t) t3 << 32) | t2) >> 14) & 0x3ffffff; +- h4 += (t3 >> 8) | (1 << 24); +- +-poly1305_donna_mul: +- t[0] = mul32x32_64(h0, r0) + mul32x32_64(h1, s4) + mul32x32_64(h2, s3) + mul32x32_64(h3, s2) + mul32x32_64(h4, s1); +- t[1] = mul32x32_64(h0, r1) + mul32x32_64(h1, r0) + mul32x32_64(h2, s4) + mul32x32_64(h3, s3) + mul32x32_64(h4, s2); +- t[2] = mul32x32_64(h0, r2) + mul32x32_64(h1, r1) + mul32x32_64(h2, r0) + mul32x32_64(h3, s4) + mul32x32_64(h4, s3); +- t[3] = mul32x32_64(h0, r3) + mul32x32_64(h1, r2) + mul32x32_64(h2, r1) + mul32x32_64(h3, r0) + mul32x32_64(h4, s4); +- t[4] = mul32x32_64(h0, r4) + mul32x32_64(h1, r3) + mul32x32_64(h2, r2) + mul32x32_64(h3, r1) + mul32x32_64(h4, r0); +- +- h0 = (uint32_t) t[0] & 0x3ffffff; +- c = (t[0] >> 26); +- t[1] += c; +- h1 = (uint32_t) t[1] & 0x3ffffff; +- b = (uint32_t)(t[1] >> 26); +- t[2] += b; +- h2 = (uint32_t) t[2] & 0x3ffffff; +- b = (uint32_t)(t[2] >> 26); +- t[3] += b; +- h3 = (uint32_t) t[3] & 0x3ffffff; +- b = (uint32_t)(t[3] >> 26); +- t[4] += b; +- h4 = (uint32_t) t[4] & 0x3ffffff; +- b = (uint32_t)(t[4] >> 26); +- h0 += b * 5; +- +- if(inlen >= 16) { +- goto poly1305_donna_16bytes; +- } ++ while(bytes >= POLY1305_BLOCK_SIZE) { ++ /* h += m[i] */ ++ h0 += (U8TO32(m + 0)) & 0x3ffffff; ++ h1 += (U8TO32(m + 3) >> 2) & 0x3ffffff; ++ h2 += (U8TO32(m + 6) >> 4) & 0x3ffffff; ++ h3 += (U8TO32(m + 9) >> 6) & 0x3ffffff; ++ h4 += (U8TO32(m + 12) >> 8) | hibit; + +- /* final bytes */ +-poly1305_donna_atmost15bytes: ++ /* h *= r */ ++ d0 = ((uint64_t)h0 * r0) + ((uint64_t)h1 * s4) + ((uint64_t)h2 * s3) + ((uint64_t)h3 * s2) + ((uint64_t)h4 * s1); ++ d1 = ((uint64_t)h0 * r1) + ((uint64_t)h1 * r0) + ((uint64_t)h2 * s4) + ((uint64_t)h3 * s3) + ((uint64_t)h4 * s2); ++ d2 = ((uint64_t)h0 * r2) + ((uint64_t)h1 * r1) + ((uint64_t)h2 * r0) + ((uint64_t)h3 * s4) + ((uint64_t)h4 * s3); ++ d3 = ((uint64_t)h0 * r3) + ((uint64_t)h1 * r2) + ((uint64_t)h2 * r1) + ((uint64_t)h3 * r0) + ((uint64_t)h4 * s4); ++ d4 = ((uint64_t)h0 * r4) + ((uint64_t)h1 * r3) + ((uint64_t)h2 * r2) + ((uint64_t)h3 * r1) + ((uint64_t)h4 * r0); + +- if(!inlen) { +- goto poly1305_donna_finish; +- } ++ /* (partial) h %= p */ ++ c = (uint32_t)(d0 >> 26); ++ h0 = (uint32_t)d0 & 0x3ffffff; ++ d1 += c; ++ c = (uint32_t)(d1 >> 26); ++ h1 = (uint32_t)d1 & 0x3ffffff; ++ d2 += c; ++ c = (uint32_t)(d2 >> 26); ++ h2 = (uint32_t)d2 & 0x3ffffff; ++ d3 += c; ++ c = (uint32_t)(d3 >> 26); ++ h3 = (uint32_t)d3 & 0x3ffffff; ++ d4 += c; ++ c = (uint32_t)(d4 >> 26); ++ h4 = (uint32_t)d4 & 0x3ffffff; ++ h0 += c * 5; ++ c = (h0 >> 26); ++ h0 = h0 & 0x3ffffff; ++ h1 += c; + +- for(j = 0; j < inlen; j++) { +- mp[j] = m[j]; ++ m += POLY1305_BLOCK_SIZE; ++ bytes -= POLY1305_BLOCK_SIZE; + } + +- mp[j++] = 1; ++ st->h[0] = h0; ++ st->h[1] = h1; ++ st->h[2] = h2; ++ st->h[3] = h3; ++ st->h[4] = h4; ++} + +- for(; j < 16; j++) { +- mp[j] = 0; +- } ++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; ++ uint64_t f; ++ uint32_t mask; + +- inlen = 0; ++ /* process the remaining block */ ++ if(st->leftover) { ++ size_t i = st->leftover; ++ st->buffer[i++] = 1; + +- t0 = U8TO32_LE(mp + 0); +- t1 = U8TO32_LE(mp + 4); +- t2 = U8TO32_LE(mp + 8); +- t3 = U8TO32_LE(mp + 12); ++ for(; i < POLY1305_BLOCK_SIZE; i++) { ++ st->buffer[i] = 0; ++ } + +- h0 += t0 & 0x3ffffff; +- h1 += ((((uint64_t) t1 << 32) | t0) >> 26) & 0x3ffffff; +- h2 += ((((uint64_t) t2 << 32) | t1) >> 20) & 0x3ffffff; +- h3 += ((((uint64_t) t3 << 32) | t2) >> 14) & 0x3ffffff; +- h4 += (t3 >> 8); ++ st->final = 1; ++ poly1305_blocks(st, st->buffer, POLY1305_BLOCK_SIZE); ++ } + +- goto poly1305_donna_mul; ++ /* fully carry h */ ++ h0 = st->h[0]; ++ h1 = st->h[1]; ++ h2 = st->h[2]; ++ h3 = st->h[3]; ++ h4 = st->h[4]; + +-poly1305_donna_finish: +- b = h0 >> 26; +- h0 = h0 & 0x3ffffff; +- h1 += b; +- b = h1 >> 26; ++ c = h1 >> 26; + h1 = h1 & 0x3ffffff; +- h2 += b; +- b = h2 >> 26; ++ h2 += c; ++ c = h2 >> 26; + h2 = h2 & 0x3ffffff; +- h3 += b; +- b = h3 >> 26; ++ h3 += c; ++ c = h3 >> 26; + h3 = h3 & 0x3ffffff; +- h4 += b; +- b = h4 >> 26; ++ h4 += c; ++ c = h4 >> 26; + h4 = h4 & 0x3ffffff; +- h0 += b * 5; +- b = h0 >> 26; ++ h0 += c * 5; ++ c = h0 >> 26; + h0 = h0 & 0x3ffffff; +- h1 += b; ++ h1 += c; + ++ /* compute h + -p */ + g0 = h0 + 5; +- b = g0 >> 26; ++ c = g0 >> 26; + g0 &= 0x3ffffff; +- g1 = h1 + b; +- b = g1 >> 26; ++ g1 = h1 + c; ++ c = g1 >> 26; + g1 &= 0x3ffffff; +- g2 = h2 + b; +- b = g2 >> 26; ++ g2 = h2 + c; ++ c = g2 >> 26; + g2 &= 0x3ffffff; +- g3 = h3 + b; +- b = g3 >> 26; ++ g3 = h3 + c; ++ c = g3 >> 26; + g3 &= 0x3ffffff; +- g4 = h4 + b - (1 << 26); +- +- b = (g4 >> 31) - 1; +- nb = ~b; +- h0 = (h0 & nb) | (g0 & b); +- h1 = (h1 & nb) | (g1 & b); +- h2 = (h2 & nb) | (g2 & b); +- h3 = (h3 & nb) | (g3 & b); +- h4 = (h4 & nb) | (g4 & b); +- +- f0 = ((h0) | (h1 << 26)) + (uint64_t) U8TO32_LE(&key[16]); +- f1 = ((h1 >> 6) | (h2 << 20)) + (uint64_t) U8TO32_LE(&key[20]); +- f2 = ((h2 >> 12) | (h3 << 14)) + (uint64_t) U8TO32_LE(&key[24]); +- f3 = ((h3 >> 18) | (h4 << 8)) + (uint64_t) U8TO32_LE(&key[28]); +- +- U32TO8_LE(&out[0], f0); +- f1 += (f0 >> 32); +- U32TO8_LE(&out[4], f1); +- f2 += (f1 >> 32); +- U32TO8_LE(&out[8], f2); +- f3 += (f2 >> 32); +- U32TO8_LE(&out[12], f3); ++ g4 = h4 + c - (1 << 26); ++ ++ /* select h if h < p, or h + -p if h >= p */ ++ mask = (g4 >> ((sizeof(uint32_t) * 8) - 1)) - 1; ++ g0 &= mask; ++ g1 &= mask; ++ g2 &= mask; ++ g3 &= mask; ++ g4 &= mask; ++ mask = ~mask; ++ h0 = (h0 & mask) | g0; ++ h1 = (h1 & mask) | g1; ++ h2 = (h2 & mask) | g2; ++ h3 = (h3 & mask) | g3; ++ h4 = (h4 & mask) | g4; ++ ++ /* h = h % (2^128) */ ++ h0 = ((h0) | (h1 << 26)) & 0xffffffff; ++ h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; ++ h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; ++ h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; ++ ++ /* mac = (h + pad) % (2^128) */ ++ f = (uint64_t)h0 + st->pad[0] ; ++ h0 = (uint32_t)f; ++ f = (uint64_t)h1 + st->pad[1] + (f >> 32); ++ h1 = (uint32_t)f; ++ f = (uint64_t)h2 + st->pad[2] + (f >> 32); ++ h2 = (uint32_t)f; ++ f = (uint64_t)h3 + st->pad[3] + (f >> 32); ++ h3 = (uint32_t)f; ++ ++ U32TO8(mac + 0, h0); ++ U32TO8(mac + 4, h1); ++ U32TO8(mac + 8, h2); ++ U32TO8(mac + 12, h3); ++ ++ /* zero out the state */ ++ st->h[0] = 0; ++ st->h[1] = 0; ++ st->h[2] = 0; ++ st->h[3] = 0; ++ st->h[4] = 0; ++ st->r[0] = 0; ++ st->r[1] = 0; ++ st->r[2] = 0; ++ st->r[3] = 0; ++ st->r[4] = 0; ++ st->pad[0] = 0; ++ st->pad[1] = 0; ++ st->pad[2] = 0; ++ st->pad[3] = 0; ++} ++ ++ ++void ++poly1305_update(struct poly1305_context *st, const unsigned char *m, size_t bytes) { ++ size_t i; ++ ++ /* handle leftover */ ++ if(st->leftover) { ++ size_t want = (POLY1305_BLOCK_SIZE - st->leftover); ++ ++ if(want > bytes) { ++ want = bytes; ++ } ++ ++ for(i = 0; i < want; i++) { ++ st->buffer[st->leftover + i] = m[i]; ++ } ++ ++ bytes -= want; ++ m += want; ++ st->leftover += want; ++ ++ if(st->leftover < POLY1305_BLOCK_SIZE) { ++ return; ++ } ++ ++ poly1305_blocks(st, st->buffer, POLY1305_BLOCK_SIZE); ++ st->leftover = 0; ++ } ++ ++ /* process full blocks */ ++ if(bytes >= POLY1305_BLOCK_SIZE) { ++ size_t want = (bytes & ~(POLY1305_BLOCK_SIZE - 1)); ++ poly1305_blocks(st, m, want); ++ m += want; ++ bytes -= want; ++ } ++ ++ /* store leftover */ ++ if(bytes) { ++#if (USE_MEMCPY == 1) ++ memcpy(st->buffer + st->leftover, m, bytes); ++#else ++ ++ for(i = 0; i < bytes; i++) { ++ st->buffer[st->leftover + i] = m[i]; ++ } ++ ++#endif ++ st->leftover += bytes; ++ } ++} ++ ++void ++poly1305_auth(unsigned char mac[16], const unsigned char *m, size_t bytes, const unsigned char key[32]) { ++ struct poly1305_context ctx; ++ poly1305_init(&ctx, key); ++ poly1305_update(&ctx, m, bytes); ++ poly1305_finish(&ctx, mac); + } +diff --git a/src/chacha-poly1305/poly1305.h b/src/chacha-poly1305/poly1305.h +index 4ece415c..624a19a9 100644 +--- a/src/chacha-poly1305/poly1305.h ++++ b/src/chacha-poly1305/poly1305.h +@@ -1,16 +1,32 @@ +-/* $OpenBSD: poly1305.h,v 1.2 2013/12/19 22:57:13 djm Exp $ */ +- +-/* +- * Public Domain poly1305 from Andrew Moon +- * poly1305-donna-unrolled.c from https://github.com/floodyberry/poly1305-donna +- */ +- + #ifndef POLY1305_H + #define POLY1305_H + +-#define POLY1305_KEYLEN 32 +-#define POLY1305_TAGLEN 16 ++#include ++#include ++#include ++ ++#define POLY1305_KEYLEN 32 ++#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_auth(uint8_t out[POLY1305_TAGLEN], const uint8_t *m, size_t inlen, const uint8_t key[POLY1305_KEYLEN]); ++#endif /* POLY1305_H */ + +-#endif /* POLY1305_H */ +diff --git a/src/sptps.c b/src/sptps.c +index 55b9e5ca..33e88ed9 100644 +--- a/src/sptps.c ++++ b/src/sptps.c +@@ -1,6 +1,6 @@ + /* + sptps.c -- Simple Peer-to-Peer Security +- Copyright (C) 2011-2015 Guus Sliepen , ++ Copyright (C) 2011-2021 Guus Sliepen , + 2010 Brandon L. Black + + This program is free software; you can redistribute it and/or modify +@@ -20,7 +20,7 @@ + + #include "system.h" + +-#include "chacha-poly1305/chacha-poly1305.h" ++#include "chacha-poly1305/chachapoly.h" + #include "ecdh.h" + #include "ecdsa.h" + #include "logger.h" +@@ -32,6 +32,8 @@ + #include + #endif + ++#define CIPHER_KEYLEN 64 ++ + unsigned int sptps_replaywin = 16; + + /* +@@ -99,8 +101,8 @@ static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_ + #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)); ++ *ctx = malloc(sizeof(struct chachapoly_ctx)); ++ return *ctx && chachapoly_init(*ctx, key + (key_half ? CIPHER_KEYLEN : 0), 256) == CHACHAPOLY_OK; + + #else + +@@ -113,7 +115,7 @@ static bool cipher_init(uint8_t suite, void **ctx, const uint8_t *key, bool key_ + + 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); ++ && EVP_EncryptInit_ex(*ctx, NULL, NULL, key + (key_half ? CIPHER_KEYLEN : 0), key); + + case SPTPS_AES256_GCM: + *ctx = EVP_CIPHER_CTX_new(); +@@ -137,7 +139,7 @@ static void cipher_exit(uint8_t suite, void *ctx) { + #ifndef HAVE_OPENSSL + + case SPTPS_CHACHA_POLY1305: +- chacha_poly1305_exit(ctx); ++ free(ctx); + break; + + #else +@@ -159,9 +161,17 @@ static bool cipher_encrypt(uint8_t suite, void *ctx, uint32_t seqno, const uint8 + switch(suite) { + #ifndef HAVE_OPENSSL + +- case SPTPS_CHACHA_POLY1305: +- chacha_poly1305_encrypt(ctx, seqno, in, inlen, out, outlen); ++ case SPTPS_CHACHA_POLY1305: { ++ if(chachapoly_crypt(ctx, nonce, NULL, 0, (void *)in, inlen, out, out + inlen, 16, 1) != CHACHAPOLY_OK) { ++ return false; ++ } ++ ++ if(outlen) { ++ *outlen = inlen + 16; ++ } ++ + return true; ++ } + + #else + +@@ -204,24 +214,32 @@ 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) { ++ if(inlen < 16) { ++ return false; ++ } ++ ++ inlen -= 16; ++ + 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); ++ if(chachapoly_crypt(ctx, nonce, NULL, 0, (void *)in, inlen, out, (void *)(in + inlen), 16, 0) != CHACHAPOLY_OK) { ++ return false; ++ } ++ ++ if(outlen) { ++ *outlen = inlen; ++ } ++ ++ return true; + + #else + + case SPTPS_CHACHA_POLY1305: + case SPTPS_AES256_GCM: { +- if(inlen < 16) { +- return false; +- } +- +- inlen -= 16; +- + if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce)) { + return false; + } +@@ -379,7 +397,7 @@ 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) { + // Allocate memory for key material +- size_t keylen = 2 * CHACHA_POLY1305_KEYLEN; ++ size_t keylen = 2 * CIPHER_KEYLEN; + + s->key = realloc(s->key, keylen); + +diff --git a/src/sptps.h b/src/sptps.h +index b9ec11fd..57f6851e 100644 +--- a/src/sptps.h ++++ b/src/sptps.h +@@ -3,7 +3,7 @@ + + /* + sptps.h -- Simple Peer-to-Peer Security +- Copyright (C) 2011-2014 Guus Sliepen ++ Copyright (C) 2011-2021 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 +@@ -22,7 +22,7 @@ + + #include "system.h" + +-#include "chacha-poly1305/chacha-poly1305.h" ++#include "chacha-poly1305/chachapoly.h" + #include "ecdh.h" + #include "ecdsa.h" + +-- +2.36.0 + diff --git a/debian/patches/alt-ciphersuite/0006-Ensure-we-are-compatible-with-LibreSSL.patch b/debian/patches/alt-ciphersuite/0006-Ensure-we-are-compatible-with-LibreSSL.patch new file mode 100644 index 0000000..cc7bfe3 --- /dev/null +++ b/debian/patches/alt-ciphersuite/0006-Ensure-we-are-compatible-with-LibreSSL.patch @@ -0,0 +1,117 @@ +From d64b9c4a2f48ce7533e9f7a8f5f6e890764515ab Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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 + diff --git a/debian/patches/alt-ciphersuite/0007-Fix-infinite-loop-on-SPTPS-errors-when-running-sptps.patch b/debian/patches/alt-ciphersuite/0007-Fix-infinite-loop-on-SPTPS-errors-when-running-sptps.patch new file mode 100644 index 0000000..b7997bd --- /dev/null +++ b/debian/patches/alt-ciphersuite/0007-Fix-infinite-loop-on-SPTPS-errors-when-running-sptps.patch @@ -0,0 +1,26 @@ +From 948be5b4a813e814e36be23a63817df283e8db91 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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 + diff --git a/debian/patches/alt-ciphersuite/0008-Fix-documentation-of-default-cipher-algorithm-used-f.patch b/debian/patches/alt-ciphersuite/0008-Fix-documentation-of-default-cipher-algorithm-used-f.patch new file mode 100644 index 0000000..ed2b2ea --- /dev/null +++ b/debian/patches/alt-ciphersuite/0008-Fix-documentation-of-default-cipher-algorithm-used-f.patch @@ -0,0 +1,40 @@ +From 440bf1e9e484ac9800308dafbb5089e400df3522 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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 + diff --git a/debian/patches/alt-ciphersuite/0009-Make-the-ExperimentalProtocol-option-obsolete.patch b/debian/patches/alt-ciphersuite/0009-Make-the-ExperimentalProtocol-option-obsolete.patch new file mode 100644 index 0000000..9116192 --- /dev/null +++ b/debian/patches/alt-ciphersuite/0009-Make-the-ExperimentalProtocol-option-obsolete.patch @@ -0,0 +1,230 @@ +From 4e64f72feb99b7933e907fb0fab93368749db779 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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) +-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 = (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) +@@ -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 = (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 + diff --git a/debian/patches/alt-ciphersuite/0010-Move-poly1305_get_tag-into-poly1305.c-hide-poly1305_.patch b/debian/patches/alt-ciphersuite/0010-Move-poly1305_get_tag-into-poly1305.c-hide-poly1305_.patch new file mode 100644 index 0000000..4857fdb --- /dev/null +++ b/debian/patches/alt-ciphersuite/0010-Move-poly1305_get_tag-into-poly1305.c-hide-poly1305_.patch @@ -0,0 +1,322 @@ +From f4db140a8ffc63b575181299c3964e4634606280 Mon Sep 17 00:00:00 2001 +From: Guus Sliepen +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 + diff --git a/debian/patches/series b/debian/patches/series index 02fa232..f0563b3 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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