Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The explanation of entropy configuration in the tutorial is incompatible with PSA #9984

Open
Martmists-GH opened this issue Feb 15, 2025 · 1 comment
Labels
bug component-docs Docs / website issues filed here for tracking

Comments

@Martmists-GH
Copy link

Martmists-GH commented Feb 15, 2025

Summary

psa_crypto_init (and specifically, mbedtls_psa_crypto_init_subsystem) pass &global_data.rng to mbedtls_psa_random_init and mbedtls_psa_random_seed. If platform entropy is disabled, there is no way to provide a random generator to these methods.

System information

Mbed TLS version (number or commit id): 3.6.2
Operating system and version: HorizonOS 19.0.1 (Nintendo Switch)
Configuration (if not default, please attach mbedtls_config.h): Too big for pasting contents, .h extension not allowed.
Compiler and options (if you used a pre-built binary, please indicate how you obtained it): devkitpro
Additional environment information: N/A

Expected behavior

mbedtls_ssl_handshake uses the configured entropy source (following the guide) to which I've provided my source with mbedtls_entropy_add_source.

Actual behavior

A different entropy source is used which returns -64 (MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED)

Steps to reproduce

Follow the guide linked above with MBEDTLS_NO_PLATFORM_ENTROPY enabled in config, and add your own entropy source to your entropy context.

Additional information

expand for source file
#include <cstring>
#include <regex>
#include "WebSocket.h"
#include "logger/logger.h"
#include "crypto.h"
#include "mbedtls/debug.h"

auto url_regex = std::regex(R"(wss://([^:/]+)(:\d+)?(/.+)?)");

void debug_func(void *, int level, const char * file, int line, const char * str) {
Logger::log("[MBEDTLS] %s\n", str);
}

int WebSocket::init(std::string url) {
Logger::log("Initializing WS\n");

std::smatch m;
std::regex_match(url, m, url_regex);
if (m.size() == 0) return -1;

std::string host = m[1].str();
std::string port;
std::string path;
if (m.size() >= 3) {
    port = m[2].str();
    if (port.empty()) {
        port = "443";
    }
} else {
    port = "443";
}
if (m.size() >= 4) {
    path = m[3].str();
    if (path.empty()) {
        path = "/";
    }
} else {
    path = "/";
}

int tmp;

mbedtls_net_init(&server_fd);
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
mbedtls_x509_crt_init(&cacert);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);

tmp = mbedtls_entropy_add_source(&entropy, [](void *data, unsigned char *output, size_t len,
                                        size_t *olen){
    Logger::log("Requested: %d\n", len);
    nn::crypto::GenerateCryptographicallyRandomBytes(output, len);
    *olen = len;
    return 0;
}, nullptr, 128, MBEDTLS_ENTROPY_SOURCE_STRONG);
if (tmp < 0) {
    Logger::log("Failed to add entropy source\n");
    goto exit;
}

tmp = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)"Test", strlen("Test"));
if (tmp != 0) {
    Logger::log("Seed failed: %d\n", tmp);
    goto exit;
}

Logger::log("Opening connection to: [%s]:[%s]\n", host.c_str(), port.c_str());
tmp = mbedtls_net_connect(&server_fd, host.c_str(), port.c_str(), MBEDTLS_NET_PROTO_TCP);
if (tmp != 0) {
    Logger::log("Connect failed: %d\n", tmp);
    goto exit;
}

Logger::log("Setting up SSL defaults\n");
tmp = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
                                  MBEDTLS_SSL_TRANSPORT_STREAM,
                                  MBEDTLS_SSL_PRESET_DEFAULT);
if (tmp != 0) {
    Logger::log("SSL Defaults failed: %d\n", tmp);
    goto exit;
}

Logger::log("Setting up SSL Auth\n");
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);

Logger::log("Setting up SSL rng/dbg\n");
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_ssl_conf_dbg(&conf, debug_func, nullptr);
mbedtls_debug_set_threshold(4);

Logger::log("Setting up SSL core\n");
if (mbedtls_ssl_setup(&ssl, &conf) != 0) {
    Logger::log("SSL Setup failed!\n");
    goto exit;
}

Logger::log("Finishing up SSL\n");
if (mbedtls_ssl_set_hostname(&ssl, host.c_str()) != 0) {
    Logger::log("SSL Hostname failed!\n");
    goto exit;
}
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, nullptr);

tmp = mbedtls_ssl_handshake(&ssl);
if (tmp < 0) {
    Logger::log("SSL Handshake failed: %d\n", tmp);
    goto exit;
}

char request_headers[1024];
snprintf(request_headers, 1024, "GET %s HTTP/1.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nHost: %s:%s\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n", path.c_str(), host.c_str(), port.c_str());
if (write(request_headers, strlen(request_headers)) < 0) {
    Logger::log("WS Handshake failed!\n");
    goto exit;
}

Logger::log("WS init success!");

return 0;

exit:
Logger::log("WS init failed!\n");
close();
return -1;
}

