feat: common TLS library, weather app, UI scaling fix in Window Server, and more

This commit is contained in:
2026-02-21 13:05:28 +01:00
parent 9d5e7eac8d
commit e0bfc97f0f
32 changed files with 1440 additions and 1048 deletions
+2 -1
View File
@@ -16,6 +16,7 @@ endif
# ---- Paths ----
BEARSSL := ../../lib/bearssl
TLS_LIB := ../../lib/tls
LIBC_LIB := ../../lib/libc
LIBC_INC := ../../include/libc
PROG_INC := ../../include
@@ -66,7 +67,7 @@ LDFLAGS := \
# ---- Libraries ----
LIBS := $(BEARSSL)/libbearssl.a $(LIBC_LIB)/liblibc.a
LIBS := $(TLS_LIB)/libtls.a $(BEARSSL)/libbearssl.a $(LIBC_LIB)/liblibc.a
# ---- Source files ----
+27 -255
View File
@@ -10,9 +10,9 @@
#include <zenith/heap.h>
#include <gui/gui.hpp>
#include <gui/truetype.hpp>
#include <tls/tls.hpp>
extern "C" {
#include <bearssl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@@ -27,11 +27,11 @@ using namespace gui::colors;
static constexpr int INIT_W = 820;
static constexpr int INIT_H = 580;
static constexpr int TOOLBAR_H = 42;
static int TOOLBAR_H = 42;
static constexpr int SCROLLBAR_W = 14;
static constexpr int FONT_SIZE = 18;
static constexpr int TITLE_SIZE = 32;
static constexpr int SECTION_SIZE = 24;
static int FONT_SIZE = 18;
static int TITLE_SIZE = 32;
static int SECTION_SIZE = 24;
static constexpr int TEXT_PAD = 16;
static constexpr int RESP_MAX = 131072;
static constexpr int MAX_LINES = 2000;
@@ -79,13 +79,7 @@ static TrueTypeFont* g_font_serif = nullptr; // NotoSerif SemiBold (headings)
// TLS state (lazy-init on first search)
static bool g_tls_ready = false;
static uint32_t g_server_ip = 0;
struct TrustAnchors {
br_x509_trust_anchor* anchors;
size_t count;
size_t capacity;
};
static TrustAnchors g_tas = {nullptr, 0, 0};
static tls::TrustAnchors g_tas = {nullptr, 0, 0};
// ============================================================================
// Pixel buffer helpers
@@ -124,252 +118,23 @@ static void px_rect_outline(uint32_t* px, int bw, int x, int y, int w, int h, Co
}
// ============================================================================
// Trust anchor loading
// UI scale
// ============================================================================
struct DerAccum { unsigned char* data; size_t len, cap; };
struct DnAccum { unsigned char* data; size_t len, cap; };
static void der_append(void* ctx, const void* buf, size_t len) {
DerAccum* a = (DerAccum*)ctx;
if (a->len + len > a->cap) {
size_t nc = a->cap * 2;
if (nc < a->len + len) nc = a->len + len + 4096;
unsigned char* nb = (unsigned char*)malloc(nc);
if (!nb) return;
if (a->data) { memcpy(nb, a->data, a->len); free(a->data); }
a->data = nb; a->cap = nc;
static void apply_scale(int scale) {
switch (scale) {
case 0: FONT_SIZE=14; TITLE_SIZE=26; SECTION_SIZE=20; TOOLBAR_H=34; break;
case 2: FONT_SIZE=22; TITLE_SIZE=40; SECTION_SIZE=30; TOOLBAR_H=52; break;
default: FONT_SIZE=18; TITLE_SIZE=32; SECTION_SIZE=24; TOOLBAR_H=42; break;
}
memcpy(a->data + a->len, buf, len);
a->len += len;
}
static void dn_append(void* ctx, const void* buf, size_t len) {
DnAccum* a = (DnAccum*)ctx;
if (a->len + len > a->cap) {
size_t nc = a->cap * 2;
if (nc < a->len + len) nc = a->len + len + 256;
unsigned char* nb = (unsigned char*)malloc(nc);
if (!nb) return;
if (a->data) { memcpy(nb, a->data, a->len); free(a->data); }
a->data = nb; a->cap = nc;
}
memcpy(a->data + a->len, buf, len);
a->len += len;
}
static void ta_add(TrustAnchors* tas, const br_x509_trust_anchor* ta) {
if (tas->count >= tas->capacity) {
size_t nc = tas->capacity == 0 ? 64 : tas->capacity * 2;
br_x509_trust_anchor* na = (br_x509_trust_anchor*)malloc(nc * sizeof(*na));
if (!na) return;
if (tas->anchors) { memcpy(na, tas->anchors, tas->count * sizeof(*na)); free(tas->anchors); }
tas->anchors = na; tas->capacity = nc;
}
tas->anchors[tas->count++] = *ta;
}
static bool process_cert_der(TrustAnchors* tas, const unsigned char* der, size_t der_len) {
static br_x509_decoder_context dc;
DnAccum dn = {nullptr, 0, 0};
br_x509_decoder_init(&dc, dn_append, &dn);
br_x509_decoder_push(&dc, der, der_len);
br_x509_pkey* pk = br_x509_decoder_get_pkey(&dc);
if (!pk) { if (dn.data) free(dn.data); return false; }
br_x509_trust_anchor ta;
memset(&ta, 0, sizeof(ta));
ta.dn.data = dn.data; ta.dn.len = dn.len; ta.flags = 0;
if (br_x509_decoder_isCA(&dc)) 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 = (unsigned char*)malloc(pk->key.rsa.nlen);
if (ta.pkey.key.rsa.n) memcpy(ta.pkey.key.rsa.n, pk->key.rsa.n, pk->key.rsa.nlen);
ta.pkey.key.rsa.elen = pk->key.rsa.elen;
ta.pkey.key.rsa.e = (unsigned char*)malloc(pk->key.rsa.elen);
if (ta.pkey.key.rsa.e) memcpy(ta.pkey.key.rsa.e, 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 = (unsigned char*)malloc(pk->key.ec.qlen);
if (ta.pkey.key.ec.q) memcpy(ta.pkey.key.ec.q, pk->key.ec.q, pk->key.ec.qlen);
break;
default:
if (dn.data) free(dn.data);
return false;
}
ta_add(tas, &ta);
return true;
}
static TrustAnchors load_trust_anchors() {
TrustAnchors tas = {nullptr, 0, 0};
int fh = zenith::open("0:/etc/ca-certificates.crt");
if (fh < 0) return tas;
uint64_t fsize = zenith::getsize(fh);
if (fsize == 0 || fsize > 512 * 1024) { zenith::close(fh); return tas; }
unsigned char* pem = (unsigned char*)malloc(fsize + 1);
if (!pem) { zenith::close(fh); return tas; }
zenith::read(fh, pem, 0, fsize);
zenith::close(fh);
pem[fsize] = 0;
static br_pem_decoder_context pc;
br_pem_decoder_init(&pc);
DerAccum der = {nullptr, 0, 0};
bool inCert = false;
size_t offset = 0;
while (offset < fsize) {
size_t pushed = br_pem_decoder_push(&pc, pem + offset, fsize - offset);
offset += pushed;
int ev = br_pem_decoder_event(&pc);
if (ev == BR_PEM_BEGIN_OBJ) {
inCert = (strcmp(br_pem_decoder_name(&pc), "CERTIFICATE") == 0);
br_pem_decoder_setdest(&pc, inCert ? der_append : nullptr, inCert ? &der : nullptr);
if (inCert) der.len = 0;
} else if (ev == BR_PEM_END_OBJ) {
if (inCert && der.len > 0) process_cert_der(&tas, der.data, der.len);
inCert = false;
} else if (ev == BR_PEM_ERROR) {
break;
}
}
if (der.data) free(der.data);
free(pem);
return tas;
if (g_font) g_line_h = g_font->get_line_height(FONT_SIZE) + 4;
}
// ============================================================================
// BearSSL time
// HTTPS fetch wrapper
// ============================================================================
static void get_bearssl_time(uint32_t* days, uint32_t* seconds) {
Zenith::DateTime dt;
zenith::gettime(&dt);
int y = dt.Year, m = dt.Month, d = dt.Day;
uint32_t total = 365u * (uint32_t)y
+ (uint32_t)(y/4) - (uint32_t)(y/100) + (uint32_t)(y/400);
const int md[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
for (int mo = 1; mo < m && mo <= 12; mo++) total += md[mo];
if (y%4==0 && (y%100!=0 || y%400==0) && m > 2) total++;
total += d - 1;
*days = total;
*seconds = (uint32_t)(dt.Hour*3600 + dt.Minute*60 + dt.Second);
}
// ============================================================================
// TLS I/O
// ============================================================================
static int tls_send_all(int fd, const unsigned char* data, size_t len) {
size_t sent = 0;
uint64_t deadline = zenith::get_milliseconds() + 15000;
while (sent < len) {
int r = zenith::send(fd, data + sent, (uint32_t)(len - sent));
if (r > 0) { sent += r; deadline = zenith::get_milliseconds() + 15000; }
else if (r < 0) return -1;
else { if (zenith::get_milliseconds() >= deadline) return -1; zenith::sleep_ms(1); }
}
return (int)sent;
}
static int tls_recv_some(int fd, unsigned char* buf, size_t maxlen) {
uint64_t deadline = zenith::get_milliseconds() + 15000;
while (true) {
int r = zenith::recv(fd, buf, (uint32_t)maxlen);
if (r > 0) return r;
if (r < 0) return -1;
if (zenith::get_milliseconds() >= deadline) return -1;
zenith::sleep_ms(1);
}
}
static int tls_exchange(int fd, br_ssl_engine_context* eng,
const char* request, int reqLen,
char* respBuf, int respMax) {
bool requestSent = false;
int respLen = 0;
uint64_t deadline = zenith::get_milliseconds() + 30000;
while (true) {
unsigned state = br_ssl_engine_current_state(eng);
if (state & BR_SSL_CLOSED) {
int err = br_ssl_engine_last_error(eng);
if (err != BR_ERR_OK && err != BR_ERR_IO && respLen == 0) return -1;
return respLen;
}
if (state & BR_SSL_SENDREC) {
size_t len; unsigned char* buf = br_ssl_engine_sendrec_buf(eng, &len);
int sent = tls_send_all(fd, buf, len);
if (sent < 0) { br_ssl_engine_close(eng); return respLen > 0 ? respLen : -1; }
br_ssl_engine_sendrec_ack(eng, len);
deadline = zenith::get_milliseconds() + 30000; continue;
}
if (state & BR_SSL_RECVAPP) {
size_t len; unsigned char* buf = br_ssl_engine_recvapp_buf(eng, &len);
size_t toCopy = len;
if (respLen + (int)toCopy > respMax - 1) toCopy = respMax - 1 - respLen;
if (toCopy > 0) { memcpy(respBuf + respLen, buf, toCopy); respLen += toCopy; }
br_ssl_engine_recvapp_ack(eng, len);
deadline = zenith::get_milliseconds() + 30000; continue;
}
if ((state & BR_SSL_SENDAPP) && !requestSent) {
size_t len; unsigned char* buf = br_ssl_engine_sendapp_buf(eng, &len);
size_t toWrite = (size_t)reqLen;
if (toWrite > len) toWrite = len;
memcpy(buf, request, toWrite);
br_ssl_engine_sendapp_ack(eng, toWrite);
br_ssl_engine_flush(eng, 0);
requestSent = true;
deadline = zenith::get_milliseconds() + 30000; continue;
}
if (state & BR_SSL_RECVREC) {
size_t len; unsigned char* buf = br_ssl_engine_recvrec_buf(eng, &len);
int got = tls_recv_some(fd, buf, len);
if (got < 0) { br_ssl_engine_close(eng); return respLen > 0 ? respLen : -1; }
br_ssl_engine_recvrec_ack(eng, got);
deadline = zenith::get_milliseconds() + 30000; continue;
}
if (zenith::get_milliseconds() >= deadline) return respLen > 0 ? respLen : -1;
zenith::sleep_ms(1);
}
}
static int wiki_fetch(const char* path, char* respBuf, int respMax) {
int fd = zenith::socket(Zenith::SOCK_TCP);
if (fd < 0) return -1;
if (zenith::connect(fd, g_server_ip, 443) < 0) { zenith::closesocket(fd); return -1; }
br_ssl_client_context* cc = (br_ssl_client_context*)malloc(sizeof(*cc));
br_x509_minimal_context* xc = (br_x509_minimal_context*)malloc(sizeof(*xc));
void* iobuf = malloc(BR_SSL_BUFSIZE_BIDI);
if (!cc || !xc || !iobuf) {
if (cc) free(cc); if (xc) free(xc); if (iobuf) free(iobuf);
zenith::closesocket(fd); return -1;
}
br_ssl_client_init_full(cc, xc, g_tas.anchors, g_tas.count);
uint32_t days, secs;
get_bearssl_time(&days, &secs);
br_x509_minimal_set_time(xc, days, secs);
unsigned char seed[32];
zenith::getrandom(seed, sizeof(seed));
br_ssl_engine_set_buffer(&cc->eng, iobuf, BR_SSL_BUFSIZE_BIDI, 1);
br_ssl_engine_inject_entropy(&cc->eng, seed, sizeof(seed));
if (!br_ssl_client_reset(cc, WIKI_HOST, 0)) {
zenith::closesocket(fd); free(cc); free(xc); free(iobuf); return -1;
}
static char request[2560];
int reqLen = snprintf(request, sizeof(request),
"GET %s HTTP/1.0\r\n"
@@ -379,11 +144,9 @@ static int wiki_fetch(const char* path, char* respBuf, int respMax) {
"Connection: close\r\n"
"\r\n",
path, WIKI_HOST);
int respLen = tls_exchange(fd, &cc->eng, request, reqLen, respBuf, respMax);
zenith::closesocket(fd);
free(cc); free(xc); free(iobuf);
return respLen;
return tls::https_fetch(WIKI_HOST, g_server_ip, 443,
request, reqLen, g_tas,
respBuf, respMax);
}
// ============================================================================
@@ -624,7 +387,7 @@ static void do_search(const char* query) {
"Error: could not resolve en.wikipedia.org");
g_phase = AppPhase::ERR; return;
}
g_tas = load_trust_anchors();
g_tas = tls::load_trust_anchors();
if (g_tas.count == 0) {
snprintf(g_status, sizeof(g_status), "Error: no CA certificates loaded");
g_phase = AppPhase::ERR; return;
@@ -793,6 +556,8 @@ extern "C" void _start() {
g_line_h = g_font->get_line_height(FONT_SIZE) + 4;
apply_scale(zenith::win_getscale());
// Create window
Zenith::WinCreateResult wres;
if (zenith::win_create("Wikipedia", INIT_W, INIT_H, &wres) < 0 || wres.id < 0)
@@ -823,6 +588,13 @@ extern "C" void _start() {
// ---- Handle event ----
if (ev.type == 3) break; // close
if (ev.type == 4) {
apply_scale(ev.scale.scale);
if (g_phase == AppPhase::DONE && g_line_count > 0) {
build_display_lines(g_title, g_extract_buf, g_extract_len);
}
}
if (ev.type == 2) {
int new_w = ev.resize.w;
int new_h = ev.resize.h;