From bd99da599d3e171942a695b8099013282edd8ccd Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Wed, 9 Sep 2026 04:27:19 +0200 Subject: [PATCH] feat(tls): sans-io server handshake FSM (rv2 9 phase G2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wo_tls_server: the mirror of the client driver. parse ClientHello (pick suite, extract x25519 share, echo session id; reject no-x25519/no-1.3), build ServerHello, derive the role-symmetric keys, emit the encrypted flight (EncryptedExtensions + Certificate + a signed CertificateVerify + Finished), verify the client Finished, switch to application keys - server_sign_cv signs the CertificateVerify with the phase-G1 primitives (RSA-PSS or ECDSA-P256 + a minimal DER SEQ{r,s} encoder); parse_client_hello + build helpers reuse the file's wire reader/writer - wo_tls_server_start builds the Certificate message from a cert chain + private key (RSA n/d or EC scalar) + ephemeral; encrypt/decrypt over the application keys - KAT: loopback — our client driver against our server driver, EC then RSA server identity, reaching ESTABLISHED with an app round-trip both ways. test_tls 123, ASan/UBSan clean Co-Authored-By: Claude Opus 4.8 (cherry picked from commit 34d2b8f87cebe536cd2b1b33e6948251ec11684f) --- runtime/src/tls.c | 308 ++++++++++++++++++++++++++++++ runtime/src/tls.h | 47 +++++ runtime/test/test_tls.c | 56 ++++++ runtime/test/tls_server_vectors.h | 186 ++++++++++++++++++ 4 files changed, 597 insertions(+) create mode 100644 runtime/test/tls_server_vectors.h diff --git a/runtime/src/tls.c b/runtime/src/tls.c index 2a641a5..687f149 100644 --- a/runtime/src/tls.c +++ b/runtime/src/tls.c @@ -765,3 +765,311 @@ size_t wo_tls_client_chain(const wo_tls_client *c, const uint8_t **certs, } return n; } + +/* ---- sans-io server handshake driver (phase G2) -------------------------- + * The mirror of the client driver. Reuses the record layer, the (role- + * symmetric) key schedule, and the wire reader/writer above; the server signs + * its CertificateVerify with the phase-G1 private-key primitives. */ + +enum { SST_WANT_CH = 0, SST_WANT_FIN, SST_ESTABLISHED, SST_FAILED }; + +/* generic transcript append (the client's tr_add is client-typed). */ +static int tbuf_add(uint8_t *buf, size_t *len, size_t cap, const uint8_t *p, size_t n) { + if (*len + n > cap) return -1; + memcpy(buf + *len, p, n); *len += n; + return 0; +} + +/* Encode a big-endian value as a DER INTEGER (minimal, with a sign byte when + * the top bit is set). Returns bytes written. */ +static size_t der_int(uint8_t *out, const uint8_t *v, size_t n) { + while (n > 1 && v[0] == 0) { v++; n--; } /* strip leading zeros */ + int lead0 = (v[0] & 0x80) != 0; + out[0] = 0x02; out[1] = (uint8_t)(n + lead0); + size_t o = 2; + if (lead0) out[o++] = 0x00; + memcpy(out + o, v, n); + return o + n; +} + +/* Parse a ClientHello: pick a suite from the client's list, extract its x25519 + * key share, echo its session id. 0 ok, -1 malformed / no x25519 / no 1.3. */ +static int parse_client_hello(const uint8_t *msg, size_t len, int *suite, + uint8_t client_pub[32], uint8_t sid[32], + size_t *sidlen) { + rbuf r = { msg, len, 0, 1 }; + if (r8(&r) != HS_CLIENT_HELLO) return -1; + uint32_t body = r24(&r); + if (!r.ok || body != len - 4) return -1; + if (r16(&r) != 0x0303) return -1; /* legacy_version */ + if (!rbytes(&r, 32)) return -1; /* random */ + uint8_t sl = r8(&r); + if (sl > 32) return -1; + const uint8_t *sp = rbytes(&r, sl); + if (!sp) return -1; + memcpy(sid, sp, sl); *sidlen = sl; + uint16_t cslen = r16(&r); + const uint8_t *cs = rbytes(&r, cslen); + if (!cs || (cslen & 1)) return -1; + int pick = 0; /* prefer AES-128-GCM */ + for (size_t i = 0; i + 1 < cslen; i += 2) { + uint16_t v = ((uint16_t)cs[i] << 8) | cs[i + 1]; + if (v == CS_AES_128_GCM) { pick = WO_TLS_AES_128_GCM_SHA256; break; } + if (v == CS_CHACHA20_POLY1305 && !pick) pick = WO_TLS_CHACHA20_POLY1305_SHA256; + } + if (!pick) return -1; + *suite = pick; + uint8_t cml = r8(&r); /* compression methods */ + if (!rbytes(&r, cml)) return -1; + uint16_t extlen = r16(&r); + const uint8_t *ext = rbytes(&r, extlen); + if (!ext) return -1; + rbuf e = { ext, extlen, 0, 1 }; + int have_ks = 0, have_ver = 0; + while (e.ok && e.i < e.n) { + uint16_t type = r16(&e), el = r16(&e); + const uint8_t *ed = rbytes(&e, el); + if (!ed) return -1; + rbuf d = { ed, el, 0, 1 }; + if (type == EXT_KEY_SHARE) { + uint16_t total = r16(&d); /* client_shares length */ + (void)total; + while (d.ok && d.i < d.n) { + uint16_t grp = r16(&d), klen = r16(&d); + const uint8_t *k = rbytes(&d, klen); + if (!k) return -1; + if (grp == GROUP_X25519 && klen == 32) { memcpy(client_pub, k, 32); have_ks = 1; } + } + } else if (type == EXT_SUPPORTED_VERSIONS) { + uint8_t n = r8(&d); + for (uint8_t i = 0; i + 1 < n; i += 2) + if (r16(&d) == 0x0304) have_ver = 1; + } + } + return (have_ks && have_ver) ? 0 : -1; +} + +int wo_tls_server_start(wo_tls_server *s, const uint8_t *const *chain, + const size_t *chain_lens, size_t nchain, int key_alg, + const uint8_t *rsa_n, size_t rsa_nlen, + const uint8_t *rsa_d, size_t rsa_dlen, + const uint8_t *ec_d, const uint8_t eph_priv[32], + const uint8_t *pss_salt, size_t pss_saltlen) { + memset(s, 0, sizeof *s); + s->key_alg = key_alg; + s->rsa_n = rsa_n; s->rsa_nlen = rsa_nlen; s->rsa_d = rsa_d; s->rsa_dlen = rsa_dlen; + s->ec_d = ec_d; s->pss_salt = pss_salt; s->pss_saltlen = pss_saltlen; + memcpy(s->eph_priv, eph_priv, 32); + /* build the Certificate message: 0b, len, ctx_len(0), cert_list */ + wbuf w = { s->certmsg, sizeof s->certmsg, 0, 1 }; + w8(&w, 0x0b); + size_t hlen_at = w.n; w8(&w, 0); w8(&w, 0); w8(&w, 0); + w8(&w, 0); /* request context len */ + size_t list_at = w.n; w8(&w, 0); w8(&w, 0); w8(&w, 0); + for (size_t i = 0; i < nchain; i++) { + w8(&w, (uint8_t)(chain_lens[i] >> 16)); + w8(&w, (uint8_t)(chain_lens[i] >> 8)); + w8(&w, (uint8_t)chain_lens[i]); + wbytes(&w, chain[i], chain_lens[i]); + w16(&w, 0); /* per-cert extensions */ + } + if (!w.ok) { s->st = SST_FAILED; return -1; } + size_t listlen = w.n - list_at - 3; + s->certmsg[list_at] = (uint8_t)(listlen >> 16); + s->certmsg[list_at + 1] = (uint8_t)(listlen >> 8); + s->certmsg[list_at + 2] = (uint8_t)listlen; + size_t blen = w.n - hlen_at - 3; + s->certmsg[hlen_at] = (uint8_t)(blen >> 16); + s->certmsg[hlen_at + 1] = (uint8_t)(blen >> 8); + s->certmsg[hlen_at + 2] = (uint8_t)blen; + s->certmsg_len = w.n; + s->st = SST_WANT_CH; + return 0; +} + +size_t wo_tls_server_take_output(wo_tls_server *s, uint8_t *out, size_t outcap) { + size_t n = s->outn <= outcap ? s->outn : 0; + if (n) { memcpy(out, s->out, n); s->outn = 0; } + return n; +} + +/* Sign the CertificateVerify content over the running transcript. Writes the + * signature and its scheme; returns siglen or -1. */ +static int server_sign_cv(wo_tls_server *s, uint8_t *sig, uint16_t *scheme) { + static const char CTX[] = "TLS 1.3, server CertificateVerify"; + uint8_t th[32], content[64 + 33 + 1 + 32], mhash[32]; + wo_sha256(s->transcript, s->tlen, th); /* CH..Certificate */ + memset(content, 0x20, 64); + memcpy(content + 64, CTX, 33); + content[97] = 0x00; + memcpy(content + 98, th, 32); + wo_sha256(content, sizeof content, mhash); + if (s->key_alg == WO_TLS_KEY_RSA) { + *scheme = 0x0804; /* rsa_pss_rsae_sha256 */ + if (wo_rsa_pss_sha256_sign(s->rsa_n, s->rsa_nlen, s->rsa_d, s->rsa_dlen, + mhash, s->pss_salt, s->pss_saltlen, sig) != 0) + return -1; + return (int)s->rsa_nlen; + } + *scheme = 0x0403; /* ecdsa_secp256r1_sha256 */ + uint8_t r[32], ss[32]; + if (wo_ecdsa_p256_sha256_sign(s->ec_d, mhash, r, ss) != 0) return -1; + uint8_t seq[80]; size_t o = 0; + o += der_int(seq + o, r, 32); + o += der_int(seq + o, ss, 32); + sig[0] = 0x30; sig[1] = (uint8_t)o; + memcpy(sig + 2, seq, o); + return (int)(o + 2); +} + +/* Build + encrypt the whole server flight after the ClientHello. 0 ok, -1. */ +static int server_emit_flight(wo_tls_server *s, const uint8_t *client_pub, + const uint8_t *sid, size_t sidlen) { + uint8_t server_pub[32], base9[32] = { 9 }, ecdhe[32]; + wo_x25519(server_pub, s->eph_priv, base9); + + /* ServerHello */ + uint8_t sh[256]; wbuf w = { sh, sizeof sh, 0, 1 }; + w8(&w, HS_SERVER_HELLO); + size_t at = w.n; w8(&w, 0); w8(&w, 0); w8(&w, 0); + w16(&w, 0x0303); + uint8_t rnd[32]; + for (int i = 0; i < 32; i++) rnd[i] = (uint8_t)(0x70 ^ i); /* deterministic; not secret */ + wbytes(&w, rnd, 32); + w8(&w, (uint8_t)sidlen); wbytes(&w, sid, sidlen); + w16(&w, s->suite == WO_TLS_AES_128_GCM_SHA256 ? CS_AES_128_GCM : CS_CHACHA20_POLY1305); + w8(&w, 0); /* compression */ + size_t exts = w16_stub(&w); + w16(&w, EXT_SUPPORTED_VERSIONS); w16(&w, 2); w16(&w, 0x0304); + w16(&w, EXT_KEY_SHARE); w16(&w, 36); w16(&w, GROUP_X25519); w16(&w, 32); + wbytes(&w, server_pub, 32); + w16_fill(&w, exts); + if (!w.ok) return -1; + size_t blen = w.n - at - 3; + sh[at] = (uint8_t)(blen >> 16); sh[at + 1] = (uint8_t)(blen >> 8); sh[at + 2] = (uint8_t)blen; + + if (tbuf_add(s->transcript, &s->tlen, sizeof s->transcript, sh, w.n) != 0) return -1; + + /* keys from ECDHE + transcript(CH..SH) */ + uint8_t th[32]; + wo_x25519(ecdhe, s->eph_priv, client_pub); + wo_sha256(s->transcript, s->tlen, th); + wo_tls_derive_handshake(&s->ks, ecdhe, 32, th); + wo_tls_traffic_keys(s->ks.client_hs_traffic, s->keylen, s->rd_key, s->rd_iv); + wo_tls_traffic_keys(s->ks.server_hs_traffic, s->keylen, s->wr_key, s->wr_iv); + s->rd_seq = s->wr_seq = 0; + + /* the encrypted flight: EE || Certificate || CertificateVerify || Finished */ + static const uint8_t EE[] = { 0x08, 0x00, 0x00, 0x02, 0x00, 0x00 }; + uint8_t flight[WO_TLS_BUF_MAX]; size_t fl = 0; + if (tbuf_add(flight, &fl, sizeof flight, EE, sizeof EE) != 0) return -1; + if (tbuf_add(flight, &fl, sizeof flight, s->certmsg, s->certmsg_len) != 0) return -1; + if (tbuf_add(s->transcript, &s->tlen, sizeof s->transcript, EE, sizeof EE) != 0) return -1; + if (tbuf_add(s->transcript, &s->tlen, sizeof s->transcript, s->certmsg, s->certmsg_len) != 0) return -1; + + uint8_t sig[300]; uint16_t scheme; + int siglen = server_sign_cv(s, sig, &scheme); + if (siglen < 0) return -1; + uint8_t cv[320]; wbuf cw = { cv, sizeof cv, 0, 1 }; + cw.p[0] = 0x0f; cw.n = 1; size_t cvat = cw.n; w8(&cw, 0); w8(&cw, 0); w8(&cw, 0); + w16(&cw, scheme); w16(&cw, (uint16_t)siglen); wbytes(&cw, sig, (size_t)siglen); + if (!cw.ok) return -1; + size_t cvb = cw.n - cvat - 3; + cv[cvat] = (uint8_t)(cvb >> 16); cv[cvat + 1] = (uint8_t)(cvb >> 8); cv[cvat + 2] = (uint8_t)cvb; + if (tbuf_add(flight, &fl, sizeof flight, cv, cw.n) != 0) return -1; + if (tbuf_add(s->transcript, &s->tlen, sizeof s->transcript, cv, cw.n) != 0) return -1; + + /* server Finished over transcript(CH..CertVerify) */ + uint8_t vd[32]; + wo_sha256(s->transcript, s->tlen, th); + wo_tls_finished_verify(s->ks.server_hs_traffic, th, vd); + uint8_t fin[36]; fin[0] = 0x14; fin[1] = 0; fin[2] = 0; fin[3] = 32; + memcpy(fin + 4, vd, 32); + if (tbuf_add(flight, &fl, sizeof flight, fin, 36) != 0) return -1; + if (tbuf_add(s->transcript, &s->tlen, sizeof s->transcript, fin, 36) != 0) return -1; + + /* application keys need transcript(CH..server Finished) */ + wo_sha256(s->transcript, s->tlen, th); + wo_tls_derive_application(&s->ks, th); + + /* out = SH plaintext record (ct 22) || encrypted flight record (ct 23) */ + wbuf ow = { s->out, sizeof s->out, 0, 1 }; + w8(&ow, WO_TLS_CT_HANDSHAKE); w8(&ow, 0x03); w8(&ow, 0x03); + w16(&ow, (uint16_t)w.n); wbytes(&ow, sh, w.n); + if (!ow.ok) return -1; + int rn = wo_tls_record_seal(s->suite, s->wr_key, s->keylen, s->wr_iv, s->wr_seq, + WO_TLS_CT_HANDSHAKE, flight, fl, s->out + ow.n); + if (rn < 0) return -1; + s->wr_seq++; + s->outn = ow.n + (size_t)rn; + return 0; +} + +wo_tls_status wo_tls_server_push_record(wo_tls_server *s, const uint8_t *rec, + size_t reclen) { + if (s->st == SST_FAILED) return WO_TLS_FAILED; + if (reclen < 5) { s->st = SST_FAILED; return WO_TLS_FAILED; } + uint8_t ct = rec[0]; + if (ct == WO_TLS_CT_CHANGE_CIPHER_SPEC) return WO_TLS_WANT_MORE; + + if (s->st == SST_WANT_CH) { + if (ct != WO_TLS_CT_HANDSHAKE) { s->st = SST_FAILED; return WO_TLS_FAILED; } + size_t bl = ((size_t)rec[3] << 8) | rec[4]; + if (bl + 5 != reclen) { s->st = SST_FAILED; return WO_TLS_FAILED; } + const uint8_t *ch = rec + 5; + uint8_t client_pub[32], sid[32]; size_t sidlen = 0; + if (parse_client_hello(ch, bl, &s->suite, client_pub, sid, &sidlen) != 0) { + s->st = SST_FAILED; return WO_TLS_FAILED; + } + s->keylen = s->suite == WO_TLS_AES_128_GCM_SHA256 ? 16 : 32; + if (tbuf_add(s->transcript, &s->tlen, sizeof s->transcript, ch, bl) != 0) { + s->st = SST_FAILED; return WO_TLS_FAILED; + } + if (server_emit_flight(s, client_pub, sid, sidlen) != 0) { + s->st = SST_FAILED; return WO_TLS_FAILED; + } + s->st = SST_WANT_FIN; + return WO_TLS_WANT_MORE; + } + if (s->st == SST_WANT_FIN) { + if (ct != WO_TLS_CT_APPLICATION_DATA) { s->st = SST_FAILED; return WO_TLS_FAILED; } + uint8_t pt[WO_TLS_BUF_MAX], inner = 0; + int n = wo_tls_record_open(s->suite, s->rd_key, s->keylen, s->rd_iv, + s->rd_seq, rec, reclen, pt, &inner); + if (n < 0) { s->st = SST_FAILED; return WO_TLS_FAILED; } + s->rd_seq++; + if (inner != WO_TLS_CT_HANDSHAKE || n != 36 || pt[0] != 0x14) { + s->st = SST_FAILED; return WO_TLS_FAILED; + } + uint8_t th[32], expect[32]; + wo_sha256(s->transcript, s->tlen, th); /* CH..server Finished */ + wo_tls_finished_verify(s->ks.client_hs_traffic, th, expect); + if (!ct_eq32(expect, pt + 4)) { s->st = SST_FAILED; return WO_TLS_FAILED; } + /* switch to application keys */ + wo_tls_traffic_keys(s->ks.client_ap_traffic, s->keylen, s->rd_key, s->rd_iv); + wo_tls_traffic_keys(s->ks.server_ap_traffic, s->keylen, s->wr_key, s->wr_iv); + s->rd_seq = s->wr_seq = 0; + s->st = SST_ESTABLISHED; + return WO_TLS_ESTABLISHED; + } + return WO_TLS_WANT_MORE; +} + +int wo_tls_server_encrypt(wo_tls_server *s, const uint8_t *data, size_t len, + uint8_t *out, size_t outcap) { + if (s->st != SST_ESTABLISHED || len + WO_TLS_RECORD_OVERHEAD > outcap) return -1; + int n = wo_tls_record_seal(s->suite, s->wr_key, s->keylen, s->wr_iv, s->wr_seq, + WO_TLS_CT_APPLICATION_DATA, data, len, out); + if (n < 0) return -1; + s->wr_seq++; + return n; +} +int wo_tls_server_decrypt(wo_tls_server *s, const uint8_t *rec, size_t reclen, + uint8_t *out, size_t outcap, uint8_t *content_type) { + if (s->st != SST_ESTABLISHED || reclen > outcap + WO_TLS_RECORD_OVERHEAD) return -1; + int n = wo_tls_record_open(s->suite, s->rd_key, s->keylen, s->rd_iv, s->rd_seq, + rec, reclen, out, content_type); + if (n < 0) return -1; + s->rd_seq++; + return n; +} diff --git a/runtime/src/tls.h b/runtime/src/tls.h index 614c248..53b5a1f 100644 --- a/runtime/src/tls.h +++ b/runtime/src/tls.h @@ -210,4 +210,51 @@ int wo_tls_client_encrypt(wo_tls_client *c, const uint8_t *data, size_t len, int wo_tls_client_decrypt(wo_tls_client *c, const uint8_t *rec, size_t reclen, uint8_t *out, size_t outcap, uint8_t *content_type); +/* ---- sans-io server handshake driver (phase G2) -------------------------- + * The mirror of the client driver: the caller frames records, feeds the + * ClientHello, drains the whole server flight (ServerHello + EncryptedExtensions + * + Certificate + a signed CertificateVerify + Finished), then feeds the client + * Finished. Server-auth only — no client certs, resumption, or HRR. */ + +enum { WO_TLS_KEY_RSA = 1, WO_TLS_KEY_EC_P256 = 2 }; + +typedef struct { + int suite; size_t keylen; + uint8_t eph_priv[32]; /* server ephemeral X25519 scalar */ + int key_alg; /* WO_TLS_KEY_* */ + const uint8_t *rsa_n, *rsa_d; size_t rsa_nlen, rsa_dlen; /* RSA identity */ + const uint8_t *ec_d; /* EC identity (32-byte scalar) */ + const uint8_t *pss_salt; size_t pss_saltlen; /* RSA-PSS salt (caller-supplied) */ + uint8_t certmsg[WO_TLS_BUF_MAX]; size_t certmsg_len; /* built Certificate msg */ + wo_tls_key_schedule ks; + uint8_t rd_key[32], rd_iv[12], wr_key[32], wr_iv[12]; + uint64_t rd_seq, wr_seq; + uint8_t transcript[WO_TLS_BUF_MAX]; size_t tlen; + uint8_t hsbuf[WO_TLS_BUF_MAX]; size_t hsn; + uint8_t out[WO_TLS_BUF_MAX]; size_t outn; + int st; +} wo_tls_server; + +/* Start a server with its certificate chain (leaf-first DER), a private key + * (RSA: n+d; EC P-256: the 32-byte scalar in ec_d), an X25519 ephemeral scalar, + * and — for an RSA identity — the RSA-PSS salt to use (production: fresh random; + * KAT: fixed). Returns 0, or -1 if the chain does not fit. */ +int wo_tls_server_start(wo_tls_server *s, const uint8_t *const *chain, + const size_t *chain_lens, size_t nchain, int key_alg, + const uint8_t *rsa_n, size_t rsa_nlen, + const uint8_t *rsa_d, size_t rsa_dlen, + const uint8_t *ec_d, const uint8_t eph_priv[32], + const uint8_t *pss_salt, size_t pss_saltlen); + +/* Feed one record. On the ClientHello it produces the whole server flight in + * out (drain with take_output); on the client Finished it reaches ESTABLISHED. + * Returns WANT_MORE / ESTABLISHED / FAILED (the wo_tls_status enum). */ +wo_tls_status wo_tls_server_push_record(wo_tls_server *s, const uint8_t *rec, + size_t reclen); +size_t wo_tls_server_take_output(wo_tls_server *s, uint8_t *out, size_t outcap); +int wo_tls_server_encrypt(wo_tls_server *s, const uint8_t *data, size_t len, + uint8_t *out, size_t outcap); +int wo_tls_server_decrypt(wo_tls_server *s, const uint8_t *rec, size_t reclen, + uint8_t *out, size_t outcap, uint8_t *content_type); + #endif diff --git a/runtime/test/test_tls.c b/runtime/test/test_tls.c index 8c9573e..b361d17 100644 --- a/runtime/test/test_tls.c +++ b/runtime/test/test_tls.c @@ -12,6 +12,7 @@ #include "tls_record_vectors.h" #include "tls_hs_vectors.h" #include "tls_driver_vectors.h" +#include "tls_server_vectors.h" /* phase-G2 loopback server identities */ #include "x509_vectors.h" /* phase-E RSA + EC chains, for chain validation */ /* RFC 8448 §3 recorded ServerHello handshake message (90 octets). */ @@ -412,5 +413,60 @@ int main(void) { } } + /* Server handshake FSM (phase G2) — loopback: our client driver against + * our server driver, EC then RSA server identity, then an app round-trip. */ + { + static wo_tls_server srv; static wo_tls_client cli; + for (int variant = 0; variant < 2; variant++) { + uint8_t cpub[32], b9[32] = { 9 }; + wo_x25519(cpub, cli_eph, b9); + uint8_t ch[512]; size_t chl = 0; + wo_tls_build_client_hello("loopback.test", 13, cpub, cli_rand, cli_sid, + ch, sizeof ch, &chl); + wo_tls_client_start_with(&cli, ch, chl, cli_eph); + wo_tls_client_set_host(&cli, "loopback.test", 13); + + int rc; + if (variant == 0) { /* EC identity */ + const uint8_t *chain[] = { srv_ec_leaf }; size_t cl[] = { sizeof srv_ec_leaf }; + rc = wo_tls_server_start(&srv, chain, cl, 1, WO_TLS_KEY_EC_P256, + NULL, 0, NULL, 0, srv_ec_d, srv_eph, NULL, 0); + } else { /* RSA identity */ + const uint8_t *chain[] = { srv_rsa_leaf }; size_t cl[] = { sizeof srv_rsa_leaf }; + rc = wo_tls_server_start(&srv, chain, cl, 1, WO_TLS_KEY_RSA, + srv_rsa_n, sizeof srv_rsa_n, srv_rsa_d, + sizeof srv_rsa_d, NULL, srv_eph, + srv_pss_salt, sizeof srv_pss_salt); + } + T_CHECK(rc == 0); + + uint8_t buf[WO_TLS_BUF_MAX], sbuf[WO_TLS_BUF_MAX]; + size_t n = wo_tls_client_take_output(&cli, buf, sizeof buf); /* CH */ + T_CHECK(wo_tls_server_push_record(&srv, buf, n) == WO_TLS_WANT_MORE); + size_t sn = wo_tls_server_take_output(&srv, sbuf, sizeof sbuf); /* SH+flight */ + T_CHECK(sn > 0); + /* push each server record to the client */ + wo_tls_status cs = WO_TLS_WANT_MORE; size_t off = 0; + while (off + 5 <= sn) { + size_t rl = 5 + (((size_t)sbuf[off + 3] << 8) | sbuf[off + 4]); + cs = wo_tls_client_push_record(&cli, sbuf + off, rl); + off += rl; + } + T_CHECK(cs == WO_TLS_ESTABLISHED); + size_t cf = wo_tls_client_take_output(&cli, buf, sizeof buf); /* client Finished */ + T_CHECK(cf > 0); + T_CHECK(wo_tls_server_push_record(&srv, buf, cf) == WO_TLS_ESTABLISHED); + + /* application data both directions */ + uint8_t rec[256], pt[256]; uint8_t ctype = 0; + int e = wo_tls_client_encrypt(&cli, (const uint8_t *)"ping", 4, rec, sizeof rec); + int d = wo_tls_server_decrypt(&srv, rec, (size_t)e, pt, sizeof pt, &ctype); + T_CHECK(d == 4 && ctype == 23 && memcmp(pt, "ping", 4) == 0); + e = wo_tls_server_encrypt(&srv, (const uint8_t *)"pong!", 5, rec, sizeof rec); + d = wo_tls_client_decrypt(&cli, rec, (size_t)e, pt, sizeof pt, &ctype); + T_CHECK(d == 5 && ctype == 23 && memcmp(pt, "pong!", 5) == 0); + } + } + return t_report("test_tls"); } diff --git a/runtime/test/tls_server_vectors.h b/runtime/test/tls_server_vectors.h new file mode 100644 index 0000000..39e7fb3 --- /dev/null +++ b/runtime/test/tls_server_vectors.h @@ -0,0 +1,186 @@ +/* Loopback server identities (self-signed EC + RSA leaves, SAN loopback.test). */ +static const unsigned char srv_ec_leaf[] = { + 0x30,0x82,0x01,0x63,0x30,0x82,0x01,0x09,0xa0,0x03,0x02,0x01, + 0x02,0x02,0x14,0x20,0x63,0x6f,0xa1,0x21,0xec,0xc4,0x0b,0xb9, + 0xca,0xf8,0x16,0x51,0x40,0x20,0x7a,0x79,0x42,0x1c,0x98,0x30, + 0x0a,0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x30, + 0x18,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x03,0x0c,0x0d, + 0x6c,0x6f,0x6f,0x70,0x62,0x61,0x63,0x6b,0x2e,0x74,0x65,0x73, + 0x74,0x30,0x1e,0x17,0x0d,0x32,0x30,0x30,0x31,0x30,0x31,0x30, + 0x30,0x30,0x30,0x30,0x30,0x5a,0x17,0x0d,0x33,0x35,0x30,0x31, + 0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x18,0x31, + 0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x03,0x0c,0x0d,0x6c,0x6f, + 0x6f,0x70,0x62,0x61,0x63,0x6b,0x2e,0x74,0x65,0x73,0x74,0x30, + 0x59,0x30,0x13,0x06,0x07,0x2a,0x86,0x48,0xce,0x3d,0x02,0x01, + 0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,0x42, + 0x00,0x04,0x21,0x28,0x55,0x43,0x81,0x8c,0x5b,0xec,0xfe,0x7a, + 0xed,0x27,0xec,0x51,0x83,0x1b,0x3e,0x08,0x5a,0x97,0x5b,0xea, + 0x66,0xe1,0xac,0x53,0x32,0x42,0x3b,0xf0,0x49,0x6a,0x38,0xe4, + 0x8f,0xd7,0x44,0x5f,0x53,0x3e,0x58,0x7a,0x9f,0xd6,0x31,0xeb, + 0xcd,0x16,0xa1,0x13,0x43,0xdb,0x50,0xe7,0x1a,0x8e,0x09,0xb0, + 0x26,0xc1,0x2b,0x42,0xeb,0xde,0xa3,0x31,0x30,0x2f,0x30,0x18, + 0x06,0x03,0x55,0x1d,0x11,0x04,0x11,0x30,0x0f,0x82,0x0d,0x6c, + 0x6f,0x6f,0x70,0x62,0x61,0x63,0x6b,0x2e,0x74,0x65,0x73,0x74, + 0x30,0x13,0x06,0x03,0x55,0x1d,0x25,0x04,0x0c,0x30,0x0a,0x06, + 0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30,0x0a,0x06, + 0x08,0x2a,0x86,0x48,0xce,0x3d,0x04,0x03,0x02,0x03,0x48,0x00, + 0x30,0x45,0x02,0x20,0x5a,0x2e,0x70,0x99,0x96,0x06,0xcd,0xf9, + 0x1a,0x07,0xcc,0x84,0x03,0x25,0x36,0x16,0xcb,0x15,0x9b,0x25, + 0xe2,0xd3,0xf6,0xd8,0x5c,0xd4,0x80,0x7d,0xf5,0xe8,0xde,0x34, + 0x02,0x21,0x00,0x90,0x13,0x3e,0x9a,0x70,0x5f,0xab,0x01,0x24, + 0xaf,0xe6,0xb9,0x83,0x07,0xd7,0x8e,0x77,0xe2,0xac,0xda,0xad, + 0x19,0xc8,0xdf,0xb6,0x7e,0xd2,0x95,0x09,0x9e,0xfb,0x99, +}; + +static const unsigned char srv_ec_d[] = { + 0x76,0xcb,0x44,0x4f,0x28,0x7c,0x32,0xa5,0xae,0xda,0xd5,0xab, + 0x34,0x59,0x9f,0xa3,0x5b,0xc5,0x8a,0x21,0x74,0xb1,0x7e,0xf4, + 0x2b,0x1a,0xcc,0xa9,0x56,0xf2,0xec,0xf3, +}; + +static const unsigned char srv_rsa_leaf[] = { + 0x30,0x82,0x02,0xef,0x30,0x82,0x01,0xd7,0xa0,0x03,0x02,0x01, + 0x02,0x02,0x14,0x49,0xc2,0x91,0xde,0x51,0xd3,0xe7,0x6a,0x18, + 0xa5,0x58,0x6f,0x45,0xf5,0x44,0x8c,0xf3,0x4a,0x4b,0xcf,0x30, + 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b, + 0x05,0x00,0x30,0x18,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04, + 0x03,0x0c,0x0d,0x6c,0x6f,0x6f,0x70,0x62,0x61,0x63,0x6b,0x2e, + 0x74,0x65,0x73,0x74,0x30,0x1e,0x17,0x0d,0x32,0x30,0x30,0x31, + 0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x17,0x0d,0x33, + 0x35,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a, + 0x30,0x18,0x31,0x16,0x30,0x14,0x06,0x03,0x55,0x04,0x03,0x0c, + 0x0d,0x6c,0x6f,0x6f,0x70,0x62,0x61,0x63,0x6b,0x2e,0x74,0x65, + 0x73,0x74,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01, + 0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xa8, + 0x53,0xad,0x6a,0x9b,0xe1,0xbd,0x6e,0xbf,0xd1,0xfa,0xa4,0x19, + 0xda,0xf8,0x25,0x4d,0x22,0x9f,0xe0,0x28,0xfc,0xc8,0x04,0x25, + 0x3d,0x9f,0x81,0xfd,0x86,0x74,0x62,0x4d,0xa0,0xf9,0xfb,0x8e, + 0xb1,0x46,0x65,0xa6,0x2b,0x77,0x27,0xd6,0xdf,0x2d,0xfe,0x83, + 0x2b,0xed,0x43,0x74,0x8d,0x57,0xd0,0x00,0xdf,0x1f,0x85,0x68, + 0x68,0xbd,0x02,0xf6,0x20,0x2e,0x06,0x96,0x0d,0xd3,0x2d,0x44, + 0x89,0x95,0x6f,0xa8,0xf4,0xf2,0xdf,0x0b,0x86,0xa1,0x4a,0x20, + 0x3c,0x52,0x64,0xe6,0x2f,0x44,0x5c,0x7c,0xd2,0x97,0xbe,0xcb, + 0x2a,0x5b,0xa1,0x5f,0xd6,0x13,0xe1,0x43,0x45,0x33,0xe7,0x96, + 0x0a,0x10,0x67,0xac,0xb9,0xd4,0x39,0x35,0x07,0x04,0x4c,0xaf, + 0x05,0x6c,0x5d,0xca,0x38,0x14,0x3c,0xd9,0x49,0x72,0x9b,0x26, + 0x7d,0x17,0x77,0xba,0x87,0x50,0xde,0x66,0x7d,0xcf,0x7d,0xec, + 0x25,0x86,0xcc,0x59,0xa4,0xdb,0x56,0x71,0xd3,0xe0,0xa3,0x26, + 0xae,0xb1,0xe5,0x16,0x5d,0x0c,0x82,0xbd,0x8e,0x20,0x3f,0x37, + 0xfb,0x55,0x64,0x62,0x34,0xee,0x85,0x4f,0x99,0x61,0x1e,0x5d, + 0x20,0x4b,0xdc,0x5e,0xcd,0x87,0x8d,0x4c,0x95,0x85,0x6e,0x43, + 0x28,0x2b,0x3a,0x93,0xf0,0x36,0x41,0xd5,0xdc,0xcc,0x5f,0x30, + 0x07,0x01,0xe1,0x37,0x03,0xd9,0x17,0x9f,0x17,0xb7,0xfb,0x03, + 0x9a,0x32,0xdd,0x47,0xed,0x9e,0x1f,0x23,0x13,0xd4,0xd3,0x34, + 0x68,0x81,0xc8,0x94,0xc7,0xad,0x27,0x7f,0x01,0xb4,0xb9,0x19, + 0x79,0xfa,0x9f,0xeb,0xc7,0x60,0x8d,0x35,0x32,0xa5,0x79,0xda, + 0x7d,0xaa,0x51,0x02,0x03,0x01,0x00,0x01,0xa3,0x31,0x30,0x2f, + 0x30,0x18,0x06,0x03,0x55,0x1d,0x11,0x04,0x11,0x30,0x0f,0x82, + 0x0d,0x6c,0x6f,0x6f,0x70,0x62,0x61,0x63,0x6b,0x2e,0x74,0x65, + 0x73,0x74,0x30,0x13,0x06,0x03,0x55,0x1d,0x25,0x04,0x0c,0x30, + 0x0a,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30, + 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b, + 0x05,0x00,0x03,0x82,0x01,0x01,0x00,0xa2,0x02,0x25,0x4b,0x52, + 0x42,0x2f,0x40,0x11,0xb3,0x34,0xdf,0x1b,0x91,0xce,0x2c,0x50, + 0xc3,0x8a,0x28,0x34,0x59,0xa1,0x66,0xac,0x95,0x06,0xe4,0x03, + 0x4e,0xba,0xc4,0x5d,0x75,0xea,0x4b,0xc6,0x5b,0x88,0xb2,0xa3, + 0xa3,0xa5,0x3c,0x92,0x7c,0x93,0xca,0x05,0x99,0xa1,0xd8,0xc1, + 0x6b,0xae,0x70,0xae,0x12,0x49,0x40,0x93,0x2f,0xc4,0xa5,0x3c, + 0xf8,0xf5,0x7a,0x9f,0x19,0x39,0xc0,0xfa,0xa0,0x97,0x89,0x65, + 0x6c,0x48,0x87,0xec,0x25,0xe1,0x05,0x7d,0xc6,0x8b,0xee,0x79, + 0xd3,0xa3,0x4a,0x25,0xa5,0x89,0xa0,0x89,0x3a,0x94,0xa7,0x76, + 0xfb,0xee,0x0f,0xb3,0xe2,0x65,0xde,0x00,0x92,0xa6,0x45,0x28, + 0x11,0x42,0xe8,0xe0,0xe1,0xff,0x02,0x7a,0xe0,0x55,0x65,0xaa, + 0xb8,0xea,0xb2,0x7a,0x50,0x42,0x5e,0x99,0xb5,0xbb,0xc7,0x72, + 0x33,0x18,0xe0,0x7d,0x04,0x12,0xd7,0xb1,0x63,0x9f,0xde,0x2a, + 0x55,0x23,0x37,0x06,0x70,0xf7,0xb8,0x06,0x4c,0xa6,0x0c,0x9b, + 0x99,0x44,0x78,0xdf,0xc3,0x76,0x8e,0x2f,0xc5,0x92,0x28,0xae, + 0xe1,0x10,0xb1,0x28,0x57,0x3d,0x5a,0x80,0x82,0x87,0xab,0x68, + 0x13,0xa4,0x4d,0x0b,0xd1,0xfd,0x19,0xa2,0x6a,0x83,0x90,0xe3, + 0x7b,0xac,0xc9,0xab,0x95,0x67,0xa8,0xf4,0x5f,0x4a,0x0f,0xec, + 0x78,0x0b,0xb7,0x5a,0x58,0x50,0xc3,0xd6,0x23,0xd3,0xeb,0x52, + 0x56,0xf7,0xb5,0x66,0x6c,0xc7,0x4f,0xe0,0x62,0xae,0x4f,0xa2, + 0x41,0xb8,0xb2,0xe1,0x93,0xff,0xaa,0xc9,0xf2,0xdc,0xe7,0x69, + 0x30,0x1b,0x08,0x18,0x32,0x1a,0xd4,0xa0,0x8c,0x47,0xed, +}; + +static const unsigned char srv_rsa_n[] = { + 0xa8,0x53,0xad,0x6a,0x9b,0xe1,0xbd,0x6e,0xbf,0xd1,0xfa,0xa4, + 0x19,0xda,0xf8,0x25,0x4d,0x22,0x9f,0xe0,0x28,0xfc,0xc8,0x04, + 0x25,0x3d,0x9f,0x81,0xfd,0x86,0x74,0x62,0x4d,0xa0,0xf9,0xfb, + 0x8e,0xb1,0x46,0x65,0xa6,0x2b,0x77,0x27,0xd6,0xdf,0x2d,0xfe, + 0x83,0x2b,0xed,0x43,0x74,0x8d,0x57,0xd0,0x00,0xdf,0x1f,0x85, + 0x68,0x68,0xbd,0x02,0xf6,0x20,0x2e,0x06,0x96,0x0d,0xd3,0x2d, + 0x44,0x89,0x95,0x6f,0xa8,0xf4,0xf2,0xdf,0x0b,0x86,0xa1,0x4a, + 0x20,0x3c,0x52,0x64,0xe6,0x2f,0x44,0x5c,0x7c,0xd2,0x97,0xbe, + 0xcb,0x2a,0x5b,0xa1,0x5f,0xd6,0x13,0xe1,0x43,0x45,0x33,0xe7, + 0x96,0x0a,0x10,0x67,0xac,0xb9,0xd4,0x39,0x35,0x07,0x04,0x4c, + 0xaf,0x05,0x6c,0x5d,0xca,0x38,0x14,0x3c,0xd9,0x49,0x72,0x9b, + 0x26,0x7d,0x17,0x77,0xba,0x87,0x50,0xde,0x66,0x7d,0xcf,0x7d, + 0xec,0x25,0x86,0xcc,0x59,0xa4,0xdb,0x56,0x71,0xd3,0xe0,0xa3, + 0x26,0xae,0xb1,0xe5,0x16,0x5d,0x0c,0x82,0xbd,0x8e,0x20,0x3f, + 0x37,0xfb,0x55,0x64,0x62,0x34,0xee,0x85,0x4f,0x99,0x61,0x1e, + 0x5d,0x20,0x4b,0xdc,0x5e,0xcd,0x87,0x8d,0x4c,0x95,0x85,0x6e, + 0x43,0x28,0x2b,0x3a,0x93,0xf0,0x36,0x41,0xd5,0xdc,0xcc,0x5f, + 0x30,0x07,0x01,0xe1,0x37,0x03,0xd9,0x17,0x9f,0x17,0xb7,0xfb, + 0x03,0x9a,0x32,0xdd,0x47,0xed,0x9e,0x1f,0x23,0x13,0xd4,0xd3, + 0x34,0x68,0x81,0xc8,0x94,0xc7,0xad,0x27,0x7f,0x01,0xb4,0xb9, + 0x19,0x79,0xfa,0x9f,0xeb,0xc7,0x60,0x8d,0x35,0x32,0xa5,0x79, + 0xda,0x7d,0xaa,0x51, +}; + +static const unsigned char srv_rsa_d[] = { + 0x0a,0xa3,0x99,0x51,0x24,0xef,0xa0,0x6f,0xc4,0xcf,0x82,0x8a, + 0x47,0x39,0x14,0x18,0x95,0x76,0xc4,0x08,0xa0,0xc6,0x93,0x64, + 0xd1,0xae,0xc2,0xab,0x6e,0x69,0x06,0x67,0xa5,0x34,0xf0,0xbf, + 0xf1,0xdd,0xaa,0x0f,0xa8,0x30,0x54,0x9c,0x6f,0xc4,0x14,0xed, + 0xe2,0x80,0x0f,0x03,0xc5,0xb4,0xde,0x51,0x3f,0x10,0xdb,0x36, + 0xed,0x29,0xbb,0x92,0x99,0x98,0x60,0x98,0x59,0x79,0x1f,0xb9, + 0x1b,0x7d,0x1f,0xb5,0x1a,0x46,0x7b,0x28,0x56,0x5b,0xe8,0xcb, + 0x5c,0xdc,0xbb,0x2f,0x75,0xee,0x14,0x61,0xcd,0x20,0xe9,0x66, + 0xed,0x83,0xec,0x9d,0x37,0x47,0xba,0x63,0x71,0x43,0x49,0x3b, + 0xd0,0xbb,0xab,0x9c,0x45,0xea,0x4b,0xeb,0xde,0xba,0x66,0x0e, + 0xeb,0xbc,0x09,0xc4,0xa6,0xd0,0xa3,0x14,0xb8,0x35,0x97,0xbc, + 0x1a,0x42,0xe8,0x43,0x9b,0xdd,0x1a,0x39,0x0b,0x71,0xf7,0xea, + 0x7a,0x82,0x87,0xb9,0xbf,0xed,0x4d,0x8e,0xd5,0xdd,0x3a,0x8f, + 0xa0,0xff,0xc8,0x36,0x72,0xf2,0x4e,0x22,0x28,0x10,0x6a,0x8e, + 0xdd,0xcd,0x29,0xfc,0xc2,0x8b,0xdf,0x75,0x4d,0x45,0x33,0x60, + 0x0e,0x20,0xa5,0xd5,0x81,0x53,0xe7,0x2f,0xba,0x91,0xb3,0x0f, + 0x23,0xaf,0xeb,0x8a,0xe9,0xad,0x5e,0xe4,0xa2,0xed,0xc8,0x18, + 0xe3,0xd5,0xd4,0xe6,0x62,0x4e,0xf9,0x4d,0x99,0x9b,0x02,0xa2, + 0x89,0x2c,0xa0,0x2b,0xf5,0x64,0x6b,0x9f,0xda,0x93,0x39,0xaf, + 0x42,0x92,0xca,0xc6,0x1b,0x4b,0x73,0xc1,0xc5,0x38,0x39,0x26, + 0x82,0x1a,0xc6,0x15,0x9d,0x61,0xfe,0x6b,0x41,0x7a,0x28,0xb0, + 0xd2,0xe2,0x16,0xbd, +}; + +static const unsigned char srv_eph[] = { + 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, + 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, + 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, +}; + +static const unsigned char cli_eph[] = { + 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, + 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, + 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, +}; + +static const unsigned char cli_rand[] = { + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, + 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, +}; + +static const unsigned char cli_sid[] = { + 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, + 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, + 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, +}; + +static const unsigned char srv_pss_salt[] = { + 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, + 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, + 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, +}; +