From 430430107c3ee191f5518c6e949d31f69775a541 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Mon, 25 Mar 2024 08:34:05 +0000 Subject: [PATCH] TLS client implementation --- Makefile | 2 +- mongoose.c | 1646 +++++++++++++++++++++++++++++++-------------- mongoose.h | 36 +- src/sha256.c | 13 +- src/sha256.h | 6 + src/tls.h | 9 +- src/tls_builtin.c | 1389 ++++++++++++++++++++++++-------------- src/tls_x25519.c | 246 +++++++ src/tls_x25519.h | 13 + test/unit_test.c | 34 +- 10 files changed, 2355 insertions(+), 1039 deletions(-) create mode 100644 src/tls_x25519.c create mode 100644 src/tls_x25519.h diff --git a/Makefile b/Makefile index d94aeeca7aa..93ed5d467ec 100644 --- a/Makefile +++ b/Makefile @@ -184,7 +184,7 @@ mongoose.c: Makefile $(wildcard src/*.c) $(wildcard src/drivers/*.c) (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ mongoose.h: $(HDRS) Makefile - (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/tls_aes128.h src/tls_uecc.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/device.h src/net_builtin.h src/profile.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ + (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/device.h src/net_builtin.h src/profile.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ clean: clean_examples clean_embedded diff --git a/mongoose.c b/mongoose.c index 0ffd4d68d8f..f72a9254e77 100644 --- a/mongoose.c +++ b/mongoose.c @@ -6863,6 +6863,12 @@ void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) { #ifdef MG_ENABLE_LINES #line 1 "src/sha256.c" #endif +// https://github.com/B-Con/crypto-algorithms +// Author: Brad Conte (brad AT bradconte.com) +// Disclaimer: This code is presented "as is" without any guarantees. +// Details: Defines the API for the corresponding SHA1 implementation. +// Copyright: public domain + #define ror(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) @@ -6904,8 +6910,10 @@ static void mg_sha256_chunk(mg_sha256_ctx *ctx) { uint32_t a, b, c, d, e, f, g, h; uint32_t m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (uint32_t) ((ctx->buffer[j] << 24) | (ctx->buffer[j + 1] << 16) | - (ctx->buffer[j + 2] << 8) | (ctx->buffer[j + 3])); + m[i] = (uint32_t) (((uint32_t) ctx->buffer[j] << 24) | + ((uint32_t) ctx->buffer[j + 1] << 16) | + ((uint32_t) ctx->buffer[j + 2] << 8) | + ((uint32_t) ctx->buffer[j + 3])); for (; i < 64; ++i) m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16]; @@ -7023,7 +7031,6 @@ void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, mg_sha256_final(dst, &ctx); } - #ifdef MG_ENABLE_LINES #line 1 "src/sntp.c" #endif @@ -9176,34 +9183,72 @@ int aes_gcm_decrypt(unsigned char *output, const unsigned char *input, #endif + + +#define mg_tls_random(x, n) memset(x, 0xab, n) #if MG_TLS == MG_TLS_BUILTIN -// handshake is re-entrant, so we need to keep track of its state +/* TLS 1.3 Record Content Type (RFC8446 B.1) */ +#define MG_TLS_CHANGE_CIPHER 20 +#define MG_TLS_ALERT 21 +#define MG_TLS_HANDSHAKE 22 +#define MG_TLS_APP_DATA 23 +#define MG_TLS_HEARTBEAT 24 + +/* TLS 1.3 Handshake Message Type (RFC8446 B.3) */ +#define MG_TLS_CLIENT_HELLO 1 +#define MG_TLS_SERVER_HELLO 2 +#define MG_TLS_ENCRYPTED_EXTENSIONS 8 +#define MG_TLS_CERTIFICATE 11 +#define MG_TLS_CERTIFICATE_VERIFY 15 +#define MG_TLS_FINISHED 20 + +// handshake is re-entrant, so we need to keep track of its state state names +// refer to RFC8446#A.1 enum mg_tls_hs_state { - MG_TLS_HS_CLIENT_HELLO, // first, wait for ClientHello - MG_TLS_HS_SERVER_HELLO, // then, send all server handshake data at once - MG_TLS_HS_CLIENT_CHANGE_CIPHER, // finally wait for ClientChangeCipher - MG_TLS_HS_CLIENT_FINISH, // and ClientFinish (encrypted) - MG_TLS_HS_DONE, // finish handshake, start application data flow + // Client state machine: + MG_TLS_STATE_CLIENT_START, // Send ClientHello + MG_TLS_STATE_CLIENT_WAIT_SH, // Wait for ServerHello + MG_TLS_STATE_CLIENT_WAIT_EE, // Wait for EncryptedExtensions + MG_TLS_STATE_CLIENT_WAIT_CERT, // Wait for Certificate + MG_TLS_STATE_CLIENT_WAIT_CV, // Wait for CertificateVerify + MG_TLS_STATE_CLIENT_WAIT_FINISHED, // Wait for Finished + MG_TLS_STATE_CLIENT_CONNECTED, // Done + + // Server state machine: + MG_TLS_STATE_SERVER_START, // Wait for ClientHello + MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finished + MG_TLS_STATE_SERVER_CONNECTED, // Done }; // per-connection TLS data struct tls_data { enum mg_tls_hs_state state; // keep track of connection handshake progress - struct mg_iobuf send; // For the receive path, we're reusing c->rtls + struct mg_iobuf send; // For the receive path, we're reusing c->rtls + struct mg_iobuf recv; // While c->rtls contains full records, recv reuses + // the same underlying buffer but points at individual + // decrypted messages + uint8_t content_type; // Last received record content type mg_sha256_ctx sha256; // incremental SHA-256 hash for TLS handshake uint32_t sseq; // server sequence number, used in encryption uint32_t cseq; // client sequence number, used in decryption + uint8_t random[32]; // client random from ClientHello uint8_t session_id[32]; // client session ID between the handshake states uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t x25519_sec[32]; // x25519 secret between the handshake states + int skip_verification; // perform checks on server certificate? struct mg_str server_cert_der; // server certificate in DER format uint8_t server_key[32]; // server EC private key + char hostname[254]; // server hostname (client extension) + + uint8_t certhash[32]; // certificate message hash + uint8_t pubkey[64]; // server EC public key to verify cert + uint8_t sighash[32]; // server EC public key to verify cert // keys for AES encryption uint8_t handshake_secret[32]; @@ -9216,7 +9261,46 @@ struct tls_data { }; #define MG_LOAD_BE16(p) ((uint16_t) ((MG_U8P(p)[0] << 8U) | MG_U8P(p)[1])) -#define TLS_HDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes len +#define MG_LOAD_BE24(p) \ + ((uint32_t) ((MG_U8P(p)[0] << 16U) | (MG_U8P(p)[1] << 8U) | MG_U8P(p)[2])) +#define MG_STORE_BE16(p, n) \ + do { \ + MG_U8P(p)[0] = ((n) >> 8U) & 255; \ + MG_U8P(p)[1] = (n) & 255; \ + } while (0) + +#define TLS_RECHDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes length +#define TLS_MSGHDR_SIZE 4 // 1 byte type, 3 bytes length + +#if 1 +static void mg_ssl_key_log(const char *label, uint8_t client_random[32], + uint8_t *secret, size_t secretsz) { + (void) label; + (void) client_random; + (void) secret; + (void) secretsz; +} +#else +#include +static void mg_ssl_key_log(const char *label, uint8_t client_random[32], + uint8_t *secret, size_t secretsz) { + char *keylogfile = getenv("SSLKEYLOGFILE"); + if (keylogfile == NULL) { + return; + } + FILE *f = fopen(keylogfile, "a"); + fprintf(f, "%s ", label); + for (int i = 0; i < 32; i++) { + fprintf(f, "%02x", client_random[i]); + } + fprintf(f, " "); + for (unsigned int i = 0; i < secretsz; i++) { + fprintf(f, "%02x", secret[i]); + } + fprintf(f, "\n"); + fclose(f); +} +#endif // for derived tls keys we need SHA256([0]*32) static uint8_t zeros[32] = {0}; @@ -9224,248 +9308,96 @@ static uint8_t zeros_sha256_digest[32] = "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24" "\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55"; -#define X25519_BYTES 32 -const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9}; - -#define X25519_WBITS 32 - -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -typedef int64_t sdlimb_t; -#define LIMB(x) (uint32_t)(x##ull), (uint32_t) ((x##ull) >> 32) - -#define NLIMBS (256 / X25519_WBITS) -typedef limb_t fe[NLIMBS]; - -static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) { - dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry; - *carry = (limb_t) (tmp >> X25519_WBITS); - return (limb_t) tmp; -} - -// These functions are implemented in terms of umaal on ARM -static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) { - dlimb_t total = (dlimb_t) *carry + acc + mand; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -static limb_t adc0(limb_t *carry, limb_t acc) { - dlimb_t total = (dlimb_t) *carry + acc; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -// - Precondition: carry is small. -// - Invariant: result of propagate is < 2^255 + 1 word -// - In particular, always less than 2p. -// - Also, output x >= min(x,19) -static void propagate(fe x, limb_t over) { - unsigned i; - limb_t carry; - over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1; - x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1)); - - carry = over * 19; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry, x[i]); - } +// helper to hexdump buffers inline +static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) { + char p[8 * 4096]; + MG_VERBOSE(("%s: %s", msg, mg_hex(buf, bufsz, p))); } -static void add(fe out, const fe a, const fe b) { - unsigned i; - limb_t carry = 0; - for (i = 0; i < NLIMBS; i++) { - out[i] = adc(&carry, a[i], b[i]); - } - propagate(out, carry); -} +// helper utilities to parse ASN.1 DER +struct mg_der_tlv { + uint8_t type; + uint32_t len; + uint8_t *value; +}; -static void sub(fe out, const fe a, const fe b) { - unsigned i; - sdlimb_t carry = -38; - for (i = 0; i < NLIMBS; i++) { - carry = carry + a[i] - b[i]; - out[i] = (limb_t) carry; - carry >>= X25519_WBITS; +// parse DER into a TLV record +static int mg_der_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { + if (dersz < 2) { + return -1; } - propagate(out, (limb_t) (1 + carry)); -} - -// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `fe` -// to avoid build warnings -static void mul(fe out, const fe a, const limb_t *b, unsigned nb) { - limb_t accum[2 * NLIMBS] = {0}; - unsigned i, j; - - limb_t carry2; - for (i = 0; i < nb; i++) { - limb_t mand = b[i]; - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - accum[i + j] = umaal(&carry2, accum[i + j], mand, a[j]); + tlv->type = der[0]; + tlv->len = der[1]; + tlv->value = der + 2; + if (tlv->len > 0x7f) { + int i; + int n = tlv->len - 0x80; + tlv->len = 0; + for (i = 0; i < n; i++) { + tlv->len = (tlv->len << 8) | (der[2 + i]); } - accum[i + j] = carry2; - } - - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]); + tlv->value = der + 2 + n; } - propagate(out, carry2); -} - -static void sqr(fe out, const fe a) { - mul(out, a, a, NLIMBS); -} -static void mul1(fe out, const fe a) { - mul(out, a, out, NLIMBS); -} -static void sqr1(fe a) { - mul1(a, a); -} - -static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS], - limb_t doswap) { - unsigned i; - for (i = 0; i < 2 * NLIMBS; i++) { - limb_t xor = (a[i] ^ b[i]) & doswap; - a[i] ^= xor; - b[i] ^= xor; + if (der + dersz < tlv->value + tlv->len) { + return -1; } + return 0; } -// Canonicalize a field element x, reducing it to the least residue which is -// congruent to it mod 2^255-19 -// - Precondition: x < 2^255 + 1 word -static limb_t canon(fe x) { - // First, add 19. - unsigned i; - limb_t carry0 = 19; - limb_t res; - sdlimb_t carry; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry0, x[i]); +static int mg_der_find(uint8_t *der, size_t dersz, uint8_t *oid, size_t oidsz, + struct mg_der_tlv *tlv) { + uint8_t *p, *end; + struct mg_der_tlv child; + if (mg_der_tlv(der, dersz, tlv) < 0) { + return -1; // invalid DER + } else if (tlv->type == 6) { // found OID, check value + return (tlv->len == oidsz && memcmp(tlv->value, oid, oidsz) == 0); + } else if ((tlv->type & 0x20) == 0) { + return 0; // Primitive, but not OID: not found } - propagate(x, carry0); - - // Here, 19 <= x2 < 2^255 - // - This is because we added 19, so before propagate it can't be less - // than 19. After propagate, it still can't be less than 19, because if - // propagate does anything it adds 19. - // - We know that the high bit must be clear, because either the input was ~ - // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or - // it was < 2^255. So now, if we subtract 19, we will get back to something in - // [0,2^255-19). - carry = -19; - res = 0; - for (i = 0; i < NLIMBS; i++) { - carry += x[i]; - res |= x[i] = (limb_t) carry; - carry >>= X25519_WBITS; + // Constructed object: scan children + p = tlv->value; + end = tlv->value + tlv->len; + while (end > p) { + int r; + mg_der_tlv(p, end - p, &child); + r = mg_der_find(p, end - p, oid, oidsz, tlv); + if (r < 0) return -1; // error + if (r > 0) return 1; // found OID! + p = child.value + child.len; } - return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS); + return 0; // not found } -static const limb_t a24[1] = {121665}; - -static void ladder_part1(fe xs[5]) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - add(t1, x2, z2); // t1 = A - sub(z2, x2, z2); // z2 = B - add(x2, x3, z3); // x2 = C - sub(z3, x3, z3); // z3 = D - mul1(z3, t1); // z3 = DA - mul1(x2, z2); // x3 = BC - add(x3, z3, x2); // x3 = DA+CB - sub(z3, z3, x2); // z3 = DA-CB - sqr1(t1); // t1 = AA - sqr1(z2); // z2 = BB - sub(x2, t1, z2); // x2 = E = AA-BB - mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24 - add(z2, z2, t1); // z2 = E*a24 + AA +// Did we receive a full TLS record in the c->rtls buffer? +static bool mg_tls_got_record(struct mg_connection *c) { + return c->rtls.len >= (size_t) TLS_RECHDR_SIZE && + c->rtls.len >= + (size_t) (TLS_RECHDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3)); } -static void ladder_part2(fe xs[5], const fe x1) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - sqr1(z3); // z3 = (DA-CB)^2 - mul1(z3, x1); // z3 = x1 * (DA-CB)^2 - sqr1(x3); // x3 = (DA+CB)^2 - mul1(z2, x2); // z2 = AA*(E*a24+AA) - sub(x2, t1, x2); // x2 = BB again - mul1(x2, t1); // x2 = AA*BB +// Remove a single TLS record from the recv buffer +static void mg_tls_drop_record(struct mg_connection *c) { + struct mg_iobuf *rio = &c->rtls; + uint16_t n = MG_LOAD_BE16(rio->buf + 3) + TLS_RECHDR_SIZE; + mg_iobuf_del(rio, 0, n); } -static void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], - const uint8_t *x1, int clamp) { - int i; - limb_t swap = 0; - limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3]; - memset(xs, 0, 4 * sizeof(fe)); - x2[0] = z3[0] = 1; - memcpy(x3, x1, sizeof(fe)); - - for (i = 255; i >= 0; i--) { - uint8_t bytei = scalar[i / 8]; - limb_t doswap; - if (clamp) { - if (i / 8 == 0) { - bytei &= (uint8_t) ~7U; - } else if (i / 8 == X25519_BYTES - 1) { - bytei &= 0x7F; - bytei |= 0x40; - } - } - doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1); - condswap(x2, x3, swap ^ doswap); - swap = doswap; - - ladder_part1(xs); - ladder_part2(xs, (const limb_t *) x1); +// Remove a single TLS message from decrypted buffer, remove the wrapping +// record if it was the last message within a record +static void mg_tls_drop_message(struct mg_connection *c) { + uint32_t len; + struct tls_data *tls = c->tls; + if (tls->recv.len == 0) { + return; } - condswap(x2, x3, swap); -} - -static int x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp) { - int i, ret; - fe xs[5]; - limb_t *x2, *z2, *z3, *prev; - static const struct { - uint8_t a, c, n; - } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1}, - {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125}, - {3, 1, 2}, {3, 1, 2}, {3, 1, 1}}; - x25519_core(xs, scalar, x1, clamp); - - // Precomputed inversion chain - x2 = xs[0]; - z2 = xs[1]; - z3 = xs[3]; - - prev = z2; - for (i = 0; i < 13; i++) { - int j; - limb_t *a = xs[steps[i].a]; - for (j = steps[i].n; j > 0; j--) { - sqr(a, prev); - prev = a; - } - mul1(a, xs[steps[i].c]); + len = MG_LOAD_BE24(tls->recv.buf + 1); + mg_sha256_update(&tls->sha256, tls->recv.buf, len + TLS_MSGHDR_SIZE); + tls->recv.buf += len + TLS_MSGHDR_SIZE; + tls->recv.len -= len + TLS_MSGHDR_SIZE; + if (tls->recv.len == 0) { + mg_tls_drop_record(c); } - - // Here prev = z3 - // x2 /= z2 - mul((limb_t *) out, x2, z3, NLIMBS); - ret = (int) canon((limb_t *) out); - if (!clamp) ret = 0; - return ret; -} - -// helper to hexdump buffers inline -static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) { - char p[512]; - MG_VERBOSE(("%s: %s", msg, mg_hex(buf, bufsz, p))); } // TLS1.3 secret derivation based on the key label @@ -9485,20 +9417,235 @@ static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz, memmove(hash, secret, hashsz); } -// Did we receive a full TLS message in the c->rtls buffer? -static bool mg_tls_got_msg(struct mg_connection *c) { - return c->rtls.len >= (size_t) TLS_HDR_SIZE && - c->rtls.len >= (size_t) (TLS_HDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3)); -} +// at this point we have x25519 shared secret, we can generate a set of derived +// handshake encryption keys +static void mg_tls_generate_handshake_keys(struct mg_connection *c) { + struct tls_data *tls = c->tls; -// Remove a single TLS record from the recv buffer -static void mg_tls_drop_packet(struct mg_iobuf *rio) { - uint16_t n = MG_LOAD_BE16(rio->buf + 3) + TLS_HDR_SIZE; - mg_iobuf_del(rio, 0, n); -} + mg_sha256_ctx sha256; + uint8_t early_secret[32]; + uint8_t pre_extract_secret[32]; + uint8_t hello_hash[32]; + uint8_t server_hs_secret[32]; + uint8_t client_hs_secret[32]; + + mg_hmac_sha256(early_secret, NULL, 0, zeros, sizeof(zeros)); + mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, + 32, pre_extract_secret, 32); + mg_hmac_sha256(tls->handshake_secret, pre_extract_secret, + sizeof(pre_extract_secret), tls->x25519_sec, + sizeof(tls->x25519_sec)); + mg_tls_hexdump("hs secret", tls->handshake_secret, 32); + + // mg_sha256_final is not idempotent, need to copy sha256 context to calculate + // the digest + memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); + mg_sha256_final(hello_hash, &sha256); + + mg_tls_hexdump("hello hash", hello_hash, 32); + // derive keys needed for the rest of the handshake + mg_tls_derive_secret("tls13 s hs traffic", tls->handshake_secret, 32, + hello_hash, 32, server_hs_secret, 32); + mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0, + tls->server_write_key, 16); + mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0, + tls->server_write_iv, 12); + mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0, + tls->server_finished_key, 32); + + mg_tls_derive_secret("tls13 c hs traffic", tls->handshake_secret, 32, + hello_hash, 32, client_hs_secret, 32); + mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0, + tls->client_write_key, 16); + mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0, + tls->client_write_iv, 12); + mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0, + tls->client_finished_key, 32); + + mg_tls_hexdump("s hs traffic", server_hs_secret, 32); + mg_tls_hexdump("s key", tls->server_write_key, 16); + mg_tls_hexdump("s iv", tls->server_write_iv, 12); + mg_tls_hexdump("s finished", tls->server_finished_key, 32); + mg_tls_hexdump("c hs traffic", client_hs_secret, 32); + mg_tls_hexdump("c key", tls->client_write_key, 16); + mg_tls_hexdump("c iv", tls->client_write_iv, 16); + mg_tls_hexdump("c finished", tls->client_finished_key, 32); + + mg_ssl_key_log("SERVER_HANDSHAKE_TRAFFIC_SECRET", tls->random, + server_hs_secret, 32); + mg_ssl_key_log("CLIENT_HANDSHAKE_TRAFFIC_SECRET", tls->random, + client_hs_secret, 32); +} + +static void mg_tls_generate_application_keys(struct mg_connection *c) { + struct tls_data *tls = c->tls; + uint8_t hash[32]; + uint8_t premaster_secret[32]; + uint8_t master_secret[32]; + uint8_t server_secret[32]; + uint8_t client_secret[32]; + + mg_sha256_ctx sha256; + memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); + mg_sha256_final(hash, &sha256); + + mg_tls_derive_secret("tls13 derived", tls->handshake_secret, 32, + zeros_sha256_digest, 32, premaster_secret, 32); + mg_hmac_sha256(master_secret, premaster_secret, 32, zeros, 32); + + mg_tls_derive_secret("tls13 s ap traffic", master_secret, 32, hash, 32, + server_secret, 32); + mg_tls_derive_secret("tls13 key", server_secret, 32, NULL, 0, + tls->server_write_key, 16); + mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0, + tls->server_write_iv, 12); + mg_tls_derive_secret("tls13 c ap traffic", master_secret, 32, hash, 32, + client_secret, 32); + mg_tls_derive_secret("tls13 key", client_secret, 32, NULL, 0, + tls->client_write_key, 16); + mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0, + tls->client_write_iv, 12); + + mg_tls_hexdump("s ap traffic", server_secret, 32); + mg_tls_hexdump("s key", tls->server_write_key, 16); + mg_tls_hexdump("s iv", tls->server_write_iv, 12); + mg_tls_hexdump("s finished", tls->server_finished_key, 32); + mg_tls_hexdump("c ap traffic", client_secret, 32); + mg_tls_hexdump("c key", tls->client_write_key, 16); + mg_tls_hexdump("c iv", tls->client_write_iv, 16); + mg_tls_hexdump("c finished", tls->client_finished_key, 32); + tls->sseq = tls->cseq = 0; + + mg_ssl_key_log("SERVER_TRAFFIC_SECRET_0", tls->random, server_secret, 32); + mg_ssl_key_log("CLIENT_TRAFFIC_SECRET_0", tls->random, client_secret, 32); +} + +// AES GCM encryption of the message + put encoded data into the write buffer +static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, + size_t msgsz, uint8_t msgtype) { + struct tls_data *tls = c->tls; + struct mg_iobuf *wio = &tls->send; + uint8_t *outmsg; + uint8_t *tag; + size_t encsz = msgsz + 16 + 1; + uint8_t hdr[5] = {MG_TLS_APP_DATA, 0x03, 0x03, (encsz >> 8) & 0xff, + encsz & 0xff}; + uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03, + (encsz >> 8) & 0xff, encsz & 0xff}; + uint8_t nonce[12]; + + gcm_initialize(); + + if (c->is_client) { + memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv)); + nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->cseq) & 255U); + } else { + memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv)); + nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->sseq) & 255U); + } + + mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); + mg_iobuf_resize(wio, wio->len + encsz); + outmsg = wio->buf + wio->len; + tag = wio->buf + wio->len + msgsz + 1; + memmove(outmsg, msg, msgsz); + outmsg[msgsz] = msgtype; + if (c->is_client) { + aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->client_write_key, + sizeof(tls->client_write_key), nonce, sizeof(nonce), + associated_data, sizeof(associated_data), tag, 16); + tls->cseq++; + } else { + aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->server_write_key, + sizeof(tls->server_write_key), nonce, sizeof(nonce), + associated_data, sizeof(associated_data), tag, 16); + tls->sseq++; + } + wio->len += encsz; +} + +// read an encrypted record, decrypt it in place +static int mg_tls_recv_record(struct mg_connection *c) { + struct tls_data *tls = c->tls; + struct mg_iobuf *rio = &c->rtls; + uint16_t msgsz; + uint8_t *msg; + uint8_t nonce[12]; + int r; + if (tls->recv.len > 0) { + return 0; /* some data from previous record is still present */ + } + for (;;) { + if (!mg_tls_got_record(c)) { + return MG_IO_WAIT; + } + if (rio->buf[0] == MG_TLS_APP_DATA) { + break; + } else if (rio->buf[0] == + MG_TLS_CHANGE_CIPHER) { // Skip ChangeCipher messages + mg_tls_drop_record(c); + } else if (rio->buf[0] == MG_TLS_ALERT) { // Skip Alerts + MG_INFO(("TLS ALERT packet received")); + mg_tls_drop_record(c); + } else { + mg_error(c, "unexpected packet"); + return -1; + } + } + + gcm_initialize(); + msgsz = MG_LOAD_BE16(rio->buf + 3); + msg = rio->buf + 5; + if (c->is_client) { + memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv)); + nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->sseq) & 255U); + aes_gcm_decrypt(msg, msg, msgsz - 16, tls->server_write_key, + sizeof(tls->server_write_key), nonce, sizeof(nonce)); + tls->sseq++; + } else { + memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv)); + nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->cseq) & 255U); + aes_gcm_decrypt(msg, msg, msgsz - 16, tls->client_write_key, + sizeof(tls->client_write_key), nonce, sizeof(nonce)); + tls->cseq++; + } + r = msgsz - 16 - 1; + tls->content_type = msg[msgsz - 16 - 1]; + tls->recv.buf = msg; + tls->recv.size = tls->recv.len = msgsz - 16 - 1; + return r; +} + +static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, + uint8_t hash[32]) { + struct tls_data *tls = c->tls; + uint8_t sig_content[130] = { + " " + " " + "TLS 1.3, server CertificateVerify\0"}; + mg_sha256_ctx sha256; + memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); + mg_sha256_final(sig_content + 98, &sha256); + + mg_sha256_init(&sha256); + mg_sha256_update(&sha256, sig_content, sizeof(sig_content)); + mg_sha256_final(hash, &sha256); +} // read and parse ClientHello record -static int mg_tls_client_hello(struct mg_connection *c) { +static int mg_tls_server_recv_hello(struct mg_connection *c) { struct tls_data *tls = c->tls; struct mg_iobuf *rio = &c->rtls; uint8_t session_id_len; @@ -9506,15 +9653,20 @@ static int mg_tls_client_hello(struct mg_connection *c) { uint16_t cipher_suites_len; uint16_t ext_len; uint8_t *ext; + uint16_t msgsz; - if (!mg_tls_got_msg(c)) { + if (!mg_tls_got_record(c)) { return MG_IO_WAIT; } - if (rio->buf[0] != 0x16 || rio->buf[5] != 0x01) { - mg_error(c, "not a hello packet"); + if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) { + mg_error(c, "not a client hello packet"); return -1; } - mg_sha256_update(&tls->sha256, rio->buf + 5, rio->len - 5); + msgsz = MG_LOAD_BE16(rio->buf + 3); + mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz); + // store client random + memmove(tls->random, rio->buf + 11, sizeof(tls->random)); + // store session_id session_id_len = rio->buf[43]; if (session_id_len == sizeof(tls->session_id)) { memmove(tls->session_id, rio->buf + 44, session_id_len); @@ -9540,7 +9692,7 @@ static int mg_tls_client_hello(struct mg_connection *c) { uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) { memmove(tls->x25519_cli, key_exchange + k + 4, m); - mg_tls_drop_packet(rio); + mg_tls_drop_record(c); return 0; } k += (uint16_t) (m + 4); @@ -9552,7 +9704,7 @@ static int mg_tls_client_hello(struct mg_connection *c) { } // put ServerHello record into wio buffer -static void mg_tls_server_hello(struct mg_connection *c) { +static void mg_tls_server_send_hello(struct mg_connection *c) { struct tls_data *tls = c->tls; struct mg_iobuf *wio = &tls->send; @@ -9560,12 +9712,10 @@ static void mg_tls_server_hello(struct mg_connection *c) { // server hello, tls 1.2 "\x02\x00\x00\x76\x03\x03" // random (32 bytes) - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // session ID length + session ID (32 bytes) "\x20" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" #if defined(CHACHA20) && CHACHA20 // TLS_CHACHA20_POLY1305_SHA256 + no compression "\x13\x03\x00" @@ -9576,20 +9726,20 @@ static void mg_tls_server_hello(struct mg_connection *c) { // extensions + keyshare "\x00\x2e\x00\x33\x00\x24\x00\x1d\x00\x20" // x25519 keyshare - "\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab" - "\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // supported versions (tls1.3 == 0x304) "\x00\x2b\x00\x02\x03\x04"; // calculate keyshare uint8_t x25519_pub[X25519_BYTES]; uint8_t x25519_prv[X25519_BYTES]; - mg_random(x25519_prv, sizeof(x25519_prv)); - x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1); - x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1); - mg_tls_hexdump("x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); + mg_tls_random(x25519_prv, sizeof(x25519_prv)); + mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1); + mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1); + mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); - // fill in the gaps: session ID + keyshare + // fill in the gaps: random + session ID + keyshare + memmove(msg_server_hello + 6, tls->random, sizeof(tls->random)); memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id)); memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub)); @@ -9602,138 +9752,15 @@ static void mg_tls_server_hello(struct mg_connection *c) { mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6); } -// at this point we have x25519 shared secret, we can generate a set of derived -// handshake encryption keys -static void mg_tls_generate_handshake_keys(struct mg_connection *c) { - struct tls_data *tls = c->tls; - - mg_sha256_ctx sha256; - uint8_t early_secret[32]; - uint8_t pre_extract_secret[32]; - uint8_t hello_hash[32]; - uint8_t server_hs_secret[32]; - uint8_t client_hs_secret[32]; - - mg_hmac_sha256(early_secret, NULL, 0, zeros, sizeof(zeros)); - mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, - 32, pre_extract_secret, 32); - mg_hmac_sha256(tls->handshake_secret, pre_extract_secret, - sizeof(pre_extract_secret), tls->x25519_sec, - sizeof(tls->x25519_sec)); - mg_tls_hexdump("hs secret", tls->handshake_secret, 32); - - // mg_sha256_final is not idempotent, need to copy sha256 context to calculate - // the digest - memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); - mg_sha256_final(hello_hash, &sha256); - - // derive keys needed for the rest of the handshake - mg_tls_derive_secret("tls13 s hs traffic", tls->handshake_secret, 32, - hello_hash, 32, server_hs_secret, 32); - mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0, - tls->server_write_key, 16); - mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0, - tls->server_write_iv, 12); - mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0, - tls->server_finished_key, 32); - mg_tls_hexdump("s hs traffic", server_hs_secret, 32); - - mg_tls_derive_secret("tls13 c hs traffic", tls->handshake_secret, 32, - hello_hash, 32, client_hs_secret, 32); - mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0, - tls->client_write_key, 16); - mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0, - tls->client_write_iv, 12); - mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0, - tls->client_finished_key, 32); -} - -// AES GCM encryption of the message + put encoded data into the write buffer -static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, - size_t msgsz, uint8_t msgtype) { - struct tls_data *tls = c->tls; - struct mg_iobuf *wio = &tls->send; - uint8_t *outmsg; - uint8_t *tag; - size_t encsz = msgsz + 16 + 1; - uint8_t hdr[5] = {0x17, 0x03, 0x03, (encsz >> 8) & 0xff, encsz & 0xff}; - uint8_t associated_data[5] = {0x17, 0x03, 0x03, (encsz >> 8) & 0xff, - encsz & 0xff}; - uint8_t nonce[12]; - memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv)); - nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U); - nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U); - nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((tls->sseq) & 255U); - - gcm_initialize(); - mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); - mg_iobuf_resize(wio, wio->len + encsz); - outmsg = wio->buf + wio->len; - tag = wio->buf + wio->len + msgsz + 1; - memmove(outmsg, msg, msgsz); - outmsg[msgsz] = msgtype; - aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->server_write_key, - sizeof(tls->server_write_key), nonce, sizeof(nonce), - associated_data, sizeof(associated_data), tag, 16); - wio->len += encsz; - tls->sseq++; -} - -// read an encrypted message, decrypt it into read buffer (AES GCM) -static int mg_tls_recv_decrypt(struct mg_connection *c, void *buf, - size_t bufsz) { - struct tls_data *tls = c->tls; - struct mg_iobuf *rio = &c->rtls; - // struct mg_iobuf *rio = &tls->recv; - uint16_t msgsz; - uint8_t *msg; - uint8_t nonce[12]; - int r; - for (;;) { - if (!mg_tls_got_msg(c)) { - return MG_IO_WAIT; - } - if (rio->buf[0] == 0x17) { - break; - } else if (rio->buf[0] == 0x15) { - MG_INFO(("TLS ALERT packet received")); // TODO: drop packet? - } else { - mg_error(c, "unexpected packet"); - return -1; - } - } - msgsz = MG_LOAD_BE16(rio->buf + 3); - msg = rio->buf + 5; - memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv)); - nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U); - nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U); - nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((tls->cseq) & 255U); - aes_gcm_decrypt(msg, msg, msgsz - 16, tls->client_write_key, - sizeof(tls->client_write_key), nonce, sizeof(nonce)); - r = msgsz - 16 - 1; - if (msg[r] == 0x17) { - if (bufsz > 0) { - memmove(buf, msg, msgsz - 16); - } - } else { - r = 0; - } - tls->cseq++; - mg_tls_drop_packet(rio); - return r; -} - -static void mg_tls_server_extensions(struct mg_connection *c) { +static void mg_tls_server_send_ext(struct mg_connection *c) { struct tls_data *tls = c->tls; // server extensions uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0}; mg_sha256_update(&tls->sha256, ext, sizeof(ext)); - mg_tls_encrypt(c, ext, sizeof(ext), 0x16); + mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE); } -static void mg_tls_server_cert(struct mg_connection *c) { +static void mg_tls_server_send_cert(struct mg_connection *c) { struct tls_data *tls = c->tls; // server DER certificate (empty) size_t n = tls->server_cert_der.len; @@ -9754,7 +9781,7 @@ static void mg_tls_server_cert(struct mg_connection *c) { memmove(cert + 11, tls->server_cert_der.ptr, n); cert[11 + n] = cert[12 + n] = 0; // certificate extensions (none) mg_sha256_update(&tls->sha256, cert, 13 + n); - mg_tls_encrypt(c, cert, 13 + n, 0x16); + mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE); } // type adapter between uECC hash context and our sha256 implementation @@ -9778,7 +9805,7 @@ static void finish_SHA256(const uECC_HashContext *base, uint8_t *hash_result) { mg_sha256_final(hash_result, &c->ctx); } -static void mg_tls_server_verify_ecdsa(struct mg_connection *c) { +static void mg_tls_server_send_cert_verify(struct mg_connection *c) { struct tls_data *tls = c->tls; // server certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; @@ -9788,17 +9815,9 @@ static void mg_tls_server_verify_ecdsa(struct mg_connection *c) { {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, {{0}, 0, 0, {0}}}; int neg1, neg2; - uint8_t sig[64], sig_content[130] = { - " " - " " - "TLS 1.3, server CertificateVerify\0"}; - mg_sha256_ctx sha256; - memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); - mg_sha256_final(sig_content + 98, &sha256); + uint8_t sig[64]; - mg_sha256_init(&sha256); - mg_sha256_update(&sha256, sig_content, sizeof(sig_content)); - mg_sha256_final(hash, &sha256); + mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash); uECC_sign_deterministic(tls->server_key, hash, sizeof(hash), &ctx.uECC, sig, uECC_secp256r1()); @@ -9819,13 +9838,11 @@ static void mg_tls_server_verify_ecdsa(struct mg_connection *c) { verify[3] = (uint8_t) (sigsz + 4); verify[7] = (uint8_t) sigsz; - mg_tls_hexdump("verify", verify, verifysz); - mg_sha256_update(&tls->sha256, verify, verifysz); - mg_tls_encrypt(c, verify, verifysz, 0x16); + mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE); } -static void mg_tls_server_finish(struct mg_connection *c) { +static void mg_tls_server_send_finish(struct mg_connection *c) { struct tls_data *tls = c->tls; struct mg_iobuf *wio = &tls->send; mg_sha256_ctx sha256; @@ -9834,112 +9851,442 @@ static void mg_tls_server_finish(struct mg_connection *c) { memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); mg_sha256_final(hash, &sha256); mg_hmac_sha256(finish + 4, tls->server_finished_key, 32, hash, 32); - mg_tls_hexdump("hash", hash, sizeof(hash)); - mg_tls_hexdump("key", tls->server_finished_key, - sizeof(tls->server_finished_key)); - mg_tls_encrypt(c, finish, sizeof(finish), 0x16); + mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE); mg_io_send(c, wio->buf, wio->len); wio->len = 0; mg_sha256_update(&tls->sha256, finish, sizeof(finish)); } -static int mg_tls_client_change_cipher(struct mg_connection *c) { - // struct tls_data *tls = c->tls; +static int mg_tls_server_recv_finish(struct mg_connection *c) { + struct tls_data *tls = c->tls; + // we have to backup sha256 value to restore it later, since Finished record + // is exceptional and is not supposed to be added to the rolling hash + // calculation. + mg_sha256_ctx sha256 = tls->sha256; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_FINISHED) { + mg_error(c, "expected Finish but got msg 0x%02x", tls->recv.buf[0]); + return -1; + } + mg_tls_drop_message(c); + + // restore hash + tls->sha256 = sha256; + return 0; +} + +static void mg_tls_client_send_hello(struct mg_connection *c) { + struct tls_data *tls = c->tls; + struct mg_iobuf *wio = &tls->send; + + const char *hostname = tls->hostname; + size_t hostnamesz = strlen(tls->hostname); + uint8_t x25519_pub[X25519_BYTES]; + + uint8_t msg_client_hello[162 + 32] = + // TLS Client Hello header reported as TLS1.2 (5) + "\x16\x03\x01\x00\xfe" + // server hello, tls 1.2 (6) + "\x01\x00\x00\x8c" + "\x03\x03" + // random (32 bytes) + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + // session ID length + session ID (32 bytes) + "\x20" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + // supported algorithms +#if defined(CHACHA20) && CHACHA20 + // TLS_CHACHA20_POLY1305_SHA256 + no compression + "\x13\x03\x00" +#else + "\x00\x02" // size = 2 bytes + "\x13\x01" // TLS_AES_128_GCM_SHA256 + "\x01\x00" // no compression +#endif + + // extensions + keyshare + "\x00\xfe" /* NN */ + // x25519 keyshare + "\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + // supported groups (x25519) + "\x00\x0a\x00\x04\x00\x02\x00\x1d" + // supported versions (tls1.3 == 0x304) + "\x00\x2b\x00\x03\x02\x03\x04" + // session ticket (none) + "\x00\x23\x00\x00" + // signature algorithms (we don't care, so list all the common ones) + "\x00\x0d\x00\x24\x00\x22\x04\x03\x05\x03\x06\x03\x08\x07\x08\x08" + "\x08\x1a\x08\x1b\x08\x1c\x08\x09\x08\x0a\x08\x0b\x08\x04\x08\x05" + "\x08\x06\x04\x01\x05\x01\x06\x01" + // server name + "\x00\x00\x00\xfe" + "\x00\xfe\x00\x00\xfe"; + + // patch ClientHello with correct hostname length + offset: + MG_STORE_BE16(msg_client_hello + 3, hostnamesz + 189); + MG_STORE_BE16(msg_client_hello + 7, hostnamesz + 185); + MG_STORE_BE16(msg_client_hello + 82, hostnamesz + 110); + MG_STORE_BE16(msg_client_hello + 187, hostnamesz + 5); + MG_STORE_BE16(msg_client_hello + 189, hostnamesz + 3); + MG_STORE_BE16(msg_client_hello + 192, hostnamesz); + + // calculate keyshare + mg_tls_random(tls->x25519_cli, sizeof(tls->x25519_cli)); + mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1); + + // fill in the gaps: random + session ID + keyshare + mg_tls_random(tls->session_id, sizeof(tls->session_id)); + mg_tls_random(tls->random, sizeof(tls->random)); + memmove(msg_client_hello + 11, tls->random, sizeof(tls->random)); + memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id)); + memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub)); + + // server hello message + mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)); + mg_iobuf_add(wio, wio->len, hostname, strlen(hostname)); + mg_sha256_update(&tls->sha256, msg_client_hello + 5, + sizeof(msg_client_hello) - 5); + mg_sha256_update(&tls->sha256, (uint8_t *) hostname, strlen(hostname)); + + // change cipher message + mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6); + mg_io_send(c, wio->buf, wio->len); + wio->len = 0; +} + +static int mg_tls_client_recv_hello(struct mg_connection *c) { + struct tls_data *tls = c->tls; struct mg_iobuf *rio = &c->rtls; - for (;;) { - if (!mg_tls_got_msg(c)) { - return MG_IO_WAIT; + uint16_t msgsz; + uint8_t *ext; + uint16_t ext_len; + int j; + + if (!mg_tls_got_record(c)) { + return MG_IO_WAIT; + } + if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_SERVER_HELLO) { + if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) { + mg_error(c, "tls alert %d", rio->buf[6]); + return -1; } - if (rio->buf[0] == 0x14) { // got a ChangeCipher record - break; - } else if (rio->buf[0] == 0x15) { // skip Alert records - MG_DEBUG(("TLS ALERT packet received")); - mg_tls_drop_packet(rio); - } else { - mg_error(c, "unexpected packet"); - return MG_IO_ERR; + MG_INFO(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5])); + mg_error(c, "not a server hello packet"); + return -1; + } + + msgsz = MG_LOAD_BE16(rio->buf + 3); + mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz); + + ext_len = MG_LOAD_BE16(rio->buf + 5 + 39 + 32 + 3); + ext = rio->buf + 5 + 39 + 32 + 3 + 2; + + for (j = 0; j < ext_len;) { + uint16_t ext_type = MG_LOAD_BE16(ext + j); + uint16_t ext_len = MG_LOAD_BE16(ext + j + 2); + uint16_t group; + uint8_t *key_exchange; + uint16_t key_exchange_len; + if (ext_type != 0x0033) { // not a key share extension, ignore + j += (uint16_t) (ext_len + 4); + continue; } + group = MG_LOAD_BE16(ext + j + 4); + if (group != 0x001d) { + mg_error(c, "bad key exchange group"); + return -1; + } + key_exchange_len = MG_LOAD_BE16(ext + j + 6); + key_exchange = ext + j + 8; + if (key_exchange_len != 32) { + mg_error(c, "bad key exchange length"); + return -1; + } + mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1); + mg_tls_hexdump("c x25519 sec", tls->x25519_sec, 32); + mg_tls_drop_record(c); + /* generate handshake keys */ + mg_tls_generate_handshake_keys(c); + return 0; } - // consume ChangeCipher packet - mg_tls_drop_packet(rio); + mg_error(c, "bad client hello"); + return -1; +} + +static int mg_tls_client_recv_ext(struct mg_connection *c) { + struct tls_data *tls = c->tls; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_ENCRYPTED_EXTENSIONS) { + mg_error(c, "expected server extensions but got msg 0x%02x", + tls->recv.buf[0]); + return -1; + } + mg_tls_drop_message(c); return 0; } -static int mg_tls_client_finish(struct mg_connection *c) { - uint8_t tmp[2048]; - int n = mg_tls_recv_decrypt(c, tmp, sizeof(tmp)); - if (n < 0) { +static int mg_tls_client_recv_cert(struct mg_connection *c) { + uint8_t *cert; + uint32_t certsz; + struct mg_der_tlv oid, pubkey, seq, subj; + int subj_match = 0; + struct tls_data *tls = c->tls; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_CERTIFICATE) { + mg_error(c, "expected server certificate but got msg 0x%02x", + tls->recv.buf[0]); + return -1; + } + if (tls->skip_verification) { + mg_tls_drop_message(c); + return 0; + } + + if (tls->recv.len < 11) { + mg_error(c, "certificate list too short"); + return -1; + } + + cert = tls->recv.buf + 11; + certsz = MG_LOAD_BE24(tls->recv.buf + 8); + if (certsz > tls->recv.len - 11) { + mg_error(c, "certificate too long: %d vs %d", certsz, tls->recv.len - 11); + return -1; + } + + do { + // secp256r1 public key + if (mg_der_find(cert, certsz, + (uint8_t *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8, + &oid) < 0) { + mg_error(c, "certificate secp256r1 public key OID not found"); + return -1; + } + if (mg_der_tlv(oid.value + oid.len, cert + certsz - oid.value - oid.len, + &pubkey) < 0) { + mg_error(c, "certificate secp256r1 public key not found"); + return -1; + } + + // expect BIT STRING, unpadded, uncompressed: [0]+[4]+32+32 content bytes + if (pubkey.type != 3 || pubkey.len != 66 || pubkey.value[0] != 0 || + pubkey.value[1] != 4) { + mg_error(c, "unsupported public key bitstring encoding"); + return -1; + } + memmove(tls->pubkey, pubkey.value + 2, pubkey.len - 2); + } while (0); + + // Subject Alternative Names + do { + if (mg_der_find(cert, certsz, (uint8_t *) "\x55\x1d\x11", 3, &oid) < 0) { + mg_error(c, "certificate does not contain subject alternative names"); + return -1; + } + if (mg_der_tlv(oid.value + oid.len, cert + certsz - oid.value - oid.len, + &seq) < 0) { + mg_error(c, "certificate subject alternative names not found"); + return -1; + } + if (mg_der_tlv(seq.value, seq.len, &seq) < 0) { + mg_error( + c, + "certificate subject alternative names is not a constructed object"); + return -1; + } + MG_VERBOSE(("verify hostname %s", tls->hostname)); + while (seq.len > 0) { + if (mg_der_tlv(seq.value, seq.len, &subj) < 0) { + mg_error(c, "bad subject alternative name"); + return -1; + } + MG_VERBOSE(("subj=%.*s", subj.len, subj.value)); + if (mg_match(mg_str((const char *) tls->hostname), + mg_str_n((const char *) subj.value, subj.len), NULL)) { + subj_match = 1; + break; + } + seq.len = seq.len - (subj.value + subj.len - seq.value); + seq.value = subj.value + subj.len; + } + if (!subj_match) { + mg_error(c, "certificate did not match the hostname"); + return -1; + } + } while (0); + + mg_tls_drop_message(c); + mg_tls_calc_cert_verify_hash(c, tls->sighash); + return 0; +} + +static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { + struct tls_data *tls = c->tls; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_CERTIFICATE_VERIFY) { + mg_error(c, "expected server certificate verify but got msg 0x%02x", + tls->recv.buf[0]); return -1; } - // TODO: make sure it's a ClientFinish record + // Ignore CertificateVerify is strict checks are not required + if (tls->skip_verification) { + mg_tls_drop_message(c); + return 0; + } + + // Extract certificate signature and verify it using pubkey and sighash + do { + uint8_t sig[64]; + struct mg_der_tlv seq, a, b; + if (mg_der_tlv(tls->recv.buf + 8, tls->recv.len - 8, &seq) < 0) { + mg_error(c, "verification message is not an ASN.1 DER sequence"); + return -1; + } + if (mg_der_tlv(seq.value, seq.len, &a) < 0) { + mg_error(c, "missing first part of the signature"); + return -1; + } + if (mg_der_tlv(a.value + a.len, seq.len - a.len, &b) < 0) { + mg_error(c, "missing second part of the signature"); + return -1; + } + // Integers may be padded with zeroes + if (a.len > 32) { + a.value = a.value + (a.len - 32); + a.len = 32; + } + if (b.len > 32) { + b.value = b.value + (b.len - 32); + b.len = 32; + } + + memmove(sig, a.value, a.len); + memmove(sig + 32, b.value, b.len); + + if (uECC_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, + uECC_secp256r1()) != 1) { + mg_error(c, "failed to verify certificate"); + return -1; + } + } while (0); + + mg_tls_drop_message(c); return 0; } -static void mg_tls_generate_application_keys(struct mg_connection *c) { +static int mg_tls_client_recv_finish(struct mg_connection *c) { struct tls_data *tls = c->tls; - uint8_t hash[32]; - uint8_t premaster_secret[32]; - uint8_t master_secret[32]; - uint8_t server_secret[32]; - uint8_t client_secret[32]; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_FINISHED) { + mg_error(c, "expected server finished but got msg 0x%02x", + tls->recv.buf[0]); + return -1; + } + mg_tls_drop_message(c); + return 0; +} +static void mg_tls_client_send_finish(struct mg_connection *c) { + struct tls_data *tls = c->tls; + struct mg_iobuf *wio = &tls->send; mg_sha256_ctx sha256; + uint8_t hash[32]; + uint8_t finish[36] = {0x14, 0, 0, 32}; memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); mg_sha256_final(hash, &sha256); + mg_hmac_sha256(finish + 4, tls->client_finished_key, 32, hash, 32); + mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE); + mg_io_send(c, wio->buf, wio->len); + wio->len = 0; +} - mg_tls_derive_secret("tls13 derived", tls->handshake_secret, 32, - zeros_sha256_digest, 32, premaster_secret, 32); - mg_hmac_sha256(master_secret, premaster_secret, 32, zeros, 32); - - mg_tls_derive_secret("tls13 s ap traffic", master_secret, 32, hash, 32, - server_secret, 32); - mg_tls_derive_secret("tls13 key", server_secret, 32, NULL, 0, - tls->server_write_key, 16); - mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0, - tls->server_write_iv, 12); - mg_tls_derive_secret("tls13 c ap traffic", master_secret, 32, hash, 32, - client_secret, 32); - mg_tls_derive_secret("tls13 key", client_secret, 32, NULL, 0, - tls->client_write_key, 16); - mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0, - tls->client_write_iv, 12); - - tls->sseq = tls->cseq = 0; +static void mg_tls_client_handshake(struct mg_connection *c) { + struct tls_data *tls = c->tls; + switch (tls->state) { + case MG_TLS_STATE_CLIENT_START: + mg_tls_client_send_hello(c); + tls->state = MG_TLS_STATE_CLIENT_WAIT_SH; + case MG_TLS_STATE_CLIENT_WAIT_SH: + if (mg_tls_client_recv_hello(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_EE; + case MG_TLS_STATE_CLIENT_WAIT_EE: + if (mg_tls_client_recv_ext(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT; + case MG_TLS_STATE_CLIENT_WAIT_CERT: + if (mg_tls_client_recv_cert(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_CV; + case MG_TLS_STATE_CLIENT_WAIT_CV: + if (mg_tls_client_recv_cert_verify(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISHED; + case MG_TLS_STATE_CLIENT_WAIT_FINISHED: + if (mg_tls_client_recv_finish(c) < 0) { + break; + } + mg_tls_client_send_finish(c); + mg_tls_generate_application_keys(c); + tls->state = MG_TLS_STATE_CLIENT_CONNECTED; + c->is_tls_hs = 0; + break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; + } } -void mg_tls_handshake(struct mg_connection *c) { +static void mg_tls_server_handshake(struct mg_connection *c) { struct tls_data *tls = c->tls; switch (tls->state) { - case MG_TLS_HS_CLIENT_HELLO: - if (mg_tls_client_hello(c) < 0) { + case MG_TLS_STATE_SERVER_START: + if (mg_tls_server_recv_hello(c) < 0) { return; } - tls->state = MG_TLS_HS_SERVER_HELLO; - // fallthrough - case MG_TLS_HS_SERVER_HELLO: - mg_tls_server_hello(c); + mg_tls_server_send_hello(c); mg_tls_generate_handshake_keys(c); - mg_tls_server_extensions(c); - mg_tls_server_cert(c); - mg_tls_server_verify_ecdsa(c); - mg_tls_server_finish(c); - tls->state = MG_TLS_HS_CLIENT_CHANGE_CIPHER; - // fallthrough - case MG_TLS_HS_CLIENT_CHANGE_CIPHER: - if (mg_tls_client_change_cipher(c) < 0) { - return; - } - tls->state = MG_TLS_HS_CLIENT_FINISH; + mg_tls_server_send_ext(c); + mg_tls_server_send_cert(c); + mg_tls_server_send_cert_verify(c); + mg_tls_server_send_finish(c); + tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; // fallthrough - case MG_TLS_HS_CLIENT_FINISH: - if (mg_tls_client_finish(c) < 0) { + case MG_TLS_STATE_SERVER_NEGOTIATED: + if (mg_tls_server_recv_finish(c) < 0) { return; } mg_tls_generate_application_keys(c); - tls->state = MG_TLS_HS_DONE; - // fallthrough - case MG_TLS_HS_DONE: c->is_tls_hs = 0; return; + tls->state = MG_TLS_STATE_SERVER_CONNECTED; + c->is_tls_hs = 0; + return; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; + } +} + +void mg_tls_handshake(struct mg_connection *c) { + if (c->is_client) { + mg_tls_client_handshake(c); + } else { + mg_tls_server_handshake(c); } } @@ -9983,27 +10330,30 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { mg_error(c, "tls oom"); return; } - // parse PEM or DER EC key - if (opts->key.ptr == NULL || - mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) < 0) { - MG_ERROR(("Failed to load EC private key")); - return; - } - if (key.len < 39) { - MG_ERROR(("EC private key too short")); - return; + + tls->state = + c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START; + + tls->skip_verification = opts->skip_verification; + tls->send.align = MG_IO_SIZE; + + c->tls = tls; + c->is_tls = c->is_tls_hs = 1; + mg_sha256_init(&tls->sha256); + + // save hostname (client extension) + if (opts->name.len > 0) { + if (opts->name.len >= sizeof(tls->hostname) - 1) { + mg_error(c, "hostname too long"); + } + strncpy((char *) tls->hostname, opts->name.ptr, sizeof(tls->hostname) - 1); + tls->hostname[opts->name.len] = 0; } - // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...] - // 30 nn 02 01 01 04 20 [key] ... - if (key.ptr[0] != 0x30 || (key.ptr[1] & 0x80) != 0) { - MG_ERROR(("EC private key: ASN.1 bad sequence")); + + if (c->is_client) { + tls->server_cert_der.ptr = NULL; return; } - if (memcmp(key.ptr + 2, "\x02\x01\x01\x04\x20", 5) != 0) { - MG_ERROR(("EC private key: ASN.1 bad data")); - } - memmove(tls->server_key, key.ptr + 7, 32); - free((void *) key.ptr); // parse PEM or DER certificate if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->server_cert_der) < @@ -10011,12 +10361,34 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { MG_ERROR(("Failed to load certificate")); return; } - - // tls->send.align = tls->recv.align = MG_IO_SIZE; - tls->send.align = MG_IO_SIZE; - c->tls = tls; - c->is_tls = c->is_tls_hs = 1; - mg_sha256_init(&tls->sha256); + + // parse PEM or DER EC key + if (opts->key.ptr == NULL) { + mg_error(c, "certificate provided without a private key"); + return; + } + + if (mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) == 0) { + if (key.len < 39) { + MG_ERROR(("EC private key too short")); + return; + } + // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...] + // 30 nn 02 01 01 04 20 [key] ... + if (key.ptr[0] != 0x30 || (key.ptr[1] & 0x80) != 0) { + MG_ERROR(("EC private key: ASN.1 bad sequence")); + return; + } + if (memcmp(key.ptr + 2, "\x02\x01\x01\x04\x20", 5) != 0) { + MG_ERROR(("EC private key: ASN.1 bad data")); + } + memmove(tls->server_key, key.ptr + 7, 32); + free((void *) key.ptr); + } else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) { + mg_error(c, "PKCS8 private key format is not supported"); + } else { + mg_error(c, "expected EC PRIVATE KEY or PRIVATE KEY"); + } } void mg_tls_free(struct mg_connection *c) { @@ -10033,7 +10405,7 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct tls_data *tls = c->tls; long n = MG_IO_WAIT; if (len > MG_IO_SIZE) len = MG_IO_SIZE; - mg_tls_encrypt(c, buf, len, 0x17); + mg_tls_encrypt(c, buf, len, MG_TLS_APP_DATA); while (tls->send.len > 0 && (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { mg_iobuf_del(&tls->send, 0, (size_t) n); @@ -10043,11 +10415,31 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { - return mg_tls_recv_decrypt(c, buf, len); + int r = 0; + struct tls_data *tls = c->tls; + size_t minlen; + + r = mg_tls_recv_record(c); + if (r < 0) { + return r; + } + if (tls->content_type != MG_TLS_APP_DATA) { + tls->recv.len = 0; + mg_tls_drop_record(c); + return MG_IO_WAIT; + } + minlen = len < tls->recv.len ? len : tls->recv.len; + memmove(buf, tls->recv.buf, minlen); + tls->recv.buf += minlen; + tls->recv.len -= minlen; + if (tls->recv.len == 0) { + mg_tls_drop_record(c); + } + return minlen; } size_t mg_tls_pending(struct mg_connection *c) { - return mg_tls_got_msg(c) ? 1 : 0; + return mg_tls_got_record(c) ? 1 : 0; } void mg_tls_ctx_init(struct mg_mgr *mgr) { @@ -13745,6 +14137,256 @@ void uECC_point_mult(uECC_word_t *result, const uECC_word_t *point, #endif // MG_TLS_BUILTIN // End of uecc BSD-2 +#ifdef MG_ENABLE_LINES +#line 1 "src/tls_x25519.c" +#endif +/** + * Adapted from STROBE: https://strobe.sourceforge.io/ + * Copyright (c) 2015-2016 Cryptography Research, Inc. + * Author: Mike Hamburg + * License: MIT License + */ + + +const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9}; + +#define X25519_WBITS 32 + +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +typedef int64_t sdlimb_t; +#define LIMB(x) (uint32_t)(x##ull), (uint32_t) ((x##ull) >> 32) + +#define NLIMBS (256 / X25519_WBITS) +typedef limb_t fe[NLIMBS]; + +static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) { + dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry; + *carry = (limb_t) (tmp >> X25519_WBITS); + return (limb_t) tmp; +} + +// These functions are implemented in terms of umaal on ARM +static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) { + dlimb_t total = (dlimb_t) *carry + acc + mand; + *carry = (limb_t) (total >> X25519_WBITS); + return (limb_t) total; +} + +static limb_t adc0(limb_t *carry, limb_t acc) { + dlimb_t total = (dlimb_t) *carry + acc; + *carry = (limb_t) (total >> X25519_WBITS); + return (limb_t) total; +} + +// - Precondition: carry is small. +// - Invariant: result of propagate is < 2^255 + 1 word +// - In particular, always less than 2p. +// - Also, output x >= min(x,19) +static void propagate(fe x, limb_t over) { + unsigned i; + limb_t carry; + over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1; + x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1)); + + carry = over * 19; + for (i = 0; i < NLIMBS; i++) { + x[i] = adc0(&carry, x[i]); + } +} + +static void add(fe out, const fe a, const fe b) { + unsigned i; + limb_t carry = 0; + for (i = 0; i < NLIMBS; i++) { + out[i] = adc(&carry, a[i], b[i]); + } + propagate(out, carry); +} + +static void sub(fe out, const fe a, const fe b) { + unsigned i; + sdlimb_t carry = -38; + for (i = 0; i < NLIMBS; i++) { + carry = carry + a[i] - b[i]; + out[i] = (limb_t) carry; + carry >>= X25519_WBITS; + } + propagate(out, (limb_t) (1 + carry)); +} + +// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `fe` +// to avoid build warnings +static void mul(fe out, const fe a, const limb_t *b, unsigned nb) { + limb_t accum[2 * NLIMBS] = {0}; + unsigned i, j; + + limb_t carry2; + for (i = 0; i < nb; i++) { + limb_t mand = b[i]; + carry2 = 0; + for (j = 0; j < NLIMBS; j++) { + limb_t tmp; // "a" may be misaligned + memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy + accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp); + } + accum[i + j] = carry2; + } + + carry2 = 0; + for (j = 0; j < NLIMBS; j++) { + out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]); + } + propagate(out, carry2); +} + +static void sqr(fe out, const fe a) { + mul(out, a, a, NLIMBS); +} +static void mul1(fe out, const fe a) { + mul(out, a, out, NLIMBS); +} +static void sqr1(fe a) { + mul1(a, a); +} + +static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS], + limb_t doswap) { + unsigned i; + for (i = 0; i < 2 * NLIMBS; i++) { + limb_t xor = (a[i] ^ b[i]) & doswap; + a[i] ^= xor; + b[i] ^= xor; + } +} + +// Canonicalize a field element x, reducing it to the least residue which is +// congruent to it mod 2^255-19 +// - Precondition: x < 2^255 + 1 word +static limb_t canon(fe x) { + // First, add 19. + unsigned i; + limb_t carry0 = 19; + limb_t res; + sdlimb_t carry; + for (i = 0; i < NLIMBS; i++) { + x[i] = adc0(&carry0, x[i]); + } + propagate(x, carry0); + + // Here, 19 <= x2 < 2^255 + // - This is because we added 19, so before propagate it can't be less + // than 19. After propagate, it still can't be less than 19, because if + // propagate does anything it adds 19. + // - We know that the high bit must be clear, because either the input was ~ + // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or + // it was < 2^255. So now, if we subtract 19, we will get back to something in + // [0,2^255-19). + carry = -19; + res = 0; + for (i = 0; i < NLIMBS; i++) { + carry += x[i]; + res |= x[i] = (limb_t) carry; + carry >>= X25519_WBITS; + } + return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS); +} + +static const limb_t a24[1] = {121665}; + +static void ladder_part1(fe xs[5]) { + limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; + add(t1, x2, z2); // t1 = A + sub(z2, x2, z2); // z2 = B + add(x2, x3, z3); // x2 = C + sub(z3, x3, z3); // z3 = D + mul1(z3, t1); // z3 = DA + mul1(x2, z2); // x3 = BC + add(x3, z3, x2); // x3 = DA+CB + sub(z3, z3, x2); // z3 = DA-CB + sqr1(t1); // t1 = AA + sqr1(z2); // z2 = BB + sub(x2, t1, z2); // x2 = E = AA-BB + mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24 + add(z2, z2, t1); // z2 = E*a24 + AA +} + +static void ladder_part2(fe xs[5], const fe x1) { + limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; + sqr1(z3); // z3 = (DA-CB)^2 + mul1(z3, x1); // z3 = x1 * (DA-CB)^2 + sqr1(x3); // x3 = (DA+CB)^2 + mul1(z2, x2); // z2 = AA*(E*a24+AA) + sub(x2, t1, x2); // x2 = BB again + mul1(x2, t1); // x2 = AA*BB +} + +static void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], + const uint8_t *x1, int clamp) { + int i; + limb_t swap = 0; + limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3]; + memset(xs, 0, 4 * sizeof(fe)); + x2[0] = z3[0] = 1; + memcpy(x3, x1, sizeof(fe)); + + for (i = 255; i >= 0; i--) { + uint8_t bytei = scalar[i / 8]; + limb_t doswap; + if (clamp) { + if (i / 8 == 0) { + bytei &= (uint8_t) ~7U; + } else if (i / 8 == X25519_BYTES - 1) { + bytei &= 0x7F; + bytei |= 0x40; + } + } + doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1); + condswap(x2, x3, swap ^ doswap); + swap = doswap; + + ladder_part1(xs); + ladder_part2(xs, (const limb_t *) x1); + } + condswap(x2, x3, swap); +} + +int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], + const uint8_t x1[X25519_BYTES], int clamp) { + int i, ret; + fe xs[5]; + limb_t *x2, *z2, *z3, *prev; + static const struct { + uint8_t a, c, n; + } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1}, + {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125}, + {3, 1, 2}, {3, 1, 2}, {3, 1, 1}}; + x25519_core(xs, scalar, x1, clamp); + + // Precomputed inversion chain + x2 = xs[0]; + z2 = xs[1]; + z3 = xs[3]; + + prev = z2; + for (i = 0; i < 13; i++) { + int j; + limb_t *a = xs[steps[i].a]; + for (j = steps[i].n; j > 0; j--) { + sqr(a, prev); + prev = a; + } + mul1(a, xs[steps[i].c]); + } + + // Here prev = z3 + // x2 /= z2 + mul((limb_t *) out, x2, z3, NLIMBS); + ret = (int) canon((limb_t *) out); + if (!clamp) ret = 0; + return ret; +} + #ifdef MG_ENABLE_LINES #line 1 "src/url.c" #endif diff --git a/mongoose.h b/mongoose.h index 6e2ae9abcbd..4ccc53d9b11 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1201,6 +1201,12 @@ typedef struct { void mg_sha1_init(mg_sha1_ctx *); void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len); void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *); +// https://github.com/B-Con/crypto-algorithms +// Author: Brad Conte (brad AT bradconte.com) +// Disclaimer: This code is presented "as is" without any guarantees. +// Details: Defines the API for the corresponding SHA1 implementation. +// Copyright: public domain + @@ -1217,6 +1223,19 @@ void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len); void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *); void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, size_t datasz); +#ifndef TLS_X15519_H +#define TLS_X15519_H + + + +#define X25519_BYTES 32 +extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; + +int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], + const uint8_t x1[X25519_BYTES], int clamp); + + +#endif /* TLS_X15519_H */ /****************************************************************************** * * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL @@ -2345,10 +2364,11 @@ void mg_http_serve_ssi(struct mg_connection *c, const char *root, struct mg_tls_opts { - struct mg_str ca; // PEM or DER - struct mg_str cert; // PEM or DER - struct mg_str key; // PEM or DER - struct mg_str name; // If not empty, enable host name verification + struct mg_str ca; // PEM or DER + struct mg_str cert; // PEM or DER + struct mg_str key; // PEM or DER + struct mg_str name; // If not empty, enable host name verification + int skip_verification; // Skip certificate and host name verification }; void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *opts); @@ -3080,14 +3100,6 @@ struct mg_tcpip_driver_tm4c_data { #endif - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500 - -#undef MG_ENABLE_TCPIP_DRIVER_INIT -#define MG_ENABLE_TCPIP_DRIVER_INIT 0 - -#endif - #ifdef __cplusplus } #endif diff --git a/src/sha256.c b/src/sha256.c index c1dccf52a10..e2a415ff5d6 100644 --- a/src/sha256.c +++ b/src/sha256.c @@ -1,3 +1,9 @@ +// https://github.com/B-Con/crypto-algorithms +// Author: Brad Conte (brad AT bradconte.com) +// Disclaimer: This code is presented "as is" without any guarantees. +// Details: Defines the API for the corresponding SHA1 implementation. +// Copyright: public domain + #include "sha256.h" #define ror(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) @@ -39,8 +45,10 @@ static void mg_sha256_chunk(mg_sha256_ctx *ctx) { uint32_t a, b, c, d, e, f, g, h; uint32_t m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (uint32_t) ((ctx->buffer[j] << 24) | (ctx->buffer[j + 1] << 16) | - (ctx->buffer[j + 2] << 8) | (ctx->buffer[j + 3])); + m[i] = (uint32_t) (((uint32_t) ctx->buffer[j] << 24) | + ((uint32_t) ctx->buffer[j + 1] << 16) | + ((uint32_t) ctx->buffer[j + 2] << 8) | + ((uint32_t) ctx->buffer[j + 3])); for (; i < 64; ++i) m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16]; @@ -157,4 +165,3 @@ void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, mg_sha256_update(&ctx, dst, 32); mg_sha256_final(dst, &ctx); } - diff --git a/src/sha256.h b/src/sha256.h index b8473e9be06..71e8a6df21e 100644 --- a/src/sha256.h +++ b/src/sha256.h @@ -1,3 +1,9 @@ +// https://github.com/B-Con/crypto-algorithms +// Author: Brad Conte (brad AT bradconte.com) +// Disclaimer: This code is presented "as is" without any guarantees. +// Details: Defines the API for the corresponding SHA1 implementation. +// Copyright: public domain + #pragma once #include "arch.h" diff --git a/src/tls.h b/src/tls.h index 0ccd5e371b1..dbcb112481d 100644 --- a/src/tls.h +++ b/src/tls.h @@ -15,10 +15,11 @@ #include "tls_openssl.h" struct mg_tls_opts { - struct mg_str ca; // PEM or DER - struct mg_str cert; // PEM or DER - struct mg_str key; // PEM or DER - struct mg_str name; // If not empty, enable host name verification + struct mg_str ca; // PEM or DER + struct mg_str cert; // PEM or DER + struct mg_str key; // PEM or DER + struct mg_str name; // If not empty, enable host name verification + int skip_verification; // Skip certificate and host name verification }; void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *opts); diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 97da213733f..c698d89bbf9 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -1,33 +1,71 @@ #include "tls.h" +#include "tls_aes128.h" +#include "tls_x25519.h" +#define mg_tls_random(x, n) memset(x, 0xab, n) #if MG_TLS == MG_TLS_BUILTIN -// handshake is re-entrant, so we need to keep track of its state +/* TLS 1.3 Record Content Type (RFC8446 B.1) */ +#define MG_TLS_CHANGE_CIPHER 20 +#define MG_TLS_ALERT 21 +#define MG_TLS_HANDSHAKE 22 +#define MG_TLS_APP_DATA 23 +#define MG_TLS_HEARTBEAT 24 + +/* TLS 1.3 Handshake Message Type (RFC8446 B.3) */ +#define MG_TLS_CLIENT_HELLO 1 +#define MG_TLS_SERVER_HELLO 2 +#define MG_TLS_ENCRYPTED_EXTENSIONS 8 +#define MG_TLS_CERTIFICATE 11 +#define MG_TLS_CERTIFICATE_VERIFY 15 +#define MG_TLS_FINISHED 20 + +// handshake is re-entrant, so we need to keep track of its state state names +// refer to RFC8446#A.1 enum mg_tls_hs_state { - MG_TLS_HS_CLIENT_HELLO, // first, wait for ClientHello - MG_TLS_HS_SERVER_HELLO, // then, send all server handshake data at once - MG_TLS_HS_CLIENT_CHANGE_CIPHER, // finally wait for ClientChangeCipher - MG_TLS_HS_CLIENT_FINISH, // and ClientFinish (encrypted) - MG_TLS_HS_DONE, // finish handshake, start application data flow + // Client state machine: + MG_TLS_STATE_CLIENT_START, // Send ClientHello + MG_TLS_STATE_CLIENT_WAIT_SH, // Wait for ServerHello + MG_TLS_STATE_CLIENT_WAIT_EE, // Wait for EncryptedExtensions + MG_TLS_STATE_CLIENT_WAIT_CERT, // Wait for Certificate + MG_TLS_STATE_CLIENT_WAIT_CV, // Wait for CertificateVerify + MG_TLS_STATE_CLIENT_WAIT_FINISHED, // Wait for Finished + MG_TLS_STATE_CLIENT_CONNECTED, // Done + + // Server state machine: + MG_TLS_STATE_SERVER_START, // Wait for ClientHello + MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finished + MG_TLS_STATE_SERVER_CONNECTED, // Done }; // per-connection TLS data struct tls_data { enum mg_tls_hs_state state; // keep track of connection handshake progress - struct mg_iobuf send; // For the receive path, we're reusing c->rtls + struct mg_iobuf send; // For the receive path, we're reusing c->rtls + struct mg_iobuf recv; // While c->rtls contains full records, recv reuses + // the same underlying buffer but points at individual + // decrypted messages + uint8_t content_type; // Last received record content type mg_sha256_ctx sha256; // incremental SHA-256 hash for TLS handshake uint32_t sseq; // server sequence number, used in encryption uint32_t cseq; // client sequence number, used in decryption + uint8_t random[32]; // client random from ClientHello uint8_t session_id[32]; // client session ID between the handshake states uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t x25519_sec[32]; // x25519 secret between the handshake states + int skip_verification; // perform checks on server certificate? struct mg_str server_cert_der; // server certificate in DER format uint8_t server_key[32]; // server EC private key + char hostname[254]; // server hostname (client extension) + + uint8_t certhash[32]; // certificate message hash + uint8_t pubkey[64]; // server EC public key to verify cert + uint8_t sighash[32]; // server EC public key to verify cert // keys for AES encryption uint8_t handshake_secret[32]; @@ -40,7 +78,46 @@ struct tls_data { }; #define MG_LOAD_BE16(p) ((uint16_t) ((MG_U8P(p)[0] << 8U) | MG_U8P(p)[1])) -#define TLS_HDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes len +#define MG_LOAD_BE24(p) \ + ((uint32_t) ((MG_U8P(p)[0] << 16U) | (MG_U8P(p)[1] << 8U) | MG_U8P(p)[2])) +#define MG_STORE_BE16(p, n) \ + do { \ + MG_U8P(p)[0] = ((n) >> 8U) & 255; \ + MG_U8P(p)[1] = (n) & 255; \ + } while (0) + +#define TLS_RECHDR_SIZE 5 // 1 byte type, 2 bytes version, 2 bytes length +#define TLS_MSGHDR_SIZE 4 // 1 byte type, 3 bytes length + +#if 1 +static void mg_ssl_key_log(const char *label, uint8_t client_random[32], + uint8_t *secret, size_t secretsz) { + (void) label; + (void) client_random; + (void) secret; + (void) secretsz; +} +#else +#include +static void mg_ssl_key_log(const char *label, uint8_t client_random[32], + uint8_t *secret, size_t secretsz) { + char *keylogfile = getenv("SSLKEYLOGFILE"); + if (keylogfile == NULL) { + return; + } + FILE *f = fopen(keylogfile, "a"); + fprintf(f, "%s ", label); + for (int i = 0; i < 32; i++) { + fprintf(f, "%02x", client_random[i]); + } + fprintf(f, " "); + for (unsigned int i = 0; i < secretsz; i++) { + fprintf(f, "%02x", secret[i]); + } + fprintf(f, "\n"); + fclose(f); +} +#endif // for derived tls keys we need SHA256([0]*32) static uint8_t zeros[32] = {0}; @@ -48,248 +125,96 @@ static uint8_t zeros_sha256_digest[32] = "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24" "\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55"; -#define X25519_BYTES 32 -const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9}; - -#define X25519_WBITS 32 - -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -typedef int64_t sdlimb_t; -#define LIMB(x) (uint32_t)(x##ull), (uint32_t) ((x##ull) >> 32) - -#define NLIMBS (256 / X25519_WBITS) -typedef limb_t fe[NLIMBS]; - -static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) { - dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry; - *carry = (limb_t) (tmp >> X25519_WBITS); - return (limb_t) tmp; -} - -// These functions are implemented in terms of umaal on ARM -static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) { - dlimb_t total = (dlimb_t) *carry + acc + mand; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -static limb_t adc0(limb_t *carry, limb_t acc) { - dlimb_t total = (dlimb_t) *carry + acc; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; +// helper to hexdump buffers inline +static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) { + char p[8 * 4096]; + MG_VERBOSE(("%s: %s", msg, mg_hex(buf, bufsz, p))); } -// - Precondition: carry is small. -// - Invariant: result of propagate is < 2^255 + 1 word -// - In particular, always less than 2p. -// - Also, output x >= min(x,19) -static void propagate(fe x, limb_t over) { - unsigned i; - limb_t carry; - over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1; - x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1)); +// helper utilities to parse ASN.1 DER +struct mg_der_tlv { + uint8_t type; + uint32_t len; + uint8_t *value; +}; - carry = over * 19; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry, x[i]); +// parse DER into a TLV record +static int mg_der_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { + if (dersz < 2) { + return -1; } -} - -static void add(fe out, const fe a, const fe b) { - unsigned i; - limb_t carry = 0; - for (i = 0; i < NLIMBS; i++) { - out[i] = adc(&carry, a[i], b[i]); + tlv->type = der[0]; + tlv->len = der[1]; + tlv->value = der + 2; + if (tlv->len > 0x7f) { + int i; + int n = tlv->len - 0x80; + tlv->len = 0; + for (i = 0; i < n; i++) { + tlv->len = (tlv->len << 8) | (der[2 + i]); + } + tlv->value = der + 2 + n; + } + if (der + dersz < tlv->value + tlv->len) { + return -1; } - propagate(out, carry); + return 0; } -static void sub(fe out, const fe a, const fe b) { - unsigned i; - sdlimb_t carry = -38; - for (i = 0; i < NLIMBS; i++) { - carry = carry + a[i] - b[i]; - out[i] = (limb_t) carry; - carry >>= X25519_WBITS; +static int mg_der_find(uint8_t *der, size_t dersz, uint8_t *oid, size_t oidsz, + struct mg_der_tlv *tlv) { + uint8_t *p, *end; + struct mg_der_tlv child; + if (mg_der_tlv(der, dersz, tlv) < 0) { + return -1; // invalid DER + } else if (tlv->type == 6) { // found OID, check value + return (tlv->len == oidsz && memcmp(tlv->value, oid, oidsz) == 0); + } else if ((tlv->type & 0x20) == 0) { + return 0; // Primitive, but not OID: not found } - propagate(out, (limb_t) (1 + carry)); + // Constructed object: scan children + p = tlv->value; + end = tlv->value + tlv->len; + while (end > p) { + int r; + mg_der_tlv(p, end - p, &child); + r = mg_der_find(p, end - p, oid, oidsz, tlv); + if (r < 0) return -1; // error + if (r > 0) return 1; // found OID! + p = child.value + child.len; + } + return 0; // not found } -// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `fe` -// to avoid build warnings -static void mul(fe out, const fe a, const limb_t *b, unsigned nb) { - limb_t accum[2 * NLIMBS] = {0}; - unsigned i, j; - - limb_t carry2; - for (i = 0; i < nb; i++) { - limb_t mand = b[i]; - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - accum[i + j] = umaal(&carry2, accum[i + j], mand, a[j]); - } - accum[i + j] = carry2; - } - - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]); - } - propagate(out, carry2); -} - -static void sqr(fe out, const fe a) { - mul(out, a, a, NLIMBS); -} -static void mul1(fe out, const fe a) { - mul(out, a, out, NLIMBS); -} -static void sqr1(fe a) { - mul1(a, a); -} - -static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS], - limb_t doswap) { - unsigned i; - for (i = 0; i < 2 * NLIMBS; i++) { - limb_t xor = (a[i] ^ b[i]) & doswap; - a[i] ^= xor; - b[i] ^= xor; - } -} - -// Canonicalize a field element x, reducing it to the least residue which is -// congruent to it mod 2^255-19 -// - Precondition: x < 2^255 + 1 word -static limb_t canon(fe x) { - // First, add 19. - unsigned i; - limb_t carry0 = 19; - limb_t res; - sdlimb_t carry; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry0, x[i]); - } - propagate(x, carry0); - - // Here, 19 <= x2 < 2^255 - // - This is because we added 19, so before propagate it can't be less - // than 19. After propagate, it still can't be less than 19, because if - // propagate does anything it adds 19. - // - We know that the high bit must be clear, because either the input was ~ - // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or - // it was < 2^255. So now, if we subtract 19, we will get back to something in - // [0,2^255-19). - carry = -19; - res = 0; - for (i = 0; i < NLIMBS; i++) { - carry += x[i]; - res |= x[i] = (limb_t) carry; - carry >>= X25519_WBITS; - } - return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS); -} - -static const limb_t a24[1] = {121665}; - -static void ladder_part1(fe xs[5]) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - add(t1, x2, z2); // t1 = A - sub(z2, x2, z2); // z2 = B - add(x2, x3, z3); // x2 = C - sub(z3, x3, z3); // z3 = D - mul1(z3, t1); // z3 = DA - mul1(x2, z2); // x3 = BC - add(x3, z3, x2); // x3 = DA+CB - sub(z3, z3, x2); // z3 = DA-CB - sqr1(t1); // t1 = AA - sqr1(z2); // z2 = BB - sub(x2, t1, z2); // x2 = E = AA-BB - mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24 - add(z2, z2, t1); // z2 = E*a24 + AA -} - -static void ladder_part2(fe xs[5], const fe x1) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - sqr1(z3); // z3 = (DA-CB)^2 - mul1(z3, x1); // z3 = x1 * (DA-CB)^2 - sqr1(x3); // x3 = (DA+CB)^2 - mul1(z2, x2); // z2 = AA*(E*a24+AA) - sub(x2, t1, x2); // x2 = BB again - mul1(x2, t1); // x2 = AA*BB -} - -static void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], - const uint8_t *x1, int clamp) { - int i; - limb_t swap = 0; - limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3]; - memset(xs, 0, 4 * sizeof(fe)); - x2[0] = z3[0] = 1; - memcpy(x3, x1, sizeof(fe)); - - for (i = 255; i >= 0; i--) { - uint8_t bytei = scalar[i / 8]; - limb_t doswap; - if (clamp) { - if (i / 8 == 0) { - bytei &= (uint8_t) ~7U; - } else if (i / 8 == X25519_BYTES - 1) { - bytei &= 0x7F; - bytei |= 0x40; - } - } - doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1); - condswap(x2, x3, swap ^ doswap); - swap = doswap; - - ladder_part1(xs); - ladder_part2(xs, (const limb_t *) x1); - } - condswap(x2, x3, swap); -} - -static int x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp) { - int i, ret; - fe xs[5]; - limb_t *x2, *z2, *z3, *prev; - static const struct { - uint8_t a, c, n; - } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1}, - {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125}, - {3, 1, 2}, {3, 1, 2}, {3, 1, 1}}; - x25519_core(xs, scalar, x1, clamp); - - // Precomputed inversion chain - x2 = xs[0]; - z2 = xs[1]; - z3 = xs[3]; - - prev = z2; - for (i = 0; i < 13; i++) { - int j; - limb_t *a = xs[steps[i].a]; - for (j = steps[i].n; j > 0; j--) { - sqr(a, prev); - prev = a; - } - mul1(a, xs[steps[i].c]); - } +// Did we receive a full TLS record in the c->rtls buffer? +static bool mg_tls_got_record(struct mg_connection *c) { + return c->rtls.len >= (size_t) TLS_RECHDR_SIZE && + c->rtls.len >= + (size_t) (TLS_RECHDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3)); +} - // Here prev = z3 - // x2 /= z2 - mul((limb_t *) out, x2, z3, NLIMBS); - ret = (int) canon((limb_t *) out); - if (!clamp) ret = 0; - return ret; +// Remove a single TLS record from the recv buffer +static void mg_tls_drop_record(struct mg_connection *c) { + struct mg_iobuf *rio = &c->rtls; + uint16_t n = MG_LOAD_BE16(rio->buf + 3) + TLS_RECHDR_SIZE; + mg_iobuf_del(rio, 0, n); } -// helper to hexdump buffers inline -static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) { - char p[512]; - MG_VERBOSE(("%s: %s", msg, mg_hex(buf, bufsz, p))); +// Remove a single TLS message from decrypted buffer, remove the wrapping +// record if it was the last message within a record +static void mg_tls_drop_message(struct mg_connection *c) { + uint32_t len; + struct tls_data *tls = c->tls; + if (tls->recv.len == 0) { + return; + } + len = MG_LOAD_BE24(tls->recv.buf + 1); + mg_sha256_update(&tls->sha256, tls->recv.buf, len + TLS_MSGHDR_SIZE); + tls->recv.buf += len + TLS_MSGHDR_SIZE; + tls->recv.len -= len + TLS_MSGHDR_SIZE; + if (tls->recv.len == 0) { + mg_tls_drop_record(c); + } } // TLS1.3 secret derivation based on the key label @@ -309,20 +234,235 @@ static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz, memmove(hash, secret, hashsz); } -// Did we receive a full TLS message in the c->rtls buffer? -static bool mg_tls_got_msg(struct mg_connection *c) { - return c->rtls.len >= (size_t) TLS_HDR_SIZE && - c->rtls.len >= (size_t) (TLS_HDR_SIZE + MG_LOAD_BE16(c->rtls.buf + 3)); +// at this point we have x25519 shared secret, we can generate a set of derived +// handshake encryption keys +static void mg_tls_generate_handshake_keys(struct mg_connection *c) { + struct tls_data *tls = c->tls; + + mg_sha256_ctx sha256; + uint8_t early_secret[32]; + uint8_t pre_extract_secret[32]; + uint8_t hello_hash[32]; + uint8_t server_hs_secret[32]; + uint8_t client_hs_secret[32]; + + mg_hmac_sha256(early_secret, NULL, 0, zeros, sizeof(zeros)); + mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, + 32, pre_extract_secret, 32); + mg_hmac_sha256(tls->handshake_secret, pre_extract_secret, + sizeof(pre_extract_secret), tls->x25519_sec, + sizeof(tls->x25519_sec)); + mg_tls_hexdump("hs secret", tls->handshake_secret, 32); + + // mg_sha256_final is not idempotent, need to copy sha256 context to calculate + // the digest + memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); + mg_sha256_final(hello_hash, &sha256); + + mg_tls_hexdump("hello hash", hello_hash, 32); + // derive keys needed for the rest of the handshake + mg_tls_derive_secret("tls13 s hs traffic", tls->handshake_secret, 32, + hello_hash, 32, server_hs_secret, 32); + mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0, + tls->server_write_key, 16); + mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0, + tls->server_write_iv, 12); + mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0, + tls->server_finished_key, 32); + + mg_tls_derive_secret("tls13 c hs traffic", tls->handshake_secret, 32, + hello_hash, 32, client_hs_secret, 32); + mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0, + tls->client_write_key, 16); + mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0, + tls->client_write_iv, 12); + mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0, + tls->client_finished_key, 32); + + mg_tls_hexdump("s hs traffic", server_hs_secret, 32); + mg_tls_hexdump("s key", tls->server_write_key, 16); + mg_tls_hexdump("s iv", tls->server_write_iv, 12); + mg_tls_hexdump("s finished", tls->server_finished_key, 32); + mg_tls_hexdump("c hs traffic", client_hs_secret, 32); + mg_tls_hexdump("c key", tls->client_write_key, 16); + mg_tls_hexdump("c iv", tls->client_write_iv, 16); + mg_tls_hexdump("c finished", tls->client_finished_key, 32); + + mg_ssl_key_log("SERVER_HANDSHAKE_TRAFFIC_SECRET", tls->random, + server_hs_secret, 32); + mg_ssl_key_log("CLIENT_HANDSHAKE_TRAFFIC_SECRET", tls->random, + client_hs_secret, 32); } -// Remove a single TLS record from the recv buffer -static void mg_tls_drop_packet(struct mg_iobuf *rio) { - uint16_t n = MG_LOAD_BE16(rio->buf + 3) + TLS_HDR_SIZE; - mg_iobuf_del(rio, 0, n); +static void mg_tls_generate_application_keys(struct mg_connection *c) { + struct tls_data *tls = c->tls; + uint8_t hash[32]; + uint8_t premaster_secret[32]; + uint8_t master_secret[32]; + uint8_t server_secret[32]; + uint8_t client_secret[32]; + + mg_sha256_ctx sha256; + memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); + mg_sha256_final(hash, &sha256); + + mg_tls_derive_secret("tls13 derived", tls->handshake_secret, 32, + zeros_sha256_digest, 32, premaster_secret, 32); + mg_hmac_sha256(master_secret, premaster_secret, 32, zeros, 32); + + mg_tls_derive_secret("tls13 s ap traffic", master_secret, 32, hash, 32, + server_secret, 32); + mg_tls_derive_secret("tls13 key", server_secret, 32, NULL, 0, + tls->server_write_key, 16); + mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0, + tls->server_write_iv, 12); + mg_tls_derive_secret("tls13 c ap traffic", master_secret, 32, hash, 32, + client_secret, 32); + mg_tls_derive_secret("tls13 key", client_secret, 32, NULL, 0, + tls->client_write_key, 16); + mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0, + tls->client_write_iv, 12); + + mg_tls_hexdump("s ap traffic", server_secret, 32); + mg_tls_hexdump("s key", tls->server_write_key, 16); + mg_tls_hexdump("s iv", tls->server_write_iv, 12); + mg_tls_hexdump("s finished", tls->server_finished_key, 32); + mg_tls_hexdump("c ap traffic", client_secret, 32); + mg_tls_hexdump("c key", tls->client_write_key, 16); + mg_tls_hexdump("c iv", tls->client_write_iv, 16); + mg_tls_hexdump("c finished", tls->client_finished_key, 32); + tls->sseq = tls->cseq = 0; + + mg_ssl_key_log("SERVER_TRAFFIC_SECRET_0", tls->random, server_secret, 32); + mg_ssl_key_log("CLIENT_TRAFFIC_SECRET_0", tls->random, client_secret, 32); +} + +// AES GCM encryption of the message + put encoded data into the write buffer +static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, + size_t msgsz, uint8_t msgtype) { + struct tls_data *tls = c->tls; + struct mg_iobuf *wio = &tls->send; + uint8_t *outmsg; + uint8_t *tag; + size_t encsz = msgsz + 16 + 1; + uint8_t hdr[5] = {MG_TLS_APP_DATA, 0x03, 0x03, (encsz >> 8) & 0xff, + encsz & 0xff}; + uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03, + (encsz >> 8) & 0xff, encsz & 0xff}; + uint8_t nonce[12]; + + gcm_initialize(); + + if (c->is_client) { + memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv)); + nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->cseq) & 255U); + } else { + memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv)); + nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->sseq) & 255U); + } + + mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); + mg_iobuf_resize(wio, wio->len + encsz); + outmsg = wio->buf + wio->len; + tag = wio->buf + wio->len + msgsz + 1; + memmove(outmsg, msg, msgsz); + outmsg[msgsz] = msgtype; + if (c->is_client) { + aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->client_write_key, + sizeof(tls->client_write_key), nonce, sizeof(nonce), + associated_data, sizeof(associated_data), tag, 16); + tls->cseq++; + } else { + aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->server_write_key, + sizeof(tls->server_write_key), nonce, sizeof(nonce), + associated_data, sizeof(associated_data), tag, 16); + tls->sseq++; + } + wio->len += encsz; +} + +// read an encrypted record, decrypt it in place +static int mg_tls_recv_record(struct mg_connection *c) { + struct tls_data *tls = c->tls; + struct mg_iobuf *rio = &c->rtls; + uint16_t msgsz; + uint8_t *msg; + uint8_t nonce[12]; + int r; + if (tls->recv.len > 0) { + return 0; /* some data from previous record is still present */ + } + for (;;) { + if (!mg_tls_got_record(c)) { + return MG_IO_WAIT; + } + if (rio->buf[0] == MG_TLS_APP_DATA) { + break; + } else if (rio->buf[0] == + MG_TLS_CHANGE_CIPHER) { // Skip ChangeCipher messages + mg_tls_drop_record(c); + } else if (rio->buf[0] == MG_TLS_ALERT) { // Skip Alerts + MG_INFO(("TLS ALERT packet received")); + mg_tls_drop_record(c); + } else { + mg_error(c, "unexpected packet"); + return -1; + } + } + + gcm_initialize(); + msgsz = MG_LOAD_BE16(rio->buf + 3); + msg = rio->buf + 5; + if (c->is_client) { + memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv)); + nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->sseq) & 255U); + aes_gcm_decrypt(msg, msg, msgsz - 16, tls->server_write_key, + sizeof(tls->server_write_key), nonce, sizeof(nonce)); + tls->sseq++; + } else { + memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv)); + nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U); + nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U); + nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U); + nonce[11] ^= (uint8_t) ((tls->cseq) & 255U); + aes_gcm_decrypt(msg, msg, msgsz - 16, tls->client_write_key, + sizeof(tls->client_write_key), nonce, sizeof(nonce)); + tls->cseq++; + } + r = msgsz - 16 - 1; + tls->content_type = msg[msgsz - 16 - 1]; + tls->recv.buf = msg; + tls->recv.size = tls->recv.len = msgsz - 16 - 1; + return r; +} + +static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, + uint8_t hash[32]) { + struct tls_data *tls = c->tls; + uint8_t sig_content[130] = { + " " + " " + "TLS 1.3, server CertificateVerify\0"}; + mg_sha256_ctx sha256; + memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); + mg_sha256_final(sig_content + 98, &sha256); + + mg_sha256_init(&sha256); + mg_sha256_update(&sha256, sig_content, sizeof(sig_content)); + mg_sha256_final(hash, &sha256); } // read and parse ClientHello record -static int mg_tls_client_hello(struct mg_connection *c) { +static int mg_tls_server_recv_hello(struct mg_connection *c) { struct tls_data *tls = c->tls; struct mg_iobuf *rio = &c->rtls; uint8_t session_id_len; @@ -330,15 +470,20 @@ static int mg_tls_client_hello(struct mg_connection *c) { uint16_t cipher_suites_len; uint16_t ext_len; uint8_t *ext; + uint16_t msgsz; - if (!mg_tls_got_msg(c)) { + if (!mg_tls_got_record(c)) { return MG_IO_WAIT; } - if (rio->buf[0] != 0x16 || rio->buf[5] != 0x01) { - mg_error(c, "not a hello packet"); + if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) { + mg_error(c, "not a client hello packet"); return -1; } - mg_sha256_update(&tls->sha256, rio->buf + 5, rio->len - 5); + msgsz = MG_LOAD_BE16(rio->buf + 3); + mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz); + // store client random + memmove(tls->random, rio->buf + 11, sizeof(tls->random)); + // store session_id session_id_len = rio->buf[43]; if (session_id_len == sizeof(tls->session_id)) { memmove(tls->session_id, rio->buf + 44, session_id_len); @@ -364,7 +509,7 @@ static int mg_tls_client_hello(struct mg_connection *c) { uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) { memmove(tls->x25519_cli, key_exchange + k + 4, m); - mg_tls_drop_packet(rio); + mg_tls_drop_record(c); return 0; } k += (uint16_t) (m + 4); @@ -376,7 +521,7 @@ static int mg_tls_client_hello(struct mg_connection *c) { } // put ServerHello record into wio buffer -static void mg_tls_server_hello(struct mg_connection *c) { +static void mg_tls_server_send_hello(struct mg_connection *c) { struct tls_data *tls = c->tls; struct mg_iobuf *wio = &tls->send; @@ -384,12 +529,10 @@ static void mg_tls_server_hello(struct mg_connection *c) { // server hello, tls 1.2 "\x02\x00\x00\x76\x03\x03" // random (32 bytes) - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" - "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // session ID length + session ID (32 bytes) "\x20" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" #if defined(CHACHA20) && CHACHA20 // TLS_CHACHA20_POLY1305_SHA256 + no compression "\x13\x03\x00" @@ -400,20 +543,20 @@ static void mg_tls_server_hello(struct mg_connection *c) { // extensions + keyshare "\x00\x2e\x00\x33\x00\x24\x00\x1d\x00\x20" // x25519 keyshare - "\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab" - "\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab\xab" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // supported versions (tls1.3 == 0x304) "\x00\x2b\x00\x02\x03\x04"; // calculate keyshare uint8_t x25519_pub[X25519_BYTES]; uint8_t x25519_prv[X25519_BYTES]; - mg_random(x25519_prv, sizeof(x25519_prv)); - x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1); - x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1); - mg_tls_hexdump("x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); + mg_tls_random(x25519_prv, sizeof(x25519_prv)); + mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1); + mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1); + mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); - // fill in the gaps: session ID + keyshare + // fill in the gaps: random + session ID + keyshare + memmove(msg_server_hello + 6, tls->random, sizeof(tls->random)); memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id)); memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub)); @@ -426,138 +569,15 @@ static void mg_tls_server_hello(struct mg_connection *c) { mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6); } -// at this point we have x25519 shared secret, we can generate a set of derived -// handshake encryption keys -static void mg_tls_generate_handshake_keys(struct mg_connection *c) { - struct tls_data *tls = c->tls; - - mg_sha256_ctx sha256; - uint8_t early_secret[32]; - uint8_t pre_extract_secret[32]; - uint8_t hello_hash[32]; - uint8_t server_hs_secret[32]; - uint8_t client_hs_secret[32]; - - mg_hmac_sha256(early_secret, NULL, 0, zeros, sizeof(zeros)); - mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, - 32, pre_extract_secret, 32); - mg_hmac_sha256(tls->handshake_secret, pre_extract_secret, - sizeof(pre_extract_secret), tls->x25519_sec, - sizeof(tls->x25519_sec)); - mg_tls_hexdump("hs secret", tls->handshake_secret, 32); - - // mg_sha256_final is not idempotent, need to copy sha256 context to calculate - // the digest - memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); - mg_sha256_final(hello_hash, &sha256); - - // derive keys needed for the rest of the handshake - mg_tls_derive_secret("tls13 s hs traffic", tls->handshake_secret, 32, - hello_hash, 32, server_hs_secret, 32); - mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0, - tls->server_write_key, 16); - mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0, - tls->server_write_iv, 12); - mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0, - tls->server_finished_key, 32); - mg_tls_hexdump("s hs traffic", server_hs_secret, 32); - - mg_tls_derive_secret("tls13 c hs traffic", tls->handshake_secret, 32, - hello_hash, 32, client_hs_secret, 32); - mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0, - tls->client_write_key, 16); - mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0, - tls->client_write_iv, 12); - mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0, - tls->client_finished_key, 32); -} - -// AES GCM encryption of the message + put encoded data into the write buffer -static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, - size_t msgsz, uint8_t msgtype) { - struct tls_data *tls = c->tls; - struct mg_iobuf *wio = &tls->send; - uint8_t *outmsg; - uint8_t *tag; - size_t encsz = msgsz + 16 + 1; - uint8_t hdr[5] = {0x17, 0x03, 0x03, (encsz >> 8) & 0xff, encsz & 0xff}; - uint8_t associated_data[5] = {0x17, 0x03, 0x03, (encsz >> 8) & 0xff, - encsz & 0xff}; - uint8_t nonce[12]; - memmove(nonce, tls->server_write_iv, sizeof(tls->server_write_iv)); - nonce[8] ^= (uint8_t) ((tls->sseq >> 24) & 255U); - nonce[9] ^= (uint8_t) ((tls->sseq >> 16) & 255U); - nonce[10] ^= (uint8_t) ((tls->sseq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((tls->sseq) & 255U); - - gcm_initialize(); - mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)); - mg_iobuf_resize(wio, wio->len + encsz); - outmsg = wio->buf + wio->len; - tag = wio->buf + wio->len + msgsz + 1; - memmove(outmsg, msg, msgsz); - outmsg[msgsz] = msgtype; - aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, tls->server_write_key, - sizeof(tls->server_write_key), nonce, sizeof(nonce), - associated_data, sizeof(associated_data), tag, 16); - wio->len += encsz; - tls->sseq++; -} - -// read an encrypted message, decrypt it into read buffer (AES GCM) -static int mg_tls_recv_decrypt(struct mg_connection *c, void *buf, - size_t bufsz) { - struct tls_data *tls = c->tls; - struct mg_iobuf *rio = &c->rtls; - // struct mg_iobuf *rio = &tls->recv; - uint16_t msgsz; - uint8_t *msg; - uint8_t nonce[12]; - int r; - for (;;) { - if (!mg_tls_got_msg(c)) { - return MG_IO_WAIT; - } - if (rio->buf[0] == 0x17) { - break; - } else if (rio->buf[0] == 0x15) { - MG_INFO(("TLS ALERT packet received")); // TODO: drop packet? - } else { - mg_error(c, "unexpected packet"); - return -1; - } - } - msgsz = MG_LOAD_BE16(rio->buf + 3); - msg = rio->buf + 5; - memmove(nonce, tls->client_write_iv, sizeof(tls->client_write_iv)); - nonce[8] ^= (uint8_t) ((tls->cseq >> 24) & 255U); - nonce[9] ^= (uint8_t) ((tls->cseq >> 16) & 255U); - nonce[10] ^= (uint8_t) ((tls->cseq >> 8) & 255U); - nonce[11] ^= (uint8_t) ((tls->cseq) & 255U); - aes_gcm_decrypt(msg, msg, msgsz - 16, tls->client_write_key, - sizeof(tls->client_write_key), nonce, sizeof(nonce)); - r = msgsz - 16 - 1; - if (msg[r] == 0x17) { - if (bufsz > 0) { - memmove(buf, msg, msgsz - 16); - } - } else { - r = 0; - } - tls->cseq++; - mg_tls_drop_packet(rio); - return r; -} - -static void mg_tls_server_extensions(struct mg_connection *c) { +static void mg_tls_server_send_ext(struct mg_connection *c) { struct tls_data *tls = c->tls; // server extensions uint8_t ext[6] = {0x08, 0, 0, 2, 0, 0}; mg_sha256_update(&tls->sha256, ext, sizeof(ext)); - mg_tls_encrypt(c, ext, sizeof(ext), 0x16); + mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE); } -static void mg_tls_server_cert(struct mg_connection *c) { +static void mg_tls_server_send_cert(struct mg_connection *c) { struct tls_data *tls = c->tls; // server DER certificate (empty) size_t n = tls->server_cert_der.len; @@ -578,7 +598,7 @@ static void mg_tls_server_cert(struct mg_connection *c) { memmove(cert + 11, tls->server_cert_der.ptr, n); cert[11 + n] = cert[12 + n] = 0; // certificate extensions (none) mg_sha256_update(&tls->sha256, cert, 13 + n); - mg_tls_encrypt(c, cert, 13 + n, 0x16); + mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE); } // type adapter between uECC hash context and our sha256 implementation @@ -602,7 +622,7 @@ static void finish_SHA256(const uECC_HashContext *base, uint8_t *hash_result) { mg_sha256_final(hash_result, &c->ctx); } -static void mg_tls_server_verify_ecdsa(struct mg_connection *c) { +static void mg_tls_server_send_cert_verify(struct mg_connection *c) { struct tls_data *tls = c->tls; // server certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; @@ -612,17 +632,9 @@ static void mg_tls_server_verify_ecdsa(struct mg_connection *c) { {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, {{0}, 0, 0, {0}}}; int neg1, neg2; - uint8_t sig[64], sig_content[130] = { - " " - " " - "TLS 1.3, server CertificateVerify\0"}; - mg_sha256_ctx sha256; - memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); - mg_sha256_final(sig_content + 98, &sha256); + uint8_t sig[64]; - mg_sha256_init(&sha256); - mg_sha256_update(&sha256, sig_content, sizeof(sig_content)); - mg_sha256_final(hash, &sha256); + mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash); uECC_sign_deterministic(tls->server_key, hash, sizeof(hash), &ctx.uECC, sig, uECC_secp256r1()); @@ -643,13 +655,11 @@ static void mg_tls_server_verify_ecdsa(struct mg_connection *c) { verify[3] = (uint8_t) (sigsz + 4); verify[7] = (uint8_t) sigsz; - mg_tls_hexdump("verify", verify, verifysz); - mg_sha256_update(&tls->sha256, verify, verifysz); - mg_tls_encrypt(c, verify, verifysz, 0x16); + mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE); } -static void mg_tls_server_finish(struct mg_connection *c) { +static void mg_tls_server_send_finish(struct mg_connection *c) { struct tls_data *tls = c->tls; struct mg_iobuf *wio = &tls->send; mg_sha256_ctx sha256; @@ -658,112 +668,442 @@ static void mg_tls_server_finish(struct mg_connection *c) { memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); mg_sha256_final(hash, &sha256); mg_hmac_sha256(finish + 4, tls->server_finished_key, 32, hash, 32); - mg_tls_hexdump("hash", hash, sizeof(hash)); - mg_tls_hexdump("key", tls->server_finished_key, - sizeof(tls->server_finished_key)); - mg_tls_encrypt(c, finish, sizeof(finish), 0x16); + mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE); mg_io_send(c, wio->buf, wio->len); wio->len = 0; mg_sha256_update(&tls->sha256, finish, sizeof(finish)); } -static int mg_tls_client_change_cipher(struct mg_connection *c) { - // struct tls_data *tls = c->tls; +static int mg_tls_server_recv_finish(struct mg_connection *c) { + struct tls_data *tls = c->tls; + // we have to backup sha256 value to restore it later, since Finished record + // is exceptional and is not supposed to be added to the rolling hash + // calculation. + mg_sha256_ctx sha256 = tls->sha256; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_FINISHED) { + mg_error(c, "expected Finish but got msg 0x%02x", tls->recv.buf[0]); + return -1; + } + mg_tls_drop_message(c); + + // restore hash + tls->sha256 = sha256; + return 0; +} + +static void mg_tls_client_send_hello(struct mg_connection *c) { + struct tls_data *tls = c->tls; + struct mg_iobuf *wio = &tls->send; + + const char *hostname = tls->hostname; + size_t hostnamesz = strlen(tls->hostname); + uint8_t x25519_pub[X25519_BYTES]; + + uint8_t msg_client_hello[162 + 32] = + // TLS Client Hello header reported as TLS1.2 (5) + "\x16\x03\x01\x00\xfe" + // server hello, tls 1.2 (6) + "\x01\x00\x00\x8c" + "\x03\x03" + // random (32 bytes) + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + // session ID length + session ID (32 bytes) + "\x20" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + // supported algorithms +#if defined(CHACHA20) && CHACHA20 + // TLS_CHACHA20_POLY1305_SHA256 + no compression + "\x13\x03\x00" +#else + "\x00\x02" // size = 2 bytes + "\x13\x01" // TLS_AES_128_GCM_SHA256 + "\x01\x00" // no compression +#endif + + // extensions + keyshare + "\x00\xfe" /* NN */ + // x25519 keyshare + "\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + // supported groups (x25519) + "\x00\x0a\x00\x04\x00\x02\x00\x1d" + // supported versions (tls1.3 == 0x304) + "\x00\x2b\x00\x03\x02\x03\x04" + // session ticket (none) + "\x00\x23\x00\x00" + // signature algorithms (we don't care, so list all the common ones) + "\x00\x0d\x00\x24\x00\x22\x04\x03\x05\x03\x06\x03\x08\x07\x08\x08" + "\x08\x1a\x08\x1b\x08\x1c\x08\x09\x08\x0a\x08\x0b\x08\x04\x08\x05" + "\x08\x06\x04\x01\x05\x01\x06\x01" + // server name + "\x00\x00\x00\xfe" + "\x00\xfe\x00\x00\xfe"; + + // patch ClientHello with correct hostname length + offset: + MG_STORE_BE16(msg_client_hello + 3, hostnamesz + 189); + MG_STORE_BE16(msg_client_hello + 7, hostnamesz + 185); + MG_STORE_BE16(msg_client_hello + 82, hostnamesz + 110); + MG_STORE_BE16(msg_client_hello + 187, hostnamesz + 5); + MG_STORE_BE16(msg_client_hello + 189, hostnamesz + 3); + MG_STORE_BE16(msg_client_hello + 192, hostnamesz); + + // calculate keyshare + mg_tls_random(tls->x25519_cli, sizeof(tls->x25519_cli)); + mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1); + + // fill in the gaps: random + session ID + keyshare + mg_tls_random(tls->session_id, sizeof(tls->session_id)); + mg_tls_random(tls->random, sizeof(tls->random)); + memmove(msg_client_hello + 11, tls->random, sizeof(tls->random)); + memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id)); + memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub)); + + // server hello message + mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)); + mg_iobuf_add(wio, wio->len, hostname, strlen(hostname)); + mg_sha256_update(&tls->sha256, msg_client_hello + 5, + sizeof(msg_client_hello) - 5); + mg_sha256_update(&tls->sha256, (uint8_t *) hostname, strlen(hostname)); + + // change cipher message + mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6); + mg_io_send(c, wio->buf, wio->len); + wio->len = 0; +} + +static int mg_tls_client_recv_hello(struct mg_connection *c) { + struct tls_data *tls = c->tls; struct mg_iobuf *rio = &c->rtls; - for (;;) { - if (!mg_tls_got_msg(c)) { - return MG_IO_WAIT; + uint16_t msgsz; + uint8_t *ext; + uint16_t ext_len; + int j; + + if (!mg_tls_got_record(c)) { + return MG_IO_WAIT; + } + if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_SERVER_HELLO) { + if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) { + mg_error(c, "tls alert %d", rio->buf[6]); + return -1; } - if (rio->buf[0] == 0x14) { // got a ChangeCipher record - break; - } else if (rio->buf[0] == 0x15) { // skip Alert records - MG_DEBUG(("TLS ALERT packet received")); - mg_tls_drop_packet(rio); - } else { - mg_error(c, "unexpected packet"); - return MG_IO_ERR; + MG_INFO(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5])); + mg_error(c, "not a server hello packet"); + return -1; + } + + msgsz = MG_LOAD_BE16(rio->buf + 3); + mg_sha256_update(&tls->sha256, rio->buf + 5, msgsz); + + ext_len = MG_LOAD_BE16(rio->buf + 5 + 39 + 32 + 3); + ext = rio->buf + 5 + 39 + 32 + 3 + 2; + + for (j = 0; j < ext_len;) { + uint16_t ext_type = MG_LOAD_BE16(ext + j); + uint16_t ext_len = MG_LOAD_BE16(ext + j + 2); + uint16_t group; + uint8_t *key_exchange; + uint16_t key_exchange_len; + if (ext_type != 0x0033) { // not a key share extension, ignore + j += (uint16_t) (ext_len + 4); + continue; + } + group = MG_LOAD_BE16(ext + j + 4); + if (group != 0x001d) { + mg_error(c, "bad key exchange group"); + return -1; } + key_exchange_len = MG_LOAD_BE16(ext + j + 6); + key_exchange = ext + j + 8; + if (key_exchange_len != 32) { + mg_error(c, "bad key exchange length"); + return -1; + } + mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1); + mg_tls_hexdump("c x25519 sec", tls->x25519_sec, 32); + mg_tls_drop_record(c); + /* generate handshake keys */ + mg_tls_generate_handshake_keys(c); + return 0; } - // consume ChangeCipher packet - mg_tls_drop_packet(rio); + mg_error(c, "bad client hello"); + return -1; +} + +static int mg_tls_client_recv_ext(struct mg_connection *c) { + struct tls_data *tls = c->tls; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_ENCRYPTED_EXTENSIONS) { + mg_error(c, "expected server extensions but got msg 0x%02x", + tls->recv.buf[0]); + return -1; + } + mg_tls_drop_message(c); + return 0; +} + +static int mg_tls_client_recv_cert(struct mg_connection *c) { + uint8_t *cert; + uint32_t certsz; + struct mg_der_tlv oid, pubkey, seq, subj; + int subj_match = 0; + struct tls_data *tls = c->tls; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_CERTIFICATE) { + mg_error(c, "expected server certificate but got msg 0x%02x", + tls->recv.buf[0]); + return -1; + } + if (tls->skip_verification) { + mg_tls_drop_message(c); + return 0; + } + + if (tls->recv.len < 11) { + mg_error(c, "certificate list too short"); + return -1; + } + + cert = tls->recv.buf + 11; + certsz = MG_LOAD_BE24(tls->recv.buf + 8); + if (certsz > tls->recv.len - 11) { + mg_error(c, "certificate too long: %d vs %d", certsz, tls->recv.len - 11); + return -1; + } + + do { + // secp256r1 public key + if (mg_der_find(cert, certsz, + (uint8_t *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8, + &oid) < 0) { + mg_error(c, "certificate secp256r1 public key OID not found"); + return -1; + } + if (mg_der_tlv(oid.value + oid.len, cert + certsz - oid.value - oid.len, + &pubkey) < 0) { + mg_error(c, "certificate secp256r1 public key not found"); + return -1; + } + + // expect BIT STRING, unpadded, uncompressed: [0]+[4]+32+32 content bytes + if (pubkey.type != 3 || pubkey.len != 66 || pubkey.value[0] != 0 || + pubkey.value[1] != 4) { + mg_error(c, "unsupported public key bitstring encoding"); + return -1; + } + memmove(tls->pubkey, pubkey.value + 2, pubkey.len - 2); + } while (0); + + // Subject Alternative Names + do { + if (mg_der_find(cert, certsz, (uint8_t *) "\x55\x1d\x11", 3, &oid) < 0) { + mg_error(c, "certificate does not contain subject alternative names"); + return -1; + } + if (mg_der_tlv(oid.value + oid.len, cert + certsz - oid.value - oid.len, + &seq) < 0) { + mg_error(c, "certificate subject alternative names not found"); + return -1; + } + if (mg_der_tlv(seq.value, seq.len, &seq) < 0) { + mg_error( + c, + "certificate subject alternative names is not a constructed object"); + return -1; + } + MG_VERBOSE(("verify hostname %s", tls->hostname)); + while (seq.len > 0) { + if (mg_der_tlv(seq.value, seq.len, &subj) < 0) { + mg_error(c, "bad subject alternative name"); + return -1; + } + MG_VERBOSE(("subj=%.*s", subj.len, subj.value)); + if (mg_match(mg_str((const char *) tls->hostname), + mg_str_n((const char *) subj.value, subj.len), NULL)) { + subj_match = 1; + break; + } + seq.len = seq.len - (subj.value + subj.len - seq.value); + seq.value = subj.value + subj.len; + } + if (!subj_match) { + mg_error(c, "certificate did not match the hostname"); + return -1; + } + } while (0); + + mg_tls_drop_message(c); + mg_tls_calc_cert_verify_hash(c, tls->sighash); return 0; } -static int mg_tls_client_finish(struct mg_connection *c) { - uint8_t tmp[2048]; - int n = mg_tls_recv_decrypt(c, tmp, sizeof(tmp)); - if (n < 0) { +static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { + struct tls_data *tls = c->tls; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_CERTIFICATE_VERIFY) { + mg_error(c, "expected server certificate verify but got msg 0x%02x", + tls->recv.buf[0]); return -1; } - // TODO: make sure it's a ClientFinish record + // Ignore CertificateVerify is strict checks are not required + if (tls->skip_verification) { + mg_tls_drop_message(c); + return 0; + } + + // Extract certificate signature and verify it using pubkey and sighash + do { + uint8_t sig[64]; + struct mg_der_tlv seq, a, b; + if (mg_der_tlv(tls->recv.buf + 8, tls->recv.len - 8, &seq) < 0) { + mg_error(c, "verification message is not an ASN.1 DER sequence"); + return -1; + } + if (mg_der_tlv(seq.value, seq.len, &a) < 0) { + mg_error(c, "missing first part of the signature"); + return -1; + } + if (mg_der_tlv(a.value + a.len, seq.len - a.len, &b) < 0) { + mg_error(c, "missing second part of the signature"); + return -1; + } + // Integers may be padded with zeroes + if (a.len > 32) { + a.value = a.value + (a.len - 32); + a.len = 32; + } + if (b.len > 32) { + b.value = b.value + (b.len - 32); + b.len = 32; + } + + memmove(sig, a.value, a.len); + memmove(sig + 32, b.value, b.len); + + if (uECC_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, + uECC_secp256r1()) != 1) { + mg_error(c, "failed to verify certificate"); + return -1; + } + } while (0); + + mg_tls_drop_message(c); return 0; } -static void mg_tls_generate_application_keys(struct mg_connection *c) { +static int mg_tls_client_recv_finish(struct mg_connection *c) { struct tls_data *tls = c->tls; - uint8_t hash[32]; - uint8_t premaster_secret[32]; - uint8_t master_secret[32]; - uint8_t server_secret[32]; - uint8_t client_secret[32]; + if (mg_tls_recv_record(c) < 0) { + return -1; + } + if (tls->recv.buf[0] != MG_TLS_FINISHED) { + mg_error(c, "expected server finished but got msg 0x%02x", + tls->recv.buf[0]); + return -1; + } + mg_tls_drop_message(c); + return 0; +} +static void mg_tls_client_send_finish(struct mg_connection *c) { + struct tls_data *tls = c->tls; + struct mg_iobuf *wio = &tls->send; mg_sha256_ctx sha256; + uint8_t hash[32]; + uint8_t finish[36] = {0x14, 0, 0, 32}; memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); mg_sha256_final(hash, &sha256); + mg_hmac_sha256(finish + 4, tls->client_finished_key, 32, hash, 32); + mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE); + mg_io_send(c, wio->buf, wio->len); + wio->len = 0; +} - mg_tls_derive_secret("tls13 derived", tls->handshake_secret, 32, - zeros_sha256_digest, 32, premaster_secret, 32); - mg_hmac_sha256(master_secret, premaster_secret, 32, zeros, 32); - - mg_tls_derive_secret("tls13 s ap traffic", master_secret, 32, hash, 32, - server_secret, 32); - mg_tls_derive_secret("tls13 key", server_secret, 32, NULL, 0, - tls->server_write_key, 16); - mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0, - tls->server_write_iv, 12); - mg_tls_derive_secret("tls13 c ap traffic", master_secret, 32, hash, 32, - client_secret, 32); - mg_tls_derive_secret("tls13 key", client_secret, 32, NULL, 0, - tls->client_write_key, 16); - mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0, - tls->client_write_iv, 12); - - tls->sseq = tls->cseq = 0; +static void mg_tls_client_handshake(struct mg_connection *c) { + struct tls_data *tls = c->tls; + switch (tls->state) { + case MG_TLS_STATE_CLIENT_START: + mg_tls_client_send_hello(c); + tls->state = MG_TLS_STATE_CLIENT_WAIT_SH; + case MG_TLS_STATE_CLIENT_WAIT_SH: + if (mg_tls_client_recv_hello(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_EE; + case MG_TLS_STATE_CLIENT_WAIT_EE: + if (mg_tls_client_recv_ext(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT; + case MG_TLS_STATE_CLIENT_WAIT_CERT: + if (mg_tls_client_recv_cert(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_CV; + case MG_TLS_STATE_CLIENT_WAIT_CV: + if (mg_tls_client_recv_cert_verify(c) < 0) { + break; + } + tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISHED; + case MG_TLS_STATE_CLIENT_WAIT_FINISHED: + if (mg_tls_client_recv_finish(c) < 0) { + break; + } + mg_tls_client_send_finish(c); + mg_tls_generate_application_keys(c); + tls->state = MG_TLS_STATE_CLIENT_CONNECTED; + c->is_tls_hs = 0; + break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; + } } -void mg_tls_handshake(struct mg_connection *c) { +static void mg_tls_server_handshake(struct mg_connection *c) { struct tls_data *tls = c->tls; switch (tls->state) { - case MG_TLS_HS_CLIENT_HELLO: - if (mg_tls_client_hello(c) < 0) { + case MG_TLS_STATE_SERVER_START: + if (mg_tls_server_recv_hello(c) < 0) { return; } - tls->state = MG_TLS_HS_SERVER_HELLO; - // fallthrough - case MG_TLS_HS_SERVER_HELLO: - mg_tls_server_hello(c); + mg_tls_server_send_hello(c); mg_tls_generate_handshake_keys(c); - mg_tls_server_extensions(c); - mg_tls_server_cert(c); - mg_tls_server_verify_ecdsa(c); - mg_tls_server_finish(c); - tls->state = MG_TLS_HS_CLIENT_CHANGE_CIPHER; - // fallthrough - case MG_TLS_HS_CLIENT_CHANGE_CIPHER: - if (mg_tls_client_change_cipher(c) < 0) { - return; - } - tls->state = MG_TLS_HS_CLIENT_FINISH; + mg_tls_server_send_ext(c); + mg_tls_server_send_cert(c); + mg_tls_server_send_cert_verify(c); + mg_tls_server_send_finish(c); + tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; // fallthrough - case MG_TLS_HS_CLIENT_FINISH: - if (mg_tls_client_finish(c) < 0) { + case MG_TLS_STATE_SERVER_NEGOTIATED: + if (mg_tls_server_recv_finish(c) < 0) { return; } mg_tls_generate_application_keys(c); - tls->state = MG_TLS_HS_DONE; - // fallthrough - case MG_TLS_HS_DONE: c->is_tls_hs = 0; return; + tls->state = MG_TLS_STATE_SERVER_CONNECTED; + c->is_tls_hs = 0; + return; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; + } +} + +void mg_tls_handshake(struct mg_connection *c) { + if (c->is_client) { + mg_tls_client_handshake(c); + } else { + mg_tls_server_handshake(c); } } @@ -807,27 +1147,30 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { mg_error(c, "tls oom"); return; } - // parse PEM or DER EC key - if (opts->key.ptr == NULL || - mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) < 0) { - MG_ERROR(("Failed to load EC private key")); - return; - } - if (key.len < 39) { - MG_ERROR(("EC private key too short")); - return; + + tls->state = + c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START; + + tls->skip_verification = opts->skip_verification; + tls->send.align = MG_IO_SIZE; + + c->tls = tls; + c->is_tls = c->is_tls_hs = 1; + mg_sha256_init(&tls->sha256); + + // save hostname (client extension) + if (opts->name.len > 0) { + if (opts->name.len >= sizeof(tls->hostname) - 1) { + mg_error(c, "hostname too long"); + } + strncpy((char *) tls->hostname, opts->name.ptr, sizeof(tls->hostname) - 1); + tls->hostname[opts->name.len] = 0; } - // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...] - // 30 nn 02 01 01 04 20 [key] ... - if (key.ptr[0] != 0x30 || (key.ptr[1] & 0x80) != 0) { - MG_ERROR(("EC private key: ASN.1 bad sequence")); + + if (c->is_client) { + tls->server_cert_der.ptr = NULL; return; } - if (memcmp(key.ptr + 2, "\x02\x01\x01\x04\x20", 5) != 0) { - MG_ERROR(("EC private key: ASN.1 bad data")); - } - memmove(tls->server_key, key.ptr + 7, 32); - free((void *) key.ptr); // parse PEM or DER certificate if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->server_cert_der) < @@ -836,11 +1179,33 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { return; } - // tls->send.align = tls->recv.align = MG_IO_SIZE; - tls->send.align = MG_IO_SIZE; - c->tls = tls; - c->is_tls = c->is_tls_hs = 1; - mg_sha256_init(&tls->sha256); + // parse PEM or DER EC key + if (opts->key.ptr == NULL) { + mg_error(c, "certificate provided without a private key"); + return; + } + + if (mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) == 0) { + if (key.len < 39) { + MG_ERROR(("EC private key too short")); + return; + } + // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...] + // 30 nn 02 01 01 04 20 [key] ... + if (key.ptr[0] != 0x30 || (key.ptr[1] & 0x80) != 0) { + MG_ERROR(("EC private key: ASN.1 bad sequence")); + return; + } + if (memcmp(key.ptr + 2, "\x02\x01\x01\x04\x20", 5) != 0) { + MG_ERROR(("EC private key: ASN.1 bad data")); + } + memmove(tls->server_key, key.ptr + 7, 32); + free((void *) key.ptr); + } else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) { + mg_error(c, "PKCS8 private key format is not supported"); + } else { + mg_error(c, "expected EC PRIVATE KEY or PRIVATE KEY"); + } } void mg_tls_free(struct mg_connection *c) { @@ -857,7 +1222,7 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { struct tls_data *tls = c->tls; long n = MG_IO_WAIT; if (len > MG_IO_SIZE) len = MG_IO_SIZE; - mg_tls_encrypt(c, buf, len, 0x17); + mg_tls_encrypt(c, buf, len, MG_TLS_APP_DATA); while (tls->send.len > 0 && (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { mg_iobuf_del(&tls->send, 0, (size_t) n); @@ -867,11 +1232,31 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { } long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { - return mg_tls_recv_decrypt(c, buf, len); + int r = 0; + struct tls_data *tls = c->tls; + size_t minlen; + + r = mg_tls_recv_record(c); + if (r < 0) { + return r; + } + if (tls->content_type != MG_TLS_APP_DATA) { + tls->recv.len = 0; + mg_tls_drop_record(c); + return MG_IO_WAIT; + } + minlen = len < tls->recv.len ? len : tls->recv.len; + memmove(buf, tls->recv.buf, minlen); + tls->recv.buf += minlen; + tls->recv.len -= minlen; + if (tls->recv.len == 0) { + mg_tls_drop_record(c); + } + return minlen; } size_t mg_tls_pending(struct mg_connection *c) { - return mg_tls_got_msg(c) ? 1 : 0; + return mg_tls_got_record(c) ? 1 : 0; } void mg_tls_ctx_init(struct mg_mgr *mgr) { diff --git a/src/tls_x25519.c b/src/tls_x25519.c new file mode 100644 index 00000000000..c260bd19825 --- /dev/null +++ b/src/tls_x25519.c @@ -0,0 +1,246 @@ +/** + * Adapted from STROBE: https://strobe.sourceforge.io/ + * Copyright (c) 2015-2016 Cryptography Research, Inc. + * Author: Mike Hamburg + * License: MIT License + */ +#include "tls_x25519.h" + +const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9}; + +#define X25519_WBITS 32 + +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +typedef int64_t sdlimb_t; +#define LIMB(x) (uint32_t)(x##ull), (uint32_t) ((x##ull) >> 32) + +#define NLIMBS (256 / X25519_WBITS) +typedef limb_t fe[NLIMBS]; + +static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) { + dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry; + *carry = (limb_t) (tmp >> X25519_WBITS); + return (limb_t) tmp; +} + +// These functions are implemented in terms of umaal on ARM +static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) { + dlimb_t total = (dlimb_t) *carry + acc + mand; + *carry = (limb_t) (total >> X25519_WBITS); + return (limb_t) total; +} + +static limb_t adc0(limb_t *carry, limb_t acc) { + dlimb_t total = (dlimb_t) *carry + acc; + *carry = (limb_t) (total >> X25519_WBITS); + return (limb_t) total; +} + +// - Precondition: carry is small. +// - Invariant: result of propagate is < 2^255 + 1 word +// - In particular, always less than 2p. +// - Also, output x >= min(x,19) +static void propagate(fe x, limb_t over) { + unsigned i; + limb_t carry; + over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1; + x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1)); + + carry = over * 19; + for (i = 0; i < NLIMBS; i++) { + x[i] = adc0(&carry, x[i]); + } +} + +static void add(fe out, const fe a, const fe b) { + unsigned i; + limb_t carry = 0; + for (i = 0; i < NLIMBS; i++) { + out[i] = adc(&carry, a[i], b[i]); + } + propagate(out, carry); +} + +static void sub(fe out, const fe a, const fe b) { + unsigned i; + sdlimb_t carry = -38; + for (i = 0; i < NLIMBS; i++) { + carry = carry + a[i] - b[i]; + out[i] = (limb_t) carry; + carry >>= X25519_WBITS; + } + propagate(out, (limb_t) (1 + carry)); +} + +// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `fe` +// to avoid build warnings +static void mul(fe out, const fe a, const limb_t *b, unsigned nb) { + limb_t accum[2 * NLIMBS] = {0}; + unsigned i, j; + + limb_t carry2; + for (i = 0; i < nb; i++) { + limb_t mand = b[i]; + carry2 = 0; + for (j = 0; j < NLIMBS; j++) { + limb_t tmp; // "a" may be misaligned + memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy + accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp); + } + accum[i + j] = carry2; + } + + carry2 = 0; + for (j = 0; j < NLIMBS; j++) { + out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]); + } + propagate(out, carry2); +} + +static void sqr(fe out, const fe a) { + mul(out, a, a, NLIMBS); +} +static void mul1(fe out, const fe a) { + mul(out, a, out, NLIMBS); +} +static void sqr1(fe a) { + mul1(a, a); +} + +static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS], + limb_t doswap) { + unsigned i; + for (i = 0; i < 2 * NLIMBS; i++) { + limb_t xor = (a[i] ^ b[i]) & doswap; + a[i] ^= xor; + b[i] ^= xor; + } +} + +// Canonicalize a field element x, reducing it to the least residue which is +// congruent to it mod 2^255-19 +// - Precondition: x < 2^255 + 1 word +static limb_t canon(fe x) { + // First, add 19. + unsigned i; + limb_t carry0 = 19; + limb_t res; + sdlimb_t carry; + for (i = 0; i < NLIMBS; i++) { + x[i] = adc0(&carry0, x[i]); + } + propagate(x, carry0); + + // Here, 19 <= x2 < 2^255 + // - This is because we added 19, so before propagate it can't be less + // than 19. After propagate, it still can't be less than 19, because if + // propagate does anything it adds 19. + // - We know that the high bit must be clear, because either the input was ~ + // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or + // it was < 2^255. So now, if we subtract 19, we will get back to something in + // [0,2^255-19). + carry = -19; + res = 0; + for (i = 0; i < NLIMBS; i++) { + carry += x[i]; + res |= x[i] = (limb_t) carry; + carry >>= X25519_WBITS; + } + return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS); +} + +static const limb_t a24[1] = {121665}; + +static void ladder_part1(fe xs[5]) { + limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; + add(t1, x2, z2); // t1 = A + sub(z2, x2, z2); // z2 = B + add(x2, x3, z3); // x2 = C + sub(z3, x3, z3); // z3 = D + mul1(z3, t1); // z3 = DA + mul1(x2, z2); // x3 = BC + add(x3, z3, x2); // x3 = DA+CB + sub(z3, z3, x2); // z3 = DA-CB + sqr1(t1); // t1 = AA + sqr1(z2); // z2 = BB + sub(x2, t1, z2); // x2 = E = AA-BB + mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24 + add(z2, z2, t1); // z2 = E*a24 + AA +} + +static void ladder_part2(fe xs[5], const fe x1) { + limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; + sqr1(z3); // z3 = (DA-CB)^2 + mul1(z3, x1); // z3 = x1 * (DA-CB)^2 + sqr1(x3); // x3 = (DA+CB)^2 + mul1(z2, x2); // z2 = AA*(E*a24+AA) + sub(x2, t1, x2); // x2 = BB again + mul1(x2, t1); // x2 = AA*BB +} + +static void x25519_core(fe xs[5], const uint8_t scalar[X25519_BYTES], + const uint8_t *x1, int clamp) { + int i; + limb_t swap = 0; + limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3]; + memset(xs, 0, 4 * sizeof(fe)); + x2[0] = z3[0] = 1; + memcpy(x3, x1, sizeof(fe)); + + for (i = 255; i >= 0; i--) { + uint8_t bytei = scalar[i / 8]; + limb_t doswap; + if (clamp) { + if (i / 8 == 0) { + bytei &= (uint8_t) ~7U; + } else if (i / 8 == X25519_BYTES - 1) { + bytei &= 0x7F; + bytei |= 0x40; + } + } + doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1); + condswap(x2, x3, swap ^ doswap); + swap = doswap; + + ladder_part1(xs); + ladder_part2(xs, (const limb_t *) x1); + } + condswap(x2, x3, swap); +} + +int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], + const uint8_t x1[X25519_BYTES], int clamp) { + int i, ret; + fe xs[5]; + limb_t *x2, *z2, *z3, *prev; + static const struct { + uint8_t a, c, n; + } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1}, + {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125}, + {3, 1, 2}, {3, 1, 2}, {3, 1, 1}}; + x25519_core(xs, scalar, x1, clamp); + + // Precomputed inversion chain + x2 = xs[0]; + z2 = xs[1]; + z3 = xs[3]; + + prev = z2; + for (i = 0; i < 13; i++) { + int j; + limb_t *a = xs[steps[i].a]; + for (j = steps[i].n; j > 0; j--) { + sqr(a, prev); + prev = a; + } + mul1(a, xs[steps[i].c]); + } + + // Here prev = z3 + // x2 /= z2 + mul((limb_t *) out, x2, z3, NLIMBS); + ret = (int) canon((limb_t *) out); + if (!clamp) ret = 0; + return ret; +} diff --git a/src/tls_x25519.h b/src/tls_x25519.h new file mode 100644 index 00000000000..8addbb34271 --- /dev/null +++ b/src/tls_x25519.h @@ -0,0 +1,13 @@ +#ifndef TLS_X15519_H +#define TLS_X15519_H + +#include "arch.h" + +#define X25519_BYTES 32 +extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; + +int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], + const uint8_t x1[X25519_BYTES], int clamp); + + +#endif /* TLS_X15519_H */ diff --git a/test/unit_test.c b/test/unit_test.c index 03de2a87c58..00da7ad2980 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -17,6 +17,8 @@ static int s_num_tests = 0; #define FETCH_BUF_SIZE (256 * 1024) // Self-signed CA, CERT, KEY +// openssl ecparam -noout -name prime256v1 -genkey > ec_key.pem +// openssl req -x509 -new -key ec_key.pem -nodes -days 3650 -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost" -out ec_cert.pem const char *s_tls_ca = "-----BEGIN CERTIFICATE-----\n" "MIIBqjCCAU+gAwIBAgIUESoOPGqMhf9uarzblVFwzrQweMcwCgYIKoZIzj0EAwIw\n" @@ -32,23 +34,23 @@ const char *s_tls_ca = const char *s_tls_cert = "-----BEGIN CERTIFICATE-----\n" - "MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw\n" - "RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\n" - "YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx\n" - "NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" - "QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW\n" - "H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw\n" - "EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ\n" - "QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50\n" - "BllCI0eYQ9ggp/o=\n" + "MIIBlDCCATmgAwIBAgIUX6hjF41BqPDrd8cAtxfb6FdH8eUwCgYIKoZIzj0EAwIw\n" + "FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDMxNjIwMzM1MVoXDTM0MDMxNDIw\n" + "MzM1MVowFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\n" + "AQcDQgAEIszFeWhW0isH2B6CB6wQ1YEy4kotjNIrlDnsYa1jPn2yGN6ri1PXpwra\n" + "R5wNO551J5d2M6P7jF9yiCtuQDuELaNpMGcwHQYDVR0OBBYEFP2Oz5uZhcpN5Z1S\n" + "AxAcOk3MbLgEMB8GA1UdIwQYMBaAFP2Oz5uZhcpN5Z1SAxAcOk3MbLgEMA8GA1Ud\n" + "EwEB/wQFMAMBAf8wFAYDVR0RBA0wC4IJbG9jYWxob3N0MAoGCCqGSM49BAMCA0kA\n" + "MEYCIQDiqi4g9hED/DyCI5b5Bz3CsZ1zBNrRsmFRNiNCmicqbgIhAOCiOYI9j5Hz\n" + "e5srHXm/0xfIuk/cbreive0ifatPmQAm\n" "-----END CERTIFICATE-----\n"; const char *s_tls_key = - "-----BEGIN PRIVATE KEY-----\n" - "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w\n" - "kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70\n" - "wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo\n" - "-----END PRIVATE KEY-----\n"; + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIMgrmi6ktYU9tWXhtndr++vOFLdxQ7Ag16mQx6AzwTYToAoGCCqGSM49\n" + "AwEHoUQDQgAEIszFeWhW0isH2B6CB6wQ1YEy4kotjNIrlDnsYa1jPn2yGN6ri1PX\n" + "pwraR5wNO551J5d2M6P7jF9yiCtuQDuELQ==\n" + "-----END EC PRIVATE KEY-----\n"; // Important: we use different port numbers for the Windows bug workaround. See // https://support.microsoft.com/en-ae/help/3039044/error-10013-wsaeacces-is-returned-when-a-second-bind-to-a-excluded-por @@ -743,6 +745,7 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url, if (c != NULL && mg_url_is_ssl(url)) { struct mg_tls_opts opts; memset(&opts, 0, sizeof(opts)); // read CA from packed_fs + opts.name = mg_url_host(url); opts.ca = mg_unpacked("/test/data/ca.pem"); if (strstr(url, "127.0.0.1") != NULL) { // Local connection, use self-signed certificates @@ -1207,7 +1210,7 @@ static void test_tls(void) { #if MG_TLS struct mg_mgr mgr; struct mg_connection *c; - const char *url = "https://127.0.0.1:12347"; + const char *url = "https://localhost:12347"; char buf[FETCH_BUF_SIZE]; struct mg_tls_opts opts; struct mg_str data = mg_unpacked("/Makefile"); @@ -1334,6 +1337,7 @@ static void test_host_validation(void) { c = mg_http_connect(&mgr, url, f3, &ok); ASSERT(c != NULL); opts.ca = mg_unpacked("/test/data/ca.pem"); + opts.name = mg_url_host(url); mg_tls_init(c, &opts); for (i = 0; i < 1500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10); ASSERT(ok == 200);