Skip to content

Instantly share code, notes, and snippets.

@alfredh
Created August 21, 2020 16:39
Show Gist options
  • Save alfredh/374935864adcb3e27b8cb36dfd4d0d0c to your computer and use it in GitHub Desktop.
Save alfredh/374935864adcb3e27b8cb36dfd4d0d0c to your computer and use it in GitHub Desktop.
TLS stack for libre
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, &copy);
+
+ 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