feat(tls): sans-io server handshake FSM (rv2 9 phase G2)

- 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 <noreply@anthropic.com>
(cherry picked from commit 34d2b8f87cebe536cd2b1b33e6948251ec11684f)
This commit is contained in:
shoney.arickathil 2026-09-09 04:27:19 +02:00
parent cd779afa7f
commit bd99da599d
4 changed files with 597 additions and 0 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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");
}

View file

@ -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,
};