Files
subhobhai943andClaude Opus 4.8 8a55ac345b feat(crypto,net): TLS 1.3 client -- SUB-OS can now browse HTTPS sites
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
2026-08-27 18:08:51 +05:30

86 lines
3.1 KiB
C

#ifndef _CRYPTO_CRYPTO_H
#define _CRYPTO_CRYPTO_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// CRC32 API
uint32_t crc32(uint32_t crc, const void* buf, size_t size);
// MD5 API
typedef struct {
uint32_t state[4];
uint32_t count[2];
uint8_t buffer[64];
} md5_ctx_t;
void md5_init(md5_ctx_t* ctx);
void md5_update(md5_ctx_t* ctx, const void* input, size_t input_len);
void md5_final(uint8_t digest[16], md5_ctx_t* ctx);
void md5_string(const char* str, char output[33]);
// SHA-256 API
typedef struct {
uint32_t state[8];
uint64_t bitlen;
uint8_t buffer[64];
size_t datalen;
} sha256_ctx_t;
void sha256_init(sha256_ctx_t* ctx);
void sha256_update(sha256_ctx_t* ctx, const void* data, size_t len);
void sha256_final(uint8_t hash[32], sha256_ctx_t* ctx);
// One-shot hash helpers
static inline void md5(const void* data, size_t len, uint8_t digest[16]) {
md5_ctx_t ctx;
md5_init(&ctx);
md5_update(&ctx, data, len);
md5_final(digest, &ctx);
}
static inline void sha256(const void* data, size_t len, uint8_t hash[32]) {
sha256_ctx_t ctx;
sha256_init(&ctx);
sha256_update(&ctx, data, len);
sha256_final(hash, &ctx);
}
// X25519 (Curve25519 ECDH), RFC 7748.
void x25519_base(uint8_t out_pub[32], const uint8_t priv[32]);
void x25519(uint8_t out_shared[32], const uint8_t priv[32], const uint8_t peer_pub[32]);
// ChaCha20-Poly1305 AEAD, RFC 8439.
void chacha20_xor(uint8_t* dst, const uint8_t* src, size_t len,
const uint8_t key[32], uint32_t counter, const uint8_t nonce[12]);
void chacha20poly1305_encrypt(uint8_t* ct, uint8_t tag[16],
const uint8_t key[32], const uint8_t nonce[12],
const uint8_t* aad, size_t aad_len,
const uint8_t* pt, size_t pt_len);
bool chacha20poly1305_decrypt(uint8_t* pt, const uint8_t key[32],
const uint8_t nonce[12],
const uint8_t* aad, size_t aad_len,
const uint8_t* ct, size_t ct_len,
const uint8_t tag[16]);
// HMAC-SHA256 / HKDF (RFC 2104, 5869) and the TLS 1.3 key-schedule helpers.
void hmac_sha256(const uint8_t* key, size_t key_len,
const uint8_t* msg, size_t msg_len, uint8_t out[32]);
void hkdf_extract(const uint8_t* salt, size_t salt_len,
const uint8_t* ikm, size_t ikm_len, uint8_t prk[32]);
void hkdf_expand(const uint8_t prk[32], const uint8_t* info, size_t info_len,
uint8_t* out, size_t out_len);
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);
void tls13_derive_secret(const uint8_t secret[32], const char* label,
const uint8_t transcript_hash[32], uint8_t out[32]);
// PRNG (Cryptographic Pseudo-Random Number Generator)
void prng_seed(uint64_t seed1, uint64_t seed2);
uint64_t prng_rand64(void);
uint32_t prng_rand32(void);
void prng_get_bytes(void* buf, size_t len);
#endif // _CRYPTO_CRYPTO_H