| /* |
| * 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 <time.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| #include "util.h" |
| #include "private.h" |
| #include "tls.h" |
| #include "checksum.h" |
| #include "cipher.h" |
| #include "random.h" |
| #include "pem.h" |
| #include "tls-private.h" |
| #include "key.h" |
| #include "asn1-private.h" |
| |
| void tls10_prf(const uint8_t *secret, size_t secret_len, |
| const char *label, |
| const uint8_t *seed, size_t seed_len, |
| uint8_t *out, size_t out_len) |
| { |
| uint8_t p_hash2[out_len]; |
| uint8_t l_s1 = (secret_len + 1) / 2; |
| unsigned int i; |
| |
| tls12_prf(L_CHECKSUM_MD5, 16, |
| secret, l_s1, |
| label, seed, seed_len, |
| out, out_len); |
| tls12_prf(L_CHECKSUM_SHA1, 20, |
| secret + secret_len - l_s1, l_s1, |
| label, seed, seed_len, |
| p_hash2, out_len); |
| |
| for (i = 0; i < out_len; i++) |
| out[i] ^= p_hash2[i]; |
| } |
| |
| void tls12_prf(enum l_checksum_type type, size_t hash_len, |
| const uint8_t *secret, size_t secret_len, |
| const char *label, |
| const uint8_t *seed, size_t seed_len, |
| uint8_t *out, size_t out_len) |
| { |
| struct l_checksum *hmac = l_checksum_new_hmac(type, secret, secret_len); |
| size_t a_len, chunk_len, prfseed_len = strlen(label) + seed_len; |
| uint8_t a[128], prfseed[prfseed_len]; |
| |
| /* Generate the hash seed or A(0) as label + seed */ |
| memcpy(prfseed, label, strlen(label)); |
| memcpy(prfseed + strlen(label), seed, seed_len); |
| |
| memcpy(a, prfseed, prfseed_len); |
| a_len = prfseed_len; |
| |
| while (out_len) { |
| /* Generate A(i) */ |
| l_checksum_reset(hmac); |
| l_checksum_update(hmac, a, a_len); |
| l_checksum_get_digest(hmac, a, hash_len); |
| a_len = hash_len; |
| |
| /* Append seed & generate output */ |
| memcpy(a + a_len, prfseed, prfseed_len); |
| l_checksum_reset(hmac); |
| l_checksum_update(hmac, a, a_len + prfseed_len); |
| |
| chunk_len = out_len < hash_len ? out_len : hash_len; |
| l_checksum_get_digest(hmac, out, chunk_len); |
| out += chunk_len; |
| out_len -= chunk_len; |
| } |
| |
| l_checksum_free(hmac); |
| } |
| |
| void tls_prf_get_bytes(struct l_tls *tls, |
| enum l_checksum_type type, size_t hash_len, |
| const uint8_t *secret, size_t secret_len, |
| const char *label, |
| const uint8_t *seed, size_t seed_len, |
| uint8_t *buf, size_t len) |
| { |
| if (tls->negotiated_version >= TLS_V12) |
| tls12_prf(type, hash_len, secret, secret_len, label, |
| seed, seed_len, buf, len); |
| else |
| tls10_prf(secret, secret_len, label, seed, seed_len, buf, len); |
| } |
| |
| static void tls_write_random(uint8_t *buf) |
| { |
| l_put_be32(time(NULL), buf); |
| |
| l_getrandom(buf + 4, 28); |
| } |
| |
| static void tls_drop_handshake_hash(struct l_tls *tls, |
| enum handshake_hash_type hash) |
| { |
| if (tls->handshake_hash[hash]) { |
| l_checksum_free(tls->handshake_hash[hash]); |
| |
| tls->handshake_hash[hash] = NULL; |
| } |
| } |
| |
| static void tls_reset_handshake(struct l_tls *tls) |
| { |
| enum handshake_hash_type hash; |
| |
| memset(tls->pending.key_block, 0, sizeof(tls->pending.key_block)); |
| |
| l_free(tls->peer_cert); |
| l_key_free(tls->peer_pubkey); |
| |
| tls->peer_cert = NULL; |
| tls->peer_pubkey = NULL; |
| tls->peer_pubkey_size = 0; |
| |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| tls_drop_handshake_hash(tls, hash); |
| |
| tls->state = TLS_HANDSHAKE_WAIT_HELLO; |
| tls->cert_requested = 0; |
| tls->cert_sent = 0; |
| } |
| |
| static void tls_cleanup_handshake(struct l_tls *tls) |
| { |
| memset(tls->pending.client_random, 0, 32); |
| memset(tls->pending.server_random, 0, 32); |
| memset(tls->pending.master_secret, 0, 48); |
| } |
| |
| static bool tls_change_cipher_spec(struct l_tls *tls, bool txrx) |
| { |
| struct tls_bulk_encryption_algorithm *enc; |
| struct tls_mac_algorithm *mac; |
| int key_offset; |
| |
| if (tls->cipher[txrx]) { |
| l_cipher_free(tls->cipher[txrx]); |
| tls->cipher[txrx] = NULL; |
| tls->cipher_type[txrx] = TLS_CIPHER_STREAM; |
| } |
| |
| if (tls->mac[txrx]) { |
| l_checksum_free(tls->mac[txrx]); |
| tls->mac[txrx] = NULL; |
| } |
| |
| tls->mac_length[txrx] = 0; |
| tls->block_length[txrx] = 0; |
| tls->record_iv_length[txrx] = 0; |
| |
| tls->seq_num[txrx] = 0; |
| |
| tls->cipher_suite[txrx] = tls->pending.cipher_suite; |
| if (!tls->cipher_suite[txrx]) |
| return true; |
| |
| key_offset = 0; |
| |
| if (tls->cipher_suite[txrx]->mac) { |
| mac = tls->cipher_suite[txrx]->mac; |
| |
| /* Server write / client read is 2nd in the key block */ |
| if ((tls->server && txrx) || (!tls->server && !txrx)) |
| key_offset += mac->mac_length; |
| |
| tls->mac[txrx] = l_checksum_new_hmac(mac->hmac_type, |
| tls->pending.key_block + |
| key_offset, mac->mac_length); |
| |
| /* Wipe out the now unneeded part of the key block */ |
| memset(tls->pending.key_block + key_offset, 0, mac->mac_length); |
| |
| if (!tls->mac[txrx]) |
| return false; |
| |
| tls->mac_length[txrx] = mac->mac_length; |
| |
| key_offset = 2 * mac->mac_length; |
| } |
| |
| if (tls->cipher_suite[txrx]->encryption) { |
| enc = tls->cipher_suite[txrx]->encryption; |
| |
| /* Server write / client read is 4th in the key block */ |
| if ((tls->server && txrx) || (!tls->server && !txrx)) |
| key_offset += enc->key_length; |
| |
| tls->cipher[txrx] = l_cipher_new(enc->l_id, |
| tls->pending.key_block + |
| key_offset, enc->key_length); |
| |
| /* Wipe out the now unneeded part of the key block */ |
| memset(tls->pending.key_block + key_offset, 0, enc->key_length); |
| |
| if (!tls->cipher[txrx]) |
| return false; |
| |
| tls->cipher_type[txrx] = enc->cipher_type; |
| if (enc->cipher_type == TLS_CIPHER_BLOCK) { |
| tls->record_iv_length[txrx] = enc->iv_length; |
| tls->block_length[txrx] = enc->block_length; |
| } |
| |
| if ((tls->server && txrx) || (!tls->server && !txrx)) |
| key_offset += enc->key_length; |
| else |
| key_offset += 2 * enc->key_length; |
| } |
| |
| if (tls->negotiated_version <= TLS_V10 && |
| tls->cipher_suite[txrx]->encryption && |
| tls->cipher_suite[txrx]->encryption->cipher_type == |
| TLS_CIPHER_BLOCK) { |
| enc = tls->cipher_suite[txrx]->encryption; |
| |
| /* Server write / client read is 6th in the key block */ |
| if ((tls->server && txrx) || (!tls->server && !txrx)) |
| key_offset += enc->iv_length; |
| |
| l_cipher_set_iv(tls->cipher[txrx], tls->pending.key_block + |
| key_offset, enc->iv_length); |
| |
| /* Wipe out the now unneeded part of the key block */ |
| memset(tls->pending.key_block + key_offset, 0, enc->iv_length); |
| } |
| |
| return true; |
| } |
| |
| static void tls_reset_cipher_spec(struct l_tls *tls, bool txrx) |
| { |
| /* Reset everything to the TLS_NULL_WITH_NULL_NULL state */ |
| |
| tls->pending.cipher_suite = NULL; |
| |
| tls_change_cipher_spec(tls, txrx); |
| } |
| |
| static bool tls_send_rsa_client_key_xchg(struct l_tls *tls); |
| static void tls_handle_rsa_client_key_xchg(struct l_tls *tls, |
| const uint8_t *buf, size_t len); |
| |
| static ssize_t tls_rsa_sign(struct l_tls *tls, uint8_t *out, size_t len, |
| tls_get_hash_t get_hash); |
| static bool tls_rsa_verify(struct l_tls *tls, const uint8_t *in, size_t len, |
| tls_get_hash_t get_hash); |
| |
| static bool tls_rsa_validate_cert_key(struct tls_cert *cert) |
| { |
| return tls_cert_get_pubkey_type(cert) == TLS_CERT_KEY_RSA; |
| } |
| |
| static struct tls_key_exchange_algorithm tls_rsa = { |
| .id = 1, /* RSA_sign */ |
| .certificate_check = true, |
| .validate_cert_key_type = tls_rsa_validate_cert_key, |
| .send_client_key_exchange = tls_send_rsa_client_key_xchg, |
| .handle_client_key_exchange = tls_handle_rsa_client_key_xchg, |
| .sign = tls_rsa_sign, |
| .verify = tls_rsa_verify, |
| }; |
| |
| static struct tls_bulk_encryption_algorithm tls_rc4 = { |
| .cipher_type = TLS_CIPHER_STREAM, |
| .l_id = L_CIPHER_ARC4, |
| .key_length = 16, |
| }, tls_aes128 = { |
| .cipher_type = TLS_CIPHER_BLOCK, |
| .l_id = L_CIPHER_AES_CBC, |
| .key_length = 16, |
| .iv_length = 16, |
| .block_length = 16, |
| }, tls_aes256 = { |
| .cipher_type = TLS_CIPHER_BLOCK, |
| .l_id = L_CIPHER_AES_CBC, |
| .key_length = 32, |
| .iv_length = 16, |
| .block_length = 16, |
| }, tls_3des_ede = { |
| .cipher_type = TLS_CIPHER_BLOCK, |
| .l_id = L_CIPHER_DES3_EDE_CBC, |
| .key_length = 24, |
| .iv_length = 8, |
| .block_length = 8, |
| }; |
| |
| static struct tls_mac_algorithm tls_md5 = { |
| .id = 1, |
| .hmac_type = L_CHECKSUM_MD5, |
| .mac_length = 16, |
| }, tls_sha = { |
| .id = 2, |
| .hmac_type = L_CHECKSUM_SHA1, |
| .mac_length = 20, |
| }, tls_sha256 = { |
| .id = 4, |
| .hmac_type = L_CHECKSUM_SHA256, |
| .mac_length = 32, |
| }; |
| |
| static struct tls_cipher_suite tls_cipher_suite_pref[] = { |
| { |
| .id = { 0x00, 0x35 }, |
| .name = "TLS_RSA_WITH_AES_256_CBC_SHA", |
| .verify_data_length = 12, |
| .encryption = &tls_aes256, |
| .mac = &tls_sha, |
| .key_xchg = &tls_rsa, |
| }, |
| { |
| .id = { 0x00, 0x2f }, |
| .name = "TLS_RSA_WITH_AES_128_CBC_SHA", |
| .verify_data_length = 12, |
| .encryption = &tls_aes128, |
| .mac = &tls_sha, |
| .key_xchg = &tls_rsa, |
| }, |
| { |
| .id = { 0x00, 0x3d }, |
| .name = "TLS_RSA_WITH_AES_256_CBC_SHA256", |
| .verify_data_length = 12, |
| .encryption = &tls_aes256, |
| .mac = &tls_sha256, |
| .key_xchg = &tls_rsa, |
| }, |
| { |
| .id = { 0x00, 0x3c }, |
| .name = "TLS_RSA_WITH_AES_128_CBC_SHA256", |
| .verify_data_length = 12, |
| .encryption = &tls_aes128, |
| .mac = &tls_sha256, |
| .key_xchg = &tls_rsa, |
| }, |
| { |
| .id = { 0x00, 0x0a }, |
| .name = "TLS_RSA_WITH_3DES_EDE_CBC_SHA", |
| .verify_data_length = 12, |
| .encryption = &tls_3des_ede, |
| .mac = &tls_sha, |
| .key_xchg = &tls_rsa, |
| }, |
| { |
| .id = { 0x00, 0x05 }, |
| .name = "TLS_RSA_WITH_RC4_128_SHA", |
| .verify_data_length = 12, |
| .encryption = &tls_rc4, |
| .mac = &tls_sha, |
| .key_xchg = &tls_rsa, |
| }, |
| { |
| .id = { 0x00, 0x04 }, |
| .name = "TLS_RSA_WITH_RC4_128_MD5", |
| .verify_data_length = 12, |
| .encryption = &tls_rc4, |
| .mac = &tls_md5, |
| .key_xchg = &tls_rsa, |
| }, |
| }; |
| |
| static struct tls_cipher_suite *tls_find_cipher_suite(const uint8_t *id) |
| { |
| int i; |
| |
| for (i = 0; i < (int) L_ARRAY_SIZE(tls_cipher_suite_pref); i++) |
| if (tls_cipher_suite_pref[i].id[0] == id[0] && |
| tls_cipher_suite_pref[i].id[1] == id[1]) |
| return &tls_cipher_suite_pref[i]; |
| |
| return NULL; |
| } |
| |
| static struct tls_compression_method tls_compression_pref[] = { |
| /* CompressionMethod.null */ |
| { |
| 0, |
| }, |
| }; |
| |
| static struct tls_compression_method *tls_find_compression_method( |
| const uint8_t id) |
| { |
| int i; |
| |
| for (i = 0; i < (int) L_ARRAY_SIZE(tls_compression_pref); i++) |
| if (tls_compression_pref[i].id == id) |
| return &tls_compression_pref[i]; |
| |
| return NULL; |
| } |
| |
| static const struct tls_hash_algorithm tls_handshake_hash_data[] = { |
| [HANDSHAKE_HASH_SHA256] = { 4, L_CHECKSUM_SHA256, 32 }, |
| [HANDSHAKE_HASH_MD5] = { 1, L_CHECKSUM_MD5, 16 }, |
| [HANDSHAKE_HASH_SHA1] = { 2, L_CHECKSUM_SHA1, 20 }, |
| }; |
| |
| static bool tls_init_handshake_hash(struct l_tls *tls) |
| { |
| enum handshake_hash_type hash; |
| |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) { |
| if (tls->handshake_hash[hash]) |
| goto err; |
| |
| tls->handshake_hash[hash] = l_checksum_new( |
| tls_handshake_hash_data[hash].l_id); |
| |
| if (!tls->handshake_hash[hash]) |
| goto err; |
| } |
| |
| return true; |
| err: |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| tls_drop_handshake_hash(tls, hash); |
| |
| return false; |
| } |
| |
| enum tls_handshake_type { |
| TLS_HELLO_REQUEST = 0, |
| TLS_CLIENT_HELLO = 1, |
| TLS_SERVER_HELLO = 2, |
| TLS_CERTIFICATE = 11, |
| TLS_SERVER_KEY_EXCHANGE = 12, |
| TLS_CERTIFICATE_REQUEST = 13, |
| TLS_SERVER_HELLO_DONE = 14, |
| TLS_CERTIFICATE_VERIFY = 15, |
| TLS_CLIENT_KEY_EXCHANGE = 16, |
| TLS_FINISHED = 20, |
| }; |
| |
| static const uint8_t pkcs1_digest_info_md5_start[] = { |
| 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, |
| 0x02, 0x05, 0x05, 0x00, 0x04, 0x10, |
| }; |
| static const uint8_t pkcs1_digest_info_sha1_start[] = { |
| 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, |
| 0x00, 0x04, 0x14, |
| }; |
| static const uint8_t pkcs1_digest_info_sha256_start[] = { |
| 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, |
| 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, |
| }; |
| static const uint8_t pkcs1_digest_info_sha384_start[] = { |
| 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, |
| 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30, |
| }; |
| static const uint8_t pkcs1_digest_info_sha512_start[] = { |
| 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, |
| 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, |
| }; |
| |
| static void pkcs1_write_digest_info(enum l_checksum_type type, |
| uint8_t *out, size_t *out_len, |
| const uint8_t *hash, size_t hash_len) |
| { |
| switch (type) { |
| case L_CHECKSUM_MD5: |
| memcpy(out, pkcs1_digest_info_md5_start, |
| sizeof(pkcs1_digest_info_md5_start)); |
| *out_len = sizeof(pkcs1_digest_info_md5_start); |
| break; |
| case L_CHECKSUM_SHA1: |
| memcpy(out, pkcs1_digest_info_sha1_start, |
| sizeof(pkcs1_digest_info_sha1_start)); |
| *out_len = sizeof(pkcs1_digest_info_sha1_start); |
| break; |
| case L_CHECKSUM_SHA256: |
| memcpy(out, pkcs1_digest_info_sha256_start, |
| sizeof(pkcs1_digest_info_sha256_start)); |
| *out_len = sizeof(pkcs1_digest_info_sha256_start); |
| break; |
| case L_CHECKSUM_SHA384: |
| memcpy(out, pkcs1_digest_info_sha384_start, |
| sizeof(pkcs1_digest_info_sha384_start)); |
| *out_len = sizeof(pkcs1_digest_info_sha384_start); |
| break; |
| case L_CHECKSUM_SHA512: |
| memcpy(out, pkcs1_digest_info_sha512_start, |
| sizeof(pkcs1_digest_info_sha512_start)); |
| *out_len = sizeof(pkcs1_digest_info_sha512_start); |
| break; |
| default: |
| abort(); |
| } |
| |
| memcpy(out + *out_len, hash, hash_len); |
| *out_len += hash_len; |
| } |
| |
| static void tls_send_alert(struct l_tls *tls, bool fatal, |
| enum l_tls_alert_desc alert_desc) |
| { |
| uint8_t buf[2]; |
| |
| buf[0] = fatal ? 2 : 1; |
| buf[1] = alert_desc; |
| |
| tls_tx_record(tls, TLS_CT_ALERT, buf, 2); |
| } |
| |
| /* |
| * Callers make sure this is about the last function before returning |
| * from the stack frames up to the exported library call so that the |
| * user-supplied disconnected callback here is free to use l_tls_free |
| * for example. |
| */ |
| void tls_disconnect(struct l_tls *tls, enum l_tls_alert_desc desc, |
| enum l_tls_alert_desc local_desc) |
| { |
| tls_send_alert(tls, true, desc); |
| |
| tls_reset_handshake(tls); |
| tls_cleanup_handshake(tls); |
| |
| tls_reset_cipher_spec(tls, 0); |
| tls_reset_cipher_spec(tls, 1); |
| |
| tls->negotiated_version = 0; |
| tls->ready = false; |
| |
| tls->disconnected(local_desc ?: desc, local_desc && !desc, |
| tls->user_data); |
| } |
| |
| #define TLS_HANDSHAKE_HEADER_SIZE 4 |
| |
| static void tls_tx_handshake(struct l_tls *tls, int type, uint8_t *buf, |
| size_t length) |
| { |
| int i; |
| |
| /* Fill in the handshake header */ |
| |
| buf[0] = type; |
| buf[1] = (length - TLS_HANDSHAKE_HEADER_SIZE) >> 16; |
| buf[2] = (length - TLS_HANDSHAKE_HEADER_SIZE) >> 8; |
| buf[3] = (length - TLS_HANDSHAKE_HEADER_SIZE) >> 0; |
| |
| for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) |
| if (tls->handshake_hash[i]) |
| l_checksum_update(tls->handshake_hash[i], buf, length); |
| |
| tls_tx_record(tls, TLS_CT_HANDSHAKE, buf, length); |
| } |
| |
| static void tls_send_client_hello(struct l_tls *tls) |
| { |
| uint8_t buf[128 + L_ARRAY_SIZE(tls_compression_pref) + |
| 2 * L_ARRAY_SIZE(tls_cipher_suite_pref)]; |
| uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; |
| int i; |
| |
| /* Fill in the Client Hello body */ |
| |
| *ptr++ = (uint8_t) (TLS_VERSION >> 8); |
| *ptr++ = (uint8_t) (TLS_VERSION >> 0); |
| |
| tls_write_random(tls->pending.client_random); |
| memcpy(ptr, tls->pending.client_random, 32); |
| ptr += 32; |
| |
| *ptr++ = 0; /* No SessionID */ |
| |
| /* |
| * We can list all supported key exchange mechanisms regardless of the |
| * certificate type we are actually presenting (if any). |
| * |
| * TODO: perhaps scan /proc/crypto for supported ciphers so we don't |
| * include ones that will cause an internal error later in the |
| * handshake. We can add camellia whan this is done. |
| */ |
| l_put_be16(L_ARRAY_SIZE(tls_cipher_suite_pref) * 2, ptr); |
| ptr += 2; |
| |
| for (i = 0; i < (int) L_ARRAY_SIZE(tls_cipher_suite_pref); i++) { |
| *ptr++ = tls_cipher_suite_pref[i].id[0]; |
| *ptr++ = tls_cipher_suite_pref[i].id[1]; |
| } |
| |
| *ptr++ = L_ARRAY_SIZE(tls_compression_pref); |
| |
| for (i = 0; i < (int) L_ARRAY_SIZE(tls_compression_pref); i++) |
| *ptr++ = tls_compression_pref[i].id; |
| |
| tls_tx_handshake(tls, TLS_CLIENT_HELLO, buf, ptr - buf); |
| } |
| |
| static void tls_send_server_hello(struct l_tls *tls) |
| { |
| uint8_t buf[128]; |
| uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; |
| |
| /* Fill in the Server Hello body */ |
| |
| *ptr++ = tls->negotiated_version >> 8; |
| *ptr++ = tls->negotiated_version >> 0; |
| |
| tls_write_random(tls->pending.server_random); |
| memcpy(ptr, tls->pending.server_random, 32); |
| ptr += 32; |
| |
| *ptr++ = 0; /* Sessions are not cached */ |
| |
| *ptr++ = tls->pending.cipher_suite->id[0]; |
| *ptr++ = tls->pending.cipher_suite->id[1]; |
| |
| *ptr++ = tls->pending.compression_method->id; |
| |
| tls_tx_handshake(tls, TLS_SERVER_HELLO, buf, ptr - buf); |
| } |
| |
| static bool tls_send_certificate(struct l_tls *tls) |
| { |
| uint8_t *buf, *ptr; |
| struct tls_cert *cert, *i; |
| size_t total; |
| |
| if (tls->cert_path) |
| cert = tls_cert_load_file(tls->cert_path); |
| else |
| cert = NULL; |
| |
| if (tls->server && !cert) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| TLS_ALERT_BAD_CERT); |
| return false; |
| } |
| |
| if (cert && !tls_cert_find_certchain(cert, tls->ca_cert_path)) { |
| if (tls->server) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| TLS_ALERT_UNKNOWN_CA); |
| |
| return false; |
| } else |
| cert = NULL; |
| } |
| |
| /* TODO: might want check this earlier and exclude the cipher suite */ |
| if (cert && !tls->pending.cipher_suite->key_xchg-> |
| validate_cert_key_type(cert)) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| TLS_ALERT_CERT_UNKNOWN); |
| |
| return false; |
| } |
| |
| /* |
| * TODO: check that the certificate is compatible with hash and |
| * signature algorithms lists supplied to us in the Client Hello |
| * extensions (if we're a server) or in the Certificate Request |
| * (if we act as a 1.2+ client). |
| * |
| * - for the hash and signature_algorithms list, check all |
| * certs in the cert chain. |
| * |
| * - also if !cipher_suite->key_xchg->key_exchange_msg, check that the |
| * end entity certificate's key type matches and is usable with some |
| * hash/signature pair. |
| * |
| * - on client check if any of the supplied DNs (if any) match |
| * anything in our cert chain. |
| */ |
| |
| total = 0; |
| for (i = cert; i; i = i->issuer) |
| total += 3 + i->size; |
| |
| buf = l_malloc(128 + total); |
| ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; |
| |
| /* Fill in the Certificate body */ |
| |
| *ptr++ = total >> 16; |
| *ptr++ = total >> 8; |
| *ptr++ = total >> 0; |
| |
| for (i = cert; i; i = i->issuer) { |
| *ptr++ = i->size >> 16; |
| *ptr++ = i->size >> 8; |
| *ptr++ = i->size >> 0; |
| |
| memcpy(ptr, i->asn1, i->size); |
| ptr += i->size; |
| } |
| |
| tls_tx_handshake(tls, TLS_CERTIFICATE, buf, ptr - buf); |
| |
| l_free(buf); |
| |
| if (cert) |
| tls->cert_sent = true; |
| |
| l_free(cert); |
| |
| return true; |
| } |
| |
| static uint8_t tls_cert_type_pref[] = { |
| 1, /* RSA_sign */ |
| }; |
| |
| struct tls_signature_hash_algorithms { |
| uint8_t hash_id; |
| uint8_t signature_id; |
| }; |
| |
| static struct tls_signature_hash_algorithms tls_signature_hash_pref[] = { |
| { 6, 1 }, /* SHA512 + RSA */ |
| { 5, 1 }, /* SHA384 + RSA */ |
| { 4, 1 }, /* SHA256 + RSA */ |
| { 2, 1 }, /* SHA1 + RSA */ |
| { 1, 1 }, /* MD5 + RSA */ |
| }; |
| |
| static bool tls_send_certificate_request(struct l_tls *tls) |
| { |
| uint8_t *buf, *ptr, *dn_ptr, *signature_hash_ptr; |
| unsigned int i; |
| |
| buf = l_malloc(128 + L_ARRAY_SIZE(tls_cert_type_pref) + |
| 2 * L_ARRAY_SIZE(tls_signature_hash_pref)); |
| ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; |
| |
| /* Fill in the Certificate Request body */ |
| |
| *ptr++ = L_ARRAY_SIZE(tls_cert_type_pref); |
| for (i = 0; i < L_ARRAY_SIZE(tls_cert_type_pref); i++) |
| *ptr++ = tls_cert_type_pref[i]; |
| |
| /* |
| * This only makes sense as a variable-length field, assume there's |
| * a typo in RFC5246 7.4.4 here. |
| * |
| * TODO: we support the full list of hash algorithms when used |
| * in the client certificate chain but we can only verify the |
| * Certificate Verify signature when the hash algorithm matches |
| * one of HANDSHAKE_HASH_*. The values we include here will |
| * affect both of these steps so revisit which set we're passing |
| * here. |
| */ |
| if (tls->negotiated_version >= TLS_V12) { |
| signature_hash_ptr = ptr; |
| ptr += 2; |
| |
| for (i = 0; i < L_ARRAY_SIZE(tls_signature_hash_pref); i++) { |
| *ptr++ = tls_signature_hash_pref[i].hash_id; |
| *ptr++ = tls_signature_hash_pref[i].signature_id; |
| } |
| |
| l_put_be16(ptr - (signature_hash_ptr + 2), signature_hash_ptr); |
| } |
| |
| dn_ptr = ptr; |
| ptr += 2; /* Leave space for sizes */ |
| l_put_be16(0, dn_ptr); /* DistinguishedNames size */ |
| |
| tls_tx_handshake(tls, TLS_CERTIFICATE_REQUEST, buf, ptr - buf); |
| |
| l_free(buf); |
| |
| return true; |
| } |
| |
| static void tls_send_server_hello_done(struct l_tls *tls) |
| { |
| uint8_t buf[32]; |
| |
| /* No body */ |
| |
| tls_tx_handshake(tls, TLS_SERVER_HELLO_DONE, buf, |
| TLS_HANDSHAKE_HEADER_SIZE); |
| } |
| |
| static void tls_generate_master_secret(struct l_tls *tls, |
| const uint8_t *pre_master_secret, |
| int pre_master_secret_len) |
| { |
| uint8_t seed[64]; |
| int key_block_size; |
| |
| memcpy(seed + 0, tls->pending.client_random, 32); |
| memcpy(seed + 32, tls->pending.server_random, 32); |
| |
| tls_prf_get_bytes(tls, L_CHECKSUM_SHA256, 32, |
| pre_master_secret, pre_master_secret_len, |
| "master secret", seed, 64, |
| tls->pending.master_secret, 48); |
| |
| /* Directly generate the key block while we're at it */ |
| key_block_size = 0; |
| |
| if (tls->pending.cipher_suite->encryption) |
| key_block_size += 2 * |
| tls->pending.cipher_suite->encryption->key_length; |
| |
| if (tls->pending.cipher_suite->mac) |
| key_block_size += 2 * |
| tls->pending.cipher_suite->mac->mac_length; |
| |
| if (tls->pending.cipher_suite->encryption && |
| tls->negotiated_version <= TLS_V10 && |
| tls->pending.cipher_suite->encryption->cipher_type == |
| TLS_CIPHER_BLOCK) |
| key_block_size += 2 * |
| tls->pending.cipher_suite->encryption->iv_length; |
| /* Note: 2x fixed_IV_length also needed for AEAD ciphers */ |
| |
| /* Reverse order from the master secret seed */ |
| memcpy(seed + 0, tls->pending.server_random, 32); |
| memcpy(seed + 32, tls->pending.client_random, 32); |
| |
| tls_prf_get_bytes(tls, L_CHECKSUM_SHA256, 32, |
| tls->pending.master_secret, 48, |
| "key expansion", seed, 64, |
| tls->pending.key_block, key_block_size); |
| |
| memset(seed, 0, 64); |
| } |
| |
| static bool tls_send_rsa_client_key_xchg(struct l_tls *tls) |
| { |
| uint8_t buf[1024 + 32]; |
| uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; |
| uint8_t pre_master_secret[48]; |
| ssize_t bytes_encrypted; |
| |
| if (!tls->peer_pubkey) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| |
| return false; |
| } |
| |
| pre_master_secret[0] = (uint8_t) (TLS_VERSION >> 8); |
| pre_master_secret[1] = (uint8_t) (TLS_VERSION >> 0); |
| l_getrandom(pre_master_secret + 2, 46); |
| |
| if (tls->peer_pubkey_size + 32 > (int) sizeof(buf)) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| |
| return false; |
| } |
| |
| l_put_be16(tls->peer_pubkey_size, ptr); |
| bytes_encrypted = l_key_encrypt(tls->peer_pubkey, |
| L_KEY_RSA_PKCS1_V1_5, L_CHECKSUM_NONE, |
| pre_master_secret, ptr + 2, 48, |
| tls->peer_pubkey_size); |
| ptr += tls->peer_pubkey_size + 2; |
| |
| if (bytes_encrypted != (ssize_t) tls->peer_pubkey_size) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| |
| return false; |
| } |
| |
| tls_tx_handshake(tls, TLS_CLIENT_KEY_EXCHANGE, buf, ptr - buf); |
| |
| tls_generate_master_secret(tls, pre_master_secret, 48); |
| memset(pre_master_secret, 0, 48); |
| |
| return true; |
| } |
| |
| static ssize_t tls_rsa_sign(struct l_tls *tls, uint8_t *out, size_t len, |
| tls_get_hash_t get_hash) |
| { |
| ssize_t result; |
| const struct tls_hash_algorithm *hash_type; |
| uint8_t hash[HANDSHAKE_HASH_MAX_SIZE]; |
| uint8_t sign_input[HANDSHAKE_HASH_MAX_SIZE * 2 + 32]; |
| size_t sign_input_len; |
| bool prepend_hash_type = false; |
| size_t expected_bytes; |
| |
| if (!tls->priv_key || !tls->priv_key_size) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| TLS_ALERT_BAD_CERT); |
| |
| return -ENOKEY; |
| } |
| |
| expected_bytes = tls->priv_key_size + 2; |
| |
| if (tls->negotiated_version >= TLS_V12) { |
| hash_type = &tls_handshake_hash_data[tls->signature_hash]; |
| get_hash(tls, hash_type->tls_id, hash, NULL, NULL); |
| |
| pkcs1_write_digest_info(hash_type->l_id, |
| sign_input, &sign_input_len, |
| hash, hash_type->length); |
| |
| prepend_hash_type = true; |
| expected_bytes += 2; |
| } else { |
| get_hash(tls, 1, sign_input + 0, NULL, NULL); /* MD5 */ |
| get_hash(tls, 2, sign_input + 16, NULL, NULL); /* SHA1 */ |
| sign_input_len = 36; |
| } |
| |
| result = -EMSGSIZE; |
| |
| if (len >= expected_bytes) { |
| if (prepend_hash_type) { |
| *out++ = hash_type->tls_id; |
| *out++ = 1; /* RSA_sign */ |
| } |
| |
| l_put_be16(tls->priv_key_size, out); |
| result = l_key_sign(tls->priv_key, L_KEY_RSA_PKCS1_V1_5, |
| L_CHECKSUM_NONE, sign_input, out + 2, |
| sign_input_len, tls->priv_key_size); |
| |
| if (result == (ssize_t) tls->priv_key_size) |
| result = expected_bytes; |
| } |
| |
| if (result < 0) |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| |
| return result; |
| } |
| |
| static bool tls_rsa_verify(struct l_tls *tls, const uint8_t *in, size_t len, |
| tls_get_hash_t get_hash) |
| { |
| uint8_t hash[HANDSHAKE_HASH_MAX_SIZE]; |
| size_t hash_len; |
| enum l_checksum_type hash_type; |
| uint8_t expected[HANDSHAKE_HASH_MAX_SIZE * 2 + 32]; |
| size_t expected_len; |
| unsigned int offset; |
| bool success; |
| |
| /* 2 bytes for SignatureAndHashAlgorithm if version >= 1.2 */ |
| offset = 2; |
| if (tls->negotiated_version < TLS_V12) |
| offset = 0; |
| |
| if (len < offset + 2 || |
| (size_t) l_get_be16(in + offset) + offset + 2 != len) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return false; |
| } |
| |
| /* Only the default hash type supported */ |
| if (len != offset + 2 + tls->peer_pubkey_size) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return false; |
| } |
| |
| if (tls->negotiated_version >= TLS_V12) { |
| /* Only RSA supported */ |
| if (in[1] != 1 /* RSA_sign */) { |
| tls_disconnect(tls, TLS_ALERT_DECRYPT_ERROR, 0); |
| |
| return false; |
| } |
| |
| if (!get_hash(tls, in[0], hash, &hash_len, &hash_type)) { |
| tls_disconnect(tls, TLS_ALERT_DECRYPT_ERROR, 0); |
| |
| return false; |
| } |
| |
| /* |
| * TODO: According to 4.7 we need to support at least two forms |
| * of the signed content in the verification: |
| * - DigestInfo with NULL AlgorithmIdentifier.parameters, |
| * - DigestInfo with empty AlgorithmIdentifier.parameters. |
| * |
| * Additionally PKCS#1 now says BER is used in place of DER for |
| * DigestInfo encoding which adds more ambiguity in the |
| * encoding. |
| */ |
| pkcs1_write_digest_info(hash_type, expected, &expected_len, |
| hash, hash_len); |
| } else { |
| get_hash(tls, 1, expected + 0, NULL, NULL); /* MD5 */ |
| get_hash(tls, 2, expected + 16, NULL, NULL); /* SHA1 */ |
| expected_len = 36; |
| |
| /* |
| * Within the RSA padding for signatures PKCS#1 1.5 allows |
| * the block format to be either 0 or 1, while PKCS#1 2.0 |
| * mandates block type 1 making the signatures unambiguous. |
| * The l_asymmetric_cipher_verify implementation only |
| * accepts block type 1. |
| * TODO: TLS 1.0 doesn't specify that block type must be 1 |
| * like TLS 1.2 does meaning that both PKCS#1 1.5 types are |
| * probably allowed. |
| */ |
| } |
| |
| success = l_key_verify(tls->peer_pubkey, L_KEY_RSA_PKCS1_V1_5, |
| L_CHECKSUM_NONE, expected, in + 4, |
| expected_len, tls->peer_pubkey_size); |
| |
| if (!success) |
| tls_disconnect(tls, TLS_ALERT_DECRYPT_ERROR, 0); |
| |
| return success; |
| } |
| |
| static void tls_get_handshake_hash(struct l_tls *tls, |
| enum handshake_hash_type type, |
| uint8_t *out) |
| { |
| struct l_checksum *hash = l_checksum_clone(tls->handshake_hash[type]); |
| |
| if (!hash) |
| return; |
| |
| l_checksum_get_digest(hash, out, tls_handshake_hash_data[type].length); |
| |
| l_checksum_free(hash); |
| } |
| |
| static bool tls_get_handshake_hash_by_id(struct l_tls *tls, uint8_t hash_id, |
| uint8_t *out, size_t *len, |
| enum l_checksum_type *type) |
| { |
| enum handshake_hash_type hash; |
| |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| if (tls_handshake_hash_data[hash].tls_id == hash_id && |
| tls->handshake_hash[hash]) { |
| tls_get_handshake_hash(tls, hash, out); |
| |
| if (len) |
| *len = tls_handshake_hash_data[hash].length; |
| |
| if (type) |
| *type = tls_handshake_hash_data[hash].l_id; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool tls_send_certificate_verify(struct l_tls *tls) |
| { |
| uint8_t buf[2048]; |
| int i; |
| ssize_t sign_len; |
| |
| /* Fill in the Certificate Verify body */ |
| |
| sign_len = tls->pending.cipher_suite->key_xchg->sign(tls, |
| buf + TLS_HANDSHAKE_HEADER_SIZE, |
| 2048 - TLS_HANDSHAKE_HEADER_SIZE, |
| tls_get_handshake_hash_by_id); |
| |
| if (sign_len < 0) |
| return false; |
| |
| /* Stop maintaining handshake message hashes other than SHA256. */ |
| if (tls->negotiated_version >= TLS_V12) |
| for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) |
| if (i != HANDSHAKE_HASH_SHA256) |
| tls_drop_handshake_hash(tls, i); |
| |
| tls_tx_handshake(tls, TLS_CERTIFICATE_VERIFY, buf, |
| sign_len + TLS_HANDSHAKE_HEADER_SIZE); |
| |
| return true; |
| } |
| |
| static void tls_send_change_cipher_spec(struct l_tls *tls) |
| { |
| uint8_t buf = 1; |
| |
| tls_tx_record(tls, TLS_CT_CHANGE_CIPHER_SPEC, &buf, 1); |
| } |
| |
| static void tls_send_finished(struct l_tls *tls) |
| { |
| uint8_t buf[512]; |
| uint8_t *ptr = buf + TLS_HANDSHAKE_HEADER_SIZE; |
| uint8_t seed[HANDSHAKE_HASH_MAX_SIZE * 2]; |
| size_t seed_len; |
| |
| if (tls->negotiated_version >= TLS_V12) { |
| /* |
| * Same hash type as that used for the PRF, i.e. SHA256 |
| * unless an exotic cipher suite was negotiated that |
| * dictates a different hash for the PRF and for the |
| * Finished hash. We don't support any such ciphers so |
| * it's always SHA256. |
| */ |
| tls_get_handshake_hash(tls, HANDSHAKE_HASH_TLS12, seed); |
| seed_len = tls_handshake_hash_data[HANDSHAKE_HASH_TLS12].length; |
| } else { |
| tls_get_handshake_hash(tls, HANDSHAKE_HASH_MD5, seed + 0); |
| tls_get_handshake_hash(tls, HANDSHAKE_HASH_SHA1, seed + 16); |
| seed_len = 36; |
| } |
| |
| tls_prf_get_bytes(tls, L_CHECKSUM_SHA256, 32, |
| tls->pending.master_secret, 48, |
| tls->server ? "server finished" : |
| "client finished", |
| seed, seed_len, |
| ptr, tls->cipher_suite[1]->verify_data_length); |
| ptr += tls->cipher_suite[1]->verify_data_length; |
| |
| tls_tx_handshake(tls, TLS_FINISHED, buf, ptr - buf); |
| } |
| |
| static bool tls_verify_finished(struct l_tls *tls, const uint8_t *received, |
| size_t len) |
| { |
| uint8_t expected[tls->cipher_suite[0]->verify_data_length]; |
| uint8_t *seed; |
| size_t seed_len; |
| |
| if (len != (size_t) tls->cipher_suite[0]->verify_data_length) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return false; |
| } |
| |
| if (tls->negotiated_version >= TLS_V12) { |
| seed = tls->prev_digest[HANDSHAKE_HASH_TLS12]; |
| seed_len = tls_handshake_hash_data[HANDSHAKE_HASH_TLS12].length; |
| } else { |
| seed = alloca(36); |
| memcpy(seed + 0, tls->prev_digest[HANDSHAKE_HASH_MD5], 16); |
| memcpy(seed + 16, tls->prev_digest[HANDSHAKE_HASH_SHA1], 20); |
| seed_len = 36; |
| } |
| |
| tls_prf_get_bytes(tls, L_CHECKSUM_SHA256, 32, |
| tls->pending.master_secret, 48, |
| tls->server ? "client finished" : |
| "server finished", |
| seed, seed_len, |
| expected, |
| tls->cipher_suite[0]->verify_data_length); |
| |
| if (memcmp(received, expected, len)) { |
| tls_disconnect(tls, TLS_ALERT_DECRYPT_ERROR, 0); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static void tls_handle_client_hello(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| uint16_t cipher_suites_size; |
| uint8_t session_id_size, compression_methods_size; |
| const uint8_t *cipher_suites; |
| const uint8_t *compression_methods; |
| int i; |
| |
| /* Do we have enough for ProtocolVersion + Random + SessionID size? */ |
| if (len < 2 + 32 + 1) |
| goto decode_error; |
| |
| memcpy(tls->pending.client_random, buf + 2, 32); |
| session_id_size = buf[34]; |
| len -= 35; |
| |
| /* |
| * Do we have enough to hold the actual session ID + 2 byte field for |
| * cipher_suite len + minimum of a single cipher suite identifier |
| */ |
| if (len < (size_t) session_id_size + 4) |
| goto decode_error; |
| |
| len -= session_id_size + 2; |
| |
| cipher_suites_size = l_get_be16(buf + 35 + session_id_size); |
| cipher_suites = buf + 37 + session_id_size; |
| |
| /* |
| * Check that size is not odd, more than 0 and we have enough |
| * data in the packet for cipher_suites_size + 2 bytes for |
| * compression_methods_size + a single compression method |
| */ |
| if (len < (size_t) cipher_suites_size + 2 || |
| (cipher_suites_size & 1) || cipher_suites_size == 0) |
| goto decode_error; |
| |
| len -= cipher_suites_size + 1; |
| |
| compression_methods_size = cipher_suites[cipher_suites_size]; |
| compression_methods = cipher_suites + cipher_suites_size + 1; |
| |
| if (len < (size_t) compression_methods_size || |
| compression_methods_size == 0) |
| goto decode_error; |
| |
| len -= compression_methods_size; |
| |
| if (len) { |
| uint16_t extensions_size; |
| |
| if (len < 2 || len > 2 + 65535) |
| goto decode_error; |
| |
| extensions_size = l_get_be16(compression_methods + |
| compression_methods_size); |
| len -= 2; |
| |
| if (len != extensions_size) |
| goto decode_error; |
| |
| /* TODO: validate each extension in the vector, 7.4.1.4 */ |
| /* TODO: check for duplicates? */ |
| } |
| |
| /* |
| * Note: if the client is supplying a SessionID we know it is false |
| * because our server implementation never generates any SessionIDs |
| * yet so either the client is attempting something strange or was |
| * trying to connect somewhere else. We might want to throw an error. |
| */ |
| |
| /* |
| * TODO: Obligatory in 1.2: check for signature_algorithms extension, |
| * store the list of algorithms for later checking in |
| * tls_send_certificate on both server and client sides. If not |
| * present assume only SHA1+RSA (7.4.1.4.1). |
| */ |
| |
| /* Save client_version for Premaster Secret verification */ |
| tls->client_version = l_get_be16(buf); |
| |
| if (tls->client_version < TLS_MIN_VERSION) { |
| tls_disconnect(tls, TLS_ALERT_PROTOCOL_VERSION, 0); |
| return; |
| } |
| |
| tls->negotiated_version = TLS_VERSION < tls->client_version ? |
| TLS_VERSION : tls->client_version; |
| |
| /* Stop maintaining handshake message hashes other than MD1 and SHA. */ |
| if (tls->negotiated_version < TLS_V12) |
| for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) |
| if (i != HANDSHAKE_HASH_SHA1 && i != HANDSHAKE_HASH_MD5) |
| tls_drop_handshake_hash(tls, i); |
| |
| /* Select a cipher suite according to client's preference list */ |
| while (cipher_suites_size) { |
| /* |
| * TODO: filter supported cipher suites by the certificate/key |
| * type that was submitted by tls_set_auth_data() if any. |
| * Perhaps just call cipher_suite->verify_cert_type() on each |
| * cipher suite passing a pre-parsed certificate ASN.1 struct. |
| */ |
| tls->pending.cipher_suite = |
| tls_find_cipher_suite(cipher_suites); |
| |
| if (tls->pending.cipher_suite) |
| break; |
| |
| cipher_suites += 2; |
| cipher_suites_size -= 2; |
| } |
| |
| if (!cipher_suites_size) { |
| tls_disconnect(tls, TLS_ALERT_HANDSHAKE_FAIL, 0); |
| return; |
| } |
| |
| /* Select a compression method */ |
| |
| /* CompressionMethod.null must be present in the vector */ |
| if (!memchr(compression_methods, 0, compression_methods_size)) |
| goto decode_error; |
| |
| while (compression_methods_size) { |
| tls->pending.compression_method = |
| tls_find_compression_method(*compression_methods); |
| |
| if (tls->pending.compression_method) |
| break; |
| |
| compression_methods++; |
| compression_methods_size--; |
| } |
| |
| tls_send_server_hello(tls); |
| |
| if (tls->pending.cipher_suite->key_xchg->certificate_check && |
| tls->cert_path) |
| if (!tls_send_certificate(tls)) |
| return; |
| |
| /* TODO: don't bother if configured to not authenticate client */ |
| if (tls->pending.cipher_suite->key_xchg->certificate_check && |
| tls->ca_cert_path) |
| if (!tls_send_certificate_request(tls)) |
| return; |
| |
| tls_send_server_hello_done(tls); |
| |
| if (tls->pending.cipher_suite->key_xchg->certificate_check && |
| tls->ca_cert_path) |
| tls->state = TLS_HANDSHAKE_WAIT_CERTIFICATE; |
| else |
| tls->state = TLS_HANDSHAKE_WAIT_KEY_EXCHANGE; |
| |
| return; |
| |
| decode_error: |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| } |
| |
| static void tls_handle_server_hello(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| uint8_t session_id_size, cipher_suite_id[2], compression_method_id; |
| int i; |
| |
| /* Do we have enough for ProtocolVersion + Random + SessionID len ? */ |
| if (len < 2 + 32 + 1) |
| goto decode_error; |
| |
| memcpy(tls->pending.server_random, buf + 2, 32); |
| session_id_size = buf[34]; |
| len -= 35; |
| |
| /* Do we have enough for SessionID + CipherSuite ID + Compression ID */ |
| if (len < (size_t) session_id_size + 2 + 1) |
| goto decode_error; |
| |
| cipher_suite_id[0] = buf[35 + session_id_size + 0]; |
| cipher_suite_id[1] = buf[35 + session_id_size + 1]; |
| compression_method_id = buf[35 + session_id_size + 2]; |
| len -= session_id_size + 2 + 1; |
| |
| if (len != 0) { /* We know we haven't solicited any extensions */ |
| tls_disconnect(tls, TLS_ALERT_UNSUPPORTED_EXTENSION, 0); |
| return; |
| } |
| |
| tls->negotiated_version = l_get_be16(buf); |
| |
| if (tls->negotiated_version < TLS_MIN_VERSION || |
| tls->negotiated_version > TLS_VERSION) { |
| tls_disconnect(tls, tls->negotiated_version < TLS_MIN_VERSION ? |
| TLS_ALERT_PROTOCOL_VERSION : |
| TLS_ALERT_ILLEGAL_PARAM, 0); |
| return; |
| } |
| |
| /* Stop maintaining handshake message hashes other than MD1 and SHA. */ |
| if (tls->negotiated_version < TLS_V12) |
| for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) |
| if (i != HANDSHAKE_HASH_SHA1 && i != HANDSHAKE_HASH_MD5) |
| tls_drop_handshake_hash(tls, i); |
| |
| /* Set the new cipher suite and compression method structs */ |
| tls->pending.cipher_suite = tls_find_cipher_suite(cipher_suite_id); |
| if (!tls->pending.cipher_suite) { |
| tls_disconnect(tls, TLS_ALERT_HANDSHAKE_FAIL, 0); |
| return; |
| } |
| |
| tls->pending.compression_method = |
| tls_find_compression_method(compression_method_id); |
| if (!tls->pending.compression_method) { |
| tls_disconnect(tls, TLS_ALERT_HANDSHAKE_FAIL, 0); |
| return; |
| } |
| |
| if (tls->pending.cipher_suite->key_xchg->certificate_check) |
| tls->state = TLS_HANDSHAKE_WAIT_CERTIFICATE; |
| else |
| tls->state = TLS_HANDSHAKE_WAIT_KEY_EXCHANGE; |
| |
| return; |
| |
| decode_error: |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| } |
| |
| static void tls_handle_certificate(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| int total, cert_len; |
| struct tls_cert *certchain = NULL, **tail = &certchain; |
| struct tls_cert *ca_cert = NULL; |
| bool dummy; |
| |
| /* Length checks */ |
| |
| total = *buf++ << 16; |
| total |= *buf++ << 8; |
| total |= *buf++ << 0; |
| if ((size_t) total + 3 != len) |
| goto decode_error; |
| |
| while (total) { |
| cert_len = *buf++ << 16; |
| cert_len |= *buf++ << 8; |
| cert_len |= *buf++ << 0; |
| |
| if (cert_len + 3 > total) |
| goto decode_error; |
| |
| *tail = l_malloc(sizeof(struct tls_cert) + cert_len); |
| (*tail)->size = cert_len; |
| (*tail)->issuer = NULL; |
| memcpy((*tail)->asn1, buf, cert_len); |
| |
| tail = &(*tail)->issuer; |
| |
| buf += cert_len; |
| total -= cert_len + 3; |
| } |
| |
| /* |
| * "Note that a client MAY send no certificates if it does not have any |
| * appropriate certificate to send in response to the server's |
| * authentication request." -- for now we unconditionally accept |
| * an empty certificate chain from the client. Later on we need to |
| * make this configurable, if we don't want to authenticate the |
| * client then also don't bother sending a Certificate Request. |
| */ |
| if (!certchain) { |
| if (!tls->server) { |
| tls_disconnect(tls, TLS_ALERT_HANDSHAKE_FAIL, 0); |
| |
| goto done; |
| } |
| |
| tls->state = TLS_HANDSHAKE_WAIT_KEY_EXCHANGE; |
| |
| goto done; |
| } |
| |
| /* |
| * Validate the certificate chain's consistency and validate it |
| * against our CA if we have any. |
| */ |
| |
| if (tls->ca_cert_path) { |
| ca_cert = tls_cert_load_file(tls->ca_cert_path); |
| if (!ca_cert) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| TLS_ALERT_BAD_CERT); |
| |
| goto done; |
| } |
| } |
| |
| if (!tls_cert_verify_certchain(certchain, ca_cert)) { |
| tls_disconnect(tls, TLS_ALERT_BAD_CERT, 0); |
| |
| goto done; |
| } |
| |
| /* |
| * RFC5246 7.4.2: |
| * "The end entity certificate's public key (and associated |
| * restrictions) MUST be compatible with the selected key exchange |
| * algorithm." |
| */ |
| if (!tls->pending.cipher_suite->key_xchg-> |
| validate_cert_key_type(certchain)) { |
| tls_disconnect(tls, TLS_ALERT_UNSUPPORTED_CERT, 0); |
| |
| goto done; |
| } |
| |
| /* Save the end-entity cert and free the rest of the chain */ |
| tls->peer_cert = certchain; |
| tls_cert_free_certchain(certchain->issuer); |
| certchain->issuer = NULL; |
| certchain = NULL; |
| |
| tls->peer_pubkey = l_key_new(L_KEY_RSA, tls->peer_cert->asn1, |
| tls->peer_cert->size); |
| |
| if (!tls->peer_pubkey) { |
| tls_disconnect(tls, TLS_ALERT_UNSUPPORTED_CERT, 0); |
| |
| goto done; |
| } |
| |
| if (!l_key_get_info(tls->peer_pubkey, L_KEY_RSA_PKCS1_V1_5, |
| L_CHECKSUM_NONE, &tls->peer_pubkey_size, |
| &dummy)) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| |
| goto done; |
| } |
| |
| tls->peer_pubkey_size /= 8; |
| |
| if (tls->server) |
| tls->state = TLS_HANDSHAKE_WAIT_KEY_EXCHANGE; |
| else |
| tls->state = TLS_HANDSHAKE_WAIT_HELLO_DONE; |
| |
| goto done; |
| |
| decode_error: |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| done: |
| if (ca_cert) |
| l_free(ca_cert); |
| |
| tls_cert_free_certchain(certchain); |
| } |
| |
| static void tls_handle_certificate_request(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| int cert_type_len, signature_hash_len, dn_len, i; |
| enum handshake_hash_type first_supported, hash; |
| const uint8_t *signature_hash_data; |
| uint8_t hash_id; |
| |
| tls->cert_requested = 1; |
| |
| cert_type_len = *buf++; |
| if (len < (size_t) 1 + cert_type_len + 2) |
| goto decode_error; |
| |
| /* Skip certificate_types */ |
| buf += cert_type_len; |
| len -= 1 + cert_type_len; |
| |
| /* |
| * TODO: parse and save certificate_types, |
| * supported_signature_algorithms and certificate_authorities |
| * lists for use in tls_send_certificate. |
| */ |
| |
| if (tls->negotiated_version >= TLS_V12) { |
| /* |
| * This only makes sense as a variable-length field, assume |
| * there's a typo in RFC5246 7.4.4 here. |
| */ |
| signature_hash_len = l_get_be16(buf); |
| signature_hash_data = buf + 2; |
| |
| if (len < (size_t) 2 + signature_hash_len + 2 || |
| (signature_hash_len & 1)) |
| goto decode_error; |
| |
| len -= 2 + signature_hash_len; |
| buf += 2 + signature_hash_len; |
| |
| /* |
| * In 1.2 SHA256 is the default because that is most likely |
| * to be supported in all the scenarios and optimal because |
| * SHA256 is required independently for the Finished hash |
| * meaning that we'll just need one hash type instead of |
| * two. If not available fall back to the first common |
| * hash algorithm. |
| */ |
| first_supported = -1; |
| |
| for (i = 0; i < signature_hash_len; i += 2) { |
| hash_id = signature_hash_data[i + 0]; |
| |
| /* Ignore hash types for signatures other than ours */ |
| if (signature_hash_data[i + 1] != tls_rsa.id) |
| continue; |
| |
| if (hash_id == tls_handshake_hash_data[ |
| HANDSHAKE_HASH_SHA256].tls_id) |
| break; |
| |
| if ((int) first_supported != -1) |
| continue; |
| |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| if (hash_id == tls_handshake_hash_data[hash]. |
| tls_id && |
| tls->handshake_hash[hash]) { |
| first_supported = hash; |
| break; |
| } |
| } |
| |
| if (i < signature_hash_len) |
| tls->signature_hash = HANDSHAKE_HASH_SHA256; |
| else if ((int) first_supported != -1) |
| tls->signature_hash = first_supported; |
| else { |
| tls_disconnect(tls, TLS_ALERT_UNSUPPORTED_CERT, 0); |
| |
| return; |
| } |
| |
| /* |
| * We can now safely stop maintaining handshake message |
| * hashes other than SHA256 and the one selected for |
| * signing. |
| */ |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| if (hash != HANDSHAKE_HASH_SHA256 && |
| hash != tls->signature_hash) |
| tls_drop_handshake_hash(tls, hash); |
| } |
| |
| dn_len = l_get_be16(buf); |
| if ((size_t) 2 + dn_len != len) |
| goto decode_error; |
| |
| return; |
| |
| decode_error: |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| } |
| |
| static void tls_handle_server_hello_done(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| if (len) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| return; |
| } |
| |
| if (tls->cert_requested) |
| if (!tls_send_certificate(tls)) |
| return; |
| |
| if (!tls->pending.cipher_suite->key_xchg->send_client_key_exchange(tls)) |
| return; |
| |
| if (tls->cert_sent) |
| if (!tls_send_certificate_verify(tls)) |
| return; |
| |
| tls_send_change_cipher_spec(tls); |
| |
| if (!tls_change_cipher_spec(tls, 1)) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| return; |
| } |
| |
| tls_send_finished(tls); |
| |
| tls->state = TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC; |
| } |
| |
| static void tls_handle_rsa_client_key_xchg(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| uint8_t pre_master_secret[48], random_secret[46]; |
| ssize_t bytes_decrypted; |
| |
| if (!tls->priv_key || !tls->priv_key_size) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| TLS_ALERT_BAD_CERT); |
| |
| return; |
| } |
| |
| if (len != tls->priv_key_size + 2) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return; |
| } |
| |
| len = l_get_be16(buf); |
| |
| if (len != tls->priv_key_size) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return; |
| } |
| |
| bytes_decrypted = l_key_decrypt(tls->priv_key, L_KEY_RSA_PKCS1_V1_5, |
| L_CHECKSUM_NONE, buf + 2, |
| pre_master_secret, tls->priv_key_size, |
| 48); |
| |
| /* |
| * Assume correct premaster secret client version which according |
| * to the TLS1.2 spec is unlikely in client implementations SSLv3 |
| * and prior. Spec suggests either not supporting them or adding |
| * a configurable override for <= SSLv3 clients. For now we have |
| * no need to support them. |
| * |
| * On any decode error randomise the Pre Master Secret as per the |
| * countermeasures in 7.4.7.1 and don't generate any alerts. |
| */ |
| l_getrandom(random_secret, 46); |
| |
| pre_master_secret[0] = tls->client_version >> 8; |
| pre_master_secret[1] = tls->client_version >> 0; |
| |
| if (bytes_decrypted != 48) |
| memcpy(pre_master_secret + 2, random_secret, 46); |
| |
| tls_generate_master_secret(tls, pre_master_secret, 48); |
| memset(pre_master_secret, 0, 48); |
| memset(random_secret, 0, 46); |
| } |
| |
| static bool tls_get_prev_digest_by_id(struct l_tls *tls, uint8_t hash_id, |
| uint8_t *out, size_t *out_len, |
| enum l_checksum_type *type) |
| { |
| enum handshake_hash_type hash; |
| size_t len; |
| |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| if (tls_handshake_hash_data[hash].tls_id == hash_id && |
| tls->handshake_hash[hash]) { |
| len = tls_handshake_hash_data[hash].length; |
| memcpy(out, tls->prev_digest[hash], len); |
| |
| if (out_len) |
| *out_len = len; |
| |
| if (type) |
| *type = tls_handshake_hash_data[hash].l_id; |
| |
| return len; |
| } |
| |
| return 0; |
| } |
| |
| static void tls_handle_certificate_verify(struct l_tls *tls, |
| const uint8_t *buf, size_t len) |
| { |
| int i; |
| |
| if (!tls->pending.cipher_suite->key_xchg->verify(tls, buf, len, |
| tls_get_prev_digest_by_id)) |
| return; |
| |
| /* Stop maintaining handshake message hashes other than SHA256. */ |
| if (tls->negotiated_version >= TLS_V12) |
| for (i = 0; i < __HANDSHAKE_HASH_COUNT; i++) |
| if (i != HANDSHAKE_HASH_SHA256) |
| tls_drop_handshake_hash(tls, i); |
| |
| /* |
| * The client's certificate is now verified based on the following |
| * logic: |
| * - If we received an (expected) Certificate Verify, we must have |
| * sent a Certificate Request. |
| * - If we sent a Certificate Request that's because |
| * tls->ca_cert_path is non-NULL. |
| * - If tls->ca_cert_path is non-NULL then tls_handle_certificate |
| * will have checked the whole certificate chain to be valid and |
| * additionally trusted by our CA if known. |
| * - Additionally cipher_suite->key_xchg->verify has just confirmed |
| * that the peer owns the end-entity certificate because it was |
| * able to sign the contents of the handshake messages and that |
| * signature could be verified with the public key from that |
| * certificate. |
| */ |
| tls->peer_authenticated = true; |
| |
| tls->state = TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC; |
| } |
| |
| static void tls_finished(struct l_tls *tls) |
| { |
| char *peer_identity = NULL; |
| |
| /* Free up the resources used in the handshake */ |
| tls_reset_handshake(tls); |
| |
| tls->state = TLS_HANDSHAKE_DONE; |
| tls->ready = true; |
| |
| tls->ready_handle(peer_identity, tls->user_data); |
| |
| tls_cleanup_handshake(tls); |
| |
| if (peer_identity) |
| l_free(peer_identity); |
| } |
| |
| static void tls_handle_handshake(struct l_tls *tls, int type, |
| const uint8_t *buf, size_t len) |
| { |
| switch (type) { |
| case TLS_HELLO_REQUEST: |
| if (tls->server) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| if (len != 0) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| break; |
| } |
| |
| /* |
| * May be sent by the server at any time but "SHOULD be ignored |
| * by the client if it arrives in the middle of a handshake" |
| * and "MAY be ignored by the client if it does not wish to |
| * renegotiate a session". |
| */ |
| |
| break; |
| |
| case TLS_CLIENT_HELLO: |
| if (!tls->server) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| if (tls->state != TLS_HANDSHAKE_WAIT_HELLO && |
| tls->state != TLS_HANDSHAKE_DONE) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls_handle_client_hello(tls, buf, len); |
| |
| break; |
| |
| case TLS_SERVER_HELLO: |
| if (tls->server) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| if (tls->state != TLS_HANDSHAKE_WAIT_HELLO) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls_handle_server_hello(tls, buf, len); |
| |
| break; |
| |
| case TLS_CERTIFICATE: |
| if (tls->state != TLS_HANDSHAKE_WAIT_CERTIFICATE) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls_handle_certificate(tls, buf, len); |
| |
| break; |
| |
| case TLS_CERTIFICATE_REQUEST: |
| /* |
| * Server sends this optionally so in the WAIT_HELLO_DONE |
| * state we accept either this or a Server Hello Done (below). |
| */ |
| if (tls->state != TLS_HANDSHAKE_WAIT_HELLO_DONE || |
| tls->cert_requested || |
| !tls->pending.cipher_suite->key_xchg-> |
| certificate_check) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls_handle_certificate_request(tls, buf, len); |
| |
| break; |
| |
| case TLS_SERVER_HELLO_DONE: |
| if (tls->state != TLS_HANDSHAKE_WAIT_HELLO_DONE) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls_handle_server_hello_done(tls, buf, len); |
| |
| break; |
| |
| case TLS_CERTIFICATE_VERIFY: |
| if (tls->state != TLS_HANDSHAKE_WAIT_CERTIFICATE_VERIFY) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls_handle_certificate_verify(tls, buf, len); |
| |
| break; |
| |
| case TLS_CLIENT_KEY_EXCHANGE: |
| if (!tls->server) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| if (tls->state != TLS_HANDSHAKE_WAIT_KEY_EXCHANGE) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| tls->pending.cipher_suite->key_xchg->handle_client_key_exchange( |
| tls, buf, len); |
| |
| /* |
| * If we accepted a client Certificate message with a |
| * certificate that has signing capability (TODO: check |
| * usage bitmask), Certiifcate Verify is received next. It |
| * sounds as if this is mandatory for the client although |
| * this isn't 100% clear. |
| */ |
| if (tls->peer_pubkey) |
| tls->state = TLS_HANDSHAKE_WAIT_CERTIFICATE_VERIFY; |
| else |
| tls->state = TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC; |
| |
| break; |
| |
| case TLS_FINISHED: |
| if (tls->state != TLS_HANDSHAKE_WAIT_FINISHED) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| break; |
| } |
| |
| if (!tls_verify_finished(tls, buf, len)) |
| break; |
| |
| if (tls->server) { |
| tls_send_change_cipher_spec(tls); |
| if (!tls_change_cipher_spec(tls, 1)) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, |
| 0); |
| break; |
| } |
| tls_send_finished(tls); |
| } |
| |
| /* |
| * On the client, the server's certificate is only now |
| * verified, based on the following logic: |
| * - tls->ca_cert_path is non-NULL so tls_handle_certificate |
| * (always called on the client) must have veritifed the |
| * server's certificate chain to be valid and additionally |
| * trusted by our CA. |
| * - the correct receival of this Finished message confirms |
| * that the peer owns the end-entity certificate because |
| * it was able to decrypt the master secret which we had |
| * encrypted with the public key from that certificate, and |
| * the posession of the master secret in turn is verified |
| * by both the successful decryption and the MAC of this |
| * message (either should be enough). |
| */ |
| if (!tls->server && tls->cipher_suite[0]->key_xchg-> |
| certificate_check && |
| tls->ca_cert_path) |
| tls->peer_authenticated = true; |
| |
| tls_finished(tls); |
| |
| break; |
| |
| default: |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| } |
| } |
| |
| LIB_EXPORT struct l_tls *l_tls_new(bool server, |
| l_tls_write_cb_t app_data_handler, |
| l_tls_write_cb_t tx_handler, |
| l_tls_ready_cb_t ready_handler, |
| l_tls_disconnect_cb_t disconnect_handler, |
| void *user_data) |
| { |
| struct l_tls *tls; |
| |
| tls = l_new(struct l_tls, 1); |
| tls->server = server; |
| tls->rx = app_data_handler; |
| tls->tx = tx_handler; |
| tls->ready_handle = ready_handler; |
| tls->disconnected = disconnect_handler; |
| tls->user_data = user_data; |
| |
| tls->signature_hash = HANDSHAKE_HASH_SHA256; |
| |
| /* If we're the client, start the handshake right away */ |
| if (!tls->server) { |
| if (!tls_init_handshake_hash(tls)) { |
| l_free(tls); |
| |
| return NULL; |
| } |
| |
| tls_send_client_hello(tls); |
| } |
| |
| tls->state = TLS_HANDSHAKE_WAIT_HELLO; |
| |
| return tls; |
| } |
| |
| LIB_EXPORT void l_tls_free(struct l_tls *tls) |
| { |
| enum handshake_hash_type hash; |
| |
| if (unlikely(!tls)) |
| return; |
| |
| l_tls_set_cacert(tls, NULL); |
| l_tls_set_auth_data(tls, NULL, NULL, NULL); |
| |
| tls_reset_handshake(tls); |
| tls_cleanup_handshake(tls); |
| |
| tls_reset_cipher_spec(tls, 0); |
| tls_reset_cipher_spec(tls, 1); |
| |
| if (tls->record_buf) |
| l_free(tls->record_buf); |
| |
| if (tls->message_buf) |
| l_free(tls->message_buf); |
| |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) |
| tls_drop_handshake_hash(tls, hash); |
| |
| l_free(tls); |
| } |
| |
| LIB_EXPORT void l_tls_write(struct l_tls *tls, const uint8_t *data, size_t len) |
| { |
| if (unlikely(!tls->ready)) { |
| return; |
| } |
| |
| tls_tx_record(tls, TLS_CT_APPLICATION_DATA, data, len); |
| } |
| |
| bool tls_handle_message(struct l_tls *tls, const uint8_t *message, |
| int len, enum tls_content_type type, uint16_t version) |
| { |
| enum handshake_hash_type hash; |
| |
| switch (type) { |
| case TLS_CT_CHANGE_CIPHER_SPEC: |
| if (len != 1 || message[0] != 0x01) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return false; |
| } |
| |
| if (tls->state != TLS_HANDSHAKE_WAIT_CHANGE_CIPHER_SPEC) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| |
| return false; |
| } |
| |
| if (!tls_change_cipher_spec(tls, 0)) { |
| tls_disconnect(tls, TLS_ALERT_INTERNAL_ERROR, 0); |
| |
| return false; |
| } |
| |
| tls->state = TLS_HANDSHAKE_WAIT_FINISHED; |
| |
| return true; |
| |
| case TLS_CT_ALERT: |
| /* Verify AlertLevel */ |
| if (message[0] != 0x01 && message[0] != 0x02) { |
| tls_disconnect(tls, TLS_ALERT_DECODE_ERROR, 0); |
| |
| return false; |
| } |
| |
| /* |
| * On a fatal alert we are obligated to respond with a |
| * fatal alert and disconnect but also not complain if |
| * the connection has been torn down by the peer before |
| * we were able to send our alert. However on a non-fatal |
| * alert (warning) we're also allowed to panic and send |
| * a fatal alert, then disconnect, so we do that |
| * regardless of the alert level. |
| */ |
| tls_disconnect(tls, TLS_ALERT_CLOSE_NOTIFY, message[1]); |
| |
| return false; |
| |
| case TLS_CT_HANDSHAKE: |
| /* Start hashing the handshake contents on first message */ |
| if (tls->server && message[0] == TLS_CLIENT_HELLO && |
| (tls->state == TLS_HANDSHAKE_WAIT_HELLO || |
| tls->state != TLS_HANDSHAKE_DONE)) |
| if (!tls_init_handshake_hash(tls)) |
| return false; |
| |
| /* |
| * Corner case: When handling a Certificate Verify or a |
| * Finished message we need access to the messages hash from |
| * before this message was transmitted on the Tx side so we |
| * can verify it matches the hash the sender included in the |
| * message. We save it here for that purpose. Everywhere |
| * else we need to update the hash before handling the new |
| * message because 1. we may need the new hash to build our |
| * own Certificate Verify or Finished messages, and 2. we |
| * update the message hash with newly transmitted messages |
| * inside tls_tx_handshake which may be called as part of |
| * handling incoming message, and if we didn't call |
| * l_checksum_update before, the calls would end up being |
| * out of order. |
| */ |
| if (message[0] == TLS_CERTIFICATE_VERIFY || |
| message[0] == TLS_FINISHED) |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) { |
| if (!tls->handshake_hash[hash]) |
| continue; |
| |
| tls_get_handshake_hash(tls, hash, |
| tls->prev_digest[hash]); |
| } |
| |
| /* |
| * RFC 5246, Section 7.4.1.1: |
| * This message MUST NOT be included in the message hashes |
| * that are maintained throughout the handshake and used in |
| * the Finished messages and the certificate verify message. |
| */ |
| if (message[0] != TLS_HELLO_REQUEST) |
| for (hash = 0; hash < __HANDSHAKE_HASH_COUNT; hash++) { |
| if (!tls->handshake_hash[hash]) |
| continue; |
| |
| l_checksum_update(tls->handshake_hash[hash], |
| message, len); |
| } |
| |
| tls_handle_handshake(tls, message[0], |
| message + TLS_HANDSHAKE_HEADER_SIZE, |
| len - TLS_HANDSHAKE_HEADER_SIZE); |
| |
| return true; |
| |
| case TLS_CT_APPLICATION_DATA: |
| if (!tls->ready) { |
| tls_disconnect(tls, TLS_ALERT_UNEXPECTED_MESSAGE, 0); |
| |
| return false; |
| } |
| |
| if (!len) |
| return true; |
| |
| tls->rx(message, len, tls->user_data); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| LIB_EXPORT void l_tls_close(struct l_tls *tls) |
| { |
| tls_disconnect(tls, TLS_ALERT_CLOSE_NOTIFY, 0); |
| } |
| |
| LIB_EXPORT void l_tls_set_cacert(struct l_tls *tls, const char *ca_cert_path) |
| { |
| if (tls->ca_cert_path) { |
| l_free(tls->ca_cert_path); |
| tls->ca_cert_path = NULL; |
| } |
| |
| if (ca_cert_path) |
| tls->ca_cert_path = l_strdup(ca_cert_path); |
| } |
| |
| LIB_EXPORT bool l_tls_set_auth_data(struct l_tls *tls, const char *cert_path, |
| const char *priv_key_path, |
| const char *priv_key_passphrase) |
| { |
| if (tls->cert_path) { |
| l_free(tls->cert_path); |
| tls->cert_path = NULL; |
| } |
| |
| if (tls->priv_key) { |
| l_key_free(tls->priv_key); |
| tls->priv_key = NULL; |
| tls->priv_key_size = 0; |
| } |
| |
| if (priv_key_path) { |
| uint8_t *priv_key; |
| bool is_public = true; |
| |
| priv_key = l_pem_load_private_key(priv_key_path, |
| priv_key_passphrase, |
| NULL, |
| &tls->priv_key_size); |
| if (!priv_key) |
| return false; |
| |
| tls->priv_key = l_key_new(L_KEY_RSA, priv_key, |
| tls->priv_key_size); |
| memset(priv_key, 0, tls->priv_key_size); |
| l_free(priv_key); |
| |
| if (!l_key_get_info(tls->priv_key, L_KEY_RSA_PKCS1_V1_5, |
| L_CHECKSUM_NONE, &tls->priv_key_size, |
| &is_public) || is_public) { |
| l_key_free(tls->priv_key); |
| tls->priv_key = NULL; |
| tls->priv_key_size = 0; |
| return false; |
| } |
| |
| tls->priv_key_size /= 8; |
| } |
| |
| if (cert_path) |
| tls->cert_path = l_strdup(cert_path); |
| |
| return true; |
| } |
| |
| LIB_EXPORT const char *l_tls_alert_to_str(enum l_tls_alert_desc desc) |
| { |
| switch (desc) { |
| case TLS_ALERT_CLOSE_NOTIFY: |
| return "close_notify"; |
| case TLS_ALERT_UNEXPECTED_MESSAGE: |
| return "unexpected_message"; |
| case TLS_ALERT_BAD_RECORD_MAC: |
| return "bad_record_mac"; |
| case TLS_ALERT_DECRYPT_FAIL_RESERVED: |
| return "decryption_failure_RESERVED"; |
| case TLS_ALERT_RECORD_OVERFLOW: |
| return "record_overflow"; |
| case TLS_ALERT_DECOMPRESS_FAIL: |
| return "decompression_failure"; |
| case TLS_ALERT_HANDSHAKE_FAIL: |
| return "handshake_failure"; |
| case TLS_ALERT_NO_CERT_RESERVED: |
| return "no_certificate_RESERVED"; |
| case TLS_ALERT_BAD_CERT: |
| return "bad_certificate"; |
| case TLS_ALERT_UNSUPPORTED_CERT: |
| return "unsupported_certificate"; |
| case TLS_ALERT_CERT_REVOKED: |
| return "certificate_revoked"; |
| case TLS_ALERT_CERT_EXPIRED: |
| return "certificate_expired"; |
| case TLS_ALERT_CERT_UNKNOWN: |
| return "certificate_unknown"; |
| case TLS_ALERT_ILLEGAL_PARAM: |
| return "illegal_parameter"; |
| case TLS_ALERT_UNKNOWN_CA: |
| return "unknown_ca"; |
| case TLS_ALERT_ACCESS_DENIED: |
| return "access_denied"; |
| case TLS_ALERT_DECODE_ERROR: |
| return "decode_error"; |
| case TLS_ALERT_DECRYPT_ERROR: |
| return "decrypt_error"; |
| case TLS_ALERT_EXPORT_RES_RESERVED: |
| return "export_restriction_RESERVED"; |
| case TLS_ALERT_PROTOCOL_VERSION: |
| return "protocol_version"; |
| case TLS_ALERT_INSUFFICIENT_SECURITY: |
| return "insufficient_security"; |
| case TLS_ALERT_INTERNAL_ERROR: |
| return "internal_error"; |
| case TLS_ALERT_USER_CANCELED: |
| return "user_canceled"; |
| case TLS_ALERT_NO_RENEGOTIATION: |
| return "no_renegotiation"; |
| case TLS_ALERT_UNSUPPORTED_EXTENSION: |
| return "unsupported_extension"; |
| } |
| |
| return NULL; |
| } |
| |
| /* X509 Certificates and Certificate Chains */ |
| |
| #define X509_CERTIFICATE_POS 0 |
| #define X509_TBSCERTIFICATE_POS 0 |
| #define X509_TBSCERT_VERSION_POS 0 |
| #define X509_TBSCERT_SERIAL_POS 1 |
| #define X509_TBSCERT_SIGNATURE_POS 2 |
| #define X509_ALGORITHM_ID_ALGORITHM_POS 0 |
| #define X509_ALGORITHM_ID_PARAMS_POS 1 |
| #define X509_TBSCERT_ISSUER_DN_POS 3 |
| #define X509_TBSCERT_VALIDITY_POS 4 |
| #define X509_TBSCERT_SUBJECT_DN_POS 5 |
| #define X509_TBSCERT_SUBJECT_KEY_POS 6 |
| #define X509_SUBJECT_KEY_ALGORITHM_POS 0 |
| #define X509_SUBJECT_KEY_VALUE_POS 1 |
| #define X509_TBSCERT_ISSUER_UID_POS 7 |
| #define X509_TBSCERT_SUBJECT_UID_POS 8 |
| #define X509_TBSCERT_EXTENSIONS_POS 9 |
| #define X509_SIGNATURE_ALGORITHM_POS 1 |
| #define X509_SIGNATURE_VALUE_POS 2 |
| |
| struct tls_cert *tls_cert_load_file(const char *filename) |
| { |
| uint8_t *der; |
| size_t len; |
| struct tls_cert *cert; |
| |
| der = l_pem_load_certificate(filename, &len); |
| if (!der) |
| return NULL; |
| |
| if (!len || der[0] != ASN1_ID_SEQUENCE) { |
| l_free(der); |
| return NULL; |
| } |
| |
| cert = l_malloc(sizeof(struct tls_cert) + len); |
| cert->size = len; |
| cert->issuer = NULL; |
| memcpy(cert->asn1, der, len); |
| |
| l_free(der); |
| |
| return cert; |
| } |
| |
| bool tls_cert_find_certchain(struct tls_cert *cert, |
| const char *cacert_filename) |
| { |
| return true; |
| } |
| |
| static const struct pkcs1_encryption_oid { |
| enum tls_cert_key_type key_type; |
| struct asn1_oid oid; |
| } pkcs1_encryption_oids[] = { |
| { /* rsaEncryption */ |
| TLS_CERT_KEY_RSA, |
| { 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 } }, |
| }, |
| }; |
| |
| static void tls_key_cleanup(struct l_key **p) |
| { |
| l_key_free_norevoke(*p); |
| } |
| |
| static bool tls_cert_verify_with_keyring(struct tls_cert *cert, |
| struct l_keyring *ring, |
| struct tls_cert *root, |
| struct l_keyring *trusted) |
| { |
| if (!cert) |
| return true; |
| |
| /* |
| * RFC5246 7.4.2: |
| * "Because certificate validation requires that root keys be |
| * distributed independently, the self-signed certificate that |
| * specifies the root certificate authority MAY be omitted from |
| * the chain, under the assumption that the remote end must |
| * already possess it in order to validate it in any case." |
| */ |
| if (!cert->issuer && root && cert->size == root->size && |
| !memcmp(cert->asn1, root->asn1, root->size)) |
| return true; |
| |
| if (tls_cert_verify_with_keyring(cert->issuer, ring, root, trusted)) { |
| L_AUTO_CLEANUP_VAR(struct l_key *, key, tls_key_cleanup); |
| |
| key = l_key_new(L_KEY_RSA, cert->asn1, cert->size); |
| if (!key) |
| return false; |
| |
| if (!l_keyring_link(ring, key)) |
| return false; |
| |
| if (trusted || cert->issuer) |
| return true; |
| |
| /* |
| * If execution reaches this point, it's known that: |
| * * No trusted root key was supplied, so the chain is only |
| * being checked against its own root |
| * * The keyring 'ring' is not restricted yet |
| * * The chain's root cert was just linked in to the |
| * previously empty keyring 'ring'. |
| * |
| * By restricting 'ring' now, the rest of the certs in |
| * the chain will have their signature validated using 'key' |
| * as the root. |
| */ |
| return l_keyring_restrict(ring, L_KEYRING_RESTRICT_ASYM_CHAIN, |
| trusted); |
| } |
| |
| return false; |
| } |
| |
| static void tls_keyring_cleanup(struct l_keyring **p) |
| { |
| l_keyring_free(*p); |
| } |
| |
| bool tls_cert_verify_certchain(struct tls_cert *certchain, |
| struct tls_cert *ca_cert) |
| { |
| L_AUTO_CLEANUP_VAR(struct l_keyring *, ca_ring, tls_keyring_cleanup); |
| L_AUTO_CLEANUP_VAR(struct l_keyring *, verify_ring, |
| tls_keyring_cleanup); |
| |
| ca_ring = NULL; |
| verify_ring = NULL; |
| |
| if (ca_cert) { |
| L_AUTO_CLEANUP_VAR(struct l_key *, ca_key, tls_key_cleanup); |
| ca_key = NULL; |
| |
| ca_ring = l_keyring_new(); |
| if (!ca_ring) |
| return false; |
| |
| ca_key = l_key_new(L_KEY_RSA, ca_cert->asn1, ca_cert->size); |
| if (!ca_key || !l_keyring_link(ca_ring, ca_key)) |
| return false; |
| } |
| |
| verify_ring = l_keyring_new(); |
| if (!verify_ring) |
| return false; |
| |
| /* |
| * If a CA cert was supplied, restrict verify_ring now so |
| * everything else in certchain is validated against the CA. |
| * Otherwise, verify_ring will be restricted after the root of |
| * certchain is added to verify_ring by |
| * tls_cert_verify_with_keyring(). |
| */ |
| if (ca_ring && !l_keyring_restrict(verify_ring, |
| L_KEYRING_RESTRICT_ASYM_CHAIN, |
| ca_ring)) { |
| return false; |
| } |
| |
| return tls_cert_verify_with_keyring(certchain, verify_ring, ca_cert, |
| ca_ring); |
| } |
| |
| void tls_cert_free_certchain(struct tls_cert *cert) |
| { |
| struct tls_cert *next; |
| |
| while (cert) { |
| next = cert->issuer; |
| l_free(cert); |
| cert = next; |
| } |
| } |
| |
| enum tls_cert_key_type tls_cert_get_pubkey_type(struct tls_cert *cert) |
| { |
| const uint8_t *key_type; |
| size_t key_type_len; |
| int i; |
| |
| key_type = asn1_der_find_elem_by_path(cert->asn1, cert->size, |
| ASN1_ID_OID, &key_type_len, |
| X509_CERTIFICATE_POS, |
| X509_TBSCERTIFICATE_POS, |
| X509_TBSCERT_SUBJECT_KEY_POS, |
| X509_SUBJECT_KEY_ALGORITHM_POS, |
| X509_ALGORITHM_ID_ALGORITHM_POS, |
| -1); |
| if (!key_type) |
| return TLS_CERT_KEY_UNKNOWN; |
| |
| for (i = 0; i < (int) L_ARRAY_SIZE(pkcs1_encryption_oids); i++) |
| if (asn1_oid_eq(&pkcs1_encryption_oids[i].oid, |
| key_type_len, key_type)) |
| break; |
| |
| if (i == L_ARRAY_SIZE(pkcs1_encryption_oids)) |
| return TLS_CERT_KEY_UNKNOWN; |
| |
| return pkcs1_encryption_oids[i].key_type; |
| } |