mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
The browser could only reach plain HTTP, which is almost none of the real
web. This adds a from-scratch TLS 1.3 client so it can fetch https:// pages,
including real sites on the open internet.
Crypto primitives (all integer-only, freestanding, verified on the host
against their RFC test vectors before going in the kernel):
- crypto/x25519.c X25519 ECDH (RFC 7748), compact TweetNaCl field
arithmetic.
- crypto/chacha20poly1305.c ChaCha20 + Poly1305 AEAD (RFC 8439).
- crypto/hkdf.c HMAC-SHA256, HKDF (RFC 2104/5869) and the TLS 1.3
HKDF-Expand-Label / Derive-Secret helpers, on top
of the existing SHA-256.
A host harness checked every one against its published vectors (X25519 s6.1,
ChaCha20 s2.4.2, AEAD s2.8.2 incl. tamper rejection, HKDF TC1) -- all pass.
TLS 1.3 client (net/tls.c), single suite TLS_CHACHA20_POLY1305_SHA256 over
X25519: ClientHello (with SNI and ALPN http/1.1) -> ServerHello -> the RFC 8446
key schedule over a running transcript hash -> decrypt the server flight ->
send Finished -> switch to application keys. A small record layer handles the
plaintext-then-AEAD split and the middlebox ChangeCipherSpec. All large buffers
live in the heap-allocated connection, never on the 16 KB worker stack.
http_client routes https:// through TLS instead of raw TCP, so the async
worker, webget, netcheck and the GUI Web app all get HTTPS for free; the
redirect follower now follows http->https upgrades. The Web app's default page
is now https://example.com/.
Verified end to end on x86_64: the handshake interoperates with openssl
s_server (TLS 1.3, ChaCha20) -- GET returned HTTP 200 with the decrypted body
-- and, over QEMU's NAT, `webget https://example.com/` and the GUI Web app both
loaded the real Example Domain page served by Cloudflare over TLS 1.3 (HTTP
200, 828 bytes). The server only accepts our Finished if the transcript hash
and key schedule match byte-for-byte, so a 200 is proof the whole stack is
correct. Builds clean on x86_64, aarch64 and armv8i.
SECURITY CAVEAT, stated plainly: the server certificate is NOT verified. The
channel is encrypted and interoperates, but a man-in-the-middle could
impersonate the peer. Real certificate-chain validation (X.509 parsing,
signature checks, a CA bundle, hostname matching) is a large separate piece and
is not built here. This is fine for a hobby OS over a trusted local NAT; it is
not fit to protect real secrets. Also: TLS 1.3 only, one cipher suite, and
still no JavaScript/media -- so JS-heavy or app-style sites (YouTube) remain
out of reach; this makes static/text HTTPS pages readable.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
114 lines
3.8 KiB
C
114 lines
3.8 KiB
C
/*
|
|
* HMAC-SHA256 (RFC 2104), HKDF (RFC 5869), and the TLS 1.3 HKDF-Expand-Label
|
|
* / Derive-Secret helpers (RFC 8446 section 7.1).
|
|
*
|
|
* These sit on top of the existing SHA-256 and drive the TLS 1.3 key schedule:
|
|
* every traffic key, IV and Finished key is produced by an Expand-Label over a
|
|
* secret and the running transcript hash.
|
|
*/
|
|
#include <crypto/crypto.h>
|
|
#include <lib/string.h>
|
|
|
|
#define SHA256_BLOCK 64
|
|
#define SHA256_LEN 32
|
|
|
|
void hmac_sha256(const uint8_t* key, size_t key_len,
|
|
const uint8_t* msg, size_t msg_len, uint8_t out[32]) {
|
|
uint8_t k[SHA256_BLOCK];
|
|
uint8_t ipad[SHA256_BLOCK], opad[SHA256_BLOCK];
|
|
|
|
if (key_len > SHA256_BLOCK) {
|
|
sha256(key, key_len, k);
|
|
memset(k + SHA256_LEN, 0, SHA256_BLOCK - SHA256_LEN);
|
|
} else {
|
|
memcpy(k, key, key_len);
|
|
memset(k + key_len, 0, SHA256_BLOCK - key_len);
|
|
}
|
|
|
|
for (int i = 0; i < SHA256_BLOCK; i++) {
|
|
ipad[i] = k[i] ^ 0x36;
|
|
opad[i] = k[i] ^ 0x5c;
|
|
}
|
|
|
|
uint8_t inner[SHA256_LEN];
|
|
sha256_ctx_t c;
|
|
sha256_init(&c);
|
|
sha256_update(&c, ipad, SHA256_BLOCK);
|
|
sha256_update(&c, msg, msg_len);
|
|
sha256_final(inner, &c);
|
|
|
|
sha256_init(&c);
|
|
sha256_update(&c, opad, SHA256_BLOCK);
|
|
sha256_update(&c, inner, SHA256_LEN);
|
|
sha256_final(out, &c);
|
|
}
|
|
|
|
void hkdf_extract(const uint8_t* salt, size_t salt_len,
|
|
const uint8_t* ikm, size_t ikm_len, uint8_t prk[32]) {
|
|
static const uint8_t zero[SHA256_LEN] = { 0 };
|
|
if (!salt || salt_len == 0) { salt = zero; salt_len = SHA256_LEN; }
|
|
hmac_sha256(salt, salt_len, ikm, ikm_len, prk);
|
|
}
|
|
|
|
void hkdf_expand(const uint8_t prk[32], const uint8_t* info, size_t info_len,
|
|
uint8_t* out, size_t out_len) {
|
|
uint8_t t[SHA256_LEN];
|
|
size_t t_len = 0;
|
|
size_t done = 0;
|
|
uint8_t counter = 1;
|
|
|
|
while (done < out_len) {
|
|
// T(n) = HMAC(PRK, T(n-1) | info | counter)
|
|
sha256_ctx_t hc; (void)hc;
|
|
uint8_t block[SHA256_LEN + 512 + 1]; // t | info | counter
|
|
size_t bl = 0;
|
|
if (t_len) { memcpy(block, t, t_len); bl += t_len; }
|
|
if (info_len > 512) info_len = 512;
|
|
memcpy(block + bl, info, info_len); bl += info_len;
|
|
block[bl++] = counter;
|
|
|
|
hmac_sha256(prk, SHA256_LEN, block, bl, t);
|
|
t_len = SHA256_LEN;
|
|
|
|
size_t n = out_len - done < SHA256_LEN ? out_len - done : SHA256_LEN;
|
|
memcpy(out + done, t, n);
|
|
done += n;
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
// TLS 1.3 HKDF-Expand-Label:
|
|
// struct { uint16 length; opaque label<7..255>; opaque context<0..255>; }
|
|
// label is prefixed with "tls13 ".
|
|
void tls13_expand_label(const uint8_t secret[32], const char* label,
|
|
const uint8_t* context, size_t context_len,
|
|
uint8_t* out, size_t out_len) {
|
|
uint8_t info[512];
|
|
size_t p = 0;
|
|
|
|
info[p++] = (uint8_t)(out_len >> 8);
|
|
info[p++] = (uint8_t)(out_len & 0xff);
|
|
|
|
char full_label[64] = "tls13 ";
|
|
size_t ll = strlen(full_label);
|
|
size_t rest = strlen(label);
|
|
if (ll + rest > sizeof(full_label) - 1) rest = sizeof(full_label) - 1 - ll;
|
|
memcpy(full_label + ll, label, rest);
|
|
size_t label_len = ll + rest;
|
|
|
|
info[p++] = (uint8_t)label_len;
|
|
memcpy(info + p, full_label, label_len); p += label_len;
|
|
|
|
info[p++] = (uint8_t)context_len;
|
|
if (context_len) { memcpy(info + p, context, context_len); p += context_len; }
|
|
|
|
hkdf_expand(secret, info, p, out, out_len);
|
|
}
|
|
|
|
// Derive-Secret(secret, label, messages) = Expand-Label(secret, label,
|
|
// Hash(messages), Hash.length). `transcript_hash` is that Hash(messages).
|
|
void tls13_derive_secret(const uint8_t secret[32], const char* label,
|
|
const uint8_t transcript_hash[32], uint8_t out[32]) {
|
|
tls13_expand_label(secret, label, transcript_hash, SHA256_LEN, out, SHA256_LEN);
|
|
}
|