diff --git a/GNUmakefile b/GNUmakefile index 4357f03ec..01ba18b66 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -191,6 +191,7 @@ MIN_VER = 10.15 OBJS := $(OBJS) \ src/hid/hid.o \ src/unix/apple/image.o \ + src/unix/apple/macosx/dtls.o \ src/unix/apple/macosx/hid.o \ src/unix/apple/macosx/gfx/metal-ctx.o @@ -202,6 +203,10 @@ ifeq ($(ARCH), x86_64) FLAGS := $(FLAGS) -maes -mpclmul endif +# macOS overrides this with a LibreSSL backed implementation above +OBJS := $(OBJS) \ + src/unix/apple/dtls.o + endif OBJS := $(OBJS) \ @@ -209,7 +214,6 @@ OBJS := $(OBJS) \ src/unix/apple/audio.o \ src/unix/apple/base64.o \ src/unix/apple/crypto.o \ - src/unix/apple/dtls.o \ src/unix/apple/request.o \ src/unix/apple/webview.o \ src/unix/apple/ws.o \ diff --git a/src/matoya.h b/src/matoya.h index 6643f062b..7d4f7a219 100644 --- a/src/matoya.h +++ b/src/matoya.h @@ -3553,7 +3553,7 @@ MTY_GetJNIEnv(void); //- #mbrief DTLS protocol wrapper. //- #mdetails This module performs no IO and acts as a DTLS engine that requires you //- to feed it input and in turn will output data suitable for sending over a network. -//- #msupport Windows Linux +//- #msupport Windows macOS Linux #define MTY_FINGERPRINT_MAX 112 ///< Maximum size of the string set by MTY_CertGetFingerprint. @@ -3573,13 +3573,13 @@ typedef bool (*MTY_DTLSWriteFunc)(const void *buf, size_t size, void *opaque); /// negotiation. /// @returns On failure, NULL is returned. Call MTY_GetLog for details.\n\n /// The returned MTY_Cert must be destroyed with MTY_CertDestroy. -//- #support Windows Linux +//- #support Windows macOS Linux MTY_EXPORT MTY_Cert * MTY_CertCreate(void); /// @brief Destroy an MTY_Cert. /// @param cert Passed by reference and set to NULL after being destroyed. -//- #support Windows Linux +//- #support Windows macOS Linux MTY_EXPORT void MTY_CertDestroy(MTY_Cert **cert); @@ -3589,7 +3589,7 @@ MTY_CertDestroy(MTY_Cert **cert); /// @param fingerprint Output buffer to receive the fingerprint. This buffer can be /// of size MTY_FINGERPRINT_MAX. /// @param size Size in bytes of `fingerprint`. -//- #support Windows Linux +//- #support Windows macOS Linux MTY_EXPORT void MTY_CertGetFingerprint(MTY_Cert *ctx, char *fingerprint, size_t size); diff --git a/src/unix/apple/macosx/dl/libssl.h b/src/unix/apple/macosx/dl/libssl.h new file mode 100644 index 000000000..6c8d24b6c --- /dev/null +++ b/src/unix/apple/macosx/dl/libssl.h @@ -0,0 +1,242 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT License was not distributed with this file, +// You can obtain one at https://spdx.org/licenses/MIT.html. + +#pragma once + +#include +#include + +#include "sym.h" + + +// Interface + +#define SSL_ERROR_WANT_READ 2 +#define SSL_ERROR_WANT_WRITE 3 +#define SSL_ERROR_ZERO_RETURN 6 + +#define SSL_CTRL_SET_MTU 17 +#define SSL_CTRL_OPTIONS 32 + +#define SSL_VERIFY_PEER 0x01 +#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 +#define SSL_OP_NO_QUERY_MTU 0x00001000U +#define SSL_OP_NO_TICKET 0x00004000U +#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 0x00010000U + +#define DTLS1_2_VERSION 0xFEFD + +#define MBSTRING_FLAG 0x1000 +#define MBSTRING_ASC (MBSTRING_FLAG | 1) +#define RSA_F4 0x10001L +#define EVP_MAX_MD_SIZE 64 + +typedef struct rsa_st RSA; +typedef struct bio_st BIO; +typedef struct bignum_st BIGNUM; +typedef struct x509_st X509; +typedef struct X509_name_st X509_NAME; +typedef struct x509_store_ctx_st X509_STORE_CTX; +typedef struct evp_pkey_st EVP_PKEY; +typedef struct bn_gencb_st BN_GENCB; +typedef struct asn1_string_st ASN1_INTEGER; +typedef struct asn1_string_st ASN1_TIME; +typedef struct evp_md_st EVP_MD; +typedef struct bio_method_st BIO_METHOD; + +typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_method_st SSL_METHOD; +typedef struct ssl_st SSL; + +typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx); + +static SSL *(*SSL_new)(SSL_CTX *ctx); +static void (*SSL_free)(SSL *ssl); +static int (*SSL_read)(SSL *ssl, void *buf, int num); +static int (*SSL_write)(SSL *ssl, const void *buf, int num); +static void (*SSL_set_verify)(SSL *s, int mode, SSL_verify_cb callback); +static int (*SSL_get_error)(const SSL *s, int ret_code); +static long (*SSL_ctrl)(SSL *ssl, int cmd, long larg, void *parg); +static void (*SSL_set_bio)(SSL *s, BIO *rbio, BIO *wbio); +static void (*SSL_set_connect_state)(SSL *s); +static int (*SSL_do_handshake)(SSL *s); +static int (*SSL_use_certificate)(SSL *ssl, X509 *x); +static int (*SSL_use_RSAPrivateKey)(SSL *ssl, RSA *rsa); +static X509 *(*SSL_get_peer_certificate)(const SSL *s); + +static const SSL_METHOD *(*DTLS_method)(void); +static SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *meth); +static void (*SSL_CTX_free)(SSL_CTX *ctx); +static int (*SSL_CTX_set_max_proto_version)(SSL_CTX *ctx, uint16_t version); + +static BIO *(*BIO_new)(const BIO_METHOD *type); +static int (*BIO_free)(BIO *a); +static const BIO_METHOD *(*BIO_s_mem)(void); +static int (*BIO_write)(BIO *b, const void *data, int len); +static size_t (*BIO_ctrl_pending)(BIO *b); +static int (*BIO_read)(BIO *b, void *data, int len); + +static void (*X509_free)(X509 *a); +static X509 *(*X509_new)(void); +static int (*X509_set_pubkey)(X509 *x, EVP_PKEY *pkey); +static int (*X509_sign)(X509 *x, EVP_PKEY *pkey, const EVP_MD *md); +static int (*X509_digest)(const X509 *data, const EVP_MD *type, unsigned char *md, unsigned int *len); +static ASN1_TIME *(*X509_getm_notBefore)(const X509 *x); +static ASN1_TIME *(*X509_getm_notAfter)(const X509 *x); +static int (*X509_set_version)(X509 *x, long version); +static int (*X509_set_issuer_name)(X509 *x, X509_NAME *name); +static X509_NAME *(*X509_get_subject_name)(const X509 *a); +static ASN1_INTEGER *(*X509_get_serialNumber)(X509 *x); +static ASN1_TIME *(*X509_gmtime_adj)(ASN1_TIME *s, long adj); +static int (*X509_NAME_add_entry_by_txt)(X509_NAME *name, const char *field, int type, + const unsigned char *bytes, int len, int loc, int set); + +static BIGNUM *(*BN_new)(void); +static void (*BN_free)(BIGNUM *a); +static int (*BN_set_word)(BIGNUM *a, unsigned long long w); + +static EVP_PKEY *(*EVP_PKEY_new)(void); +static void (*EVP_PKEY_free)(EVP_PKEY *pkey); +static const EVP_MD *(*EVP_sha256)(void); +static int (*EVP_PKEY_set1_RSA)(EVP_PKEY *pkey, RSA *key); + +static RSA *(*RSA_new)(void); +static void (*RSA_free)(RSA *r); +static int (*RSA_generate_key_ex)(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); + +static int (*ASN1_INTEGER_set)(ASN1_INTEGER *a, long v); + + +// Runtime open + +// macOS ships LibreSSL as versioned dylibs inside the dyld shared cache. The unversioned +// libssl.dylib deliberately aborts the process ("is loading libcrypto in an unsafe way"), +// so only the versioned soname may be opened. Several versions are present side by side +// for binary compatibility, but the LibreSSL 2.x ones predate DTLS_method, so probe from +// the newest soname down and take the first that can actually speak DTLS. +#define LIBSSL_SO_MIN 48 +#define LIBSSL_SO_MAX 80 + +static MTY_Atomic32 LIBSSL_LOCK; +static MTY_SO *LIBSSL_SO; +static bool LIBSSL_INIT; + +static void libssl_global_destroy_lockfree(void) +{ + MTY_SOUnload(&LIBSSL_SO); + LIBSSL_INIT = false; +} + +static void __attribute__((destructor)) libssl_global_destroy(void) +{ + MTY_GlobalLock(&LIBSSL_LOCK); + + libssl_global_destroy_lockfree(); + + MTY_GlobalUnlock(&LIBSSL_LOCK); +} + +static MTY_SO *libssl_dlopen(void) +{ + for (int32_t v = LIBSSL_SO_MAX; v >= LIBSSL_SO_MIN; v--) { + char name[32]; + snprintf(name, sizeof(name), "libssl.%d.dylib", v); + + // Probed via dlopen directly rather than MTY_SOLoad to keep the misses out of the log + void *so = dlopen(name, RTLD_NOW | RTLD_LOCAL); + if (!so) + continue; + + // libcrypto symbols resolve through the libssl handle via its dependency + if (dlsym(so, "DTLS_method") && dlsym(so, "X509_new")) + return so; + + dlclose(so); + } + + MTY_Log("'dlopen' failed to find a libssl dylib with DTLS support"); + + return NULL; +} + +static bool libssl_global_init(void) +{ + MTY_GlobalLock(&LIBSSL_LOCK); + + if (!LIBSSL_INIT) { + bool r = true; + LIBSSL_SO = libssl_dlopen(); + + if (!LIBSSL_SO) { + r = false; + goto except; + } + + LOAD_SYM(LIBSSL_SO, SSL_new); + LOAD_SYM(LIBSSL_SO, SSL_free); + LOAD_SYM(LIBSSL_SO, SSL_read); + LOAD_SYM(LIBSSL_SO, SSL_write); + LOAD_SYM(LIBSSL_SO, SSL_set_verify); + LOAD_SYM(LIBSSL_SO, SSL_get_error); + LOAD_SYM(LIBSSL_SO, SSL_ctrl); + LOAD_SYM(LIBSSL_SO, SSL_set_bio); + LOAD_SYM(LIBSSL_SO, SSL_set_connect_state); + LOAD_SYM(LIBSSL_SO, SSL_do_handshake); + LOAD_SYM(LIBSSL_SO, SSL_use_certificate); + LOAD_SYM(LIBSSL_SO, SSL_use_RSAPrivateKey); + LOAD_SYM(LIBSSL_SO, SSL_get_peer_certificate); + + LOAD_SYM(LIBSSL_SO, DTLS_method); + LOAD_SYM(LIBSSL_SO, SSL_CTX_new); + LOAD_SYM(LIBSSL_SO, SSL_CTX_free); + LOAD_SYM(LIBSSL_SO, SSL_CTX_set_max_proto_version); + + LOAD_SYM(LIBSSL_SO, BIO_new); + LOAD_SYM(LIBSSL_SO, BIO_s_mem); + LOAD_SYM(LIBSSL_SO, BIO_write); + LOAD_SYM(LIBSSL_SO, BIO_ctrl_pending); + LOAD_SYM(LIBSSL_SO, BIO_read); + LOAD_SYM(LIBSSL_SO, BIO_free); + + LOAD_SYM(LIBSSL_SO, X509_new); + LOAD_SYM(LIBSSL_SO, X509_free); + LOAD_SYM(LIBSSL_SO, X509_set_pubkey); + LOAD_SYM(LIBSSL_SO, X509_sign); + LOAD_SYM(LIBSSL_SO, X509_digest); + LOAD_SYM(LIBSSL_SO, X509_getm_notBefore); + LOAD_SYM(LIBSSL_SO, X509_getm_notAfter); + LOAD_SYM(LIBSSL_SO, X509_set_version); + LOAD_SYM(LIBSSL_SO, X509_set_issuer_name); + LOAD_SYM(LIBSSL_SO, X509_get_subject_name); + LOAD_SYM(LIBSSL_SO, X509_get_serialNumber); + LOAD_SYM(LIBSSL_SO, X509_gmtime_adj); + LOAD_SYM(LIBSSL_SO, X509_NAME_add_entry_by_txt); + + LOAD_SYM(LIBSSL_SO, RSA_new); + LOAD_SYM(LIBSSL_SO, RSA_free); + LOAD_SYM(LIBSSL_SO, RSA_generate_key_ex); + + LOAD_SYM(LIBSSL_SO, BN_new); + LOAD_SYM(LIBSSL_SO, BN_free); + LOAD_SYM(LIBSSL_SO, BN_set_word); + + LOAD_SYM(LIBSSL_SO, EVP_PKEY_new); + LOAD_SYM(LIBSSL_SO, EVP_PKEY_free); + LOAD_SYM(LIBSSL_SO, EVP_sha256); + LOAD_SYM(LIBSSL_SO, EVP_PKEY_set1_RSA); + + LOAD_SYM(LIBSSL_SO, ASN1_INTEGER_set); + + except: + + if (!r) + libssl_global_destroy_lockfree(); + + LIBSSL_INIT = r; + } + + MTY_GlobalUnlock(&LIBSSL_LOCK); + + return LIBSSL_INIT; +} diff --git a/src/unix/apple/macosx/dl/sym.h b/src/unix/apple/macosx/dl/sym.h new file mode 100644 index 000000000..d54a9a4c8 --- /dev/null +++ b/src/unix/apple/macosx/dl/sym.h @@ -0,0 +1,14 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT License was not distributed with this file, +// You can obtain one at https://spdx.org/licenses/MIT.html. + +#pragma once + +#include "matoya.h" + +#define LOAD_SYM(so, name) \ + name = MTY_SOGetSymbol(so, #name); \ + if (!name) {r = false; goto except;} + +#define LOAD_SYM_OPT(so, name) \ + name = MTY_SOGetSymbol(so, #name); diff --git a/src/unix/apple/macosx/dtls.c b/src/unix/apple/macosx/dtls.c new file mode 100644 index 000000000..3f3a8946b --- /dev/null +++ b/src/unix/apple/macosx/dtls.c @@ -0,0 +1,352 @@ +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT License was not distributed with this file, +// You can obtain one at https://spdx.org/licenses/MIT.html. + +#include "matoya.h" + +#include +#include +#include + +#include "dl/libssl.h" + +#define DTLS_RSA_BITS 2048 + +struct MTY_Cert { + char cn[64]; + X509 *cert; + RSA *key; +}; + +struct MTY_DTLS { + char *fp; + + SSL *ssl; + SSL_CTX *ctx; + BIO *bio_in; + BIO *bio_out; +}; + + +// Cert + +MTY_Cert *MTY_CertCreate(void) +{ + if (!libssl_global_init()) + return NULL; + + MTY_Cert *cert = MTY_Alloc(1, sizeof(MTY_Cert)); + + BIGNUM *bne = BN_new(); + BN_set_word(bne, RSA_F4); + + cert->key = RSA_new(); + RSA_generate_key_ex(cert->key, DTLS_RSA_BITS, bne, NULL); + BN_free(bne); + + cert->cert = X509_new(); + X509_set_version(cert->cert, 2); + ASN1_INTEGER_set(X509_get_serialNumber(cert->cert), MTY_GetRandomUInt(1000000000, 2000000000)); + + X509_gmtime_adj(X509_getm_notBefore(cert->cert), -24 * 3600); // 1 day ago + X509_gmtime_adj(X509_getm_notAfter(cert->cert), 30 * 24 * 3600); // 30 days out + + uint8_t rand_name[16]; + MTY_GetRandomBytes(rand_name, 16); + MTY_BytesToHex(rand_name, 16, cert->cn, 64); + + X509_NAME *x509_name = X509_get_subject_name(cert->cert); + X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC, (const unsigned char *) cert->cn, -1, -1, 0); + X509_set_issuer_name(cert->cert, x509_name); + + // set1 takes a reference rather than ownership, so the RSA key remains owned by MTY_Cert + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(pkey, cert->key); + X509_set_pubkey(cert->cert, pkey); + X509_sign(cert->cert, pkey, EVP_sha256()); + EVP_PKEY_free(pkey); + + return cert; +} + +void MTY_CertDestroy(MTY_Cert **cert) +{ + if (!cert || !*cert) + return; + + MTY_Cert *ctx = *cert; + + if (ctx->key) + RSA_free(ctx->key); + + if (ctx->cert) + X509_free(ctx->cert); + + MTY_Free(ctx); + *cert = NULL; +} + +static void dtls_x509_to_fingerprint(X509 *cert, char *fingerprint, size_t size) +{ + memset(fingerprint, 0, size); + + uint32_t len = 0; + uint8_t buf[EVP_MAX_MD_SIZE]; + X509_digest(cert, EVP_sha256(), buf, &len); + + MTY_Strcat(fingerprint, size, "sha-256 "); + + for (size_t x = 0; x < len; x++) { + char append[8]; + snprintf(append, 8, "%02X:", buf[x]); + MTY_Strcat(fingerprint, size, append); + } + + // Remove the trailing ':' + fingerprint[strlen(fingerprint) - 1] = '\0'; +} + +void MTY_CertGetFingerprint(MTY_Cert *ctx, char *fingerprint, size_t size) +{ + dtls_x509_to_fingerprint(ctx->cert, fingerprint, size); +} + + +// DTLS + +static int32_t dtls_verify(int32_t ok, X509_STORE_CTX *ctx) +{ + return 1; +} + +MTY_DTLS *MTY_DTLSCreate(MTY_Cert *cert, const char *peerFingerprint, uint32_t mtu) +{ + if (!libssl_global_init()) + return NULL; + + bool r = true; + + MTY_DTLS *ctx = MTY_Alloc(1, sizeof(MTY_DTLS)); + + const SSL_METHOD *method = DTLS_method(); + if (!method) { + MTY_Log("DTLS is unsupported"); + r = false; + goto except; + } + + ctx->ctx = SSL_CTX_new(method); + if (!ctx->ctx) { + MTY_Log("'SSL_CTX_new' failed"); + r = false; + goto except; + } + + // Pin to DTLS 1.2 to match the other platforms and MTY_IsDTLSHandshake's version check + SSL_CTX_set_max_proto_version(ctx->ctx, DTLS1_2_VERSION); + + ctx->ssl = SSL_new(ctx->ctx); + if (!ctx->ssl) { + MTY_Log("'SSL_new' failed"); + r = false; + goto except; + } + + SSL_set_connect_state(ctx->ssl); + + SSL_ctrl(ctx->ssl, SSL_CTRL_OPTIONS, SSL_OP_NO_TICKET | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION, NULL); + SSL_set_verify(ctx->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, dtls_verify); + + // This will tell libssl to create larger datagrams + SSL_ctrl(ctx->ssl, SSL_CTRL_OPTIONS, SSL_OP_NO_QUERY_MTU, NULL); + SSL_ctrl(ctx->ssl, SSL_CTRL_SET_MTU, mtu, NULL); + + if (peerFingerprint) + ctx->fp = MTY_Strdup(peerFingerprint); + + // Use cert if supplied + if (cert) { + SSL_use_certificate(ctx->ssl, cert->cert); + SSL_use_RSAPrivateKey(ctx->ssl, cert->key); + } + + ctx->bio_in = BIO_new(BIO_s_mem()); + ctx->bio_out = BIO_new(BIO_s_mem()); + SSL_set_bio(ctx->ssl, ctx->bio_in, ctx->bio_out); + + except: + + if (!r) + MTY_DTLSDestroy(&ctx); + + return ctx; +} + +void MTY_DTLSDestroy(MTY_DTLS **dtls) +{ + if (!dtls || !*dtls) + return; + + MTY_DTLS *ctx = *dtls; + + if (ctx->ssl) + SSL_free(ctx->ssl); + + if (ctx->ctx) + SSL_CTX_free(ctx->ctx); + + MTY_Free(ctx->fp); + + MTY_Free(ctx); + *dtls = NULL; +} + +static bool dtls_verify_peer_fingerprint(MTY_DTLS *dtls, const char *fingerprint) +{ + X509 *peer_cert = SSL_get_peer_certificate(dtls->ssl); + + if (peer_cert) { + char found[MTY_FINGERPRINT_MAX]; + dtls_x509_to_fingerprint(peer_cert, found, MTY_FINGERPRINT_MAX); + + bool match = !strcmp(found, fingerprint); + + X509_free(peer_cert); + + return match; + } + + return false; +} + +MTY_Async MTY_DTLSHandshake(MTY_DTLS *ctx, const void *buf, size_t size, MTY_DTLSWriteFunc writeFunc, void *opaque) +{ + MTY_Async r = MTY_ASYNC_CONTINUE; + + // If we have incoming data add it to the state machine + if (buf && size > 0) { + int32_t n = BIO_write(ctx->bio_in, buf, (int32_t) size); + + if (n != (int32_t) size) + return MTY_ASYNC_ERROR; + } + + // Poll for response data + while (true) { + int32_t e = SSL_do_handshake(ctx->ssl); + + // Handshake has encountered an error or needs to continue + if (e <= 0) { + int32_t ssle = SSL_get_error(ctx->ssl, e); + + if (e == 0 || (ssle != SSL_ERROR_WANT_READ && ssle != SSL_ERROR_WANT_WRITE)) { + MTY_Log("'SSL_do_handhsake' failed with error %d:%d", e, ssle); + return MTY_ASYNC_ERROR; + } + } + + // Handshake has completed successfully + if (e == 1) + r = MTY_ASYNC_OK; + + // Look for pending data to write even if the handshake has completed successfully + int32_t pending = (int32_t) BIO_ctrl_pending(ctx->bio_out); + + if (pending > 0) { + bool write_ok = false; + void *pbuf = MTY_Alloc(pending, 1); + + if (BIO_read(ctx->bio_out, pbuf, pending) == pending) + write_ok = writeFunc(pbuf, pending, opaque); + + MTY_Free(pbuf); + + if (!write_ok) + return MTY_ASYNC_ERROR; + + // Break when there is no more data available to write + } else { + break; + } + } + + // Verify peer fingerprint on successful handshake if supplied during creation + if (r == MTY_ASYNC_OK && ctx->fp) + return dtls_verify_peer_fingerprint(ctx, ctx->fp) ? MTY_ASYNC_OK : MTY_ASYNC_ERROR; + + return r; +} + +bool MTY_DTLSEncrypt(MTY_DTLS *ctx, const void *in, size_t inSize, void *out, size_t outSize, size_t *written) +{ + // Perform the encryption, outputs to bio_out + int32_t n = SSL_write(ctx->ssl, in, (int32_t) inSize); + + // SSL_write threw an error with our input data + if (n <= 0) { + MTY_Log("'SSL_write' failed with error %d:%d", n, SSL_get_error(ctx->ssl, n)); + return false; + } + + // Retrieve pending encrypted data + int32_t pending = (int32_t) BIO_ctrl_pending(ctx->bio_out); + + // There is no pending data in bio_out even though SSL_write succeeded + if (pending <= 0) { + MTY_Log("'BIO_ctrl_pending' with return value %d", pending); + return false; + } + + // The pending data exceeds the size of our output buffer + if (pending > (int32_t) outSize) { + MTY_Log("'BIO_ctrl_pending' with return value %d", pending); + return false; + } + + // Read the encrypted data from bio_out + n = BIO_read(ctx->bio_out, out, pending); + + // The size of the encrypted data does not match BIO_ctrl_pending. Something is wrong + if (n != pending) { + MTY_Log("'BIO_read' failed with return value %d", n); + return false; + } + + *written = n; + + return true; +} + +bool MTY_DTLSDecrypt(MTY_DTLS *ctx, const void *in, size_t inSize, void *out, size_t outSize, size_t *read) +{ + // Fill bio_in with encrypted data + int32_t n = BIO_write(ctx->bio_in, in, (int32_t) inSize); + + // The size of the encrypted data written does not match inSize. Something is wrong + if (n != (int32_t) inSize) { + MTY_Log("'BIO_write' failed with return value %d", n); + return false; + } + + // Decrypt the data + n = SSL_read(ctx->ssl, out, (int32_t) outSize); + + // SSL_read can either output data immediately or cause a SSL_ERROR_WANT_READ + if (n <= 0) { + int32_t e = SSL_get_error(ctx->ssl, n); + + // SSL needs more encrypted data until it outputs + if (e == SSL_ERROR_WANT_READ) { + n = 0; + + // Fatal + } else { + MTY_Log("'SSL_read' failed with error %d:%d", n, e); + return false; + } + } + + *read = n; + + return true; +}