From ad2e8db4730e3c4355db2cea911422e7efd6a1ee 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 | 276 +++++++++++++++++++++++++++++++++++--------- src/sptps.h | 48 +++++++- src/sptps_test.c | 13 ++- 7 files changed, 372 insertions(+), 76 deletions(-) diff --git a/doc/SPTPS b/doc/SPTPS index 2d8fee5bf..2da27604f 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 e068ae89b..d83e13783 100644 --- a/src/invitation.c +++ b/src/invitation.c @@ -1397,7 +1397,16 @@ int cmd_join(int argc, char *argv[]) { } // 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 263a13172..6a7a9196f 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -359,7 +359,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 */ @@ -454,7 +464,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 0890755e5..71f3d8e69 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 35c68bc9d..7be8cd807 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -28,6 +28,10 @@ #include "random.h" #include "xalloc.h" +#ifdef HAVE_OPENSSL +#include +#endif + unsigned int sptps_replaywin = 16; /* @@ -108,25 +112,160 @@ static void free_sptps_key(sptps_key_t *key) { xzfree(key, sizeof(sptps_key_t)); } +static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool key_half) { + const uint8_t *key = key_half ? keys->key1 : keys->key0; + + switch(suite) { + case SPTPS_CHACHA_POLY1305: + *ctx = chacha_poly1305_init(); + return ctx && chacha_poly1305_set_key(*ctx, key); + + 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 + 32); +#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). @@ -135,11 +274,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; @@ -147,11 +286,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); } } @@ -181,6 +323,8 @@ static bool send_kex(sptps_t *s) { // Set version byte to zero. s->mykex->version = SPTPS_VERSION; + s->mykex->preferred_suite = s->preferred_suite; + s->mykex->cipher_suites = s->cipher_suites; // Create a random nonce. randomize(s->mykex->nonce, ECDH_SIZE); @@ -225,16 +369,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 s->key = new_sptps_key(); @@ -276,10 +410,8 @@ static bool receive_ack(sptps_t *s, const uint8_t *data, uint16_t len) { return error(s, EIO, "Invalid ACK record length"); } - uint8_t *key = s->initiator ? s->key->key0 : s->key->key1; - - if(!chacha_poly1305_set_key(s->incipher, key)) { - 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_sptps_key(s->key); @@ -289,9 +421,35 @@ 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 != sizeof(sptps_kex_t)) { return error(s, EIO, "Invalid KEX record length"); } @@ -300,6 +458,16 @@ static bool receive_kex(sptps_t *s, const uint8_t *data, uint16_t len) { return error(s, EINVAL, "Received incorrect version %d", *data); } + 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) { return error(s, EINVAL, "Received a second KEX message before first has been processed"); @@ -365,11 +533,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 - uint8_t *key = s->initiator ? s->key->key1 : s->key->key0; - - if(!chacha_poly1305_set_key(s->outcipher, 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; @@ -519,15 +684,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. @@ -538,7 +701,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; @@ -564,7 +726,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"); } @@ -636,10 +798,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, "%s", strerror(errno)); @@ -652,7 +813,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; @@ -663,7 +824,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; } @@ -673,13 +834,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]; @@ -705,16 +866,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); @@ -726,13 +889,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, "%s", strerror(errno)); } - if(!datagram) { + memcpy(s->label, params->label, s->labellen); + + if(!s->datagram) { s->inbuf = malloc(7); if(!s->inbuf) { @@ -742,11 +908,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; @@ -756,8 +920,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_sptps_kex(s->mykex); diff --git a/src/sptps.h b/src/sptps.h index 633eb810e..e21804a48 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); @@ -49,13 +52,15 @@ typedef enum sptps_state_t { PACKED(struct sptps_kex_t { uint8_t version; + uint8_t preferred_suite; + uint16_t cipher_suites; uint8_t nonce[ECDH_SIZE]; uint8_t pubkey[ECDH_SIZE]; }); typedef struct sptps_kex_t sptps_kex_t; -STATIC_ASSERT(sizeof(sptps_kex_t) == 65, "sptps_kex_t has invalid size"); +STATIC_ASSERT(sizeof(sptps_kex_t) == 68, "sptps_kex_t has invalid size"); typedef union sptps_key_t { struct { @@ -67,9 +72,40 @@ typedef union sptps_key_t { STATIC_ASSERT(sizeof(sptps_key_t) == 128, "sptps_key_t has invalid size"); +// 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; @@ -77,7 +113,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; @@ -85,7 +121,7 @@ typedef struct sptps { uint8_t *late; bool outstate; - chacha_poly1305_ctx_t *outcipher; + void *outcipher; uint32_t outseqno; ecdsa_t *mykey; @@ -107,7 +143,7 @@ extern unsigned int sptps_replaywin; extern void sptps_log_quiet(sptps_t *s, int s_errno, const char *format, va_list ap) ATTR_FORMAT(printf, 3, 0); extern void sptps_log_stderr(sptps_t *s, int s_errno, const char *format, va_list ap) ATTR_FORMAT(printf, 3, 0); extern void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap) ATTR_FORMAT(printf, 3, 0); -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 38e8b50da..73f4ec02e 100644 --- a/src/sptps_test.c +++ b/src/sptps_test.c @@ -583,7 +583,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)) { ecdsa_free(mykey); ecdsa_free(hiskey); return 1; From ed980699ea98b66612b6b004d7c173336aa1b80b 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 | 90 +++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/src/sptps_test.c b/src/sptps_test.c index 73f4ec02e..37b5a5af4 100644 --- a/src/sptps_test.c +++ b/src/sptps_test.c @@ -119,38 +119,42 @@ static bool receive_record(void *handle, uint8_t type, const void *data, uint16_ } typedef enum option_t { - OPT_BAD_OPTION = '?', - OPT_LONG_OPTION = 0, + OPT_BAD_OPTION = '?', + OPT_LONG_OPTION = 0, // Short options - OPT_DATAGRAM = 'd', - OPT_QUIT_ON_EOF = 'q', - OPT_READONLY = 'r', - OPT_WRITEONLY = 'w', - OPT_PACKET_LOSS = 'L', - OPT_REPLAY_WINDOW = 'W', - OPT_SPECIAL_CHAR = 's', - OPT_TUN = 't', - OPT_VERBOSE = 'v', - OPT_IPV4 = '4', - OPT_IPV6 = '6', + OPT_DATAGRAM = 'd', + OPT_QUIT_ON_EOF = 'q', + OPT_READONLY = 'r', + OPT_WRITEONLY = 'w', + OPT_PACKET_LOSS = 'L', + OPT_REPLAY_WINDOW = 'W', + OPT_SPECIAL_CHAR = 's', + OPT_TUN = 't', + OPT_VERBOSE = 'v', + OPT_CIPHER_SUITES = 'M', + OPT_PREFERRED_SUITE = 'P', + OPT_IPV4 = '4', + OPT_IPV6 = '6', // Long options - OPT_HELP = 255, + OPT_HELP = 255, } option_t; static struct option const long_options[] = { - {"datagram", no_argument, NULL, OPT_DATAGRAM}, - {"quit", no_argument, NULL, OPT_QUIT_ON_EOF}, - {"readonly", no_argument, NULL, OPT_READONLY}, - {"writeonly", no_argument, NULL, OPT_WRITEONLY}, - {"packet-loss", required_argument, NULL, OPT_PACKET_LOSS}, - {"replay-window", required_argument, NULL, OPT_REPLAY_WINDOW}, - {"special", no_argument, NULL, OPT_SPECIAL_CHAR}, - {"tun", no_argument, NULL, OPT_TUN}, - {"verbose", required_argument, NULL, OPT_VERBOSE}, - {"help", no_argument, NULL, OPT_HELP}, - {NULL, 0, NULL, 0} + {"datagram", no_argument, NULL, OPT_DATAGRAM}, + {"quit", no_argument, NULL, OPT_QUIT_ON_EOF}, + {"readonly", no_argument, NULL, OPT_READONLY}, + {"writeonly", no_argument, NULL, OPT_WRITEONLY}, + {"packet-loss", required_argument, NULL, OPT_PACKET_LOSS}, + {"replay-window", required_argument, NULL, OPT_REPLAY_WINDOW}, + {"special", no_argument, NULL, OPT_SPECIAL_CHAR}, + {"tun", no_argument, NULL, OPT_TUN}, + {"verbose", required_argument, NULL, OPT_VERBOSE}, + {"cipher-suites", required_argument, NULL, OPT_CIPHER_SUITES}, + {"preferred-suite", required_argument, NULL, OPT_PREFERRED_SUITE}, + {"help", no_argument, NULL, OPT_HELP}, + {NULL, 0, NULL, 0} }; static void usage(void) { @@ -158,19 +162,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", program_name); @@ -347,6 +353,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((option_t) r) { @@ -391,6 +399,14 @@ static int run_test(int argc, char *argv[]) { sptps_replaywin = atoi(optarg); break; + case OPT_CIPHER_SUITES: + cipher_suites = strtoul(optarg, NULL, 0); + break; + + case OPT_PREFERRED_SUITE: + preferred_suite = strtoul(optarg, NULL, 0); + break; + case OPT_VERBOSE: verbose = true; break; @@ -592,6 +608,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)) { From 28b8e10015574784f59d4893de11d80f07412459 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 c7c6e5463..45bbeb5cb 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); From 9ce00234190baec0eaa608a5093bb3823487ef0c 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 | 126 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 49 deletions(-) diff --git a/src/sptps.c b/src/sptps.c index 7be8cd807..500bf83d5 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -116,12 +116,26 @@ static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool const uint8_t *key = key_half ? keys->key1 : keys->key0; switch(suite) { +#ifndef HAVE_OPENSSL + case SPTPS_CHACHA_POLY1305: *ctx = chacha_poly1305_init(); return ctx && chacha_poly1305_set_key(*ctx, key); +#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 + 32); + case SPTPS_AES256_GCM: -#ifdef HAVE_OPENSSL *ctx = EVP_CIPHER_CTX_new(); if(!ctx) { @@ -129,7 +143,7 @@ static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool } 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_CIPHER_CTX_ctrl(*ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, NULL) && EVP_EncryptInit_ex(*ctx, NULL, NULL, key, key + 32); #endif @@ -140,12 +154,16 @@ static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool 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 @@ -157,42 +175,47 @@ 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) { 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: { + uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24}; - if(!EVP_EncryptUpdate(ctx, out, &outlen1, in, (int)inlen)) { - return false; - } + if(!EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, nonce)) { + return false; + } - if(!EVP_EncryptFinal_ex(ctx, out + outlen1, &outlen2)) { - return false; - } + int outlen1 = 0, outlen2 = 0; - outlen1 += outlen2; + if(!EVP_EncryptUpdate(ctx, out, &outlen1, in, (int)inlen)) { + return false; + } - if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out + outlen1)) { - return false; - } + if(!EVP_EncryptFinal_ex(ctx, out + outlen1, &outlen2)) { + return false; + } - outlen1 += 16; + outlen1 += outlen2; - if(outlen) { - *outlen = outlen1; - } + if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out + outlen1)) { + return false; + } + + outlen1 += 16; - return true; + if(outlen) { + *outlen = outlen1; } + return true; + } + #endif default: @@ -202,43 +225,48 @@ 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) { 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; + uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24}; - if(!EVP_DecryptUpdate(ctx, out, &outlen1, in, (int)inlen)) { - return false; - } + if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce)) { + return false; + } - if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void *)(in + inlen))) { - return false; - } + int outlen1 = 0, outlen2 = 0; - if(!EVP_DecryptFinal_ex(ctx, out + outlen1, &outlen2)) { - return false; - } + if(!EVP_DecryptUpdate(ctx, out, &outlen1, in, (int)inlen)) { + return false; + } - if(outlen) { - *outlen = outlen1 + outlen2; - } + 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; + } - return true; + if(outlen) { + *outlen = outlen1 + outlen2; } + return true; + } + #endif default: From 63df2139835bb532125562c73ec3efbc437a3634 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 | 102 ------ 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 | 48 ++- src/sptps.h | 13 +- src/sptps_test.c | 4 +- 12 files changed, 650 insertions(+), 341 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 77d531add..000000000 --- a/src/chacha-poly1305/chacha-poly1305.c +++ /dev/null @@ -1,102 +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) { - return xzalloc(sizeof(chacha_poly1305_ctx_t)); -} - -void chacha_poly1305_exit(chacha_poly1305_ctx_t *ctx) { - xzfree(ctx, sizeof(chacha_poly1305_ctx_t)); -} - -bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const uint8_t *key) { - 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 e75d984f2..000000000 --- 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 void chacha_poly1305_exit(chacha_poly1305_ctx_t *); -extern chacha_poly1305_ctx_t *chacha_poly1305_init(void) ATTR_DEALLOCATOR(chacha_poly1305_exit); -extern bool chacha_poly1305_set_key(chacha_poly1305_ctx_t *ctx, const uint8_t *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 696f44a52..4452aca2a 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 103c3d812..a137ab6b2 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 000000000..9a6620ce4 --- /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 000000000..ffc9576d1 --- /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 d8fd74cc1..60a20ab35 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 4d99b8c3a..0c90564cb 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 4ece415c2..624a19a99 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 500bf83d5..de4ef6ee6 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 "prf.h" @@ -32,6 +32,8 @@ #include #endif +#define CIPHER_KEYLEN 64 + unsigned int sptps_replaywin = 16; /* @@ -113,14 +115,14 @@ static void free_sptps_key(sptps_key_t *key) { } static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool key_half) { - const uint8_t *key = key_half ? keys->key1 : keys->key0; + const uint8_t *key = key_half ? keys->key1 : keys->key0; switch(suite) { #ifndef HAVE_OPENSSL case SPTPS_CHACHA_POLY1305: - *ctx = chacha_poly1305_init(); - return ctx && chacha_poly1305_set_key(*ctx, key); + *ctx = malloc(sizeof(struct chachapoly_ctx)); + return *ctx && chachapoly_init(*ctx, key, 256) == CHACHAPOLY_OK; #else @@ -157,7 +159,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 @@ -177,9 +179,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 @@ -224,22 +234,30 @@ 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; + 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; - uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24}; if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, nonce)) { diff --git a/src/sptps.h b/src/sptps.h index e21804a48..6d01891a2 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" @@ -62,12 +62,15 @@ typedef struct sptps_kex_t sptps_kex_t; STATIC_ASSERT(sizeof(sptps_kex_t) == 68, "sptps_kex_t has invalid size"); +// Big enough to handle a 256 bit key + IV +#define SPTPS_KEYLEN 64 + typedef union sptps_key_t { struct { - uint8_t key0[CHACHA_POLY1305_KEYLEN]; - uint8_t key1[CHACHA_POLY1305_KEYLEN]; + uint8_t key0[SPTPS_KEYLEN]; + uint8_t key1[SPTPS_KEYLEN]; }; - uint8_t both[CHACHA_POLY1305_KEYLEN * 2]; + uint8_t both[SPTPS_KEYLEN * 2]; } sptps_key_t; STATIC_ASSERT(sizeof(sptps_key_t) == 128, "sptps_key_t has invalid size"); diff --git a/src/sptps_test.c b/src/sptps_test.c index 37b5a5af4..e113a8471 100644 --- a/src/sptps_test.c +++ b/src/sptps_test.c @@ -132,8 +132,8 @@ typedef enum option_t { OPT_SPECIAL_CHAR = 's', OPT_TUN = 't', OPT_VERBOSE = 'v', - OPT_CIPHER_SUITES = 'M', - OPT_PREFERRED_SUITE = 'P', + OPT_CIPHER_SUITES = 'M', + OPT_PREFERRED_SUITE = 'P', OPT_IPV4 = '4', OPT_IPV6 = '6', From 79809c5f7d6ead13b5857307cc262ed0754e650d 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 | 64 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/sptps.c b/src/sptps.c index de4ef6ee6..9e637bef3 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -127,25 +127,25 @@ static bool cipher_init(uint8_t suite, void **ctx, const sptps_key_t *keys, bool #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 + 32); +#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) + 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 + 32); #endif @@ -165,6 +165,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; @@ -194,6 +200,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: { uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24}; @@ -257,6 +280,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: { uint8_t nonce[12] = {seqno, seqno >> 8, seqno >> 16, seqno >> 24}; From e65df6619c48b94e6f2b7eee089cf99ebb52f215 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 e113a8471..131b0f403 100644 --- a/src/sptps_test.c +++ b/src/sptps_test.c @@ -744,6 +744,8 @@ static int run_test(int argc, char *argv[]) { ecdsa_free(mykey); ecdsa_free(hiskey); return 1; + } else { + break; } } From ee997c818e5339b8893d844ed41a7482d7c6092b 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 f0e765dc2..a45202b65 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -603,7 +603,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 2e519d1cf..ab3dca233 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. From 4f48176209832913975843b6cf3bf1753152da3f 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. --- 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 ++-- 6 files changed, 16 insertions(+), 37 deletions(-) diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index a45202b65..c6af6d7c2 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 @@ -610,8 +601,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 @@ -626,8 +616,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. @@ -637,8 +626,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 ab3dca233..c1e62a521 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 2ed286a62..676ecec69 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -1677,7 +1677,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 b056c7d54..52e0f8209 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 845ac3458..f7ab1bd21 100755 --- a/test/integration/legacy_protocol.py +++ b/test/integration/legacy_protocol.py @@ -73,14 +73,14 @@ def run_keys_test(foo: Tinc, bar: Tinc, empty: bool) -> None: 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 ce8136aa5..2166a4003 100755 --- a/test/integration/splice.py +++ b/test/integration/splice.py @@ -30,7 +30,7 @@ def init(ctx: Test, *options: str) -> T.Tuple[Tinc, Tinc]: set Sandbox {SANDBOX_LEVEL} {custom} """ - foo.cmd(stdin=stdin) + foo.cmd("--force", stdin=stdin) stdin = f""" init {bar} @@ -42,7 +42,7 @@ def init(ctx: Test, *options: str) -> T.Tuple[Tinc, Tinc]: set Sandbox {SANDBOX_LEVEL} {custom} """ - bar.cmd(stdin=stdin) + bar.cmd("--force", stdin=stdin) foo.add_script(Script.SUBNET_UP) bar.add_script(Script.SUBNET_UP) From e994222a4370621a9ac69c13ed23531c1eaa6809 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 a137ab6b2..d4784f49c 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 9a6620ce4..68f04edd7 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 ffc9576d1..5d01f5258 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 0c90564cb..b25435a71 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 624a19a99..5fc3b903d 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 9e637bef3..b36079d3d 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -186,7 +186,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; } @@ -267,7 +267,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; }