#include "base64.h" #include "config.h" #include "printf.h" #include "sha256.h" #include "tls.h" #include "tls_aes128.h" #include "tls_chacha20.h" #include "tls_uecc.h" #include "tls_x25519.h" #if MG_TLS == MG_TLS_BUILTIN #define CHACHA20 1 /* 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_REQUEST 13 #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 }; // encryption keys for a TLS connection struct tls_enc { uint32_t sseq; // server sequence number, used in encryption uint32_t cseq; // client sequence number, used in decryption // keys for AES encryption or ChaCha20 uint8_t handshake_secret[32]; uint8_t server_write_key[32]; uint8_t server_write_iv[12]; uint8_t server_finished_key[32]; uint8_t client_write_key[32]; uint8_t client_write_iv[12]; uint8_t client_finished_key[32]; }; // 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 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? int cert_requested; // client received a CertificateRequest? struct mg_str cert_der; // certificate in DER format uint8_t ec_key[32]; // 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 struct tls_enc enc; }; #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 #ifdef MG_TLS_SSLKEYLOGFILE #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}; 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]; #if CHACHA20 const size_t keysz = 32; #else const size_t keysz = 16; #endif 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->enc.handshake_secret, pre_extract_secret, sizeof(pre_extract_secret), tls->x25519_sec, sizeof(tls->x25519_sec)); mg_tls_hexdump("hs secret", tls->enc.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->enc.handshake_secret, 32, hello_hash, 32, server_hs_secret, 32); mg_tls_derive_secret("tls13 c hs traffic", tls->enc.handshake_secret, 32, hello_hash, 32, client_hs_secret, 32); mg_tls_derive_secret("tls13 key", server_hs_secret, 32, NULL, 0, tls->enc.server_write_key, keysz); mg_tls_derive_secret("tls13 iv", server_hs_secret, 32, NULL, 0, tls->enc.server_write_iv, 12); mg_tls_derive_secret("tls13 finished", server_hs_secret, 32, NULL, 0, tls->enc.server_finished_key, 32); mg_tls_derive_secret("tls13 key", client_hs_secret, 32, NULL, 0, tls->enc.client_write_key, keysz); mg_tls_derive_secret("tls13 iv", client_hs_secret, 32, NULL, 0, tls->enc.client_write_iv, 12); mg_tls_derive_secret("tls13 finished", client_hs_secret, 32, NULL, 0, tls->enc.client_finished_key, 32); mg_tls_hexdump("s hs traffic", server_hs_secret, 32); mg_tls_hexdump("s key", tls->enc.server_write_key, keysz); mg_tls_hexdump("s iv", tls->enc.server_write_iv, 12); mg_tls_hexdump("s finished", tls->enc.server_finished_key, 32); mg_tls_hexdump("c hs traffic", client_hs_secret, 32); mg_tls_hexdump("c key", tls->enc.client_write_key, keysz); mg_tls_hexdump("c iv", tls->enc.client_write_iv, 12); mg_tls_hexdump("c finished", tls->enc.client_finished_key, 32); #ifdef MG_TLS_SSLKEYLOGFILE 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); #endif } 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]; #if CHACHA20 const size_t keysz = 32; #else const size_t keysz = 16; #endif mg_sha256_ctx sha256; memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); mg_sha256_final(hash, &sha256); mg_tls_derive_secret("tls13 derived", tls->enc.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->enc.server_write_key, keysz); mg_tls_derive_secret("tls13 iv", server_secret, 32, NULL, 0, tls->enc.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->enc.client_write_key, keysz); mg_tls_derive_secret("tls13 iv", client_secret, 32, NULL, 0, tls->enc.client_write_iv, 12); mg_tls_hexdump("s ap traffic", server_secret, 32); mg_tls_hexdump("s key", tls->enc.server_write_key, keysz); mg_tls_hexdump("s iv", tls->enc.server_write_iv, 12); mg_tls_hexdump("s finished", tls->enc.server_finished_key, 32); mg_tls_hexdump("c ap traffic", client_secret, 32); mg_tls_hexdump("c key", tls->enc.client_write_key, keysz); mg_tls_hexdump("c iv", tls->enc.client_write_iv, 12); mg_tls_hexdump("c finished", tls->enc.client_finished_key, 32); tls->enc.sseq = tls->enc.cseq = 0; #ifdef MG_TLS_SSLKEYLOGFILE 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); #endif } // 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]; uint32_t seq = c->is_client ? tls->enc.cseq : tls->enc.sseq; uint8_t *key = c->is_client ? tls->enc.client_write_key : tls->enc.server_write_key; uint8_t *iv = c->is_client ? tls->enc.client_write_iv : tls->enc.server_write_iv; #if !CHACHA20 mg_gcm_initialize(); #endif memmove(nonce, iv, sizeof(nonce)); nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); nonce[11] ^= (uint8_t) ((seq) & 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 CHACHA20 (void) tag; // tag is only used in aes gcm uint8_t enc[8192]; size_t n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data, sizeof(associated_data), outmsg, msgsz + 1); memmove(outmsg, enc, n); #else mg_aes_gcm_encrypt(outmsg, outmsg, msgsz + 1, key, 16, nonce, sizeof(nonce), associated_data, sizeof(associated_data), tag, 16); #endif c->is_client ? tls->enc.cseq++ : tls->enc.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; uint32_t seq = c->is_client ? tls->enc.sseq : tls->enc.cseq; uint8_t *key = c->is_client ? tls->enc.server_write_key : tls->enc.client_write_key; uint8_t *iv = c->is_client ? tls->enc.server_write_iv : tls->enc.client_write_iv; 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; } } #if !CHACHA20 mg_gcm_initialize(); #endif msgsz = MG_LOAD_BE16(rio->buf + 3); msg = rio->buf + 5; memmove(nonce, iv, sizeof(nonce)); nonce[8] ^= (uint8_t) ((seq >> 24) & 255U); nonce[9] ^= (uint8_t) ((seq >> 16) & 255U); nonce[10] ^= (uint8_t) ((seq >> 8) & 255U); nonce[11] ^= (uint8_t) ((seq) & 255U); #if CHACHA20 uint8_t dec[8192]; size_t n = mg_chacha20_poly1305_decrypt(dec, key, nonce, msg, msgsz); memmove(msg, dec, n); #else mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce)); #endif r = msgsz - 16 - 1; tls->content_type = msg[msgsz - 16 - 1]; tls->recv.buf = msg; tls->recv.size = tls->recv.len = msgsz - 16 - 1; c->is_client ? tls->enc.sseq++ : tls->enc.cseq++; return r; } static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, uint8_t hash[32], int is_client) { struct tls_data *tls = (struct tls_data *) c->tls; uint8_t server_context[34] = "TLS 1.3, server CertificateVerify"; uint8_t client_context[34] = "TLS 1.3, client CertificateVerify"; uint8_t sig_content[130]; mg_sha256_ctx sha256; memset(sig_content, 0x20, 64); if (is_client) { memmove(sig_content + 64, client_context, sizeof(client_context)); } else { memmove(sig_content + 64, server_context, sizeof(server_context)); } 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; // clang-format off 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}; // clang-format on // 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->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->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_send_cert_verify(struct mg_connection *c, int is_client) { 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, is_client); mg_uecc_sign_deterministic(tls->ec_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->enc.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; uint8_t x25519_pub[X25519_BYTES]; // the only signature algorithm we actually support uint8_t secp256r1_sig_algs[8] = { 0x00, 0x0d, 0x00, 0x04, 0x00, 0x02, 0x04, 0x03, }; // all popular signature algorithms (if we don't care about verification) uint8_t all_sig_algs[34] = { 0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01}; uint8_t server_name_ext[9] = {0x00, 0x00, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe}; // clang-format off uint8_t msg_client_hello[145] = { // TLS Client Hello header reported as TLS1.2 (5) 0x16, 0x03, 0x03, 0x00, 0xfe, // client 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, 0x00, 0x02, // size = 2 bytes #if defined(CHACHA20) && CHACHA20 // TLS_CHACHA20_POLY1305_SHA256 0x13, 0x03, #else // TLS_AES_128_GCM_SHA256 0x13, 0x01, #endif // no compression 0x01, 0x00, // 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, // 144 bytes till here }; // clang-format on const char *hostname = tls->hostname; size_t hostnamesz = strlen(tls->hostname); size_t hostname_extsz = hostnamesz ? hostnamesz + 9 : 0; uint8_t *sig_alg = tls->skip_verification ? all_sig_algs : secp256r1_sig_algs; size_t sig_alg_sz = tls->skip_verification ? sizeof(all_sig_algs) : sizeof(secp256r1_sig_algs); // patch ClientHello with correct hostname ext length (if any) MG_STORE_BE16(msg_client_hello + 3, hostname_extsz + 183 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 7, hostname_extsz + 179 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 82, hostname_extsz + 104 - 9 - 34 + sig_alg_sz); if (hostnamesz > 0) { MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5); MG_STORE_BE16(server_name_ext + 4, hostnamesz + 3); MG_STORE_BE16(server_name_ext + 7, 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)); // client hello message mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)); mg_sha256_update(&tls->sha256, msg_client_hello + 5, sizeof(msg_client_hello) - 5); mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz); mg_sha256_update(&tls->sha256, sig_alg, sig_alg_sz); if (hostnamesz > 0) { mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext)); mg_iobuf_add(wio, wio->len, hostname, hostnamesz); mg_sha256_update(&tls->sha256, server_name_ext, sizeof(server_name_ext)); mg_sha256_update(&tls->sha256, (uint8_t *) hostname, hostnamesz); } // 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_REQUEST) { MG_VERBOSE(("got certificate request")); mg_tls_drop_message(c); tls->cert_requested = 1; 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, 0); 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->enc.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; } if (tls->cert_requested) { /* for mTLS we should generate application keys at this point * but then restore handshake keys and continue with * the rest of the handshake */ struct tls_enc app_keys; struct tls_enc hs_keys = tls->enc; mg_tls_generate_application_keys(c); app_keys = tls->enc; tls->enc = hs_keys; mg_tls_server_send_cert(c); mg_tls_send_cert_verify(c, 1); mg_tls_client_send_finish(c); tls->enc = app_keys; } else { 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_send_cert_verify(c, 0); 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 = mg_strdup(pem); 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 (opts->cert.buf == NULL) { MG_VERBOSE(("no certificate provided")); return; } // parse PEM or DER certificate if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->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->ec_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->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