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
This commit is contained in:
subhobhai943andClaude Opus 4.8 committed 2026-08-27 18:08:51 +05:30
1 parent 9f90361265
commit 8a55ac345b
9 files changed
+1257 -41

No files matched your search

+285
View File
@@ -0,0 +1,285 @@
/*
* ChaCha20-Poly1305 AEAD, RFC 8439.
*
* The record-protection cipher for the TLS 1.3 client. ChaCha20 is a pure
* add-rotate-xor stream cipher (no tables, so no data-dependent timing), and
* Poly1305 is its one-time authenticator; together they form the AEAD that
* encrypts and authenticates every TLS record. All integer arithmetic, so it
* is safe in the freestanding kernel.
*
* Poly1305 here is the 32-bit "donna" formulation (public domain), which needs
* only 64-bit intermediate products.
*/
#include <crypto/crypto.h>
#include <lib/string.h>
// ===========================================================================
// ChaCha20
// ===========================================================================
static inline uint32_t rotl32(uint32_t x, int n) {
return (x << n) | (x >> (32 - n));
}
static inline uint32_t load32_le(const uint8_t* p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static inline void store32_le(uint8_t* p, uint32_t v) {
p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8);
p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24);
}
#define QR(a, b, c, d) \
a += b; d ^= a; d = rotl32(d, 16); \
c += d; b ^= c; b = rotl32(b, 12); \
a += b; d ^= a; d = rotl32(d, 8); \
c += d; b ^= c; b = rotl32(b, 7)
static void chacha20_block(const uint32_t in[16], uint8_t out[64]) {
uint32_t x[16];
for (int i = 0; i < 16; i++) x[i] = in[i];
for (int i = 0; i < 10; i++) {
QR(x[0], x[4], x[8], x[12]);
QR(x[1], x[5], x[9], x[13]);
QR(x[2], x[6], x[10], x[14]);
QR(x[3], x[7], x[11], x[15]);
QR(x[0], x[5], x[10], x[15]);
QR(x[1], x[6], x[11], x[12]);
QR(x[2], x[7], x[8], x[13]);
QR(x[3], x[4], x[9], x[14]);
}
for (int i = 0; i < 16; i++) store32_le(out + 4 * i, x[i] + in[i]);
}
static void chacha20_init(uint32_t state[16], const uint8_t key[32],
uint32_t counter, const uint8_t nonce[12]) {
state[0] = 0x61707865; state[1] = 0x3320646e;
state[2] = 0x79622d32; state[3] = 0x6b206574;
for (int i = 0; i < 8; i++) state[4 + i] = load32_le(key + 4 * i);
state[12] = counter;
state[13] = load32_le(nonce + 0);
state[14] = load32_le(nonce + 4);
state[15] = load32_le(nonce + 8);
}
// Encrypt/decrypt src into dst (they may alias) starting at block counter.
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]) {
uint32_t state[16];
uint8_t block[64];
chacha20_init(state, key, counter, nonce);
size_t off = 0;
while (off < len) {
chacha20_block(state, block);
state[12]++; // next block counter
size_t n = len - off < 64 ? len - off : 64;
for (size_t i = 0; i < n; i++) dst[off + i] = src[off + i] ^ block[i];
off += n;
}
}
// ===========================================================================
// Poly1305 (32-bit donna)
// ===========================================================================
typedef struct {
uint32_t r[5];
uint32_t h[5];
uint32_t pad[4];
size_t leftover;
uint8_t buffer[16];
uint8_t final;
} poly1305_ctx;
static void poly1305_init(poly1305_ctx* st, const uint8_t key[32]) {
st->r[0] = (load32_le(&key[0])) & 0x3ffffff;
st->r[1] = (load32_le(&key[3]) >> 2) & 0x3ffff03;
st->r[2] = (load32_le(&key[6]) >> 4) & 0x3ffc0ff;
st->r[3] = (load32_le(&key[9]) >> 6) & 0x3f03fff;
st->r[4] = (load32_le(&key[12]) >> 8) & 0x00fffff;
st->h[0] = st->h[1] = st->h[2] = st->h[3] = st->h[4] = 0;
st->pad[0] = load32_le(&key[16]);
st->pad[1] = load32_le(&key[20]);
st->pad[2] = load32_le(&key[24]);
st->pad[3] = load32_le(&key[28]);
st->leftover = 0;
st->final = 0;
}
static void poly1305_blocks(poly1305_ctx* st, const uint8_t* m, size_t bytes) {
const uint32_t hibit = st->final ? 0 : (1UL << 24);
uint32_t r0 = st->r[0], r1 = st->r[1], r2 = st->r[2], r3 = st->r[3], r4 = st->r[4];
uint32_t s1 = r1 * 5, s2 = r2 * 5, s3 = r3 * 5, s4 = r4 * 5;
uint32_t h0 = st->h[0], h1 = st->h[1], h2 = st->h[2], h3 = st->h[3], h4 = st->h[4];
while (bytes >= 16) {
uint64_t d0, d1, d2, d3, d4;
uint32_t c;
h0 += (load32_le(m + 0)) & 0x3ffffff;
h1 += (load32_le(m + 3) >> 2) & 0x3ffffff;
h2 += (load32_le(m + 6) >> 4) & 0x3ffffff;
h3 += (load32_le(m + 9) >> 6) & 0x3ffffff;
h4 += (load32_le(m + 12) >> 8) | hibit;
d0 = (uint64_t)h0 * r0 + (uint64_t)h1 * s4 + (uint64_t)h2 * s3 + (uint64_t)h3 * s2 + (uint64_t)h4 * s1;
d1 = (uint64_t)h0 * r1 + (uint64_t)h1 * r0 + (uint64_t)h2 * s4 + (uint64_t)h3 * s3 + (uint64_t)h4 * s2;
d2 = (uint64_t)h0 * r2 + (uint64_t)h1 * r1 + (uint64_t)h2 * r0 + (uint64_t)h3 * s4 + (uint64_t)h4 * s3;
d3 = (uint64_t)h0 * r3 + (uint64_t)h1 * r2 + (uint64_t)h2 * r1 + (uint64_t)h3 * r0 + (uint64_t)h4 * s4;
d4 = (uint64_t)h0 * r4 + (uint64_t)h1 * r3 + (uint64_t)h2 * r2 + (uint64_t)h3 * r1 + (uint64_t)h4 * r0;
c = (uint32_t)(d0 >> 26); h0 = (uint32_t)d0 & 0x3ffffff;
d1 += c; c = (uint32_t)(d1 >> 26); h1 = (uint32_t)d1 & 0x3ffffff;
d2 += c; c = (uint32_t)(d2 >> 26); h2 = (uint32_t)d2 & 0x3ffffff;
d3 += c; c = (uint32_t)(d3 >> 26); h3 = (uint32_t)d3 & 0x3ffffff;
d4 += c; c = (uint32_t)(d4 >> 26); h4 = (uint32_t)d4 & 0x3ffffff;
h0 += c * 5; c = (h0 >> 26); h0 &= 0x3ffffff;
h1 += c;
m += 16;
bytes -= 16;
}
st->h[0] = h0; st->h[1] = h1; st->h[2] = h2; st->h[3] = h3; st->h[4] = h4;
}
static void poly1305_update(poly1305_ctx* st, const uint8_t* m, size_t bytes) {
if (st->leftover) {
size_t want = 16 - st->leftover;
if (want > bytes) want = bytes;
for (size_t i = 0; i < want; i++) st->buffer[st->leftover + i] = m[i];
bytes -= want;
m += want;
st->leftover += want;
if (st->leftover < 16) return;
poly1305_blocks(st, st->buffer, 16);
st->leftover = 0;
}
if (bytes >= 16) {
size_t want = bytes & ~(size_t)15;
poly1305_blocks(st, m, want);
m += want;
bytes -= want;
}
for (size_t i = 0; i < bytes; i++) st->buffer[st->leftover + i] = m[i];
st->leftover += bytes;
}
static void poly1305_finish(poly1305_ctx* st, uint8_t mac[16]) {
if (st->leftover) {
size_t i = st->leftover;
st->buffer[i++] = 1;
for (; i < 16; i++) st->buffer[i] = 0;
st->final = 1;
poly1305_blocks(st, st->buffer, 16);
}
uint32_t h0 = st->h[0], h1 = st->h[1], h2 = st->h[2], h3 = st->h[3], h4 = st->h[4];
uint32_t c;
c = h1 >> 26; h1 &= 0x3ffffff;
h2 += c; c = h2 >> 26; h2 &= 0x3ffffff;
h3 += c; c = h3 >> 26; h3 &= 0x3ffffff;
h4 += c; c = h4 >> 26; h4 &= 0x3ffffff;
h0 += c * 5; c = h0 >> 26; h0 &= 0x3ffffff;
h1 += c;
uint32_t g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
uint32_t g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
uint32_t g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
uint32_t g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
uint32_t g4 = h4 + c - (1UL << 26);
uint32_t mask = (g4 >> 31) - 1; // 0 if g>=p (use g), 0xffffffff if g<p (use h)
g0 &= mask; g1 &= mask; g2 &= mask; g3 &= mask; g4 &= mask;
mask = ~mask;
h0 = (h0 & mask) | g0;
h1 = (h1 & mask) | g1;
h2 = (h2 & mask) | g2;
h3 = (h3 & mask) | g3;
h4 = (h4 & mask) | g4;
// Serialise h into 128-bit little-endian, add pad.
uint64_t f;
h0 = (h0) | (h1 << 26);
h1 = (h1 >> 6) | (h2 << 20);
h2 = (h2 >> 12) | (h3 << 14);
h3 = (h3 >> 18) | (h4 << 8);
f = (uint64_t)h0 + st->pad[0]; h0 = (uint32_t)f;
f = (uint64_t)h1 + st->pad[1] + (f >> 32); h1 = (uint32_t)f;
f = (uint64_t)h2 + st->pad[2] + (f >> 32); h2 = (uint32_t)f;
f = (uint64_t)h3 + st->pad[3] + (f >> 32); h3 = (uint32_t)f;
store32_le(mac + 0, h0);
store32_le(mac + 4, h1);
store32_le(mac + 8, h2);
store32_le(mac + 12, h3);
}
// ===========================================================================
// AEAD construction (RFC 8439 section 2.8)
// ===========================================================================
static void poly1305_key_gen(uint8_t out_key[32], const uint8_t key[32],
const uint8_t nonce[12]) {
uint32_t state[16];
uint8_t block[64];
chacha20_init(state, key, 0, nonce);
chacha20_block(state, block);
memcpy(out_key, block, 32);
}
static const uint8_t poly_zeros[16] = { 0 };
static void aead_mac(uint8_t tag[16], const uint8_t poly_key[32],
const uint8_t* aad, size_t aad_len,
const uint8_t* ct, size_t ct_len) {
poly1305_ctx st;
poly1305_init(&st, poly_key);
poly1305_update(&st, aad, aad_len);
if (aad_len % 16) poly1305_update(&st, poly_zeros, 16 - (aad_len % 16));
poly1305_update(&st, ct, ct_len);
if (ct_len % 16) poly1305_update(&st, poly_zeros, 16 - (ct_len % 16));
uint8_t lengths[16];
for (int i = 0; i < 8; i++) lengths[i] = (uint8_t)(aad_len >> (8 * i));
for (int i = 0; i < 8; i++) lengths[8 + i] = (uint8_t)(ct_len >> (8 * i));
poly1305_update(&st, lengths, 16);
poly1305_finish(&st, tag);
}
// Encrypt plaintext -> ciphertext (same length) and produce a 16-byte tag.
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) {
uint8_t poly_key[32];
poly1305_key_gen(poly_key, key, nonce);
chacha20_xor(ct, pt, pt_len, key, 1, nonce); // counter starts at 1
aead_mac(tag, poly_key, aad, aad_len, ct, pt_len);
}
// Verify tag and decrypt ciphertext -> plaintext. Returns true on success.
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]) {
uint8_t poly_key[32];
poly1305_key_gen(poly_key, key, nonce);
uint8_t expect[16];
aead_mac(expect, poly_key, aad, aad_len, ct, ct_len);
uint8_t diff = 0;
for (int i = 0; i < 16; i++) diff |= expect[i] ^ tag[i];
if (diff) return false; // authentication failed
chacha20_xor(pt, ct, ct_len, key, 1, nonce);
return true;
}
+113
View File
@@ -0,0 +1,113 @@
/*
* 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);
}
+163
View File
@@ -0,0 +1,163 @@
/*
* X25519 (Curve25519 ECDH), RFC 7748.
*
* This is the compact TweetNaCl field arithmetic (public domain), which uses
* only 64-bit integer operations -- no floating point, no wide multiply beyond
* what the compiler provides -- so it is safe in the freestanding kernel. It is
* the key exchange for the TLS 1.3 client: x25519_base() turns a random private
* scalar into a public key to send, and x25519() combines our private scalar
* with the peer's public key to produce the shared secret.
*/
#include <crypto/crypto.h>
#include <lib/string.h>
typedef int64_t i64;
typedef uint8_t u8;
typedef i64 gf[16];
static const gf _121665 = { 0xDB41, 1 };
static void car25519(gf o) {
int i;
i64 c;
for (i = 0; i < 16; i++) {
o[i] += (1LL << 16);
c = o[i] >> 16;
o[(i + 1) * (i < 15)] += c - 1 + 37 * (c - 1) * (i == 15);
o[i] -= c << 16;
}
}
static void sel25519(gf p, gf q, int b) {
i64 t, i, c = ~(b - 1);
for (i = 0; i < 16; i++) {
t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
static void pack25519(u8* o, const gf n) {
int i, j, b;
gf m, t;
for (i = 0; i < 16; i++) t[i] = n[i];
car25519(t);
car25519(t);
car25519(t);
for (j = 0; j < 2; j++) {
m[0] = t[0] - 0xffed;
for (i = 1; i < 15; i++) {
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
m[i - 1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
b = (m[15] >> 16) & 1;
m[14] &= 0xffff;
sel25519(t, m, 1 - b);
}
for (i = 0; i < 16; i++) {
o[2 * i] = t[i] & 0xff;
o[2 * i + 1] = t[i] >> 8;
}
}
static void unpack25519(gf o, const u8* n) {
int i;
for (i = 0; i < 16; i++) o[i] = n[2 * i] + ((i64)n[2 * i + 1] << 8);
o[15] &= 0x7fff;
}
static void A(gf o, const gf a, const gf b) {
int i;
for (i = 0; i < 16; i++) o[i] = a[i] + b[i];
}
static void Z(gf o, const gf a, const gf b) {
int i;
for (i = 0; i < 16; i++) o[i] = a[i] - b[i];
}
static void M(gf o, const gf a, const gf b) {
i64 i, j, t[31];
for (i = 0; i < 31; i++) t[i] = 0;
for (i = 0; i < 16; i++)
for (j = 0; j < 16; j++) t[i + j] += a[i] * b[j];
for (i = 0; i < 15; i++) t[i] += 38 * t[i + 16];
for (i = 0; i < 16; i++) o[i] = t[i];
car25519(o);
car25519(o);
}
static void S(gf o, const gf a) { M(o, a, a); }
static void inv25519(gf o, const gf i) {
gf c;
int a;
for (a = 0; a < 16; a++) c[a] = i[a];
for (a = 253; a >= 0; a--) {
S(c, c);
if (a != 2 && a != 4) M(c, c, i);
}
for (a = 0; a < 16; a++) o[a] = c[a];
}
static int crypto_scalarmult(u8* q, const u8* n, const u8* p) {
u8 z[32];
i64 x[80], r, i;
gf a, b, c, d, e, f;
for (i = 0; i < 31; i++) z[i] = n[i];
z[31] = (n[31] & 127) | 64;
z[0] &= 248;
unpack25519(x, p);
for (i = 0; i < 16; i++) {
b[i] = x[i];
d[i] = a[i] = c[i] = 0;
}
a[0] = d[0] = 1;
for (i = 254; i >= 0; --i) {
r = (z[i >> 3] >> (i & 7)) & 1;
sel25519(a, b, r);
sel25519(c, d, r);
A(e, a, c);
Z(a, a, c);
A(c, b, d);
Z(b, b, d);
S(d, e);
S(f, a);
M(a, c, a);
M(c, b, e);
A(e, a, c);
Z(a, a, c);
S(b, a);
Z(c, d, f);
M(a, c, _121665);
A(a, a, d);
M(c, c, a);
M(a, d, f);
M(d, b, x);
S(b, e);
sel25519(a, b, r);
sel25519(c, d, r);
}
for (i = 0; i < 16; i++) {
x[i + 16] = a[i];
x[i + 32] = c[i];
x[i + 48] = b[i];
x[i + 64] = d[i];
}
inv25519(x + 32, x + 32);
M(x + 16, x + 16, x + 32);
pack25519(q, x + 16);
return 0;
}
// The Curve25519 base point, u = 9.
static const u8 x25519_basepoint[32] = { 9 };
void x25519_base(uint8_t out_pub[32], const uint8_t priv[32]) {
crypto_scalarmult(out_pub, priv, x25519_basepoint);
}
void x25519(uint8_t out_shared[32], const uint8_t priv[32], const uint8_t peer_pub[32]) {
crypto_scalarmult(out_shared, priv, peer_pub);
}