Files
mongoose/src/tls_builtin.c
T
2024-05-09 14:10:27 -03:00

1390 lines
43 KiB
C

#include "tls.h"
#include "tls_aes128.h"
#include "tls_x25519.h"
#if MG_TLS == MG_TLS_BUILTIN
/* 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 {
// 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 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];
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 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 <stdio.h>
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};
static uint8_t zeros_sha256_digest[32] = {
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
// helper to hexdump buffers inline
static void mg_tls_hexdump(const char *msg, uint8_t *buf, size_t bufsz) {
MG_VERBOSE(("%s: %M", msg, mg_print_hex, bufsz, buf));
}
// helper utilities to parse ASN.1 DER
struct mg_der_tlv {
uint8_t type;
uint32_t len;
uint8_t *value;
};
// parse DER into a TLV record
static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
if (dersz < 2) {
return -1;
}
tlv->type = der[0];
tlv->len = der[1];
tlv->value = der + 2;
if (tlv->len > 0x7f) {
uint32_t i, 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;
}
return 0;
}
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 = {0, 0, NULL};
if (mg_der_to_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
}
// Constructed object: scan children
p = tlv->value;
end = tlv->value + tlv->len;
while (end > p) {
int r;
mg_der_to_tlv(p, (size_t) (end - p), &child);
r = mg_der_find(p, (size_t) (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
}
// 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));
}
// 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);
}
// 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 = (struct tls_data *) 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
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
if (labelsz > 0) memmove(packed + 3, label, labelsz);
packed[3 + labelsz] = (uint8_t) datasz;
if (datasz > 0) 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);
}
// 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 = (struct tls_data *) 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);
}
static void mg_tls_generate_application_keys(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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 = (struct tls_data *) 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,
(uint8_t) ((encsz >> 8) & 0xff), (uint8_t) (encsz & 0xff)};
uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03,
(uint8_t) ((encsz >> 8) & 0xff),
(uint8_t) (encsz & 0xff)};
uint8_t nonce[12];
mg_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) {
mg_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 {
mg_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 = (struct tls_data *) 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;
}
}
mg_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);
mg_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);
mg_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 = (struct tls_data *) 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_server_recv_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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;
uint16_t msgsz;
if (!mg_tls_got_record(c)) {
return MG_IO_WAIT;
}
if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) {
mg_error(c, "not a client hello packet");
return -1;
}
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);
} 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_record(c);
return 0;
}
k += (uint16_t) (m + 4);
}
j += (uint16_t) (n + 4);
}
mg_error(c, "bad client hello");
return -1;
}
#define PLACEHOLDER_8B 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'
#define PLACEHOLDER_16B PLACEHOLDER_8B, PLACEHOLDER_8B
#define PLACEHOLDER_32B PLACEHOLDER_16B, PLACEHOLDER_16B
// put ServerHello record into wio buffer
static void mg_tls_server_send_hello(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
struct mg_iobuf *wio = &tls->send;
uint8_t msg_server_hello[122] = {
// server hello, tls 1.2
0x02,
0x00,
0x00,
0x76,
0x03,
0x03,
// random (32 bytes)
PLACEHOLDER_32B,
// session ID length + session ID (32 bytes)
0x20,
PLACEHOLDER_32B,
#if defined(CHACHA20) && CHACHA20
// TLS_CHACHA20_POLY1305_SHA256 + no compression
0x13,
0x03,
0x00,
#else
// TLS_AES_128_GCM_SHA256 + no compression
0x13,
0x01,
0x00,
#endif
// extensions + keyshare
0x00,
0x2e,
0x00,
0x33,
0x00,
0x24,
0x00,
0x1d,
0x00,
0x20,
// x25519 keyshare
PLACEHOLDER_32B,
// supported versions (tls1.3 == 0x304)
0x00,
0x2b,
0x00,
0x02,
0x03,
0x04
};
// calculate keyshare
uint8_t x25519_pub[X25519_BYTES];
uint8_t x25519_prv[X25519_BYTES];
mg_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: 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));
// 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);
}
static void mg_tls_server_send_ext(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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), MG_TLS_HANDSHAKE);
}
static void mg_tls_server_send_cert(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
// server DER certificate (empty)
size_t n = tls->server_cert_der.len;
uint8_t *cert = (uint8_t *) calloc(1, 13 + n);
if (cert == NULL) {
mg_error(c, "tls cert oom");
return;
}
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.buf, 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, MG_TLS_HANDSHAKE);
free(cert);
}
// type adapter between uECC hash context and our sha256 implementation
typedef struct SHA256_HashContext {
MG_UECC_HashContext uECC;
mg_sha256_ctx ctx;
} SHA256_HashContext;
static void init_SHA256(const MG_UECC_HashContext *base) {
SHA256_HashContext *c = (SHA256_HashContext *) base;
mg_sha256_init(&c->ctx);
}
static void update_SHA256(const MG_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 MG_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_send_cert_verify(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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] = {0};
mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash);
mg_uecc_sign_deterministic(tls->server_key, hash, sizeof(hash), &ctx.uECC,
sig, mg_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_sha256_update(&tls->sha256, verify, verifysz);
mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE);
}
static void mg_tls_server_send_finish(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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_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_server_recv_finish(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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 = (struct tls_data *) 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)
0x16,
0x03,
0x01,
0x00,
0xfe,
// server hello, tls 1.2 (6)
0x01,
0x00,
0x00,
0x8c,
0x03,
0x03,
// random (32 bytes)
PLACEHOLDER_32B,
// session ID length + session ID (32 bytes)
0x20,
PLACEHOLDER_32B,
#if defined(CHACHA20) && CHACHA20
// TLS_CHACHA20_POLY1305_SHA256 + no compression
0x13,
0x03,
0x00,
#else
0x00,
0x02, // size = 2 bytes
0x13,
0x01, // TLS_AES_128_GCM_SHA256
0x01,
0x00, // no compression
#endif
// extensions + keyshare
0x00,
0xfe,
// x25519 keyshare
0x00,
0x33,
0x00,
0x26,
0x00,
0x24,
0x00,
0x1d,
0x00,
0x20,
PLACEHOLDER_32B,
// supported groups (x25519)
0x00,
0x0a,
0x00,
0x04,
0x00,
0x02,
0x00,
0x1d,
// supported versions (tls1.3 == 0x304)
0x00,
0x2b,
0x00,
0x03,
0x02,
0x03,
0x04,
// session ticket (none)
0x00,
0x23,
0x00,
0x00,
// signature algorithms (we don't care, so list all the common ones)
0x00,
0x0d,
0x00,
0x24,
0x00,
0x22,
0x04,
0x03,
0x05,
0x03,
0x06,
0x03,
0x08,
0x07,
0x08,
0x08,
0x08,
0x1a,
0x08,
0x1b,
0x08,
0x1c,
0x08,
0x09,
0x08,
0x0a,
0x08,
0x0b,
0x08,
0x04,
0x08,
0x05,
0x08,
0x06,
0x04,
0x01,
0x05,
0x01,
0x06,
0x01,
// server name
0x00,
0x00,
0x00,
0xfe,
0x00,
0xfe,
0x00,
0x00,
0xfe
};
// 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_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_random(tls->session_id, sizeof(tls->session_id));
mg_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 = (struct tls_data *) c->tls;
struct mg_iobuf *rio = &c->rtls;
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;
}
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_len2 = 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_len2 + 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;
}
mg_error(c, "bad client hello");
return -1;
}
static int mg_tls_client_recv_ext(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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 = (struct tls_data *) 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_to_tlv(oid.value + oid.len,
(size_t) (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_to_tlv(oid.value + oid.len,
(size_t) (cert + certsz - (oid.value + oid.len)),
&seq) < 0) {
mg_error(c, "certificate subject alternative names not found");
return -1;
}
if (mg_der_to_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_to_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 = (uint32_t) (seq.value + seq.len - (subj.value + subj.len));
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 = (struct tls_data *) 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;
}
// 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_to_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_to_tlv(seq.value, seq.len, &a) < 0) {
mg_error(c, "missing first part of the signature");
return -1;
}
if (mg_der_to_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 (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig,
mg_uecc_secp256r1()) != 1) {
mg_error(c, "failed to verify certificate");
return -1;
}
} while (0);
mg_tls_drop_message(c);
return 0;
}
static int mg_tls_client_recv_finish(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
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 = (struct tls_data *) 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;
}
static void mg_tls_client_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) 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;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_SH:
if (mg_tls_client_recv_hello(c) < 0) {
break;
}
tls->state = MG_TLS_STATE_CLIENT_WAIT_EE;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_EE:
if (mg_tls_client_recv_ext(c) < 0) {
break;
}
tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT;
// Fallthrough
case MG_TLS_STATE_CLIENT_WAIT_CERT:
if (mg_tls_client_recv_cert(c) < 0) {
break;
}
tls->state = MG_TLS_STATE_CLIENT_WAIT_CV;
// Fallthrough
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;
// Fallthrough
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;
}
}
static void mg_tls_server_handshake(struct mg_connection *c) {
struct tls_data *tls = (struct tls_data *) c->tls;
switch (tls->state) {
case MG_TLS_STATE_SERVER_START:
if (mg_tls_server_recv_hello(c) < 0) {
return;
}
mg_tls_server_send_hello(c);
mg_tls_generate_handshake_keys(c);
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_STATE_SERVER_NEGOTIATED:
if (mg_tls_server_recv_finish(c) < 0) {
return;
}
mg_tls_generate_application_keys(c);
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);
}
}
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->buf = mg_mprintf("%.*s", pem.len, pem.buf);
der->len = pem.len;
return 0;
}
if (mg_strcmp(caps[1], label) != 0 || mg_strcmp(caps[3], label) != 0) {
return -1; // bad label
}
if ((s = (char *) calloc(1, caps[2].len)) == NULL) {
return -1;
}
for (c = caps[2].buf; c < caps[2].buf + 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->buf = 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;
}
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.buf, sizeof(tls->hostname) - 1);
tls->hostname[opts->name.len] = 0;
}
if (c->is_client) {
tls->server_cert_der.buf = NULL;
return;
}
// 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;
}
// parse PEM or DER EC key
if (opts->key.buf == 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.buf[0] != 0x30 || (key.buf[1] & 0x80) != 0) {
MG_ERROR(("EC private key: ASN.1 bad sequence"));
return;
}
if (memcmp(key.buf + 2, "\x02\x01\x01\x04\x20", 5) != 0) {
MG_ERROR(("EC private key: ASN.1 bad data"));
}
memmove(tls->server_key, key.buf + 7, 32);
free((void *) key.buf);
} 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) {
struct tls_data *tls = (struct tls_data *) c->tls;
if (tls != NULL) {
mg_iobuf_free(&tls->send);
free((void *) tls->server_cert_der.buf);
}
free(c->tls);
c->tls = NULL;
}
long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) {
struct tls_data *tls = (struct tls_data *) c->tls;
long n = MG_IO_WAIT;
if (len > MG_IO_SIZE) len = MG_IO_SIZE;
mg_tls_encrypt(c, (const uint8_t *) 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);
}
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) {
int r = 0;
struct tls_data *tls = (struct tls_data *) 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 (long) minlen;
}
size_t mg_tls_pending(struct mg_connection *c) {
return mg_tls_got_record(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