mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-04 15:14:22 +07:00
883 lines
28 KiB
C
883 lines
28 KiB
C
#include "tls.h"
|
|
|
|
#if MG_TLS == MG_TLS_BUILTIN
|
|
|
|
// handshake is re-entrant, so we need to keep track of its state
|
|
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
|
|
};
|
|
|
|
// 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
|
|
|
|
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 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
|
|
|
|
struct mg_str server_cert_der; // server certificate in DER format
|
|
uint8_t server_key[32]; // server EC private key
|
|
|
|
// keys for AES encryption
|
|
uint8_t handshake_secret[32];
|
|
uint8_t server_write_key[16];
|
|
uint8_t server_write_iv[12];
|
|
uint8_t server_finished_key[32];
|
|
uint8_t client_write_key[16];
|
|
uint8_t client_write_iv[12];
|
|
uint8_t client_finished_key[32];
|
|
};
|
|
|
|
#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
|
|
|
|
// for derived tls keys we need SHA256([0]*32)
|
|
static uint8_t zeros[32] = {0};
|
|
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]);
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
static void mul(fe out, const fe a, const fe 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]);
|
|
}
|
|
|
|
// 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[2048];
|
|
MG_INFO(("%s: %s", msg, mg_hex(buf, bufsz, p)));
|
|
}
|
|
|
|
// TLS1.3 secret derivation based on the key label
|
|
static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz,
|
|
uint8_t *data, size_t datasz, uint8_t *hash,
|
|
size_t hashsz) {
|
|
size_t labelsz = strlen(label);
|
|
uint8_t secret[32];
|
|
uint8_t packed[256] = {0, (uint8_t) hashsz, (uint8_t) labelsz};
|
|
// TODO: assert lengths of label, key, data and hash
|
|
memmove(packed + 3, label, labelsz);
|
|
packed[3 + labelsz] = (uint8_t) datasz;
|
|
memmove(packed + labelsz + 4, data, datasz);
|
|
packed[4 + labelsz + datasz] = 1;
|
|
|
|
mg_hmac_sha256(secret, key, keysz, packed, 5 + labelsz + datasz);
|
|
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));
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// read and parse ClientHello record
|
|
static int mg_tls_client_hello(struct mg_connection *c) {
|
|
struct tls_data *tls = c->tls;
|
|
struct mg_iobuf *rio = &c->rtls;
|
|
uint8_t session_id_len;
|
|
uint16_t j;
|
|
uint16_t cipher_suites_len;
|
|
uint16_t ext_len;
|
|
uint8_t *ext;
|
|
|
|
if (!mg_tls_got_msg(c)) {
|
|
return MG_IO_WAIT;
|
|
}
|
|
if (rio->buf[0] != 0x16 || rio->buf[5] != 0x01) {
|
|
mg_error(c, "not a hello packet");
|
|
return -1;
|
|
}
|
|
mg_sha256_update(&tls->sha256, rio->buf + 5, rio->len - 5);
|
|
session_id_len = rio->buf[43];
|
|
if (session_id_len == sizeof(tls->session_id)) {
|
|
memmove(tls->session_id, rio->buf + 44, session_id_len);
|
|
} else if (session_id_len != 0) {
|
|
MG_INFO(("bad session id len"));
|
|
}
|
|
cipher_suites_len = MG_LOAD_BE16(rio->buf + 44 + session_id_len);
|
|
ext_len = MG_LOAD_BE16(rio->buf + 48 + session_id_len + cipher_suites_len);
|
|
ext = rio->buf + 50 + session_id_len + cipher_suites_len;
|
|
for (j = 0; j < ext_len;) {
|
|
uint16_t k;
|
|
uint16_t key_exchange_len;
|
|
uint8_t *key_exchange;
|
|
uint16_t n = MG_LOAD_BE16(ext + j + 2);
|
|
if (ext[j] != 0x00 ||
|
|
ext[j + 1] != 0x33) { // not a key share extension, ignore
|
|
j += (uint16_t) (n + 4);
|
|
continue;
|
|
}
|
|
key_exchange_len = MG_LOAD_BE16(ext + j + 5);
|
|
key_exchange = ext + j + 6;
|
|
for (k = 0; k < key_exchange_len;) {
|
|
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);
|
|
return 0;
|
|
}
|
|
k += (uint16_t) (m + 4);
|
|
}
|
|
j += (uint16_t) (n + 4);
|
|
}
|
|
mg_error(c, "bad client hello");
|
|
return -1;
|
|
}
|
|
|
|
// put ServerHello record into wio buffer
|
|
static void mg_tls_server_hello(struct mg_connection *c) {
|
|
struct tls_data *tls = c->tls;
|
|
struct mg_iobuf *wio = &tls->send;
|
|
|
|
uint8_t msg_server_hello[122] =
|
|
// 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"
|
|
// 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"
|
|
#if defined(CHACHA20) && CHACHA20
|
|
// TLS_CHACHA20_POLY1305_SHA256 + no compression
|
|
"\x13\x03\x00"
|
|
#else
|
|
// TLS_AES_128_GCM_SHA256 + no compression
|
|
"\x13\x01\x00"
|
|
#endif
|
|
// 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"
|
|
// 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));
|
|
|
|
// fill in the gaps: session ID + keyshare
|
|
memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id));
|
|
memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub));
|
|
|
|
// server hello message
|
|
mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5);
|
|
mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello));
|
|
mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello));
|
|
|
|
// change cipher message
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
static void mg_tls_server_cert(struct mg_connection *c) {
|
|
struct tls_data *tls = c->tls;
|
|
// server DER certificate (empty)
|
|
size_t n = tls->server_cert_der.len;
|
|
uint8_t *cert = calloc(1, 13 + n); // FIXME: free
|
|
cert[0] = 0x0b; // handshake header
|
|
cert[1] = (uint8_t) (((n + 9) >> 16) & 255U); // 3 bytes: payload length
|
|
cert[2] = (uint8_t) (((n + 9) >> 8) & 255U);
|
|
cert[3] = (uint8_t) ((n + 9) & 255U);
|
|
cert[4] = 0; // request context
|
|
cert[5] = (uint8_t) (((n + 5) >> 16) & 255U); // 3 bytes: cert (s) length
|
|
cert[6] = (uint8_t) (((n + 5) >> 8) & 255U);
|
|
cert[7] = (uint8_t) ((n + 5) & 255U);
|
|
cert[8] =
|
|
(uint8_t) (((n) >> 16) & 255U); // 3 bytes: first (and only) cert len
|
|
cert[9] = (uint8_t) (((n) >> 8) & 255U);
|
|
cert[10] = (uint8_t) (n & 255U);
|
|
// bytes 11+ are certificate in DER format
|
|
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);
|
|
}
|
|
|
|
// type adapter between uECC hash context and our sha256 implementation
|
|
typedef struct SHA256_HashContext {
|
|
uECC_HashContext uECC;
|
|
mg_sha256_ctx ctx;
|
|
} SHA256_HashContext;
|
|
|
|
static void init_SHA256(const uECC_HashContext *base) {
|
|
SHA256_HashContext *c = (SHA256_HashContext *) base;
|
|
mg_sha256_init(&c->ctx);
|
|
}
|
|
|
|
static void update_SHA256(const uECC_HashContext *base, const uint8_t *message,
|
|
unsigned message_size) {
|
|
SHA256_HashContext *c = (SHA256_HashContext *) base;
|
|
mg_sha256_update(&c->ctx, message, message_size);
|
|
}
|
|
static void finish_SHA256(const uECC_HashContext *base, uint8_t *hash_result) {
|
|
SHA256_HashContext *c = (SHA256_HashContext *) base;
|
|
mg_sha256_final(hash_result, &c->ctx);
|
|
}
|
|
|
|
static void mg_tls_server_verify_ecdsa(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};
|
|
size_t sigsz, verifysz = 0;
|
|
uint8_t hash[32] = {0}, tmp[2 * 32 + 64] = {0};
|
|
struct SHA256_HashContext ctx = {
|
|
{&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);
|
|
|
|
mg_sha256_init(&sha256);
|
|
mg_sha256_update(&sha256, sig_content, sizeof(sig_content));
|
|
mg_sha256_final(hash, &sha256);
|
|
|
|
uECC_sign_deterministic(tls->server_key, hash, sizeof(hash), &ctx.uECC, sig,
|
|
uECC_secp256r1());
|
|
|
|
neg1 = !!(sig[0] & 0x80);
|
|
neg2 = !!(sig[32] & 0x80);
|
|
verify[8] = 0x30; // ASN.1 SEQUENCE
|
|
verify[9] = (uint8_t) (68 + neg1 + neg2);
|
|
verify[10] = 0x02; // ASN.1 INTEGER
|
|
verify[11] = (uint8_t) (32 + neg1);
|
|
memmove(verify + 12 + neg1, sig, 32);
|
|
verify[12 + 32 + neg1] = 0x02; // ASN.1 INTEGER
|
|
verify[13 + 32 + neg1] = (uint8_t) (32 + neg2);
|
|
memmove(verify + 14 + 32 + neg1 + neg2, sig + 32, 32);
|
|
|
|
sigsz = (size_t) (70 + neg1 + neg2);
|
|
verifysz = 8U + sigsz;
|
|
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);
|
|
}
|
|
|
|
static void mg_tls_server_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->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_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;
|
|
struct mg_iobuf *rio = &c->rtls;
|
|
for (;;) {
|
|
if (!mg_tls_got_msg(c)) {
|
|
return MG_IO_WAIT;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
// consume ChangeCipher packet
|
|
mg_tls_drop_packet(rio);
|
|
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) {
|
|
return -1;
|
|
}
|
|
// TODO: make sure it's a ClientFinish record
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
|
|
tls->sseq = tls->cseq = 0;
|
|
}
|
|
|
|
void mg_tls_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) {
|
|
return;
|
|
}
|
|
tls->state = MG_TLS_HS_SERVER_HELLO;
|
|
// fallthrough
|
|
case MG_TLS_HS_SERVER_HELLO:
|
|
mg_tls_server_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;
|
|
// fallthrough
|
|
case MG_TLS_HS_CLIENT_FINISH:
|
|
if (mg_tls_client_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;
|
|
}
|
|
}
|
|
|
|
static int mg_parse_pem(const struct mg_str pem, const struct mg_str label,
|
|
struct mg_str *der) {
|
|
size_t n = 0, m = 0;
|
|
char *s;
|
|
const char *c;
|
|
struct mg_str caps[5];
|
|
if (!mg_match(pem, mg_str("#-----BEGIN #-----#-----END #-----#"), caps)) {
|
|
*der = mg_strdup(pem);
|
|
return 0;
|
|
}
|
|
if (mg_strcmp(caps[1], label) != 0 || mg_strcmp(caps[3], label) != 0) {
|
|
return -1; // bad label
|
|
}
|
|
if ((s = calloc(1, caps[2].len)) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for (c = caps[2].ptr; c < caps[2].ptr + caps[2].len; c++) {
|
|
if (*c == ' ' || *c == '\n' || *c == '\r' || *c == '\t') {
|
|
continue;
|
|
}
|
|
s[n++] = *c;
|
|
}
|
|
m = mg_base64_decode(s, n, s, n);
|
|
if (m == 0) {
|
|
free(s);
|
|
return -1;
|
|
}
|
|
der->ptr = s;
|
|
der->len = m;
|
|
return 0;
|
|
}
|
|
|
|
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
|
struct mg_str key;
|
|
struct tls_data *tls = (struct tls_data *) calloc(1, sizeof(struct tls_data));
|
|
if (tls == NULL) {
|
|
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;
|
|
}
|
|
// 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);
|
|
|
|
// parse PEM or DER certificate
|
|
if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->server_cert_der) <
|
|
0) {
|
|
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);
|
|
}
|
|
|
|
void mg_tls_free(struct mg_connection *c) {
|
|
struct tls_data *tls = c->tls;
|
|
if (tls != NULL) {
|
|
mg_iobuf_free(&tls->send);
|
|
free((void *) tls->server_cert_der.ptr);
|
|
}
|
|
free(c->tls);
|
|
c->tls = NULL;
|
|
}
|
|
|
|
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);
|
|
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);
|
|
}
|
|
if (n == MG_IO_ERR || n == MG_IO_WAIT) return n;
|
|
return (long) len;
|
|
}
|
|
|
|
long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) {
|
|
return mg_tls_recv_decrypt(c, buf, len);
|
|
}
|
|
|
|
size_t mg_tls_pending(struct mg_connection *c) {
|
|
return mg_tls_got_msg(c) ? 1 : 0;
|
|
}
|
|
|
|
void mg_tls_ctx_init(struct mg_mgr *mgr) {
|
|
(void) mgr;
|
|
}
|
|
|
|
void mg_tls_ctx_free(struct mg_mgr *mgr) {
|
|
(void) mgr;
|
|
}
|
|
#endif
|