Created
August 21, 2020 16:39
-
-
Save alfredh/374935864adcb3e27b8cb36dfd4d0d0c to your computer and use it in GitHub Desktop.
TLS stack for libre
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Makefile b/Makefile | |
index 2680b7c..bff75d7 100644 | |
--- a/Makefile | |
+++ b/Makefile | |
@@ -33,6 +33,7 @@ MODULES += aes srtp | |
MODULES += odict | |
MODULES += json | |
MODULES += rtmp | |
+MODULES += cert | |
INSTALL := install | |
ifeq ($(DESTDIR),) | |
diff --git a/include/re.h b/include/re.h | |
index 942083c..28a0196 100644 | |
--- a/include/re.h | |
+++ b/include/re.h | |
@@ -61,6 +61,8 @@ extern "C" { | |
#include "re_udp.h" | |
#include "re_websock.h" | |
+#include "re_cert.h" | |
+ | |
#ifdef __cplusplus | |
} | |
#endif | |
diff --git a/include/re_aes.h b/include/re_aes.h | |
index 4559bad..c7d3cbe 100644 | |
--- a/include/re_aes.h | |
+++ b/include/re_aes.h | |
@@ -13,6 +13,8 @@ | |
enum aes_mode { | |
AES_MODE_CTR, /**< AES Counter mode (CTR) */ | |
AES_MODE_GCM, /**< AES Galois Counter Mode (GCM) */ | |
+ AES_MODE_CBC_ENCRYPT, /**< AES Cipher Block Chaining (CBC) (encrypt) */ | |
+ AES_MODE_CBC_DECRYPT, /**< AES Cipher Block Chaining (CBC) (decrypt) */ | |
}; | |
struct aes; | |
diff --git a/include/re_cert.h b/include/re_cert.h | |
new file mode 100644 | |
index 0000000..c3225b4 | |
--- /dev/null | |
+++ b/include/re_cert.h | |
@@ -0,0 +1,31 @@ | |
+/** | |
+ * @file re_cert.h Interface to Certificate handling | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+ | |
+struct cert; | |
+ | |
+int cert_decode(struct cert **certp, const uint8_t *p, size_t len); | |
+int cert_decode_pem(struct cert **certp, const char *pem, size_t len); | |
+int cert_load_file(struct cert **certp, const char *filename); | |
+int cert_encode_der(const struct cert *cert, uint8_t **derp, size_t *lenp); | |
+int cert_generate_rsa(struct cert **certp, const char *cn, unsigned bits); | |
+ | |
+int cert_public_encrypt(struct cert *cert, | |
+ uint8_t *out, size_t *out_len, | |
+ const uint8_t *in, size_t in_len); | |
+int cert_private_decrypt(struct cert *cert, | |
+ uint8_t *out, size_t *out_len, | |
+ const uint8_t *in, size_t in_len); | |
+ | |
+int cert_version(const struct cert *cert); | |
+long cert_serial(const struct cert *cert); | |
+int cert_get_issuer(const struct cert *cert, char *buf, size_t size); | |
+int cert_get_subject(const struct cert *cert, char *buf, size_t size); | |
+int cert_get_fingerprint(const struct cert *cert, int type, | |
+ uint8_t *md, size_t size); | |
+ | |
+ | |
+void cert_dump(const struct cert *cert); | |
diff --git a/include/re_mem.h b/include/re_mem.h | |
index d09d5e2..27bc571 100644 | |
--- a/include/re_mem.h | |
+++ b/include/re_mem.h | |
@@ -39,6 +39,10 @@ int mem_status(struct re_printf *pf, void *unused); | |
int mem_get_stat(struct memstat *mstat); | |
+bool mem_is_valid(const void *data); | |
+void mem_print(const void *data); | |
+ | |
+ | |
/* Secure memory functions */ | |
int mem_seccmp(const volatile uint8_t *volatile s1, | |
const volatile uint8_t *volatile s2, | |
diff --git a/include/re_tls.h b/include/re_tls.h | |
index 319d601..d765733 100644 | |
--- a/include/re_tls.h | |
+++ b/include/re_tls.h | |
@@ -5,6 +5,16 @@ | |
*/ | |
+// TODO: temp to test nameclash | |
+#if 1 | |
+#include <openssl/ssl.h> | |
+#include <openssl/err.h> | |
+#include <openssl/rsa.h> | |
+#include <openssl/bn.h> | |
+#endif | |
+ | |
+ | |
+struct sa; | |
struct tls; | |
struct tls_conn; | |
struct tcp_conn; | |
@@ -56,6 +66,7 @@ const char *tls_cipher_name(const struct tls_conn *tc); | |
int tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count); | |
int tls_set_servername(struct tls_conn *tc, const char *servername); | |
int tls_set_verify_server(struct tls_conn *tc, const char *host); | |
+int tls_get_servername(struct tls_conn *tc, char *servername, size_t sz); | |
/* TCP */ | |
@@ -100,3 +111,458 @@ struct ssl_ctx_st; | |
struct ssl_ctx_st *tls_openssl_context(const struct tls *tls); | |
#endif | |
+ | |
+ | |
+#ifndef USE_OPENSSL_TLS | |
+ | |
+/* | |
+ * Low-level API for native TLS-stack | |
+ */ | |
+ | |
+ | |
+/* forward declarations */ | |
+ | |
+struct mbuf; | |
+struct cert; | |
+ | |
+ | |
+/* TLS constants */ | |
+enum { | |
+ TLS_MASTER_SECRET_LEN = 48, | |
+ TLS_CLIENT_RANDOM_LEN = 32, | |
+ TLS_SERVER_RANDOM_LEN = 32, | |
+ | |
+ TLS_RECORD_FRAGMENT_SIZE = 16384, /* 2^14 */ | |
+ | |
+ TLS_VERIFY_DATA_SIZE = 12, | |
+}; | |
+ | |
+ | |
+/* Basic types */ | |
+ | |
+typedef uint32_t uint24_t; | |
+typedef uint64_t uint48_t; | |
+ | |
+struct tls_vector { | |
+ size_t bytes; | |
+ void *data; /* note: network byte order */ | |
+}; | |
+ | |
+ | |
+/* Security Parameters */ | |
+ | |
+enum tls_connection_end { | |
+ TLS_CLIENT = 0, | |
+ TLS_SERVER = 1 | |
+}; | |
+ | |
+enum tls_bulkcipher_algorithm { | |
+ TLS_BULKCIPHER_NULL = 0, | |
+ TLS_BULKCIPHER_AES | |
+}; | |
+ | |
+enum tls_ciphertype { | |
+ TLS_CIPHERTYPE_STREAM = 0, | |
+ TLS_CIPHERTYPE_BLOCK, | |
+ /*TLS_CIPHERTYPE_AEAD*/ | |
+}; | |
+ | |
+enum tls_cipher { | |
+ TLS_C_NULL = 0, | |
+ TLS_AES_128_CBC, | |
+ TLS_AES_256_CBC, | |
+}; | |
+ | |
+enum tls_mac_algorithm { | |
+ TLS_MAC_NULL = 0, | |
+ TLS_MAC_HMAC_SHA1, | |
+ TLS_MAC_HMAC_SHA256, | |
+}; | |
+ | |
+enum tls_compression_method { | |
+ TLS_COMPRESSION_NULL = 0, | |
+}; | |
+ | |
+ | |
+/* Basic TLS Protocol types */ | |
+ | |
+enum tls_content_type { | |
+ TLS_CHANGE_CIPHER_SPEC = 20, | |
+ TLS_ALERT = 21, | |
+ TLS_HANDSHAKE = 22, | |
+ TLS_APPLICATION_DATA = 23, | |
+}; | |
+ | |
+/** DTLS versions (host endianness) */ | |
+enum tls_version { | |
+#undef TLS1_2_VERSION | |
+ TLS1_2_VERSION = 0x0303, | |
+#undef DTLS1_2_VERSION | |
+ DTLS1_2_VERSION = 0xfefd, | |
+}; | |
+ | |
+ | |
+/* Handshake types */ | |
+ | |
+enum tls_handshake_type { | |
+ TLS_HELLO_REQUEST = 0, | |
+ TLS_CLIENT_HELLO = 1, | |
+ TLS_SERVER_HELLO = 2, | |
+ TLS_HELLO_VERIFY_REQUEST = 3, /* DTLS only */ | |
+ 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, | |
+}; | |
+ | |
+enum tls_alertlevel { | |
+ TLS_LEVEL_WARNING = 1, | |
+ TLS_LEVEL_FATAL = 2 | |
+}; | |
+ | |
+enum tls_alertdescr { | |
+ TLS_ALERT_CLOSE_NOTIFY = 0, | |
+ TLS_ALERT_UNEXPECTED_MESSAGE = 10, | |
+ TLS_ALERT_BAD_RECORD_MAC = 20, | |
+ TLS_ALERT_RECORD_OVERFLOW = 22, | |
+ TLS_ALERT_DECOMPRESSION_FAILURE = 30, | |
+ TLS_ALERT_HANDSHAKE_FAILURE = 40, | |
+ TLS_ALERT_BAD_CERTIFICATE = 42, | |
+ TLS_ALERT_UNSUPPORTED_CERTIFICATE = 43, | |
+ TLS_ALERT_CERTIFICATE_REVOKED = 44, | |
+ TLS_ALERT_CERTIFICATE_EXPIRED = 45, | |
+ TLS_ALERT_CERTIFICATE_UNKNOWN = 46, | |
+ TLS_ALERT_ILLEGAL_PARAMETER = 47, | |
+ TLS_ALERT_UNKNOWN_CA = 48, | |
+ TLS_ALERT_ACCESS_DENIED = 49, | |
+ TLS_ALERT_DECODE_ERROR = 50, | |
+ TLS_ALERT_DECRYPT_ERROR = 51, | |
+ TLS_ALERT_PROTOCOL_VERSION = 70, | |
+ TLS_ALERT_INSUFFICIENT_SECURITY = 71, | |
+ TLS_ALERT_INTERNAL_ERROR = 80, | |
+ TLS_ALERT_USER_CANCELED = 90, | |
+ TLS_ALERT_NO_RENEGOTIATION = 100, | |
+ TLS_ALERT_UNSUPPORTED_EXTENSION = 110, | |
+}; | |
+ | |
+#define CIPHER(a,b) (a)<<8 | (b) | |
+enum tls_cipher_suite { | |
+ | |
+ TLS_CIPHER_NULL_WITH_NULL_NULL = CIPHER(0x00,0x00), | |
+ | |
+ /* RSA cipher-suites */ | |
+ TLS_CIPHER_RSA_WITH_NULL_SHA = CIPHER(0x00,0x02), | |
+ TLS_CIPHER_RSA_WITH_NULL_SHA256 = CIPHER(0x00,0x3B), | |
+ TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA = CIPHER(0x00,0x2F), /*mand*/ | |
+ TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA = CIPHER(0x00,0x35), | |
+ TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA256 = CIPHER(0x00,0x3C), | |
+ TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA256 = CIPHER(0x00,0x3D), | |
+}; | |
+ | |
+struct tls_change_cipher_spec { | |
+ uint8_t byte; | |
+}; | |
+ | |
+struct tls_alert { | |
+ enum tls_alertlevel level; | |
+ enum tls_alertdescr descr; | |
+}; | |
+ | |
+struct tls_handshake { | |
+ enum tls_handshake_type msg_type; | |
+ uint24_t length; | |
+ uint16_t message_seq; /* DTLS only */ | |
+ uint24_t fragment_offset; /* DTLS only */ | |
+ uint24_t fragment_length; /* DTLS only */ | |
+ | |
+ union handshake { | |
+ struct clienthello { | |
+ enum tls_version client_version; | |
+ uint8_t random[TLS_CLIENT_RANDOM_LEN]; | |
+ struct tls_vector session_id; | |
+ struct tls_vector cookie; /* DTLS only */ | |
+ struct tls_vector cipher_suites; | |
+ struct tls_vector compression_methods; | |
+ struct tls_vector extensions; | |
+ } clienthello; | |
+ | |
+ struct serverhello { | |
+ enum tls_version server_version; | |
+ uint8_t random[TLS_SERVER_RANDOM_LEN]; | |
+ struct tls_vector session_id; | |
+ enum tls_cipher_suite cipher_suite; | |
+ enum tls_compression_method compression_method; | |
+ struct tls_vector extensions; | |
+ } serverhello; | |
+ | |
+ struct hello_verify_req { | |
+ enum tls_version server_version; | |
+ struct tls_vector cookie; | |
+ } hello_verify_req; | |
+ | |
+ struct certificate { | |
+ struct tls_vector certlist[4]; | |
+ size_t count; | |
+ } certificate; | |
+ | |
+ struct client_key_exchange { | |
+ struct tls_vector encr_pms; | |
+ } client_key_exchange; | |
+ | |
+ struct finished { | |
+ uint8_t verify_data[TLS_VERIFY_DATA_SIZE]; | |
+ } finished; | |
+ } u; | |
+}; | |
+ | |
+ | |
+/** | |
+ * Defines a TLS Record. | |
+ * | |
+ * 1 record can contain multiple handshake messages, | |
+ * or N records can contain 1 handshake message | |
+ */ | |
+struct tls_record { | |
+ enum tls_content_type content_type; | |
+ enum tls_version proto_ver; | |
+ uint16_t epoch; /* DTLS only */ | |
+ uint48_t seq; /* DTLS only */ | |
+ uint16_t length; /* fragment length */ | |
+ uint8_t *fragment; | |
+}; | |
+ | |
+ | |
+enum tls_key_exchange { | |
+ TLS_KE_K_NULL = 0, | |
+ TLS_KE_RSA, /* RSA public key algorithm */ | |
+}; | |
+ | |
+struct tls_suite { | |
+ enum tls_cipher_suite suite; | |
+ enum tls_key_exchange key_exchange; | |
+ enum tls_cipher cipher; | |
+ enum tls_mac_algorithm mac; | |
+}; | |
+ | |
+ | |
+/* vector */ | |
+ | |
+int tls_vector_init(struct tls_vector *vect, | |
+ const uint8_t *data, size_t len); | |
+int tls_vector_encode(struct mbuf *mb, const struct tls_vector *vect, | |
+ unsigned hdr_bytes); | |
+int tls_vector_decode(struct tls_vector *vect, unsigned hdr_bytes, | |
+ struct mbuf *mb); | |
+int tls_vector_decode_hdr(struct tls_vector *vect, unsigned hdr_bytes, | |
+ struct mbuf *mb); | |
+void tls_vector_reset(struct tls_vector *vect); | |
+ | |
+ | |
+/* alert */ | |
+ | |
+int tls_alert_encode(struct mbuf *mb, const struct tls_alert *alert); | |
+int tls_alert_decode(struct tls_alert *alert, struct mbuf *mb); | |
+const char *tls_alert_name(enum tls_alertdescr descr); | |
+ | |
+ | |
+/* handshake */ | |
+ | |
+int tls_handshake_encode(struct mbuf *mb, enum tls_version ver, | |
+ enum tls_handshake_type msg_type, | |
+ uint16_t message_seq, | |
+ uint24_t fragment_offset, | |
+ const union handshake *hand); | |
+int tls_handshake_decode(struct tls_handshake **handp, | |
+ enum tls_version ver, struct mbuf *mb); | |
+const char *tls_handshake_name(enum tls_handshake_type typ); | |
+void tls_handshake_dump(const struct tls_handshake *hand, | |
+ enum tls_version ver); | |
+ | |
+ | |
+/* record */ | |
+ | |
+int tls_record_encode(struct mbuf *mb, enum tls_version ver, | |
+ enum tls_content_type type, | |
+ uint16_t epoch, uint64_t seq, | |
+ const uint8_t *frag, size_t fraglen); | |
+int tls_record_decode(struct tls_record **recp, struct mbuf *mb); | |
+size_t tls_record_hdrsize(enum tls_version ver); | |
+const char *tls_content_type_name(enum tls_content_type typ); | |
+void tls_record_dump(const struct tls_record *rec); | |
+ | |
+ | |
+/* cipher */ | |
+ | |
+enum tls_ciphertype tls_cipher_type(enum tls_cipher cipher); | |
+unsigned tls_cipher_keymaterial(enum tls_cipher cipher); | |
+unsigned tls_cipher_ivsize(enum tls_cipher cipher); | |
+unsigned tls_cipher_blocksize(enum tls_cipher cipher); | |
+enum tls_bulkcipher_algorithm tls_cipher_algorithm(enum tls_cipher cipher); | |
+ | |
+ | |
+unsigned tls_cipher_suite_count(void); | |
+const struct tls_suite *tls_suite_lookup(enum tls_cipher_suite cipher_suite); | |
+unsigned tls_mac_length(enum tls_mac_algorithm mac); | |
+const char *tls_cipher_suite_name(enum tls_cipher_suite cs); | |
+enum tls_cipher_suite tls_cipher_suite_resolve(const char *name); | |
+ | |
+ | |
+/* PRF (Pseudorandom Function) */ | |
+ | |
+int tls_prf_sha256(uint8_t *output, size_t output_len, | |
+ const uint8_t *secret, size_t secret_len, | |
+ const uint8_t *label, size_t label_len, | |
+ const uint8_t *seed, size_t seed_len); | |
+ | |
+ | |
+/* secparam (Security Parameters) */ | |
+ | |
+struct tls_secparam { | |
+ enum tls_connection_end entity; | |
+ enum tls_bulkcipher_algorithm bulk_cipher_algorithm; | |
+ enum tls_ciphertype cipher_type; | |
+ unsigned enc_key_length; /* bytes */ | |
+ unsigned block_length; /* bytes */ | |
+ unsigned fixed_iv_length; /* bytes */ | |
+ unsigned record_iv_length; /* bytes */ | |
+ enum tls_mac_algorithm mac_algorithm; | |
+ unsigned mac_length; | |
+ unsigned mac_key_length; | |
+ uint8_t master_secret[TLS_MASTER_SECRET_LEN]; | |
+ uint8_t client_random[TLS_CLIENT_RANDOM_LEN]; | |
+ uint8_t server_random[TLS_SERVER_RANDOM_LEN]; | |
+ | |
+ bool is_write; | |
+}; | |
+ | |
+int tls_secparam_init(struct tls_secparam *sp, | |
+ const uint8_t random[32], | |
+ bool is_write, bool client); | |
+int tls_secparam_set(struct tls_secparam *sp, | |
+ const struct tls_suite *suite); | |
+void tls_secparam_dump(const struct tls_secparam *sp); | |
+ | |
+ | |
+/* key generation */ | |
+ | |
+#define TLS_MAX_KEY_SIZE 32 | |
+struct key { | |
+ uint8_t k[TLS_MAX_KEY_SIZE]; | |
+ size_t len; | |
+}; | |
+ | |
+struct tls_key_block { | |
+ struct key client_write_MAC_key; | |
+ struct key server_write_MAC_key; | |
+ struct key client_write_key; | |
+ struct key server_write_key; | |
+ /*client_write_IV[SecurityParameters.fixed_iv_length] for AEAD */ | |
+ /*server_write_IV[SecurityParameters.fixed_iv_length] for AEAD */ | |
+}; | |
+ | |
+int tls_keys_generate(struct tls_key_block *keys, | |
+ const struct tls_secparam *sp); | |
+ | |
+ | |
+/* session */ | |
+ | |
+typedef int (tls_sess_send_h)(struct mbuf *mb, void *arg); | |
+typedef void (tls_sess_estab_h)(void *arg); | |
+typedef void (tls_data_recv_h)(uint8_t *data, size_t datalen, void *arg); | |
+typedef void (tls_sess_close_h)(int err, void *arg); | |
+ | |
+struct tls_session; | |
+ | |
+int tls_session_alloc(struct tls_session **sessp, struct tls *tls, | |
+ enum tls_connection_end conn_end, | |
+ enum tls_version ver, | |
+ const enum tls_cipher_suite *cipherv, size_t cipherc, | |
+ tls_sess_send_h *sendh, | |
+ tls_sess_estab_h *estabh, | |
+ tls_data_recv_h *datarecvh, | |
+ tls_sess_close_h *closeh, void *arg); | |
+int tls_session_start(struct tls_session *sess); | |
+struct tls_secparam *tls_session_secparam(struct tls_session *sess, | |
+ bool write); | |
+int tls_session_send_data(struct tls_session *sess, | |
+ const uint8_t *data, size_t data_len); | |
+void tls_session_recvtcp(struct tls_session *sess, struct mbuf *mb); | |
+void tls_session_recvudp(struct tls_session *sess, struct mbuf *mb); | |
+void tls_session_dump(const struct tls_session *sess); | |
+void tls_session_summary(const struct tls_session *sess); | |
+int tls_session_set_certificate(struct tls_session *sess, | |
+ const char *pem, size_t len); | |
+void tls_session_set_certificate2(struct tls_session *sess, | |
+ struct cert *cert); | |
+enum tls_cipher_suite tls_session_cipher(struct tls_session *sess); | |
+int tls_session_set_fragment_size(struct tls_session *sess, size_t size); | |
+struct cert *tls_session_peer_certificate(const struct tls_session *sess); | |
+bool tls_session_is_estab(const struct tls_session *sess); | |
+void tls_session_shutdown(struct tls_session *sess); | |
+int tls_session_get_servername(struct tls_session *sess, | |
+ char *servername, size_t sz); | |
+ | |
+ | |
+/* master secret */ | |
+ | |
+int tls_master_secret_compute(uint8_t master_secret[48], | |
+ const uint8_t *pre_master_secret, | |
+ size_t pre_master_secret_len, | |
+ const uint8_t client_random[32], | |
+ const uint8_t server_random[32]); | |
+ | |
+ | |
+/* MAC */ | |
+ | |
+int tls_mac_generate(uint8_t *mac, size_t mac_sz, | |
+ const struct key *mac_write_key, | |
+ uint64_t seq_num, | |
+ enum tls_content_type content_type, | |
+ enum tls_version proto_ver, | |
+ uint16_t fragment_length, | |
+ const uint8_t *fragment); | |
+ | |
+/* | |
+ * TLS Version | |
+ */ | |
+ | |
+bool tls_version_is_dtls(enum tls_version ver); | |
+const char *tls_version_name(enum tls_version ver); | |
+ | |
+ | |
+/* finish */ | |
+ | |
+int tls_finish_calc(uint8_t verify_data[TLS_VERIFY_DATA_SIZE], | |
+ const uint8_t master_secret[TLS_MASTER_SECRET_LEN], | |
+ const uint8_t seed[32], | |
+ enum tls_connection_end sender); | |
+ | |
+ | |
+/* trace */ | |
+ | |
+enum tls_trace_flags { | |
+ | |
+ TLS_TRACE_RECORD = 1<<0, | |
+ | |
+ TLS_TRACE_CHANGE_CIPHER_SPEC = 1<<1, | |
+ TLS_TRACE_ALERT = 1<<2, | |
+ TLS_TRACE_HANDSHAKE = 1<<3, | |
+ TLS_TRACE_APPLICATION_DATA = 1<<4, | |
+ | |
+ TLS_TRACE_ALL = ~0, | |
+}; | |
+ | |
+typedef void (tls_trace_h)(enum tls_trace_flags flag, const char *msg, | |
+ void *arg); | |
+ | |
+void tls_trace(struct tls_session *sess, enum tls_trace_flags flags, | |
+ const char *fmt, ...); | |
+void tls_set_trace(struct tls_session *sess, enum tls_trace_flags flags, | |
+ tls_trace_h *traceh); | |
+const char *tls_trace_name(enum tls_trace_flags flag); | |
+ | |
+ | |
+#endif /* USE_OPENSSL_TLS */ | |
diff --git a/mk/re.mk b/mk/re.mk | |
index 5f30810..0620de7 100644 | |
--- a/mk/re.mk | |
+++ b/mk/re.mk | |
@@ -454,9 +454,21 @@ USE_OPENSSL := $(shell [ -f $(SYSROOT)/include/openssl/ssl.h ] || \ | |
[ -f $(SYSROOT)/local/include/openssl/ssl.h ] || \ | |
[ -f $(SYSROOT_ALT)/include/openssl/ssl.h ] && echo "yes") | |
+# todo: configurable | |
+USE_OPENSSL_TLS := | |
+USE_OPENSSL_DTLS := | |
+USE_OPENSSL_SRTP := | |
+ | |
ifneq ($(USE_OPENSSL),) | |
+ | |
+ifneq ($(USE_OPENSSL_TLS),) | |
+CFLAGS += -DUSE_OPENSSL_TLS | |
+#LIBS += -lssl | |
+endif | |
+ | |
+# TODO: do we need to link to -lssl ? | |
CFLAGS += -DUSE_OPENSSL -DUSE_TLS | |
-LIBS += -lssl -lcrypto | |
+LIBS += -lcrypto | |
USE_TLS := yes | |
USE_OPENSSL_DTLS := $(shell [ -f $(SYSROOT)/include/openssl/dtls1.h ] || \ | |
@@ -681,6 +693,8 @@ info: | |
@echo " LIBRE_INC: $(LIBRE_INC)" | |
@echo " LIBRE_SO: $(LIBRE_SO)" | |
@echo " USE_OPENSSL: $(USE_OPENSSL)" | |
+ @echo " USE_OPENSSL_TLS: $(USE_OPENSSL_TLS)" | |
+ @echo " USE_OPENSSL_DTLS: $(USE_OPENSSL_DTLS)" | |
@echo " USE_OPENSSL_AES: $(USE_OPENSSL_AES)" | |
@echo " USE_OPENSSL_HMAC: $(USE_OPENSSL_HMAC)" | |
@echo " USE_TLS: $(USE_TLS)" | |
diff --git a/src/aes/openssl/aes.c b/src/aes/openssl/aes.c | |
index 4d710f2..d73fb97 100644 | |
--- a/src/aes/openssl/aes.c | |
+++ b/src/aes/openssl/aes.c | |
@@ -43,6 +43,18 @@ static const EVP_CIPHER *aes_cipher(enum aes_mode mode, size_t key_bits) | |
return NULL; | |
} | |
} | |
+ else if (mode == AES_MODE_CBC_ENCRYPT || | |
+ mode == AES_MODE_CBC_DECRYPT) { | |
+ | |
+ switch (key_bits) { | |
+ | |
+ case 128: return EVP_aes_128_cbc(); | |
+ case 192: return EVP_aes_192_cbc(); | |
+ case 256: return EVP_aes_256_cbc(); | |
+ default: | |
+ return NULL; | |
+ } | |
+ } | |
else { | |
return NULL; | |
} | |
@@ -89,6 +101,7 @@ int aes_alloc(struct aes **aesp, enum aes_mode mode, | |
const EVP_CIPHER *cipher; | |
struct aes *st; | |
int err = 0, r; | |
+ bool decrypt = mode == AES_MODE_CBC_DECRYPT; | |
if (!aesp || !key) | |
return EINVAL; | |
@@ -122,12 +135,25 @@ int aes_alloc(struct aes **aesp, enum aes_mode mode, | |
EVP_CIPHER_CTX_init(st->ctx); | |
#endif | |
- r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv); | |
- if (!r) { | |
- ERR_clear_error(); | |
- err = EPROTO; | |
+ /* NOTE: for CBC-mode, Encrypt and Decrypt is different */ | |
+ if (decrypt) { | |
+ r = EVP_DecryptInit_ex(st->ctx, cipher, NULL, key, iv); | |
+ if (!r) { | |
+ ERR_clear_error(); | |
+ err = EPROTO; | |
+ } | |
+ } | |
+ else { | |
+ r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv); | |
+ if (!r) { | |
+ ERR_clear_error(); | |
+ err = EPROTO; | |
+ } | |
} | |
+ /* disable padding, this is needed for CBC-mode */ | |
+ EVP_CIPHER_CTX_set_padding(st->ctx, 0); | |
+ | |
out: | |
if (err) | |
mem_deref(st); | |
@@ -153,7 +179,7 @@ void aes_set_iv(struct aes *aes, const uint8_t *iv) | |
int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len) | |
{ | |
- int c_len = (int)len; | |
+ int c_len; | |
if (!aes || !in) | |
return EINVAL; | |
diff --git a/src/cert/cert.c b/src/cert/cert.c | |
new file mode 100644 | |
index 0000000..a5469f4 | |
--- /dev/null | |
+++ b/src/cert/cert.c | |
@@ -0,0 +1,568 @@ | |
+/** | |
+ * @file cert.c Certificate handling | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+/* for certificate parsing */ | |
+#include <openssl/x509.h> | |
+#include <openssl/x509v3.h> | |
+#include <openssl/pem.h> | |
+#include <openssl/bio.h> | |
+#include <openssl/err.h> | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_sys.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include <re_cert.h> | |
+ | |
+ | |
+#define DEBUG_MODULE "cert" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+/* XXX: only RSA supported for now */ | |
+ | |
+ | |
+struct cert { | |
+ X509 *x509; | |
+ RSA *rsa; | |
+}; | |
+ | |
+ | |
+static void destructor(void *data) | |
+{ | |
+ struct cert *cert = data; | |
+ | |
+ if (cert->rsa) | |
+ RSA_free(cert->rsa); | |
+ if (cert->x509) | |
+ X509_free(cert->x509); | |
+} | |
+ | |
+ | |
+/* X.509v3 in ASN.1 format */ | |
+int cert_decode(struct cert **certp, const uint8_t *p, size_t len) | |
+{ | |
+ const unsigned char *data; | |
+ struct cert *cert; | |
+ int err = 0; | |
+ | |
+ if (!certp || !p || !len) | |
+ return EINVAL; | |
+ | |
+ cert = mem_zalloc(sizeof(*cert), destructor); | |
+ if (!cert) | |
+ return ENOMEM; | |
+ | |
+ data = p; | |
+ | |
+ cert->x509 = d2i_X509(NULL, &data, len); | |
+ if (!cert->x509) { | |
+ DEBUG_WARNING("unable to parse certificate %zu bytes in memory" | |
+ " at offset %zu\n", len, data - p); | |
+ ERR_print_errors_fp(stderr); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(cert); | |
+ else | |
+ *certp = cert; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+/* Certificate in PEM format (Cert + Private key) */ | |
+int cert_decode_pem(struct cert **certp, const char *pem, size_t len) | |
+{ | |
+ BIO *bio = NULL, *kbio = NULL; | |
+ struct cert *cert; | |
+ int err = 0; | |
+ | |
+ if (!certp || !pem || !len) | |
+ return EINVAL; | |
+ | |
+ cert = mem_zalloc(sizeof(*cert), destructor); | |
+ if (!cert) | |
+ return ENOMEM; | |
+ | |
+ bio = BIO_new_mem_buf((char *)pem, (int)len); | |
+ kbio = BIO_new_mem_buf((char *)pem, (int)len); | |
+ if (!bio || !kbio) { | |
+ err = ENOMEM; | |
+ goto out; | |
+ } | |
+ | |
+ cert->x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); | |
+ if (!cert->x509) { | |
+ DEBUG_WARNING("unable to parse PEM certificate (%zu bytes)\n", | |
+ len); | |
+ ERR_print_errors_fp(stderr); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ cert->rsa = PEM_read_bio_RSAPrivateKey(kbio, NULL, 0, NULL); | |
+ if (!cert->rsa) { | |
+ DEBUG_WARNING("decode_pem: RSA read error\n"); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ | |
+ out: | |
+ if (kbio) | |
+ BIO_free(kbio); | |
+ if (bio) | |
+ BIO_free(bio); | |
+ if (err) | |
+ ERR_clear_error(); | |
+ | |
+ if (err) | |
+ mem_deref(cert); | |
+ else | |
+ *certp = cert; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+/* Certificate in PEM format (Cert + Private key) */ | |
+int cert_load_file(struct cert **certp, const char *filename) | |
+{ | |
+ BIO *bio = NULL, *kbio = NULL; | |
+ struct cert *cert; | |
+ int err = 0; | |
+ | |
+ if (!certp || !filename) | |
+ return EINVAL; | |
+ | |
+ cert = mem_zalloc(sizeof(*cert), destructor); | |
+ if (!cert) | |
+ return ENOMEM; | |
+ | |
+ bio = BIO_new_file(filename, "r"); | |
+ kbio = BIO_new_file(filename, "r"); | |
+ if (!bio || !kbio) { | |
+ err = ENOMEM; | |
+ goto out; | |
+ } | |
+ | |
+ cert->x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); | |
+ if (!cert->x509) { | |
+ DEBUG_WARNING("unable to parse PEM certificate (%s)\n", | |
+ filename); | |
+ ERR_print_errors_fp(stderr); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ cert->rsa = PEM_read_bio_RSAPrivateKey(kbio, NULL, 0, NULL); | |
+ if (!cert->rsa) { | |
+ DEBUG_WARNING("decode_pem: RSA read error (%s)\n", filename); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ out: | |
+ if (kbio) | |
+ BIO_free(kbio); | |
+ if (bio) | |
+ BIO_free(bio); | |
+ if (err) | |
+ ERR_clear_error(); | |
+ | |
+ if (err) | |
+ mem_deref(cert); | |
+ else | |
+ *certp = cert; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int cert_encode_der(const struct cert *cert, uint8_t **derp, size_t *lenp) | |
+{ | |
+ uint8_t *der = NULL, *p; | |
+ int sz, len; | |
+ int err = 0; | |
+ | |
+ if (!cert || !derp || !lenp) | |
+ return EINVAL; | |
+ | |
+ sz = i2d_X509(cert->x509, NULL); | |
+ if (sz < 0) { | |
+ /* error */ | |
+ ERR_clear_error(); | |
+ | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ der = mem_alloc(sz, NULL); | |
+ if (!der) | |
+ return ENOMEM; | |
+ | |
+ p = der; | |
+ | |
+ len = i2d_X509(cert->x509, &p); | |
+ if (len < 0) { | |
+ /* error */ | |
+ ERR_clear_error(); | |
+ | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ *derp = der; | |
+ *lenp = len; | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(der); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int cert_generate_rsa(struct cert **certp, const char *cn, unsigned bits) | |
+{ | |
+ X509_NAME *subj = NULL; | |
+ EVP_PKEY *key = NULL; | |
+ X509 *x509 = NULL; | |
+ BIGNUM *bn = NULL; | |
+ RSA *rsa = NULL; | |
+ int err = ENOMEM; | |
+ struct cert *cert = 0; | |
+ | |
+ if (!certp || !cn) | |
+ return EINVAL; | |
+ | |
+ cert = mem_zalloc(sizeof(*cert), destructor); | |
+ if (!cert) | |
+ return ENOMEM; | |
+ | |
+ rsa = RSA_new(); | |
+ if (!rsa) | |
+ goto out; | |
+ | |
+ bn = BN_new(); | |
+ if (!bn) | |
+ goto out; | |
+ | |
+ BN_set_word(bn, RSA_F4); | |
+ if (!RSA_generate_key_ex(rsa, bits, bn, NULL)) | |
+ goto out; | |
+ | |
+ key = EVP_PKEY_new(); | |
+ if (!key) | |
+ goto out; | |
+ | |
+ if (!EVP_PKEY_set1_RSA(key, rsa)) | |
+ goto out; | |
+ | |
+ x509 = X509_new(); | |
+ if (!x509) | |
+ goto out; | |
+ | |
+ if (!X509_set_version(x509, 2)) | |
+ goto out; | |
+ | |
+ if (!ASN1_INTEGER_set(X509_get_serialNumber(x509), rand_u32())) | |
+ goto out; | |
+ | |
+ subj = X509_NAME_new(); | |
+ if (!subj) | |
+ goto out; | |
+ | |
+ if (!X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC, | |
+ (unsigned char *)cn, | |
+ (int)str_len(cn), -1, 0)) | |
+ goto out; | |
+ | |
+ if (!X509_set_issuer_name(x509, subj) || | |
+ !X509_set_subject_name(x509, subj)) | |
+ goto out; | |
+ | |
+ if (!X509_gmtime_adj(X509_get_notBefore(x509), -3600*24*365) || | |
+ !X509_gmtime_adj(X509_get_notAfter(x509), 3600*24*365*10)) | |
+ goto out; | |
+ | |
+ if (!X509_set_pubkey(x509, key)) | |
+ goto out; | |
+ | |
+ if (!X509_sign(x509, key, EVP_sha1())) | |
+ goto out; | |
+ | |
+ cert->x509 = x509; | |
+ x509 = NULL; | |
+ | |
+ cert->rsa = rsa; | |
+ rsa = NULL; | |
+ | |
+ *certp = cert; | |
+ | |
+ err = 0; | |
+ | |
+ out: | |
+ if (subj) | |
+ X509_NAME_free(subj); | |
+ | |
+ if (x509) | |
+ X509_free(x509); | |
+ | |
+ if (key) | |
+ EVP_PKEY_free(key); | |
+ | |
+ if (rsa) | |
+ RSA_free(rsa); | |
+ | |
+ if (bn) | |
+ BN_free(bn); | |
+ | |
+ if (err) | |
+ ERR_clear_error(); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int cert_version(const struct cert *cert) | |
+{ | |
+ if (!cert) | |
+ return 0; | |
+ | |
+ return (int)X509_get_version(cert->x509); | |
+} | |
+ | |
+ | |
+long cert_serial(const struct cert *cert) | |
+{ | |
+ ASN1_INTEGER *a; | |
+ | |
+ if (!cert) | |
+ return 0; | |
+ | |
+ a = X509_get_serialNumber(cert->x509); | |
+ if (!a) | |
+ return 0; | |
+ | |
+ return ASN1_INTEGER_get(a); | |
+} | |
+ | |
+ | |
+/* asymmetric encrypt using this Certificate's public key */ | |
+int cert_public_encrypt(struct cert *cert, | |
+ uint8_t *out, size_t *out_len, | |
+ const uint8_t *in, size_t in_len) | |
+{ | |
+ EVP_PKEY *pubkey = NULL; | |
+ RSA *rsa_key = NULL; | |
+ int key_length; | |
+ int algonid, r; | |
+ int err = 0; | |
+ | |
+ if (!cert || !out || !out_len || !*out_len || !in || !in_len) | |
+ return EINVAL; | |
+ | |
+ pubkey = X509_get_pubkey(cert->x509); | |
+ if (!pubkey) { | |
+ DEBUG_WARNING("cert: could not get public key\n"); | |
+ err = ENOENT; | |
+ goto out; | |
+ } | |
+ | |
+ algonid = EVP_PKEY_id(pubkey); | |
+#if 0 | |
+ algonid = OBJ_obj2nid(cert->x509->cert_info->key->algor->algorithm); | |
+#endif | |
+ | |
+ DEBUG_INFO("cert: public key has algorithm '%s'\n", | |
+ OBJ_nid2ln(algonid)); | |
+ | |
+ switch (algonid) { | |
+ | |
+ case EVP_PKEY_RSA: | |
+ rsa_key = EVP_PKEY_get1_RSA(pubkey); | |
+ if (!rsa_key) { | |
+ DEBUG_WARNING("no rsa_key\n"); | |
+ err = ENOENT; | |
+ goto out; | |
+ } | |
+ | |
+#if 0 | |
+ re_fprintf(stderr, "RSA public key:\n"); | |
+ RSA_print_fp(stderr, rsa_key, 16); | |
+#endif | |
+ | |
+ key_length = RSA_size(rsa_key); | |
+ if (key_length > (int)*out_len) { | |
+ re_printf("cert: RSA key is %d bits, but out" | |
+ " size is only %d bits\n", | |
+ key_length * 8, | |
+ *out_len * 8); | |
+ err = EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ r = RSA_public_encrypt((int)in_len, in, | |
+ out, | |
+ rsa_key, RSA_PKCS1_PADDING); | |
+ if (r == -1) { | |
+ DEBUG_WARNING("encrypt: %s\n", | |
+ ERR_error_string(ERR_get_error(), NULL)); | |
+ goto out; | |
+ } | |
+ if (r > (int)(*out_len)) { | |
+ DEBUG_WARNING("cert: out buffer too small\n"); | |
+ err = ENOBUFS; | |
+ goto out; | |
+ } | |
+ *out_len = r; | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("cert: unsupported public key algonid %d\n", | |
+ algonid); | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+ | |
+ out: | |
+ if (rsa_key) | |
+ RSA_free(rsa_key); | |
+ if (pubkey) | |
+ EVP_PKEY_free(pubkey); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+/* asymmetric decrypt using this Certificate's private key */ | |
+int cert_private_decrypt(struct cert *cert, | |
+ uint8_t *out, size_t *out_len, | |
+ const uint8_t *in, size_t in_len) | |
+{ | |
+ int key_length; | |
+ int r; | |
+ int err = 0; | |
+ | |
+ if (!cert || !out || !out_len || !*out_len || !in || !in_len) | |
+ return EINVAL; | |
+ | |
+ if (!cert->rsa) { | |
+ DEBUG_WARNING("decrypt: no private RSA\n"); | |
+ return EINVAL; | |
+ } | |
+ | |
+ key_length = RSA_size(cert->rsa); | |
+ if (key_length > (int)*out_len) { | |
+ re_printf("cert: RSA key is %d bits, but out" | |
+ " size is only %d bits\n", | |
+ key_length * 8, | |
+ *out_len * 8); | |
+ err = EINVAL; | |
+ goto out; | |
+ } | |
+ | |
+ r = RSA_private_decrypt((int)in_len, in, | |
+ out, cert->rsa, RSA_PKCS1_PADDING); | |
+ if (r == -1) { | |
+ err = EBADMSG; | |
+ DEBUG_WARNING("decrypt: %s\n", | |
+ ERR_error_string(ERR_get_error(), NULL)); | |
+ ERR_clear_error(); | |
+ goto out; | |
+ } | |
+ if (r > (int)(*out_len)) { | |
+ DEBUG_WARNING("cert: out buffer too small\n"); | |
+ err = ENOBUFS; | |
+ goto out; | |
+ } | |
+ *out_len = r; | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+int cert_get_issuer(const struct cert *cert, char *buf, size_t size) | |
+{ | |
+ if (!cert) | |
+ return EINVAL; | |
+ | |
+ X509_NAME_oneline(X509_get_issuer_name(cert->x509), buf, (int)size); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int cert_get_subject(const struct cert *cert, char *buf, size_t size) | |
+{ | |
+ int n; | |
+ | |
+ if (!cert) | |
+ return EINVAL; | |
+ | |
+ n = X509_NAME_get_text_by_NID(X509_get_subject_name(cert->x509), | |
+ NID_commonName, buf, (int)size); | |
+ if (n < 0) { | |
+ ERR_clear_error(); | |
+ return ENOENT; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int cert_get_fingerprint(const struct cert *cert, int type, | |
+ uint8_t *md, size_t size) | |
+{ | |
+ unsigned int len = (unsigned int)size; | |
+ int n; | |
+ | |
+ switch (type) { | |
+ | |
+ case TLS_FINGERPRINT_SHA1: | |
+ if (size < 20) | |
+ return EOVERFLOW; | |
+ | |
+ n = X509_digest(cert->x509, EVP_sha1(), md, &len); | |
+ break; | |
+ | |
+ case TLS_FINGERPRINT_SHA256: | |
+ if (size < 32) | |
+ return EOVERFLOW; | |
+ | |
+ n = X509_digest(cert->x509, EVP_sha256(), md, &len); | |
+ break; | |
+ | |
+ default: | |
+ return ENOSYS; | |
+ } | |
+ | |
+ if (n != 1) { | |
+ ERR_clear_error(); | |
+ return ENOENT; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+void cert_dump(const struct cert *cert) | |
+{ | |
+ if (!cert) | |
+ return; | |
+ | |
+ X509_print_fp(stderr, cert->x509); | |
+} | |
diff --git a/src/cert/mod.mk b/src/cert/mod.mk | |
new file mode 100644 | |
index 0000000..ac91204 | |
--- /dev/null | |
+++ b/src/cert/mod.mk | |
@@ -0,0 +1,7 @@ | |
+# | |
+# mod.mk | |
+# | |
+# Copyright (C) 2010 Creytiv.com | |
+# | |
+ | |
+SRCS += cert/cert.c | |
diff --git a/src/hmac/hmac_sha1.c b/src/hmac/hmac_sha1.c | |
index 1fdf0c5..ceaa8af 100644 | |
--- a/src/hmac/hmac_sha1.c | |
+++ b/src/hmac/hmac_sha1.c | |
@@ -5,6 +5,7 @@ | |
*/ | |
#include <string.h> | |
#include <re_types.h> | |
+#include <re_mem.h> | |
#ifdef USE_OPENSSL | |
#include <openssl/sha.h> | |
#include <openssl/hmac.h> | |
diff --git a/src/main/openssl.c b/src/main/openssl.c | |
index dfeb91e..3605573 100644 | |
--- a/src/main/openssl.c | |
+++ b/src/main/openssl.c | |
@@ -149,8 +149,10 @@ int openssl_init(void) | |
(void)signal(SIGPIPE, sigpipe_handler); | |
#endif | |
+#ifdef USE_OPENSSL_TLS | |
SSL_library_init(); | |
SSL_load_error_strings(); | |
+#endif | |
return 0; | |
} | |
diff --git a/src/mem/mem.c b/src/mem/mem.c | |
index 1f634c6..299b7aa 100644 | |
--- a/src/mem/mem.c | |
+++ b/src/mem/mem.c | |
@@ -3,6 +3,7 @@ | |
* | |
* Copyright (C) 2010 Creytiv.com | |
*/ | |
+#include <assert.h> | |
#include <ctype.h> | |
#include <stdlib.h> | |
#include <string.h> | |
@@ -37,6 +38,13 @@ struct mem { | |
#endif | |
}; | |
+ | |
+#define TRAILER_MAGIC 0xc001d00d | |
+struct trailer { | |
+ uint32_t magic; | |
+}; | |
+ | |
+ | |
#if MEM_DEBUG | |
/* Memory debugging */ | |
static struct list meml = LIST_INIT; | |
@@ -126,6 +134,9 @@ static inline void mem_unlock(void) | |
void *mem_alloc(size_t size, mem_destroy_h *dh) | |
{ | |
struct mem *m; | |
+ struct trailer *tr; | |
+ size_t rl_size; | |
+ void *ptr; | |
#if MEM_DEBUG | |
mem_lock(); | |
@@ -136,7 +147,8 @@ void *mem_alloc(size_t size, mem_destroy_h *dh) | |
mem_unlock(); | |
#endif | |
- m = malloc(sizeof(*m) + size); | |
+ rl_size = sizeof(*m) + size + sizeof(*tr); | |
+ m = malloc(rl_size); | |
if (!m) | |
return NULL; | |
@@ -152,7 +164,12 @@ void *mem_alloc(size_t size, mem_destroy_h *dh) | |
STAT_ALLOC(m, size); | |
- return (void *)(m + 1); | |
+ ptr = (void *)(m + 1); | |
+ | |
+ tr = (void *)( (char *)ptr + size); | |
+ tr->magic = TRAILER_MAGIC; | |
+ | |
+ return ptr; | |
} | |
@@ -191,10 +208,14 @@ void *mem_zalloc(size_t size, mem_destroy_h *dh) | |
void *mem_realloc(void *data, size_t size) | |
{ | |
struct mem *m, *m2; | |
+ struct trailer *tr; | |
+ void *ptr; | |
if (!data) | |
return NULL; | |
+ assert(mem_is_valid(data)); | |
+ | |
m = ((struct mem *)data) - 1; | |
MAGIC_CHECK(m); | |
@@ -214,7 +235,7 @@ void *mem_realloc(void *data, size_t size) | |
mem_unlock(); | |
#endif | |
- m2 = realloc(m, sizeof(*m2) + size); | |
+ m2 = realloc(m, sizeof(*m2) + size + sizeof(*tr)); | |
#if MEM_DEBUG | |
mem_lock(); | |
@@ -228,6 +249,12 @@ void *mem_realloc(void *data, size_t size) | |
STAT_REALLOC(m2, size); | |
+ ptr = (void *)(m2 + 1); | |
+ | |
+ tr = (void *)( (char *)ptr + size); | |
+ tr->magic = TRAILER_MAGIC; | |
+ | |
+ | |
return (void *)(m2 + 1); | |
} | |
@@ -278,6 +305,8 @@ void *mem_ref(void *data) | |
{ | |
struct mem *m; | |
+ assert(mem_is_valid(data)); | |
+ | |
if (!data) | |
return NULL; | |
@@ -307,6 +336,8 @@ void *mem_deref(void *data) | |
if (!data) | |
return NULL; | |
+ assert(mem_is_valid(data)); | |
+ | |
m = ((struct mem *)data) - 1; | |
MAGIC_CHECK(m); | |
@@ -349,6 +380,8 @@ uint32_t mem_nrefs(const void *data) | |
if (!data) | |
return 0; | |
+ assert(mem_is_valid(data)); | |
+ | |
m = ((struct mem *)data) - 1; | |
MAGIC_CHECK(m); | |
@@ -507,3 +540,51 @@ int mem_get_stat(struct memstat *mstat) | |
return ENOSYS; | |
#endif | |
} | |
+ | |
+ | |
+bool mem_is_valid(const void *data) | |
+{ | |
+ struct mem *m; | |
+ struct trailer *tr; | |
+ | |
+ if (!data) | |
+ return true; | |
+ | |
+ m = ((struct mem *)data) - 1; | |
+ | |
+ tr = (void *)((uint8_t *)data + m->size); | |
+ | |
+ if (m->magic != mem_magic) { | |
+ DEBUG_WARNING("invalid header magic\n"); | |
+ return false; | |
+ } | |
+ if (tr->magic != TRAILER_MAGIC) { | |
+ DEBUG_WARNING("invalid trailer magic (nrefs=%u, size=%zu)\n", | |
+ m->nrefs, m->size); | |
+ return false; | |
+ } | |
+ | |
+ return true; | |
+} | |
+ | |
+ | |
+void mem_print(const void *data) | |
+{ | |
+ struct mem *m; | |
+ struct trailer *tr; | |
+ | |
+ if (!data) | |
+ return; | |
+ | |
+ m = ((struct mem *)data) - 1; | |
+ | |
+ tr = (void *)((uint8_t *)data + m->size); | |
+ | |
+ re_printf("memory object at %p:\n", data); | |
+ re_printf("nrefs=%u, dh=%p, size=%zu\n", | |
+ m->nrefs, m->dh, m->size); | |
+ | |
+ hexdump(stdout, data, m->size); | |
+ | |
+ re_printf("trailer: 0x%08x\n", tr->magic); | |
+} | |
diff --git a/src/tls/mod.mk b/src/tls/mod.mk | |
index f653395..cdeca37 100644 | |
--- a/src/tls/mod.mk | |
+++ b/src/tls/mod.mk | |
@@ -4,8 +4,37 @@ | |
# Copyright (C) 2010 Creytiv.com | |
# | |
-ifneq ($(USE_OPENSSL),) | |
+ifneq ($(USE_OPENSSL_TLS),) | |
SRCS += tls/openssl/tls.c | |
SRCS += tls/openssl/tls_tcp.c | |
SRCS += tls/openssl/tls_udp.c | |
+else | |
+ | |
+SRCS += tls/re/alert.c | |
+SRCS += tls/re/cipher.c | |
+SRCS += tls/re/client.c | |
+SRCS += tls/re/crypt.c | |
+SRCS += tls/re/extension.c | |
+SRCS += tls/re/finish.c | |
+SRCS += tls/re/handshake.c | |
+SRCS += tls/re/hmac.c | |
+SRCS += tls/re/key.c | |
+SRCS += tls/re/mac.c | |
+SRCS += tls/re/master.c | |
+SRCS += tls/re/mbuf.c | |
+SRCS += tls/re/prf.c | |
+SRCS += tls/re/record.c | |
+SRCS += tls/re/record_layer.c | |
+SRCS += tls/re/secparam.c | |
+SRCS += tls/re/server.c | |
+SRCS += tls/re/session.c | |
+SRCS += tls/re/trace.c | |
+SRCS += tls/re/util.c | |
+SRCS += tls/re/vector.c | |
+SRCS += tls/re/version.c | |
+ | |
+SRCS += tls/re/tls.c | |
+SRCS += tls/re/tls_tcp.c | |
+SRCS += tls/re/tls_udp.c | |
+ | |
endif | |
diff --git a/src/tls/openssl/tls.c b/src/tls/openssl/tls.c | |
index 85e2fd4..42560af 100644 | |
--- a/src/tls/openssl/tls.c | |
+++ b/src/tls/openssl/tls.c | |
@@ -42,6 +42,46 @@ struct tls_conn { | |
}; | |
+static BIO *bio_err; | |
+ | |
+ | |
+static void apps_ssl_info_callback(const SSL *s, int where, int ret) | |
+{ | |
+ const char *str; | |
+ int w; | |
+ | |
+ if (!bio_err) | |
+ bio_err = BIO_new_fp(stderr, 0); | |
+ | |
+ w=where& ~SSL_ST_MASK; | |
+ | |
+ if (w & SSL_ST_CONNECT) str="SSL_connect"; | |
+ else if (w & SSL_ST_ACCEPT) str="SSL_accept"; | |
+ else str="undefined"; | |
+ | |
+ if (where & SSL_CB_LOOP) { | |
+ BIO_printf(bio_err,"%s:%s\n",str,SSL_state_string_long(s)); | |
+ } | |
+ else if (where & SSL_CB_ALERT) { | |
+ str=(where & SSL_CB_READ)?"read":"write"; | |
+ BIO_printf(bio_err,"SSL3 alert %s:%s:%s\n", | |
+ str, | |
+ SSL_alert_type_string_long(ret), | |
+ SSL_alert_desc_string_long(ret)); | |
+ } | |
+ else if (where & SSL_CB_EXIT) { | |
+ if (ret == 0) { | |
+ BIO_printf(bio_err,"SSL_CB_EXIT %s:failed in %s\n", | |
+ str,SSL_state_string_long(s)); | |
+ } | |
+ else if (ret < 0) { | |
+ BIO_printf(bio_err,"SSL_CB_EXIT %s:error in %s\n", | |
+ str,SSL_state_string_long(s)); | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
static void destructor(void *data) | |
{ | |
struct tls *tls = data; | |
@@ -116,6 +156,10 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, | |
tls->ctx = SSL_CTX_new(SSLv23_method()); | |
break; | |
+ case TLS_METHOD_TLSV1_2: | |
+ tls->ctx = SSL_CTX_new(TLSv1_2_method()); | |
+ break; | |
+ | |
#ifdef USE_OPENSSL_DTLS | |
case TLS_METHOD_DTLSV1: | |
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ | |
@@ -163,6 +207,15 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, | |
SSL_CTX_set_verify_depth(tls->ctx, 1); | |
#endif | |
+#if 1 | |
+ r = SSL_CTX_set_cipher_list(tls->ctx, "NULL:AES"); | |
+ if (r <= 0) { | |
+ DEBUG_WARNING("set_cipher_list failed\n"); | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+#endif | |
+ | |
/* Load our keys and certificates */ | |
if (keyfile) { | |
if (pwd) { | |
@@ -194,6 +247,9 @@ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, | |
} | |
} | |
+ /* for testing Handshake reasm code */ | |
+ SSL_CTX_set_max_send_fragment(tls->ctx, 512); | |
+ | |
err = 0; | |
out: | |
if (err) | |
diff --git a/src/tls/openssl/tls_tcp.c b/src/tls/openssl/tls_tcp.c | |
index 25bd9e1..21260f8 100644 | |
--- a/src/tls/openssl/tls_tcp.c | |
+++ b/src/tls/openssl/tls_tcp.c | |
@@ -183,6 +183,11 @@ static int tls_connect(struct tls_conn *tc) | |
if (r <= 0) { | |
const int ssl_err = SSL_get_error(tc->ssl, r); | |
+ if (ssl_err == SSL_ERROR_SSL) { | |
+ DEBUG_WARNING("connect: SSL_ERROR_SSL\n"); | |
+ ERR_print_errors_fp(stderr); | |
+ } | |
+ | |
ERR_clear_error(); | |
switch (ssl_err) { | |
@@ -212,6 +217,11 @@ static int tls_accept(struct tls_conn *tc) | |
if (r <= 0) { | |
const int ssl_err = SSL_get_error(tc->ssl, r); | |
+ if (ssl_err != SSL_ERROR_WANT_READ) { | |
+ DEBUG_WARNING("accept: ERROR\n"); | |
+ ERR_print_errors_fp(stderr); | |
+ } | |
+ | |
ERR_clear_error(); | |
switch (ssl_err) { | |
@@ -302,6 +312,11 @@ static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg) | |
if (n <= 0) { | |
const int ssl_err = SSL_get_error(tc->ssl, n); | |
+ if (ssl_err != SSL_ERROR_WANT_READ) { | |
+ DEBUG_WARNING("SSL_read: ERROR\n"); | |
+ ERR_print_errors_fp(stderr); | |
+ } | |
+ | |
ERR_clear_error(); | |
switch (ssl_err) { | |
diff --git a/src/tls/openssl/tls_udp.c b/src/tls/openssl/tls_udp.c | |
index 4ec81a3..8947254 100644 | |
--- a/src/tls/openssl/tls_udp.c | |
+++ b/src/tls/openssl/tls_udp.c | |
@@ -398,12 +398,12 @@ static void conn_recv(struct tls_conn *tc, struct mbuf *mb) | |
tc->estabh(tc->arg); | |
- nrefs = mem_nrefs(tc); | |
- mem_deref(tc); | |
+ nrefs = mem_nrefs(tc); | |
+ mem_deref(tc); | |
- /* check if connection was deref'd from handler */ | |
- if (nrefs == 1) | |
- return; | |
+ /* check if connection was deref'd from handler */ | |
+ if (nrefs == 1) | |
+ return; | |
} | |
} | |
@@ -459,6 +459,23 @@ static void conn_recv(struct tls_conn *tc, struct mbuf *mb) | |
} | |
+#if 0 | |
+static void msg_cb(int write_p, int version, int content_type, const void *buf, | |
+ size_t len, SSL *ssl, void *arg) | |
+{ | |
+ const char *str_write_p = write_p ? "send >>>" : "recv <<<"; | |
+ | |
+ re_fprintf(stderr, | |
+ "\x1b[36m" | |
+ "server: %s version=%04x type=%d len=%zu" | |
+ "\x1b[;m" | |
+ "\n" | |
+ , | |
+ str_write_p, version, content_type, len); | |
+} | |
+#endif | |
+ | |
+ | |
static int conn_alloc(struct tls_conn **ptc, struct tls *tls, | |
struct dtls_sock *sock, const struct sa *peer, | |
dtls_estab_h *estabh, dtls_recv_h *recvh, | |
@@ -527,6 +544,18 @@ static int conn_alloc(struct tls_conn **ptc, struct tls *tls, | |
SSL_set_read_ahead(tc->ssl, 1); | |
+#if 1 | |
+ SSL_set_options(tc->ssl, SSL_OP_NO_QUERY_MTU); | |
+ if (!DTLS_set_link_mtu(tc->ssl, 1480)) { | |
+ DEBUG_WARNING("DTLS_set_link_mtu error\n"); | |
+ } | |
+#endif | |
+ | |
+#if 0 | |
+ SSL_set_debug(tc->ssl, 1); | |
+ SSL_set_msg_callback(tc->ssl, msg_cb); | |
+#endif | |
+ | |
out: | |
if (err) | |
mem_deref(tc); | |
@@ -746,7 +775,7 @@ static struct tls_conn *conn_lookup(struct dtls_sock *sock, | |
const struct sa *peer) | |
{ | |
return list_ledata(hash_lookup(sock->ht, sa_hash(peer, SA_ALL), | |
- cmp_handler, (void *)peer)); | |
+ cmp_handler, (void *)peer)); | |
} | |
@@ -756,6 +785,9 @@ static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg) | |
struct tls_conn *tc; | |
uint8_t b; | |
+ DEBUG_INFO("recv %zu bytes from %J\n", | |
+ mbuf_get_left(mb), src); | |
+ | |
if (mbuf_get_left(mb) < 1) | |
return false; | |
diff --git a/src/tls/re/README b/src/tls/re/README | |
new file mode 100644 | |
index 0000000..435b8b9 | |
--- /dev/null | |
+++ b/src/tls/re/README | |
@@ -0,0 +1,258 @@ | |
+TLS Protocol stack | |
+------------------ | |
+ | |
+ | |
+ | |
+ | |
+ .---------. .--------. .-------. .-----------. | |
+ | | | Change | | | |Application| | |
+ |Handshake| | Cipher | | Alert | | Data | | |
+ | | | Spec | | | | | | |
+ '---------' '--------' '-------' '-----------' | |
+ | | | | | |
+ | | | | | |
+ .-----------------------------------------------. * Transaction-layer | |
+ | | * Payload integrity | |
+ | TLS Record layer | * Cryptographic securty | |
+ | | * Compression | |
+ '-----------------------------------------------' | |
+ | | |
+ | | |
+ .-----------. | |
+ | UDP/TCP | | |
+ '-----------' | |
+ | |
+ | |
+ RECORD LAYER OVERVIEW: | |
+ | |
+ | |
+ Write Read | |
+ .----------------------------------------------------. | |
+ | | /|\ | | |
+ | \|/ | | | |
+ | .---------------. .---------------. | | |
+ | | Fragmentation | | Reassemble | | | |
+ | '---------------' '---------------' | | |
+ | | /|\ | | |
+ | \|/ | | | |
+ | .---------------. .---------------. | | |
+ | | Compression | | Decompress | | | |
+ | '---------------' '---------------' | | |
+ | | /|\ | | |
+ | \|/ | | | |
+ | .---------------. .---------------. | | |
+ | | Apply MAC | | Verify MAC | | | |
+ | '---------------' '---------------' | | |
+ | | /|\ | | |
+ | \|/ | | | |
+ | .---------------. .---------------. | | |
+ | | Encrypt | | Decrypt | | | |
+ | '---------------' '---------------' | | |
+ | | /|\ | | |
+ | \|/ | | | |
+ '----------------------------------------------------' | |
+ transmit receive | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+Functional overview: | |
+------------------- | |
+ | |
+TLS Version 1.0 (RFC 2246) ....... NO | |
+TLS Version 1.1 (RFC 4346) ....... NO | |
+TLS Version 1.2 (RFC 5246) ....... YES | |
+DTLS Version 1.0 (RFC 4347) ...... NO | |
+DTLS Version 1.2 (RFC 6347) ...... YES | |
+ | |
+Endpoints: | |
+Client ........................... YES | |
+Server ........................... YES | |
+ | |
+Key Exchange: | |
+RSA .............................. YES | |
+RSA_PSK .......................... NO | |
+DHE_RSA .......................... NO | |
+ECDHE_RSA ........................ NO | |
+DHE_DSS .......................... NO | |
+DH_DSS ........................... NO | |
+DH_RSA ........................... NO | |
+ECDH_ECDSA ....................... NO | |
+ECDH_RSA ......................... NO | |
+ECDHE_ECDSA ...................... NO | |
+ | |
+Cipher: | |
+NULL ............................. YES | |
+RC4_128 .......................... NO | |
+3DES_EDE_CBC ..................... NO | |
+AES_128_CBC ...................... YES | |
+AES_256_CBC ...................... NO | |
+ | |
+MAC: | |
+NULL ............................. YES | |
+MD5 .............................. NO | |
+SHA .............................. YES | |
+SHA256 ........................... YES | |
+ | |
+Compression: | |
+Null ............................. YES | |
+Deflate .......................... NO (not planned) | |
+ | |
+Record types: | |
+ChangeCipherSpec ................. YES | |
+Alert ............................ YES | |
+Handshake ........................ YES | |
+ApplicationData .................. YES | |
+ | |
+Transport: | |
+UDP retransmit and queueing (tx).. NO (todo) | |
+UDP sending fragmentation ........ NO (todo) | |
+UDP receiving reassembly ......... NO (todo) | |
+TCP transport .................... YES | |
+Multiple handshakes per record ... YES | |
+Multiple records per handshake ... YES | |
+ | |
+Extensions: | |
+server_name (RFC 6066) ........... YES | |
+usr_srtp (RFC 5764) .............. YES | |
+ | |
+PRF P_SHA256 ......................YES | |
+ | |
+Debug tracing .....................YES | |
+ | |
+ | |
+verify integrity of certificates... NO (todo) | |
+support certificate revocation .... NO | |
+secure storage of keys ............ NO (todo) | |
+ | |
+RFC 5264 D.4 Implementation Pitfalls ..... NO (todo) | |
+ | |
+DTLS 4.1.2.6. Anti-Replay ............... NO | |
+ | |
+ | |
+ | |
+ | |
+--> Testing vulnerabilities | |
+ | |
+ Heartbleed (CVE-2014-0160) not vulnerable (OK) | |
+ CCS (CVE-2014-0224) not vulnerable (OK) | |
+ Secure Renegotiation (CVE-2009-3555) VULNERABLE (NOT ok) | |
+ Secure Client-Initiated Renegotiation not vulnerable (OK) | |
+ CRIME, TLS (CVE-2012-4929) not vulnerable (OK) (not using HTTP anyway) | |
+ POODLE, SSL (CVE-2014-3566) not vulnerable (OK) | |
+ TLS_FALLBACK_SCSV (RFC 7507), experim. Check failed: seems like TLS 1.2 is the only protocol | |
+ FREAK (CVE-2015-0204) not vulnerable (OK) (tested with 4/9 ciphers) | |
+ LOGJAM (CVE-2015-4000), experimental not vulnerable (OK) (tested w/ 2/4 ciphers only!), common primes not checked. See below for any DH ciphers + bit size | |
+ BEAST (CVE-2011-3389) no SSL3 or TLS1 | |
+ RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK) | |
+ | |
+ | |
+ | |
+ | |
+ | |
+TODO: | |
+---- | |
+ | |
+[DONE] Goal 1: simple DTLS client connecting to OpenSSL DTLS server | |
+ - RSA Certificate only (no DH) | |
+ - NULL cipher | |
+ - simple MAC (such as MD5,SHA1) | |
+ - check that master_secret is same | |
+ | |
+[DONE] Goal 2: simple TLS client | |
+ | |
+[DONE] Goal 3: add support for AES encryption/decryption | |
+ | |
+[DONE] Goal 4: send/recv application data | |
+ | |
+[DONE] Goal 5: simple TLS server | |
+ | |
+ Goal 6: simple DTLS server | |
+ | |
+ Goal 7: test various TLS tools against TLS server | |
+ | |
+ compile with LibreSSL on OpenBSD | |
+ | |
+ todo: add a failing testcase | |
+ | |
+ | |
+ | |
+[Client] [Server] | |
+ | |
+54 -------- ClientHello -------> | |
+ | |
+82 <------- ServerHello ------- | |
+434 <------- Certificate ------- | |
+12 <----- ServerHelloDone ----- | |
+ | |
+142 ----- ClientKeyExchange ----> | |
+ | |
+ ===== ChangeCipherSpec =====> COMMIT | |
+ -------- Finished (*) ------> | |
+ | |
+COMMIT<===== ChangeCipherSpec ===== | |
+ <--------- Finished (*) ----- | |
+ | |
+ | |
+(*) Encrypted | |
+ | |
+ | |
+ | |
+ | |
+Software architecture: | |
+ | |
+ .-------------. | |
+ | Application | | |
+ '-------------' | |
+ | | |
+ | | |
+ \|/ | |
+ .----------------------------------------. | |
+ | | | |
+ | TLS/DTLS | | |
+ | | | |
+ '----------------------------------------' | |
+ | | | | | |
+ | | | | | |
+ \|/ \|/ \|/ \|/ | |
+ .------. .-----. .------. .-----. | |
+ | Cert | | AES | | HMAC | | SHA | | |
+ '------' '-----' '------' '-----' | |
+ | |
+ Certificate AES-CBC HMAC-SHA1 SHA256 | |
+ Handling 128/256 HMAC-SHA256 | |
+ + RSA | |
+ | |
+ | |
+ | |
+ | |
+Reference: | |
+--------- | |
+ | |
+RFC 7748 Elliptic Curves for Security | |
+http://tools.ietf.org/html/rfc5746 | |
+ | |
+ | |
+Bugs: | |
+---- | |
+ | |
+$ openssl s_server -cert /etc/restund/creytiv.pem -accept 4433 -state -dtls1_2 | |
+cert: unable to parse certificate 950 bytes in memory at offset 0 | |
+ | |
+ | |
+Common testing commands: | |
+----------------------- | |
+ | |
+$ openssl s_client -connect 127.0.0.1:4433 -cipher NULL | |
+ | |
+ | |
+ | |
+TODO: | |
+---- | |
+ | |
+- Transport Layer Security (TLS) Renegotiation Indication Extension | |
+ https://tools.ietf.org/html/rfc5746 | |
+ | |
+ | |
diff --git a/src/tls/re/alert.c b/src/tls/re/alert.c | |
new file mode 100644 | |
index 0000000..4004eb1 | |
--- /dev/null | |
+++ b/src/tls/re/alert.c | |
@@ -0,0 +1,72 @@ | |
+/** | |
+ * @file alert.c TLS alerts | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+ | |
+ | |
+int tls_alert_encode(struct mbuf *mb, const struct tls_alert *alert) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (!mb || !alert) | |
+ return EINVAL; | |
+ | |
+ err |= mbuf_write_u8(mb, alert->level); | |
+ err |= mbuf_write_u8(mb, alert->descr); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_alert_decode(struct tls_alert *alert, struct mbuf *mb) | |
+{ | |
+ if (!alert || !mb) | |
+ return EINVAL; | |
+ if (mbuf_get_left(mb) < 2) | |
+ return ENODATA; | |
+ | |
+ alert->level = mbuf_read_u8(mb); | |
+ alert->descr = mbuf_read_u8(mb); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+const char *tls_alert_name(enum tls_alertdescr descr) | |
+{ | |
+ switch (descr) { | |
+ | |
+ 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_RECORD_OVERFLOW: return "record_overflow"; | |
+ case TLS_ALERT_DECOMPRESSION_FAILURE: return "decompression_failure"; | |
+ case TLS_ALERT_HANDSHAKE_FAILURE: return "handshake_failure"; | |
+ case TLS_ALERT_BAD_CERTIFICATE: return "bad_certificate"; | |
+ case TLS_ALERT_UNSUPPORTED_CERTIFICATE: | |
+ return "unsupported_certificate"; | |
+ case TLS_ALERT_CERTIFICATE_REVOKED: return "certificate_revoked"; | |
+ case TLS_ALERT_CERTIFICATE_EXPIRED: return "certificate_expired"; | |
+ case TLS_ALERT_CERTIFICATE_UNKNOWN: return "certificate_unknown"; | |
+ case TLS_ALERT_ILLEGAL_PARAMETER: 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_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"; | |
+ | |
+ default: return "???"; | |
+ } | |
+} | |
diff --git a/src/tls/re/cipher.c b/src/tls/re/cipher.c | |
new file mode 100644 | |
index 0000000..9b3f6b5 | |
--- /dev/null | |
+++ b/src/tls/re/cipher.c | |
@@ -0,0 +1,213 @@ | |
+/** | |
+ * @file cipher.c TLS ciphers | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+ | |
+ | |
+/* key exchange types */ | |
+#define K_NULL TLS_KE_K_NULL | |
+#define RSA TLS_KE_RSA | |
+ | |
+/* ciphers */ | |
+#define C_NULL TLS_C_NULL | |
+#define AES_128_CBC TLS_AES_128_CBC | |
+#define AES_256_CBC TLS_AES_256_CBC | |
+ | |
+/* MAC */ | |
+#define M_NULL TLS_MAC_NULL | |
+#define SHA TLS_MAC_HMAC_SHA1 | |
+#define SHA1 TLS_MAC_HMAC_SHA1 | |
+#define SHA256 TLS_MAC_HMAC_SHA256 | |
+ | |
+ | |
+static const struct tls_suite suites[] = { | |
+ | |
+/* | |
+ * Cipher Suite Key Cipher Mac | |
+ * Exchange | |
+ */ | |
+ | |
+{TLS_CIPHER_NULL_WITH_NULL_NULL, K_NULL, C_NULL, M_NULL}, | |
+ | |
+{TLS_CIPHER_RSA_WITH_NULL_SHA, RSA, C_NULL, SHA }, | |
+{TLS_CIPHER_RSA_WITH_NULL_SHA256, RSA, C_NULL, SHA256}, | |
+{TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA, RSA, AES_128_CBC, SHA }, | |
+{TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA, RSA, AES_256_CBC, SHA }, | |
+{TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA256, RSA, AES_128_CBC, SHA256}, | |
+{TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA256, RSA, AES_256_CBC, SHA256}, | |
+ | |
+}; | |
+ | |
+ | |
+unsigned tls_cipher_suite_count(void) | |
+{ | |
+ return ARRAY_SIZE(suites); | |
+} | |
+ | |
+ | |
+const struct tls_suite *tls_suite_lookup(enum tls_cipher_suite cipher_suite) | |
+{ | |
+ unsigned i; | |
+ | |
+ for (i=0; i<ARRAY_SIZE(suites); i++) { | |
+ | |
+ if (cipher_suite == suites[i].suite) | |
+ return &suites[i]; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+ | |
+enum tls_ciphertype tls_cipher_type(enum tls_cipher cipher) | |
+{ | |
+ switch (cipher) { | |
+ | |
+ case C_NULL: return TLS_CIPHERTYPE_STREAM; | |
+ case AES_128_CBC: return TLS_CIPHERTYPE_BLOCK; | |
+ case AES_256_CBC: return TLS_CIPHERTYPE_BLOCK; | |
+ | |
+ default: return (enum tls_ciphertype)-1; | |
+ } | |
+} | |
+ | |
+ | |
+unsigned tls_cipher_keymaterial(enum tls_cipher cipher) | |
+{ | |
+ switch (cipher) { | |
+ | |
+ case C_NULL: return 0; | |
+ case AES_128_CBC: return 16; | |
+ case AES_256_CBC: return 32; | |
+ default: return 0; | |
+ } | |
+} | |
+ | |
+ | |
+unsigned tls_cipher_ivsize(enum tls_cipher cipher) | |
+{ | |
+ switch (cipher) { | |
+ | |
+ case C_NULL: return 0; | |
+ case AES_128_CBC: return 16; | |
+ case AES_256_CBC: return 16; | |
+ default: return 0; | |
+ } | |
+} | |
+ | |
+ | |
+unsigned tls_cipher_blocksize(enum tls_cipher cipher) | |
+{ | |
+ switch (cipher) { | |
+ | |
+ case C_NULL: return 0; | |
+ case AES_128_CBC: return 16; | |
+ case AES_256_CBC: return 16; | |
+ default: return 0; | |
+ } | |
+} | |
+ | |
+ | |
+enum tls_bulkcipher_algorithm tls_cipher_algorithm(enum tls_cipher cipher) | |
+{ | |
+ switch (cipher) { | |
+ | |
+ case C_NULL: return TLS_BULKCIPHER_NULL; | |
+ case AES_128_CBC: return TLS_BULKCIPHER_AES; | |
+ case AES_256_CBC: return TLS_BULKCIPHER_AES; | |
+ default: return TLS_BULKCIPHER_NULL; | |
+ } | |
+} | |
+ | |
+ | |
+/* bytes */ | |
+unsigned tls_mac_length(enum tls_mac_algorithm mac) | |
+{ | |
+ switch (mac) { | |
+ | |
+ case TLS_MAC_NULL: return 0; | |
+ case TLS_MAC_HMAC_SHA1: return 20; | |
+ case TLS_MAC_HMAC_SHA256: return 32; | |
+ /*case TLS_MAC_HMAC_SHA384: return 48;*/ | |
+ /*case TLS_MAC_HMAC_SHA512: return 64;*/ | |
+ default: return 0; | |
+ } | |
+} | |
+ | |
+#define CIPH(a) {a, #a} | |
+ | |
+static const struct { | |
+ enum tls_cipher_suite cs; | |
+ const char *name; | |
+} table[] = { | |
+ | |
+ {TLS_CIPHER_RSA_WITH_NULL_SHA, | |
+ "TLS_RSA_WITH_NULL_SHA"}, | |
+ | |
+ {TLS_CIPHER_RSA_WITH_NULL_SHA256, | |
+ "TLS_RSA_WITH_NULL_SHA256"}, | |
+ | |
+ {TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA, | |
+ "TLS_RSA_WITH_AES_128_CBC_SHA"}, | |
+ | |
+ {TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA, | |
+ "TLS_RSA_WITH_AES_256_CBC_SHA"}, | |
+ | |
+ {TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA256, | |
+ "TLS_RSA_WITH_AES_128_CBC_SHA256"}, | |
+ | |
+ {TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA256, | |
+ "TLS_RSA_WITH_AES_256_CBC_SHA256"}, | |
+}; | |
+ | |
+ | |
+const char *tls_cipher_suite_name(enum tls_cipher_suite cs) | |
+{ | |
+ switch (cs) { | |
+ | |
+ case TLS_CIPHER_NULL_WITH_NULL_NULL: | |
+ return "TLS_NULL_WITH_NULL_NULL"; | |
+ | |
+ case TLS_CIPHER_RSA_WITH_NULL_SHA: | |
+ return "TLS_RSA_WITH_NULL_SHA"; | |
+ | |
+ case TLS_CIPHER_RSA_WITH_NULL_SHA256: | |
+ return "TLS_RSA_WITH_NULL_SHA256"; | |
+ | |
+ case TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA: | |
+ return "TLS_RSA_WITH_AES_128_CBC_SHA"; | |
+ | |
+ case TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA: | |
+ return "TLS_RSA_WITH_AES_256_CBC_SHA"; | |
+ | |
+ case TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA256: | |
+ return "TLS_RSA_WITH_AES_128_CBC_SHA256"; | |
+ | |
+ case TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA256: | |
+ return "TLS_RSA_WITH_AES_256_CBC_SHA256"; | |
+ | |
+ default: | |
+ return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+enum tls_cipher_suite tls_cipher_suite_resolve(const char *name) | |
+{ | |
+ size_t i; | |
+ | |
+ for (i=0; i<ARRAY_SIZE(table); i++) { | |
+ if (0 == str_casecmp(name, table[i].name)) | |
+ return table[i].cs; | |
+ } | |
+ | |
+ return TLS_CIPHER_NULL_WITH_NULL_NULL; | |
+} | |
diff --git a/src/tls/re/client.c b/src/tls/re/client.c | |
new file mode 100644 | |
index 0000000..43dcaee | |
--- /dev/null | |
+++ b/src/tls/re/client.c | |
@@ -0,0 +1,204 @@ | |
+/** | |
+ * @file client.c TLS client | |
+ * | |
+ * Copyright (C) 2010 - 2017 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sys.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+int tls_client_send_clienthello(struct tls_session *sess) | |
+{ | |
+ union handshake hand; | |
+ struct clienthello *hello = &hand.clienthello; | |
+ enum tls_compression_method compr_methods[1] = { | |
+ TLS_COMPRESSION_NULL | |
+ }; | |
+ uint16_t *datav = NULL; | |
+ size_t i; | |
+ int err; | |
+ | |
+ if (!sess) | |
+ return EINVAL; | |
+ | |
+ assert(TLS_CLIENT == sess->conn_end); | |
+ | |
+ datav = mem_alloc(sess->cipherc * sizeof(uint16_t), NULL); | |
+ if (!datav) | |
+ return ENOMEM; | |
+ | |
+ memset(&hand, 0, sizeof(hand)); | |
+ | |
+ hello->client_version = sess->version; | |
+ | |
+ mem_cpy(hello->random, sizeof(hello->random), | |
+ sess->sp_write.client_random, | |
+ sizeof(sess->sp_write.client_random)); | |
+ | |
+ /* Optional cookie for DTLS */ | |
+ if (sess->handshake.cookie_len) { | |
+ hello->cookie.data = sess->handshake.cookie; | |
+ hello->cookie.bytes = sess->handshake.cookie_len; | |
+ } | |
+ | |
+ for (i=0; i<sess->cipherc; i++) { | |
+ datav[i] = htons(sess->cipherv[i]); | |
+ } | |
+ | |
+ hello->cipher_suites.bytes = 2 * sess->cipherc; | |
+ hello->cipher_suites.data = datav; | |
+ | |
+ hello->compression_methods.bytes = 1; | |
+ hello->compression_methods.data = compr_methods; | |
+ | |
+ /* Local extensions */ | |
+ if (sess->tls && !list_isempty(&sess->tls->exts_local)) { | |
+ | |
+ err = tls_extensions_encode(&hello->extensions, | |
+ &sess->tls->exts_local); | |
+ if (err) { | |
+ DEBUG_WARNING("ext encode error (%m)\n", err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ err = tls_handshake_layer_send(sess, TLS_CLIENT_HELLO, &hand, | |
+ FLUSH, false); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ tls_vector_reset(&hello->extensions); | |
+ mem_deref(datav); | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_client_handle_server_hello(struct tls_session *sess, | |
+ const struct serverhello *hell) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (!sess || !hell) | |
+ return EINVAL; | |
+ | |
+ if (TLS_CLIENT != sess->conn_end) | |
+ return EPROTO; | |
+ | |
+ if (sess->state != TLS_STATE_CLIENT_HELLO_SENT) { | |
+ DEBUG_WARNING("client: recv_server_hello:" | |
+ " illegal state %d\n", sess->state); | |
+ return EPROTO; | |
+ } | |
+ if (sess->got_cert) | |
+ return EPROTO; | |
+ | |
+ /* save the Server-random */ | |
+ mem_cpy(sess->sp_read.server_random, | |
+ sizeof(sess->sp_read.server_random), | |
+ hell->random, sizeof(hell->random)); | |
+ mem_cpy(sess->sp_write.server_random, | |
+ sizeof(sess->sp_write.server_random), | |
+ hell->random, sizeof(hell->random)); | |
+ | |
+ /* the cipher-suite is now decided */ | |
+ if (!tls_cipher_suite_lookup(sess, hell->cipher_suite)) { | |
+ DEBUG_WARNING("the server gave us a cipher-suite that" | |
+ " we did not offer!\n"); | |
+ return EPROTO; | |
+ } | |
+ | |
+ sess->selected_cipher_suite = hell->cipher_suite; | |
+ | |
+ sess->suite = tls_suite_lookup(sess->selected_cipher_suite); | |
+ if (!sess->suite) { | |
+ DEBUG_WARNING("cipher suite not found (%s)\n", | |
+ tls_cipher_suite_name(hell->cipher_suite)); | |
+ return EPROTO; | |
+ } | |
+ | |
+ /* decode extensions from remote */ | |
+ if (hell->extensions.bytes) { | |
+ | |
+ err = tls_extensions_decode(&sess->exts_remote, | |
+ &hell->extensions); | |
+ if (err) { | |
+ DEBUG_WARNING("server_hello: extension error" | |
+ " (%m)\n", err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+/* send Client Key Exchange message */ | |
+int tls_client_send_clientkeyexchange(struct tls_session *sess) | |
+{ | |
+ struct tls_handshake hand; | |
+ int err; | |
+ | |
+ if (!sess) | |
+ return EINVAL; | |
+ | |
+ assert(TLS_CLIENT == sess->conn_end); | |
+ | |
+ memset(&hand, 0, sizeof(hand)); | |
+ | |
+ err = tls_vector_init(&hand.u.client_key_exchange.encr_pms, | |
+ sess->encr_pre_master_secret, | |
+ sess->encr_pre_master_secret_len); | |
+ if (err) | |
+ goto out; | |
+ | |
+ err = tls_handshake_layer_send(sess, TLS_CLIENT_KEY_EXCHANGE, | |
+ &hand.u, NO_FLUSH, false); | |
+ if (err) | |
+ goto out; | |
+ | |
+#if 1 | |
+ /* XXX: this can be moved elsewhere ? */ | |
+ | |
+ err = tls_master_secret_compute(sess->sp_read.master_secret, | |
+ sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret), | |
+ sess->sp_read.client_random, | |
+ sess->sp_read.server_random); | |
+ if (err) { | |
+ DEBUG_WARNING("master_secret_compute error (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ err = tls_master_secret_compute(sess->sp_write.master_secret, | |
+ sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret), | |
+ sess->sp_write.client_random, | |
+ sess->sp_write.server_random); | |
+ if (err) { | |
+ DEBUG_WARNING("master_secret_compute error (%m)\n", err); | |
+ goto out; | |
+ } | |
+#endif | |
+ | |
+ out: | |
+ tls_vector_reset(&hand.u.client_key_exchange.encr_pms); | |
+ return err; | |
+} | |
diff --git a/src/tls/re/crypt.c b/src/tls/re/crypt.c | |
new file mode 100644 | |
index 0000000..bfa8cd0 | |
--- /dev/null | |
+++ b/src/tls/re/crypt.c | |
@@ -0,0 +1,136 @@ | |
+/** | |
+ * @file crypt.c TLS encryption and decryption | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sys.h> | |
+#include <re_cert.h> | |
+#include <re_sha.h> | |
+#include <re_aes.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+#define IV_SIZE 16 | |
+ | |
+ | |
+int tls_crypt_encrypt(const struct key *write_key, | |
+ struct mbuf *mb_enc, struct mbuf *data) | |
+{ | |
+ struct aes *aes = NULL; | |
+ uint8_t iv[AES_BLOCK_SIZE]={1,2,3}; /* TODO: random */ | |
+ size_t padding; | |
+ int err; | |
+ | |
+ if (!write_key || !mb_enc || !data) | |
+ return EINVAL; | |
+ | |
+ err = aes_alloc(&aes, AES_MODE_CBC_ENCRYPT, | |
+ write_key->k, | |
+ write_key->len*8, | |
+ iv); | |
+ if (err) { | |
+ DEBUG_WARNING("encrypt: aes_alloc failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ padding = 16 - (data->end % 16) - 1; | |
+ | |
+ /* complete the input for block-ciphered struct */ | |
+ if (padding) | |
+ err = mbuf_fill(data, padding, padding); | |
+ err |= mbuf_write_u8(data, padding); | |
+ | |
+ err |= mbuf_write_mem(mb_enc, iv, sizeof(iv)); | |
+ if (err) | |
+ goto out; | |
+ | |
+ if (mbuf_get_space(mb_enc) < data->end) { | |
+ size_t sz = sizeof(iv) + data->end; | |
+ err = mbuf_resize(mb_enc, sz); | |
+ if (err) | |
+ goto out; | |
+ } | |
+ | |
+ err = aes_encr(aes, mbuf_buf(mb_enc), data->buf, data->end); | |
+ if (err) | |
+ goto out; | |
+ | |
+ mbuf_set_end(mb_enc, sizeof(iv) + data->end); | |
+ mbuf_set_pos(mb_enc, sizeof(iv) + data->end); | |
+ | |
+ out: | |
+ mem_deref(aes); | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_crypt_decrypt(const struct key *write_key, | |
+ struct mbuf *mb, size_t rec_length, | |
+ uint8_t *paddingp) | |
+{ | |
+ struct aes *aes = NULL; | |
+ uint8_t iv[IV_SIZE], padding; | |
+ size_t start, pos_pad, block_len; | |
+ int err; | |
+ | |
+ if (!write_key || !mb) | |
+ return EINVAL; | |
+ if (rec_length % AES_BLOCK_SIZE) { | |
+ DEBUG_WARNING("decrypt: length %zu not divisble by" | |
+ " AES_BLOCK_SIZE (16)\n", rec_length); | |
+ return EBADMSG; | |
+ } | |
+ if (rec_length < sizeof(iv)) | |
+ return ENODATA; | |
+ | |
+ start = mb->pos; | |
+ pos_pad = start + rec_length - 1; | |
+ block_len = rec_length - sizeof(iv); | |
+ | |
+ err = mbuf_read_mem(mb, iv, sizeof(iv)); | |
+ if (err) | |
+ goto out; | |
+ | |
+ if (mbuf_get_left(mb) < block_len) { | |
+ DEBUG_WARNING("decrypt: not enough data in mbuf for " | |
+ "block-cipher (%d bytes missing)\n", | |
+ (int)(block_len - mbuf_get_left(mb))); | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ err = aes_alloc(&aes, AES_MODE_CBC_DECRYPT, | |
+ write_key->k, | |
+ write_key->len*8, | |
+ iv); | |
+ if (err) | |
+ goto out; | |
+ | |
+ err = aes_decr(aes, mbuf_buf(mb), mbuf_buf(mb), block_len); | |
+ if (err) | |
+ goto out; | |
+ | |
+ /* the MBUF now contains a cleartext record */ | |
+ | |
+ padding = mb->buf[pos_pad]; | |
+ | |
+ *paddingp = padding; | |
+ | |
+ out: | |
+ mem_deref(aes); | |
+ return err; | |
+} | |
diff --git a/src/tls/re/extension.c b/src/tls/re/extension.c | |
new file mode 100644 | |
index 0000000..51b37e8 | |
--- /dev/null | |
+++ b/src/tls/re/extension.c | |
@@ -0,0 +1,334 @@ | |
+/** | |
+ * @file extension.c TLS extensions | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_list.h> | |
+#include <re_mbuf.h> | |
+#include <re_main.h> | |
+#include <re_sa.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_sys.h> | |
+#include <re_tcp.h> | |
+#include <re_cert.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+static void ext_destructor(void *data) | |
+{ | |
+ struct tls_extension *ext = data; | |
+ | |
+ switch (ext->type) { | |
+ | |
+ case TLS_EXT_SERVER_NAME: | |
+ mem_deref(ext->v.server_name.host); | |
+ break; | |
+ | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ list_unlink(&ext->le); | |
+} | |
+ | |
+ | |
+/* NOTE: the caller must fill in the data */ | |
+int tls_extension_add(struct tls_extension **extp, struct list *extl, | |
+ enum tls_extension_type type) | |
+{ | |
+ struct tls_extension *ext; | |
+ | |
+ ext = mem_zalloc(sizeof(*ext), ext_destructor); | |
+ if (!ext) | |
+ return ENOMEM; | |
+ | |
+ ext->type = type; | |
+ | |
+ if (extp) | |
+ *extp = ext; | |
+ | |
+ list_append(extl, &ext->le, ext); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_extensions_encode(struct tls_vector *vect, | |
+ const struct list *extl) | |
+{ | |
+ struct mbuf *mb = mbuf_alloc(128); | |
+ struct le *le; | |
+ size_t i; | |
+ int err = 0; | |
+ | |
+ if (!vect || !extl) | |
+ return EINVAL; | |
+ | |
+ assert(vect->data == NULL); | |
+ | |
+ for (le = list_head(extl); le; le = le->next) { | |
+ struct tls_extension *ext = le->data; | |
+ size_t pos; | |
+ size_t length; | |
+ | |
+ err = mbuf_write_u16(mb, htons(ext->type)); | |
+ | |
+ pos = mb->pos; | |
+ | |
+ mb->pos += 2; | |
+ | |
+ switch (ext->type) { | |
+ | |
+ case TLS_EXT_SERVER_NAME: { | |
+ size_t len = str_len(ext->v.server_name.host); | |
+ | |
+ err |= mbuf_write_u16(mb, htons(len + 3)); | |
+ err |= mbuf_write_u8(mb, ext->v.server_name.type); | |
+ err |= mbuf_write_u16(mb, htons(len)); | |
+ err |= mbuf_write_str(mb, ext->v.server_name.host); | |
+ } | |
+ break; | |
+ | |
+ case TLS_EXT_USE_SRTP: | |
+ for (i=0; i<ext->v.use_srtp.profilec; i++) { | |
+ uint16_t prof = ext->v.use_srtp.profilev[i]; | |
+ err |= mbuf_write_u16(mb, htons(prof)); | |
+ } | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("cannot encode ext %d\n", ext->type); | |
+ err = ENOTSUP; | |
+ goto out; | |
+ } | |
+ if (err) | |
+ goto out; | |
+ | |
+ length = mb->pos - pos - 2; | |
+ mb->pos = pos; | |
+ err = mbuf_write_u16(mb, htons(length)); | |
+ mb->pos = pos + 2 + length; | |
+ } | |
+ | |
+ vect->data = mem_ref(mb->buf); | |
+ vect->bytes = mb->end; | |
+ | |
+ out: | |
+ mem_deref(mb); | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_extensions_decode(struct list *extl, | |
+ const struct tls_vector *vect) | |
+{ | |
+ struct mbuf mb; | |
+ int err = 0; | |
+ | |
+ if (!extl || !vect) | |
+ return EINVAL; | |
+ | |
+ mb.buf = vect->data; | |
+ mb.pos = 0; | |
+ mb.end = mb.size = vect->bytes; | |
+ | |
+ while (mbuf_get_left(&mb) >= 4) { | |
+ | |
+ struct tls_extension *ext; | |
+ uint16_t type, length; | |
+ size_t i, n; | |
+ size_t stop; | |
+ | |
+ type = ntohs(mbuf_read_u16(&mb)); | |
+ length = ntohs(mbuf_read_u16(&mb)); | |
+ | |
+ if (mbuf_get_left(&mb) < length) { | |
+ DEBUG_WARNING("extension short length (%zu < %u bytes)" | |
+ "\n", | |
+ mbuf_get_left(&mb), length); | |
+ return EBADMSG; | |
+ } | |
+ | |
+#if 0 | |
+ re_printf("## ext: decode %u (%s) %u bytes\n", type, | |
+ tls_extension_name(type), length); | |
+#endif | |
+ | |
+ err = tls_extension_add(&ext, extl, type); | |
+ if (err) | |
+ return err; | |
+ | |
+ ext->length = length; | |
+ | |
+ stop = mb.pos + length; | |
+ | |
+ /* decode any known extensions */ | |
+ switch (type) { | |
+ | |
+ case TLS_EXT_SERVER_NAME: { | |
+ | |
+ uint16_t sni_list_length; | |
+ uint16_t sni_length; | |
+ | |
+ if (length == 0) | |
+ break; | |
+ | |
+ sni_list_length = ntohs(mbuf_read_u16(&mb)); | |
+ | |
+ if (mbuf_get_left(&mb) < sni_list_length) { | |
+ DEBUG_WARNING("sni: short length\n"); | |
+ return EBADMSG; | |
+ } | |
+ | |
+ ext->v.server_name.type = mbuf_read_u8(&mb); | |
+ sni_length = ntohs(mbuf_read_u16(&mb)); | |
+ | |
+ err = mbuf_strdup(&mb, &ext->v.server_name.host, | |
+ sni_length); | |
+ } | |
+ break; | |
+ | |
+ case TLS_EXT_USE_SRTP: | |
+ n = length / 2; | |
+ for (i=0; i<n; i++) { | |
+ uint16_t prof; | |
+ prof = ntohs(mbuf_read_u16(&mb)); | |
+ ext->v.use_srtp.profilev[i] = prof; | |
+ } | |
+ ext->v.use_srtp.profilec = i; | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_INFO("ext: dont know how to decode" | |
+ " extension %d (%s)\n", | |
+ type, | |
+ tls_extension_name(type)); | |
+ break; | |
+ } | |
+ | |
+ mb.pos = stop; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int tls_extension_print(struct re_printf *pf, | |
+ const struct tls_extension *ext) | |
+{ | |
+ size_t i; | |
+ int err; | |
+ | |
+ if (!ext) | |
+ return 0; | |
+ | |
+ err = re_hprintf(pf, "type=%d %-16s length=%zu", | |
+ ext->type, tls_extension_name(ext->type), | |
+ ext->length); | |
+ if (err) | |
+ return err; | |
+ | |
+ /* print all known extensions */ | |
+ switch (ext->type) { | |
+ | |
+ case TLS_EXT_SERVER_NAME: | |
+ err = re_hprintf(pf, " type=%u host='%s'", | |
+ ext->v.server_name.type, | |
+ ext->v.server_name.host); | |
+ break; | |
+ | |
+ case TLS_EXT_USE_SRTP: | |
+ err = re_hprintf(pf, " profiles=[ "); | |
+ for (i=0; i<ext->v.use_srtp.profilec; i++) { | |
+ err |= re_hprintf(pf, "0x%04x ", | |
+ ext->v.use_srtp.profilev[i]); | |
+ } | |
+ err = re_hprintf(pf, "]"); | |
+ break; | |
+ | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+struct tls_extension *tls_extension_find(const struct list *extl, | |
+ enum tls_extension_type type) | |
+{ | |
+ struct le *le; | |
+ | |
+ for (le = list_head(extl); le; le = le->next) { | |
+ struct tls_extension *ext = le->data; | |
+ | |
+ if (type == ext->type) | |
+ return ext; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+ | |
+int tls_extensions_print(struct re_printf *pf, | |
+ const struct list *extl) | |
+{ | |
+ struct le *le; | |
+ uint32_t n = list_count(extl); | |
+ int err; | |
+ | |
+ err = re_hprintf(pf, "extensions: (%u)\n", n); | |
+ | |
+ if (!n) | |
+ return err; | |
+ | |
+ for (le = list_head(extl); le; le = le->next) { | |
+ const struct tls_extension *ext = le->data; | |
+ | |
+ err |= re_hprintf(pf, ".. %H\n", tls_extension_print, ext); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+const char *tls_extension_name(enum tls_extension_type ext) | |
+{ | |
+ switch (ext) { | |
+ | |
+ case TLS_EXT_SERVER_NAME: return "server_name"; | |
+ case TLS_EXT_USE_SRTP: return "use_srtp"; | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+struct tls_extension *tls_extensions_apply(const struct list *extl, | |
+ tls_extension_h *exth, void *arg) | |
+{ | |
+ struct le *le = list_head(extl); | |
+ | |
+ while (le) { | |
+ struct tls_extension *ext = le->data; | |
+ | |
+ le = le->next; | |
+ | |
+ if (exth && exth(ext, arg)) | |
+ return ext; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
diff --git a/src/tls/re/finish.c b/src/tls/re/finish.c | |
new file mode 100644 | |
index 0000000..1043b3e | |
--- /dev/null | |
+++ b/src/tls/re/finish.c | |
@@ -0,0 +1,50 @@ | |
+/** | |
+ * @file finish.c TLS finished routines | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_sys.h> | |
+#include <re_cert.h> | |
+#include <re_sha.h> | |
+#include <re_aes.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+ | |
+ | |
+#define LABEL_LEN 15 | |
+ | |
+ | |
+int tls_finish_calc(uint8_t verify_data[TLS_VERIFY_DATA_SIZE], | |
+ const uint8_t master_secret[TLS_MASTER_SECRET_LEN], | |
+ const uint8_t seed[32], | |
+ enum tls_connection_end sender) | |
+{ | |
+ static const uint8_t label_client[LABEL_LEN] = "client finished"; | |
+ static const uint8_t label_server[LABEL_LEN] = "server finished"; | |
+ const uint8_t *label; | |
+ int err; | |
+ | |
+ if (sender == TLS_CLIENT) | |
+ label = label_client; | |
+ else if (sender == TLS_SERVER) | |
+ label = label_server; | |
+ else | |
+ return EINVAL; | |
+ | |
+ err = tls_prf_sha256(verify_data, TLS_VERIFY_DATA_SIZE, | |
+ master_secret, TLS_MASTER_SECRET_LEN, | |
+ label, LABEL_LEN, | |
+ seed, 32); | |
+ if (err) { | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
diff --git a/src/tls/re/handshake.c b/src/tls/re/handshake.c | |
new file mode 100644 | |
index 0000000..c5082a1 | |
--- /dev/null | |
+++ b/src/tls/re/handshake.c | |
@@ -0,0 +1,603 @@ | |
+/** | |
+ * @file handshake.c TLS handshake | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_list.h> | |
+#include <re_mbuf.h> | |
+#include <re_net.h> | |
+#include <re_cert.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+#define HANDSHAKE_LENGTH_MAX 65536u | |
+ | |
+ | |
+static void handshake_destructor(void *data) | |
+{ | |
+ struct tls_handshake *hand = data; | |
+ | |
+ switch (hand->msg_type) { | |
+ | |
+ case TLS_CLIENT_HELLO: { | |
+ struct clienthello *hello; | |
+ | |
+ hello = &hand->u.clienthello; | |
+ | |
+ mem_deref(hello->session_id.data); | |
+ mem_deref(hello->cookie.data); | |
+ mem_deref(hello->cipher_suites.data); | |
+ mem_deref(hello->compression_methods.data); | |
+ tls_vector_reset(&hello->extensions); | |
+ } | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO: { | |
+ struct serverhello *hello; | |
+ | |
+ hello = &hand->u.serverhello; | |
+ | |
+ mem_deref(hello->session_id.data); | |
+ tls_vector_reset(&hello->extensions); | |
+ } | |
+ break; | |
+ | |
+ case TLS_HELLO_VERIFY_REQUEST: | |
+ tls_vector_reset(&hand->u.hello_verify_req.cookie); | |
+ break; | |
+ | |
+ case TLS_CERTIFICATE: { | |
+ struct certificate *cert; | |
+ size_t i; | |
+ | |
+ cert = &hand->u.certificate; | |
+ for (i=0; i<cert->count; i++) | |
+ tls_vector_reset(&cert->certlist[i]); | |
+ cert->count = 0; | |
+ } | |
+ | |
+ case TLS_CLIENT_KEY_EXCHANGE: { | |
+ struct client_key_exchange *exch; | |
+ | |
+ exch = &hand->u.client_key_exchange; | |
+ | |
+ tls_vector_reset(&exch->encr_pms); | |
+ } | |
+ break; | |
+ | |
+ default: | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+static size_t tls_handshake_hdrsize(enum tls_version ver) | |
+{ | |
+ if (tls_version_is_dtls(ver)) | |
+ return 12; | |
+ else | |
+ return 4; | |
+} | |
+ | |
+ | |
+static int clienthello_encode(struct mbuf *mb, const struct clienthello *hello) | |
+{ | |
+ int err = 0; | |
+ | |
+ err |= mbuf_write_u16(mb, htons(hello->client_version)); | |
+ err |= mbuf_write_mem(mb, hello->random, sizeof(hello->random)); | |
+ err |= tls_vector_encode(mb, &hello->session_id, 1); | |
+ if (hello->client_version == DTLS1_2_VERSION) { | |
+ err |= tls_vector_encode(mb, &hello->cookie, 1); | |
+ } | |
+ err |= tls_vector_encode(mb, &hello->cipher_suites, 2); | |
+ err |= tls_vector_encode(mb, &hello->compression_methods, 1); | |
+ | |
+ if (hello->extensions.bytes) { | |
+ err |= tls_vector_encode(mb, &hello->extensions, 2); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int clienthello_decode(struct clienthello *hello, size_t stop, | |
+ struct mbuf *mb) | |
+{ | |
+ size_t extensions_bytes; | |
+ int err; | |
+ | |
+ if (mbuf_get_left(mb) < 2) | |
+ return ENODATA; | |
+ | |
+ hello->client_version = ntohs(mbuf_read_u16(mb)); | |
+ err = mbuf_read_mem(mb, hello->random, sizeof(hello->random)); | |
+ if (err) | |
+ return err; | |
+ | |
+ err = tls_vector_decode(&hello->session_id, 1, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (hello->client_version == DTLS1_2_VERSION) { | |
+ err = tls_vector_decode(&hello->cookie, 1, mb); | |
+ if (err) | |
+ return err; | |
+ } | |
+ | |
+ err = tls_vector_decode(&hello->cipher_suites, 2, mb); | |
+ if (err) | |
+ return err; | |
+ err = tls_vector_decode(&hello->compression_methods, 1, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ extensions_bytes = stop - mb->pos; | |
+ | |
+ if (extensions_bytes) { | |
+ | |
+ err = tls_vector_decode(&hello->extensions, 2, mb); | |
+ if (err) | |
+ return err; | |
+ } | |
+ else { | |
+ hello->extensions.data = NULL; | |
+ hello->extensions.bytes = 0; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int serverhello_encode(struct mbuf *mb, const struct serverhello *hello) | |
+{ | |
+ int err = 0; | |
+ | |
+ err |= mbuf_write_u16(mb, htons(hello->server_version)); | |
+ err |= mbuf_write_mem(mb, hello->random, sizeof(hello->random)); | |
+ err |= tls_vector_encode(mb, &hello->session_id, 1); | |
+ err |= mbuf_write_u16(mb, htons(hello->cipher_suite)); | |
+ err |= mbuf_write_u8(mb, hello->compression_method); | |
+ | |
+ if (hello->extensions.bytes) { | |
+ err |= tls_vector_encode(mb, &hello->extensions, 2); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int serverhello_decode(struct serverhello *hello, size_t stop, | |
+ struct mbuf *mb) | |
+{ | |
+ int err; | |
+ | |
+ hello->server_version = ntohs(mbuf_read_u16(mb)); | |
+ err = mbuf_read_mem(mb, hello->random, sizeof(hello->random)); | |
+ if (err) | |
+ return err; | |
+ | |
+ err = tls_vector_decode(&hello->session_id, 1, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ hello->cipher_suite = ntohs(mbuf_read_u16(mb)); | |
+ | |
+ hello->compression_method = mbuf_read_u8(mb); | |
+ | |
+ if (mb->pos < stop) { | |
+ | |
+ err = tls_vector_decode(&hello->extensions, 2, mb); | |
+ if (err) | |
+ return err; | |
+ } | |
+ else { | |
+ hello->extensions.data = NULL; | |
+ hello->extensions.bytes = 0; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int hello_verify_request_decode(struct hello_verify_req *ver, | |
+ struct mbuf *mb) | |
+{ | |
+ int err; | |
+ | |
+ if (mbuf_get_left(mb) < 2) | |
+ return EBADMSG; | |
+ | |
+ ver->server_version = ntohs(mbuf_read_u16(mb)); | |
+ | |
+ err = tls_vector_decode(&ver->cookie, 1, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int certificate_encode(struct mbuf *mb, const struct certificate *cert) | |
+{ | |
+ struct tls_vector outer; | |
+ struct mbuf *inner; | |
+ size_t i; | |
+ int err; | |
+ | |
+ inner = mbuf_alloc(1024); | |
+ | |
+ for (i=0; i<cert->count; i++) { | |
+ | |
+ const struct tls_vector *v = &cert->certlist[i]; | |
+ | |
+ err = tls_vector_encode(inner, v, 3); | |
+ if (err) { | |
+ DEBUG_WARNING("cert: inner vect (%m)\n", err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ outer.data = inner->buf; | |
+ outer.bytes = inner->end; | |
+ | |
+ err = tls_vector_encode(mb, &outer, 3); | |
+ if (err) { | |
+ DEBUG_WARNING("cert: outer %m\n", err); | |
+ } | |
+ | |
+ out: | |
+ mem_deref(inner); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int certificate_decode(struct certificate *cert, struct mbuf *mb) | |
+{ | |
+ struct tls_vector vect; | |
+ size_t i = 0; | |
+ size_t stop; | |
+ int err; | |
+ | |
+ err = tls_vector_decode_hdr(&vect, 3, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (mbuf_get_left(mb) < vect.bytes) { | |
+ return ENODATA; | |
+ } | |
+ | |
+ stop = mb->pos + vect.bytes; | |
+ | |
+ while (mb->pos < stop) { | |
+ | |
+ if (i >= ARRAY_SIZE(cert->certlist)) | |
+ return ENOSPC; | |
+ | |
+ err = tls_vector_decode(&cert->certlist[i], 3, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ ++i; | |
+ } | |
+ | |
+ cert->count = i; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int client_key_exchange_encode(struct mbuf *mb, | |
+ const struct client_key_exchange *exch) | |
+{ | |
+ if (!exch) | |
+ return EINVAL; | |
+ | |
+ return tls_vector_encode(mb, &exch->encr_pms, 2); | |
+} | |
+ | |
+ | |
+int tls_handshake_encode(struct mbuf *mb, enum tls_version ver, | |
+ enum tls_handshake_type msg_type, | |
+ uint16_t message_seq, | |
+ uint24_t fragment_offset, | |
+ const union handshake *hand) | |
+{ | |
+ size_t start; | |
+ size_t length; | |
+ int err = 0; | |
+ | |
+ start = mb->pos; | |
+ | |
+ mb->pos = start + tls_handshake_hdrsize(ver); | |
+ | |
+ switch (msg_type) { | |
+ | |
+ case TLS_CLIENT_HELLO: | |
+ err = clienthello_encode(mb, &hand->clienthello); | |
+ if (err) { | |
+ DEBUG_WARNING("clienthello_encode ERROR (%m)\n", err); | |
+ return err; | |
+ } | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO: | |
+ err = serverhello_encode(mb, &hand->serverhello); | |
+ if (err) { | |
+ DEBUG_WARNING("serverhello_encode ERROR (%m)\n", err); | |
+ return err; | |
+ } | |
+ break; | |
+ | |
+ case TLS_CLIENT_KEY_EXCHANGE: | |
+ err = client_key_exchange_encode(mb, | |
+ &hand->client_key_exchange); | |
+ break; | |
+ | |
+ case TLS_FINISHED: | |
+ err = mbuf_write_mem(mb, hand->finished.verify_data, | |
+ sizeof(hand->finished.verify_data)); | |
+ break; | |
+ | |
+ case TLS_CERTIFICATE: | |
+ err = certificate_encode(mb, &hand->certificate); | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO_DONE: | |
+ /* no payload */ | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("handshake: don't know how to encode %d (%s)\n", | |
+ msg_type, tls_handshake_name(msg_type)); | |
+ return EPROTO; | |
+ break; | |
+ } | |
+ if (err) | |
+ return err; | |
+ | |
+ length = mb->pos - start - tls_handshake_hdrsize(ver); | |
+ | |
+ mb->pos = start; | |
+ | |
+ err |= mbuf_write_u8(mb, msg_type); | |
+ err |= mbuf_write_u24_hton(mb, (uint32_t)length); | |
+ if (ver == DTLS1_2_VERSION) { | |
+ err |= mbuf_write_u16(mb, htons(message_seq)); | |
+ err |= mbuf_write_u24_hton(mb, fragment_offset); | |
+ err |= mbuf_write_u24_hton(mb, (uint32_t)length); | |
+ } | |
+ if (err) | |
+ return err; | |
+ | |
+ mb->pos = mb->end; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_handshake_decode(struct tls_handshake **handp, | |
+ enum tls_version ver, struct mbuf *mb) | |
+{ | |
+ struct tls_handshake *hand = NULL; | |
+ size_t stop; | |
+ int err = 0; | |
+ | |
+ if (!handp || !mb) | |
+ return EINVAL; | |
+ | |
+ hand = mem_zalloc(sizeof(*hand), handshake_destructor); | |
+ if (!hand) | |
+ return ENOMEM; | |
+ | |
+ if (mbuf_get_left(mb) < 4) { | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ hand->msg_type = mbuf_read_u8(mb); | |
+ hand->length = mbuf_read_u24_ntoh(mb); | |
+ | |
+ if (hand->length > HANDSHAKE_LENGTH_MAX) { | |
+ DEBUG_WARNING("handshake_decode: very long length" | |
+ " (%llu > %zu bytes)\n", | |
+ hand->length, | |
+ (size_t)HANDSHAKE_LENGTH_MAX); | |
+ } | |
+ | |
+ if (ver == DTLS1_2_VERSION) { | |
+ if (mbuf_get_left(mb) < 8) { | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ hand->message_seq = ntohs(mbuf_read_u16(mb)); | |
+ hand->fragment_offset = mbuf_read_u24_ntoh(mb); | |
+ hand->fragment_length = mbuf_read_u24_ntoh(mb); | |
+ } | |
+ | |
+ if (mbuf_get_left(mb) < hand->length) { | |
+ DEBUG_INFO("handshake: incomplete packet (missing %d bytes)\n", | |
+ (int)(hand->length - mbuf_get_left(mb))); | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ stop = mb->pos + hand->length; | |
+ | |
+ switch (hand->msg_type) { | |
+ | |
+ case TLS_CLIENT_HELLO: | |
+ err = clienthello_decode(&hand->u.clienthello, stop, mb); | |
+ if (err) | |
+ goto out; | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO: | |
+ err = serverhello_decode(&hand->u.serverhello, stop, mb); | |
+ if (err) | |
+ goto out; | |
+ break; | |
+ | |
+ case TLS_HELLO_VERIFY_REQUEST: | |
+ err = hello_verify_request_decode(&hand->u.hello_verify_req, | |
+ mb); | |
+ if (err) | |
+ goto out; | |
+ break; | |
+ | |
+ case TLS_CERTIFICATE: | |
+ err = certificate_decode(&hand->u.certificate, mb); | |
+ if (err) | |
+ goto out; | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO_DONE: | |
+ /* empty struct */ | |
+ break; | |
+ | |
+ case TLS_CLIENT_KEY_EXCHANGE: | |
+ err = tls_vector_decode(&hand->u.client_key_exchange.encr_pms, | |
+ 2, mb); | |
+ break; | |
+ | |
+ case TLS_FINISHED: | |
+ err = mbuf_read_mem(mb, hand->u.finished.verify_data, | |
+ sizeof(hand->u.finished.verify_data)); | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("handshake: don't know how to decode %d (%s)" | |
+ " (%u bytes)\n", | |
+ hand->msg_type, | |
+ tls_handshake_name(hand->msg_type), | |
+ hand->length); | |
+ err = EPROTO; | |
+ break; | |
+ } | |
+ if (err) | |
+ goto out; | |
+ | |
+ if (mb->pos != stop) { | |
+ re_printf("handshake(%s): decode: skipped %d bytes\n", | |
+ tls_handshake_name(hand->msg_type), | |
+ (int)(stop - mb->pos)); | |
+ mbuf_set_pos(mb, stop); | |
+ } | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(hand); | |
+ else | |
+ *handp = hand; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+const char *tls_handshake_name(enum tls_handshake_type typ) | |
+{ | |
+ switch (typ) { | |
+ | |
+ case TLS_HELLO_REQUEST: return "HelloRequest"; | |
+ case TLS_CLIENT_HELLO: return "ClientHello"; | |
+ case TLS_SERVER_HELLO: return "ServerHello"; | |
+ case TLS_HELLO_VERIFY_REQUEST: return "HelloVerifyRequest"; | |
+ case TLS_CERTIFICATE: return "Certificate"; | |
+ case TLS_SERVER_KEY_EXCHANGE: return "ServerKeyExchange"; | |
+ case TLS_CERTIFICATE_REQUEST: return "CertificateRequest"; | |
+ case TLS_SERVER_HELLO_DONE: return "ServerHelloDone"; | |
+ case TLS_CERTIFICATE_VERIFY: return "CertificateVerify"; | |
+ case TLS_CLIENT_KEY_EXCHANGE: return "ClientKeyExchange"; | |
+ case TLS_FINISHED: return "Finished"; | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+void tls_handshake_dump(const struct tls_handshake *hand, | |
+ enum tls_version ver) | |
+{ | |
+ if (!hand) | |
+ return; | |
+ | |
+ re_printf("msg_type: %s (%d)\n", | |
+ tls_handshake_name(hand->msg_type), hand->msg_type); | |
+ re_printf("length: %u\n", hand->length); | |
+ | |
+ if (tls_version_is_dtls(ver)) { | |
+ re_printf("message_seq: %u\n", | |
+ hand->message_seq); | |
+ re_printf("fragment_offset: %u\n", | |
+ hand->fragment_offset); | |
+ re_printf("fragment_length: %u\n", | |
+ hand->fragment_length); | |
+ } | |
+ re_printf("\n"); | |
+ | |
+ switch (hand->msg_type) { | |
+ | |
+ case TLS_CLIENT_HELLO: { | |
+ const struct clienthello *hello = &hand->u.clienthello; | |
+ | |
+ re_printf("ClientHello payload:\n"); | |
+ re_printf("proto_version: %s\n", | |
+ tls_version_name(hello->client_version)); | |
+ re_printf("random[%zu]: %w\n", | |
+ sizeof(hello->random), | |
+ hello->random, | |
+ sizeof(hello->random)); | |
+ re_printf("session_id: %u bytes\n", | |
+ hello->session_id.bytes); | |
+ if (tls_version_is_dtls(ver)) { | |
+ re_printf("cookie: %u bytes\n", | |
+ hello->cookie.bytes); | |
+ } | |
+ re_printf("cipher_suites: %u suites\n", | |
+ hello->cipher_suites.bytes / 2); | |
+ re_printf("compression_methods: %u methods\n", | |
+ hello->compression_methods.bytes); | |
+ re_printf("extensions: %u bytes\n", | |
+ hello->extensions.bytes); | |
+ } | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO: { | |
+ const struct serverhello *hello = &hand->u.serverhello; | |
+ | |
+ re_printf("ServerHello payload:\n"); | |
+ re_printf("server_version: %s\n", | |
+ tls_version_name(hello->server_version)); | |
+ re_printf("random[%zu]: %w\n", | |
+ sizeof(hello->random), | |
+ hello->random, | |
+ sizeof(hello->random)); | |
+ re_printf("session_id: %u bytes\n", | |
+ hello->session_id.bytes); | |
+ re_printf("cipher_suite: 0x%02x,0x%02x (%s)\n", | |
+ hello->cipher_suite>>8, | |
+ hello->cipher_suite&0xff, | |
+ tls_cipher_suite_name(hello->cipher_suite)); | |
+ re_printf("compression_method: %d\n", | |
+ hello->compression_method); | |
+ re_printf("extensions: %u bytes\n", | |
+ hello->extensions.bytes); | |
+ } | |
+ break; | |
+ | |
+ default: | |
+ break; | |
+ } | |
+} | |
diff --git a/src/tls/re/hmac.c b/src/tls/re/hmac.c | |
new file mode 100644 | |
index 0000000..ba4ccde | |
--- /dev/null | |
+++ b/src/tls/re/hmac.c | |
@@ -0,0 +1,36 @@ | |
+/** | |
+ * @file hmac.c TLS HMAC routines | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_list.h> | |
+#include <re_hmac.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+int hmac_sha256(const uint8_t *key, | |
+ size_t key_len, | |
+ const uint8_t *data, | |
+ size_t data_len, | |
+ uint8_t* out, | |
+ size_t out_size) | |
+{ | |
+ struct hmac *hmac; | |
+ int err; | |
+ | |
+ err = hmac_create(&hmac, HMAC_HASH_SHA256, key, key_len); | |
+ if (err) | |
+ return err; | |
+ | |
+ err = hmac_digest(hmac, out, out_size, data, data_len); | |
+ | |
+ mem_deref(hmac); | |
+ | |
+ return err; | |
+} | |
diff --git a/src/tls/re/key.c b/src/tls/re/key.c | |
new file mode 100644 | |
index 0000000..e33d35a | |
--- /dev/null | |
+++ b/src/tls/re/key.c | |
@@ -0,0 +1,93 @@ | |
+/** | |
+ * @file key.c TLS key routines | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_list.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+/* 6.3. Key Calculation */ | |
+ | |
+ | |
+#define KEY_INIT(key, p, l) \ | |
+ if ((l) > sizeof((key)->k)) { \ | |
+ re_printf("%s: key too big (%zu > %zu)\n", __func__, \ | |
+ (l), sizeof((key)->k)); \ | |
+ return EOVERFLOW; \ | |
+ } \ | |
+ memcpy((key)->k, (p), (l)); \ | |
+ (key)->len = (l); \ | |
+ (p) += (l); | |
+ | |
+ | |
+/* | |
+ key_block = PRF(SecurityParameters.master_secret, | |
+ "key expansion", | |
+ SecurityParameters.server_random + | |
+ SecurityParameters.client_random); | |
+ */ | |
+int tls_keys_generate(struct tls_key_block *keys, | |
+ const struct tls_secparam *sp) | |
+{ | |
+ size_t wanted_len; | |
+ uint8_t *buf=0, *p=0; | |
+ static const uint8_t label[13] = "key expansion"; | |
+ uint8_t seed[TLS_CLIENT_RANDOM_LEN + TLS_SERVER_RANDOM_LEN]; | |
+ int err = 0; | |
+ | |
+ if (!keys || !sp) | |
+ return EINVAL; | |
+ | |
+ if (keys->client_write_key.len) { | |
+ DEBUG_WARNING("keys already generated\n"); | |
+ return EALREADY; | |
+ } | |
+ | |
+#if 0 | |
+ /* NOTE: debug only */ | |
+ if (!secure_is_set(sp->master_secret, sizeof(sp->master_secret))) { | |
+ DEBUG_WARNING("tls_keys_generate: master secret not set\n"); | |
+ return EPROTO; | |
+ } | |
+#endif | |
+ | |
+ wanted_len = sp->mac_key_length * 2 + sp->enc_key_length * 2; | |
+ | |
+ buf = mem_alloc(wanted_len, NULL); | |
+ if (!buf) | |
+ return ENOMEM; | |
+ | |
+ p = buf; | |
+ | |
+ mem_cpy(&seed[ 0], 32, sp->server_random, sizeof(sp->server_random)); | |
+ mem_cpy(&seed[32], 32, sp->client_random, sizeof(sp->client_random)); | |
+ | |
+ err = tls_prf_sha256(p, wanted_len, | |
+ sp->master_secret, sizeof(sp->master_secret), | |
+ label, sizeof(label), | |
+ seed, sizeof(seed)); | |
+ if (err) | |
+ goto out; | |
+ | |
+ KEY_INIT(&keys->client_write_MAC_key, p, sp->mac_key_length); | |
+ KEY_INIT(&keys->server_write_MAC_key, p, sp->mac_key_length); | |
+ KEY_INIT(&keys->client_write_key, p, sp->enc_key_length); | |
+ KEY_INIT(&keys->server_write_key, p, sp->enc_key_length); | |
+ | |
+ out: | |
+ mem_deref(buf); | |
+ return err; | |
+} | |
diff --git a/src/tls/re/mac.c b/src/tls/re/mac.c | |
new file mode 100644 | |
index 0000000..f31a0f5 | |
--- /dev/null | |
+++ b/src/tls/re/mac.c | |
@@ -0,0 +1,89 @@ | |
+/** | |
+ * @file mac.c TLS MAC routines | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_hmac.h> | |
+#include <re_sys.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+ | |
+ | |
+#define DEBUG_MODULE "dtls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+/* | |
+ * Generate MAC for the record layer | |
+ * | |
+ * MAC(MAC_write_key, seq_num + | |
+ * TLSCompressed.type + | |
+ * TLSCompressed.version + | |
+ * TLSCompressed.length + | |
+ * TLSCompressed.fragment); | |
+ */ | |
+int tls_mac_generate(uint8_t *mac, size_t mac_sz, | |
+ const struct key *mac_write_key, | |
+ uint64_t seq_num, | |
+ enum tls_content_type content_type, | |
+ enum tls_version proto_ver, | |
+ uint16_t fragment_length, | |
+ const uint8_t *fragment) | |
+{ | |
+ struct mbuf *mb; | |
+ struct hmac *hmac = NULL; | |
+ enum hmac_hash hash; | |
+ int err = 0; | |
+ | |
+ if (mac_sz == 20) | |
+ hash = HMAC_HASH_SHA1; | |
+ else if (mac_sz == 32) | |
+ hash = HMAC_HASH_SHA256; | |
+ else | |
+ return EINVAL; | |
+ | |
+ if (!mac || !mac_sz) | |
+ return EINVAL; | |
+ if (!mac_write_key || !mac_write_key->len) { | |
+ DEBUG_WARNING("mac: write_key not set\n"); | |
+ return EINVAL; | |
+ } | |
+ if (!fragment_length || !fragment) | |
+ return EINVAL; | |
+ | |
+ mb = mbuf_alloc(256); | |
+ if (!mb) | |
+ return ENOMEM; | |
+ | |
+ err |= mbuf_write_u64(mb, sys_htonll(seq_num)); | |
+ err |= mbuf_write_u8(mb, content_type); | |
+ err |= mbuf_write_u16(mb, htons(proto_ver)); | |
+ err |= mbuf_write_u16(mb, htons(fragment_length)); | |
+ | |
+ assert(mb->end == 13); | |
+ | |
+ err |= mbuf_write_mem(mb, fragment, fragment_length); | |
+ if (err) | |
+ goto out; | |
+ | |
+ err = hmac_create(&hmac, hash, mac_write_key->k, mac_write_key->len); | |
+ if (err) | |
+ goto out; | |
+ err = hmac_digest(hmac, mac, mac_sz, mb->buf, mb->end); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ mem_deref(hmac); | |
+ mem_deref(mb); | |
+ return err; | |
+} | |
diff --git a/src/tls/re/master.c b/src/tls/re/master.c | |
new file mode 100644 | |
index 0000000..8b0071e | |
--- /dev/null | |
+++ b/src/tls/re/master.c | |
@@ -0,0 +1,63 @@ | |
+/** | |
+ * @file master.c TLS master secret | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+/* | |
+ * RFC 5246 Section 8.1. Computing the Master Secret | |
+ * | |
+ * <pre> | |
+ master_secret = PRF(pre_master_secret, "master secret", | |
+ ClientHello.random + ServerHello.random) | |
+ [0..47]; | |
+ * <pre> | |
+ */ | |
+ | |
+ | |
+int tls_master_secret_compute(uint8_t master_secret[48], | |
+ const uint8_t *pre_master_secret, | |
+ size_t pre_master_secret_len, | |
+ const uint8_t client_random[32], | |
+ const uint8_t server_random[32]) | |
+{ | |
+ static const uint8_t label[13] = "master secret"; | |
+ uint8_t seed[TLS_CLIENT_RANDOM_LEN + TLS_SERVER_RANDOM_LEN]; | |
+ | |
+ if (!master_secret || !pre_master_secret || !pre_master_secret_len || | |
+ !client_random || !server_random) | |
+ return EINVAL; | |
+ | |
+#if 0 | |
+ /* NOTE: debug only */ | |
+ if (!secure_is_set(client_random, TLS_CLIENT_RANDOM_LEN)) { | |
+ re_printf("master_secret_compute: client random" | |
+ " is not set\n"); | |
+ return EPROTO; | |
+ } | |
+ if (!secure_is_set(server_random, TLS_SERVER_RANDOM_LEN)) { | |
+ re_printf("master_secret_compute: server random" | |
+ " is not set\n"); | |
+ return EPROTO; | |
+ } | |
+#endif | |
+ | |
+ mem_cpy(&seed[ 0], 32, client_random, TLS_CLIENT_RANDOM_LEN); | |
+ mem_cpy(&seed[32], 32, server_random, TLS_SERVER_RANDOM_LEN); | |
+ | |
+ return tls_prf_sha256(master_secret, TLS_MASTER_SECRET_LEN, | |
+ pre_master_secret, pre_master_secret_len, | |
+ label, sizeof(label), | |
+ seed, sizeof(seed)); | |
+} | |
diff --git a/src/tls/re/mbuf.c b/src/tls/re/mbuf.c | |
new file mode 100644 | |
index 0000000..51433af | |
--- /dev/null | |
+++ b/src/tls/re/mbuf.c | |
@@ -0,0 +1,59 @@ | |
+/** | |
+ * @file mbuf.c TLS mbuf routines | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+int mbuf_write_u24_hton(struct mbuf *mb, uint24_t u) | |
+{ | |
+ int err = 0; | |
+ | |
+ err |= mbuf_write_u8(mb, u >> 16); | |
+ err |= mbuf_write_u8(mb, u >> 8); | |
+ err |= mbuf_write_u8(mb, u >> 0); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+uint24_t mbuf_read_u24_ntoh(struct mbuf *mb) | |
+{ | |
+ uint24_t u; | |
+ | |
+ u = (uint24_t)mbuf_read_u8(mb) << 16; | |
+ u |= (uint24_t)mbuf_read_u8(mb) << 8; | |
+ u |= (uint24_t)mbuf_read_u8(mb) << 0; | |
+ | |
+ return u; | |
+} | |
+ | |
+ | |
+int mbuf_write_u48_hton(struct mbuf *mb, uint48_t u) | |
+{ | |
+ int err = 0; | |
+ | |
+ err |= mbuf_write_u16(mb, htons(u >> 32)); | |
+ err |= mbuf_write_u32(mb, htonl((uint32_t)u & 0xffffffff)); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+uint48_t mbuf_read_u48_ntoh(struct mbuf *mb) | |
+{ | |
+ uint48_t v; | |
+ | |
+ v = (uint64_t)ntohs(mbuf_read_u16(mb)) << 32; | |
+ v |= ntohl(mbuf_read_u32(mb)); | |
+ | |
+ return v; | |
+} | |
diff --git a/src/tls/re/prf.c b/src/tls/re/prf.c | |
new file mode 100644 | |
index 0000000..5dfa9df | |
--- /dev/null | |
+++ b/src/tls/re/prf.c | |
@@ -0,0 +1,94 @@ | |
+/** | |
+ * @file prf.c TLS Pseudorandom Function (PRF) | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_hmac.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+static int a_func(uint8_t *out, size_t out_len, | |
+ const uint8_t *secret, size_t secret_len, | |
+ const uint8_t *a_prev, size_t a_prev_len) | |
+{ | |
+ return hmac_sha256(secret, secret_len, | |
+ a_prev, a_prev_len, | |
+ out, out_len); | |
+} | |
+ | |
+ | |
+static int P_hash_sha256(uint8_t *output, size_t output_len, | |
+ const uint8_t *secret, size_t secret_len, | |
+ const uint8_t *seed, size_t seed_len) | |
+{ | |
+ uint8_t a[32]; | |
+ int err; | |
+ | |
+ a_func(a, sizeof(a), secret, secret_len, seed, seed_len); | |
+ | |
+ while (output_len != 0) { | |
+ uint8_t data[256]; | |
+ uint8_t md[32]; | |
+ size_t chunk_len = min(32, output_len); | |
+ size_t data_len = 0; | |
+ | |
+ if (sizeof(a) + seed_len > sizeof(data)) | |
+ return EINVAL; | |
+ | |
+ memcpy(&data[data_len], a, sizeof(a)); data_len += sizeof(a); | |
+ memcpy(&data[data_len], seed, seed_len); data_len += seed_len; | |
+ | |
+ err = hmac_sha256(secret, secret_len, data, data_len, | |
+ md, sizeof(md)); | |
+ if (err) | |
+ break; | |
+ | |
+ a_func(a, sizeof(a), secret, secret_len, a, sizeof(a)); | |
+ | |
+ mem_cpy(output, output_len, md, chunk_len); | |
+ | |
+ output += chunk_len; | |
+ output_len -= chunk_len; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+/* | |
+ * RFC 5246 Section 5 | |
+ * | |
+ * 5. HMAC and the Pseudorandom Function | |
+ * | |
+ P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + | |
+ HMAC_hash(secret, A(2) + seed) + | |
+ HMAC_hash(secret, A(3) + seed) + ... | |
+ | |
+ PRF(secret, label, seed) = P_<hash>(secret, label + seed) | |
+ | |
+ */ | |
+int tls_prf_sha256(uint8_t *output, size_t output_len, | |
+ const uint8_t *secret, size_t secret_len, | |
+ const uint8_t *label, size_t label_len, | |
+ const uint8_t *seed, size_t seed_len) | |
+{ | |
+ uint8_t label_seed[256]; | |
+ | |
+ if (label_len + seed_len > sizeof(label_seed)) | |
+ return EINVAL; | |
+ | |
+ memcpy(label_seed, label, label_len); | |
+ memcpy(&label_seed[label_len], seed, seed_len); | |
+ | |
+ return P_hash_sha256(output, output_len, | |
+ secret, secret_len, | |
+ label_seed, label_len + seed_len); | |
+} | |
diff --git a/src/tls/re/record.c b/src/tls/re/record.c | |
new file mode 100644 | |
index 0000000..b6ba7f8 | |
--- /dev/null | |
+++ b/src/tls/re/record.c | |
@@ -0,0 +1,201 @@ | |
+/** | |
+ * @file record.c TLS records | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+/* | |
+ * The Record-layer is opaque to the payload it carries | |
+ */ | |
+ | |
+ | |
+static void record_destructor(void *data) | |
+{ | |
+ struct tls_record *rec = data; | |
+ | |
+ mem_deref(rec->fragment); | |
+} | |
+ | |
+ | |
+int tls_record_encode(struct mbuf *mb, enum tls_version ver, | |
+ enum tls_content_type type, | |
+ uint16_t epoch, uint64_t seq, | |
+ const uint8_t *frag, size_t fraglen) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (!mb || !frag || !fraglen) | |
+ return EINVAL; | |
+ | |
+ if (fraglen > TLS_RECORD_FRAGMENT_SIZE) | |
+ return EOVERFLOW; | |
+ | |
+ err |= mbuf_write_u8(mb, type); | |
+ err |= mbuf_write_u16(mb, htons(ver)); | |
+ if (tls_version_is_dtls(ver)) { | |
+ err |= mbuf_write_u16(mb, htons(epoch)); | |
+ err |= mbuf_write_u48_hton(mb, seq); | |
+ } | |
+ err |= mbuf_write_u16(mb, htons(fraglen)); | |
+ if (err) | |
+ return err; | |
+ | |
+ err = mbuf_write_mem(mb, frag, fraglen); | |
+ if (err) | |
+ return err; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+/* returns ENODATA if record is not complete */ | |
+int tls_record_decode(struct tls_record **recp, struct mbuf *mb) | |
+{ | |
+ struct tls_record *rec; | |
+ int err = 0; | |
+ | |
+ if (!recp || !mb) | |
+ return EINVAL; | |
+ | |
+ rec = mem_zalloc(sizeof(*rec), record_destructor); | |
+ if (!rec) | |
+ return ENOMEM; | |
+ | |
+ if (mbuf_get_left(mb) < 3) { | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ rec->content_type = mbuf_read_u8(mb); | |
+ rec->proto_ver = ntohs(mbuf_read_u16(mb)); | |
+ | |
+ if (!tls_version_isvalid(rec->proto_ver)) { | |
+ DEBUG_WARNING("record_decode: protocol version" | |
+ " is not valid (0x%04x)\n", rec->proto_ver); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ | |
+ if (tls_version_is_dtls(rec->proto_ver)) { | |
+ | |
+ if (mbuf_get_left(mb) < 8) { | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ rec->epoch = ntohs(mbuf_read_u16(mb)); | |
+ rec->seq = mbuf_read_u48_ntoh(mb); | |
+ } | |
+ | |
+ if (mbuf_get_left(mb) < 2) { | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ rec->length = ntohs(mbuf_read_u16(mb)); | |
+ | |
+ if (rec->length > TLS_RECORD_FRAGMENT_SIZE) { | |
+ DEBUG_WARNING("record_decode: length too long (%u)" | |
+ " [type=%d, ver=%d, mbuf=%zu:%zu:%zu]\n", | |
+ rec->length, | |
+ rec->content_type, rec->proto_ver, | |
+ mb->pos, mb->end, mb->size); | |
+ err = EBADMSG; | |
+ goto out; | |
+ } | |
+ if (rec->length > mbuf_get_left(mb)) { | |
+ err = ENODATA; | |
+ goto out; | |
+ } | |
+ | |
+ /* NOTE: we copy the buffer to be safe. optimize later. */ | |
+ rec->fragment = mem_alloc(rec->length, NULL); | |
+ if (!rec->fragment) { | |
+ err = ENOMEM; | |
+ goto out; | |
+ } | |
+ | |
+ err = mbuf_read_mem(mb, rec->fragment, rec->length); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(rec); | |
+ else | |
+ *recp = rec; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+size_t tls_record_hdrsize(enum tls_version ver) | |
+{ | |
+ if (tls_version_is_dtls(ver)) | |
+ return 5+8; | |
+ else | |
+ return 5; | |
+} | |
+ | |
+ | |
+const char *tls_content_type_name(enum tls_content_type typ) | |
+{ | |
+ switch (typ) { | |
+ | |
+ case TLS_CHANGE_CIPHER_SPEC: return "change_cipher_spec"; | |
+ case TLS_ALERT: return "alert"; | |
+ case TLS_HANDSHAKE: return "handshake"; | |
+ case TLS_APPLICATION_DATA: return "application_data"; | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+void tls_record_dump(const struct tls_record *rec) | |
+{ | |
+ re_printf("\x1b[33m"); | |
+ | |
+ re_printf("----- TLS record: -----\n"); | |
+ re_printf("type=%s, ", tls_content_type_name(rec->content_type)); | |
+ re_printf("version=%s (0x%04x), ", | |
+ tls_version_name(rec->proto_ver), rec->proto_ver); | |
+ if (tls_version_is_dtls(rec->proto_ver)) { | |
+ re_printf("epoch=%u, ", rec->epoch); | |
+ re_printf("sequence=%llu, ", rec->seq); | |
+ } | |
+ re_printf("length=%u\n", rec->length); | |
+ re_printf("\n"); | |
+ re_printf("\x1b[;m"); | |
+} | |
+ | |
+ | |
+int tls_record_print_prefix(struct re_printf *pf, | |
+ const struct tls_record *rec) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (!rec) | |
+ return 0; | |
+ | |
+ if (tls_version_is_dtls(rec->proto_ver)) { | |
+ err = re_hprintf(pf, "seq={%u.%llu} ", rec->epoch, rec->seq); | |
+ } | |
+ | |
+ return err; | |
+} | |
diff --git a/src/tls/re/record_layer.c b/src/tls/re/record_layer.c | |
new file mode 100644 | |
index 0000000..e90b8d0 | |
--- /dev/null | |
+++ b/src/tls/re/record_layer.c | |
@@ -0,0 +1,390 @@ | |
+/** | |
+ * @file record_layer.c TLS session -- Record layer | |
+ * | |
+ * Copyright (C) 2010 - 2017 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sys.h> | |
+#include <re_cert.h> | |
+#include <re_sha.h> | |
+#include <re_aes.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+#define MBUF_HEADROOM 4 | |
+ | |
+ | |
+static int record_layer_flush(struct tls_session *sess); | |
+ | |
+ | |
+/* this function sends a large payload data, and does fragmentation */ | |
+int tls_record_layer_send(struct tls_session *sess, | |
+ enum tls_content_type type, | |
+ struct mbuf *mb_data, bool flush_now) | |
+{ | |
+ int err = 0; | |
+ | |
+#if 0 | |
+ tls_trace(sess, TLS_TRACE_RECORD, | |
+ "record layer send " | |
+ "(type=%s datalen=%zu bytes %s)\n", | |
+ tls_content_type_name(type), mbuf_get_left(mb_data), | |
+ flush_now ? "FLUSH" : ""); | |
+#endif | |
+ | |
+ while (mbuf_get_left(mb_data)) { | |
+ | |
+ size_t sz = min(sess->record_fragment_size, | |
+ mbuf_get_left(mb_data)); | |
+ | |
+ err = tls_record_layer_write(sess, | |
+ type, | |
+ mbuf_buf(mb_data), sz, | |
+ flush_now); | |
+ if (err) | |
+ return err; | |
+ | |
+ mbuf_advance(mb_data, sz); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+/* this function sends only 1 (one) fragment (!) */ | |
+int tls_record_layer_write(struct tls_session *sess, | |
+ enum tls_content_type type, | |
+ const uint8_t *frag, size_t fraglen, | |
+ bool flush_now) | |
+{ | |
+ int err; | |
+ | |
+ tls_trace(sess, TLS_TRACE_RECORD, | |
+ "record layer write fragment " | |
+ "(type=%s fraglen=%zu bytes %s)\n", | |
+ tls_content_type_name(type), fraglen, | |
+ flush_now ? "FLUSH" : ""); | |
+ | |
+ mbuf_skip_to_end(sess->record_layer.mb_write); | |
+ | |
+ err = tls_record_encode(sess->record_layer.mb_write, | |
+ sess->version, type, | |
+ sess->record_layer.write.epoch, | |
+ sess->record_layer.write.seq, | |
+ frag, fraglen); | |
+ if (err) | |
+ goto out; | |
+ | |
+ ++sess->record_layer.write.seq; | |
+ | |
+ if (flush_now) { | |
+ err = record_layer_flush(sess); | |
+ if (err) | |
+ goto out; | |
+ } | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+/* Flush the buffer to the network */ | |
+static int record_layer_flush(struct tls_session *sess) | |
+{ | |
+ int err; | |
+ | |
+ if (!sess->sendh) | |
+ return EIO; | |
+ | |
+ sess->record_layer.mb_write->pos = MBUF_HEADROOM; | |
+ | |
+ if (!mbuf_get_left(sess->record_layer.mb_write)) { | |
+ DEBUG_WARNING("record_layer_flush: no data to send!\n"); | |
+ return EINVAL; | |
+ } | |
+ | |
+ sess->record_layer.write.bytes += | |
+ mbuf_get_left(sess->record_layer.mb_write); | |
+ | |
+ err = sess->sendh(sess->record_layer.mb_write, sess->arg); | |
+ if (err) | |
+ goto out; | |
+ | |
+ sess->record_layer.mb_write->pos = MBUF_HEADROOM; | |
+ sess->record_layer.mb_write->end = MBUF_HEADROOM; | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+void tls_record_layer_new_epoch(struct tls_record_layer *record_layer, int rw) | |
+{ | |
+ if (!record_layer) | |
+ return; | |
+ | |
+ if (rw == READ) { | |
+ /* new epoch, reset sequence number */ | |
+ ++record_layer->read.epoch; | |
+ record_layer->read.seq = 0; | |
+ | |
+ record_layer->next_receive_seq = 0; | |
+ } | |
+ else { | |
+ /* new epoch, reset sequence number */ | |
+ ++record_layer->write.epoch; | |
+ record_layer->write.seq = 0; | |
+ } | |
+} | |
+ | |
+ | |
+uint64_t tls_record_get_read_seqnum(const struct tls_session *sess) | |
+{ | |
+ uint64_t epoch_seq; | |
+ | |
+ epoch_seq = sess->record_layer.read.seq; | |
+ | |
+ if (tls_version_is_dtls(sess->version)) { | |
+ epoch_seq |= ((uint64_t)sess->record_layer.read.epoch) << 48; | |
+ } | |
+ | |
+ return epoch_seq; | |
+} | |
+ | |
+ | |
+uint64_t tls_record_get_write_seqnum(const struct tls_session *sess) | |
+{ | |
+ uint64_t epoch_seq; | |
+ | |
+ epoch_seq = sess->record_layer.write.seq; | |
+ | |
+ if (tls_version_is_dtls(sess->version)) { | |
+ epoch_seq |= ((uint64_t)sess->record_layer.write.epoch) << 48; | |
+ } | |
+ | |
+ return epoch_seq; | |
+} | |
+ | |
+ | |
+/* | |
+ * NOTE: Record layer | |
+ * | |
+ * 1. Decrypt payload using AES | |
+ * 2. Calculate and verify the MAC | |
+ * | |
+ * <pre> | |
+ * struct { | |
+ * opaque IV[SecurityParameters.record_iv_length]; | |
+ * block-ciphered struct { | |
+ * opaque content[TLSCompressed.length]; | |
+ * opaque MAC[SecurityParameters.mac_length]; | |
+ * uint8 padding[GenericBlockCipher.padding_length]; | |
+ * uint8 padding_length; | |
+ * }; | |
+ * } GenericBlockCipher; | |
+ * <pre> | |
+ * | |
+ */ | |
+static int record_decrypt_aes_and_unmac(struct tls_session *sess, | |
+ struct tls_record *rec) | |
+{ | |
+ const struct key *write_key, *write_MAC_key; | |
+ uint8_t mac_pkt[TLS_MAX_MAC_SIZE], mac_gen[TLS_MAX_MAC_SIZE], padding; | |
+ size_t start; | |
+ size_t pos_content; | |
+ size_t pos_mac; | |
+ size_t mac_sz; | |
+ size_t content_len; | |
+ int err = 0; | |
+ struct mbuf mbf; | |
+ | |
+ if (!sess || !rec) | |
+ return EINVAL; | |
+ | |
+ if (rec->length < (TLS_IV_SIZE+20)) { | |
+ DEBUG_WARNING("record too short\n"); | |
+ return EBADMSG; | |
+ } | |
+ | |
+ mbf.buf = rec->fragment; | |
+ mbf.size = rec->length; | |
+ mbf.pos = 0; | |
+ mbf.end = rec->length; | |
+ | |
+ start = 0; | |
+ pos_content = start + TLS_IV_SIZE; | |
+ mac_sz = sess->sp_read.mac_length; | |
+ | |
+ if (sess->conn_end == TLS_CLIENT) | |
+ write_key = &sess->key_block.server_write_key; | |
+ else if (sess->conn_end == TLS_SERVER) | |
+ write_key = &sess->key_block.client_write_key; | |
+ else | |
+ return EINVAL; | |
+ | |
+ err = tls_crypt_decrypt(write_key, &mbf, rec->length, &padding); | |
+ assert(mbf.pos <= mbf.size); | |
+ assert(mbf.end <= mbf.size); | |
+ if (err) { | |
+ DEBUG_WARNING("crypt_decrypt error (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ pos_mac = start + rec->length - mac_sz - padding - 1; | |
+ content_len = rec->length - TLS_IV_SIZE - mac_sz - padding - 1; | |
+ | |
+ mbf.pos = pos_mac; | |
+ err = mbuf_read_mem(&mbf, mac_pkt, mac_sz); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (sess->conn_end == TLS_CLIENT) | |
+ write_MAC_key = &sess->key_block.server_write_MAC_key; | |
+ else if (sess->conn_end == TLS_SERVER) | |
+ write_MAC_key = &sess->key_block.client_write_MAC_key; | |
+ else | |
+ return EINVAL; | |
+ | |
+ err = tls_mac_generate(mac_gen, mac_sz, write_MAC_key, | |
+ tls_record_get_read_seqnum(sess), | |
+ rec->content_type, | |
+ rec->proto_ver, content_len, | |
+ &mbf.buf[pos_content]); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (0 != mem_seccmp(mac_gen, mac_pkt, mac_sz)) { | |
+ DEBUG_WARNING("decrypt: *** MAC Mismatch!" | |
+ " (record type '%s', length %u) ***" | |
+ "\n", | |
+ tls_content_type_name(rec->content_type), | |
+ rec->length); | |
+ re_printf("write_MAC_key: %w\n", | |
+ write_MAC_key->k, write_MAC_key->len); | |
+ re_printf("read_seq_num: %llu\n", | |
+ tls_record_get_read_seqnum(sess)); | |
+ re_printf("MAC: generated: %w\n", mac_gen, mac_sz); | |
+ re_printf(" packet: %w\n", mac_pkt, mac_sz); | |
+ return EBADMSG; | |
+ } | |
+ | |
+#if 1 | |
+ /* strip away the leading IV in the front */ | |
+ memmove(rec->fragment, rec->fragment + TLS_IV_SIZE, content_len); | |
+#else | |
+ uint8_t *clear; | |
+ | |
+ clear = mem_zalloc(content_len, NULL); | |
+ | |
+ mem_cpy(clear, content_len, &rec->fragment[pos_content], content_len); | |
+ | |
+ mem_deref(rec->fragment); | |
+ rec->fragment = clear; | |
+#endif | |
+ | |
+ /* update record header with length of clear-text record */ | |
+ rec->length = content_len; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_record_layer_handle_record(struct tls_session *sess, | |
+ struct tls_record *rec) | |
+{ | |
+ int err = 0; | |
+ | |
+ tls_trace(sess, TLS_TRACE_RECORD, | |
+ "%Hdecode type '%s' fragment_length=%u\n", | |
+ tls_record_print_prefix, rec, | |
+ tls_content_type_name(rec->content_type), rec->length); | |
+ | |
+ if (tls_version_is_dtls(sess->version)) { | |
+ | |
+ if (rec->epoch == sess->record_layer.read.epoch && | |
+ rec->seq == sess->record_layer.next_receive_seq) { | |
+ | |
+ ++sess->record_layer.next_receive_seq; | |
+ } | |
+ else { | |
+ /* SHOULD queue the message but MAY discard it. */ | |
+ DEBUG_INFO("discard message: epoch_seq=%u.%llu\n", | |
+ rec->epoch, rec->seq); | |
+ | |
+ return 0; | |
+ } | |
+ } | |
+ | |
+ switch (sess->sp_read.bulk_cipher_algorithm) { | |
+ | |
+ case TLS_BULKCIPHER_NULL: | |
+ rec->length -= sess->sp_read.mac_length; | |
+ break; | |
+ | |
+ case TLS_BULKCIPHER_AES: | |
+ err = record_decrypt_aes_and_unmac(sess, rec); | |
+ if (err) | |
+ return err; | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("session_record_decode: unknown" | |
+ " bulk cipher algo %d\n", | |
+ sess->sp_read.bulk_cipher_algorithm); | |
+ return ENOTSUP; | |
+ } | |
+ if (err) { | |
+ DEBUG_WARNING("session: record decrypt error " | |
+ "on type '%s' (%m)\n", | |
+ tls_content_type_name(rec->content_type), err); | |
+ goto out; | |
+ } | |
+ | |
+ /* increment sequence number, before passing on to upper layer */ | |
+ ++sess->record_layer.read.seq; | |
+ | |
+ /* Pass the Record on to upper layers */ | |
+ err = tls_handle_cleartext_record(sess, rec); | |
+ if (err) | |
+ return err; | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+void tls_record_layer_summary(const struct tls_record_layer *record_layer) | |
+{ | |
+ if (!record_layer) | |
+ return; | |
+ | |
+ re_printf("~~~ Record-layer: ~~~\n"); | |
+ re_printf("___ write_seq %u.%llu (%zu bytes)\n", | |
+ record_layer->write.epoch, | |
+ record_layer->write.seq, | |
+ record_layer->write.bytes); | |
+ re_printf("___ read_seq %u.%llu (%zu bytes)\n", | |
+ record_layer->read.epoch, | |
+ record_layer->read.seq, | |
+ record_layer->read.bytes); | |
+ | |
+ if (record_layer->mb_write->end) { | |
+ re_printf("___ pending write: %zu bytes\n", | |
+ record_layer->mb_write->end); | |
+ } | |
+} | |
diff --git a/src/tls/re/secparam.c b/src/tls/re/secparam.c | |
new file mode 100644 | |
index 0000000..14f7877 | |
--- /dev/null | |
+++ b/src/tls/re/secparam.c | |
@@ -0,0 +1,137 @@ | |
+/** | |
+ * @file secparam.c TLS Security Parameters | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <time.h> | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sys.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+/* | |
+ * RFC 5246 A.6. The Security Parameters | |
+ */ | |
+ | |
+ | |
+int tls_secparam_init(struct tls_secparam *sp, | |
+ const uint8_t randomb[32], | |
+ bool is_write, bool client) | |
+{ | |
+ if (!sp || !randomb) | |
+ return EINVAL; | |
+ | |
+ memset(sp, 0, sizeof(*sp)); | |
+ | |
+ sp->entity = client ? TLS_CLIENT : TLS_SERVER; | |
+ | |
+ if (client) { | |
+ mem_cpy(sp->client_random, sizeof(sp->client_random), | |
+ randomb, 32); | |
+ } | |
+ else { | |
+ mem_cpy(sp->server_random, sizeof(sp->server_random), | |
+ randomb, 32); | |
+ } | |
+ | |
+ sp->is_write = is_write; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_secparam_set(struct tls_secparam *sp, | |
+ const struct tls_suite *suite) | |
+{ | |
+ if (!sp || !suite) | |
+ return EINVAL; | |
+ | |
+ sp->bulk_cipher_algorithm = tls_cipher_algorithm(suite->cipher); | |
+ sp->cipher_type = tls_cipher_type(suite->cipher); | |
+ sp->enc_key_length = tls_cipher_keymaterial(suite->cipher); | |
+ sp->block_length = tls_cipher_blocksize(suite->cipher); | |
+ sp->fixed_iv_length = tls_cipher_ivsize(suite->cipher); | |
+ sp->record_iv_length = tls_cipher_ivsize(suite->cipher); | |
+ sp->mac_algorithm = suite->mac; | |
+ sp->mac_length = tls_mac_length(suite->mac); | |
+ sp->mac_key_length = tls_mac_length(suite->mac); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static const char *tls_bulkcipher_name(enum tls_bulkcipher_algorithm algo) | |
+{ | |
+ switch (algo) { | |
+ | |
+ case TLS_BULKCIPHER_NULL: return "Null"; | |
+ case TLS_BULKCIPHER_AES: return "AES"; | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+static const char *tls_ciphertype_name(enum tls_ciphertype type) | |
+{ | |
+ switch (type) { | |
+ | |
+ case TLS_CIPHERTYPE_STREAM: return "Stream"; | |
+ case TLS_CIPHERTYPE_BLOCK: return "Block"; | |
+ /*case TLS_CIPHERTYPE_AEAD: return "AEAD";*/ | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+static const char *tls_mac_name(enum tls_mac_algorithm mac) | |
+{ | |
+ switch (mac) { | |
+ | |
+ case TLS_MAC_NULL: return "Null"; | |
+ case TLS_MAC_HMAC_SHA1: return "HMAC_SHA1"; | |
+ case TLS_MAC_HMAC_SHA256: return "HMAC_SHA256"; | |
+ /*case TLS_MAC_HMAC_SHA384: return "HMAC_SHA384";*/ | |
+ /*case TLS_MAC_HMAC_SHA512: return "HMAC_SHA512";*/ | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+void tls_secparam_dump(const struct tls_secparam *sp) | |
+{ | |
+ if (!sp) | |
+ return; | |
+ | |
+ re_printf("SecurityParameters (%s):\n", | |
+ sp->entity == TLS_CLIENT ? "Client" : "Server"); | |
+ re_printf("bulk_cipher: %s\n", | |
+ tls_bulkcipher_name(sp->bulk_cipher_algorithm)); | |
+ re_printf("cipher_type: %s\n", | |
+ tls_ciphertype_name(sp->cipher_type)); | |
+ re_printf("enc_key_length: %zu bytes\n", sp->enc_key_length); | |
+ re_printf("block_length: %zu bytes\n", sp->block_length); | |
+ re_printf("fixed_iv_length: %zu bytes\n", sp->fixed_iv_length); | |
+ re_printf("record_iv_length: %zu bytes\n", sp->record_iv_length); | |
+ re_printf("mac_algorithm: %s\n", | |
+ tls_mac_name(sp->mac_algorithm)); | |
+ re_printf("mac_length: %zu bytes\n", sp->mac_length); | |
+ re_printf("mac_key_length: %zu bytes\n", sp->mac_key_length); | |
+ | |
+ re_printf("master_secret[]: %w\n", | |
+ sp->master_secret, sizeof(sp->master_secret)); | |
+ re_printf("client_random[]: %w\n", | |
+ sp->client_random, sizeof(sp->client_random)); | |
+ re_printf("server_random[]: %w\n", | |
+ sp->server_random, sizeof(sp->server_random)); | |
+ re_printf("\n"); | |
+} | |
diff --git a/src/tls/re/server.c b/src/tls/re/server.c | |
new file mode 100644 | |
index 0000000..1a67017 | |
--- /dev/null | |
+++ b/src/tls/re/server.c | |
@@ -0,0 +1,255 @@ | |
+/** | |
+ * @file server.c TLS server | |
+ * | |
+ * Copyright (C) 2010 - 2017 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sys.h> | |
+#include <re_cert.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+static int send_serverhello(struct tls_session *sess) | |
+{ | |
+ union handshake hand; | |
+ struct serverhello *hello = &hand.serverhello; | |
+ int err = 0; | |
+ | |
+ memset(&hand, 0, sizeof(hand)); | |
+ | |
+ hello->server_version = sess->version; | |
+ | |
+ mem_cpy(hello->random, sizeof(hello->random), | |
+ sess->sp_write.server_random, | |
+ sizeof(sess->sp_write.server_random)); | |
+ | |
+ /* XXX: we only support 1 cipher-suite for now | |
+ * add support for cipher-suite negotiation | |
+ */ | |
+ hello->cipher_suite = sess->selected_cipher_suite; | |
+ hello->compression_method = TLS_COMPRESSION_NULL; | |
+ | |
+ /* Local extensions */ | |
+ | |
+ /* XXX: intersect local and remote extensions */ | |
+ if (sess->tls && !list_isempty(&sess->tls->exts_local)) { | |
+ | |
+ err = tls_extensions_encode(&hello->extensions, | |
+ &sess->tls->exts_local); | |
+ if (err) { | |
+ DEBUG_WARNING("ext encode error (%m)\n", err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ err = tls_handshake_layer_send(sess, TLS_SERVER_HELLO, &hand, | |
+ NO_FLUSH, false); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ tls_vector_reset(&hello->extensions); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int send_serverhellodone(struct tls_session *sess) | |
+{ | |
+ int err; | |
+ | |
+ err = tls_handshake_layer_send(sess, TLS_SERVER_HELLO_DONE, NULL, | |
+ FLUSH, false); | |
+ if (err) | |
+ return err; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_server_handle_client_hello(struct tls_session *sess, | |
+ const struct clienthello *chell) | |
+{ | |
+ uint16_t *suites; | |
+ size_t i, n; | |
+ bool supported = false; | |
+ int err; | |
+ | |
+ if (sess->conn_end != TLS_SERVER) { | |
+ DEBUG_WARNING("client: did not expect ClientHello\n"); | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+ | |
+ if (sess->state != TLS_STATE_IDLE) { | |
+ DEBUG_WARNING("client_hello:" | |
+ " illegal state %s\n", | |
+ tls_state_name(sess->state)); | |
+ return EPROTO; | |
+ } | |
+ | |
+ tls_session_set_state(sess, TLS_STATE_CLIENT_HELLO_RECV); | |
+ | |
+ suites = chell->cipher_suites.data; | |
+ n = chell->cipher_suites.bytes / 2; | |
+ | |
+ /* check for a common cipher-suite */ | |
+ for (i=0; i<n; i++) { | |
+ enum tls_cipher_suite cs = ntohs(suites[i]); | |
+ | |
+ if (tls_cipher_suite_lookup(sess, cs)) { | |
+ sess->selected_cipher_suite = cs; | |
+ supported = true; | |
+ break; | |
+ } | |
+ } | |
+ if (!supported) { | |
+ DEBUG_WARNING("no common cipher suites" | |
+ " (server=%zu, client=%zu)\n", | |
+ sess->cipherc, n); | |
+#if 0 | |
+ send_alert(sess, TLS_LEVEL_FATAL, | |
+ TLS_ALERT_HANDSHAKE_FAILURE); | |
+#endif | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+ | |
+ /* decode extensions from Client */ | |
+ if (chell->extensions.bytes) { | |
+ | |
+ err = tls_extensions_decode(&sess->exts_remote, | |
+ &chell->extensions); | |
+ if (err) { | |
+ DEBUG_WARNING("extension error (%m)\n", err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ /* save the Client-random */ | |
+ mem_cpy(sess->sp_read.client_random, | |
+ sizeof(sess->sp_read.client_random), | |
+ chell->random, sizeof(chell->random)); | |
+ mem_cpy(sess->sp_write.client_random, | |
+ sizeof(sess->sp_write.client_random), | |
+ chell->random, sizeof(chell->random)); | |
+ | |
+ err = send_serverhello(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("send serverhello failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ err = tls_send_certificate(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("send certificate failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ err = send_serverhellodone(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("send ServerHelloDone failed (%m)\n", | |
+ err); | |
+ goto out; | |
+ } | |
+ | |
+#if 1 | |
+ /* the cipher-suite is now decided */ | |
+ sess->suite = tls_suite_lookup(sess->selected_cipher_suite); | |
+ if (!sess->suite) { | |
+ DEBUG_WARNING("cipher suite not found (%s)\n", | |
+ tls_cipher_suite_name(sess->selected_cipher_suite)); | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+#endif | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_server_handle_clientkeyexchange(struct tls_session *sess, | |
+ const struct client_key_exchange *cke) | |
+{ | |
+ uint8_t buf[512]; | |
+ size_t buf_len = sizeof(buf); | |
+ uint16_t ver_be = htons(sess->version); | |
+ int err; | |
+ | |
+ if (!sess || !cke) | |
+ return EINVAL; | |
+ | |
+ if (sess->conn_end != TLS_SERVER) | |
+ return EPROTO; | |
+ | |
+ /* decrypt PMS using local cert's private key */ | |
+ err = cert_private_decrypt(sess->cert_local, | |
+ buf, &buf_len, | |
+ cke->encr_pms.data, | |
+ cke->encr_pms.bytes); | |
+ if (err) { | |
+ DEBUG_WARNING("private_decrypt failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ /* TODO: continue the handshake to avoid the Bleichenbacher attack | |
+ */ | |
+ if (0 != memcmp(buf, &ver_be, 2)) { | |
+ DEBUG_WARNING("version rollback attack [0x%02x 0x%02x]\n", | |
+ buf[0], buf[1]); | |
+ } | |
+ | |
+ /* save the Pre master secret (cleartext) */ | |
+ if (buf_len != sizeof(sess->pre_master_secret)) { | |
+ DEBUG_WARNING("illegal pms length\n"); | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+ mem_cpy(sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret), | |
+ buf, buf_len); | |
+ | |
+#if 1 | |
+ /* XXX: this can be moved elsewhere ? */ | |
+ | |
+ err = tls_master_secret_compute(sess->sp_read.master_secret, | |
+ sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret), | |
+ sess->sp_read.client_random, | |
+ sess->sp_read.server_random); | |
+ if (err) { | |
+ DEBUG_WARNING("master_secret_compute error (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ err = tls_master_secret_compute(sess->sp_write.master_secret, | |
+ sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret), | |
+ sess->sp_write.client_random, | |
+ sess->sp_write.server_random); | |
+ if (err) { | |
+ DEBUG_WARNING("master_secret_compute error (%m)\n", err); | |
+ goto out; | |
+ } | |
+#endif | |
+ | |
+ | |
+out: | |
+ return err; | |
+} | |
diff --git a/src/tls/re/session.c b/src/tls/re/session.c | |
new file mode 100644 | |
index 0000000..3112695 | |
--- /dev/null | |
+++ b/src/tls/re/session.c | |
@@ -0,0 +1,1447 @@ | |
+/** | |
+ * @file session.c TLS session | |
+ * | |
+ * Copyright (C) 2010 - 2017 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sys.h> | |
+#include <re_cert.h> | |
+#include <re_sha.h> | |
+#include <re_aes.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+/* | |
+ * TODO: for DTLS Server, add option to send HelloVerifyRequest | |
+ * | |
+ */ | |
+ | |
+ | |
+#define TCP_BUFSIZE_MAX (1<<24) | |
+#define TLS_SEED_SIZE 32 | |
+#define MBUF_HEADROOM 4 | |
+ | |
+ | |
+static int encrypt_send_record(struct tls_session *sess, | |
+ enum tls_content_type type, struct mbuf *data); | |
+static void handshake_layer_append(struct tls_session *sess, | |
+ bool is_write, | |
+ const uint8_t *data, size_t len); | |
+static int handle_change_cipher_spec(struct tls_session *sess); | |
+ | |
+ | |
+void tls_session_set_state(struct tls_session *sess, enum tls_state state) | |
+{ | |
+ if (state < sess->state) { | |
+ DEBUG_WARNING("illegal decrementing state transition from" | |
+ " %d to %d\n", sess->state, state); | |
+ return; | |
+ } | |
+ | |
+#if 0 | |
+ re_printf("*** [%s] state transition: %-22s ---> %s\n", | |
+ sess->conn_end == TLS_CLIENT ? "Client" : "Server", | |
+ tls_state_name(sess->state), | |
+ tls_state_name(state)); | |
+#endif | |
+ | |
+ sess->state = state; | |
+} | |
+ | |
+ | |
+/* | |
+ * HANDSHAKE LAYER | |
+ */ | |
+ | |
+ | |
+int tls_handshake_layer_send(struct tls_session *sess, | |
+ enum tls_handshake_type msg_type, | |
+ const union handshake *hand, | |
+ bool flush_now, bool crypt) | |
+{ | |
+ struct mbuf *mb = NULL; | |
+ int err; | |
+ | |
+ tls_trace(sess, TLS_TRACE_HANDSHAKE, | |
+ "send handshake: type=%s\n", | |
+ tls_handshake_name(msg_type)); | |
+ | |
+ mb = mbuf_alloc(32); | |
+ if (!mb) | |
+ return ENOMEM; | |
+ | |
+ err = tls_handshake_encode(mb, sess->version, msg_type, | |
+ sess->handshake.seq_write, 0, hand); | |
+ if (err) | |
+ goto out; | |
+ | |
+ handshake_layer_append(sess, true, mb->buf, mb->end); | |
+ | |
+ mb->pos = 0; | |
+ | |
+ if (crypt) { | |
+ err = encrypt_send_record(sess, TLS_HANDSHAKE, mb); | |
+ } | |
+ else { | |
+ err = tls_record_layer_send(sess, TLS_HANDSHAKE, | |
+ mb, flush_now); | |
+ } | |
+ if (err) | |
+ goto out; | |
+ | |
+ ++sess->handshake.seq_write; | |
+ | |
+ out: | |
+ mem_deref(mb); | |
+ return err; | |
+} | |
+ | |
+ | |
+static void handshake_layer_append(struct tls_session *sess, | |
+ bool is_write, | |
+ const uint8_t *data, size_t len) | |
+{ | |
+ if (!sess || !data) | |
+ return; | |
+ | |
+ if (is_write) | |
+ sess->handshake.bytes_write += len; | |
+ else | |
+ sess->handshake.bytes_read += len; | |
+ | |
+ SHA256_Update(&sess->handshake.ctx, data, len); | |
+ | |
+#if 0 | |
+ re_printf(" >>> HANDSHAKE %s: %zu bytes (Total %zu)\n", | |
+ is_write ? "WRITE" : "READ", | |
+ len, | |
+ is_write ? sess->hand_bytes_write : sess->hand_bytes_read); | |
+#endif | |
+} | |
+ | |
+ | |
+static int handshake_layer_get_md(const struct tls_session *sess, | |
+ uint8_t md[]) | |
+{ | |
+ SHA256_CTX copy; | |
+ | |
+ if (!sess || !md) | |
+ return EINVAL; | |
+ | |
+ copy = sess->handshake.ctx; | |
+ SHA256_Final(md, ©); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+/* | |
+ * END LAYERS | |
+ */ | |
+ | |
+ | |
+bool tls_cipher_suite_lookup(const struct tls_session *sess, | |
+ enum tls_cipher_suite cs) | |
+{ | |
+ size_t i; | |
+ | |
+ for (i=0; i<sess->cipherc; i++) { | |
+ | |
+ if (cs == sess->cipherv[i]) | |
+ return true; | |
+ } | |
+ | |
+ return false; | |
+} | |
+ | |
+ | |
+static int send_alert(struct tls_session *sess, | |
+ enum tls_alertlevel level, enum tls_alertdescr descr) | |
+{ | |
+ struct tls_alert alert; | |
+ struct mbuf *mb; | |
+ int err; | |
+ | |
+ if (sess->alert_sent) | |
+ return 0; | |
+ | |
+ tls_trace(sess, TLS_TRACE_ALERT, "send alert: %s\n", | |
+ tls_alert_name(descr)); | |
+ | |
+ if (!sess) | |
+ return EINVAL; | |
+ | |
+ alert.level = level; | |
+ alert.descr = descr; | |
+ | |
+ mb = mbuf_alloc(2); | |
+ if (!mb) | |
+ return ENOMEM; | |
+ | |
+ err = tls_alert_encode(mb, &alert); | |
+ if (err) | |
+ goto out; | |
+ mb->pos = 0; | |
+ | |
+ err = encrypt_send_record(sess, TLS_ALERT, mb); | |
+ if (err) | |
+ goto out; | |
+ | |
+ sess->alert_sent = true; | |
+ | |
+ out: | |
+ mem_deref(mb); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static void conn_close(struct tls_session *sess, int err) | |
+{ | |
+ enum tls_alertdescr descr = TLS_ALERT_INTERNAL_ERROR; | |
+ tls_sess_close_h *closeh = sess->closeh; | |
+ | |
+ sess->closed = true; | |
+ sess->closeh = NULL; | |
+ | |
+ if (err == 0) | |
+ descr = TLS_ALERT_CLOSE_NOTIFY; | |
+ else if (err == EBADMSG) | |
+ descr = TLS_ALERT_DECODE_ERROR; | |
+ | |
+ /* send alert to peer */ | |
+ send_alert(sess, TLS_LEVEL_FATAL, descr); | |
+ | |
+ /* call close-handler */ | |
+ if (closeh) { | |
+ closeh(err, sess->arg); | |
+ } | |
+} | |
+ | |
+ | |
+int tls_send_certificate(struct tls_session *sess) | |
+{ | |
+ union handshake hand; | |
+ struct certificate *cert = &hand.certificate; | |
+ uint8_t *der = NULL; | |
+ size_t der_len = 0; | |
+ int err; | |
+ | |
+ memset(&hand, 0, sizeof(hand)); | |
+ | |
+ if (!sess->cert_local) { | |
+ DEBUG_WARNING("no local certificate\n"); | |
+ return ENOENT; | |
+ } | |
+ | |
+ err = cert_encode_der(sess->cert_local, &der, &der_len); | |
+ if (err) | |
+ goto out; | |
+ | |
+ err = tls_vector_init(&cert->certlist[0], der, der_len); | |
+ if (err) | |
+ goto out; | |
+ | |
+ cert->count = 1; | |
+ | |
+ err = tls_handshake_layer_send(sess, TLS_CERTIFICATE, &hand, | |
+ NO_FLUSH, false); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ tls_vector_reset(&cert->certlist[0]); | |
+ mem_deref(der); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static void destructor(void *data) | |
+{ | |
+ struct tls_session *sess = data; | |
+ | |
+ sess->tls = NULL; | |
+ | |
+#if 0 | |
+ re_printf("\n -- session summary --\n"); | |
+ re_printf("Remote %H\n", tls_extensions_print, &sess->exts_remote); | |
+#endif | |
+ | |
+ /* send close notify alert, if session was established */ | |
+ if (sess->estab) | |
+ send_alert(sess, TLS_LEVEL_WARNING, TLS_ALERT_CLOSE_NOTIFY); | |
+ | |
+ mem_deref(sess->handshake.mb); | |
+ mem_deref(sess->record_layer.mb); | |
+ mem_deref(sess->record_layer.mb_write); | |
+ mem_deref(sess->cert_local); | |
+ mem_deref(sess->cert_remote); | |
+ mem_deref(sess->cipherv); | |
+ list_flush(&sess->exts_remote); | |
+ | |
+ /* wipe the keys from memory */ | |
+ memset(sess->sp_write.master_secret, 0, | |
+ sizeof(sess->sp_write.master_secret)); | |
+ memset(sess->sp_read.master_secret, 0, | |
+ sizeof(sess->sp_read.master_secret)); | |
+} | |
+ | |
+ | |
+int tls_session_alloc(struct tls_session **sessp, | |
+ struct tls *tls, | |
+ enum tls_connection_end conn_end, | |
+ enum tls_version ver, | |
+ const enum tls_cipher_suite *cipherv, size_t cipherc, | |
+ tls_sess_send_h *sendh, | |
+ tls_sess_estab_h *estabh, | |
+ tls_data_recv_h *datarecvh, | |
+ tls_sess_close_h *closeh, void *arg) | |
+{ | |
+ struct tls_session *sess; | |
+ uint8_t sp_random[32]; | |
+ size_t i; | |
+ int err = 0; | |
+ | |
+ if (!sessp || !cipherv || !cipherc) | |
+ return EINVAL; | |
+ | |
+ for (i=0; i<cipherc; i++) { | |
+ enum tls_cipher_suite cs = cipherv[i]; | |
+ | |
+ if (!tls_suite_lookup(cs)) { | |
+ DEBUG_WARNING("alloc: cipher suite not supported" | |
+ " 0x%04x (%s)\n", | |
+ cs, tls_cipher_suite_name(cs)); | |
+ return ENOTSUP; | |
+ } | |
+ } | |
+ | |
+ sess = mem_zalloc(sizeof(*sess), destructor); | |
+ if (!sess) | |
+ return ENOMEM; | |
+ | |
+ sess->tls = tls; | |
+ sess->conn_end = conn_end; | |
+ sess->version = ver; | |
+ | |
+ sess->cipherv = mem_reallocarray(NULL, cipherc, | |
+ sizeof(*cipherv), NULL); | |
+ if (!sess->cipherv) { | |
+ err = ENOMEM; | |
+ goto out; | |
+ } | |
+ memcpy(sess->cipherv, cipherv, cipherc * sizeof(*cipherv)); | |
+ sess->cipherc = cipherc; | |
+ | |
+ rand_bytes(sp_random, sizeof(sp_random)); | |
+ | |
+ err |= tls_secparam_init(&sess->sp_write, sp_random, | |
+ 1, conn_end == TLS_CLIENT); | |
+ err |= tls_secparam_init(&sess->sp_read, sp_random, | |
+ 0, conn_end == TLS_CLIENT); | |
+ if (err) | |
+ goto out; | |
+ | |
+ sess->sendh = sendh; | |
+ sess->estabh = estabh; | |
+ sess->datarecvh = datarecvh; | |
+ sess->closeh = closeh; | |
+ sess->arg = arg; | |
+ | |
+ if (conn_end == TLS_CLIENT) { | |
+ /* generate 48-byte premaster secret */ | |
+ rand_bytes(sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret)); | |
+ sess->pre_master_secret[0] = (unsigned)ver >> 8; | |
+ sess->pre_master_secret[1] = (unsigned)ver & 0xff; | |
+ } | |
+ | |
+ SHA256_Init(&sess->handshake.ctx); | |
+ | |
+ // XXX add tls_record_layer_init | |
+ sess->record_layer.mb_write = mbuf_alloc(64); | |
+ if (!sess->record_layer.mb_write) { | |
+ err = ENOMEM; | |
+ goto out; | |
+ } | |
+ | |
+ sess->record_layer.mb_write->pos = MBUF_HEADROOM; | |
+ sess->record_layer.mb_write->end = MBUF_HEADROOM; | |
+ | |
+ sess->record_fragment_size = TLS_RECORD_FRAGMENT_SIZE; | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(sess); | |
+ else | |
+ *sessp = sess; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_session_start(struct tls_session *sess) | |
+{ | |
+ if (!sess) | |
+ return EINVAL; | |
+ | |
+ if (sess->state != TLS_STATE_IDLE) { | |
+ DEBUG_WARNING("start: illegal state %d\n", sess->state); | |
+ return EPROTO; | |
+ } | |
+ | |
+ tls_session_set_state(sess, TLS_STATE_CLIENT_HELLO_SENT); | |
+ | |
+ return tls_client_send_clienthello(sess); | |
+} | |
+ | |
+ | |
+static int send_change_cipher_spec(struct tls_session *sess) | |
+{ | |
+ struct mbuf mb_pld = { (uint8_t*)"\x01", 1, 0, 1}; | |
+ int err; | |
+ | |
+ tls_trace(sess, TLS_TRACE_CHANGE_CIPHER_SPEC, | |
+ "send ChangeCipherSpec\n"); | |
+ | |
+ err = tls_record_layer_write(sess, TLS_CHANGE_CIPHER_SPEC, | |
+ mb_pld.buf, mb_pld.end, | |
+ false); | |
+ if (err) | |
+ goto out; | |
+ | |
+ tls_record_layer_new_epoch(&sess->record_layer, WRITE); | |
+ | |
+ err = tls_secparam_set(&sess->sp_write, sess->suite); | |
+ if (err) { | |
+ DEBUG_WARNING("tls_secparam_set failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+static int encrypt_send_record(struct tls_session *sess, | |
+ enum tls_content_type type, struct mbuf *data) | |
+{ | |
+ struct mbuf *mb_enc = mbuf_alloc(64); | |
+ struct key *write_MAC_key; | |
+ struct key *write_key; | |
+ uint8_t mac[TLS_MAX_MAC_SIZE]; | |
+ size_t mac_sz = sess->sp_write.mac_length; | |
+ int err = 0; | |
+ | |
+ if (sess->conn_end == TLS_CLIENT) { | |
+ write_MAC_key = &sess->key_block.client_write_MAC_key; | |
+ write_key = &sess->key_block.client_write_key; | |
+ } | |
+ else if (sess->conn_end == TLS_SERVER) { | |
+ write_MAC_key = &sess->key_block.server_write_MAC_key; | |
+ write_key = &sess->key_block.server_write_key; | |
+ } | |
+ else { | |
+ return EINVAL; | |
+ } | |
+ | |
+ switch (sess->sp_write.mac_algorithm) { | |
+ | |
+ case TLS_MAC_NULL: | |
+ mac_sz = 0; | |
+ break; | |
+ | |
+ case TLS_MAC_HMAC_SHA1: | |
+ case TLS_MAC_HMAC_SHA256: | |
+ | |
+ err = tls_mac_generate(mac, mac_sz, write_MAC_key, | |
+ tls_record_get_write_seqnum(sess), | |
+ type, sess->version, | |
+ data->end, /* length w/o MAC */ | |
+ data->buf); | |
+ if (err) { | |
+ DEBUG_WARNING("mac_generate failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("session: send_record: mac algorithm" | |
+ " is not supported (%d)\n", | |
+ sess->sp_write.mac_algorithm); | |
+ err = ENOTSUP; | |
+ goto out; | |
+ break; | |
+ } | |
+ | |
+ /* append the MAC trailer */ | |
+ if (mac_sz) { | |
+ data->pos = data->end; | |
+ err = mbuf_write_mem(data, mac, mac_sz); | |
+ if (err) | |
+ goto out; | |
+ } | |
+ | |
+ switch (sess->sp_write.bulk_cipher_algorithm) { | |
+ | |
+ case TLS_BULKCIPHER_NULL: | |
+ err = mbuf_write_mem(mb_enc, data->buf, data->end); | |
+ break; | |
+ | |
+ case TLS_BULKCIPHER_AES: | |
+ err = tls_crypt_encrypt(write_key, mb_enc, data); | |
+ | |
+ assert(mb_enc->pos <= mb_enc->size); | |
+ assert(mb_enc->end <= mb_enc->size); | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("unknown bulk_cipher %d\n", | |
+ sess->sp_write.bulk_cipher_algorithm); | |
+ err = ENOTSUP; | |
+ goto out; | |
+ break; | |
+ } | |
+ | |
+ if (err) | |
+ goto out; | |
+ | |
+ mb_enc->pos = 0; | |
+ | |
+ err = tls_record_layer_write(sess, type, | |
+ mbuf_buf(mb_enc), mbuf_get_left(mb_enc), | |
+ true); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ mem_deref(mb_enc); | |
+ return err; | |
+} | |
+ | |
+ | |
+/* | |
+ * A Finished message is always sent immediately after a change | |
+ * cipher spec message to verify that the key exchange and | |
+ * authentication processes were successful. | |
+ */ | |
+static int send_finished(struct tls_session *sess) | |
+{ | |
+ union handshake hand; | |
+ uint8_t seed[TLS_SEED_SIZE]; | |
+ int err; | |
+ | |
+ tls_trace(sess, TLS_TRACE_HANDSHAKE, "send Finished\n"); | |
+ | |
+ memset(&hand, 0, sizeof(hand)); | |
+ | |
+ err = handshake_layer_get_md(sess, seed); | |
+ if (err) | |
+ goto out; | |
+ | |
+ err = tls_finish_calc(hand.finished.verify_data, | |
+ sess->sp_write.master_secret, | |
+ seed, sess->conn_end); | |
+ if (err) { | |
+ DEBUG_WARNING("finished: calc failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ if (sess->conn_end == TLS_CLIENT) { | |
+ | |
+ /* todo: move this to another place? */ | |
+ err = tls_keys_generate(&sess->key_block, &sess->sp_write); | |
+ if (err) { | |
+ DEBUG_WARNING("send_finished: " | |
+ "tls_keys_generate failed (%m)\n", err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ err = tls_handshake_layer_send(sess, TLS_FINISHED, &hand, | |
+ FLUSH, true); | |
+ if (err) { | |
+ DEBUG_WARNING("finished: handshake_layer_send failed %m\n", | |
+ err); | |
+ goto out; | |
+ } | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+static int handle_certificate(struct tls_session *sess, | |
+ const struct certificate *certificate) | |
+{ | |
+ int err; | |
+ | |
+ if (sess->got_cert) { | |
+ DEBUG_WARNING("already got certificate\n"); | |
+ return EPROTO; | |
+ } | |
+ | |
+ sess->got_cert = true; | |
+ | |
+ if (certificate->count > 0) { | |
+ | |
+ /* The sender's certificate MUST come first in the list */ | |
+ const struct tls_vector *first = &certificate->certlist[0]; | |
+ | |
+ if (sess->cert_remote) { | |
+ re_printf("cert reset\n"); | |
+ sess->cert_remote = mem_deref(sess->cert_remote); | |
+ } | |
+ | |
+ err = cert_decode(&sess->cert_remote, | |
+ first->data, first->bytes); | |
+ if (err) { | |
+ DEBUG_WARNING("certificate: cert_decode" | |
+ " %zu bytes failed (%m)\n", | |
+ first->bytes, err); | |
+ return err; | |
+ } | |
+ | |
+#if 0 | |
+ cert_dump(cert->cert_remote); | |
+#endif | |
+ | |
+ /* TODO: verify certificate, add callback? */ | |
+ } | |
+ else { | |
+ DEBUG_WARNING("no certificates\n"); | |
+ return EPROTO; | |
+ } | |
+ | |
+ /* encrypt it using the public key from the server's certificate */ | |
+ sess->encr_pre_master_secret_len | |
+ = sizeof(sess->encr_pre_master_secret); | |
+ | |
+ err = cert_public_encrypt(sess->cert_remote, | |
+ sess->encr_pre_master_secret, | |
+ &sess->encr_pre_master_secret_len, | |
+ sess->pre_master_secret, | |
+ sizeof(sess->pre_master_secret)); | |
+ if (err) { | |
+ DEBUG_WARNING("cert_public_encrypt failed (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int verify_finished(struct tls_session *sess, | |
+ const struct finished *fin) | |
+{ | |
+ uint8_t seed[TLS_SEED_SIZE]; | |
+ uint8_t verify_data[TLS_VERIFY_DATA_SIZE]; | |
+ int err; | |
+ | |
+ err = handshake_layer_get_md(sess, seed); | |
+ if (err) | |
+ return err; | |
+ | |
+ err = tls_finish_calc(verify_data, | |
+ sess->sp_write.master_secret, | |
+ seed, | |
+ !sess->conn_end); | |
+ if (err) { | |
+ DEBUG_WARNING("finished: calc failed (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ if (sizeof(verify_data) != sizeof(fin->verify_data) || | |
+ 0 != mem_seccmp(verify_data, fin->verify_data, | |
+ sizeof(verify_data))) { | |
+ | |
+ DEBUG_WARNING("finished: verify_data mismatch\n"); | |
+ | |
+ re_printf("finished: packet = %w\n", | |
+ fin->verify_data, sizeof(fin->verify_data)); | |
+ re_printf(" calcul = %w\n", | |
+ verify_data, sizeof(verify_data)); | |
+ | |
+ return EPROTO; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int client_handle_server_hello_done(struct tls_session *sess) | |
+{ | |
+ int err; | |
+ | |
+ if (!sess->got_cert) { | |
+ DEBUG_WARNING("handle_server_hello_done: no certificate\n"); | |
+ return EPROTO; | |
+ } | |
+ | |
+ if (sess->state != TLS_STATE_CLIENT_HELLO_SENT) { | |
+ DEBUG_WARNING("client: recv_server_hello_done:" | |
+ " illegal state %d\n", sess->state); | |
+ return EPROTO; | |
+ } | |
+ | |
+ tls_session_set_state(sess, TLS_STATE_SERVER_HELLO_DONE_RECV); | |
+ | |
+ /* NOTE: we must wait for all handshake messages | |
+ * to be processed and hashed | |
+ */ | |
+ err = tls_client_send_clientkeyexchange(sess); | |
+ if (err) | |
+ return err; | |
+ | |
+ /* Send ChangeCipherSpec protocol */ | |
+ err = send_change_cipher_spec(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("send_change_cipher_spec failed" | |
+ " (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ /* Send "Finished" message (encrypted) */ | |
+ err = send_finished(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("client: send_finished failed (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+static int handle_finished(struct tls_session *sess, | |
+ const struct finished *fin) | |
+{ | |
+ uint32_t nrefs; | |
+ int err; | |
+ | |
+ if (!sess->got_ccs) { | |
+ DEBUG_WARNING("recv Finished, but no CCS received\n"); | |
+ return EPROTO; | |
+ } | |
+ | |
+ switch (sess->conn_end) { | |
+ | |
+ case TLS_CLIENT: | |
+ if (sess->state != TLS_STATE_SERVER_HELLO_DONE_RECV) { | |
+ DEBUG_WARNING("finished:" | |
+ " illegal state %s\n", | |
+ tls_state_name(sess->state)); | |
+ return EPROTO; | |
+ } | |
+ break; | |
+ | |
+ case TLS_SERVER: | |
+ if (sess->state != TLS_STATE_CLIENT_HELLO_RECV) { | |
+ DEBUG_WARNING("finished:" | |
+ " illegal state %s\n", | |
+ tls_state_name(sess->state)); | |
+ return EPROTO; | |
+ } | |
+ break; | |
+ } | |
+ | |
+ tls_session_set_state(sess, TLS_STATE_FINISHED_RECV); | |
+ | |
+ err = verify_finished(sess, fin); | |
+ if (err) { | |
+ goto out; | |
+ } | |
+ | |
+ sess->estab = true; | |
+ | |
+ mem_ref(sess); | |
+ | |
+ if (sess->estabh) | |
+ sess->estabh(sess->arg); | |
+ | |
+ nrefs = mem_nrefs(sess); | |
+ mem_deref(sess); | |
+ | |
+ /* check if connection was deref'ed from handler */ | |
+ if (nrefs == 1) | |
+ return ECONNRESET; | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+static void process_handshake(struct tls_session *sess, | |
+ const uint8_t *fragment, size_t length, | |
+ const struct tls_handshake *hand) | |
+{ | |
+ int err = EPROTO; | |
+ | |
+ tls_trace(sess, TLS_TRACE_HANDSHAKE, | |
+ "recv handshake: type=%s, payload_length=%zu\n", | |
+ tls_handshake_name(hand->msg_type), hand->length); | |
+ | |
+ ++sess->handshake.seq_read; | |
+ | |
+ /* XXX: exclude Finished, that is a hack */ | |
+ if (hand->msg_type != TLS_FINISHED) { | |
+ | |
+ handshake_layer_append(sess, false, | |
+ fragment, length); | |
+ } | |
+ | |
+ switch (hand->msg_type) { | |
+ | |
+ case TLS_CLIENT_HELLO: | |
+ err = tls_server_handle_client_hello(sess, | |
+ &hand->u.clienthello); | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO: | |
+ err = tls_client_handle_server_hello(sess, | |
+ &hand->u.serverhello); | |
+ break; | |
+ | |
+ case TLS_HELLO_VERIFY_REQUEST: { | |
+ const struct tls_vector *cook; | |
+ | |
+ cook = &hand->u.hello_verify_req.cookie; | |
+ | |
+ /* save the cookie from server */ | |
+ memcpy(sess->handshake.cookie, cook->data, cook->bytes); | |
+ sess->handshake.cookie_len = cook->bytes; | |
+ | |
+ /* | |
+ * in cases where the cookie exchange is used, the initial | |
+ * ClientHello and HelloVerifyRequest MUST NOT be included | |
+ * in the CertificateVerify or Finished MAC computations. | |
+ */ | |
+ SHA256_Init(&sess->handshake.ctx); | |
+ | |
+ err = tls_client_send_clienthello(sess); | |
+ } | |
+ break; | |
+ | |
+ case TLS_CERTIFICATE: | |
+ err = handle_certificate(sess, &hand->u.certificate); | |
+ break; | |
+ | |
+ case TLS_SERVER_HELLO_DONE: | |
+ err = client_handle_server_hello_done(sess); | |
+ break; | |
+ | |
+ case TLS_CLIENT_KEY_EXCHANGE: | |
+ err = tls_server_handle_clientkeyexchange(sess, | |
+ &hand->u.client_key_exchange); | |
+ break; | |
+ | |
+ case TLS_FINISHED: | |
+ err = handle_finished(sess, &hand->u.finished); | |
+ if (err) | |
+ goto out; | |
+ | |
+ /* NOTE: append hash AFTER finish calculated, | |
+ * but BEFORE we send out Finished! | |
+ */ | |
+ handshake_layer_append(sess, false, | |
+ fragment, length); | |
+ | |
+ /* | |
+ * NOTE: after Finished is verified, | |
+ * we must send CCS and Finished | |
+ */ | |
+ if (sess->conn_end == TLS_SERVER) { | |
+ | |
+ /* Send ChangeCipherSpec protocol */ | |
+ err = send_change_cipher_spec(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("send_change_cipher_spec failed" | |
+ " (%m)\n", err); | |
+ goto out; | |
+ } | |
+ | |
+ /* Send "Finished" message (encrypted) */ | |
+ err = send_finished(sess); | |
+ if (err) { | |
+ DEBUG_WARNING("server: send_finished" | |
+ " failed (%m)\n", | |
+ err); | |
+ goto out; | |
+ } | |
+ } | |
+ | |
+ break; | |
+ | |
+ default: | |
+ err = EPROTO; | |
+ DEBUG_WARNING("session: handshake message" | |
+ " not handled (%s) (%d)\n", | |
+ tls_handshake_name(hand->msg_type), | |
+ hand->msg_type); | |
+ break; | |
+ } | |
+ | |
+ out: | |
+ if (err) { | |
+ conn_close(sess, err); | |
+ } | |
+ | |
+ return; | |
+} | |
+ | |
+ | |
+static int handle_handshake_fragment(struct tls_session *sess, | |
+ const struct tls_record *rec) | |
+{ | |
+ enum tls_version ver = rec->proto_ver; | |
+ size_t pos; | |
+ int err; | |
+ | |
+ /* | |
+ * Part I -- write record to handshake buffer | |
+ */ | |
+ if (!sess->handshake.mb) { | |
+ sess->handshake.mb = mbuf_alloc(64); | |
+ if (!sess->handshake.mb) | |
+ return ENOMEM; | |
+ } | |
+ | |
+ pos = sess->handshake.mb->pos; | |
+ | |
+ sess->handshake.mb->pos = sess->handshake.mb->end; | |
+ | |
+ err = mbuf_write_mem(sess->handshake.mb, rec->fragment, rec->length); | |
+ if (err) | |
+ return err; | |
+ | |
+ sess->handshake.mb->pos = pos; | |
+ | |
+ rec = NULL; /* not used anymore */ | |
+ | |
+ | |
+ /* | |
+ * Part II -- read handshakes from buffer | |
+ */ | |
+ for (;;) { | |
+ struct tls_handshake *handshake = 0; | |
+ uint8_t *frag; | |
+ size_t length; | |
+ bool stop = false; | |
+ | |
+ pos = sess->handshake.mb->pos; | |
+ | |
+ frag = mbuf_buf(sess->handshake.mb); | |
+ | |
+ err = tls_handshake_decode(&handshake, | |
+ ver, sess->handshake.mb); | |
+ if (err) { | |
+ sess->handshake.mb->pos = pos; | |
+ if (err == ENODATA) | |
+ err = 0; | |
+ break; | |
+ } | |
+ | |
+ length = sess->handshake.mb->pos - pos; | |
+ | |
+ process_handshake(sess, frag, length, handshake); | |
+ | |
+ mem_deref(handshake); | |
+ | |
+ /* todo: the handler might deref session */ | |
+ if (sess->handshake.mb->pos >= sess->handshake.mb->end) { | |
+ sess->handshake.mb = mem_deref(sess->handshake.mb); | |
+ stop = true; | |
+ } | |
+ if (sess->closed) | |
+ stop = true; | |
+ | |
+ if (stop) | |
+ break; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+/* This is the place for de-multiplexing incoming Records */ | |
+int tls_handle_cleartext_record(struct tls_session *sess, | |
+ const struct tls_record *rec) | |
+{ | |
+ struct mbuf mb_wrap = {rec->fragment, rec->length, 0, rec->length}; | |
+ struct mbuf *mb = &mb_wrap; | |
+ int err = 0; | |
+ | |
+ if (sess->closed) | |
+ return ECONNRESET; | |
+ | |
+ switch (rec->content_type) { | |
+ | |
+ case TLS_CHANGE_CIPHER_SPEC: { | |
+ struct tls_change_cipher_spec css; | |
+ | |
+ if (mbuf_get_left(mb) < 1) { | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+ | |
+ css.byte = mbuf_read_u8(mb); | |
+ | |
+ if (css.byte != 1) { | |
+ DEBUG_WARNING("ChangeCipherSpec:" | |
+ " expected 0x01, got 0x%02x\n", | |
+ css.byte); | |
+ err = EPROTO; | |
+ goto out; | |
+ } | |
+ err = handle_change_cipher_spec(sess); | |
+ } | |
+ break; | |
+ | |
+ case TLS_ALERT: { | |
+ struct tls_alert alert; | |
+ | |
+ err = tls_alert_decode(&alert, mb); | |
+ if (err) | |
+ goto out; | |
+ | |
+ if (alert.descr == TLS_ALERT_CLOSE_NOTIFY) { | |
+ DEBUG_NOTICE("received alert: %s\n", | |
+ tls_alert_name(alert.descr)); | |
+ } | |
+ else { | |
+ DEBUG_WARNING("received alert: %s\n", | |
+ tls_alert_name(alert.descr)); | |
+ } | |
+ | |
+ tls_trace(sess, TLS_TRACE_ALERT, "recv alert: %s\n", | |
+ tls_alert_name(alert.descr)); | |
+ | |
+ if (alert.descr == TLS_ALERT_CLOSE_NOTIFY) | |
+ conn_close(sess, 0); | |
+ else | |
+ conn_close(sess, EPROTO); /* XXX: translate */ | |
+ } | |
+ break; | |
+ | |
+ case TLS_HANDSHAKE: | |
+ err = handle_handshake_fragment(sess, rec); | |
+ break; | |
+ | |
+ case TLS_APPLICATION_DATA: | |
+ | |
+ tls_trace(sess, TLS_TRACE_APPLICATION_DATA, | |
+ "receive %zu bytes\n", rec->length); | |
+ | |
+ if (sess->datarecvh) | |
+ sess->datarecvh(rec->fragment, rec->length, sess->arg); | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("record: don't know how to" | |
+ " decode content-type %d" | |
+ " (%s)" | |
+ " [ver=%04x, epoch=%u, seq=%llu, len=%u]\n", | |
+ rec->content_type, | |
+ tls_content_type_name(rec->content_type), | |
+ rec->proto_ver, rec->epoch, rec->seq, | |
+ rec->length); | |
+ err = EPROTO; | |
+ break; | |
+ } | |
+ | |
+ out: | |
+ if (err && err != ENODATA) { | |
+ DEBUG_WARNING("record: decode payload (%s, %zu bytes)" | |
+ " failed" | |
+ " (%m)\n", | |
+ tls_content_type_name(rec->content_type), | |
+ rec->length, err); | |
+ | |
+ conn_close(sess, err); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int session_record_decode(struct tls_session *sess, struct mbuf *mb) | |
+{ | |
+ struct tls_record *rec = NULL; | |
+ int err; | |
+ | |
+ err = tls_record_decode(&rec, mb); | |
+ if (err) | |
+ return err; | |
+ | |
+ err = tls_record_layer_handle_record(sess, rec); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ mem_deref(rec); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int handle_change_cipher_spec(struct tls_session *sess) | |
+{ | |
+ const struct tls_suite *suite; | |
+ int err; | |
+ | |
+ tls_trace(sess, TLS_TRACE_CHANGE_CIPHER_SPEC, | |
+ "receive ChangeCipherSpec\n"); | |
+ | |
+ suite = tls_suite_lookup(sess->selected_cipher_suite); | |
+ if (!suite) { | |
+ DEBUG_WARNING("cipher suite not found (%s)\n", | |
+ tls_cipher_suite_name(sess->selected_cipher_suite)); | |
+ return EPROTO; | |
+ } | |
+ | |
+ err = tls_secparam_set(&sess->sp_read, suite); | |
+ if (err) { | |
+ DEBUG_WARNING("tls_secparam_set failed (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ /* new epoch, reset sequence number */ | |
+ tls_record_layer_new_epoch(&sess->record_layer, READ); | |
+ | |
+ if (sess->conn_end == TLS_SERVER) { | |
+ | |
+ /* todo: move this to another place? */ | |
+ err = tls_keys_generate(&sess->key_block, | |
+ &sess->sp_read); | |
+ if (err) { | |
+ DEBUG_WARNING("css: tls_keys_generate failed" | |
+ " (%m)\n", err); | |
+ return err; | |
+ } | |
+ } | |
+ | |
+ sess->got_ccs = true; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_session_send_data(struct tls_session *sess, | |
+ const uint8_t *data, size_t data_len) | |
+{ | |
+ struct mbuf *mb; | |
+ int err; | |
+ | |
+ if (!sess || !data || !data_len) | |
+ return EINVAL; | |
+ | |
+ tls_trace(sess, TLS_TRACE_APPLICATION_DATA, | |
+ "send %zu bytes\n", data_len); | |
+ | |
+ mb = mbuf_alloc(data_len); | |
+ if (!mb) | |
+ return ENOMEM; | |
+ | |
+ err = mbuf_write_mem(mb, data, data_len); | |
+ if (err) | |
+ goto out; | |
+ | |
+ mb->pos = 0; | |
+ | |
+ err = encrypt_send_record(sess, TLS_APPLICATION_DATA, mb); | |
+ | |
+ out: | |
+ mem_deref(mb); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+void tls_session_recvtcp(struct tls_session *sess, struct mbuf *mbx) | |
+{ | |
+ size_t pos; | |
+ int err = 0; | |
+ | |
+ if (!sess || !mbx) | |
+ return; | |
+ | |
+ sess->record_layer.read.bytes += mbuf_get_left(mbx); | |
+ | |
+ if (sess->record_layer.mb) { | |
+ pos = sess->record_layer.mb->pos; | |
+ | |
+ sess->record_layer.mb->pos = sess->record_layer.mb->end; | |
+ | |
+ err = mbuf_write_mem(sess->record_layer.mb, | |
+ mbuf_buf(mbx),mbuf_get_left(mbx)); | |
+ if (err) | |
+ goto out; | |
+ | |
+ sess->record_layer.mb->pos = pos; | |
+ | |
+ if (mbuf_get_left(sess->record_layer.mb) > TCP_BUFSIZE_MAX) { | |
+ err = EOVERFLOW; | |
+ goto out; | |
+ } | |
+ } | |
+ else { | |
+ sess->record_layer.mb = mbuf_alloc(mbuf_get_left(mbx)); | |
+ if (!sess->record_layer.mb) { | |
+ err = ENOMEM; | |
+ goto out; | |
+ } | |
+ | |
+ err = mbuf_write_mem(sess->record_layer.mb, | |
+ mbuf_buf(mbx),mbuf_get_left(mbx)); | |
+ if (err) | |
+ goto out; | |
+ | |
+ sess->record_layer.mb->pos = 0; | |
+ } | |
+ | |
+ mbx = NULL; /* unused after this */ | |
+ | |
+ for (;;) { | |
+ | |
+ if (mbuf_get_left(sess->record_layer.mb) < 5) | |
+ break; | |
+ | |
+ pos = sess->record_layer.mb->pos; | |
+ | |
+ err = session_record_decode(sess, sess->record_layer.mb); | |
+ if (err) { | |
+ sess->record_layer.mb->pos = pos; | |
+ if (err == ENODATA) | |
+ err = 0; | |
+ break; | |
+ } | |
+ | |
+ if (sess->record_layer.mb->pos >= sess->record_layer.mb->end) { | |
+ sess->record_layer.mb = | |
+ mem_deref(sess->record_layer.mb); | |
+ break; | |
+ } | |
+ if (sess->closed) | |
+ break; | |
+ } | |
+ | |
+ out: | |
+ if (err) { | |
+ conn_close(sess, err); | |
+ } | |
+} | |
+ | |
+ | |
+void tls_session_recvudp(struct tls_session *sess, struct mbuf *mb) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (!sess || !mb) | |
+ return; | |
+ | |
+ sess->record_layer.read.bytes += mbuf_get_left(mb); | |
+ | |
+ while (mbuf_get_left(mb) >= 5) { | |
+ | |
+ err = session_record_decode(sess, mb); | |
+ if (err) | |
+ break; | |
+ | |
+ if (sess->closed) | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+struct tls_secparam *tls_session_secparam(struct tls_session *sess, | |
+ bool write) | |
+{ | |
+ if (!sess) | |
+ return NULL; | |
+ | |
+ if (write) | |
+ return &sess->sp_write; | |
+ else | |
+ return &sess->sp_read; | |
+} | |
+ | |
+ | |
+void tls_session_dump(const struct tls_session *sess) | |
+{ | |
+ if (!sess) | |
+ return; | |
+ | |
+ re_printf("~~~~~ DTLS Session ~~~~~\n"); | |
+ | |
+ re_printf("cipher_spec: 0x%02x 0x%02x (%s)\n", | |
+ sess->selected_cipher_suite>>8, | |
+ sess->selected_cipher_suite&0xff, | |
+ tls_cipher_suite_name(sess->selected_cipher_suite)); | |
+ re_printf("pre_master_secret[%zu]: %w\n", | |
+ sizeof(sess->pre_master_secret), | |
+ sess->pre_master_secret, sizeof(sess->pre_master_secret)); | |
+ | |
+ re_printf("key_block: client_write_MAC: %zu bytes\n", | |
+ sess->key_block.client_write_MAC_key.len); | |
+ re_printf(" server_write_MAC: %zu bytes\n", | |
+ sess->key_block.server_write_MAC_key.len); | |
+ re_printf(" client_write_key: %zu bytes\n", | |
+ sess->key_block.client_write_key.len); | |
+ re_printf(" server_write_key: %zu bytes\n", | |
+ sess->key_block.server_write_key.len); | |
+ | |
+ re_printf("WRITE:\n"); | |
+ tls_secparam_dump(&sess->sp_write); | |
+ re_printf("READ:\n"); | |
+ tls_secparam_dump(&sess->sp_read); | |
+} | |
+ | |
+ | |
+void tls_session_summary(const struct tls_session *sess) | |
+{ | |
+ if (!sess) | |
+ return; | |
+ | |
+ re_printf("~~~ Handshake-layer: ~~~\n"); | |
+ re_printf("___ write_seq: %u (%zu bytes)\n", | |
+ sess->handshake.seq_write, sess->handshake.bytes_write); | |
+ re_printf("___ read_seq: %u (%zu bytes)\n", | |
+ sess->handshake.seq_read, sess->handshake.bytes_read); | |
+ re_printf("\n"); | |
+ | |
+ tls_record_layer_summary(&sess->record_layer); | |
+ | |
+ re_printf("selected_cipher_suite: 0x%04x (%s)\n", | |
+ sess->selected_cipher_suite, | |
+ tls_cipher_suite_name(sess->selected_cipher_suite)); | |
+ | |
+ re_printf("\n"); | |
+} | |
+ | |
+ | |
+int tls_session_set_certificate(struct tls_session *sess, | |
+ const char *pem, size_t len) | |
+{ | |
+ if (!sess || !pem || !len) | |
+ return EINVAL; | |
+ | |
+ sess->cert_local = mem_deref(sess->cert_local); | |
+ | |
+ return cert_decode_pem(&sess->cert_local, pem, len); | |
+} | |
+ | |
+ | |
+void tls_session_set_certificate2(struct tls_session *sess, | |
+ struct cert *cert) | |
+{ | |
+ if (!sess || !cert) | |
+ return; | |
+ | |
+ mem_deref(sess->cert_local); | |
+ sess->cert_local = mem_ref(cert); | |
+} | |
+ | |
+ | |
+enum tls_cipher_suite tls_session_cipher(struct tls_session *sess) | |
+{ | |
+ if (!sess) | |
+ return TLS_CIPHER_NULL_WITH_NULL_NULL; | |
+ | |
+ return sess->selected_cipher_suite; | |
+} | |
+ | |
+ | |
+int tls_session_set_fragment_size(struct tls_session *sess, size_t size) | |
+{ | |
+ if (!sess || size<2) | |
+ return EINVAL; | |
+ | |
+ sess->record_fragment_size = size; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+struct cert *tls_session_peer_certificate(const struct tls_session *sess) | |
+{ | |
+ return sess ? sess->cert_remote : NULL; | |
+} | |
+ | |
+ | |
+bool tls_session_is_estab(const struct tls_session *sess) | |
+{ | |
+ return sess ? sess->estab : false; | |
+} | |
+ | |
+ | |
+void tls_session_shutdown(struct tls_session *sess) | |
+{ | |
+ DEBUG_INFO("shutdown\n"); | |
+ | |
+ if (!sess || sess->closed) | |
+ return; | |
+ | |
+ sess->closed = true; | |
+ | |
+ send_alert(sess, TLS_LEVEL_FATAL, TLS_ALERT_CLOSE_NOTIFY); | |
+} | |
+ | |
+ | |
+const struct list *tls_session_remote_exts(const struct tls_session *sess) | |
+{ | |
+ return sess ? &sess->exts_remote : NULL; | |
+} | |
+ | |
+ | |
+int tls_session_get_servername(struct tls_session *sess, | |
+ char *servername, size_t sz) | |
+{ | |
+ struct tls_extension *ext; | |
+ | |
+ if (!sess || !servername || !sz) | |
+ return EINVAL; | |
+ | |
+ ext = tls_extension_find(tls_session_remote_exts(sess), | |
+ TLS_EXT_SERVER_NAME); | |
+ if (!ext) { | |
+ DEBUG_WARNING("remote server_name is missing\n"); | |
+ return ENOENT; | |
+ } | |
+ | |
+ if (ext->v.server_name.type != 0) | |
+ return ENOTSUP; | |
+ | |
+ str_ncpy(servername, ext->v.server_name.host, sz); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+const char *tls_state_name(enum tls_state st) | |
+{ | |
+ switch (st) { | |
+ | |
+ case TLS_STATE_IDLE: return "IDLE"; | |
+ case TLS_STATE_CLIENT_HELLO_SENT: return "CLIENT_HELLO_SENT"; | |
+ case TLS_STATE_CLIENT_HELLO_RECV: return "CLIENT_HELLO_RECV"; | |
+ case TLS_STATE_SERVER_HELLO_DONE_RECV: return "SERVER_HELLO_DONE_RECV"; | |
+ case TLS_STATE_FINISHED_RECV: return "FINISHED_RECV"; | |
+ | |
+ default: return "???"; | |
+ } | |
+} | |
diff --git a/src/tls/re/tls.c b/src/tls/re/tls.c | |
new file mode 100644 | |
index 0000000..fef3526 | |
--- /dev/null | |
+++ b/src/tls/re/tls.c | |
@@ -0,0 +1,498 @@ | |
+/** | |
+ * @file tls.c TLS context | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_sa.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_sys.h> | |
+#include <re_tcp.h> | |
+#include <re_cert.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+# define SRTP_AES128_CM_SHA1_80 0x0001 | |
+# define SRTP_AES128_CM_SHA1_32 0x0002 | |
+ | |
+ | |
+/* NOTE: shadow struct defined in tls_*.c */ | |
+struct tls_conn { | |
+ struct tls_session *ssl; | |
+ struct tls *tls; | |
+}; | |
+ | |
+ | |
+/* | |
+ * Default list of supported Cipher-Suites, sorted by strength | |
+ */ | |
+static const enum tls_cipher_suite default_suitev[] = { | |
+ | |
+ TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA256, | |
+ TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA256, | |
+ TLS_CIPHER_RSA_WITH_AES_256_CBC_SHA, | |
+ TLS_CIPHER_RSA_WITH_AES_128_CBC_SHA, | |
+}; | |
+ | |
+ | |
+static void destructor(void *data) | |
+{ | |
+ struct tls *tls = data; | |
+ | |
+#if 0 | |
+ re_printf("\n -- context summary --\n"); | |
+ re_printf("Local %H\n", tls_extensions_print, &tls->exts_local); | |
+#endif | |
+ | |
+ mem_deref(tls->suitev); | |
+ mem_deref(tls->cert); | |
+ list_flush(&tls->exts_local); | |
+} | |
+ | |
+ | |
+int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, | |
+ const char *pwd) | |
+{ | |
+ struct tls *tls; | |
+ int err; | |
+ (void)pwd; | |
+ | |
+ if (!tlsp) | |
+ return EINVAL; | |
+ | |
+ tls = mem_zalloc(sizeof(*tls), destructor); | |
+ if (!tls) | |
+ return ENOMEM; | |
+ | |
+ switch (method) { | |
+ | |
+ case TLS_METHOD_SSLV23: | |
+ tls->version = TLS1_2_VERSION; | |
+ break; | |
+ | |
+ case TLS_METHOD_DTLSV1: | |
+ case TLS_METHOD_DTLS: | |
+ case TLS_METHOD_DTLSV1_2: | |
+ tls->version = DTLS1_2_VERSION; | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("tls method %d not supported\n", method); | |
+ err = ENOSYS; | |
+ goto out; | |
+ } | |
+ | |
+ tls->suitec = ARRAY_SIZE(default_suitev); | |
+ tls->suitev = mem_reallocarray(NULL, tls->suitec, | |
+ sizeof(*tls->suitev), NULL); | |
+ memcpy(tls->suitev, default_suitev, sizeof(default_suitev)); | |
+ | |
+#if 0 | |
+ re_printf("context: suitec=%zu\n", tls->suitec); | |
+ for (i=0; i<tls->suitec; i++) { | |
+ enum tls_cipher_suite cs = tls->suitev[i]; | |
+ | |
+ re_printf(".... 0x%04x (%s)\n", | |
+ cs, tls_cipher_suite_name(cs)); | |
+ } | |
+#endif | |
+ | |
+ /* Load our keys and certificates */ | |
+ if (keyfile) { | |
+ | |
+ err = cert_load_file(&tls->cert, keyfile); | |
+ if (err) | |
+ goto out; | |
+ } | |
+ | |
+ DEBUG_INFO("created context with %s (%s)\n", | |
+ dtls_version_name(tls->version), keyfile); | |
+ | |
+ err = 0; | |
+ out: | |
+ if (err) | |
+ mem_deref(tls); | |
+ else | |
+ *tlsp = tls; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_add_ca(struct tls *tls, const char *capath) | |
+{ | |
+ (void)tls; | |
+ (void)capath; | |
+ DEBUG_WARNING("add_ca: not implemented\n"); | |
+ return ENOSYS; | |
+} | |
+ | |
+ | |
+int tls_set_selfsigned(struct tls *tls, const char *cn) | |
+{ | |
+ struct cert *cert = NULL; | |
+ int err; | |
+ | |
+ if (!tls || !cn) | |
+ return EINVAL; | |
+ | |
+ err = cert_generate_rsa(&cert, cn, 1024); | |
+ if (err) | |
+ return err; | |
+ | |
+ mem_deref(tls->cert); | |
+ tls->cert = cert; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_set_certificate(struct tls *tls, const char *pem, size_t len) | |
+{ | |
+ struct cert *cert; | |
+ int err; | |
+ | |
+ if (!tls || !pem || !len) | |
+ return EINVAL; | |
+ | |
+ err = cert_decode_pem(&cert, pem, len); | |
+ if (err) | |
+ return err; | |
+ | |
+ mem_deref(tls->cert); | |
+ tls->cert = cert; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+void tls_set_verify_client(struct tls *tls) | |
+{ | |
+ if (!tls) | |
+ return; | |
+ | |
+ re_printf("TODO: implement %s\n", __REFUNC__); | |
+} | |
+ | |
+ | |
+static uint16_t profile_decode(const struct pl *pl) | |
+{ | |
+ if (!pl_strcasecmp(pl, "SRTP_AES128_CM_SHA1_80")) | |
+ return SRTP_AES128_CM_SHA1_80; | |
+ else if (!pl_strcasecmp(pl, "SRTP_AES128_CM_SHA1_32")) | |
+ return SRTP_AES128_CM_SHA1_32; | |
+ else { | |
+ return 0; | |
+ } | |
+} | |
+ | |
+ | |
+int tls_set_srtp(struct tls *tls, const char *suites) | |
+{ | |
+ struct tls_extension *ext; | |
+ struct pl pl; | |
+ size_t i=0; | |
+ int err; | |
+ | |
+ if (!tls || !suites) | |
+ return EINVAL; | |
+ | |
+ err = tls_extension_add(&ext, &tls->exts_local, TLS_EXT_USE_SRTP); | |
+ if (err) | |
+ return err; | |
+ | |
+ pl_set_str(&pl, suites); | |
+ | |
+ while (pl.l) { | |
+ struct pl pl_suite, pl_colon; | |
+ uint16_t profile; | |
+ | |
+ err = re_regex(pl.p, pl.l, "[^:]+[:]*", &pl_suite, &pl_colon); | |
+ if (err) { | |
+ DEBUG_WARNING("invalid suites string\n"); | |
+ goto out; | |
+ } | |
+ | |
+ profile = profile_decode(&pl_suite); | |
+ if (!profile) { | |
+ DEBUG_WARNING("suite not supported: %r\n", &pl_suite); | |
+ err = ENOTSUP; | |
+ goto out; | |
+ } | |
+ | |
+ ext->v.use_srtp.profilev[i] = profile; | |
+ | |
+ ++i; | |
+ pl_advance(&pl, pl_suite.l + pl_colon.l); | |
+ } | |
+ | |
+ ext->v.use_srtp.profilec = i; | |
+ | |
+ out: | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type, | |
+ uint8_t *md, size_t size) | |
+{ | |
+ if (!tls || !tls->cert || !md) | |
+ return EINVAL; | |
+ | |
+ return cert_get_fingerprint(tls->cert, type, md, size); | |
+} | |
+ | |
+ | |
+int tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count) | |
+{ | |
+ enum tls_cipher_suite *suitev; | |
+ size_t i, sz; | |
+ int err = 0; | |
+ | |
+ if (!tls || !cipherv || !count) | |
+ return EINVAL; | |
+ | |
+ sz = count * sizeof(enum tls_cipher_suite); | |
+ | |
+ suitev = mem_zalloc(sz, NULL); | |
+ if (!suitev) | |
+ return ENOMEM; | |
+ | |
+ for (i=0; i<count; i++) { | |
+ | |
+ enum tls_cipher_suite cs; | |
+ | |
+ cs = tls_cipher_suite_resolve(cipherv[i]); | |
+ if (cs == TLS_CIPHER_NULL_WITH_NULL_NULL) { | |
+ DEBUG_WARNING("set_ciphers: cipher suite" | |
+ " not supported: %s\n", | |
+ cipherv[i]); | |
+ err = ENOTSUP; | |
+ goto out; | |
+ } | |
+ | |
+ suitev[i] = cs; | |
+ } | |
+ | |
+ mem_deref(tls->suitev); | |
+ tls->suitev = suitev; | |
+ tls->suitec = count; | |
+ | |
+ suitev = NULL; | |
+ | |
+ out: | |
+ mem_deref(suitev); | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type, | |
+ uint8_t *md, size_t size) | |
+{ | |
+ struct cert *cert; | |
+ | |
+ if (!tc || !md) | |
+ return EINVAL; | |
+ | |
+ cert = tls_session_peer_certificate(tc->ssl); | |
+ if (!cert) | |
+ return ENOENT; | |
+ | |
+ return cert_get_fingerprint(cert, type, md, size); | |
+} | |
+ | |
+ | |
+int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size) | |
+{ | |
+ struct cert *cert; | |
+ | |
+ if (!tc || !cn || !size) | |
+ return EINVAL; | |
+ | |
+ cert = tls_session_peer_certificate(tc->ssl); | |
+ if (!cert) | |
+ return ENOENT; | |
+ | |
+ return cert_get_subject(cert, cn, size); | |
+} | |
+ | |
+ | |
+int tls_peer_verify(const struct tls_conn *tc) | |
+{ | |
+ (void)tc; | |
+ DEBUG_WARNING("peer_verify: not implemented\n"); | |
+ return ENOSYS; | |
+} | |
+ | |
+ | |
+#define LABEL_LEN 19 | |
+int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite, | |
+ uint8_t *cli_key, size_t cli_key_size, | |
+ uint8_t *srv_key, size_t srv_key_size) | |
+{ | |
+ static const uint8_t label[LABEL_LEN] = "EXTRACTOR-dtls_srtp"; | |
+ struct tls_secparam *secparam; | |
+ struct tls_extension *extl, *extr; | |
+ size_t key_size, salt_size, size; | |
+ uint16_t common_profile = 0; | |
+ uint8_t output[2 * 30]; | |
+ uint8_t seed[TLS_CLIENT_RANDOM_LEN + TLS_SERVER_RANDOM_LEN]; | |
+ uint8_t *sp = seed, *p; | |
+ bool write = false; | |
+ size_t i, j; | |
+ int err; | |
+ | |
+ if (!tc || !suite || !cli_key || !srv_key) | |
+ return EINVAL; | |
+ | |
+ extl = tls_extension_find(&tc->tls->exts_local, TLS_EXT_USE_SRTP); | |
+ extr = tls_extension_find(tls_session_remote_exts(tc->ssl), | |
+ TLS_EXT_USE_SRTP); | |
+ if (!extl) { | |
+ DEBUG_WARNING("keyinfo: no local extensions\n"); | |
+ return ENOENT; | |
+ } | |
+ if (!extr) { | |
+ DEBUG_WARNING("keyinfo: no remote extensions\n"); | |
+ return ENOENT; | |
+ } | |
+ | |
+ /* find a common SRTP profile */ | |
+ for (i=0; i<extr->v.use_srtp.profilec && !common_profile; i++) { | |
+ | |
+ uint16_t rprofile = extr->v.use_srtp.profilev[i]; | |
+ | |
+ for (j=0; j<extl->v.use_srtp.profilec; j++) { | |
+ | |
+ uint16_t lprofile = extl->v.use_srtp.profilev[j]; | |
+ if (rprofile == lprofile) { | |
+ common_profile = rprofile; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ if (!common_profile) { | |
+ DEBUG_WARNING("keyinfo: no common srtp profile\n"); | |
+ return ENOENT; | |
+ } | |
+ | |
+ switch (common_profile) { | |
+ | |
+ case SRTP_AES128_CM_SHA1_80: | |
+ *suite = SRTP_AES_CM_128_HMAC_SHA1_80; | |
+ key_size = 16; | |
+ salt_size = 14; | |
+ break; | |
+ | |
+ case SRTP_AES128_CM_SHA1_32: | |
+ *suite = SRTP_AES_CM_128_HMAC_SHA1_32; | |
+ key_size = 16; | |
+ salt_size = 14; | |
+ break; | |
+ | |
+ default: | |
+ DEBUG_WARNING("keyinfo: unsupported profile 0x%04x\n", | |
+ common_profile); | |
+ return ENOSYS; | |
+ } | |
+ | |
+ size = key_size + salt_size; | |
+ | |
+ if (cli_key_size < size || srv_key_size < size) | |
+ return EOVERFLOW; | |
+ | |
+ secparam = tls_session_secparam(tc->ssl, write); | |
+ | |
+ memcpy(sp, secparam->client_random, TLS_CLIENT_RANDOM_LEN); | |
+ sp += TLS_CLIENT_RANDOM_LEN; | |
+ memcpy(sp, secparam->server_random, TLS_SERVER_RANDOM_LEN); | |
+ | |
+ err = tls_prf_sha256(output, sizeof(output), | |
+ secparam->master_secret, TLS_MASTER_SECRET_LEN, | |
+ label, LABEL_LEN, | |
+ seed, sizeof(seed)); | |
+ if (err) { | |
+ DEBUG_WARNING("srtp_keyinfo: prf_sha256 failed (%m)\n", err); | |
+ return err; | |
+ } | |
+ | |
+ p = output; | |
+ | |
+ memcpy(cli_key, p, key_size); p += key_size; | |
+ memcpy(srv_key, p, key_size); p += key_size; | |
+ memcpy(cli_key + key_size, p, salt_size); p += salt_size; | |
+ memcpy(srv_key + key_size, p, salt_size); | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+const char *tls_cipher_name(const struct tls_conn *tc) | |
+{ | |
+ if (!tc) | |
+ return NULL; | |
+ | |
+ return tls_cipher_suite_name(tls_session_cipher(tc->ssl)); | |
+} | |
+ | |
+ | |
+int tls_set_servername(struct tls_conn *tc, const char *servername) | |
+{ | |
+ struct tls_extension *ext; | |
+ struct tls *tls; | |
+ int err; | |
+ | |
+ if (!tc || !servername) | |
+ return EINVAL; | |
+ | |
+ tls = tc->tls; // XXX not correct | |
+ if (!tls) { | |
+ DEBUG_WARNING("set_servername: no tls context\n"); | |
+ return ENOTSUP; | |
+ } | |
+ | |
+ err = tls_extension_add(&ext, &tls->exts_local, TLS_EXT_SERVER_NAME); | |
+ if (err) | |
+ return err; | |
+ | |
+ ext->v.server_name.type = 0; | |
+ err = str_dup(&ext->v.server_name.host, servername); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_get_servername(struct tls_conn *tc, char *servername, size_t sz) | |
+{ | |
+ struct tls_extension *ext; | |
+ | |
+ if (!tc || !servername || !sz) | |
+ return EINVAL; | |
+ | |
+ ext = tls_extension_find(tls_session_remote_exts(tc->ssl), | |
+ TLS_EXT_SERVER_NAME); | |
+ if (!ext) { | |
+ DEBUG_WARNING("remote server_name is missing\n"); | |
+ return ENOENT; | |
+ } | |
+ | |
+ if (ext->v.server_name.type != 0) | |
+ return ENOTSUP; | |
+ | |
+ str_ncpy(servername, ext->v.server_name.host, sz); | |
+ | |
+ return 0; | |
+} | |
diff --git a/src/tls/re/tls.h b/src/tls/re/tls.h | |
new file mode 100644 | |
index 0000000..1099951 | |
--- /dev/null | |
+++ b/src/tls/re/tls.h | |
@@ -0,0 +1,290 @@ | |
+/** | |
+ * @file re/tls.h TLS backend using libre (Internal API) | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+ | |
+/* constants */ | |
+ | |
+#define TLS_MAX_RSA_BYTES 512 /* 4096 bits */ | |
+#define TLS_MAX_MAC_SIZE 32 | |
+#define TLS_IV_SIZE 16 /* XXX: should be dynamic */ | |
+ | |
+#define DTLS_COOKIE_LENGTH 256 | |
+ | |
+#define FLUSH (true) | |
+#define NO_FLUSH (false) | |
+ | |
+ | |
+enum rw { | |
+ READ = 0, | |
+ WRITE = 1, | |
+}; | |
+ | |
+ | |
+/* | |
+ State Machine: | |
+ | |
+IDLE IDLE | |
+ | |
+ -------- ClientHello -------> | |
+CH CH | |
+ <------- ServerHello ------- | |
+ <------- Certificate ------- | |
+ <----- ServerHelloDone ----- | |
+SHD | |
+ ----- ClientKeyExchange ----> | |
+ | |
+ ===== ChangeCipherSpec =====> | |
+ -------- Finished (*) ------> FINI | |
+ | |
+ <===== ChangeCipherSpec ===== | |
+ <--------- Finished (*) ----- | |
+FINI | |
+ | |
+XXX: very simple fsm for now, improve later | |
+*/ | |
+enum tls_state { | |
+ | |
+ TLS_STATE_IDLE = 0, | |
+ | |
+ TLS_STATE_CLIENT_HELLO_SENT = 2, /* client */ | |
+ TLS_STATE_CLIENT_HELLO_RECV = 3, /* server */ | |
+ | |
+ TLS_STATE_SERVER_HELLO_DONE_RECV = 8, /* client */ | |
+ TLS_STATE_FINISHED_RECV = 10, | |
+}; | |
+ | |
+ | |
+struct tls { | |
+ struct cert *cert; | |
+ enum tls_version version; | |
+ | |
+ enum tls_cipher_suite *suitev; | |
+ size_t suitec; | |
+ | |
+ struct list exts_local; /* Local extensions */ | |
+}; | |
+ | |
+ | |
+/* crypt */ | |
+ | |
+int tls_crypt_encrypt(const struct key *write_key, | |
+ struct mbuf *mb_enc, struct mbuf *data); | |
+int tls_crypt_decrypt(const struct key *write_key, | |
+ struct mbuf *mb, size_t rec_length, | |
+ uint8_t *paddingp); | |
+ | |
+ | |
+/* hmac */ | |
+ | |
+int hmac_sha256(const uint8_t *key, | |
+ size_t key_len, | |
+ const uint8_t *data, | |
+ size_t data_len, | |
+ uint8_t* out, | |
+ size_t out_size); | |
+ | |
+ | |
+/* mbuf */ | |
+ | |
+int mbuf_write_u24_hton(struct mbuf *mb, uint24_t u); | |
+int mbuf_write_u48_hton(struct mbuf *mb, uint48_t u); | |
+uint24_t mbuf_read_u24_ntoh(struct mbuf *mb); | |
+uint48_t mbuf_read_u48_ntoh(struct mbuf *mb); | |
+ | |
+ | |
+/* memory utils */ | |
+ | |
+void mem_cpy(uint8_t *dst, size_t dst_sz, | |
+ const uint8_t *src, size_t src_sz); | |
+ | |
+ | |
+/* | |
+ * extensions | |
+ */ | |
+ | |
+enum tls_extension_type { | |
+ TLS_EXT_SERVER_NAME = 0, /* RFC 6066 */ | |
+ TLS_EXT_USE_SRTP = 14, /* RFC 5764 */ | |
+}; | |
+ | |
+struct tls_extension { | |
+ struct le le; | |
+ enum tls_extension_type type; | |
+ size_t length; /* only set for decoded ext's */ | |
+ | |
+ //struct tls_vector data; | |
+ | |
+ union { | |
+ struct { | |
+ uint8_t type; | |
+ char *host; | |
+ } server_name; | |
+ | |
+ struct { | |
+ uint16_t profilev[4]; | |
+ size_t profilec; | |
+ /* srtp_mki */ | |
+ } use_srtp; | |
+ } v; | |
+}; | |
+ | |
+ | |
+typedef bool (tls_extension_h)(const struct tls_extension *ext, void *arg); | |
+ | |
+ | |
+int tls_extension_add(struct tls_extension **extp, struct list *extl, | |
+ enum tls_extension_type type); | |
+int tls_extensions_encode(struct tls_vector *vect, | |
+ const struct list *extl); | |
+int tls_extensions_decode(struct list *extl, | |
+ const struct tls_vector *vect); | |
+struct tls_extension *tls_extension_find(const struct list *extl, | |
+ enum tls_extension_type type); | |
+int tls_extensions_print(struct re_printf *pf, | |
+ const struct list *extl); | |
+const char *tls_extension_name(enum tls_extension_type ext); | |
+struct tls_extension *tls_extensions_apply(const struct list *extl, | |
+ tls_extension_h *exth, void *arg); | |
+ | |
+ | |
+/* | |
+ * session | |
+ */ | |
+ | |
+/* XXX: split into client.c and server.c */ | |
+struct tls_session { | |
+ const struct tls *tls; /* pointer to parent context */ | |
+ | |
+ struct tls_secparam sp_write; | |
+ struct tls_secparam sp_read; | |
+ | |
+ struct tls_key_block key_block; | |
+ | |
+ enum tls_connection_end conn_end; | |
+ enum tls_version version; | |
+ | |
+ enum tls_cipher_suite *cipherv; | |
+ size_t cipherc; | |
+ | |
+ enum tls_cipher_suite selected_cipher_suite; | |
+ | |
+ const struct tls_suite *suite; | |
+ | |
+ uint8_t pre_master_secret[48]; | |
+ uint8_t encr_pre_master_secret[TLS_MAX_RSA_BYTES]; | |
+ size_t encr_pre_master_secret_len; | |
+ | |
+ /* certificates (X509v3) */ | |
+ struct cert *cert_local; | |
+ struct cert *cert_remote; | |
+ | |
+ /* callback handlers */ | |
+ tls_sess_send_h *sendh; | |
+ tls_data_recv_h *datarecvh; | |
+ tls_sess_estab_h *estabh; | |
+ tls_sess_close_h *closeh; | |
+ void *arg; | |
+ | |
+ enum tls_trace_flags trace_flags; | |
+ tls_trace_h *traceh; | |
+ | |
+ bool estab; | |
+ bool closed; | |
+ bool alert_sent; | |
+ bool got_ccs; | |
+ bool got_cert; | |
+ | |
+ /* | |
+ * PROTOCOL LAYERS BELOW: | |
+ */ | |
+ | |
+ /* handshake layer: */ | |
+ struct { | |
+ | |
+ SHA256_CTX ctx; /* hash of Handshakes sent/received */ | |
+ | |
+ uint16_t seq_write; | |
+ uint16_t seq_read; | |
+ size_t bytes_write; | |
+ size_t bytes_read; | |
+ | |
+ struct mbuf *mb; /* buffer incoming handshake fragments */ | |
+ | |
+ uint8_t cookie[DTLS_COOKIE_LENGTH]; /* DTLS only */ | |
+ size_t cookie_len; | |
+ | |
+ } handshake; | |
+ | |
+ enum tls_state state; | |
+ | |
+ /* record layer: */ | |
+ struct tls_record_layer { | |
+ struct mbuf *mb_write; /* buffer outgoing records */ | |
+ struct mbuf *mb; /* buffer for incoming TCP-packets */ | |
+ | |
+ struct { | |
+ uint64_t seq; /* sequence number for each record */ | |
+ uint16_t epoch; /* only for DTLS */ | |
+ size_t bytes; | |
+ | |
+ } write, read; | |
+ | |
+ uint64_t next_receive_seq; /* DTLS only */ | |
+ } record_layer; | |
+ | |
+ size_t record_fragment_size; | |
+ | |
+ struct list exts_remote; | |
+}; | |
+ | |
+bool tls_cipher_suite_lookup(const struct tls_session *sess, | |
+ enum tls_cipher_suite cs); | |
+void tls_session_set_state(struct tls_session *sess, enum tls_state state); | |
+int tls_send_certificate(struct tls_session *sess); | |
+int tls_handle_cleartext_record(struct tls_session *sess, | |
+ const struct tls_record *rec); | |
+const struct list *tls_session_remote_exts(const struct tls_session *sess); | |
+const char *tls_state_name(enum tls_state st); | |
+ | |
+int tls_client_send_clienthello(struct tls_session *sess); | |
+int tls_client_handle_server_hello(struct tls_session *sess, | |
+ const struct serverhello *hell); | |
+int tls_client_send_clientkeyexchange(struct tls_session *sess); | |
+ | |
+int tls_server_handle_client_hello(struct tls_session *sess, | |
+ const struct clienthello *chell); | |
+int tls_server_handle_clientkeyexchange(struct tls_session *sess, | |
+ const struct client_key_exchange *cke); | |
+ | |
+int tls_handshake_layer_send(struct tls_session *sess, | |
+ enum tls_handshake_type msg_type, | |
+ const union handshake *hand, | |
+ bool flush_now, bool crypt); | |
+ | |
+int tls_record_layer_write(struct tls_session *sess, | |
+ enum tls_content_type type, | |
+ const uint8_t *frag, size_t fraglen, | |
+ bool flush_now); | |
+int tls_record_layer_send(struct tls_session *sess, | |
+ enum tls_content_type type, | |
+ struct mbuf *mb_data, bool flush_now); | |
+void tls_record_layer_new_epoch(struct tls_record_layer *record_layer, int rw); | |
+int tls_record_layer_handle_record(struct tls_session *sess, | |
+ struct tls_record *rec); | |
+void tls_record_layer_summary(const struct tls_record_layer *record_layer); | |
+uint64_t tls_record_get_read_seqnum(const struct tls_session *sess); | |
+uint64_t tls_record_get_write_seqnum(const struct tls_session *sess); | |
+ | |
+ | |
+/* | |
+ * version | |
+ */ | |
+ | |
+bool tls_version_isvalid(enum tls_version ver); | |
+ | |
+ | |
+int tls_record_print_prefix(struct re_printf *pf, | |
+ const struct tls_record *rec); | |
diff --git a/src/tls/re/tls_tcp.c b/src/tls/re/tls_tcp.c | |
new file mode 100644 | |
index 0000000..9b9f88d | |
--- /dev/null | |
+++ b/src/tls/re/tls_tcp.c | |
@@ -0,0 +1,302 @@ | |
+/** | |
+ * @file tls_tcp.c TLS with TCP-transport | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_main.h> | |
+#include <re_sa.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tcp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+#define TRACE 0 | |
+ | |
+ | |
+/* NOTE: shadow struct defined in tls_*.c */ | |
+struct tls_conn { | |
+ struct tls_session *ssl; /* inheritance */ | |
+ struct tls *tls; /* inheritance */ | |
+ | |
+ struct tcp_helper *th; | |
+ struct tcp_conn *tcp; | |
+ struct mbuf *mb; | |
+ bool active; | |
+ bool up; | |
+ bool closed; | |
+ int err; | |
+}; | |
+ | |
+ | |
+static void destructor(void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+#if 0 | |
+ tls_session_dump(tc->ssl); | |
+#endif | |
+ | |
+ if (tc->ssl) { | |
+ tls_session_shutdown(tc->ssl); | |
+ mem_deref(tc->ssl); | |
+ } | |
+ mem_deref(tc->th); | |
+ mem_deref(tc->tcp); | |
+ mem_deref(tc->tls); | |
+ mem_deref(tc->mb); | |
+} | |
+ | |
+ | |
+static int dtls_send_handler(struct mbuf *mb, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ DEBUG_INFO("send %zu bytes\n", mbuf_get_left(mb)); | |
+ | |
+ return tcp_send_helper(tc->tcp, mb, tc->th); | |
+} | |
+ | |
+ | |
+static void dtls_data_recv_handler(uint8_t *data, size_t datalen, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ if (!tc->mb) | |
+ tc->mb = mbuf_alloc(datalen); | |
+ | |
+ tc->mb->pos = tc->mb->end; | |
+ mbuf_write_mem(tc->mb, data, datalen); | |
+} | |
+ | |
+ | |
+static void dtls_sess_close_handler(int err, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ tc->up = false; | |
+ tc->closed = true; | |
+ | |
+ tc->err = err; | |
+} | |
+ | |
+ | |
+static int resolve_color(enum tls_trace_flags flag) | |
+{ | |
+ switch (flag) { | |
+ | |
+ case TLS_TRACE_RECORD: return 37; /* white */ | |
+ case TLS_TRACE_CHANGE_CIPHER_SPEC: return 35; /* magenta */ | |
+ case TLS_TRACE_ALERT: return 31; /* red */ | |
+ case TLS_TRACE_HANDSHAKE: return 34; /* blue */ | |
+ case TLS_TRACE_APPLICATION_DATA: return 33; /* yellow */ | |
+ default: return 37; | |
+ } | |
+} | |
+ | |
+ | |
+static void tls_trace_handler(enum tls_trace_flags flag, const char *msg, | |
+ void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ re_printf("[" "\x1b[%dm" "%-18s" "\x1b[;m" "]" " %s:" " " "%s" | |
+ , | |
+ resolve_color(flag), | |
+ tls_trace_name(flag), | |
+ tc->active ? "Client" : "Server", | |
+ msg); | |
+} | |
+ | |
+ | |
+static int tls_connect(struct tls_conn *tc) | |
+{ | |
+ struct tls *tls = tc->tls; | |
+ int err = 0; | |
+ | |
+ if (!tc->ssl) { | |
+ | |
+ err = tls_session_alloc(&tc->ssl, tls, | |
+ TLS_CLIENT, | |
+ tls->version , | |
+ tls->suitev, tls->suitec, | |
+ dtls_send_handler, | |
+ NULL, | |
+ dtls_data_recv_handler, | |
+ dtls_sess_close_handler, tc); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (TRACE) { | |
+ tls_set_trace(tc->ssl, TLS_TRACE_ALL, | |
+ tls_trace_handler); | |
+ } | |
+ | |
+ if (tc->tls->cert) | |
+ tls_session_set_certificate2(tc->ssl, tc->tls->cert); | |
+ | |
+ err = tls_session_start(tc->ssl); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int tls_accept(struct tls_conn *tc) | |
+{ | |
+ struct tls *tls = tc->tls; | |
+ int err = 0; | |
+ | |
+ if (!tc->ssl) { | |
+ | |
+ err = tls_session_alloc(&tc->ssl, tls, | |
+ TLS_SERVER, | |
+ tls->version , | |
+ tls->suitev, tls->suitec, | |
+ dtls_send_handler, | |
+ NULL, | |
+ dtls_data_recv_handler, | |
+ dtls_sess_close_handler, tc); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (TRACE) { | |
+ tls_set_trace(tc->ssl, TLS_TRACE_ALL, | |
+ tls_trace_handler); | |
+ } | |
+ | |
+ if (tc->tls->cert) | |
+ tls_session_set_certificate2(tc->ssl, tc->tls->cert); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static bool estab_handler(int *err, bool active, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ DEBUG_INFO("tcp established (active=%u)\n", active); | |
+ | |
+ if (!active) | |
+ return true; | |
+ | |
+ tc->active = true; | |
+ *err = tls_connect(tc); | |
+ | |
+ return true; | |
+} | |
+ | |
+ | |
+static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ DEBUG_INFO("[up=%d] recv %zu bytes\n", tc->up, mbuf_get_left(mb)); | |
+ | |
+ if (!tc->up) { | |
+ | |
+ if (tc->active) { | |
+ *err = tls_connect(tc); | |
+ } | |
+ else { | |
+ *err = tls_accept(tc); | |
+ } | |
+ } | |
+ | |
+ /* feed SSL data to the BIO */ | |
+ tls_session_recvtcp(tc->ssl, mb); | |
+ | |
+ if (tc->closed) { | |
+ *err = tc->err; | |
+ return true; | |
+ } | |
+ | |
+ if (!tc->up) { | |
+ | |
+ /* TLS connection is established */ | |
+ | |
+ if (!tls_session_is_estab(tc->ssl)) | |
+ return true; | |
+ | |
+ *estab = true; | |
+ tc->up = true; | |
+ } | |
+ | |
+ mbuf_set_pos(mb, 0); | |
+ | |
+ if (mbuf_get_space(mb) < 4096) { | |
+ *err = mbuf_resize(mb, mb->size + 8192); | |
+ if (*err) | |
+ return true; | |
+ } | |
+ | |
+ if (tc->mb) { | |
+ | |
+ mbuf_write_mem(mb, tc->mb->buf, tc->mb->end); | |
+ | |
+ tc->mb = mem_deref(tc->mb); | |
+ } | |
+ | |
+ mbuf_set_end(mb, mb->pos); | |
+ mbuf_set_pos(mb, 0); | |
+ | |
+ return false; | |
+} | |
+ | |
+ | |
+static bool send_handler(int *err, struct mbuf *mb, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ *err = tls_session_send_data(tc->ssl, | |
+ mbuf_buf(mb), mbuf_get_left(mb)); | |
+ | |
+ return true; | |
+} | |
+ | |
+ | |
+int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp, | |
+ int layer) | |
+{ | |
+ struct tls_conn *tc; | |
+ int err; | |
+ | |
+ if (!ptc || !tls || !tcp) | |
+ return EINVAL; | |
+ | |
+ tc = mem_zalloc(sizeof(*tc), destructor); | |
+ if (!tc) | |
+ return ENOMEM; | |
+ | |
+ err = tcp_register_helper(&tc->th, tcp, layer, estab_handler, | |
+ send_handler, recv_handler, tc); | |
+ if (err) | |
+ goto out; | |
+ | |
+ tc->tcp = mem_ref(tcp); | |
+ tc->tls = mem_ref(tls); | |
+ | |
+ err = 0; | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(tc); | |
+ else | |
+ *ptc = tc; | |
+ | |
+ return err; | |
+} | |
diff --git a/src/tls/re/tls_udp.c b/src/tls/re/tls_udp.c | |
new file mode 100644 | |
index 0000000..325af7b | |
--- /dev/null | |
+++ b/src/tls/re/tls_udp.c | |
@@ -0,0 +1,484 @@ | |
+/** | |
+ * @file tls_udp.c TLS with UDP-transport | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_hash.h> | |
+#include <re_sa.h> | |
+#include <re_srtp.h> | |
+#include <re_udp.h> | |
+#include <re_tmr.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "dtls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+enum { | |
+ MTU_DEFAULT = 1400, | |
+ MTU_FALLBACK = 548, | |
+}; | |
+ | |
+ | |
+struct dtls_sock { | |
+ struct sa peer; | |
+ struct udp_helper *uh; | |
+ struct udp_sock *us; | |
+ struct hash *ht; | |
+ struct mbuf *mb; | |
+ dtls_conn_h *connh; | |
+ void *arg; | |
+ size_t mtu; | |
+}; | |
+ | |
+ | |
+/* NOTE: shadow struct defined in tls_*.c */ | |
+struct tls_conn { | |
+ struct tls_session *ssl; /* inheritance */ | |
+ struct tls *tls; /* inheritance */ | |
+ | |
+ struct sa peer; | |
+ struct le he; | |
+ struct dtls_sock *sock; | |
+ dtls_estab_h *estabh; | |
+ dtls_recv_h *recvh; | |
+ dtls_close_h *closeh; | |
+ void *arg; | |
+ bool active; | |
+ bool up; | |
+ bool closed; | |
+}; | |
+ | |
+ | |
+static void tls_close(struct tls_conn *tc) | |
+{ | |
+ if (!tc->ssl) | |
+ return; | |
+ | |
+ tls_session_shutdown(tc->ssl); | |
+ tc->ssl = mem_deref(tc->ssl); | |
+} | |
+ | |
+ | |
+static void conn_destructor(void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ hash_unlink(&tc->he); | |
+ tls_close(tc); | |
+ mem_deref(tc->sock); | |
+ mem_deref(tc->tls); | |
+} | |
+ | |
+ | |
+static void conn_close(struct tls_conn *tc, int err) | |
+{ | |
+ tc->closed = true; | |
+ tls_close(tc); | |
+ tc->up = false; | |
+ | |
+ if (tc->closeh) | |
+ tc->closeh(err, tc->arg); | |
+} | |
+ | |
+ | |
+static int dtls_send_handler(struct mbuf *mb, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ return udp_send_helper(tc->sock->us, &tc->peer, mb, tc->sock->uh); | |
+} | |
+ | |
+ | |
+static void dtls_data_recv_handler(uint8_t *data, size_t datalen, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ struct mbuf *mb = mbuf_alloc(datalen); | |
+ | |
+ mbuf_write_mem(mb, data, datalen); | |
+ mb->pos = 0; | |
+ | |
+ if (tc->recvh) | |
+ tc->recvh(mb, tc->arg); | |
+ | |
+ mem_deref(mb); | |
+} | |
+ | |
+ | |
+static void dtls_sess_close_handler(int err, void *arg) | |
+{ | |
+ struct tls_conn *tc = arg; | |
+ | |
+ re_printf("### closed (%m) ###\n", err); | |
+ | |
+ conn_close(tc, err); | |
+} | |
+ | |
+ | |
+static int tls_connect(struct tls_conn *tc) | |
+{ | |
+ struct tls *tls = tc->tls; | |
+ int err = 0; | |
+ | |
+ if (!tc->ssl) { | |
+ | |
+ err = tls_session_alloc(&tc->ssl, tls, | |
+ TLS_CLIENT, | |
+ tls->version , | |
+ tls->suitev, tls->suitec, | |
+ dtls_send_handler, | |
+ NULL, | |
+ dtls_data_recv_handler, | |
+ dtls_sess_close_handler, tc); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (tc->tls->cert) | |
+ tls_session_set_certificate2(tc->ssl, tc->tls->cert); | |
+ | |
+ err = tls_session_start(tc->ssl); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static int tls_accept(struct tls_conn *tc) | |
+{ | |
+ struct tls *tls = tc->tls; | |
+ int err = 0; | |
+ | |
+ if (!tc->ssl) { | |
+ | |
+ err = tls_session_alloc(&tc->ssl, tls, | |
+ TLS_SERVER, | |
+ tls->version , | |
+ tls->suitev, tls->suitec, | |
+ dtls_send_handler, | |
+ NULL, | |
+ dtls_data_recv_handler, | |
+ dtls_sess_close_handler, tc); | |
+ if (err) | |
+ return err; | |
+ | |
+ if (tc->tls->cert) | |
+ tls_session_set_certificate2(tc->ssl, tc->tls->cert); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+static void conn_recv(struct tls_conn *tc, struct mbuf *mb) | |
+{ | |
+ int err; | |
+ | |
+ if (!tc->up) { | |
+ | |
+ if (tc->active) { | |
+ err = tls_connect(tc); | |
+ } | |
+ else { | |
+ err = tls_accept(tc); | |
+ } | |
+ | |
+ if (err) { | |
+ conn_close(tc, err); | |
+ return; | |
+ } | |
+ } | |
+ | |
+ tls_session_recvudp(tc->ssl, mb); | |
+ | |
+ if (tc->closed) | |
+ return; | |
+ | |
+ if (!tc->up) { | |
+ | |
+ DEBUG_INFO("%s: state: up=%d estab=%d\n", | |
+ tc->active ? "client" : "server", | |
+ tc->up, | |
+ tls_session_is_estab(tc->ssl)); | |
+ | |
+ /* TLS connection is established */ | |
+ if (!tls_session_is_estab(tc->ssl)) | |
+ return; | |
+ | |
+ tc->up = true; | |
+ | |
+ if (tc->estabh) { | |
+ uint32_t nrefs; | |
+ | |
+ mem_ref(tc); | |
+ | |
+ tc->estabh(tc->arg); | |
+ | |
+ nrefs = mem_nrefs(tc); | |
+ mem_deref(tc); | |
+ | |
+ /* check if connection was deref'd from handler */ | |
+ if (nrefs == 1) | |
+ return; | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+static int conn_alloc(struct tls_conn **ptc, struct tls *tls, | |
+ struct dtls_sock *sock, const struct sa *peer, | |
+ dtls_estab_h *estabh, dtls_recv_h *recvh, | |
+ dtls_close_h *closeh, void *arg) | |
+{ | |
+ struct tls_conn *tc; | |
+ int err = 0; | |
+ | |
+ tc = mem_zalloc(sizeof(*tc), conn_destructor); | |
+ if (!tc) | |
+ return ENOMEM; | |
+ | |
+ hash_append(sock->ht, sa_hash(peer, SA_ALL), &tc->he, tc); | |
+ | |
+ tc->sock = mem_ref(sock); | |
+ tc->tls = mem_ref(tls); | |
+ tc->peer = *peer; | |
+ tc->estabh = estabh; | |
+ tc->recvh = recvh; | |
+ tc->closeh = closeh; | |
+ tc->arg = arg; | |
+ | |
+ if (err) | |
+ mem_deref(tc); | |
+ else | |
+ *ptc = tc; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int dtls_connect(struct tls_conn **ptc, struct tls *tls, | |
+ struct dtls_sock *sock, const struct sa *peer, | |
+ dtls_estab_h *estabh, dtls_recv_h *recvh, | |
+ dtls_close_h *closeh, void *arg) | |
+{ | |
+ struct tls_conn *tc; | |
+ int err; | |
+ | |
+ if (!ptc || !tls || !sock || !peer) | |
+ return EINVAL; | |
+ | |
+ err = conn_alloc(&tc, tls, sock, peer, estabh, recvh, closeh, arg); | |
+ if (err) | |
+ return err; | |
+ | |
+ tc->active = true; | |
+ | |
+ err = tls_connect(tc); | |
+ if (err) | |
+ goto out; | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(tc); | |
+ else | |
+ *ptc = tc; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int dtls_accept(struct tls_conn **ptc, struct tls *tls, | |
+ struct dtls_sock *sock, | |
+ dtls_estab_h *estabh, dtls_recv_h *recvh, | |
+ dtls_close_h *closeh, void *arg) | |
+{ | |
+ struct tls_conn *tc; | |
+ int err; | |
+ | |
+ if (!ptc || !tls || !sock || !sock->mb) | |
+ return EINVAL; | |
+ | |
+ err = conn_alloc(&tc, tls, sock, &sock->peer, estabh, recvh, closeh, | |
+ arg); | |
+ if (err) | |
+ return err; | |
+ | |
+ tc->active = false; | |
+ | |
+#if 1 | |
+ | |
+ err = tls_accept(tc); | |
+ if (err) | |
+ goto out; | |
+ | |
+ tls_session_recvudp(tc->ssl, sock->mb); | |
+#endif | |
+ | |
+ sock->mb = mem_deref(sock->mb); | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(tc); | |
+ else | |
+ *ptc = tc; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int dtls_send(struct tls_conn *tc, struct mbuf *mb) | |
+{ | |
+ if (!tc || !mb) | |
+ return EINVAL; | |
+ | |
+ if (!tc->up || !tc->ssl) | |
+ return ENOTCONN; | |
+ | |
+ return tls_session_send_data(tc->ssl, | |
+ mbuf_buf(mb), mbuf_get_left(mb)); | |
+} | |
+ | |
+ | |
+void dtls_set_handlers(struct tls_conn *tc, dtls_estab_h *estabh, | |
+ dtls_recv_h *recvh, dtls_close_h *closeh, void *arg) | |
+{ | |
+ if (!tc) | |
+ return; | |
+ | |
+ tc->estabh = estabh; | |
+ tc->recvh = recvh; | |
+ tc->closeh = closeh; | |
+ tc->arg = arg; | |
+} | |
+ | |
+ | |
+static void sock_destructor(void *arg) | |
+{ | |
+ struct dtls_sock *sock = arg; | |
+ | |
+ hash_clear(sock->ht); | |
+ mem_deref(sock->uh); | |
+ mem_deref(sock->us); | |
+ mem_deref(sock->ht); | |
+ mem_deref(sock->mb); | |
+} | |
+ | |
+ | |
+static bool cmp_handler(struct le *le, void *arg) | |
+{ | |
+ struct tls_conn *tc = le->data; | |
+ | |
+ return sa_cmp(&tc->peer, arg, SA_ALL); | |
+} | |
+ | |
+ | |
+static struct tls_conn *conn_lookup(struct dtls_sock *sock, | |
+ const struct sa *peer) | |
+{ | |
+ return list_ledata(hash_lookup(sock->ht, sa_hash(peer, SA_ALL), | |
+ cmp_handler, (void *)peer)); | |
+} | |
+ | |
+ | |
+static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg) | |
+{ | |
+ struct dtls_sock *sock = arg; | |
+ struct tls_conn *tc; | |
+ uint8_t b; | |
+ | |
+ DEBUG_INFO("recv %zu bytes from %J\n", | |
+ mbuf_get_left(mb), src); | |
+ | |
+ if (mbuf_get_left(mb) < 1) | |
+ return false; | |
+ | |
+ b = mb->buf[mb->pos]; | |
+ if (b < 20 || b > 63) | |
+ return false; | |
+ | |
+ tc = conn_lookup(sock, src); | |
+ if (tc) { | |
+ conn_recv(tc, mb); | |
+ return true; | |
+ } | |
+ | |
+ if (sock->connh) { | |
+ | |
+ mem_deref(sock->mb); | |
+ sock->mb = mem_ref(mb); | |
+ sock->peer = *src; | |
+ | |
+ sock->connh(src, sock->arg); | |
+ } | |
+ | |
+ return true; | |
+} | |
+ | |
+ | |
+int dtls_listen(struct dtls_sock **sockp, const struct sa *laddr, | |
+ struct udp_sock *us, uint32_t htsize, int layer, | |
+ dtls_conn_h *connh, void *arg) | |
+{ | |
+ struct dtls_sock *sock; | |
+ int err; | |
+ | |
+ if (!sockp) | |
+ return EINVAL; | |
+ | |
+ sock = mem_zalloc(sizeof(*sock), sock_destructor); | |
+ if (!sock) | |
+ return ENOMEM; | |
+ | |
+ if (us) { | |
+ sock->us = mem_ref(us); | |
+ } | |
+ else { | |
+ err = udp_listen(&sock->us, laddr, NULL, NULL); | |
+ if (err) | |
+ goto out; | |
+ } | |
+ | |
+ err = udp_register_helper(&sock->uh, sock->us, layer, | |
+ NULL, recv_handler, sock); | |
+ if (err) | |
+ goto out; | |
+ | |
+ err = hash_alloc(&sock->ht, hash_valid_size(htsize)); | |
+ if (err) | |
+ goto out; | |
+ | |
+ sock->mtu = MTU_DEFAULT; | |
+ sock->connh = connh; | |
+ sock->arg = arg; | |
+ | |
+ out: | |
+ if (err) | |
+ mem_deref(sock); | |
+ else | |
+ *sockp = sock; | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+struct udp_sock *dtls_udp_sock(struct dtls_sock *sock) | |
+{ | |
+ return sock ? sock->us : NULL; | |
+} | |
+ | |
+ | |
+void dtls_set_mtu(struct dtls_sock *sock, size_t mtu) | |
+{ | |
+ if (!sock) | |
+ return; | |
+ | |
+ sock->mtu = mtu; | |
+} | |
diff --git a/src/tls/re/trace.c b/src/tls/re/trace.c | |
new file mode 100644 | |
index 0000000..e388c30 | |
--- /dev/null | |
+++ b/src/tls/re/trace.c | |
@@ -0,0 +1,69 @@ | |
+/** | |
+ * @file trace.c TLS message tracing | |
+ * | |
+ * Copyright (C) 2010 - 2017 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_list.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+void tls_trace(struct tls_session *sess, enum tls_trace_flags flags, | |
+ const char *fmt, ...) | |
+{ | |
+ char buf[1024]; | |
+ va_list ap; | |
+ int r; | |
+ | |
+ if (!sess || !fmt) | |
+ return; | |
+ | |
+ if (!(sess->trace_flags & flags)) | |
+ return; | |
+ if (!sess->traceh) | |
+ return; | |
+ | |
+ va_start(ap, fmt); | |
+ r = re_vsnprintf(buf, sizeof(buf), fmt, ap); | |
+ va_end(ap); | |
+ | |
+ if (r < 0) | |
+ return; | |
+ | |
+ sess->traceh(flags, buf, sess->arg); | |
+} | |
+ | |
+ | |
+void tls_set_trace(struct tls_session *sess, enum tls_trace_flags flags, | |
+ tls_trace_h *traceh) | |
+{ | |
+ if (!sess) | |
+ return; | |
+ | |
+ sess->trace_flags = flags; | |
+ sess->traceh = traceh; | |
+} | |
+ | |
+ | |
+const char *tls_trace_name(enum tls_trace_flags flag) | |
+{ | |
+ switch (flag) { | |
+ | |
+ case TLS_TRACE_RECORD: return "RECORD"; | |
+ case TLS_TRACE_CHANGE_CIPHER_SPEC: return "CHANGE_CIPHER_SPEC"; | |
+ case TLS_TRACE_ALERT: return "ALERT"; | |
+ case TLS_TRACE_HANDSHAKE: return "HANDSHAKE"; | |
+ case TLS_TRACE_APPLICATION_DATA: return "APPLICATION_DATA"; | |
+ default: return "???"; | |
+ } | |
+} | |
diff --git a/src/tls/re/util.c b/src/tls/re/util.c | |
new file mode 100644 | |
index 0000000..c879539 | |
--- /dev/null | |
+++ b/src/tls/re/util.c | |
@@ -0,0 +1,34 @@ | |
+/** | |
+ * @file util.c TLS utilities | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <string.h> | |
+#include <assert.h> /* XXX: temporary during development */ | |
+#include <re_types.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+void mem_cpy(uint8_t *dst, size_t dst_sz, | |
+ const uint8_t *src, size_t src_sz) | |
+{ | |
+ if (src_sz > dst_sz) { | |
+ DEBUG_WARNING("mem_cpy: dst buf too small (%zu > %zu)\n", | |
+ src_sz, dst_sz); | |
+ assert(0); | |
+ return; | |
+ } | |
+ | |
+ memcpy(dst, src, src_sz); | |
+} | |
diff --git a/src/tls/re/vector.c b/src/tls/re/vector.c | |
new file mode 100644 | |
index 0000000..772b822 | |
--- /dev/null | |
+++ b/src/tls/re/vector.c | |
@@ -0,0 +1,139 @@ | |
+/** | |
+ * @file vector.c TLS vector | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mem.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_net.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#define DEBUG_MODULE "tls" | |
+#define DEBUG_LEVEL 5 | |
+#include <re_dbg.h> | |
+ | |
+ | |
+#define VECTOR_MAX_LENGTH (1U<<24) | |
+ | |
+ | |
+int tls_vector_init(struct tls_vector *vect, | |
+ const uint8_t *data, size_t len) | |
+{ | |
+ if (!vect) | |
+ return EINVAL; | |
+ | |
+ vect->bytes = len; | |
+ | |
+ if (data && len) { | |
+ | |
+ vect->data = mem_alloc(len, NULL); | |
+ if (!vect->data) | |
+ return ENOMEM; | |
+ | |
+ mem_cpy(vect->data, vect->bytes, data, len); | |
+ } | |
+ else { | |
+ vect->data = NULL; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+int tls_vector_encode(struct mbuf *mb, const struct tls_vector *vect, | |
+ unsigned hdr_bytes) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (hdr_bytes == 1) | |
+ err = mbuf_write_u8(mb, vect->bytes); | |
+ else if (hdr_bytes == 2) | |
+ err = mbuf_write_u16(mb, htons(vect->bytes)); | |
+ else if (hdr_bytes == 3) | |
+ err = mbuf_write_u24_hton(mb, (uint32_t)vect->bytes); | |
+ else { | |
+ DEBUG_WARNING("vector: invalid hdr_bytes=%zu\n", hdr_bytes); | |
+ return EINVAL; | |
+ } | |
+ | |
+ if (vect->bytes) { | |
+ err |= mbuf_write_mem(mb, vect->data, vect->bytes); | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_vector_decode(struct tls_vector *vect, unsigned hdr_bytes, | |
+ struct mbuf *mb) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (hdr_bytes > mbuf_get_left(mb)) | |
+ return ENODATA; | |
+ | |
+ if (hdr_bytes == 1) | |
+ vect->bytes = mbuf_read_u8(mb); | |
+ else if (hdr_bytes == 2) | |
+ vect->bytes = ntohs(mbuf_read_u16(mb)); | |
+ else if (hdr_bytes == 3) | |
+ vect->bytes = mbuf_read_u24_ntoh(mb); | |
+ else | |
+ return EINVAL; | |
+ | |
+ if (vect->bytes) { | |
+ vect->data = mem_alloc(vect->bytes, NULL); | |
+ if (!vect->data) | |
+ return ENOMEM; | |
+ | |
+ err = mbuf_read_mem(mb, vect->data, vect->bytes); | |
+ } | |
+ else { | |
+ vect->data = NULL; | |
+ } | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+int tls_vector_decode_hdr(struct tls_vector *vect, unsigned hdr_bytes, | |
+ struct mbuf *mb) | |
+{ | |
+ int err = 0; | |
+ | |
+ if (mbuf_get_left(mb) < hdr_bytes) | |
+ return ENODATA; | |
+ | |
+ if (hdr_bytes == 1) | |
+ vect->bytes = mbuf_read_u8(mb); | |
+ else if (hdr_bytes == 2) | |
+ vect->bytes = ntohs(mbuf_read_u16(mb)); | |
+ else if (hdr_bytes == 3) | |
+ vect->bytes = mbuf_read_u24_ntoh(mb); | |
+ else | |
+ return EINVAL; | |
+ | |
+ if (mbuf_get_left(mb) < vect->bytes) | |
+ return ENODATA; | |
+ | |
+ vect->data = mbuf_buf(mb); | |
+ | |
+ return err; | |
+} | |
+ | |
+ | |
+void tls_vector_reset(struct tls_vector *vect) | |
+{ | |
+ if (!vect) | |
+ return; | |
+ | |
+ vect->bytes = 0; | |
+ vect->data = mem_deref(vect->data); | |
+} | |
diff --git a/src/tls/re/version.c b/src/tls/re/version.c | |
new file mode 100644 | |
index 0000000..1ec7738 | |
--- /dev/null | |
+++ b/src/tls/re/version.c | |
@@ -0,0 +1,52 @@ | |
+/** | |
+ * @file version.c TLS version | |
+ * | |
+ * Copyright (C) 2010 - 2016 Creytiv.com | |
+ */ | |
+ | |
+#include <re_types.h> | |
+#include <re_fmt.h> | |
+#include <re_mbuf.h> | |
+#include <re_list.h> | |
+#include <re_srtp.h> | |
+#include <re_tls.h> | |
+#include "tls.h" | |
+ | |
+ | |
+#undef TLS1_0_VERSION | |
+#define TLS1_0_VERSION (enum tls_version)(0x0301) | |
+ | |
+ | |
+bool tls_version_is_dtls(enum tls_version ver) | |
+{ | |
+ switch (ver) { | |
+ | |
+ case DTLS1_2_VERSION: return true; | |
+ default: return false; | |
+ } | |
+} | |
+ | |
+ | |
+const char *tls_version_name(enum tls_version ver) | |
+{ | |
+ switch (ver) { | |
+ | |
+ case TLS1_2_VERSION: return "TLSv1.2"; | |
+ case DTLS1_2_VERSION: return "DTLSv1.2"; | |
+ default: return "???"; | |
+ } | |
+} | |
+ | |
+ | |
+bool tls_version_isvalid(enum tls_version ver) | |
+{ | |
+ switch (ver) { | |
+ | |
+ case TLS1_2_VERSION: return true; | |
+ case DTLS1_2_VERSION: return true; | |
+ default: | |
+ if (ver == TLS1_0_VERSION) | |
+ return true; | |
+ return false; | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment