/* Copyright (c) 2014 Anton Titov. Copyright (c) 2014 pCloud Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of pCloud Ltd nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "pssl_crypto_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pssl_crypto_compat.h" #include "pcache.h" #include "pcompiler.h" #include "plibs.h" #include "pmem.h" #include "prand.h" #include "psettings.h" #include "pssl.h" #include "psslcerts.h" #include "psynclib.h" #include "putil.h" static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void psync_aes256_encode_block(psync_aes256_encoder enc, const unsigned char *src, unsigned char *dst) { #if MBEDTLS_VERSION_MAJOR >= 4 size_t output_len; pdbg_assert(psa_cipher_encrypt(enc->key_id, PSA_ALG_ECB_NO_PADDING, src, PSYNC_AES256_BLOCK_SIZE, dst, PSYNC_AES256_BLOCK_SIZE, &output_len) == PSA_SUCCESS); pdbg_assert(output_len == PSYNC_AES256_BLOCK_SIZE); #else pdbg_assert(mbedtls_aes_crypt_ecb(&enc->ctx, MBEDTLS_AES_ENCRYPT, src, dst) == 0); #endif } void psync_aes256_decode_block(psync_aes256_decoder dec, const unsigned char *src, unsigned char *dst) { #if MBEDTLS_VERSION_MAJOR >= 4 size_t output_len; pdbg_assert(psa_cipher_decrypt(dec->key_id, PSA_ALG_ECB_NO_PADDING, src, PSYNC_AES256_BLOCK_SIZE, dst, PSYNC_AES256_BLOCK_SIZE, &output_len) == PSA_SUCCESS); pdbg_assert(output_len == PSYNC_AES256_BLOCK_SIZE); #else pdbg_assert(mbedtls_aes_crypt_ecb(&dec->ctx, MBEDTLS_AES_DECRYPT, src, dst) == 0); #endif } void psync_aes256_encode_2blocks_consec( psync_aes256_encoder enc, const unsigned char *src, unsigned char *dst) { psync_aes256_encode_block(enc, src, dst); psync_aes256_encode_block(enc, src + PSYNC_AES256_BLOCK_SIZE, dst + PSYNC_AES256_BLOCK_SIZE); } void psync_aes256_decode_2blocks_consec( psync_aes256_decoder dec, const unsigned char *src, unsigned char *dst) { psync_aes256_decode_block(dec, src, dst); psync_aes256_decode_block(dec, src + PSYNC_AES256_BLOCK_SIZE, dst + PSYNC_AES256_BLOCK_SIZE); } void psync_aes256_decode_4blocks_consec_xor( psync_aes256_decoder dec, const unsigned char *src, unsigned char *dst, unsigned char *bxor) { size_t i; psync_aes256_decode_block(dec, src, dst); psync_aes256_decode_block(dec, src + PSYNC_AES256_BLOCK_SIZE, dst + PSYNC_AES256_BLOCK_SIZE); psync_aes256_decode_block(dec, src + PSYNC_AES256_BLOCK_SIZE * 2, dst + PSYNC_AES256_BLOCK_SIZE * 2); psync_aes256_decode_block(dec, src + PSYNC_AES256_BLOCK_SIZE * 3, dst + PSYNC_AES256_BLOCK_SIZE * 3); for (i = 0; i < PSYNC_AES256_BLOCK_SIZE * 4; ++i) dst[i] ^= bxor[i]; } static void ssl_pdbg_logf(int loglevel, int errnum, const char *msg) { char ebuf[100]; mbedtls_strerror(errnum, ebuf, sizeof(ebuf)); pdbg_logf(loglevel, "%s: %s (-0x%04x)", msg, ebuf, (unsigned int)-errnum); } static void free_encrypted(psync_encrypted_data_t e) { putil_wipe(e->data, e->datalen); pmem_free(PMEM_SUBSYS_OTHER, e); } void prsa_free_binary(psync_binary_rsa_key_t bin) { free_encrypted(bin); } void psymkey_free(psync_symmetric_key_t key) { putil_wipe(key->key, key->keylen); pmem_free(PMEM_SUBSYS_OTHER, key); } psync_encrypted_symmetric_key_t psymkey_alloc_encrypted(size_t len) { psync_encrypted_symmetric_key_t ret; ret = pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_encrypted_data_struct_t, data) + len); ret->datalen = len; return ret; } psync_encrypted_symmetric_key_t psymkey_copy_encrypted(psync_encrypted_symmetric_key_t src) { psync_encrypted_symmetric_key_t ret; ret = pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_encrypted_data_struct_t, data) + src->datalen); ret->datalen = src->datalen; memcpy(ret->data, src->data, src->datalen); return ret; } psync_symmetric_key_t prsa_decrypt_symm_key_lock( psync_rsa_privatekey_t *rsa, const psync_encrypted_symmetric_key_t *enckey) { psync_symmetric_key_t sym_key; pdbg_logf(D_NOTICE, "Get RSA decrypt key lock."); pthread_mutex_lock(&mutex); sym_key = psymkey_decrypt(*rsa, *enckey); pthread_mutex_unlock(&mutex); pdbg_logf(D_NOTICE, "RSA decrypt key Lock released."); return sym_key; } static const int ciphersuites[] = { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0}; typedef struct { #if MBEDTLS_VERSION_MAJOR < 4 mbedtls_ctr_drbg_context rnd; #endif pthread_mutex_t mutex; } rng_ctx; typedef struct { mbedtls_net_context srv; mbedtls_ssl_context ssl; mbedtls_ssl_config cfg; int sock; int isbroken; char cachekey[]; } ssl_connection_t; static rng_ctx rng; #if MBEDTLS_VERSION_MAJOR < 4 static mbedtls_entropy_context entropy; #endif static mbedtls_x509_crt ca_chain; PSYNC_THREAD int psync_ssl_errno; static pssl_debug_callback_t debug_cb = NULL; static void *debug_ctx = NULL; void pssl_log_threshold(int threshold) { mbedtls_debug_set_threshold(threshold); } void pssl_debug_cb(pssl_debug_callback_t cb, void *ctx) { debug_cb = cb; debug_ctx = ctx; } int rng_get(void *p_rng, unsigned char *output, size_t output_len) { rng_ctx *rng = (rng_ctx *)p_rng; int ret; pthread_mutex_lock(&rng->mutex); #if MBEDTLS_VERSION_MAJOR >= 4 (void)p_rng; ret = (int)psa_generate_random(output, output_len); #else ret = mbedtls_ctr_drbg_random(&rng->rnd, output, output_len); #endif pthread_mutex_unlock(&rng->mutex); return ret; } int pssl_init() { unsigned char seed[PSYNC_LHASH_DIGEST_LEN]; unsigned long i; int result; if (pthread_mutex_init(&rng.mutex, NULL)) return pdbg_return(-1); #if MBEDTLS_VERSION_MAJOR >= 4 result = psa_crypto_init(); if (result != PSA_SUCCESS) { pdbg_logf(D_ERROR, "psa_crypto_init failed with return code %d", result); return pdbg_return(-1); } #else mbedtls_entropy_init(&entropy); prand_seed(seed, seed, sizeof(seed), 0); mbedtls_entropy_update_manual(&entropy, seed, sizeof(seed)); mbedtls_ctr_drbg_init(&rng.rnd); if ((result = mbedtls_ctr_drbg_seed(&rng.rnd, mbedtls_entropy_func, &entropy, NULL, 0))) { pdbg_logf(D_ERROR, "mbedtls_ctr_drbg_seed failed with return code %d", result); return pdbg_return(-1); } #endif mbedtls_x509_crt_init(&ca_chain); for (i = 0; i < ARRAY_SIZE(psync_ssl_trusted_certs); i++) { result = mbedtls_x509_crt_parse(&ca_chain, (unsigned char *)psync_ssl_trusted_certs[i], 1 + strlen(psync_ssl_trusted_certs[i])); if (result) { pdbg_logf(D_ERROR, "failed to load certificate %lu, got result %d", (unsigned long)i, result); } } return 0; } static ssl_connection_t *conn_alloc(const char *hostname) { ssl_connection_t *conn; size_t len; len = strlen(hostname) + 1; conn = (ssl_connection_t *)pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(ssl_connection_t, cachekey) + len + 4); conn->isbroken = 0; memcpy(conn->cachekey, "SSLS", 4); memcpy(conn->cachekey + 4, hostname, len); return conn; } static void set_error(ssl_connection_t *conn, int err) { if (err == MBEDTLS_ERR_SSL_WANT_READ) psync_ssl_errno = PSYNC_SSL_ERR_WANT_READ; else if (err == MBEDTLS_ERR_SSL_WANT_WRITE) psync_ssl_errno = PSYNC_SSL_ERR_WANT_WRITE; else { psync_ssl_errno = PSYNC_SSL_ERR_UNKNOWN; conn->isbroken = 1; if (err == MBEDTLS_ERR_NET_RECV_FAILED) pdbg_logf(D_NOTICE, "got MBEDTLS_ERR_NET_RECV_FAILED"); else if (err == MBEDTLS_ERR_NET_SEND_FAILED) pdbg_logf(D_NOTICE, "got MBEDTLS_ERR_NET_SEND_FAILED"); else pdbg_logf(D_NOTICE, "got error %d", err); } } static int mbed_read(void *ptr, unsigned char *buf, size_t len) { ssl_connection_t *conn; ssize_t ret; int err; conn = (ssl_connection_t *)ptr; ret = read(conn->sock, buf, len); if (ret == -1) { err = errno; if (err == EWOULDBLOCK || err == EAGAIN || err == EINTR) return MBEDTLS_ERR_SSL_WANT_READ; else return MBEDTLS_ERR_NET_RECV_FAILED; } else return (int)ret; } static int mbed_write(void *ptr, const unsigned char *buf, size_t len) { ssl_connection_t *conn; ssize_t ret; int err; conn = (ssl_connection_t *)ptr; ret = write(conn->sock, buf, len); if (ret == -1) { err = errno; if (err == EWOULDBLOCK || err == EAGAIN || err == EINTR) return MBEDTLS_ERR_SSL_WANT_WRITE; else return MBEDTLS_ERR_NET_SEND_FAILED; } else return (int)ret; } static void free_session(void *ptr) { mbedtls_ssl_session_free((mbedtls_ssl_session *)ptr); pmem_free(PMEM_SUBSYS_OTHER, ptr); } static void save_session(ssl_connection_t *conn) { mbedtls_ssl_session *sess; sess = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(mbedtls_ssl_session)); // mbedtls_ssl_get_session seems to copy all elements, instead of referencing // them, therefore it is thread safe to add session upon connect memset(sess, 0, sizeof(mbedtls_ssl_session)); if (mbedtls_ssl_get_session(&conn->ssl, sess)) pmem_free(PMEM_SUBSYS_OTHER, sess); else pcache_add(conn->cachekey, sess, PSYNC_SSL_SESSION_CACHE_TIMEOUT, free_session, PSYNC_MAX_SSL_SESSIONS_PER_DOMAIN); } static int check_peer_pubkey(ssl_connection_t *conn) { const mbedtls_x509_crt *cert; unsigned char buff[1024], sigbin[32]; char sighex[66]; int i; int match = 0; cert = mbedtls_ssl_get_peer_cert(&conn->ssl); if (!cert) { pdbg_logf(D_WARNING, "ssl_get_peer_cert returned NULL"); return -1; } #if MBEDTLS_VERSION_MAJOR >= 4 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_type_t type; int ret = mbedtls_pk_get_psa_attributes(&cert->pk, PSA_KEY_USAGE_VERIFY_HASH, &attributes); if (ret) { pdbg_logf(D_WARNING, "pk_get_psa_attributes failed with code %d", ret); return -1; } type = psa_get_key_type(&attributes); psa_reset_key_attributes(&attributes); if (type != PSA_KEY_TYPE_RSA_PUBLIC_KEY && type != PSA_KEY_TYPE_RSA_KEY_PAIR) { pdbg_logf(D_WARNING, "public key is not RSA"); return -1; } #else if (mbedtls_pk_get_type(&cert->pk) != MBEDTLS_PK_RSA) { pdbg_logf(D_WARNING, "public key is not RSA"); return -1; } #endif i = mbedtls_pk_write_pubkey_der((const mbedtls_pk_context *)&cert->pk, buff, sizeof(buff)); if (i <= 0 || i > (int)sizeof(buff)) { pdbg_logf(D_WARNING, "pk_write_pubkey_der returned %d (buffer size %zu)", i, sizeof(buff)); return -1; } #if MBEDTLS_VERSION_MAJOR >= 4 size_t hash_len; if (psa_hash_compute(PSA_ALG_SHA_256, buff + sizeof(buff) - i, i, sigbin, sizeof(sigbin), &hash_len) != PSA_SUCCESS || hash_len != sizeof(sigbin)) { pdbg_logf(D_WARNING, "psa_hash_compute failed"); return -1; } #else mbedtls_sha256(buff + sizeof(buff) - i, i, sigbin, 0); #endif psync_binhex(sighex, sigbin, 32); sighex[64] = 0; for (i = 0; i < ARRAY_SIZE(psync_ssl_trusted_pk_sha256); i++) match |= (memcmp(sighex, psync_ssl_trusted_pk_sha256[i], 64) == 0); if (match) return 0; pdbg_logf(D_ERROR, "got sha256hex of public key %s that does not match any approved " "fingerprint", sighex); return -1; } int pssl_connect(int sock, void **sslconn, const char *hostname) { ssl_connection_t *conn; mbedtls_ssl_session *sess; int ret; conn = conn_alloc(hostname); mbedtls_ssl_init(&conn->ssl); mbedtls_ssl_config_init(&conn->cfg); mbedtls_net_init(&conn->srv); conn->sock = sock; if ((ret = mbedtls_ssl_config_defaults(&conn->cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { pdbg_logf(D_ERROR, "failed to set ssl cfg defaults: ! mbedtls_ssl_config_defaults " "returned %d\n\n", ret); goto err0; } #if MBEDTLS_VERSION_MAJOR >= 4 mbedtls_ssl_conf_max_tls_version(&conn->cfg, MBEDTLS_SSL_VERSION_TLS1_2); mbedtls_ssl_conf_min_tls_version(&conn->cfg, MBEDTLS_SSL_VERSION_TLS1_2); #else mbedtls_ssl_conf_max_version(&conn->cfg, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); mbedtls_ssl_conf_min_version(&conn->cfg, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); #endif mbedtls_ssl_conf_endpoint(&conn->cfg, MBEDTLS_SSL_IS_CLIENT); mbedtls_ssl_conf_dbg(&conn->cfg, debug_cb, debug_ctx); mbedtls_ssl_conf_authmode(&conn->cfg, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_ca_chain(&conn->cfg, &ca_chain, NULL); mbedtls_ssl_conf_ciphersuites(&conn->cfg, ciphersuites); #if MBEDTLS_VERSION_MAJOR < 4 mbedtls_ssl_conf_rng(&conn->cfg, rng_get, &rng); #endif mbedtls_ssl_set_bio(&conn->ssl, &conn->srv, mbed_write, mbed_read, NULL); mbedtls_ssl_set_hostname(&conn->ssl, hostname); mbedtls_ssl_setup(&conn->ssl, &conn->cfg); // attach config to ssl if ((sess = (mbedtls_ssl_session *)pcache_get(conn->cachekey))) { pdbg_logf(D_NOTICE, "reusing cached session for %s", hostname); if (mbedtls_ssl_set_session(&conn->ssl, sess)) { pdbg_logf(D_WARNING, "ssl_set_session failed"); } mbedtls_ssl_session_free(sess); pmem_free(PMEM_SUBSYS_OTHER, sess); } ret = mbedtls_ssl_handshake(&conn->ssl); if (ret == 0) { if ((check_peer_pubkey(conn))) { goto err1; } *sslconn = conn; save_session(conn); return PSYNC_SSL_SUCCESS; } set_error(conn, ret); if (pdbg_likely(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)) { *sslconn = conn; return PSYNC_SSL_NEED_FINISH; } err1: mbedtls_ssl_free(&conn->ssl); err0: pmem_free(PMEM_SUBSYS_OTHER, conn); return pdbg_return_const(PSYNC_SSL_FAIL); } int pssl_connect_finish(void *sslconn, const char *hostname) { ssl_connection_t *conn; int ret; conn = (ssl_connection_t *)sslconn; ret = mbedtls_ssl_handshake(&conn->ssl); if (ret == 0) { if ((check_peer_pubkey(conn))) { goto fail; } save_session(conn); return PSYNC_SSL_SUCCESS; } else { pdbg_logf(D_ERROR, "handshake failed, return code was %d", ret); } set_error(conn, ret); if (pdbg_likely(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)) return PSYNC_SSL_NEED_FINISH; fail: mbedtls_ssl_free(&conn->ssl); pmem_free(PMEM_SUBSYS_OTHER, conn); return pdbg_return_const(PSYNC_SSL_FAIL); } int pssl_shutdown(void *sslconn) { ssl_connection_t *conn; int ret; conn = (ssl_connection_t *)sslconn; if (conn->isbroken) goto noshutdown; ret = mbedtls_ssl_close_notify(&conn->ssl); if (ret == 0) goto noshutdown; set_error(conn, ret); if (pdbg_likely(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)) return PSYNC_SSL_NEED_FINISH; noshutdown: mbedtls_ssl_free(&conn->ssl); pmem_free(PMEM_SUBSYS_OTHER, conn); return PSYNC_SSL_SUCCESS; } void pssl_free(void *sslconn) { ssl_connection_t *conn; conn = (ssl_connection_t *)sslconn; mbedtls_ssl_free(&conn->ssl); pmem_free(PMEM_SUBSYS_OTHER, conn); } int pssl_pendingdata(void *sslconn) { return mbedtls_ssl_get_bytes_avail(&((ssl_connection_t *)sslconn)->ssl); } int pssl_read(void *sslconn, void *buf, int num) { ssl_connection_t *conn; int res; conn = (ssl_connection_t *)sslconn; res = mbedtls_ssl_read(&conn->ssl, (unsigned char *)buf, num); if (res >= 0) return res; set_error(conn, res); return PSYNC_SSL_FAIL; } int pssl_write(void *sslconn, const void *buf, int num) { ssl_connection_t *conn; int res; conn = (ssl_connection_t *)sslconn; res = mbedtls_ssl_write(&conn->ssl, (const unsigned char *)buf, num); if (res >= 0) return res; set_error(conn, res); return PSYNC_SSL_FAIL; } void pssl_rand_strong(unsigned char *buf, int num) { // FIXME: causing segfault if (unlikely(rng_get(&rng, buf, num))) { pdbg_logf(D_CRITICAL, "could not generate %d random bytes, exiting", num); abort(); } } psync_rsa_t pssl_gen_rsa(int bits) { psync_rsa_t rsa; rsa = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*rsa)); #if MBEDTLS_VERSION_MAJOR >= 4 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_status_t status; psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR); psa_set_key_bits(&attributes, bits); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_SIGN_HASH); psa_set_key_algorithm(&attributes, PSA_ALG_NONE); status = psa_generate_key(&attributes, &rsa->key_id); if (status != PSA_SUCCESS) { psa_reset_key_attributes(&attributes); pmem_free(PMEM_SUBSYS_OTHER, rsa); return PSYNC_INVALID_RSA; } psa_reset_key_attributes(&attributes); rsa->bits = bits; #else mbedtls_rsa_init(&rsa->ctx); mbedtls_rsa_set_padding(&rsa->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); if (mbedtls_rsa_gen_key(&rsa->ctx, rng_get, &rng, bits, 65537)) { mbedtls_rsa_free(&rsa->ctx); pmem_free(PMEM_SUBSYS_OTHER, rsa); return PSYNC_INVALID_RSA; } #endif return rsa; } void pssl_free_rsa(psync_rsa_t rsa) { #if MBEDTLS_VERSION_MAJOR >= 4 psa_destroy_key(rsa->key_id); #else mbedtls_rsa_free(&rsa->ctx); #endif putil_wipe(rsa, sizeof(*rsa)); pmem_free(PMEM_SUBSYS_OTHER, rsa); } psync_rsa_publickey_t prsa_get_public(psync_rsa_t rsa) { psync_binary_rsa_key_t bin; psync_rsa_publickey_t ret; bin = prsa_public_to_binary(rsa); if (bin == PSYNC_INVALID_BIN_RSA) return PSYNC_INVALID_RSA; ret = prsa_binary_to_public(bin); prsa_free_binary(bin); return ret; } void prsa_free_public(psync_rsa_publickey_t key) { pssl_free_rsa(key); } psync_rsa_privatekey_t prsa_get_private(psync_rsa_t rsa) { #if MBEDTLS_VERSION_MAJOR >= 4 psync_rsa_struct *ret = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*ret)); ret->key_id = rsa->key_id; ret->bits = rsa->bits; return ret; #else psync_rsa_struct *rsactx; rsactx = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(psync_rsa_struct)); mbedtls_rsa_init(&rsactx->ctx); mbedtls_rsa_set_padding(&rsactx->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); if (unlikely(mbedtls_rsa_copy(&rsactx->ctx, &rsa->ctx))) { mbedtls_rsa_free(&rsactx->ctx); pmem_free(PMEM_SUBSYS_OTHER, rsactx); return PSYNC_INVALID_RSA; } return rsactx; #endif } void prsa_free_private(psync_rsa_privatekey_t key) { pssl_free_rsa(key); } psync_binary_rsa_key_t prsa_public_to_binary(psync_rsa_publickey_t rsa) { unsigned char buff[4096], *p; mbedtls_pk_context ctx; psync_binary_rsa_key_t ret; int len; int result; mbedtls_pk_init(&ctx); #if MBEDTLS_VERSION_MAJOR >= 4 result = mbedtls_pk_copy_public_from_psa(rsa->key_id, &ctx); #else result = mbedtls_pk_setup(&ctx, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); if (!result) result = mbedtls_rsa_copy(mbedtls_pk_rsa(ctx), &rsa->ctx); #endif if (result) { mbedtls_pk_free(&ctx); return PSYNC_INVALID_BIN_RSA; } p = buff + sizeof(buff); #if MBEDTLS_VERSION_MAJOR >= 4 len = mbedtls_pk_write_pubkey_der(&ctx, buff, sizeof(buff)); if (len > 0) p = buff + sizeof(buff) - len; #else len = mbedtls_pk_write_pubkey(&p, buff, &ctx); #endif mbedtls_pk_free(&ctx); if (len <= 0) return PSYNC_INVALID_BIN_RSA; ret = pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_encrypted_data_struct_t, data) + len); ret->datalen = len; memcpy(ret->data, p, len); return ret; } psync_binary_rsa_key_t prsa_private_to_binary(psync_rsa_privatekey_t rsa) { unsigned char buff[4096]; mbedtls_pk_context ctx; psync_binary_rsa_key_t ret; int len; int result; mbedtls_pk_init(&ctx); #if MBEDTLS_VERSION_MAJOR >= 4 result = mbedtls_pk_copy_from_psa(rsa->key_id, &ctx); #else result = mbedtls_pk_setup(&ctx, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)); if (!result) result = mbedtls_rsa_copy(mbedtls_pk_rsa(ctx), &rsa->ctx); #endif if (result) { mbedtls_pk_free(&ctx); return PSYNC_INVALID_BIN_RSA; } len = mbedtls_pk_write_key_der(&ctx, buff, sizeof(buff)); mbedtls_pk_free(&ctx); if (len <= 0) return PSYNC_INVALID_BIN_RSA; ret = pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_encrypted_data_struct_t, data) + len); ret->datalen = len; memcpy(ret->data, buff + sizeof(buff) - len, len); putil_wipe(buff + sizeof(buff) - len, len); return ret; } psync_rsa_publickey_t prsa_load_public(const unsigned char *keydata, size_t keylen) { mbedtls_pk_context ctx; int ret; mbedtls_pk_init(&ctx); ret = mbedtls_pk_parse_public_key(&ctx, keydata, keylen); if (unlikely(ret)) { pdbg_logf(D_WARNING, "pk_parse_public_key failed with code %d (-0x%04x); " "resorting to mbedtls 1.x RSA fallback", ret, -ret); mbedtls_pk_free(&ctx); return PSYNC_INVALID_RSA; } #if MBEDTLS_VERSION_MAJOR >= 4 psync_rsa_publickey_t rsa; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; rsa = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*rsa)); ret = mbedtls_pk_get_psa_attributes(&ctx, PSA_KEY_USAGE_ENCRYPT, &attributes); if (!ret) ret = mbedtls_pk_import_into_psa(&ctx, &attributes, &rsa->key_id); if (!ret) { rsa->bits = psa_get_key_bits(&attributes); psa_reset_key_attributes(&attributes); mbedtls_pk_free(&ctx); return rsa; } psa_reset_key_attributes(&attributes); mbedtls_pk_free(&ctx); pmem_free(PMEM_SUBSYS_OTHER, rsa); return PSYNC_INVALID_RSA; #else psync_rsa_publickey_t rsa; rsa = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*rsa)); mbedtls_rsa_init(&rsa->ctx); mbedtls_rsa_set_padding(&rsa->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); ret = mbedtls_rsa_copy(&rsa->ctx, mbedtls_pk_rsa(ctx)); mbedtls_pk_free(&ctx); if (unlikely(ret)) { pdbg_logf(D_WARNING, "rsa_copy failed with code %d", ret); mbedtls_rsa_free(&rsa->ctx); pmem_free(PMEM_SUBSYS_OTHER, rsa); return PSYNC_INVALID_RSA; } mbedtls_rsa_set_padding(&rsa->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); return rsa; #endif } // mbedtls 3.x: mbedtls_pk_parse_key rejects keys with trailing garbage. This // function gets the actual key length from the ASN.1 header, trims any bytes // that exceed that length, and writes the trimmed key and length to keydata // and keylen. static void trim_der_key(unsigned char *keydata, size_t *keylen) { size_t len, header_size, correct_len; unsigned char *p = (unsigned char *)keydata; const unsigned char *end; int ret; unsigned char *trimmed; end = keydata + *keylen; ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret != 0) { return; } header_size = p - keydata; correct_len = header_size + len; if (header_size > *keylen || correct_len > *keylen) { return; } trimmed = pmem_malloc(PMEM_SUBSYS_OTHER, correct_len); if (!trimmed) return; memcpy(trimmed, keydata, correct_len); memcpy(keydata, trimmed, correct_len); pmem_free(PMEM_SUBSYS_OTHER, trimmed); *keylen = correct_len; } psync_rsa_privatekey_t prsa_load_private(const unsigned char *keydata, size_t keylen) { mbedtls_pk_context ctx; int ret; trim_der_key((unsigned char *)keydata, &keylen); mbedtls_pk_init(&ctx); #if MBEDTLS_VERSION_MAJOR >= 4 ret = mbedtls_pk_parse_key(&ctx, keydata, keylen, NULL, 0); #else ret = mbedtls_pk_parse_key(&ctx, keydata, keylen, NULL, 0, rng_get, &rng); #endif if (unlikely(ret)) { ssl_pdbg_logf(D_WARNING, ret, "pk_parse_key failed"); mbedtls_pk_free(&ctx); return PSYNC_INVALID_RSA; } #if MBEDTLS_VERSION_MAJOR >= 4 psync_rsa_privatekey_t rsa; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; rsa = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*rsa)); ret = mbedtls_pk_get_psa_attributes(&ctx, PSA_KEY_USAGE_DECRYPT, &attributes); if (!ret) ret = mbedtls_pk_import_into_psa(&ctx, &attributes, &rsa->key_id); if (!ret) { rsa->bits = psa_get_key_bits(&attributes); psa_reset_key_attributes(&attributes); mbedtls_pk_free(&ctx); return rsa; } psa_reset_key_attributes(&attributes); mbedtls_pk_free(&ctx); pmem_free(PMEM_SUBSYS_OTHER, rsa); return PSYNC_INVALID_RSA; #else psync_rsa_privatekey_t rsa; rsa = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*rsa)); mbedtls_rsa_init(&rsa->ctx); mbedtls_rsa_set_padding(&rsa->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); ret = mbedtls_rsa_copy(&rsa->ctx, mbedtls_pk_rsa(ctx)); mbedtls_pk_free(&ctx); if (unlikely(ret)) { pdbg_logf(D_WARNING, "rsa_copy failed with code %d", ret); mbedtls_rsa_free(&rsa->ctx); pmem_free(PMEM_SUBSYS_OTHER, rsa); return PSYNC_INVALID_RSA; } mbedtls_rsa_set_padding(&rsa->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1); return rsa; #endif } psync_rsa_publickey_t prsa_binary_to_public(psync_binary_rsa_key_t bin) { return prsa_load_public(bin->data, bin->datalen); } psync_rsa_privatekey_t prsa_binary_to_private(psync_binary_rsa_key_t bin) { return prsa_load_private(bin->data, bin->datalen); } psync_symmetric_key_t psymkey_generate(const char *password, size_t keylen, const unsigned char *salt, size_t saltlen, size_t iterations) { psync_symmetric_key_t key = (psync_symmetric_key_t)pmem_malloc(PMEM_SUBSYS_OTHER, keylen + offsetof(psync_symmetric_key_struct_t, key)); key->keylen = keylen; #if MBEDTLS_VERSION_MAJOR >= 4 psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); psa_status_t status; status = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_512)); if (status == PSA_SUCCESS) status = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, iterations); if (status == PSA_SUCCESS) status = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, salt, saltlen); if (status == PSA_SUCCESS) status = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, (const uint8_t *)password, strlen(password)); if (status == PSA_SUCCESS) status = psa_key_derivation_set_capacity(&op, keylen); if (status == PSA_SUCCESS) status = psa_key_derivation_output_bytes(&op, key->key, keylen); psa_key_derivation_abort(&op); if (status != PSA_SUCCESS) { pdbg_logf(D_ERROR, "PSA PBKDF2 failed with status %d", status); pmem_free(PMEM_SUBSYS_OTHER, key); return PSYNC_INVALID_SYM_KEY; } #else mbedtls_md_context_t ctx; mbedtls_md_init(&ctx); const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); mbedtls_md_setup(&ctx, md_info, 1); mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)password, strlen(password), salt, saltlen, iterations, keylen, key->key); mbedtls_md_free(&ctx); #endif return key; } char *psymkey_derive(const char *username, const char *passphrase) { unsigned char *usercopy; unsigned char usersha512[PSYNC_SHA512_DIGEST_LEN], passwordbin[32]; mbedtls_md_context_t ctx; size_t userlen, i; userlen = strlen(username); usercopy = pmem_malloc_array(PMEM_SUBSYS_OTHER, userlen, sizeof(unsigned char)); for (i = 0; i < userlen; i++) if ((unsigned char)username[i] <= 127) usercopy[i] = tolower((unsigned char)username[i]); else usercopy[i] = '*'; psync_sha512(usercopy, userlen, usersha512); pmem_free(PMEM_SUBSYS_OTHER, usercopy); #if MBEDTLS_VERSION_MAJOR >= 4 psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); psa_status_t status; status = psa_key_derivation_setup(&op, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_512)); if (status == PSA_SUCCESS) status = psa_key_derivation_input_integer(&op, PSA_KEY_DERIVATION_INPUT_COST, 5000); if (status == PSA_SUCCESS) status = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SALT, usersha512, sizeof(usersha512)); if (status == PSA_SUCCESS) status = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, (const uint8_t *)passphrase, strlen(passphrase)); if (status == PSA_SUCCESS) status = psa_key_derivation_set_capacity(&op, sizeof(passwordbin)); if (status == PSA_SUCCESS) status = psa_key_derivation_output_bytes(&op, passwordbin, sizeof(passwordbin)); psa_key_derivation_abort(&op); if (status != PSA_SUCCESS) { pdbg_logf(D_ERROR, "PSA PBKDF2 failed with status %d", status); return NULL; } #else mbedtls_md_init(&ctx); const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); mbedtls_md_setup(&ctx, md_info, 1); mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)passphrase, strlen(passphrase), usersha512, PSYNC_SHA512_DIGEST_LEN, 5000, sizeof(passwordbin), passwordbin); mbedtls_md_free(&ctx); #endif usercopy = putil_base64_encode(passwordbin, sizeof(passwordbin), &userlen); return (char *)usercopy; } psync_encrypted_symmetric_key_t prsa_encrypt_data(psync_rsa_publickey_t rsa, const unsigned char *data, size_t datalen) { psync_encrypted_symmetric_key_t ret; int code; size_t rsalen; #if MBEDTLS_VERSION_MAJOR >= 4 rsalen = (rsa->bits + 7) / 8; #else rsalen = mbedtls_rsa_get_len(&rsa->ctx); #endif ret = (psync_encrypted_symmetric_key_t)pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_encrypted_data_struct_t, data) + rsalen); #if MBEDTLS_VERSION_MAJOR >= 4 size_t olen; if (psa_asymmetric_encrypt(rsa->key_id, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_1), data, datalen, NULL, 0, ret->data, rsalen, &olen) != PSA_SUCCESS) { pmem_free(PMEM_SUBSYS_OTHER, ret); return PSYNC_INVALID_ENC_SYM_KEY; } ret->datalen = olen; return ret; #else if ((code = mbedtls_rsa_rsaes_oaep_encrypt( &rsa->ctx, rng_get, &rng, NULL, 0, datalen, data, ret->data))) { pmem_free(PMEM_SUBSYS_OTHER, ret); pdbg_logf( D_WARNING, "rsa_rsaes_oaep_encrypt failed with error=%d, datalen=%lu, rsasize=%d", code, (unsigned long)datalen, (int)rsalen); return PSYNC_INVALID_ENC_SYM_KEY; } ret->datalen = rsalen; pdbg_logf(D_NOTICE, "datalen=%lu", (unsigned long)ret->datalen); return ret; #endif } psync_symmetric_key_t prsa_decrypt_data(psync_rsa_privatekey_t rsa, const unsigned char *data, size_t datalen) { unsigned char buff[2048]; psync_symmetric_key_t ret; size_t len; #if MBEDTLS_VERSION_MAJOR >= 4 if (psa_asymmetric_decrypt(rsa->key_id, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_1), data, datalen, NULL, 0, buff, sizeof(buff), &len) != PSA_SUCCESS) return PSYNC_INVALID_SYM_KEY; #else if (mbedtls_rsa_rsaes_oaep_decrypt(&rsa->ctx, rng_get, &rng, NULL, 0, &len, data, buff, sizeof(buff))) return PSYNC_INVALID_SYM_KEY; #endif ret = (psync_symmetric_key_t)pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_symmetric_key_struct_t, key) + len); ret->keylen = len; memcpy(ret->key, buff, len); putil_wipe(buff, len); return ret; } psync_encrypted_symmetric_key_t psymkey_encrypt(psync_rsa_publickey_t rsa, const psync_symmetric_key_t key) { return prsa_encrypt_data(rsa, key->key, key->keylen); } psync_symmetric_key_t psymkey_decrypt( psync_rsa_privatekey_t rsa, const psync_encrypted_symmetric_key_t enckey) { return prsa_decrypt_data(rsa, enckey->data, enckey->datalen); } psync_aes256_encoder paes_create_encoder(psync_symmetric_key_t key) { psync_aes256_encoder aes; pdbg_assert(key->keylen >= PSYNC_AES256_KEY_SIZE); aes = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*aes)); #if MBEDTLS_VERSION_MAJOR >= 4 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_status_t status; psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); psa_set_key_bits(&attributes, 256); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); psa_set_key_algorithm(&attributes, PSA_ALG_ECB_NO_PADDING); status = psa_import_key(&attributes, key->key, PSYNC_AES256_KEY_SIZE, &aes->key_id); psa_reset_key_attributes(&attributes); if (status != PSA_SUCCESS) { pmem_free(PMEM_SUBSYS_OTHER, aes); return PSYNC_INVALID_ENCODER; } #else if (mbedtls_aes_setkey_enc(&aes->ctx, key->key, 256)) { pmem_free(PMEM_SUBSYS_OTHER, aes); return PSYNC_INVALID_ENCODER; } #endif return aes; } void paes_free_encoder(psync_aes256_encoder aes) { #if MBEDTLS_VERSION_MAJOR >= 4 psa_destroy_key(aes->key_id); #endif putil_wipe(aes, sizeof(*aes)); pmem_free(PMEM_SUBSYS_OTHER, aes); } psync_aes256_decoder paes_create_decoder(psync_symmetric_key_t key) { psync_aes256_decoder aes; pdbg_assert(key->keylen >= PSYNC_AES256_KEY_SIZE); aes = pmem_malloc(PMEM_SUBSYS_OTHER, sizeof(*aes)); #if MBEDTLS_VERSION_MAJOR >= 4 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_status_t status; psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); psa_set_key_bits(&attributes, 256); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); psa_set_key_algorithm(&attributes, PSA_ALG_ECB_NO_PADDING); status = psa_import_key(&attributes, key->key, PSYNC_AES256_KEY_SIZE, &aes->key_id); psa_reset_key_attributes(&attributes); if (status != PSA_SUCCESS) { pmem_free(PMEM_SUBSYS_OTHER, aes); return PSYNC_INVALID_DECODER; } #else if (mbedtls_aes_setkey_dec(&aes->ctx, key->key, 256)) { pmem_free(PMEM_SUBSYS_OTHER, aes); return PSYNC_INVALID_DECODER; } #endif return aes; } void paes_free_decoder(psync_aes256_decoder aes) { #if MBEDTLS_VERSION_MAJOR >= 4 psa_destroy_key(aes->key_id); #endif putil_wipe(aes, sizeof(*aes)); pmem_free(PMEM_SUBSYS_OTHER, aes); } psync_rsa_signature_t prsa_sign_sha256_hash(psync_rsa_privatekey_t rsa, const unsigned char *data) { psync_rsa_signature_t ret; int padding, hash_id; size_t rsalen; #if MBEDTLS_VERSION_MAJOR >= 4 rsalen = (rsa->bits + 7) / 8; #else rsalen = mbedtls_rsa_get_len(&rsa->ctx); #endif ret = (psync_rsa_signature_t)pmem_malloc(PMEM_SUBSYS_OTHER, offsetof(psync_symmetric_key_struct_t, key) + rsalen); if (!ret) return (psync_rsa_signature_t)(void *)PERROR_NO_MEMORY; ret->datalen = rsalen; #if MBEDTLS_VERSION_MAJOR >= 4 size_t olen; if (psa_sign_hash(rsa->key_id, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), data, PSYNC_SHA256_DIGEST_LEN, ret->data, rsalen, &olen) != PSA_SUCCESS) { pmem_free(PMEM_SUBSYS_OTHER, ret); return (psync_rsa_signature_t)(void *)PSYNC_CRYPTO_NOT_STARTED; } ret->datalen = olen; return ret; #else /* Save current padding settings using getter functions */ padding = mbedtls_rsa_get_padding_mode(&rsa->ctx); hash_id = mbedtls_rsa_get_md_alg(&rsa->ctx); mbedtls_rsa_set_padding(&rsa->ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256); if (mbedtls_rsa_rsassa_pss_sign(&rsa->ctx, rng_get, &rng, MBEDTLS_MD_SHA256, PSYNC_SHA256_DIGEST_LEN, data, ret->data)) { pmem_free(PMEM_SUBSYS_OTHER, ret); mbedtls_rsa_set_padding(&rsa->ctx, padding, hash_id); return (psync_rsa_signature_t)(void *)PSYNC_CRYPTO_NOT_STARTED; } mbedtls_rsa_set_padding(&rsa->ctx, padding, hash_id); return ret; #endif }