diff --git a/tunnels/client/reality/CMakeLists.txt b/tunnels/client/reality/CMakeLists.txt index 15aebe78..ecc09f2d 100644 --- a/tunnels/client/reality/CMakeLists.txt +++ b/tunnels/client/reality/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(RealityClient STATIC target_include_directories(RealityClient PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../../ww) target_link_libraries(RealityClient ww) target_include_directories(RealityClient PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../shared/openssl) +target_include_directories(RealityClient PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../shared/reality) # add dependencies diff --git a/tunnels/client/reality/reality_client.c b/tunnels/client/reality/reality_client.c index 6d9039ae..fe4a729f 100644 --- a/tunnels/client/reality/reality_client.c +++ b/tunnels/client/reality/reality_client.c @@ -2,11 +2,9 @@ #include "buffer_pool.h" #include "buffer_stream.h" #include "context_queue.h" -#include "frand.h" #include "loggers/network_logger.h" #include "openssl_globals.h" -#include "shiftbuffer.h" -#include "tunnel.h" +#include "reality_helpers.h" #include "utils/hashutils.h" #include "utils/jsonutils.h" #include "utils/mathutils.h" @@ -15,19 +13,6 @@ #include #include #include -#include -#include - -// these should not be modified, code may break -enum reality_consts -{ - kEncryptionBlockSize = 16, - kSignLen = (224 / 8), - kSignPasswordLen = kEncryptionBlockSize, - kIVlen = 16, // iv size for *most* modes is the same as the block size. For AES this is 128 bits - kTLSVersion12 = 0x0303, - kTLS12ApplicationData = 0x17, -}; typedef struct reailty_client_state_s { @@ -121,138 +106,6 @@ static void flushWriteQueue(tunnel_t *self, context_t *c) } } -static bool verifyMessage(shift_buffer_t *buf, reailty_client_con_state_t *cstate) -{ - int rc = EVP_DigestSignInit(cstate->sign_context, NULL, cstate->msg_digest, NULL, cstate->sign_key); - uint8_t expect[EVP_MAX_MD_SIZE]; - memcpy(expect, rawBuf(buf), kSignLen); - shiftr(buf, kSignLen); - assert(rc == 1); - rc = EVP_DigestSignUpdate(cstate->sign_context, rawBuf(buf), bufLen(buf)); - assert(rc == 1); - uint8_t buff[EVP_MAX_MD_SIZE]; - size_t size = sizeof(buff); - rc = EVP_DigestSignFinal(cstate->sign_context, buff, &size); - assert(rc == 1); - assert(size == kSignLen); - return ! ! CRYPTO_memcmp(expect, buff, size); -} - -static void signMessage(shift_buffer_t *buf, reailty_client_con_state_t *cstate) -{ - int rc = EVP_DigestSignInit(cstate->sign_context, NULL, cstate->msg_digest, NULL, cstate->sign_key); - assert(rc == 1); - rc = EVP_DigestSignUpdate(cstate->sign_context, rawBuf(buf), bufLen(buf)); - assert(rc == 1); - size_t req = 0; - rc = EVP_DigestSignFinal(cstate->sign_context, NULL, &req); - assert(rc == 1); - shiftl(buf, req); - size_t slen = 0; - rc = EVP_DigestSignFinal(cstate->sign_context, rawBufMut(buf), &slen); - assert(rc == 1); - assert(req == slen == kSignLen); -} - -static shift_buffer_t *genericDecrypt(shift_buffer_t *in, reailty_client_con_state_t *cstate, char *password, - buffer_pool_t *pool) -{ - shift_buffer_t *out = popBuffer(pool); - uint16_t input_length = bufLen(in); - - uint8_t iv[kIVlen]; - memcpy(iv, rawBuf(in), kIVlen); - shiftr(in, kIVlen); - - EVP_DecryptInit_ex(cstate->decryption_context, EVP_aes_128_cbc(), NULL, (const uint8_t *) password, - (const uint8_t *) iv); - - reserveBufSpace(out, input_length + (2 * kEncryptionBlockSize)); - int out_len = 0; - - /* - * Provide the message to be decrypted, and obtain the plaintext output. - * EVP_DecryptUpdate can be called multiple times if necessary. - */ - if (1 != EVP_DecryptUpdate(cstate->decryption_context, rawBufMut(out), &out_len, rawBuf(in), input_length)) - { - printSSLErrorAndAbort(); - } - setLen(out, out_len); - - /* - * Finalise the decryption. Further plaintext bytes may be written at - * this stage. - */ - if (1 != EVP_DecryptFinal_ex(cstate->decryption_context, rawBufMut(out) + out_len, &out_len)) - { - printSSLErrorAndAbort(); - } - reuseBuffer(pool, in); - - setLen(out, bufLen(out) + out_len); - return out; -} -static shift_buffer_t *genericEncrypt(shift_buffer_t *in, reailty_client_con_state_t *cstate, char *password, - buffer_pool_t *pool) -{ - shift_buffer_t *out = popBuffer(pool); - int input_length = (int) bufLen(in); - - uint8_t iv[kIVlen]; - for (int i; i < kIVlen / sizeof(uint32_t); i++) - { - ((uint32_t *) iv)[i] = fastRand(); - } - - EVP_EncryptInit_ex(cstate->encryption_context, EVP_aes_128_cbc(), NULL, (const uint8_t *) password, - (const uint8_t *) iv); - - reserveBufSpace(out, input_length + (input_length % kEncryptionBlockSize)); - int out_len = 0; - - /* - * Provide the message to be encrypted, and obtain the encrypted output. - * EVP_EncryptUpdate can be called multiple times if necessary - */ - if (1 != EVP_EncryptUpdate(cstate->encryption_context, rawBufMut(out), &out_len, rawBuf(in), input_length)) - { - printSSLErrorAndAbort(); - } - - setLen(out, bufLen(out) + out_len); - - /* - * Finalise the encryption. Further ciphertext bytes may be written at - * this stage. - */ - if (1 != EVP_EncryptFinal_ex(cstate->encryption_context, rawBufMut(out) + out_len, &out_len)) - { - printSSLErrorAndAbort(); - } - reuseBuffer(pool, in); - setLen(out, bufLen(out) + out_len); - - shiftl(out, kIVlen); - memcpy(rawBufMut(out), iv, kIVlen); - return out; -} - -static void appendTlsHeader(shift_buffer_t *buf) -{ - unsigned int data_length = bufLen(buf); - assert(data_length < (1U << 16)); - - shiftl(buf, sizeof(uint16_t)); - writeUI16(buf, (uint16_t) data_length); - - shiftl(buf, sizeof(uint16_t)); - writeUI16(buf, kTLSVersion12); - - shiftl(buf, sizeof(uint8_t)); - writeUI8(buf, kTLS12ApplicationData); -} - static void upStream(tunnel_t *self, context_t *c) { reailty_client_state_t *state = STATE(self); @@ -275,8 +128,8 @@ static void upStream(tunnel_t *self, context_t *c) shift_buffer_t *chunk = shallowSliceBuffer(buf, remain); shiftl(chunk, 2); writeUI16(chunk, remain); - chunk = genericEncrypt(chunk, cstate, state->context_password, getContextBufferPool(c)); - signMessage(chunk, cstate); + chunk = genericEncrypt(chunk, cstate->encryption_context, state->context_password, getContextBufferPool(c)); + signMessage(chunk, cstate->msg_digest, cstate->sign_context, cstate->sign_key); appendTlsHeader(chunk); context_t *cout = newContextFrom(c); cout->payload = chunk; @@ -394,14 +247,15 @@ static void downStream(tunnel_t *self, context_t *c) shift_buffer_t *buf = bufferStreamRead(cstate->read_stream, sizeof(tls_header) + length); shiftr(buf, sizeof(tls_header)); - if (! verifyMessage(buf, cstate)) + if (! verifyMessage(buf, cstate->msg_digest, cstate->sign_context, cstate->sign_key)) { LOGE("RealityClient: verifyMessage failed"); reuseBuffer(getContextBufferPool(c), buf); goto failed; } - buf = genericDecrypt(buf, cstate, state->context_password, getContextBufferPool(c)); + buf = genericDecrypt(buf, cstate->decryption_context, state->context_password, + getContextBufferPool(c)); uint16_t length = 0; readUI16(buf, &(length)); shiftr(buf, sizeof(uint16_t)); @@ -528,7 +382,6 @@ static void downStream(tunnel_t *self, context_t *c) // queue is flushed and we are done } } - } // done with socket data reuseContextBuffer(c); diff --git a/tunnels/server/reality/CMakeLists.txt b/tunnels/server/reality/CMakeLists.txt index 84b234d3..f02f4222 100644 --- a/tunnels/server/reality/CMakeLists.txt +++ b/tunnels/server/reality/CMakeLists.txt @@ -11,6 +11,7 @@ target_include_directories(RealityServer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../. target_link_libraries(RealityServer ww) target_include_directories(RealityServer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../shared/openssl) +target_include_directories(RealityServer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../shared/reality) # add dependencies diff --git a/tunnels/server/reality/reality_server.c b/tunnels/server/reality/reality_server.c index fd742475..01ee545c 100644 --- a/tunnels/server/reality/reality_server.c +++ b/tunnels/server/reality/reality_server.c @@ -2,29 +2,17 @@ #include "basic_types.h" #include "buffer_pool.h" #include "buffer_stream.h" -#include "frand.h" -#include "hloop.h" #include "loggers/network_logger.h" #include "managers/node_manager.h" -#include "openssl_globals.h" +#include "reality_helpers.h" #include "shiftbuffer.h" #include "tunnel.h" #include "utils/jsonutils.h" +#include "utils/mathutils.h" #include #include #include -enum reality_consts -{ - kEncryptionBlockSize = 16, - kSignLen = (224 / 8), - kSignPasswordLen = kEncryptionBlockSize, - kIVlen = 16, // iv size for *most* modes is the same as the block size. For AES this is 128 bits - kTLSVersion12 = 0x0303, - kTLS12ApplicationData = 0x17, - kTLSHeaderlen = 1 + 2 + 2, -}; - enum connection_auth_state { kConAuthPending, @@ -98,137 +86,7 @@ static void cleanup(tunnel_t *self, context_t *c) CSTATE_MUT(c) = NULL; } } -static bool verifyMessage(shift_buffer_t *buf, reality_server_con_state_t *cstate) -{ - int rc = EVP_DigestSignInit(cstate->sign_context, NULL, cstate->msg_digest, NULL, cstate->sign_key); - uint8_t expect[EVP_MAX_MD_SIZE]; - memcpy(expect, rawBuf(buf), kSignLen); - shiftr(buf, kSignLen); - assert(rc == 1); - rc = EVP_DigestSignUpdate(cstate->sign_context, rawBuf(buf), bufLen(buf)); - assert(rc == 1); - uint8_t buff[EVP_MAX_MD_SIZE]; - size_t size = sizeof(buff); - rc = EVP_DigestSignFinal(cstate->sign_context, buff, &size); - assert(rc == 1); - assert(size == kSignLen); - return ! ! CRYPTO_memcmp(expect, buff, size); -} - -static void signMessage(shift_buffer_t *buf, reality_server_con_state_t *cstate) -{ - int rc = EVP_DigestSignInit(cstate->sign_context, NULL, cstate->msg_digest, NULL, cstate->sign_key); - assert(rc == 1); - rc = EVP_DigestSignUpdate(cstate->sign_context, rawBuf(buf), bufLen(buf)); - assert(rc == 1); - size_t req = 0; - rc = EVP_DigestSignFinal(cstate->sign_context, NULL, &req); - assert(rc == 1); - shiftl(buf, req); - size_t slen = 0; - rc = EVP_DigestSignFinal(cstate->sign_context, rawBufMut(buf), &slen); - assert(rc == 1); - assert(req == slen == kSignLen); -} - -static shift_buffer_t *genericDecrypt(shift_buffer_t *in, reality_server_con_state_t *cstate, char *password, - buffer_pool_t *pool) -{ - shift_buffer_t *out = popBuffer(pool); - uint16_t input_length = bufLen(in); - - uint8_t iv[kIVlen]; - memcpy(iv, rawBuf(in), kIVlen); - shiftr(in, kIVlen); - - EVP_DecryptInit_ex(cstate->decryption_context, EVP_aes_128_cbc(), NULL, (const uint8_t *) password, - (const uint8_t *) iv); - - reserveBufSpace(out, input_length + (2 * kEncryptionBlockSize)); - int out_len = 0; - - /* - * Provide the message to be decrypted, and obtain the plaintext output. - * EVP_DecryptUpdate can be called multiple times if necessary. - */ - if (1 != EVP_DecryptUpdate(cstate->decryption_context, rawBufMut(out), &out_len, rawBuf(in), input_length)) - { - printSSLErrorAndAbort(); - } - setLen(out, out_len); - - /* - * Finalise the decryption. Further plaintext bytes may be written at - * this stage. - */ - if (1 != EVP_DecryptFinal_ex(cstate->decryption_context, rawBufMut(out) + out_len, &out_len)) - { - printSSLErrorAndAbort(); - } - reuseBuffer(pool, in); - - setLen(out, bufLen(out) + out_len); - return out; -} -static shift_buffer_t *genericEncrypt(shift_buffer_t *in, reality_server_con_state_t *cstate, char *password, - buffer_pool_t *pool) -{ - shift_buffer_t *out = popBuffer(pool); - int input_length = (int) bufLen(in); - - uint8_t iv[kIVlen]; - for (int i; i < kIVlen / sizeof(uint32_t); i++) - { - ((uint32_t *) iv)[i] = fastRand(); - } - - EVP_EncryptInit_ex(cstate->encryption_context, EVP_aes_128_cbc(), NULL, (const uint8_t *) password, - (const uint8_t *) iv); - - reserveBufSpace(out, input_length + (input_length % kEncryptionBlockSize)); - int out_len = 0; - - /* - * Provide the message to be encrypted, and obtain the encrypted output. - * EVP_EncryptUpdate can be called multiple times if necessary - */ - if (1 != EVP_EncryptUpdate(cstate->encryption_context, rawBufMut(out), &out_len, rawBuf(in), input_length)) - { - printSSLErrorAndAbort(); - } - - setLen(out, bufLen(out) + out_len); - - /* - * Finalise the encryption. Further ciphertext bytes may be written at - * this stage. - */ - if (1 != EVP_EncryptFinal_ex(cstate->encryption_context, rawBufMut(out) + out_len, &out_len)) - { - printSSLErrorAndAbort(); - } - reuseBuffer(pool, in); - setLen(out, bufLen(out) + out_len); - - shiftl(out, kIVlen); - memcpy(rawBufMut(out), iv, kIVlen); - return out; -} - -static void appendTlsHeader(shift_buffer_t *buf) -{ - unsigned int data_length = bufLen(buf); - assert(data_length < (1U << 16)); - - shiftl(buf, sizeof(uint16_t)); - writeUI16(buf, (uint16_t) data_length); - shiftl(buf, sizeof(uint16_t)); - writeUI16(buf, kTLSVersion12); - - shiftl(buf, sizeof(uint8_t)); - writeUI8(buf, kTLS12ApplicationData); -} static void upStream(tunnel_t *self, context_t *c) { reality_server_state_t *state = STATE(self); @@ -243,7 +101,7 @@ static void upStream(tunnel_t *self, context_t *c) shift_buffer_t *buf = c->payload; shiftr(buf, kTLSHeaderlen); - bool valid = verifyMessage(buf, cstate); + bool valid = verifyMessage(buf, cstate->msg_digest, cstate->sign_context, cstate->sign_key); shiftl(buf, kTLSHeaderlen); if (valid) @@ -284,14 +142,15 @@ static void upStream(tunnel_t *self, context_t *c) shift_buffer_t *buf = bufferStreamRead(cstate->read_stream, sizeof(tls_header) + length); shiftr(buf, sizeof(tls_header)); - if (! verifyMessage(buf, cstate)) + if (! verifyMessage(buf, cstate->msg_digest, cstate->sign_context, cstate->sign_key)) { LOGE("RealityServer: verifyMessage failed"); reuseBuffer(getContextBufferPool(c), buf); goto failed; } - buf = genericDecrypt(buf, cstate, state->context_password, getContextBufferPool(c)); + buf = genericDecrypt(buf, cstate->decryption_context, state->context_password, + getContextBufferPool(c)); uint16_t length = 0; readUI16(buf, &(length)); shiftr(buf, sizeof(uint16_t)); @@ -300,7 +159,7 @@ static void upStream(tunnel_t *self, context_t *c) context_t *plain_data_ctx = newContextFrom(c); plain_data_ctx->payload = buf; - self->dw->downStream(self->dw, plain_data_ctx); + self->up->upStream(self->up, plain_data_ctx); } else { @@ -362,21 +221,28 @@ static void downStream(tunnel_t *self, context_t *c) { case kConAuthPending: case kConUnAuthorized: - - if (cstate->send_counter != 2) - { - cstate->send_counter += 1; - if (cstate->send_counter == 2) - { - LOGD("RealityServer: second reply was %d bytes", (int) bufLen(c->payload)); - cstate->magic = CALC_HASH_BYTES(rawBuf(c->payload), bufLen(c->payload)); - LOGD("RealityServer: magic = %02x", cstate->magic); - } - } self->dw->downStream(self->dw, c); - break; - case kConAuthorized: + case kConAuthorized:; + shift_buffer_t *buf = c->payload; + while (bufLen(buf) > 0) + { + const int chunk_size = ((1 << 16) - (1 + kSignLen + kEncryptionBlockSize + kIVlen)); + const uint16_t remain = (uint16_t) min(bufLen(buf), chunk_size); + shift_buffer_t *chunk = shallowSliceBuffer(buf, remain); + shiftl(chunk, 2); + writeUI16(chunk, remain); + chunk = + genericEncrypt(chunk, cstate->encryption_context, state->context_password, getContextBufferPool(c)); + signMessage(chunk, cstate->msg_digest, cstate->sign_context, cstate->sign_key); + appendTlsHeader(chunk); + context_t *cout = newContextFrom(c); + cout->payload = chunk; + assert(bufLen(chunk) % 16 == 5); + self->dw->downStream(self->dw, cout); + } + reuseContextBuffer(c); + destroyContext(c); break; } @@ -426,15 +292,12 @@ tunnel_t *newRealityServer(node_instance_context_t *instance_info) } // memset already made buff 0 memcpy(state->context_password, state->password, state->password_length); - - state->hash1 = CALC_HASH_BYTES(state->password, strlen(state->password)); - state->hash2 = CALC_HASH_PRIMITIVE(state->hash1); - state->hash3 = CALC_HASH_PRIMITIVE(state->hash2); - // the iv must be unpredictable, so initializing it from password - for (int i = 0; i < kSignIVlen; i++) + assert(EVP_MAX_MD_SIZE % sizeof(uint64_t) == 0); + uint64_t *p64 = (uint64_t *) state->hashes; + p64[0] = CALC_HASH_BYTES(state->password, strlen(state->password)); + for (int i = 1; i < EVP_MAX_MD_SIZE / sizeof(uint64_t); i++) { - const uint8_t seed = (uint8_t) (state->hash3 * (i + 7)); - state->context_base_iv[i] = (uint8_t) (CALC_HASH_PRIMITIVE(seed)); + p64[i] = p64[i - 1]; } char *dest_node_name = NULL; diff --git a/tunnels/shared/reality/reality_helpers.h b/tunnels/shared/reality/reality_helpers.h new file mode 100644 index 00000000..b8ce75a3 --- /dev/null +++ b/tunnels/shared/reality/reality_helpers.h @@ -0,0 +1,150 @@ +#pragma once +#include "buffer_pool.h" +#include "frand.h" +#include "openssl_globals.h" /* These helpers depened on openssl */ +#include +#include +#include +#include + +enum reality_consts +{ + kEncryptionBlockSize = 16, + kSignPasswordLen = kEncryptionBlockSize, + kIVlen = 16, // iv size for *most* modes is the same as the block size. For AES this is 128 bits + kSignLen = (224 / 8), + kTLSVersion12 = 0x0303, + kTLS12ApplicationData = 0x17, + kTLSHeaderlen = 1 + 2 + 2, + +}; + +static bool verifyMessage(shift_buffer_t *buf, EVP_MD *msg_digest, EVP_MD_CTX *sign_context, EVP_PKEY *sign_key) +{ + int rc = EVP_DigestSignInit(sign_context, NULL, msg_digest, NULL, sign_key); + uint8_t expect[EVP_MAX_MD_SIZE]; + memcpy(expect, rawBuf(buf), kSignLen); + shiftr(buf, kSignLen); + assert(rc == 1); + rc = EVP_DigestSignUpdate(sign_context, rawBuf(buf), bufLen(buf)); + assert(rc == 1); + uint8_t buff[EVP_MAX_MD_SIZE]; + size_t size = sizeof(buff); + rc = EVP_DigestSignFinal(sign_context, buff, &size); + assert(rc == 1); + assert(size == kSignLen); + return ! ! CRYPTO_memcmp(expect, buff, size); +} + +static void signMessage(shift_buffer_t *buf, EVP_MD *msg_digest, EVP_MD_CTX *sign_context, EVP_PKEY *sign_key) +{ + int rc = EVP_DigestSignInit(sign_context, NULL, msg_digest, NULL, sign_key); + assert(rc == 1); + rc = EVP_DigestSignUpdate(sign_context, rawBuf(buf), bufLen(buf)); + assert(rc == 1); + size_t req = 0; + rc = EVP_DigestSignFinal(sign_context, NULL, &req); + assert(rc == 1); + shiftl(buf, req); + size_t slen = 0; + rc = EVP_DigestSignFinal(sign_context, rawBufMut(buf), &slen); + assert(rc == 1); + assert(req == slen == kSignLen); +} + +static shift_buffer_t *genericDecrypt(shift_buffer_t *in, EVP_CIPHER_CTX *decryption_context, char *password, + buffer_pool_t *pool) +{ + shift_buffer_t *out = popBuffer(pool); + uint16_t input_length = bufLen(in); + + uint8_t iv[kIVlen]; + memcpy(iv, rawBuf(in), kIVlen); + shiftr(in, kIVlen); + + EVP_DecryptInit_ex(decryption_context, EVP_aes_128_cbc(), NULL, (const uint8_t *) password, (const uint8_t *) iv); + + reserveBufSpace(out, input_length + (2 * kEncryptionBlockSize)); + int out_len = 0; + + /* + * Provide the message to be decrypted, and obtain the plaintext output. + * EVP_DecryptUpdate can be called multiple times if necessary. + */ + if (1 != EVP_DecryptUpdate(decryption_context, rawBufMut(out), &out_len, rawBuf(in), input_length)) + { + printSSLErrorAndAbort(); + } + setLen(out, out_len); + + /* + * Finalise the decryption. Further plaintext bytes may be written at + * this stage. + */ + if (1 != EVP_DecryptFinal_ex(decryption_context, rawBufMut(out) + out_len, &out_len)) + { + printSSLErrorAndAbort(); + } + reuseBuffer(pool, in); + + setLen(out, bufLen(out) + out_len); + return out; +} +static shift_buffer_t *genericEncrypt(shift_buffer_t *in, EVP_CIPHER_CTX *encryption_context, char *password, + buffer_pool_t *pool) +{ + shift_buffer_t *out = popBuffer(pool); + int input_length = (int) bufLen(in); + + uint8_t iv[kIVlen]; + for (int i; i < kIVlen / sizeof(uint32_t); i++) + { + ((uint32_t *) iv)[i] = fastRand(); + } + + EVP_EncryptInit_ex(encryption_context, EVP_aes_128_cbc(), NULL, (const uint8_t *) password, (const uint8_t *) iv); + + reserveBufSpace(out, input_length + (input_length % kEncryptionBlockSize)); + int out_len = 0; + + /* + * Provide the message to be encrypted, and obtain the encrypted output. + * EVP_EncryptUpdate can be called multiple times if necessary + */ + if (1 != EVP_EncryptUpdate(encryption_context, rawBufMut(out), &out_len, rawBuf(in), input_length)) + { + printSSLErrorAndAbort(); + } + + setLen(out, bufLen(out) + out_len); + + /* + * Finalise the encryption. Further ciphertext bytes may be written at + * this stage. + */ + if (1 != EVP_EncryptFinal_ex(encryption_context, rawBufMut(out) + out_len, &out_len)) + { + printSSLErrorAndAbort(); + } + reuseBuffer(pool, in); + setLen(out, bufLen(out) + out_len); + + shiftl(out, kIVlen); + memcpy(rawBufMut(out), iv, kIVlen); + return out; +} + +static void appendTlsHeader(shift_buffer_t *buf) +{ + unsigned int data_length = bufLen(buf); + assert(data_length < (1U << 16)); + + shiftl(buf, sizeof(uint16_t)); + writeUI16(buf, (uint16_t) data_length); + + shiftl(buf, sizeof(uint16_t)); + writeUI16(buf, kTLSVersion12); + + shiftl(buf, sizeof(uint8_t)); + writeUI8(buf, kTLS12ApplicationData); +}