From 0da07280882253b792ddf9c6bd8b6690ba585b7a Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 13 Oct 2013 01:02:52 +0200 Subject: [PATCH] Use AES-256-GCM for the SPTPS protocol. It is faster than AES-256-CTR + HMAC-SHA256, especially on Intel chips with AES and PCLMULQDQ instructions. --- src/cipher.h | 7 +- src/openssl/cipher.c | 114 ++++++++++++++++++------- src/sptps.c | 199 ++++++++++++++++++------------------------- src/sptps_speed.c | 100 +++++++++++++++++----- 4 files changed, 251 insertions(+), 169 deletions(-) diff --git a/src/cipher.h b/src/cipher.h index 17ca6148..bbba5690 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -38,7 +38,12 @@ extern bool cipher_set_counter(cipher_t *, const void *, size_t) __attribute__ ( extern bool cipher_set_counter_key(cipher_t *, void *) __attribute__ ((__warn_unused_result__)); extern bool cipher_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) __attribute__ ((__warn_unused_result__)); extern bool cipher_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) __attribute__ ((__warn_unused_result__)); -extern bool cipher_counter_xor(cipher_t *, const void *indata, size_t inlen, void *outdata) __attribute__ ((__warn_unused_result__)); +extern bool cipher_gcm_encrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__)); +extern bool cipher_gcm_encrypt_start(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__)); +extern bool cipher_gcm_encrypt_finish(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__)); +extern bool cipher_gcm_decrypt(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__)); +extern bool cipher_gcm_decrypt_start(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__)); +extern bool cipher_gcm_decrypt_finish(cipher_t *, const void *indata, size_t inlen, void *outdata, size_t *outlen) __attribute__ ((__warn_unused_result__)); extern int cipher_get_nid(const cipher_t *); extern bool cipher_active(const cipher_t *); diff --git a/src/openssl/cipher.c b/src/openssl/cipher.c index 5d9bebcd..b01f5267 100644 --- a/src/openssl/cipher.c +++ b/src/openssl/cipher.c @@ -84,7 +84,7 @@ size_t cipher_keylength(const cipher_t *cipher) { if(!cipher || !cipher->cipher) return 0; - return cipher->cipher->key_len + cipher->cipher->block_size; + return cipher->cipher->key_len + cipher->cipher->iv_len; } bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) { @@ -118,13 +118,12 @@ bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encry } bool cipher_set_counter(cipher_t *cipher, const void *counter, size_t len) { - if(len > cipher->cipher->block_size - 4) { + if(len > cipher->cipher->iv_len - 4) { logger(DEBUG_ALWAYS, LOG_ERR, "Counter too long"); - abort(); + return false; } - memcpy(cipher->counter->counter + cipher->cipher->block_size - len, counter, len); - memset(cipher->counter->counter, 0, 4); + memcpy(cipher->counter->counter, counter, len); cipher->counter->n = 0; return true; @@ -142,41 +141,96 @@ bool cipher_set_counter_key(cipher_t *cipher, void *key) { else cipher->counter->n = 0; - memcpy(cipher->counter->counter, (unsigned char *)key + cipher->cipher->key_len, cipher->cipher->block_size); + memcpy(cipher->counter->counter, (unsigned char *)key + cipher->cipher->key_len, cipher->cipher->iv_len); return true; } -bool cipher_counter_xor(cipher_t *cipher, const void *indata, size_t inlen, void *outdata) { - if(!cipher->counter) { - logger(DEBUG_ALWAYS, LOG_ERR, "Counter not initialized"); +bool cipher_gcm_encrypt_start(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + int len = 0; + if(!EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter) + || (inlen && !EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen))) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + return false; + } + if(outlen) + *outlen = len; + return true; +} + +bool cipher_gcm_encrypt_finish(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + int len = 0, pad = 0; + if(!(inlen && EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen)) + || !EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + return false; + } + EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char *)outdata + len + pad); + if(outlen) + *outlen = len + pad + 16; + return true; +} + +bool cipher_gcm_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + int len = 0, pad = 0; + if(!EVP_EncryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter) || + !EVP_EncryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen) || + !EVP_EncryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + return false; + } + EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_GET_TAG, 16, (unsigned char *)outdata + len + pad); + if(outlen) + *outlen = len + pad + 16; + return true; +} + +bool cipher_gcm_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + if(inlen < 16) + return false; + + int len = 0, pad = 0; + if(!EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL)); return false; } - const unsigned char *in = indata; - unsigned char *out = outdata; + EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)indata + inlen - 16); - while(inlen--) { - // Encrypt the new counter value if we need it - if(!cipher->counter->n) { - int len; - if(!EVP_EncryptUpdate(&cipher->ctx, cipher->counter->block, &len, cipher->counter->counter, cipher->cipher->block_size)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", ERR_error_string(ERR_get_error(), NULL)); - return false; - } - - // Increase the counter value - for(int i = 0; i < cipher->cipher->block_size; i++) - if(++cipher->counter->counter[i]) - break; - } - - *out++ = *in++ ^ cipher->counter->block[cipher->counter->n++]; - - if(cipher->counter->n >= cipher->cipher->block_size) - cipher->counter->n = 0; + if(!EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen - 16) || + !EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + return false; } + if(outlen) + *outlen = len; + return true; +} +bool cipher_gcm_decrypt_start(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + int len = 0; + if(!EVP_DecryptInit_ex(&cipher->ctx, NULL, NULL, NULL, cipher->counter->counter) + || (inlen && !EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen))) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + return false; + } + if(outlen) + *outlen = len; + return true; +} + +bool cipher_gcm_decrypt_finish(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen) { + if(inlen < 16) + return false; + + EVP_CIPHER_CTX_ctrl(&cipher->ctx, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)indata + inlen - 16); + + int len = 0, pad = 0; + if((inlen > 16 && !EVP_DecryptUpdate(&cipher->ctx, (unsigned char *)outdata, &len, (unsigned char *)indata, inlen - 16)) + || !EVP_DecryptFinal(&cipher->ctx, (unsigned char *)outdata + len, &pad)) { + logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", ERR_error_string(ERR_get_error(), NULL)); + return false; + } return true; } diff --git a/src/sptps.c b/src/sptps.c index 62cfb1f9..0369d283 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -22,7 +22,6 @@ #include "cipher.h" #include "crypto.h" -#include "digest.h" #include "ecdh.h" #include "ecdsa.h" #include "logger.h" @@ -83,34 +82,30 @@ static void warning(sptps_t *s, const char *format, ...) { // Send a record (datagram version, accepts all record types, handles encryption and authentication). static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const char *data, uint16_t len) { - char buffer[len + 23UL]; + char buffer[len + 21UL]; // Create header with sequence number, length and record type uint32_t seqno = htonl(s->outseqno++); - uint16_t netlen = htons(len); - memcpy(buffer, &netlen, 2); - memcpy(buffer + 2, &seqno, 4); - buffer[6] = type; - - // Add plaintext (TODO: avoid unnecessary copy) - memcpy(buffer + 7, data, len); + memcpy(buffer, &seqno, 4); + buffer[4] = type; if(s->outstate) { // If first handshake has finished, encrypt and HMAC if(!cipher_set_counter(s->outcipher, &seqno, sizeof seqno)) - return false; + return error(s, EINVAL, "Failed to set counter"); - if(!cipher_counter_xor(s->outcipher, buffer + 6, len + 1UL, buffer + 6)) - return false; + if(!cipher_gcm_encrypt_start(s->outcipher, buffer + 4, 1, buffer + 4, NULL)) + return error(s, EINVAL, "Error encrypting record"); - if(!digest_create(s->outdigest, buffer, len + 7UL, buffer + 7UL + len)) - return false; + if(!cipher_gcm_encrypt_finish(s->outcipher, data, len, buffer + 5, NULL)) + return error(s, EINVAL, "Error encrypting record"); - return s->send_data(s->handle, type, buffer + 2, len + 21UL); + return s->send_data(s->handle, type, buffer, len + 21UL); } else { // Otherwise send as plaintext - return s->send_data(s->handle, type, buffer + 2, len + 5UL); + memcpy(buffer + 5, data, len); + return s->send_data(s->handle, type, buffer, len + 5UL); } } // Send a record (private version, accepts all record types, handles encryption and authentication). @@ -118,31 +113,31 @@ static bool send_record_priv(sptps_t *s, uint8_t type, const char *data, uint16_ if(s->datagram) return send_record_priv_datagram(s, type, data, len); - char buffer[len + 23UL]; + char buffer[len + 19UL]; // Create header with sequence number, length and record type uint32_t seqno = htonl(s->outseqno++); uint16_t netlen = htons(len); - memcpy(buffer, &seqno, 4); - memcpy(buffer + 4, &netlen, 2); - buffer[6] = type; - - // Add plaintext (TODO: avoid unnecessary copy) - memcpy(buffer + 7, data, len); + memcpy(buffer, &netlen, 2); + buffer[2] = type; if(s->outstate) { // If first handshake has finished, encrypt and HMAC - if(!cipher_counter_xor(s->outcipher, buffer + 4, len + 3UL, buffer + 4)) - return false; + if(!cipher_set_counter(s->outcipher, &seqno, 4)) + return error(s, EINVAL, "Failed to set counter"); - if(!digest_create(s->outdigest, buffer, len + 7UL, buffer + 7UL + len)) - return false; + if(!cipher_gcm_encrypt_start(s->outcipher, buffer, 3, buffer, NULL)) + return error(s, EINVAL, "Error encrypting record"); - return s->send_data(s->handle, type, buffer + 4, len + 19UL); + if(!cipher_gcm_encrypt_finish(s->outcipher, data, len, buffer + 3, NULL)) + return error(s, EINVAL, "Error encrypting record"); + + return s->send_data(s->handle, type, buffer, len + 19UL); } else { // Otherwise send as plaintext - return s->send_data(s->handle, type, buffer + 4, len + 3UL); + memcpy(buffer + 3, data, len); + return s->send_data(s->handle, type, buffer, len + 3UL); } } @@ -165,7 +160,7 @@ static bool send_kex(sptps_t *s) { // Make room for our KEX message, which we will keep around since send_sig() needs it. if(s->mykex) - abort(); + return false; s->mykex = realloc(s->mykex, 1 + 32 + keylen); if(!s->mykex) return error(s, errno, strerror(errno)); @@ -178,7 +173,7 @@ static bool send_kex(sptps_t *s) { // Create a new ECDH public key. if(!(s->ecdh = ecdh_generate_public(s->mykex + 1 + 32))) - return false; + return error(s, EINVAL, "Failed to generate ECDH public key"); return send_record_priv(s, SPTPS_HANDSHAKE, s->mykex, 1 + 32 + keylen); } @@ -199,7 +194,7 @@ static bool send_sig(sptps_t *s) { // Sign the result. if(!ecdsa_sign(s->mykey, msg, sizeof msg, sig)) - return false; + return error(s, EINVAL, "Failed to sign SIG record"); // Send the SIG exchange record. return send_record_priv(s, SPTPS_HANDSHAKE, sig, sizeof sig); @@ -209,16 +204,14 @@ static bool send_sig(sptps_t *s) { static bool generate_key_material(sptps_t *s, const char *shared, size_t len) { // Initialise cipher and digest structures if necessary if(!s->outstate) { - s->incipher = cipher_open_by_name("aes-256-ecb"); - s->outcipher = cipher_open_by_name("aes-256-ecb"); - s->indigest = digest_open_by_name("sha256", 16); - s->outdigest = digest_open_by_name("sha256", 16); - if(!s->incipher || !s->outcipher || !s->indigest || !s->outdigest) - return false; + s->incipher = cipher_open_by_name("aes-256-gcm"); + s->outcipher = cipher_open_by_name("aes-256-gcm"); + if(!s->incipher || !s->outcipher) + return error(s, EINVAL, "Failed to open cipher"); } // Allocate memory for key material - size_t keylen = digest_keylength(s->indigest) + digest_keylength(s->outdigest) + cipher_keylength(s->incipher) + cipher_keylength(s->outcipher); + size_t keylen = cipher_keylength(s->incipher) + cipher_keylength(s->outcipher); s->key = realloc(s->key, keylen); if(!s->key) @@ -238,7 +231,7 @@ static bool generate_key_material(sptps_t *s, const char *shared, size_t len) { // Use PRF to generate the key material if(!prf(shared, len, seed, s->labellen + 64 + 13, s->key, keylen)) - return false; + return error(s, EINVAL, "Failed to generate key material"); return true; } @@ -254,17 +247,11 @@ static bool receive_ack(sptps_t *s, const char *data, uint16_t len) { return error(s, EIO, "Invalid ACK record length"); if(s->initiator) { - bool result - = cipher_set_counter_key(s->incipher, s->key) - && digest_set_key(s->indigest, s->key + cipher_keylength(s->incipher), digest_keylength(s->indigest)); - if(!result) - return false; + if(!cipher_set_counter_key(s->incipher, s->key)) + return error(s, EINVAL, "Failed to set counter"); } else { - bool result - = cipher_set_counter_key(s->incipher, s->key + cipher_keylength(s->outcipher) + digest_keylength(s->outdigest)) - && digest_set_key(s->indigest, s->key + cipher_keylength(s->outcipher) + digest_keylength(s->outdigest) + cipher_keylength(s->incipher), digest_keylength(s->indigest)); - if(!result) - return false; + if(!cipher_set_counter_key(s->incipher, s->key + cipher_keylength(s->outcipher))) + return error(s, EINVAL, "Failed to set counter"); } free(s->key); @@ -284,7 +271,7 @@ static bool receive_kex(sptps_t *s, const char *data, uint16_t len) { // Make a copy of the KEX message, send_sig() and receive_sig() need it if(s->hiskex) - abort(); + return error(s, EINVAL, "Received a second KEX message before first has been processed"); s->hiskex = realloc(s->hiskex, len); if(!s->hiskex) return error(s, errno, strerror(errno)); @@ -313,12 +300,12 @@ static bool receive_sig(sptps_t *s, const char *data, uint16_t len) { // Verify signature. if(!ecdsa_verify(s->hiskey, msg, sizeof msg, data)) - return false; + return error(s, EIO, "Failed to verify SIG record"); // Compute shared secret. char shared[ECDH_SHARED_SIZE]; if(!ecdh_compute_shared(s->ecdh, s->hiskex + 1 + 32, shared)) - return false; + return error(s, EINVAL, "Failed to compute ECDH shared secret"); s->ecdh = NULL; // Generate key material from shared secret. @@ -337,17 +324,11 @@ static bool receive_sig(sptps_t *s, const char *data, uint16_t len) { // TODO: only set new keys after ACK has been set/received if(s->initiator) { - bool result - = cipher_set_counter_key(s->outcipher, s->key + cipher_keylength(s->incipher) + digest_keylength(s->indigest)) - && digest_set_key(s->outdigest, s->key + cipher_keylength(s->incipher) + digest_keylength(s->indigest) + cipher_keylength(s->outcipher), digest_keylength(s->outdigest)); - if(!result) - return false; + if(!cipher_set_counter_key(s->outcipher, s->key + cipher_keylength(s->incipher))) + return error(s, EINVAL, "Failed to set counter"); } else { - bool result - = cipher_set_counter_key(s->outcipher, s->key) - && digest_set_key(s->outdigest, s->key + cipher_keylength(s->outcipher), digest_keylength(s->outdigest)); - if(!result) - return false; + if(!cipher_set_counter_key(s->outcipher, s->key)) + return error(s, EINVAL, "Failed to set counter"); } return true; @@ -407,15 +388,11 @@ static bool receive_handshake(sptps_t *s, const char *data, uint16_t len) { // Check datagram for valid HMAC bool sptps_verify_datagram(sptps_t *s, const char *data, size_t len) { if(!s->instate || len < 21) - return false; + return error(s, EIO, "Received short packet"); - char buffer[len + 23]; - uint16_t netlen = htons(len - 21); + // TODO: just decrypt without updating the replay window - memcpy(buffer, &netlen, 2); - memcpy(buffer + 2, data, len); - - return digest_verify(s->indigest, buffer, len - 14, buffer + len - 14); + return true; } // Receive incoming data, datagram version. @@ -441,16 +418,16 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len return receive_handshake(s, data + 5, len - 5); } - // Check HMAC. - uint16_t netlen = htons(len - 21); + // Decrypt - char buffer[len + 23]; + char buffer[len]; - memcpy(buffer, &netlen, 2); - memcpy(buffer + 2, data, len); + if(!cipher_set_counter(s->incipher, data, sizeof seqno)) + return error(s, EINVAL, "Failed to set counter"); + size_t outlen; - if(!digest_verify(s->indigest, buffer, len - 14, buffer + len - 14)) - return error(s, EIO, "Invalid HMAC"); + if(!cipher_gcm_decrypt(s->incipher, data + 4, len - 4, buffer, &outlen)) + return error(s, EIO, "Failed to decrypt and verify packet"); // Replay protection using a sliding window of configurable size. // s->inseqno is expected sequence number @@ -492,26 +469,19 @@ static bool sptps_receive_data_datagram(sptps_t *s, const char *data, size_t len else s->received++; - // Decrypt. - memcpy(&seqno, buffer + 2, 4); - if(!cipher_set_counter(s->incipher, &seqno, sizeof seqno)) - return false; - if(!cipher_counter_xor(s->incipher, buffer + 6, len - 4, buffer + 6)) - return false; - // Append a NULL byte for safety. - buffer[len - 14] = 0; + buffer[len - 20] = 0; - uint8_t type = buffer[6]; + uint8_t type = buffer[0]; if(type < SPTPS_HANDSHAKE) { if(!s->instate) return error(s, EIO, "Application record received before handshake finished"); - if(!s->receive_record(s->handle, type, buffer + 7, len - 21)) - return false; + if(!s->receive_record(s->handle, type, buffer + 1, len - 21)) + abort(); } else if(type == SPTPS_HANDSHAKE) { - if(!receive_handshake(s, buffer + 7, len - 21)) - return false; + if(!receive_handshake(s, buffer + 1, len - 21)) + abort(); } else { return error(s, EIO, "Invalid record type %d", type); } @@ -529,8 +499,8 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { while(len) { // First read the 2 length bytes. - if(s->buflen < 6) { - size_t toread = 6 - s->buflen; + if(s->buflen < 2) { + size_t toread = 2 - s->buflen; if(toread > len) toread = len; @@ -541,36 +511,39 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { data += toread; // Exit early if we don't have the full length. - if(s->buflen < 6) + if(s->buflen < 2) return true; + // Update sequence number. + + uint32_t seqno = htonl(s->inseqno++); + // Decrypt the length bytes if(s->instate) { - if(!cipher_counter_xor(s->incipher, s->inbuf + 4, 2, &s->reclen)) - return false; + if(!cipher_set_counter(s->incipher, &seqno, 4)) + return error(s, EINVAL, "Failed to set counter"); + + if(!cipher_gcm_decrypt_start(s->incipher, s->inbuf, 2, &s->reclen, NULL)) + return error(s, EINVAL, "Failed to decrypt record"); } else { - memcpy(&s->reclen, s->inbuf + 4, 2); + 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 + 23UL); + s->inbuf = realloc(s->inbuf, s->reclen + 19UL); if(!s->inbuf) return error(s, errno, strerror(errno)); - // Add sequence number. - uint32_t seqno = htonl(s->inseqno++); - memcpy(s->inbuf, &seqno, 4); - // Exit early if we have no more data to process. if(!len) return true; } // Read up to the end of the record. - size_t toread = s->reclen + (s->instate ? 23UL : 7UL) - s->buflen; + size_t toread = s->reclen + (s->instate ? 19UL : 3UL) - s->buflen; if(toread > len) toread = len; @@ -580,36 +553,33 @@ bool sptps_receive_data(sptps_t *s, const char *data, size_t len) { data += toread; // If we don't have a whole record, exit. - if(s->buflen < s->reclen + (s->instate ? 23UL : 7UL)) + if(s->buflen < s->reclen + (s->instate ? 19UL : 3UL)) return true; // Check HMAC and decrypt. if(s->instate) { - if(!digest_verify(s->indigest, s->inbuf, s->reclen + 7UL, s->inbuf + s->reclen + 7UL)) - return error(s, EIO, "Invalid HMAC"); - - if(!cipher_counter_xor(s->incipher, s->inbuf + 6UL, s->reclen + 1UL, s->inbuf + 6UL)) - return false; + if(!cipher_gcm_decrypt_finish(s->incipher, 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 + 7UL] = 0; + s->inbuf[s->reclen + 3UL] = 0; - uint8_t type = s->inbuf[6]; + uint8_t type = s->inbuf[2]; if(type < SPTPS_HANDSHAKE) { if(!s->instate) return error(s, EIO, "Application record received before handshake finished"); - if(!s->receive_record(s->handle, type, s->inbuf + 7, s->reclen)) + if(!s->receive_record(s->handle, type, s->inbuf + 3, s->reclen)) return false; } else if(type == SPTPS_HANDSHAKE) { - if(!receive_handshake(s, s->inbuf + 7, s->reclen)) + if(!receive_handshake(s, s->inbuf + 3, s->reclen)) return false; } else { return error(s, EIO, "Invalid record type %d", type); } - s->buflen = 4; + s->buflen = 0; } return true; @@ -641,8 +611,7 @@ bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_ s->inbuf = malloc(7); if(!s->inbuf) return error(s, errno, strerror(errno)); - s->buflen = 4; - memset(s->inbuf, 0, 4); + s->buflen = 0; } memcpy(s->label, label, labellen); diff --git a/src/sptps_speed.c b/src/sptps_speed.c index 69f75997..268e6c9b 100644 --- a/src/sptps_speed.c +++ b/src/sptps_speed.c @@ -48,7 +48,8 @@ static void receive_data(sptps_t *sptps) { char buf[4096]; int fd = *(int *)sptps->handle; size_t len = recv(fd, buf, sizeof buf, 0); - sptps_receive_data(sptps, buf, len); + if(!sptps_receive_data(sptps, buf, len)) + abort(); } struct timespec start; @@ -62,12 +63,12 @@ static void clock_start() { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); } -static bool clock_countto(int seconds) { +static bool clock_countto(double seconds) { clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end); elapsed = end.tv_sec + end.tv_nsec * 1e-9 - start.tv_sec - start.tv_nsec * 1e-9; if(elapsed < seconds) return ++count; - + rate = count / elapsed; return false; } @@ -77,38 +78,39 @@ int main(int argc, char *argv[]) { ecdh_t *ecdh1, *ecdh2; sptps_t sptps1, sptps2; char buf1[4096], buf2[4096], buf3[4096]; + double duration = argc > 1 ? atof(argv[1]) : 10; crypto_init(); // Key generation - fprintf(stderr, "Generating keys for 10 seconds: "); - for(clock_start(); clock_countto(10);) + fprintf(stderr, "Generating keys for %lg seconds: ", duration); + for(clock_start(); clock_countto(duration);) ecdsa_free(ecdsa_generate()); - fprintf(stderr, "%13.2lf op/s\n", rate); + fprintf(stderr, "%17.2lf op/s\n", rate); key1 = ecdsa_generate(); key2 = ecdsa_generate(); // ECDSA signatures - fprintf(stderr, "ECDSA sign for 10 seconds: "); - for(clock_start(); clock_countto(10);) + fprintf(stderr, "ECDSA sign for %lg seconds: ", duration); + for(clock_start(); clock_countto(duration);) ecdsa_sign(key1, buf1, 256, buf2); - fprintf(stderr, "%18.2lf op/s\n", rate); - - fprintf(stderr, "ECDSA verify for 10 seconds: "); - for(clock_start(); clock_countto(10);) + fprintf(stderr, "%22.2lf op/s\n", rate); + + fprintf(stderr, "ECDSA verify for %lg seconds: ", duration); + for(clock_start(); clock_countto(duration);) ecdsa_verify(key1, buf1, 256, buf2); - fprintf(stderr, "%16.2lf op/s\n", rate); + fprintf(stderr, "%20.2lf op/s\n", rate); ecdh1 = ecdh_generate_public(buf1); - fprintf(stderr, "ECDH for 10 seconds: "); - for(clock_start(); clock_countto(10);) { + fprintf(stderr, "ECDH for %lg seconds: ", duration); + for(clock_start(); clock_countto(duration);) { ecdh2 = ecdh_generate_public(buf2); ecdh_compute_shared(ecdh2, buf1, buf3); } - fprintf(stderr, "%24.2lf op/s\n", rate); + fprintf(stderr, "%28.2lf op/s\n", rate); ecdh_free(ecdh1); // SPTPS authentication phase @@ -121,8 +123,8 @@ int main(int argc, char *argv[]) { struct pollfd pfd[2] = {{fd[0], POLLIN}, {fd[1], POLLIN}}; - fprintf(stderr, "SPTPS authenticate for 10 seconds: "); - for(clock_start(); clock_countto(10);) { + fprintf(stderr, "SPTPS/TCP authenticate for %lg seconds: ", duration); + 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); while(poll(pfd, 2, 0)) { @@ -133,8 +135,8 @@ int main(int argc, char *argv[]) { } sptps_stop(&sptps1); sptps_stop(&sptps2); - } - fprintf(stderr, "%10.2lf op/s\n", rate * 2); + } + fprintf(stderr, "%10.2lf op/s\n", rate * 2); // SPTPS data @@ -146,9 +148,61 @@ int main(int argc, char *argv[]) { if(pfd[1].revents) receive_data(&sptps2); } - fprintf(stderr, "SPTPS transmit for 10 seconds: "); - for(clock_start(); clock_countto(10);) { - sptps_send_record(&sptps1, 0, buf1, 1451); + fprintf(stderr, "SPTPS/TCP transmit for %lg seconds: ", duration); + for(clock_start(); clock_countto(duration);) { + if(!sptps_send_record(&sptps1, 0, buf1, 1451)) + abort(); + receive_data(&sptps2); + } + rate *= 2 * 1451 * 8; + 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); + sptps_stop(&sptps1); + sptps_stop(&sptps2); + + // SPTPS datagram authentication phase + + close(fd[0]); + close(fd[1]); + + if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)) { + fprintf(stderr, "Could not create a UNIX socket pair: %s\n", strerror(errno)); + return 1; + } + + fprintf(stderr, "SPTPS/UDP authenticate for %lg seconds: ", duration); + 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); + while(poll(pfd, 2, 0)) { + if(pfd[0].revents) + receive_data(&sptps1); + if(pfd[1].revents) + receive_data(&sptps2); + } + sptps_stop(&sptps1); + sptps_stop(&sptps2); + } + fprintf(stderr, "%10.2lf op/s\n", rate * 2); + + // 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); + if(pfd[1].revents) + receive_data(&sptps2); + } + fprintf(stderr, "SPTPS/UDP transmit for %lg seconds: ", duration); + for(clock_start(); clock_countto(duration);) { + if(!sptps_send_record(&sptps1, 0, buf1, 1451)) + abort(); receive_data(&sptps2); } rate *= 2 * 1451 * 8;