void WebSocket::close() {
// TODO: Send close packet if connected?

mbedtls_net_free(&server_fd);
mbedtls_ssl_free(&ssl);
mbedtls_ssl_config_free(&conf);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);

}

int WebSocket::write(void* buf, size_t size) {
if (mbedtls_ssl_write(&ssl, (const unsigned char*) buf, size) < 0) {
close();
return -1;
}
return 0;
}

int WebSocket::read(void* buf, size_t size) {
auto ret = mbedtls_ssl_read(&ssl, (unsigned char*) buf, size);

if (ret >= 0) return 0;

if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
    return 0;
}

close();
return -1;

}

struct ws_header {
bool fin : 1;
bool rsv1 : 1;
bool rsv2 : 1;
bool rsv3 : 1;
u8 opcode : 4;
// ---
bool mask : 1;
u8 payload_len : 7;
};

int WebSocket::send(const char *text) {
auto size = strlen(text);
int hsize;
if (size > 0xffff) {
hsize = 127;
} else if (size > 125) {
hsize = 126;
} else {
hsize = size;
}

ws_header h = {0};
h.fin = true;
h.opcode = 1;
h.mask = 1;
h.payload_len = hsize;

write(&h, sizeof(ws_header));
if (hsize == 126) {
    auto tmp = __builtin_bswap16((uint16_t)size);
    write(&tmp, sizeof(uint16_t));
} else if (hsize == 127) {
    auto tmp = __builtin_bswap64((uint64_t)size);
    write(&tmp, sizeof(uint64_t));
}

// Empty mask
uint8_t mask[4] = {0};
write(&mask, sizeof(mask));

write((void *) text, size);

return 0;

}

std::string WebSocket::recv() {
start:

ws_header h;
if (read(&h, sizeof(ws_header)) < 0) {
    return "";
}

auto hsize = __builtin_bswap64(h.payload_len);

uint64_t size;
if (hsize == 126) {
    uint16_t tmp;
    if (read(&tmp, sizeof(uint16_t)) < 0) {
        return "";
    }
    size = (uint64_t) __builtin_bswap16(tmp);
} else if (hsize == 127) {
    uint64_t tmp;
    if (read(&tmp, sizeof(uint64_t)) < 0) {
        return "";
    }
    size = __builtin_bswap64(tmp);
} else {
    size = (uint64_t) hsize;
}

char buffer[size];
memset(buffer, 0, size);

if (read(buffer, size) < 0) {
    return "";
}

// Server must never mask, we can safely ignore that part

// If ping, respond with pong and restart read
if (h.opcode == 9) {
    h.opcode = 10;
    write(&h, sizeof(h));
    if (hsize == 126) {
        auto tmp = __builtin_bswap16((uint16_t)size);
        write(&tmp, sizeof(uint16_t));
    } else if (hsize == 127) {
        auto tmp = __builtin_bswap64(size);
        write(&tmp, sizeof(uint64_t));
    }
    write(buffer, size);

    goto start;
}

// If close, close socket
if (h.opcode == 8) {
    Logger::log("Received close with status code %d\n", __builtin_bswap16(((uint16_t*)&buffer)[0]));

    // Respond with same close payload
    write(&h, sizeof(h));
    if (hsize == 126) {
        auto tmp = __builtin_bswap16((uint16_t)size);
        write(&tmp, sizeof(uint16_t));
    } else if (hsize == 127) {
        auto tmp = __builtin_bswap64(size);
        write(&tmp, sizeof(uint64_t));
    }
    write(buffer, size);

    // Close socket
    close();
    return "";
}

if (h.opcode == 1) {
    // If text, return text
    return std::string(buffer, size);
} else if (h.opcode == 2) {
    // If binary, return binary data
    return std::string(buffer, size);
}

if (!h.fin) {
    // TODO: Handle additional frames?
    Logger::log("Unhandled unfinished frame!\n");
}

// If unknown opcode, ignore
Logger::log("Unhandled opcode: %d\n", h.opcode);

return "";

}

@gilles-peskine-arm
Copy link
Contributor

The PSA subsystem has its own random generator. It doesn't use the RNG passed to mbedtls_ssl_conf_rng, and more generally it does not support runtime configuration. This was a deliberate design choice, because very few scenarios require a runtime-configurable RNG: highly constrained platforms typically have an entropy source that's configured by the system integrator as mbedtls_hardware_poll(), and higher-end platforms such as Linux or Windows have an entropy source in the operating system. Conversely, experience shows that giving application writers the ability to configure entropy sources tends to result is misconfigurations.

I'm afraid the tutorial is somewhat out of date. The recommended way to configure a platform-specific entropy source is through MBEDTLS_ENTROPY_HARDWARE_ALT.

@gilles-peskine-arm gilles-peskine-arm added bug component-docs Docs / website issues filed here for tracking labels Feb 16, 2025
@gilles-peskine-arm gilles-peskine-arm changed the title psa_crypto_init does not use provided entropy source The explanation of entropy configuration in the tutorial is incompatible with PSA Feb 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug component-docs Docs / website issues filed here for tracking
Projects
Status: No status
Development

No branches or pull requests

2 participants