From 18f46e65ea048f79ddecde76536ec78399f03984 Mon Sep 17 00:00:00 2001 From: Alcaro Date: Sat, 19 Sep 2020 00:31:36 +0200 Subject: [PATCH] Add BearSSL support --- Makefile.common | 115 +++++- libretro-common/include/net/net_socket.h | 3 + libretro-common/net/net_socket.c | 13 +- libretro-common/net/net_socket_ssl_bear.c | 340 ++++++++++++++++++ ...net_socket_ssl.c => net_socket_ssl_mbed.c} | 0 5 files changed, 464 insertions(+), 7 deletions(-) create mode 100644 libretro-common/net/net_socket_ssl_bear.c rename libretro-common/net/{net_socket_ssl.c => net_socket_ssl_mbed.c} (100%) diff --git a/Makefile.common b/Makefile.common index b70cc3d36a..34320a2991 100644 --- a/Makefile.common +++ b/Makefile.common @@ -536,7 +536,114 @@ ifeq ($(HAVE_LIBRETRODB), 1) endif endif -ifeq ($(HAVE_BUILTINMBEDTLS), 1) +HAVE_BEARSSL := 0 +ifeq ($(HAVE_BEARSSL), 1) + HAVE_SSL = 1 + DEFINES += -DHAVE_SSL -DHAVE_BEARSSL + + # these -Is are only needed for BearSSL itself + INCLUDE_DIRS += -Ideps/bearssl-0.6/src -Ideps/bearssl-0.6/inc + OBJS_BEAR = deps/bearssl-0.6/src/aead/ccm.o \ + deps/bearssl-0.6/src/codec/ccopy.o \ + deps/bearssl-0.6/src/codec/dec32be.o \ + deps/bearssl-0.6/src/codec/dec32le.o \ + deps/bearssl-0.6/src/codec/dec64be.o \ + deps/bearssl-0.6/src/codec/enc32be.o \ + deps/bearssl-0.6/src/codec/enc32le.o \ + deps/bearssl-0.6/src/codec/enc64be.o \ + deps/bearssl-0.6/src/ec/ec_all_m31.o \ + deps/bearssl-0.6/src/ec/ec_c25519_m31.o \ + deps/bearssl-0.6/src/ec/ecdsa_atr.o \ + deps/bearssl-0.6/src/ec/ecdsa_i31_bits.o \ + deps/bearssl-0.6/src/ec/ecdsa_i31_vrfy_asn1.o \ + deps/bearssl-0.6/src/ec/ecdsa_i31_vrfy_raw.o \ + deps/bearssl-0.6/src/ec/ec_p256_m31.o \ + deps/bearssl-0.6/src/ec/ec_prime_i31.o \ + deps/bearssl-0.6/src/ec/ec_secp256r1.o \ + deps/bearssl-0.6/src/ec/ec_secp384r1.o \ + deps/bearssl-0.6/src/ec/ec_secp521r1.o \ + deps/bearssl-0.6/src/hash/ghash_ctmul64.o \ + deps/bearssl-0.6/src/hash/ghash_pclmul.o \ + deps/bearssl-0.6/src/hash/md5.o \ + deps/bearssl-0.6/src/hash/multihash.o \ + deps/bearssl-0.6/src/hash/sha1.o \ + deps/bearssl-0.6/src/hash/sha2big.o \ + deps/bearssl-0.6/src/hash/sha2small.o \ + deps/bearssl-0.6/src/int/i31_add.o \ + deps/bearssl-0.6/src/int/i31_bitlen.o \ + deps/bearssl-0.6/src/int/i31_decmod.o \ + deps/bearssl-0.6/src/int/i31_decode.o \ + deps/bearssl-0.6/src/int/i31_decred.o \ + deps/bearssl-0.6/src/int/i31_encode.o \ + deps/bearssl-0.6/src/int/i31_fmont.o \ + deps/bearssl-0.6/src/int/i31_iszero.o \ + deps/bearssl-0.6/src/int/i31_modpow2.o \ + deps/bearssl-0.6/src/int/i31_modpow.o \ + deps/bearssl-0.6/src/int/i31_montmul.o \ + deps/bearssl-0.6/src/int/i31_muladd.o \ + deps/bearssl-0.6/src/int/i31_ninv31.o \ + deps/bearssl-0.6/src/int/i31_rshift.o \ + deps/bearssl-0.6/src/int/i31_sub.o \ + deps/bearssl-0.6/src/int/i31_tmont.o \ + deps/bearssl-0.6/src/int/i32_div32.o \ + deps/bearssl-0.6/src/int/i62_modpow2.o \ + deps/bearssl-0.6/src/mac/hmac_ct.o \ + deps/bearssl-0.6/src/mac/hmac.o \ + deps/bearssl-0.6/src/rand/hmac_drbg.o \ + deps/bearssl-0.6/src/rand/sysrng.o \ + deps/bearssl-0.6/src/rsa/rsa_default_pkcs1_vrfy.o \ + deps/bearssl-0.6/src/rsa/rsa_default_pub.o \ + deps/bearssl-0.6/src/rsa/rsa_i31_pkcs1_vrfy.o \ + deps/bearssl-0.6/src/rsa/rsa_i31_pub.o \ + deps/bearssl-0.6/src/rsa/rsa_i62_pkcs1_vrfy.o \ + deps/bearssl-0.6/src/rsa/rsa_i62_pub.o \ + deps/bearssl-0.6/src/rsa/rsa_pkcs1_sig_unpad.o \ + deps/bearssl-0.6/src/ssl/prf_md5sha1.o \ + deps/bearssl-0.6/src/ssl/prf.o \ + deps/bearssl-0.6/src/ssl/prf_sha256.o \ + deps/bearssl-0.6/src/ssl/prf_sha384.o \ + deps/bearssl-0.6/src/ssl/ssl_client_default_rsapub.o \ + deps/bearssl-0.6/src/ssl/ssl_client_full.o \ + deps/bearssl-0.6/src/ssl/ssl_client.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_aescbc.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_aesccm.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_aesgcm.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_chapol.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_descbc.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_ecdsa.o \ + deps/bearssl-0.6/src/ssl/ssl_engine_default_rsavrfy.o \ + deps/bearssl-0.6/src/ssl/ssl_engine.o \ + deps/bearssl-0.6/src/ssl/ssl_hs_client.o \ + deps/bearssl-0.6/src/ssl/ssl_rec_cbc.o \ + deps/bearssl-0.6/src/ssl/ssl_rec_ccm.o \ + deps/bearssl-0.6/src/ssl/ssl_rec_chapol.o \ + deps/bearssl-0.6/src/ssl/ssl_rec_gcm.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64_cbcdec.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64_cbcenc.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64_ctrcbc.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64_ctr.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64_dec.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64_enc.o \ + deps/bearssl-0.6/src/symcipher/aes_ct64.o \ + deps/bearssl-0.6/src/symcipher/aes_x86ni_cbcdec.o \ + deps/bearssl-0.6/src/symcipher/aes_x86ni_cbcenc.o \ + deps/bearssl-0.6/src/symcipher/aes_x86ni_ctrcbc.o \ + deps/bearssl-0.6/src/symcipher/aes_x86ni_ctr.o \ + deps/bearssl-0.6/src/symcipher/aes_x86ni.o \ + deps/bearssl-0.6/src/symcipher/chacha20_ct.o \ + deps/bearssl-0.6/src/symcipher/chacha20_sse2.o \ + deps/bearssl-0.6/src/symcipher/des_ct_cbcdec.o \ + deps/bearssl-0.6/src/symcipher/des_ct_cbcenc.o \ + deps/bearssl-0.6/src/symcipher/des_ct.o \ + deps/bearssl-0.6/src/symcipher/des_support.o \ + deps/bearssl-0.6/src/symcipher/poly1305_ctmul.o \ + deps/bearssl-0.6/src/symcipher/poly1305_ctmulq.o \ + deps/bearssl-0.6/src/x509/x509_decoder.o \ + deps/bearssl-0.6/src/x509/x509_minimal_full.o \ + deps/bearssl-0.6/src/x509/x509_minimal.o \ + + OBJ += $(OBJS_BEAR) +else ifeq ($(HAVE_BUILTINMBEDTLS), 1) HAVE_SSL = 1 DEFINES += -DHAVE_SSL @@ -1910,8 +2017,10 @@ ifeq ($(HAVE_NETWORKING), 1) OBJ += tasks/task_core_updater.o endif - ifeq ($(HAVE_SSL), 1) - OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl.o + ifeq ($(HAVE_BEARSSL), 1) + OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl_bear.o + else ifeq ($(HAVE_SSL), 1) + OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl_mbed.o endif ifneq ($(HAVE_SOCKET_LEGACY),1) diff --git a/libretro-common/include/net/net_socket.h b/libretro-common/include/net/net_socket.h index 58378f3553..2bf0b33378 100644 --- a/libretro-common/include/net/net_socket.h +++ b/libretro-common/include/net/net_socket.h @@ -64,6 +64,9 @@ int socket_next(void **address); int socket_close(int fd); +bool socket_set_block(int fd, bool block); + +/* TODO: all callers should be converted to socket_set_block() */ bool socket_nonblock(int fd); int socket_select(int nfds, fd_set *readfs, fd_set *writefds, diff --git a/libretro-common/net/net_socket.c b/libretro-common/net/net_socket.c index 8de6730e48..6e906b6e4c 100644 --- a/libretro-common/net/net_socket.c +++ b/libretro-common/net/net_socket.c @@ -123,20 +123,25 @@ int socket_receive_all_blocking(int fd, void *data_, size_t size) return true; } -bool socket_nonblock(int fd) +bool socket_set_block(int fd, bool block) { #if defined(__CELLOS_LV2__) || defined(VITA) || defined(WIIU) - int i = 1; + int i = block; setsockopt(fd, SOL_SOCKET, SO_NBIO, &i, sizeof(int)); return true; #elif defined(_WIN32) - u_long mode = 1; + u_long mode = block; return ioctlsocket(fd, FIONBIO, &mode) == 0; #else - return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == 0; + return fcntl(fd, F_SETFL, (fcntl(fd, F_GETFL) & ~O_NONBLOCK) | (block ? 0 : O_NONBLOCK)) == 0; #endif } +bool socket_nonblock(int fd) +{ + return socket_set_block(fd, false); +} + int socket_close(int fd) { #if defined(_WIN32) && !defined(_XBOX360) diff --git a/libretro-common/net/net_socket_ssl_bear.c b/libretro-common/net/net_socket_ssl_bear.c new file mode 100644 index 0000000000..87d40d60f1 --- /dev/null +++ b/libretro-common/net/net_socket_ssl_bear.c @@ -0,0 +1,340 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (net_socket.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "../../deps/bearssl-0.6/inc/bearssl.h" + +static br_x509_trust_anchor TAs[500] = {}; +static size_t TAs_NUM = 0; + +static uint8_t* current_vdn; +static size_t current_vdn_size; + +static uint8_t* blobdup(const void * src, size_t len) +{ + uint8_t * ret = malloc(len); + memcpy(ret, src, len); + return ret; +} +static void vdn_append(void* dest_ctx, const void * src, size_t len) +{ + current_vdn = realloc(current_vdn, current_vdn_size + len); + memcpy(current_vdn+current_vdn_size, src, len); + current_vdn_size += len; +} +static bool append_cert_x509(void* x509, size_t len) +{ + br_x509_trust_anchor* ta = &TAs[TAs_NUM]; + br_x509_decoder_context dc; + br_x509_pkey* pk; + + current_vdn = NULL; + current_vdn_size = 0; + + br_x509_decoder_init(&dc, vdn_append, NULL); + br_x509_decoder_push(&dc, x509, len); + pk = br_x509_decoder_get_pkey(&dc); + if (pk == NULL || !br_x509_decoder_isCA(&dc)) return false; + + ta->dn.len = current_vdn_size; + ta->dn.data = current_vdn; + ta->flags = BR_X509_TA_CA; + + switch (pk->key_type) + { + case BR_KEYTYPE_RSA: + ta->pkey.key_type = BR_KEYTYPE_RSA; + ta->pkey.key.rsa.nlen = pk->key.rsa.nlen; + ta->pkey.key.rsa.n = blobdup(pk->key.rsa.n, pk->key.rsa.nlen); + ta->pkey.key.rsa.elen = pk->key.rsa.elen; + ta->pkey.key.rsa.e = blobdup(pk->key.rsa.e, pk->key.rsa.elen); + break; + case BR_KEYTYPE_EC: + ta->pkey.key_type = BR_KEYTYPE_EC; + ta->pkey.key.ec.curve = pk->key.ec.curve; + ta->pkey.key.ec.qlen = pk->key.ec.qlen; + ta->pkey.key.ec.q = blobdup(pk->key.ec.q, pk->key.ec.qlen); + break; + default: + return false; + } + + TAs_NUM++; + return true; +} + +static char* delete_linebreaks(char* in) +{ + char* iter_in; + char* iter_out; + while (*in == '\n') in++; + + iter_in = in; + + while (*iter_in != '\n' && *iter_in != '\0') iter_in++; + iter_out = iter_in; + while (*iter_in != '\0') + { + while (*iter_in == '\n') iter_in++; + *iter_out++ = *iter_in++; + } + + return in; +} + +/* this rearranges its input, it's easier to implement that way and caller doesn't need it anymore anyways */ +static void append_certs_pem_x509(char * certs_pem) +{ + char * cert = certs_pem; + char * cert_end = certs_pem; + + void * cert_bin; + int cert_bin_len; + + while (true) + { + cert = strstr(cert_end, "-----BEGIN CERTIFICATE-----"); + if (!cert) break; + cert += strlen("-----BEGIN CERTIFICATE-----"); + cert_end = strstr(cert, "-----END CERTIFICATE-----"); + + *cert_end = '\0'; + cert = delete_linebreaks(cert); + + cert_bin = unbase64(cert, cert_end-cert, &cert_bin_len); + append_cert_x509(cert_bin, cert_bin_len); + free(cert_bin); + + cert_end++; /* skip the NUL we just added */ + } +} + +/* TODO: not thread safe, rthreads doesn't provide any statically allocatable mutex/etc */ +static void initialize() +{ + void* certs_pem; + if (TAs_NUM) return; + /* filestream_read_file appends a NUL */ + filestream_read_file("/etc/ssl/certs/ca-certificates.crt", &certs_pem, NULL); + append_certs_pem_x509((char*)certs_pem); + free(certs_pem); +} + + + +struct ssl_state +{ + int fd; + br_ssl_client_context sc; + br_x509_minimal_context xc; + uint8_t iobuf[BR_SSL_BUFSIZE_BIDI]; +}; + +void* ssl_socket_init(int fd, const char *domain) +{ + struct ssl_state *state = (struct ssl_state*)calloc(1, sizeof(*state)); + + initialize(); + + br_ssl_client_init_full(&state->sc, &state->xc, TAs, TAs_NUM); + br_ssl_engine_set_buffer(&state->sc.eng, state->iobuf, sizeof(state->iobuf), true); + br_ssl_client_reset(&state->sc, domain, false); + + state->fd = fd; + return state; +} + +static bool process_inner(struct ssl_state *state, bool blocking) +{ + size_t buflen; + uint8_t * buf; + ssize_t bytes; + bool dummy; + + buf = br_ssl_engine_sendrec_buf(&state->sc.eng, &buflen); + if (buflen) + { + if (blocking) bytes = (socket_send_all_blocking(state->fd, buf, buflen, true) ? buflen : -1); + else bytes = socket_send_all_nonblocking(state->fd, buf, buflen, true); + + if (bytes > 0) br_ssl_engine_sendrec_ack(&state->sc.eng, bytes); + if (bytes < 0) return false; + + return true; /* if we did something, return immediately so we don't try to read if Bear still wants to send */ + } + + buf = br_ssl_engine_recvrec_buf(&state->sc.eng, &buflen); + if (buflen) + { + /* if the socket is blocking, socket_receive_all_nonblocking blocks, + * but only to read at least 1 byte which is exactly what we want */ + bytes = socket_receive_all_nonblocking(state->fd, &dummy, buf, buflen); + if (bytes > 0) br_ssl_engine_recvrec_ack(&state->sc.eng, bytes); + if (bytes < 0) return false; + } + + return true; +} + +int ssl_socket_connect(void *state_data, + void *data, bool timeout_enable, bool nonblock) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + unsigned bearstate; + + if (socket_connect(state->fd, data, timeout_enable)) + return -1; + + while (true) + { + if (!process_inner(state, true)) + return -1; + + bearstate = br_ssl_engine_current_state(&state->sc.eng); + if (bearstate & BR_SSL_SENDAPP) break; /* handshake done */ + if (bearstate & BR_SSL_CLOSED) return -1; /* failed */ + } + + return 1; +} + +ssize_t ssl_socket_receive_all_nonblocking(void *state_data, + bool *error, void *data_, size_t size) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + uint8_t * bear_data; + size_t bear_data_size; + + socket_set_block(state->fd, false); + + if (!process_inner(state, false)) + { + *error = true; + return -1; + } + + bear_data = br_ssl_engine_recvapp_buf(&state->sc.eng, &bear_data_size); + if (bear_data_size > size) bear_data_size = size; + memcpy(data_, bear_data, bear_data_size); + if (bear_data_size) + br_ssl_engine_recvapp_ack(&state->sc.eng, bear_data_size); + + return bear_data_size; +} + +int ssl_socket_receive_all_blocking(void *state_data, + void *data_, size_t size) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + uint8_t *data = (uint8_t*)data_; + uint8_t * bear_data; + size_t bear_data_size; + + socket_set_block(state->fd, true); + + while (true) + { + bear_data = br_ssl_engine_recvapp_buf(&state->sc.eng, &bear_data_size); + if (bear_data_size > size) bear_data_size = size; + memcpy(data, bear_data, bear_data_size); + if (bear_data_size) + br_ssl_engine_recvapp_ack(&state->sc.eng, bear_data_size); + data += bear_data_size; + size -= bear_data_size; + + if (size) process_inner(state, true); + else break; + } + return 1; +} + +int ssl_socket_send_all_blocking(void *state_data, + const void *data_, size_t size, bool no_signal) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + const uint8_t *data = (const uint8_t*)data_; + uint8_t * bear_data; + size_t bear_data_size; + + socket_set_block(state->fd, true); + + while (true) + { + bear_data = br_ssl_engine_sendapp_buf(&state->sc.eng, &bear_data_size); + if (bear_data_size > size) bear_data_size = size; + memcpy(bear_data, data_, bear_data_size); + if (bear_data_size) + br_ssl_engine_sendapp_ack(&state->sc.eng, bear_data_size); + data += bear_data_size; + size -= bear_data_size; + + if (size) process_inner(state, true); + else break; + } + + br_ssl_engine_flush(&state->sc.eng, false); + process_inner(state, false); + return 1; +} + +ssize_t ssl_socket_send_all_nonblocking(void *state_data, + const void *data_, size_t size, bool no_signal) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + uint8_t * bear_data; + size_t bear_data_size; + + socket_set_block(state->fd, false); + + bear_data = br_ssl_engine_sendapp_buf(&state->sc.eng, &bear_data_size); + if (bear_data_size > size) bear_data_size = size; + memcpy(bear_data, data_, bear_data_size); + if (bear_data_size) + { + br_ssl_engine_sendapp_ack(&state->sc.eng, bear_data_size); + br_ssl_engine_flush(&state->sc.eng, false); + } + + if (!process_inner(state, false)) + return -1; + + return bear_data_size; +} + +void ssl_socket_close(void *state_data) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + + br_ssl_engine_close(&state->sc.eng); + process_inner(state, false); /* send close notification */ + socket_close(state->fd); /* but immediately close socket and don't worry about recipient getting our message */ +} + +void ssl_socket_free(void *state_data) +{ + struct ssl_state *state = (struct ssl_state*)state_data; + /* BearSSL does zero allocations of its own, so other than this struct, there is nothing to free */ + free(state); +} diff --git a/libretro-common/net/net_socket_ssl.c b/libretro-common/net/net_socket_ssl_mbed.c similarity index 100% rename from libretro-common/net/net_socket_ssl.c rename to libretro-common/net/net_socket_ssl_mbed.c