| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- /*
- * Embedded Linux library
- *
- * Copyright (C) 2015 Intel Corporation. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <alloca.h>
- #include "private.h"
- #include "tls.h"
- #include "checksum.h"
- #include "cipher.h"
- #include "cert.h"
- #include "tls-private.h"
- #include "random.h"
- /* Implementation-specific max Record Layer fragment size (must be < 16kB) */
- #define TX_RECORD_MAX_LEN 4096
- /* TLSPlaintext + TLSCompressed + TLSCiphertext headers + seq_num sizes */
- #define TX_RECORD_MAX_HEADERS (5 + 5 + 8 + 5)
- #define TX_RECORD_MAX_MAC 64
- /* Head room and tail room for the buffer passed to the cipher */
- #define TX_RECORD_HEADROOM TX_RECORD_MAX_HEADERS
- #define TX_RECORD_TAILROOM TX_RECORD_MAX_MAC
- static void tls_write_mac(struct l_tls *tls, uint8_t *compressed,
- uint16_t compressed_len, uint8_t *out_buf,
- bool txrx)
- {
- uint8_t *in_buf;
- /* Prepend the sequence number to the TLSCompressed buffer */
- in_buf = compressed - 8;
- l_put_be64(tls->seq_num[txrx]++, in_buf);
- if (tls->mac[txrx]) {
- l_checksum_reset(tls->mac[txrx]);
- l_checksum_update(tls->mac[txrx], in_buf, compressed_len + 8);
- l_checksum_get_digest(tls->mac[txrx], out_buf,
- tls->mac_length[txrx]);
- }
- }
- static void tls_tx_record_plaintext(struct l_tls *tls,
- uint8_t *plaintext,
- uint16_t plaintext_len)
- {
- uint8_t *compressed;
- uint16_t compressed_len;
- uint8_t *cipher_input;
- uint16_t cipher_input_len;
- uint8_t *ciphertext;
- uint16_t ciphertext_len;
- uint8_t padding_length;
- uint8_t buf[TX_RECORD_HEADROOM + TX_RECORD_MAX_LEN +
- TX_RECORD_TAILROOM];
- uint8_t iv[32];
- uint8_t *assocdata;
- int offset;
- /*
- * TODO: if DEFLATE is selected in current state, use a new buffer
- * on stack to write a TLSCompressed structure there, otherwise use
- * the provided buffer. Since only null compression is supported
- * today we always use the provided buffer.
- */
- compressed_len = plaintext_len - 5;
- compressed = plaintext;
- /* Build a TLSCompressed struct */
- compressed[0] = plaintext[0]; /* Copy type and version fields */
- compressed[1] = plaintext[1];
- compressed[2] = plaintext[2];
- compressed[3] = compressed_len >> 8;
- compressed[4] = compressed_len >> 0;
- switch (tls->cipher_type[1]) {
- case TLS_CIPHER_STREAM:
- /* Append the MAC after TLSCompressed.fragment, if needed */
- tls_write_mac(tls, compressed, compressed_len + 5,
- compressed + compressed_len + 5, true);
- cipher_input = compressed + 5;
- cipher_input_len = compressed_len + tls->mac_length[1];
- if (!tls->cipher[1]) {
- ciphertext = cipher_input;
- ciphertext_len = cipher_input_len;
- } else {
- ciphertext = buf + TX_RECORD_HEADROOM;
- ciphertext_len = cipher_input_len;
- l_cipher_encrypt(tls->cipher[1], cipher_input,
- ciphertext, cipher_input_len);
- }
- break;
- case TLS_CIPHER_BLOCK:
- /* Append the MAC after TLSCompressed.fragment, if needed */
- cipher_input = compressed + 5;
- tls_write_mac(tls, compressed, compressed_len + 5,
- cipher_input + compressed_len, true);
- cipher_input_len = compressed_len + tls->mac_length[1];
- /* Add minimum padding */
- padding_length = (~cipher_input_len) &
- (tls->block_length[1] - 1);
- memset(cipher_input + cipher_input_len, padding_length,
- padding_length + 1);
- cipher_input_len += padding_length + 1;
- /* Generate an IV */
- ciphertext = buf + TX_RECORD_HEADROOM;
- offset = 0;
- if (tls->negotiated_version >= L_TLS_V12) {
- l_getrandom(ciphertext, tls->record_iv_length[1]);
- l_cipher_set_iv(tls->cipher[1], ciphertext,
- tls->record_iv_length[1]);
- offset = tls->record_iv_length[1];
- } else if (tls->negotiated_version >= L_TLS_V11) {
- l_getrandom(iv, tls->record_iv_length[1]);
- l_cipher_encrypt(tls->cipher[1], iv, ciphertext,
- tls->record_iv_length[1]);
- offset = tls->record_iv_length[1];
- }
- l_cipher_encrypt(tls->cipher[1], cipher_input,
- ciphertext + offset, cipher_input_len);
- ciphertext_len = offset + cipher_input_len;
- break;
- case TLS_CIPHER_AEAD:
- /* Prepend seq_num to TLSCompressed.type + .version + .length */
- assocdata = compressed - 8;
- l_put_be64(tls->seq_num[1]++, assocdata);
- cipher_input = compressed + 5;
- cipher_input_len = compressed_len;
- /*
- * Build the IV. The explicit part generation method is
- * actually cipher suite-specific but our only AEAD cipher
- * suites only require this part to be unique for each
- * record. For future suites there may need to be a callback
- * that generates the per-record IV or an enum for the suite
- * to select one of a few IV types.
- *
- * Note kernel's rfc4106(gcm(...)) algorithm could potentially
- * be used to build the IV.
- */
- memcpy(iv, tls->fixed_iv[1], tls->fixed_iv_length[1]);
- l_put_le64(tls->seq_num[1], iv + tls->fixed_iv_length[1]);
- if (tls->record_iv_length[1] > 8)
- memset(iv + tls->fixed_iv_length[1] + 8, 42,
- tls->record_iv_length[1] - 8);
- /* Build the GenericAEADCipher struct */
- ciphertext = buf + TX_RECORD_HEADROOM;
- memcpy(ciphertext, iv + tls->fixed_iv_length[1],
- tls->record_iv_length[1]);
- l_aead_cipher_encrypt(tls->aead_cipher[1],
- cipher_input, cipher_input_len,
- assocdata, 13,
- iv, tls->fixed_iv_length[1] +
- tls->record_iv_length[1],
- ciphertext + tls->record_iv_length[1],
- cipher_input_len +
- tls->auth_tag_length[1]);
- ciphertext_len = tls->record_iv_length[1] +
- cipher_input_len + tls->auth_tag_length[1];
- break;
- default:
- return;
- }
- /* Build a TLSCiphertext struct */
- ciphertext -= 5;
- ciphertext[0] = plaintext[0]; /* Copy type and version fields */
- ciphertext[1] = plaintext[1];
- ciphertext[2] = plaintext[2];
- ciphertext[3] = ciphertext_len >> 8;
- ciphertext[4] = ciphertext_len >> 0;
- tls->tx(ciphertext, ciphertext_len + 5, tls->user_data);
- }
- void tls_tx_record(struct l_tls *tls, enum tls_content_type type,
- const uint8_t *data, size_t len)
- {
- uint8_t buf[TX_RECORD_HEADROOM + TX_RECORD_MAX_LEN +
- TX_RECORD_TAILROOM];
- uint8_t *fragment, *plaintext;
- uint16_t fragment_len;
- uint16_t version = tls->negotiated_version ?: tls->min_version;
- if (type == TLS_CT_ALERT)
- tls->record_flush = true;
- while (len) {
- fragment = buf + TX_RECORD_HEADROOM;
- fragment_len = len < TX_RECORD_MAX_LEN ?
- len : TX_RECORD_MAX_LEN;
- /* Build a TLSPlaintext struct */
- plaintext = fragment - 5;
- plaintext[0] = type;
- plaintext[1] = (uint8_t) (version >> 8);
- plaintext[2] = (uint8_t) (version >> 0);
- plaintext[3] = fragment_len >> 8;
- plaintext[4] = fragment_len >> 0;
- memcpy(plaintext + 5, data, fragment_len);
- tls_tx_record_plaintext(tls, plaintext, fragment_len + 5);
- data += fragment_len;
- len -= fragment_len;
- }
- }
- static bool tls_handle_plaintext(struct l_tls *tls, const uint8_t *plaintext,
- int len, uint8_t type, uint16_t version)
- {
- if (len > (1 << 14)) {
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
- "Plaintext message too long: %i", len);
- return false;
- }
- switch (type) {
- case TLS_CT_CHANGE_CIPHER_SPEC:
- case TLS_CT_APPLICATION_DATA:
- return tls_handle_message(tls, plaintext, len, type, version);
- /*
- * We need to perform input reassembly twice at different levels:
- * once to make sure we're handling complete TLSCiphertext messages,
- * in l_tls_handle_rx(), and again here so that the Alert and
- * Handshake message type handlers deal with complete messages.
- * This does not affect ChangeCipherSpec messages because they're
- * just a single byte and there are never more than one such message
- * in a row. Similarly it doesn't affect application data because
- * the application is not guaranteed that message boundaries are
- * preserved in any way and we don't know its message lengths anyway.
- * It does affect Alert because these messages are 2 byte long and
- * could potentially be split over two TLSPlaintext messages but
- * there are never more than one Alert in a TLSPlaintext for the same
- * reason as with ChangeCipherSpec. Handshake messages are the
- * most affected although the need to do the reassembly twice still
- * seems wasteful considering most of these messages are sent in
- * plaintext and TLSCiphertext maps to TLSPlaintext records.
- */
- case TLS_CT_ALERT:
- case TLS_CT_HANDSHAKE:
- break;
- default:
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
- "Unknown content type %i", type);
- return false;
- }
- if (tls->message_buf_len && type != tls->message_content_type) {
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
- "Message fragment type %i doesn't match "
- "previous type %i", type,
- tls->message_content_type);
- return false;
- }
- tls->message_content_type = type;
- while (1) {
- int header_len, need_len;
- int chunk_len;
- /* Do we have a full header in tls->message_buf? */
- header_len = (type == TLS_CT_ALERT) ? 2 : 4;
- need_len = header_len;
- if (tls->message_buf_len >= header_len) {
- if (type == TLS_CT_HANDSHAKE) {
- uint32_t hs_len = (tls->message_buf[1] << 16) |
- (tls->message_buf[2] << 8) |
- (tls->message_buf[3] << 0);
- if (hs_len > (1 << 14)) {
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR,
- 0, "Handshake message "
- "too long: %i",
- (int) hs_len);
- return false;
- }
- need_len += hs_len;
- }
- /* Do we have a full structure? */
- if (tls->message_buf_len == need_len) {
- if (!tls_handle_message(tls, tls->message_buf,
- need_len, type,
- version))
- return false;
- tls->message_buf_len = 0;
- if (tls->record_flush)
- break;
- continue;
- }
- if (!len)
- break;
- }
- /* Try to fill up tls->message_buf up to need_len */
- if (tls->message_buf_max_len < need_len) {
- tls->message_buf_max_len = need_len;
- tls->message_buf =
- l_realloc(tls->message_buf, need_len);
- }
- need_len -= tls->message_buf_len;
- chunk_len = need_len;
- if (len < chunk_len)
- chunk_len = len;
- memcpy(tls->message_buf + tls->message_buf_len, plaintext,
- chunk_len);
- tls->message_buf_len += chunk_len;
- plaintext += chunk_len;
- len -= chunk_len;
- if (chunk_len < need_len)
- break;
- }
- return true;
- }
- static bool tls_handle_ciphertext(struct l_tls *tls)
- {
- uint8_t type;
- uint16_t version;
- uint16_t fragment_len;
- uint8_t mac_buf[TX_RECORD_MAX_MAC], i, padding_len;
- int cipher_output_len, error;
- uint8_t *compressed;
- int compressed_len;
- uint8_t iv[32];
- uint8_t *assocdata;
- type = tls->record_buf[0];
- version = l_get_be16(tls->record_buf + 1);
- fragment_len = l_get_be16(tls->record_buf + 3);
- if (fragment_len > (1 << 14) + 2048) {
- TLS_DISCONNECT(TLS_ALERT_RECORD_OVERFLOW, 0,
- "Record fragment too long: %u", fragment_len);
- return false;
- }
- if ((tls->negotiated_version && tls->negotiated_version != version) ||
- (!tls->negotiated_version &&
- tls->record_buf[1] != 0x03 /* Appending E.1 */)) {
- TLS_DISCONNECT(TLS_ALERT_PROTOCOL_VERSION, 0,
- "Record version mismatch: %02x", version);
- return false;
- }
- if (fragment_len < tls->mac_length[0]) {
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
- "Record fragment too short: %u", fragment_len);
- return false;
- }
- compressed = alloca(8 + 5 + fragment_len);
- /* Copy the type and version fields */
- compressed[8] = type;
- l_put_be16(version, compressed + 9);
- switch (tls->cipher_type[0]) {
- case TLS_CIPHER_STREAM:
- cipher_output_len = fragment_len;
- compressed_len = cipher_output_len - tls->mac_length[0];
- l_put_be16(compressed_len, compressed + 11);
- if (!tls->cipher[0])
- memcpy(compressed + 13, tls->record_buf + 5,
- cipher_output_len);
- else if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5,
- compressed + 13,
- cipher_output_len)) {
- TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
- "Decrypting record fragment failed");
- return false;
- }
- /* Calculate the MAC if needed */
- tls_write_mac(tls, compressed + 8, 5 + compressed_len,
- mac_buf, false);
- if (memcmp(mac_buf, compressed + 13 + compressed_len,
- tls->mac_length[0])) {
- TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0,
- "Record fragment MAC mismatch");
- return false;
- }
- compressed += 13;
- break;
- case TLS_CIPHER_BLOCK:
- i = 0;
- if (tls->negotiated_version >= L_TLS_V11)
- i = tls->record_iv_length[0];
- if (fragment_len <= tls->mac_length[0] + i) {
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
- "Record fragment too short: %u",
- fragment_len);
- return false;
- }
- cipher_output_len = fragment_len - i;
- if (cipher_output_len % tls->block_length[0] != 0) {
- /*
- * In strict TLS 1.0 TLS_ALERT_DECRYPT_FAIL_RESERVED
- * should be returned here but that was declared
- * unsafe in the TLS 1.1 spec.
- */
- TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0,
- "Fragment data len %i not a multiple "
- "of block length %zi",
- cipher_output_len,
- tls->block_length[0]);
- return false;
- }
- if (tls->negotiated_version >= L_TLS_V12) {
- if (!l_cipher_set_iv(tls->cipher[0],
- tls->record_buf + 5,
- tls->record_iv_length[0])) {
- TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
- "Setting fragment IV failed");
- return false;
- }
- } else if (tls->negotiated_version >= L_TLS_V11)
- if (!l_cipher_decrypt(tls->cipher[0],
- tls->record_buf + 5, iv,
- tls->record_iv_length[0])) {
- TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
- "Setting fragment IV failed");
- return false;
- }
- if (!l_cipher_decrypt(tls->cipher[0], tls->record_buf + 5 + i,
- compressed + 13, cipher_output_len)) {
- TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
- "Fragment decryption failed");
- return false;
- }
- /*
- * RFC 5246, page 24:
- * In order to defend against this attack, implementations
- * MUST ensure that record processing time is essentially the
- * same whether or not the padding is correct. In general,
- * the best way to do this is to compute the MAC even if the
- * padding is incorrect, and only then reject the packet. For
- * instance, if the pad appears to be incorrect, the
- * implementation might assume a zero-length pad and then
- * compute the MAC.
- */
- padding_len = compressed[13 + cipher_output_len - 1];
- error = 0;
- if (padding_len + tls->mac_length[0] + 1 >
- (size_t) cipher_output_len) {
- /*
- * In strict TLS 1.0 TLS_ALERT_DECRYPT_FAIL_RESERVED
- * should be returned here but that was declared
- * unsafe in the TLS 1.1 spec.
- */
- padding_len = 0;
- error = 1;
- }
- compressed_len = cipher_output_len - 1 - padding_len -
- tls->mac_length[0];
- l_put_be16(compressed_len, compressed + 11);
- error |= !l_secure_memeq(compressed + 13 + cipher_output_len -
- 1 - padding_len, padding_len,
- padding_len);
- /* Calculate the MAC if needed */
- tls_write_mac(tls, compressed + 8, 5 + compressed_len,
- mac_buf, false);
- if ((tls->mac_length[0] && memcmp(mac_buf, compressed + 13 +
- compressed_len, tls->mac_length[0])) ||
- error) {
- TLS_DISCONNECT(TLS_ALERT_BAD_RECORD_MAC, 0,
- "Record fragment MAC mismatch");
- return false;
- }
- compressed += 13;
- break;
- case TLS_CIPHER_AEAD:
- if (fragment_len <= tls->record_iv_length[0] +
- tls->auth_tag_length[0]) {
- TLS_DISCONNECT(TLS_ALERT_DECODE_ERROR, 0,
- "Record fragment too short: %u",
- fragment_len);
- return false;
- }
- compressed_len = fragment_len - tls->record_iv_length[0] -
- tls->auth_tag_length[0];
- l_put_be16(compressed_len, compressed + 11);
- /* Prepend seq_num to TLSCompressed.type + .version + .length */
- assocdata = compressed;
- l_put_be64(tls->seq_num[0]++, assocdata);
- compressed += 13;
- /* Build the IV */
- memcpy(iv, tls->fixed_iv[0], tls->fixed_iv_length[0]);
- memcpy(iv + tls->fixed_iv_length[0], tls->record_buf + 5,
- tls->record_iv_length[0]);
- if (!l_aead_cipher_decrypt(tls->aead_cipher[0],
- tls->record_buf + 5 + tls->record_iv_length[0],
- fragment_len - tls->record_iv_length[0],
- assocdata, 13, iv, tls->fixed_iv_length[0] +
- tls->record_iv_length[0],
- compressed, compressed_len)) {
- TLS_DISCONNECT(TLS_ALERT_INTERNAL_ERROR, 0,
- "Decrypting record fragment failed");
- return false;
- }
- break;
- default:
- return false;
- }
- /* DEFLATE not supported so just pass on compressed / compressed_len */
- return tls_handle_plaintext(tls, compressed, compressed_len,
- type, version);
- }
- LIB_EXPORT void l_tls_handle_rx(struct l_tls *tls, const uint8_t *data,
- size_t len)
- {
- int need_len;
- int chunk_len;
- tls->record_flush = false;
- /* Reassemble TLSCiphertext structures from the received chunks */
- while (1) {
- /* Do we have a full header in tls->record_buf? */
- if (tls->record_buf_len >= 5) {
- need_len = 5 + l_get_be16(tls->record_buf + 3);
- /* Do we have a full structure? */
- if (tls->record_buf_len == need_len) {
- if (!tls_handle_ciphertext(tls))
- return;
- tls->record_buf_len = 0;
- need_len = 5;
- if (tls->record_flush)
- break;
- }
- if (!len)
- break;
- } else
- need_len = 5;
- /* Try to fill up tls->record_buf up to need_len */
- if (tls->record_buf_max_len < need_len) {
- tls->record_buf_max_len = need_len;
- tls->record_buf = l_realloc(tls->record_buf, need_len);
- }
- need_len -= tls->record_buf_len;
- chunk_len = need_len;
- if (len < (size_t) chunk_len)
- chunk_len = len;
- memcpy(tls->record_buf + tls->record_buf_len, data, chunk_len);
- tls->record_buf_len += chunk_len;
- data += chunk_len;
- len -= chunk_len;
- if (chunk_len < need_len)
- break;
- }
- }
|