#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_rsa.h" #include "tls_uecc.h" #include "tls_x25519.h" #include "util.h" #if MG_TLS == MG_TLS_BUILTIN #ifndef MG_MAX_TLSOID_DEPTH #define MG_MAX_TLSOID_DEPTH 15 #endif // PKCS#8 algorithm OIDs static const uint8_t mg_rsa_oid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 // 1.2.840.113549.1.1.1 rsaEncryption }; static const uint8_t mg_ec_public_key_oid[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 // 1.2.840.10045.2.1 ecPublicKey }; static const uint8_t mg_secp256r1_oid[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 // 1.2.840.10045.3.1.7 secp256r1 }; /* 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 #define MG_TLS_RSA_USE_CRT 1 // CRT instead of naive RSA // 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_FINISH, // Wait for Finish MG_TLS_STATE_CLIENT_CONNECTED, // Done // Server state machine: MG_TLS_STATE_SERVER_START, // Wait for ClientHello MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish 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]; }; struct mg_rsa_key { struct mg_str n; // modulus struct mg_str e; // public exponent struct mg_str d; // private exponent struct mg_str p; // prime1 struct mg_str q; // prime2 struct mg_str dP; // exponent1 (d mod (p-1)) struct mg_str dQ; // exponent2 (d mod (q-1)) struct mg_str qInv; // coefficient ((inverse of q) mod p) }; // 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 size_t recv_offset; // While c->rtls contains full records, reuse that size_t recv_len; // buffer but point 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 p256_cli[32]; // client P-256 key between the handshake states uint8_t x25519_sec[32]; // ECDHE secret (X25519 or P-256) between the states bool skip_verification; // do not perform checks on server certificate bool cert_requested; // client received a CertificateRequest bool is_twoway; // server is configured to authenticate clients bool is_sntp_pending; // TLS handshake is waiting for wall-clock time struct mg_connection *timec; // SNTP connection for wall-clock time struct mg_str cert_der; // certificate in DER format struct mg_str ca_der; // current CA certificate struct mg_str *ca_bundle_der; // bundle of CA certificates size_t ca_bundle_len; // number of certificates in bundle struct mg_str *chain_der; // certificate chain (intermediate certs) size_t chain_len; // number of certificates in chain uint8_t ec_key[32]; // EC private key struct mg_rsa_key rsa; struct mg_str rsa_key_der; // RSA private key in DER format char hostname[254]; // matching hostname bool is_ec_pubkey; // EC or RSA. TODO(): currently unused uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to // verify cert size_t pubkeysz; // size of the server public key uint8_t sighash[32]; // calculated signature verification hash uint8_t sighash384[48]; // same, SHA-384, for ecdsa_secp384r1_sha384 struct tls_enc enc; // actual keys in use at this time struct tls_enc app_keys; // storage during two-way auth handshake }; #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) { FILE *f; char *keylogfile = getenv("SSLKEYLOGFILE"); if (keylogfile == NULL) return; MG_DEBUG(("Dumping key log into %s", keylogfile)); f = fopen(keylogfile, "a"); if (f != NULL) { size_t i; fprintf(f, "%s ", label); for (i = 0; i < 32; i++) { fprintf(f, "%02x", client_random[i]); } fprintf(f, " "); for (i = 0; i < secretsz; i++) { fprintf(f, "%02x", secret[i]); } fprintf(f, "\n"); fclose(f); } else { MG_ERROR(("Cannot open %s", keylogfile)); } } #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) { uint32_t n = 0; if (dersz < 2) return -1; tlv->type = der[0]; tlv->len = der[1]; tlv->value = der + 2; if (tlv->len > 0x7f) { // long-form length uint32_t i; n = tlv->len - 0x80; if (n == 0 || n > 4 || dersz < (2 + n)) return -1; tlv->len = 0; for (i = 0; i < n; i++) { tlv->len = (tlv->len << 8) | der[2 + i]; } tlv->value += n; } if (tlv->len > dersz - (size_t) (tlv->value - der)) return -1; return (int) n; } static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) { int n = mg_der_to_tlv(der, dersz, tlv); if (n < 0) return -1; if (tlv->len >= (uint32_t)((unsigned) -6)) return -1; // avoid overflow return 2 + n + (int) tlv->len; // 2: type, len; n = long form len bytes } static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) { int consumed; if (parent->len == 0) return 0; consumed = mg_der_parse(parent->value, parent->len, child); if (consumed < 0) return -1; parent->value += consumed; parent->len -= (uint32_t) consumed; return 1; } static int der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid, size_t oid_len, struct mg_der_tlv *found, int depth) { struct mg_der_tlv parent, child; int r; parent = *tlv; while ((r = mg_der_next(&parent, &child)) > 0) { if (child.type == 0x06 && child.len == oid_len && memcmp(child.value, oid, oid_len) == 0) { return mg_der_next(&parent, found); } else if (child.type & 0x20) { struct mg_der_tlv sub_parent = child; if (depth >= MG_MAX_TLSOID_DEPTH) { MG_ERROR(("too nested der")); return -1; } if ((r = der_find_oid(&sub_parent, oid, oid_len, found, depth + 1)) > 0) return 1; if (r < 0) return -1; // exit on error; r = 0 => not found, keep parsing } } if (r < 0) return -1; return 0; } static int mg_der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid, size_t oid_len, struct mg_der_tlv *found) { return der_find_oid(tlv, oid, oid_len, found, 0); } #if 0 static void mg_der_debug(struct mg_der_tlv *tlv, int depth) { MG_DEBUG(("> %.*sd=%d Type: 0x%02X, Length: %u\n", depth * 4, " ", depth, tlv->type, tlv->len)); if (tlv->type & 0x20) { // Constructed: recurse into children struct mg_der_tlv child; struct mg_der_tlv parent = *tlv; while (mg_der_next(&parent, &child) > 0) { mg_der_debug(&child, depth + 1); } } } #endif // Did we receive a full TLS record in the c->rtls buffer? static bool mg_tls_got_record(struct mg_connection *c) { uint16_t rl; if (c->rtls.len < (size_t) TLS_RECHDR_SIZE) return false; rl = MG_LOAD_BE16(c->rtls.buf + 3); if (rl > 16384 + 256) { // TLS 1.3 record max length (RFC-9846 5.1) mg_error(c, "TLS record too large"); return false; } return c->rtls.len >= (size_t) (TLS_RECHDR_SIZE + rl); } // 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; unsigned char *recv_buf = &c->rtls.buf[tls->recv_offset]; if (tls->recv_len == 0) return; len = MG_LOAD_BE24(recv_buf + 1) + TLS_MSGHDR_SIZE; if (tls->recv_len < len) { mg_error(c, "wrong size"); return; } mg_sha256_update(&tls->sha256, recv_buf, len); tls->recv_offset += len; tls->recv_len -= len; 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 MG_ENABLE_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)); // ECDHE keys and secret are not needed anymore, wipe for forward secrecy mg_bzero(tls->x25519_cli, sizeof(tls->x25519_cli)); mg_bzero(tls->p256_cli, sizeof(tls->p256_cli)); mg_bzero(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 MG_ENABLE_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 bool 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 (msgsz > 16384) { MG_ERROR(("msg longer than recordsz")); return false; } #if MG_ENABLE_CHACHA20 #else 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); if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 || !mg_iobuf_resize(wio, wio->len + encsz)) return false; outmsg = wio->buf + wio->len; tag = wio->buf + wio->len + msgsz + 1; memmove(outmsg, msg, msgsz); outmsg[msgsz] = msgtype; #if MG_ENABLE_CHACHA20 (void) tag; // tag is only used in aes gcm { size_t n; uint8_t *enc = (uint8_t *) mg_calloc(1, msgsz + 256 + 1); if (enc == NULL) return false; n = mg_chacha20_poly1305_encrypt(enc, key, nonce, associated_data, sizeof(associated_data), outmsg, msgsz + 1); memmove(outmsg, enc, n); mg_free(enc); } #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; return true; } static void verbose_alert(uint8_t *buf) { uint8_t level = buf[0], desc = buf[1]; MG_INFO(("TLS ALERT received: level=%d, desc=%d (%s)", level, desc, desc == 0 ? "close_notify" : desc == 10 ? "unexpected_message" : desc == 20 ? "bad_record_mac" : desc == 21 ? "decryption_failed" : desc == 40 ? "handshake_failure" : desc == 42 ? "bad_certificate" : desc == 43 ? "unsupported_certificate" : desc == 44 ? "certificate_revoked" : desc == 45 ? "certificate_expired" : desc == 46 ? "certificate_unknown" : desc == 48 ? "unknown_ca" : "unknown")); } // 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 CCS mg_tls_drop_record(c); } else if (rio->buf[0] == MG_TLS_ALERT) { // Skip Alerts if (rio->len >= 7) { verbose_alert(&rio->buf[5]); } else { MG_INFO(("TLS ALERT packet received (short)")); } mg_tls_drop_record(c); } else { mg_error(c, "unexpected packet"); return -1; } } msgsz = MG_LOAD_BE16(rio->buf + 3); msg = rio->buf + 5; if (msgsz < 17) { mg_error(c, "wrong size"); return -1; } 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 MG_ENABLE_CHACHA20 { uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03, (uint8_t) ((msgsz >> 8) & 0xff), (uint8_t) (msgsz & 0xff)}; uint8_t *dec = (uint8_t *) mg_calloc(1, msgsz); size_t n; if (dec == NULL) { mg_error(c, "TLS OOM"); return -1; } n = mg_chacha20_poly1305_decrypt(dec, key, nonce, associated_data, sizeof(associated_data), msg, msgsz); if (n == (size_t) -1) { mg_free(dec); mg_error(c, "decryption error"); return -1; } memmove(msg, dec, n); mg_free(dec); } #else { uint8_t associated_data[5] = {MG_TLS_APP_DATA, 0x03, 0x03, (uint8_t) ((msgsz >> 8) & 0xff), (uint8_t) (msgsz & 0xff)}; mg_gcm_initialize(); if (mg_aes_gcm_decrypt(msg, msg, msgsz - 16, key, 16, nonce, sizeof(nonce), associated_data, sizeof(associated_data), msg + msgsz - 16, 16) != 0) { mg_error(c, "GCM tag verify failed"); return -1; } } #endif r = msgsz - 16 - 1; tls->content_type = msg[msgsz - 16 - 1]; if (tls->content_type == MG_TLS_ALERT) { // Process Alerts verbose_alert(msg); if (msg[0] == 2) { mg_error(c, "TLS Fatal alert"); return -1; } else { mg_tls_drop_record(c); return MG_IO_WAIT; } } tls->recv_offset = (size_t) msg - (size_t) rio->buf; tls->recv_len = (size_t) msgsz - 16 - 1; c->is_client ? tls->enc.sseq++ : tls->enc.cseq++; return r; } // Hash the CertificateVerify content: SHA-256 into hash, and if hash384 is not // NULL, SHA-384 into hash384 for ecdsa_secp384r1_sha384 static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, uint8_t hash[32], uint8_t *hash384, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; uint8_t sig_content[130]; mg_sha256_ctx sha256; memset(sig_content, 0x20, 64); if (is_client) { uint8_t client_context[34] = "TLS 1.3, client CertificateVerify"; memcpy(sig_content + 64, client_context, sizeof(client_context)); } else { uint8_t server_context[34] = "TLS 1.3, server CertificateVerify"; memcpy(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); if (hash384 != NULL) mg_sha384(hash384, sig_content, sizeof(sig_content)); } // 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; } if (rio->len < 50) goto fail; msgsz = MG_LOAD_BE16(rio->buf + 3); if (((uint32_t) msgsz + 4) > rio->len) goto fail; 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)) { if (rio->len < (size_t)(46 + session_id_len)) goto fail; // 2: ciphers len memmove(tls->session_id, rio->buf + 44, session_id_len); } else if (session_id_len != 0) { MG_ERROR(("bad session id len")); goto fail; } // session_id_len is either sizeof(tls->session_id) or 0 if (((uint32_t) 44 + 2 + session_id_len) > rio->len) goto fail; cipher_suites_len = MG_LOAD_BE16(rio->buf + 44 + session_id_len); if (((uint32_t) cipher_suites_len + 46 + 2 + 2 + session_id_len) > rio->len) goto fail; ext_len = MG_LOAD_BE16(rio->buf + 48 + session_id_len + cipher_suites_len); ext = rio->buf + 50 + session_id_len + cipher_suites_len; if (((unsigned char *) ext + ext_len) > (rio->buf + rio->len)) goto fail; 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 (((uint32_t) n + j + 4) > ext_len) goto fail; if (MG_LOAD_BE16(ext + j) != 0x0033) { // not a key share extension, ignore j += (uint16_t) (n + 4); continue; } key_exchange_len = MG_LOAD_BE16(ext + j + 4); key_exchange = ext + j + 6; if ((key_exchange + key_exchange_len) > ((uint8_t *) rio->buf + rio->len)) goto fail; for (k = 0; k < key_exchange_len;) { uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); if (((uint32_t) m + k + 4) > key_exchange_len) goto fail; 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); } fail: 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 bool 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 MG_ENABLE_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]; if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG"); if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) { mg_error(c, "bad key"); return false; } 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 if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 || mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) == 0) return false; mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello)); // change cipher message if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0) return false; return true; } static bool 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)); return mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE); } // signature algorithms we actually support: ecdsa_secp256r1_sha256, // ecdsa_secp384r1_sha384 (if enabled), rsa_pss_rsae_sha256, rsa_pkcs1_sha256 static const uint8_t secp256r1_sig_algs[] = { #if MG_UECC_SUPPORTS_secp384r1 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01}; #else 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01}; #endif static bool mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; uint8_t req[13 + sizeof(secp256r1_sig_algs)]; req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header MG_STORE_BE24(req + 1, 9 + sizeof(secp256r1_sig_algs)); req[4] = 0; // context length MG_STORE_BE16(req + 5, 6 + sizeof(secp256r1_sig_algs)); // extensions length MG_STORE_BE16(req + 7, 13); // "signature algorithms" MG_STORE_BE16(req + 9, 2 + sizeof(secp256r1_sig_algs)); // length MG_STORE_BE16( req + 11, sizeof(secp256r1_sig_algs)); // signature hash algorithms length memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs)); mg_sha256_update(&tls->sha256, req, sizeof(req)); return mg_tls_encrypt(c, req, sizeof(req), MG_TLS_HANDSHAKE); } static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; int send_ca = !is_client && tls->ca_der.len > 0; // DER certificate + CA (server optional) size_t i, offset, total_size = tls->cert_der.len + 5; uint8_t *cert; bool res = false; for (i = 1; i < tls->chain_len; i++) { total_size += tls->chain_der[i].len + 5; } if (send_ca) { total_size += tls->ca_der.len + 5; } cert = (uint8_t *) mg_calloc(1, 13 + total_size); if (cert == NULL) return res; cert[0] = MG_TLS_CERTIFICATE; // handshake header if (is_client && tls->cert_der.len == 0) total_size = 0; // empty list MG_STORE_BE24(cert + 1, total_size + 4); cert[4] = 0; // request context MG_STORE_BE24(cert + 5, total_size); // 3 bytes: cert (s) length offset = 8; if (total_size > 0) { // handle empty list, RFC-8446 4.4.2 MG_STORE_BE24(cert + offset, tls->cert_der.len); // 3 bytes: 1st cert len offset += 3; // bytes 11+ are certificate in DER format memmove(cert + offset, tls->cert_der.buf, tls->cert_der.len); offset += tls->cert_der.len; MG_STORE_BE16(cert + offset, 0); // certificate extensions (none) offset += 2; for (i = 1; i < tls->chain_len; i++) { MG_STORE_BE24(cert + offset, tls->chain_der[i].len); offset += 3; memmove(cert + offset, tls->chain_der[i].buf, tls->chain_der[i].len); offset += tls->chain_der[i].len; MG_STORE_BE16(cert + offset, 0); // certificate extensions (none) offset += 2; } if (send_ca) { MG_STORE_BE24(cert + offset, tls->ca_der.len); // 3 bytes: CA cert length offset += 3; memmove(cert + offset, tls->ca_der.buf, tls->ca_der.len); // CA cert data offset += tls->ca_der.len; MG_STORE_BE16(cert + offset, 0); // certificate extensions (none) offset += 2; } } mg_sha256_update(&tls->sha256, cert, offset); res = mg_tls_encrypt(c, cert, offset, MG_TLS_HANDSHAKE); mg_free(cert); return res; } // 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_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed, size_t seed_len) { uint32_t counter = 0; size_t chunk, chunk_len, generated = 0; while (generated < mask_len) { mg_sha256_ctx ctx; uint8_t digest[32]; uint8_t ctr[4]; ctr[0] = (uint8_t) (counter >> 24); ctr[1] = (uint8_t) (counter >> 16); ctr[2] = (uint8_t) (counter >> 8); ctr[3] = (uint8_t) counter; mg_sha256_init(&ctx); mg_sha256_update(&ctx, seed, seed_len); mg_sha256_update(&ctx, ctr, sizeof(ctr)); mg_sha256_final(digest, &ctx); chunk_len = mask_len - generated; chunk = (chunk_len < sizeof(digest) ? chunk_len : sizeof(digest)); memmove(mask + generated, digest, chunk); generated += chunk; counter++; } } static unsigned int mg_tls_rsa_bits(const struct mg_str *n) { size_t i = 0; unsigned int bits = 0; while (i < n->len && n->buf[i] == 0) i++; if (i == n->len) return 0; bits = (unsigned int) ((n->len - i) * 8); { uint8_t byte = (uint8_t) n->buf[i]; while ((byte & 0x80U) == 0) { bits--; byte = (uint8_t) (byte << 1); } } return bits; } static bool mg_tls_pss_encode(const uint8_t *hash, size_t hashlen, const struct mg_str *n, uint8_t *em) { size_t emlen = n->len, saltlen = hashlen, dblen, pslen, i; uint8_t salt[64]; // Max salt size for any reasonable hash uint8_t m[136]; // 8 + max hash (64) + max salt (64) = 136 uint8_t H[64]; // Max hash size uint8_t DB[512]; // Max for 4096-bit RSA uint8_t mask[512]; // Max for 4096-bit RSA mg_sha256_ctx ctx; // Check bounds if (saltlen > sizeof(salt) || (8 + hashlen + saltlen) > sizeof(m) || hashlen > sizeof(H) || emlen > sizeof(DB)) { MG_ERROR(("RSA key too large for static buffers")); return false; } if (emlen < hashlen + saltlen + 2) { return false; } if (!mg_random(salt, saltlen)) { return false; } MG_VERBOSE(("PSS salt: %M", mg_print_hex, saltlen, salt)); // Build m = 8 zero bytes || hash || salt memset(m, 0, 8); memcpy(m + 8, hash, hashlen); memcpy(m + 8 + hashlen, salt, saltlen); mg_sha256_init(&ctx); mg_sha256_update(&ctx, m, 8 + hashlen + saltlen); mg_sha256_final(H, &ctx); MG_VERBOSE(("PSS H: %M", mg_print_hex, hashlen, H)); dblen = emlen - hashlen - 1; pslen = emlen - hashlen - saltlen - 2; // Build DB = PS || 0x01 || salt memset(DB, 0, pslen); DB[pslen] = 0x01; memcpy(DB + pslen + 1, salt, saltlen); // Generate mask and apply to DB mg_tls_mgf1(mask, dblen, H, hashlen); for (i = 0; i < dblen; i++) DB[i] ^= mask[i]; { // PSS standard: emBits = modulus_bit_length - 1 unsigned rsa_bits = mg_tls_rsa_bits(n); unsigned embits = rsa_bits - 1; unsigned unused = (unsigned) (8 * emlen - embits); MG_VERBOSE(("RSA modulus bits: %u, emBits: %u, emlen: %zu, unused: %u", rsa_bits, embits, emlen, unused)); if (unused > 0 && unused < 8) { uint8_t mask_byte = (uint8_t) (0xff >> unused); MG_VERBOSE(("Applying mask 0x%02x to first byte (was 0x%02x)", mask_byte, DB[0])); DB[0] &= mask_byte; MG_VERBOSE(("First byte after mask: 0x%02x", DB[0])); } } // Build final em = maskedDB || H || 0xbc memcpy(em, DB, dblen); memcpy(em + dblen, H, hashlen); em[emlen - 1] = 0xbc; return true; } static bool mg_tls_rsa_sign(struct tls_data *tls, const uint8_t *em, size_t emlen, uint8_t *sig) { const uint8_t *n = (const uint8_t *) tls->rsa.n.buf; size_t nlen = tls->rsa.n.len; int crt_result; while (nlen > 0 && *n == 0) n++, nlen--; if (emlen != nlen) return false; #if MG_TLS_RSA_USE_CRT // RSA CRT (Chinese Remainder Theorem) optimization: // s1 = em^dP mod p // s2 = em^dQ mod q // h = qInv * (s1 - s2) mod p // s = s2 + h * q if (tls->rsa.p.len == 0 || tls->rsa.q.len == 0 || tls->rsa.dP.len == 0 || tls->rsa.dQ.len == 0 || tls->rsa.qInv.len == 0) { MG_ERROR(("CRT parameters missing, cannot use CRT optimization")); return false; } MG_VERBOSE(("Using RSA-CRT optimization")); crt_result = mg_rsa_crt_sign( em, emlen, (const uint8_t *) tls->rsa.dP.buf, tls->rsa.dP.len, (const uint8_t *) tls->rsa.dQ.buf, tls->rsa.dQ.len, (const uint8_t *) tls->rsa.p.buf, tls->rsa.p.len, (const uint8_t *) tls->rsa.q.buf, tls->rsa.q.len, (const uint8_t *) tls->rsa.qInv.buf, tls->rsa.qInv.len, sig, nlen); if (crt_result == 0) { uint8_t check[512]; bool verified; if (nlen > sizeof(check) || mg_rsa_mod_pow(n, nlen, (const uint8_t *) tls->rsa.e.buf, tls->rsa.e.len, sig, nlen, check, nlen) != 0) { MG_ERROR(("CRT signature verification failed")); return false; } verified = memcmp(check, em, emlen) == 0; mg_bzero(check, sizeof(check)); if (!verified) { MG_ERROR(("CRT signature verification failed")); return false; } MG_VERBOSE(("CRT signature successful (first 4 bytes): %02x %02x %02x %02x", sig[0], sig[1], sig[2], sig[3])); MG_VERBOSE(("CRT signature successful (last 4 bytes): %02x %02x %02x %02x", sig[nlen - 4], sig[nlen - 3], sig[nlen - 2], sig[nlen - 1])); return true; } else { MG_ERROR(("CRT signing failed")); return false; } #else int ret; // Standard RSA: s = em^d mod n memset(sig, 0, nlen); ret = mg_rsa_mod_pow(n, nlen, (const uint8_t *) tls->rsa.d.buf, tls->rsa.d.len, em, emlen, sig, nlen); if (ret == 0) { MG_VERBOSE(("RSA signature first 4 bytes: %02x %02x %02x %02x", sig[0], sig[1], sig[2], sig[3])); MG_VERBOSE(("RSA signature last 4 bytes: %02x %02x %02x %02x", sig[nlen - 4], sig[nlen - 3], sig[nlen - 2], sig[nlen - 1])); } return ret == 0; #endif } static size_t mg_rsa_trim_len(const uint8_t **p, size_t n) { while (n > 0 && **p == 0) (*p)++, n--; return n; } static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; uint8_t hash[32] = {0}; mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash, NULL, is_client); if (tls->rsa.n.len > 0 && tls->rsa.d.len > 0) { // RSA certificate verify packet const uint8_t *n = (const uint8_t *) tls->rsa.n.buf; size_t nlen = tls->rsa.n.len; struct mg_str rn; size_t emlen, verifysz; uint8_t em[512]; // Max for 4096-bit RSA uint8_t verify[520]; // 8 + 512 max nlen = mg_rsa_trim_len(&n, nlen); rn = mg_str_n((char *) n, nlen); emlen = nlen; verifysz = 8U + emlen; // Check bounds if (emlen > sizeof(em) || verifysz > sizeof(verify)) { MG_ERROR(("RSA key too large for static buffers")); return false; } if (!mg_tls_pss_encode(hash, sizeof(hash), &rn, em)) { MG_ERROR(("Failed PSS encode")); return false; } // Validate PSS encoded message format if (em[emlen - 1] != 0xbc) { MG_ERROR(("Invalid PSS encoding: last byte is 0x%02x, expected 0xbc", em[emlen - 1])); return false; } // Build verify packet header, then sign directly into the packet verify[0] = 0x0f; MG_STORE_BE24(verify + 1, emlen + 4); MG_STORE_BE16(verify + 4, 0x0804); MG_STORE_BE16(verify + 6, emlen); // Sign directly into the verify buffer (verify + 8 = signature location) memset(verify + 8, 0, emlen); // Initialize signature area if (!mg_tls_rsa_sign(tls, em, emlen, verify + 8)) { MG_ERROR(("Failed RSA sign")); return false; } MG_VERBOSE( ("PSS EM first 4: %02x %02x %02x %02x", em[0], em[1], em[2], em[3])); MG_VERBOSE(("PSS EM last 4: %02x %02x %02x %02x", em[emlen - 4], em[emlen - 3], em[emlen - 2], em[emlen - 1])); mg_sha256_update(&tls->sha256, verify, verifysz); return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE); } else { // EC certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; uint8_t tmp[2 * 32 + 64] = {0}; struct SHA256_HashContext ctx = { {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, {{0}, 0, 0, {0}}}; size_t sigsz, verifysz = 0; int neg1, neg2; uint8_t sig[64] = {0}; 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); return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE); } } static bool mg_tls_server_send_finish(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; 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); if (!mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE)) return false; mg_sha256_update(&tls->sha256, finish, sizeof(finish)); return true; } static int mg_tls_server_recv_finish(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; // 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; } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] != MG_TLS_FINISHED) { mg_error(c, "expected Finish but got msg 0x%02x", recv_buf[0]); return -1; } mg_tls_drop_message(c); // restore hash tls->sha256 = sha256; return 0; } static bool 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]; uint8_t p256_pub[64]; // X || Y, sent as an uncompressed point 0x04 || X || Y // - "signature algorithms we actually support", see above // uint8_t secp256r1_sig_algs[] // - 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[216] = { // 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 MG_ENABLE_CHACHA20 // TLS_CHACHA20_POLY1305_SHA256 0x13, 0x03, #else // TLS_AES_128_GCM_SHA256 0x13, 0x01, #endif // no compression 0x01, 0x00, // extensions + keyshare 0x00, 0xfe, // keyshare: x25519 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, 0x20, PLACEHOLDER_32B, // keyshare: secp256r1, uncompressed point 0x00, 0x17, 0x00, 0x41, 0x04, PLACEHOLDER_32B, PLACEHOLDER_32B, // supported groups (x25519, secp256r1) 0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x17, // supported versions (tls1.3 == 0x304) 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, // session ticket (none) 0x00, 0x23, 0x00, 0x00, // 215 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 : (uint8_t *) 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 + 254 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 7, hostname_extsz + 250 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 82, hostname_extsz + 175 - 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 keyshares if (!mg_random(tls->x25519_cli, sizeof(tls->x25519_cli))) mg_error(c, "RNG"); mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1); if (!mg_uecc_make_key(p256_pub, tls->p256_cli, mg_uecc_secp256r1())) { mg_error(c, "P-256 key"); return false; // Do not send uninitialised p256_pub } // fill in the gaps: random + session ID + keyshares if (!mg_random(tls->session_id, sizeof(tls->session_id))) mg_error(c, "RNG"); if (!mg_random(tls->random, sizeof(tls->random))) mg_error(c, "RNG"); 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)); memmove(msg_client_hello + 131, p256_pub, sizeof(p256_pub)); // client hello message if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) == 0) return false; mg_sha256_update(&tls->sha256, msg_client_hello + 5, sizeof(msg_client_hello) - 5); if (mg_iobuf_add(wio, wio->len, sig_alg, sig_alg_sz) == 0) return false; mg_sha256_update(&tls->sha256, sig_alg, sig_alg_sz); if (hostnamesz > 0) { if (mg_iobuf_add(wio, wio->len, server_name_ext, sizeof(server_name_ext)) == 0 || mg_iobuf_add(wio, wio->len, hostname, hostnamesz) == 0) return false; mg_sha256_update(&tls->sha256, server_name_ext, sizeof(server_name_ext)); mg_sha256_update(&tls->sha256, (uint8_t *) hostname, hostnamesz); } // change cipher message if (mg_iobuf_add(wio, wio->len, (const char *) "\x14\x03\x03\x00\x01\x01", 6) == 0) return false; return true; } 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->len < 6 || rio->buf[5] != MG_TLS_SERVER_HELLO) { if (rio->buf[0] == MG_TLS_ALERT && rio->len >= 7) { verbose_alert(&rio->buf[5]); mg_error(c, "TLS error alert"); return -1; } if (rio->len >= 6) MG_ERROR(("got packet type 0x%02x/0x%02x", rio->buf[0], rio->buf[5])); mg_error(c, "not a server hello packet"); return -1; } if (rio->len < 5 + 39 + 32 + 3 + 2) goto fail; msgsz = MG_LOAD_BE16(rio->buf + 3); if (msgsz > rio->len - 5) goto fail; 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; if (ext_len > (rio->len - (5 + 39 + 32 + 3 + 2))) goto fail; for (j = 0; j < ext_len;) { uint16_t ext_type, ext_len2, group, key_exchange_len; uint8_t *key_exchange; if ((ext_len - j) < 4) goto fail; ext_type = MG_LOAD_BE16(ext + j); ext_len2 = MG_LOAD_BE16(ext + j + 2); if (ext_len2 > (ext_len - j - 4)) goto fail; if (ext_type != 0x0033) { // not a key share extension, ignore j += (uint16_t) (ext_len2 + 4); continue; } if (ext_len2 < (2 + 2 + 32)) goto fail; group = MG_LOAD_BE16(ext + j + 4); key_exchange_len = MG_LOAD_BE16(ext + j + 6); key_exchange = ext + j + 8; if (key_exchange_len > ext_len2 - 4) goto fail; if (group == 0x001d) { // x25519 if (key_exchange_len != 32 || mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) < 0) { mg_error(c, "bad key"); return -1; } } else if (group == 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y if (key_exchange_len != 65 || key_exchange[0] != 0x04 || !mg_uecc_valid_public_key(key_exchange + 1, mg_uecc_secp256r1()) || !mg_uecc_shared_secret(key_exchange + 1, tls->p256_cli, tls->x25519_sec, mg_uecc_secp256r1())) { mg_error(c, "bad key"); return -1; } } else { mg_error(c, "bad key exchange group"); return -1; } // mg_tls_hexdump("c ecdhe sec", tls->x25519_sec, 32); mg_tls_drop_record(c); /* generate handshake keys */ mg_tls_generate_handshake_keys(c); return 0; } fail: mg_error(c, "bad server hello"); return -1; } static int mg_tls_client_recv_ext(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; if (mg_tls_recv_record(c) < 0) { return -1; } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] != MG_TLS_ENCRYPTED_EXTENSIONS) { mg_error(c, "expected server extensions but got msg 0x%02x", recv_buf[0]); return -1; } mg_tls_drop_message(c); return 0; } struct mg_tls_cert { bool is_ec_pubkey; bool is_ca; struct mg_str sn; struct mg_str pubkey; struct mg_der_tlv issuer; struct mg_der_tlv subj; struct mg_str sig; // signature uint8_t tbshash[48]; // 32B for sha256/secp256, 48B for sha384/secp384 size_t tbshashsz; // actual TBS hash size }; static void mg_der_debug_cert_name(const char *name, struct mg_der_tlv *tlv) { struct mg_der_tlv v; struct mg_str cn, c, o, ou; if (mg_log_level < MG_LL_VERBOSE) return; // skip recursive computations cn = c = o = ou = mg_str(""); if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x03", 3, &v) > 0) cn = mg_str_n((const char *) v.value, v.len); if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x06", 3, &v) > 0) c = mg_str_n((const char *) v.value, v.len); if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0a", 3, &v) > 0) o = mg_str_n((const char *) v.value, v.len); if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0b", 3, &v) > 0) ou = mg_str_n((const char *) v.value, v.len); MG_VERBOSE(("%s: CN=%.*s, C=%.*s, O=%.*s, OU=%.*s", name, cn.len, cn.buf, c.len, c.buf, o.len, o.buf, ou.len, ou.buf)); } static uint64_t asnt2t(uint8_t *v, uint8_t type) { unsigned int y, mo, d, h, mi, ss; y = 10U * (*v++ - '0'), y += (*v++ - '0'); if (type == 0x17) { // UTCTime, RFC-5280 4.1.2.5.1 YYMMDDHHMMSSZ if (y >= 50) return (uint64_t) 0; // 19YY is in the past y += 2000; } else { // GeneralizedTime, RFC-5280 4.1.2.5.2 YYYYMMDDHHMMSSZ y *= 100U, y += 10U * (*v++ - '0'), y += (*v++ - '0'); } mo = 10U * (*v++ - '0'), mo += (*v++ - '0'); d = 10U * (*v++ - '0'), d += (*v++ - '0'); h = 10U * (*v++ - '0'), h += (*v++ - '0'); mi = 10U * (*v++ - '0'), mi += (*v++ - '0'); ss = 10U * (*v++ - '0'), ss += (*v++ - '0'); if (*v != 'Z') return 0; // invalid return mg_timegm(y, mo, d, h, mi, ss); } static int mg_tls_parse_cert_der(void *buf, size_t dersz, struct mg_tls_cert *cert) { uint8_t *tbs, *der = (uint8_t *) buf; size_t tbssz; struct mg_der_tlv root, tbs_cert, field, algo; // pubkey, signature; struct mg_der_tlv pki, pki_algo, pki_key, pki_curve, raw_sig; // Parse outermost SEQUENCE if (mg_der_parse(der, dersz, &root) <= 0 || root.type != 0x30) return -1; // Parse TBSCertificate SEQUENCE tbs = root.value; if (mg_der_next(&root, &tbs_cert) <= 0 || tbs_cert.type != 0x30) return -1; tbssz = (size_t) (tbs_cert.value + tbs_cert.len - tbs); // Parse Version (optional field) if (mg_der_next(&tbs_cert, &field) <= 0) return -1; if (field.type == 0xa0) { // v3 if (mg_der_parse(field.value, field.len, &field) <= 0 || field.len != 1 || field.value[0] != 2) return -1; if (mg_der_next(&tbs_cert, &field) <= 0) return -1; } // Parse Serial Number if (field.type != 2) return -1; cert->sn = mg_str_n((char *) field.value, field.len); MG_VERBOSE(("cert s/n: %M", mg_print_hex, cert->sn.len, cert->sn.buf)); // Parse signature algorithm (first occurrence) if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1; if (mg_der_next(&field, &algo) <= 0 || algo.type != 0x06) return -1; MG_VERBOSE(("sig algo (oid): %M", mg_print_hex, algo.len, algo.value)); // Signature algorithm OID mapping if (algo.len == 8 && memcmp(algo.value, "\x2A\x86\x48\xCE\x3D\x04\x03\x02", 8) == 0) { MG_VERBOSE(("sig algo: ECDSA with SHA256")); mg_sha256(cert->tbshash, tbs, tbssz); cert->tbshashsz = 32; } else if (algo.len == 9 && memcmp(algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B", 9) == 0) { MG_VERBOSE(("sig algo: RSA with SHA256")); mg_sha256(cert->tbshash, tbs, tbssz); cert->tbshashsz = 32; } else if (algo.len == 8 && memcmp(algo.value, "\x2A\x86\x48\xCE\x3D\x04\x03\x03", 8) == 0) { MG_VERBOSE(("sig algo: ECDSA with SHA384")); mg_sha384(cert->tbshash, tbs, tbssz); cert->tbshashsz = 48; } else if (algo.len == 9 && memcmp(algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0C", 9) == 0) { MG_VERBOSE(("sig algo: RSA with SHA384")); mg_sha384(cert->tbshash, tbs, tbssz); cert->tbshashsz = 48; } else { MG_ERROR( ("sig algo: unsupported OID: %M", mg_print_hex, algo.len, algo.value)); return -1; } MG_VERBOSE(("tbs hash: %M", mg_print_hex, cert->tbshashsz, cert->tbshash)); // issuer if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1; cert->issuer = field; mg_der_debug_cert_name("issuer", &field); // validity dates (before/after) if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1; { struct mg_der_tlv before, after; uint64_t now = mg_now() / 1000U, t; if (mg_der_next(&field, &before) <= 0 || ((before.type != 0x17 || before.len != 13) && (before.type != 0x18 || before.len != 15))) return -1; if (now < (t = asnt2t(before.value, before.type))) { MG_ERROR(("cert is not yet valid: before=%.*s (%lu), now=%lu", before.len, before.value, t, now)); return -1; } if (mg_der_next(&field, &after) <= 0 || ((after.type != 0x17 || after.len != 13) && (after.type != 0x18 || after.len != 15))) return -1; if (memcmp(after.value, "99991231235959Z", 15) == 0) { // RFC-5280 4.1.2.5 MG_ERROR(("No well-defined expiration date")); return -1; } if (now > (t = asnt2t(after.value, after.type))) { MG_ERROR(("cert is no longer valid: after=%.*s (%lu), now=%lu", after.len, after.value, t, now)); return -1; } } // subject if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1; cert->subj = field; mg_der_debug_cert_name("subject", &field); // subject public key info if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1; if (mg_der_next(&field, &pki) <= 0 || pki.type != 0x30) return -1; if (mg_der_next(&pki, &pki_algo) <= 0 || pki_algo.type != 0x06) return -1; // public key algorithm MG_VERBOSE(("pk algo (oid): %M", mg_print_hex, pki_algo.len, pki_algo.value)); if (pki_algo.len == 8 && memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8) == 0) { cert->is_ec_pubkey = true; MG_VERBOSE(("pk algo: ECDSA secp256r1")); } else if (pki_algo.len == 8 && memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x03\x01\x08", 8) == 0) { cert->is_ec_pubkey = true; MG_VERBOSE(("pk algo: ECDSA secp384r1")); } else if (pki_algo.len == 7 && memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x02\x01", 7) == 0) { cert->is_ec_pubkey = true; MG_VERBOSE(("pk algo: EC public key")); } else if (pki_algo.len == 9 && memcmp(pki_algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9) == 0) { cert->is_ec_pubkey = false; MG_VERBOSE(("pk algo: RSA")); } else { MG_ERROR(("unsupported pk algo: %M", mg_print_hex, pki_algo.len, pki_algo.value)); return -1; } // Parse public key if (cert->is_ec_pubkey) { if (mg_der_next(&pki, &pki_curve) <= 0 || pki_curve.type != 0x06) return -1; } if (mg_der_next(&field, &pki_key) <= 0 || pki_key.type != 0x03) return -1; if (cert->is_ec_pubkey) { // Skip leading 0x00 and 0x04 (=uncompressed) if (pki_key.len < 2) return -1; cert->pubkey = mg_str_n((char *) pki_key.value + 2, pki_key.len - 2); } else { // Skip leading 0x00 byte if (pki_key.len < 1) return -1; cert->pubkey = mg_str_n((char *) pki_key.value + 1, pki_key.len - 1); } { // parse optional fields int r; // unique ids while ((r = mg_der_next(&tbs_cert, &field)) > 0 && field.type == 0xa1); if (r > 0 && field.type == 0xa3) { // extensions bool ca = false, certsign = true; struct mg_der_tlv ext, e, i; // ext[e(i, ...), ...] if (mg_der_next(&field, &ext) <= 0 || ext.type != 0x30) return -1; while (mg_der_next(&ext, &e) > 0) { if (mg_der_next(&e, &i) <= 0 || i.type != 0x06) return -1; if (i.len == 3 && memcmp(i.value, (uint8_t *) "\x55\x1d\x13", 3) == 0) { struct mg_der_tlv s, v; // basicConstraints MG_VERBOSE(("basicConstraints")); if (mg_der_next(&e, &i) <= 0 || i.type != 0x01 || i.len != 1 || *i.value != 0xff) return -1; if (mg_der_next(&e, &i) <= 0 || i.type != 0x04) return -1; if (mg_der_next(&i, &s) <= 0 || s.type != 0x30) return -1; if (s.len == 0) break; if (mg_der_next(&s, &v) <= 0) return -1; if (v.type == 0x01 && v.len == 1 && *v.value == 0xff) ca = true; } else if (i.len == 3 && memcmp(i.value, (uint8_t *) "\x55\x1d\x0f", 3) == 0) { struct mg_der_tlv b; // keyUsage, enforce when present MG_VERBOSE(("keyUsage")); if (mg_der_next(&e, &i) <= 0) return -1; if (i.type == 0x01) { // SHOULD, defaults to false if (i.len != 1) return -1; if (mg_der_next(&e, &i) <= 0) return -1; } if (i.type != 0x04) return -1; if (mg_der_next(&i, &b) <= 0 || b.type != 0x03) return -1; if (!(b.value[1] & MG_BIT(2))) certsign = false; } } if (ca && certsign) cert->is_ca = true; } } // Parse signature if (mg_der_next(&root, &field) <= 0 || field.type != 0x30) return -1; if (mg_der_next(&root, &raw_sig) <= 0 || raw_sig.type != 0x03) return -1; if (raw_sig.len < 1 || raw_sig.value[0] != 0x00) return -1; cert->sig = mg_str_n((char *) raw_sig.value + 1, raw_sig.len - 1); MG_VERBOSE(("sig: %M", mg_print_hex, cert->sig.len, cert->sig.buf)); return 0; } static int countdots(struct mg_str s) { int count = 0; size_t len = s.len; char *p = s.buf; while (len--) { if (*(p++) == '.') ++count; } return count; } static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz, const char *server_name, struct mg_addr *ref_ip) { struct mg_der_tlv root, field, name; bool is_addr = ref_ip != NULL; if (mg_der_parse((uint8_t *) der, dersz, &root) < 0) { MG_ERROR(("failed to parse certificate")); return -1; } if (mg_der_find_oid(&root, (uint8_t *) "\x55\x1d\x11", 3, &field) <= 0) { MG_ERROR(("failed to extract SAN")); return -1; } if (mg_der_parse(field.value, field.len, &field) < 0) { MG_ERROR(("SAN is not a constructed object")); return -1; } while (mg_der_next(&field, &name) > 0) { if (is_addr) { // match ref IP to IP and name to DNS name (RFC-9525 6.2) if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address uint32_t cert_ip; MG_VERBOSE(("Found SAN, IP: %M", mg_print_ip4, name.value)); memcpy(&cert_ip, name.value, 4); if (!ref_ip->is_ip6 && cert_ip == ref_ip->addr.ip4) // RFC-9525 6.4 return 1; // and byte-matches the one we wanted to connected to } #if MG_ENABLE_IPV6 else if (name.type == 0x87 && name.len == 16) { // is an IPv6 address MG_VERBOSE(("Found SAN, IPv6: %M", mg_print_ip6, name.value)); if (ref_ip->is_ip6 && memcmp(name.value, ref_ip->addr.ip6, 16) == 0) return 1; // and byte-matches the one we wanted to connected to } #endif } else if (name.type == 0x82) { // this is a DNS name struct mg_str sn, tn; MG_VERBOSE(("Found SAN, DNS: %.*s", name.len, name.value)); sn = mg_str(server_name), tn = mg_str_n((char *) name.value, name.len); if (countdots(sn) == countdots(tn) && mg_match(sn, tn, NULL)) return 1; // and matches the host name } } return -1; } static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, const struct mg_tls_cert *issuer) { if (issuer->is_ec_pubkey) { uint8_t sig[128]; struct mg_der_tlv seq = {0, 0, 0}, a = {0, 0, 0}, b = {0, 0, 0}; mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq); mg_der_next(&seq, &a); mg_der_next(&seq, &b); if (a.len == 0 || b.len == 0) { MG_ERROR(("cert verification error")); return 0; } #if MG_UECC_SUPPORTS_secp256r1 if (issuer->pubkey.len == 64) { const uint32_t N = 32; if (a.len > N) a.value += (a.len - N), a.len = N; // padding if (b.len > N) b.value += (b.len - N), b.len = N; memset(sig, 0, N - a.len); // short encoding memmove(sig + (N - a.len), a.value, a.len); memset(sig + N, 0, N - b.len); memmove(sig + N + (N - b.len), b.value, b.len); return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, (unsigned) cert->tbshashsz, sig, mg_uecc_secp256r1()); } else #endif #if MG_UECC_SUPPORTS_secp384r1 if (issuer->pubkey.len == 96) { const uint32_t N = 48; if (a.len > N) a.value += (a.len - N), a.len = N; // padding if (b.len > N) b.value += (b.len - N), b.len = N; memset(sig, 0, N - a.len); // short encoding memmove(sig + (N - a.len), a.value, a.len); memset(sig + N, 0, N - b.len); memmove(sig + N + (N - b.len), b.value, b.len); return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, (unsigned) cert->tbshashsz, sig, mg_uecc_secp384r1()); } else #endif { MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len)); return 0; } } else { const uint8_t *n; size_t nlen; uint8_t sig2[512]; // 4096 bits struct mg_der_tlv seq, modulus, exponent; if (mg_der_parse((uint8_t *) issuer->pubkey.buf, issuer->pubkey.len, &seq) <= 0 || mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2 || exponent.len == 0) { return -1; } n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len); if (nlen > sizeof(sig2) || cert->tbshashsz > nlen || mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len, (uint8_t *) cert->sig.buf, cert->sig.len, sig2, nlen) != 0) { return 0; } return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz); } } static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) { struct mg_der_tlv v; int matched = 0; if (mg_der_find_oid(subj, (uint8_t *) "\x55\x04\x03", 3, &v) > 0) { struct mg_str hn, cn; MG_VERBOSE(("using CN: %.*s <-> %s", v.len, v.value, host)); hn = mg_str(host), cn = mg_str_n((char *) v.value, v.len); matched = (int) (countdots(hn) == countdots(cn) && mg_match(hn, cn, NULL)); } return matched; } // 1: found, 0: not found, < 0: parse error: -1: bundle (unused), -2: name static int tls_bundle_find(struct tls_data *tls, struct mg_der_tlv *name, struct mg_tls_cert *cert) { size_t i; struct mg_der_tlv v; struct mg_str *p = tls->ca_bundle_der, tgt; if (mg_der_find_oid(name, (uint8_t *) "\x55\x04\x03", 3, &v) <= 0) return -2; tgt = mg_str_n((const char *) v.value, v.len); for (i = 0; i < tls->ca_bundle_len; i++, p++) { struct mg_str subj; if (mg_tls_parse_cert_der(p->buf, p->len, cert) < 0 || mg_der_find_oid(&cert->subj, (uint8_t *) "\x55\x04\x03", 3, &v) <= 0) { MG_ERROR(("failed to parse certificate #%u in bundle", i + 1)); continue; // skip this certificate but don't halt the process } subj = mg_str_n((const char *) v.value, v.len); MG_VERBOSE(("#%u: %.*s (%.*s)", i + 1, subj.len, subj.buf, tgt.len, tgt.buf)); if (mg_strcasecmp(subj, tgt) == 0) return 1; } return 0; } bool mg_aton_(struct mg_str str, struct mg_addr *addr); static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; if (mg_tls_recv_record(c) < 0) { return -1; } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] == MG_TLS_CERTIFICATE_REQUEST) { MG_VERBOSE(("got certificate request")); mg_tls_drop_message(c); tls->cert_requested = 1; return -1; } if (recv_buf[0] != MG_TLS_CERTIFICATE) { mg_error(c, "expected %s certificate but got msg 0x%02x", is_client ? "server" : "client", recv_buf[0]); return -1; } if (tls->recv_len < 13) { // 8 + 3 + 2, chain len + cert len + ext len mg_error(c, "certificate list too short"); return -1; } { // Normally, there are 2-3 certs in a chain (when is_client) struct mg_tls_cert certs[8]; int certnum = 0; uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5); uint8_t *p = recv_buf + 8; uint8_t *endp = recv_buf + cert_chain_len + 8; bool found_ca = false; struct mg_tls_cert ca; if (cert_chain_len != full_cert_chain_len - 4 || cert_chain_len > (tls->recv_len - 8)) { MG_ERROR(("full chain length: %d, chain length: %d, msg length: %d", full_cert_chain_len, cert_chain_len, tls->recv_len)); mg_error(c, "invalid certificate chain length"); return -1; } memset(certs, 0, sizeof(certs)); memset(&ca, 0, sizeof(ca)); if (tls->ca_der.len > 0) { // single cert or server if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) { mg_error(c, "failed to parse CA certificate"); return -1; } MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); } while (p < endp) { struct mg_tls_cert *ci = &certs[certnum++]; uint32_t certsz; uint16_t certext; uint8_t *cert = p + 3; if ((endp - p) < 5) { // 3 + 2, cert len + ext len fields mg_error(c, "truncated certificate in chain"); return -1; } certsz = MG_LOAD_BE24(p); p = cert + certsz + 2; // skip cert extensions (size only, not supported) if (p > endp) { mg_error(c, "invalid certificate length"); return -1; } certext = MG_LOAD_BE16(cert + certsz); if (certext != 0) { mg_error(c, "certificate extensions are not supported"); return -1; } if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) { mg_error(c, "failed to parse certificate"); return -1; } if (ci == certs) { // First certificate in the chain is peer cert, check SAN if requested, // and store public key for further CertVerify step if (tls->hostname[0] != '\0') { struct mg_addr addr; // use aton_ so localhost evals as a name bool is_addr = mg_aton_(mg_str_s(tls->hostname), &addr); if (mg_tls_verify_cert_san(cert, certsz, tls->hostname, is_addr ? &addr : NULL) <= 0 && (!is_addr || // DNS CN fallback mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0)) { mg_error(c, "failed to verify hostname"); return -1; } } if (ci->pubkey.len > sizeof(tls->pubkey)) { mg_error(c, "peer public key too large"); return -1; } memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len); tls->pubkeysz = ci->pubkey.len; } else { if (!ci->is_ca || !mg_tls_verify_cert_signature(ci - 1, ci)) { mg_error(c, "failed to verify certificate chain %s", ci->is_ca ? "" : "(not a true CA)"); return -1; } } if (tls->ca_bundle_len > 0) { // bundle, find subject and compare keys int r; MG_VERBOSE(("Search current cert in bundle")); r = tls_bundle_find(tls, &ci->subj, &ca); // find current cert ci if (r == -1) { mg_error(c, "failed to parse CA bundle"); return -1; } else if (r > 0 && ca.pubkey.len == ci->pubkey.len && memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) { found_ca = true; MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); break; } // else r = 0 or -2 => subj not found or pubkey not matching } // Single CA: accept the chain if the CA itself is in it, e.g. a // cross-signed copy of the root. Same subject and same public key // means the same trusted key, and the links below ci are verified if (tls->ca_der.len > 0 && ca.subj.len == ci->subj.len && memcmp(ca.subj.value, ci->subj.value, ca.subj.len) == 0 && ca.pubkey.len == ci->pubkey.len && memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) { found_ca = true; MG_VERBOSE(("CA found in chain at depth %d", certnum - 1)); break; } if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) { mg_error(c, "too many certificates in the chain"); return -1; } } if (!found_ca && certnum > 0 && tls->ca_bundle_len > 0) { // bundle int r; MG_VERBOSE(("Search bundle for issuer of last cert in chain")); r = tls_bundle_find(tls, &certs[certnum - 1].issuer, &ca); if (r <= 0) { mg_error(c, "failed to %s", r == 0 ? "find issuing CA in bundle" : r == -1 ? "parse CA bundle" : "parse name OID"); return -1; } else if (!ca.is_ca || // candidate issuer found !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { mg_error(c, "failed to verify CA"); return -1; } // issuer verified. NOTE(): may fail if two diff CAs share a CN MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf)); } if (!found_ca && tls->ca_der.len > 0) { // single cert or server if (certnum < 1 || !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { mg_error(c, "failed to verify CA"); return -1; } else if (is_client) { MG_VERBOSE(("no CA in chain; verification with builtin CA passed")); } } } mg_tls_drop_message(c); mg_tls_calc_cert_verify_hash(c, tls->sighash, tls->sighash384, !is_client); return 0; } static int mg_tls_recv_cert_verify(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; if (mg_tls_recv_record(c) < 0) { return -1; } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] != MG_TLS_CERTIFICATE_VERIFY) { mg_error(c, "expected %s certificate verify but got msg 0x%02x", c->is_client ? "server" : "client", recv_buf[0]); return -1; } if (tls->recv_len < 8) { mg_error(c, "server certificate verify is too short: %d bytes", tls->recv_len); return -1; } // Ignore CertificateVerify if strict checks are not required if (tls->skip_verification) { mg_tls_drop_message(c); return 0; } { uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4); uint16_t siglen = MG_LOAD_BE16(recv_buf + 6); uint8_t *sigbuf = recv_buf + 8; if (siglen > tls->recv_len - 8) { mg_error(c, "invalid certverify signature length: %d, expected %d", siglen, tls->recv_len - 8); return -1; } MG_VERBOSE( ("certificate verification, algo=%04x, siglen=%d", sigalg, siglen)); if (sigalg == 0x0804) { // rsa_pss_rsae_sha256 uint8_t sig2[512]; // 2048 or 4096 bits const uint8_t *n; size_t nlen; struct mg_der_tlv seq, modulus, exponent; if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 || mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 || modulus.len == 0 || mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2 || exponent.len == 0) { mg_error(c, "invalid public key"); return -1; } n = modulus.value, nlen = mg_rsa_trim_len(&n, modulus.len); if (nlen > sizeof(sig2) || mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len, sigbuf, siglen, sig2, nlen) != 0 || !mg_rsa_verify(sig2, nlen, tls->sighash)) { mg_error(c, "failed to verify RSA certificate (certverify)"); return -1; } MG_VERBOSE(("certificate verification successful (RSA)")); } else if (sigalg == 0x0403 || (sigalg == 0x0503 && MG_UECC_SUPPORTS_secp384r1)) { // ecdsa_secp256r1_sha256 or ecdsa_secp384r1_sha384. Extract certificate // signature and verify it using pubkey and sighash bool is384 = sigalg == 0x0503; uint32_t N = is384 ? 48 : 32; // curve size: r and s are N bytes each int ok; uint8_t sig[96]; struct mg_der_tlv seq, r, s; memset(sig, 0, sizeof(sig)); if (tls->pubkeysz != 2 * N) { mg_error(c, "certverify scheme %04x does not match the key", sigalg); return -1; } if (mg_der_to_tlv(sigbuf, siglen, &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, &r) < 0) { mg_error(c, "missing first part of the signature"); return -1; } if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) { mg_error(c, "missing second part of the signature"); return -1; } // Integers may be padded with zeroes if (r.len > N) r.value = r.value + (r.len - N), r.len = N; if (s.len > N) s.value = s.value + (s.len - N), s.len = N; // r or s may be shorter than N bytes, "right-justify" (network order) memmove(sig + (N - r.len), r.value, r.len); memmove(sig + N + (N - s.len), s.value, s.len); #if MG_UECC_SUPPORTS_secp384r1 if (is384) { ok = mg_uecc_verify(tls->pubkey, tls->sighash384, sizeof(tls->sighash384), sig, mg_uecc_secp384r1()); } else #endif { ok = mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig, mg_uecc_secp256r1()); } if (ok != 1) { mg_error(c, "failed to verify EC certificate (certverify)"); return -1; } MG_VERBOSE(("certificate verification successful (EC)")); } else { // From // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml: // 0805 = rsa_pss_rsae_sha384 // 0806 = rsa_pss_rsae_sha512 // 0807 = ed25519 // 0808 = ed448 // 0809 = rsa_pss_pss_sha256 // 080A = rsa_pss_pss_sha384 // 080B = rsa_pss_pss_sha512 MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", sigalg, siglen)); return -1; } } 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; unsigned char *recv_buf; if (mg_tls_recv_record(c) < 0) { return -1; } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] != MG_TLS_FINISHED) { mg_error(c, "expected server finished but got msg 0x%02x", recv_buf[0]); return -1; } mg_tls_drop_message(c); return 0; } static bool mg_tls_client_send_finish(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; 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); return mg_tls_encrypt(c, finish, sizeof(finish), MG_TLS_HANDSHAKE); } static bool 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: if (!mg_tls_client_send_hello(c)) return false; 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_recv_cert(c, true) < 0) break; tls->state = MG_TLS_STATE_CLIENT_WAIT_CV; // Fallthrough case MG_TLS_STATE_CLIENT_WAIT_CV: if (mg_tls_recv_cert_verify(c) < 0) break; tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH; // Fallthrough case MG_TLS_STATE_CLIENT_WAIT_FINISH: if (mg_tls_client_recv_finish(c) < 0) break; if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth // generate application keys at this point, keep using handshake keys struct tls_enc hs_keys = tls->enc; mg_tls_generate_application_keys(c); tls->app_keys = tls->enc; tls->enc = hs_keys; if (!mg_tls_send_cert(c, true) || !mg_tls_send_cert_verify(c, true) || !mg_tls_client_send_finish(c)) return false; tls->enc = tls->app_keys; } else { if (!mg_tls_client_send_finish(c)) return false; mg_tls_generate_application_keys(c); } tls->state = MG_TLS_STATE_CLIENT_CONNECTED; c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; default: mg_error(c, "unexpected client state: %d", tls->state); break; } return true; } static bool 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) break; if (!mg_tls_server_send_hello(c)) return false; mg_tls_generate_handshake_keys(c); if (!mg_tls_server_send_ext(c)) return false; if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false; if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || !mg_tls_server_send_finish(c)) return false; if (tls->is_twoway) { // generate application keys at this point, keep using handshake keys struct tls_enc hs_keys = tls->enc; mg_tls_generate_application_keys(c); tls->app_keys = tls->enc; tls->enc = hs_keys; tls->state = MG_TLS_STATE_SERVER_WAIT_CERT; break; } tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; // fallthrough case MG_TLS_STATE_SERVER_NEGOTIATED: if (mg_tls_server_recv_finish(c) < 0) break; if (tls->is_twoway) { // use previously generated keys tls->enc = tls->app_keys; } else { // generate keys now mg_tls_generate_application_keys(c); } tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; break; case MG_TLS_STATE_SERVER_WAIT_CERT: if (mg_tls_recv_cert(c, false) < 0) break; tls->state = MG_TLS_STATE_SERVER_WAIT_CV; // Fallthrough case MG_TLS_STATE_SERVER_WAIT_CV: if (mg_tls_recv_cert_verify(c) < 0) break; tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; break; default: mg_error(c, "unexpected server state: %d", tls->state); break; } return true; } void mg_tls_handshake(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; long n; bool res; if (c->is_closing) return; // we don't clear rx buf, so ignore what's left if (tls->is_sntp_pending) return; if (c->is_client) { // will clear is_hs when sending last chunk res = mg_tls_client_handshake(c); } else { res = mg_tls_server_handshake(c); } if (!res) { mg_error(c, "TLS OOM"); return; } 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 last chunk fails to be sent, it will be sent with first app data, // otherwise, it needs to be flushed } static int mg_rsa_parse_der_int(const uint8_t **p, const uint8_t *end, struct mg_str *out) { const uint8_t *start = *p, *value_start, *value_end; uint8_t i; uint32_t len; if (end - start < 2) { MG_VERBOSE(("DER INT: not enough bytes (%d < 2)", (int) (end - start))); return -1; } if (start[0] != 0x02) { MG_VERBOSE(("DER INT: expected 0x02, got 0x%02x", start[0])); return -1; } len = start[1]; *p = start + 2; if (len > 0x7F) { // Long form length uint8_t len_bytes = len & 0x7F; MG_VERBOSE(("DER INT: long form, %d length bytes", len_bytes)); if (len_bytes == 0 || len_bytes > 4 || (size_t) (end - *p) < len_bytes) { MG_VERBOSE(("DER INT: not enough bytes for length")); return -1; } len = 0; for (i = 0; i < len_bytes; i++) { len = (len << 8) | (*p)[i]; } *p += len_bytes; } MG_VERBOSE(("DER INT: length=%u, remaining=%d", len, (int) (end - *p))); if ((size_t) (end - *p) < len) { MG_VERBOSE(("DER INT: length exceeds remaining bytes")); return -1; } // The encoded length tells us how many bytes to consume from the stream value_start = *p; value_end = *p + len; // Skip leading zero byte if present (for positive numbers) // This doesn't change how many bytes we consume, just what we expose if (len > 0 && (*p)[0] == 0x00) { (*p)++; len--; } out->buf = (char *) *p; out->len = len; // Advance pointer by the ORIGINAL encoded length, not the adjusted length *p = value_end; MG_VERBOSE(("DER INT: parsed %u bytes (skipped zero=%d)", len, (size_t) (value_end - value_start) != (size_t) len ? 1 : 0)); return 0; } // RFC 5915 ECPrivateKey ::= SEQUENCE { // version INTEGER { ecPrivkeyVer1(1) }, // privateKey OCTET STRING, // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, // publicKey [1] BIT STRING OPTIONAL // } static int mg_parse_ec_private_key(const uint8_t *der, size_t dersz, uint8_t *ec_key) { struct mg_der_tlv root, version, private_key_octets; if (mg_der_parse((uint8_t *) der, dersz, &root) < 0 || root.type != 0x30) { MG_ERROR(("EC private key: invalid SEQUENCE")); return -1; } if (mg_der_next(&root, &version) <= 0 || version.type != 0x02) { MG_ERROR(("EC private key: invalid version")); return -1; } if (mg_der_next(&root, &private_key_octets) <= 0 || private_key_octets.type != 0x04) { MG_ERROR(("EC private key: invalid privateKey OCTET STRING")); return -1; } if (private_key_octets.len != 32) { MG_ERROR( ("EC private key: expected 32 bytes, got %u", private_key_octets.len)); return -1; } memcpy(ec_key, private_key_octets.value, 32); return 0; } // Parse RSA private key from DER format // RSAPrivateKey ::= SEQUENCE { // version INTEGER (0), // modulus INTEGER, -- n // publicExponent INTEGER, -- e // privateExponent INTEGER, -- d // prime1 INTEGER, -- p // prime2 INTEGER, -- q // exponent1 INTEGER, -- dP = d mod (p-1) // exponent2 INTEGER, -- dQ = d mod (q-1) // coefficient INTEGER, -- qInv = (inverse of q) mod p // } static int mg_rsa_parse_key(const uint8_t *der, size_t dersz, struct mg_rsa_key *key) { const uint8_t *p = der; const uint8_t *end = der + dersz; uint32_t seq_len; struct mg_str version; memset(key, 0, sizeof(*key)); // Parse outer SEQUENCE if (end - p < 2) { MG_ERROR(("RSA key too short for SEQUENCE header")); return -1; } if (p[0] != 0x30) { MG_ERROR(("RSA key: expected SEQUENCE (0x30), got 0x%02x", p[0])); return -1; } seq_len = p[1]; p += 2; if (seq_len > 0x7F) { // Long form length uint8_t i, len_bytes = seq_len & 0x7F; MG_VERBOSE(("Long form length: %d bytes", len_bytes)); if (len_bytes == 0 || len_bytes > 4 || (size_t) (end - p) < len_bytes) { MG_ERROR(("Not enough bytes for long form length")); return -1; } seq_len = 0; for (i = 0; i < len_bytes; i++) { seq_len = (seq_len << 8) | p[i]; } p += len_bytes; } MG_VERBOSE( ("SEQUENCE length: %u, total DER size: %u", seq_len, (unsigned) dersz)); if ((size_t) (end - p) < seq_len) { MG_ERROR(("SEQUENCE length exceeds buffer")); return -1; } end = p + seq_len; // Adjust end to sequence boundary // Parse version (should be 0) MG_VERBOSE(("Before version: offset=%d, bytes: %02x %02x %02x %02x", (int) (p - der), p < end ? p[0] : 0xFF, p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF, p + 3 < end ? p[3] : 0xFF)); if (mg_rsa_parse_der_int(&p, end, &version) < 0) { MG_ERROR(("Failed to parse version")); return -1; } MG_DEBUG(("Version: %d byte(s), value=%d, offset now=%d", (int) version.len, version.len > 0 ? (int) (unsigned char) version.buf[0] : -1, (int) (p - der))); // Parse the 8 components: n, e, d, p, q, dP, dQ, qInv MG_VERBOSE(("Before n: offset=%d, bytes: %02x %02x %02x %02x %02x %02x", (int) (p - der), p < end ? p[0] : 0xFF, p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF, p + 3 < end ? p[3] : 0xFF, p + 4 < end ? p[4] : 0xFF, p + 5 < end ? p[5] : 0xFF)); if (mg_rsa_parse_der_int(&p, end, &key->n) < 0) { MG_ERROR(("Failed to parse n (modulus)")); return -1; } MG_VERBOSE(("Parsed n: %d bytes, offset now=%d, consumed=%d bytes total", (int) key->n.len, (int) (p - der), (int) (p - der))); MG_VERBOSE((" First 8 bytes of n: %02x %02x %02x %02x %02x %02x %02x %02x", key->n.len > 0 ? (unsigned char) key->n.buf[0] : 0xFF, key->n.len > 1 ? (unsigned char) key->n.buf[1] : 0xFF, key->n.len > 2 ? (unsigned char) key->n.buf[2] : 0xFF, key->n.len > 3 ? (unsigned char) key->n.buf[3] : 0xFF, key->n.len > 4 ? (unsigned char) key->n.buf[4] : 0xFF, key->n.len > 5 ? (unsigned char) key->n.buf[5] : 0xFF, key->n.len > 6 ? (unsigned char) key->n.buf[6] : 0xFF, key->n.len > 7 ? (unsigned char) key->n.buf[7] : 0xFF)); MG_VERBOSE((" Next bytes after n: %02x %02x %02x %02x %02x %02x", p < end ? p[0] : 0xFF, p + 1 < end ? p[1] : 0xFF, p + 2 < end ? p[2] : 0xFF, p + 3 < end ? p[3] : 0xFF, p + 4 < end ? p[4] : 0xFF, p + 5 < end ? p[5] : 0xFF)); if (mg_rsa_parse_der_int(&p, end, &key->e) < 0) { MG_ERROR(("Failed to parse e (public exponent), bytes remaining: %d", (int) (end - p))); if (end - p >= 4) { MG_ERROR((" Next 4 bytes: %02x %02x %02x %02x", p[0], p[1], p[2], p[3])); } return -1; } if (mg_rsa_parse_der_int(&p, end, &key->d) < 0) { MG_ERROR(("Failed to parse d (private exponent)")); return -1; } if (mg_rsa_parse_der_int(&p, end, &key->p) < 0) { MG_ERROR(("Failed to parse p (prime1)")); return -1; } if (mg_rsa_parse_der_int(&p, end, &key->q) < 0) { MG_ERROR(("Failed to parse q (prime2)")); return -1; } if (mg_rsa_parse_der_int(&p, end, &key->dP) < 0) { MG_ERROR(("Failed to parse dP (exponent1)")); return -1; } if (mg_rsa_parse_der_int(&p, end, &key->dQ) < 0) { MG_ERROR(("Failed to parse dQ (exponent2)")); return -1; } if (mg_rsa_parse_der_int(&p, end, &key->qInv) < 0) { MG_ERROR(("Failed to parse qInv (coefficient)")); return -1; } MG_VERBOSE(("Successfully parsed RSA key")); return 0; } // PKCS#8 PrivateKeyInfo ::= SEQUENCE { // version INTEGER, // privateKeyAlgorithm AlgorithmIdentifier, // privateKey OCTET STRING, // attributes [0] Attributes OPTIONAL // } // AlgorithmIdentifier ::= SEQUENCE { // algorithm OBJECT IDENTIFIER, // parameters ANY OPTIONAL // } static int mg_parse_pkcs8_key(const uint8_t *der, size_t dersz, struct mg_rsa_key *rsa, uint8_t *ec_key) { struct mg_der_tlv root, version, alg_id, private_key_octets; struct mg_der_tlv alg_oid, alg_params; if (mg_der_parse((uint8_t *) der, dersz, &root) < 0 || root.type != 0x30) { MG_ERROR(("PKCS#8: invalid PrivateKeyInfo SEQUENCE")); return -1; } if (mg_der_next(&root, &version) <= 0 || version.type != 0x02) { MG_ERROR(("PKCS#8: invalid version")); return -1; } if (mg_der_next(&root, &alg_id) <= 0 || alg_id.type != 0x30) { MG_ERROR(("PKCS#8: invalid AlgorithmIdentifier SEQUENCE")); return -1; } if (mg_der_next(&alg_id, &alg_oid) <= 0 || alg_oid.type != 0x06) { MG_ERROR(("PKCS#8: invalid algorithm OID")); return -1; } if (mg_der_next(&root, &private_key_octets) <= 0 || private_key_octets.type != 0x04) { MG_ERROR(("PKCS#8: invalid privateKey OCTET STRING")); return -1; } if (alg_oid.len == sizeof(mg_rsa_oid) && memcmp(alg_oid.value, mg_rsa_oid, sizeof(mg_rsa_oid)) == 0) { struct mg_rsa_key rsa_key; if (rsa == NULL) return -1; if (mg_rsa_parse_key(private_key_octets.value, private_key_octets.len, &rsa_key) < 0) { MG_ERROR(("PKCS#8: failed to parse inner RSA key")); return -1; } *rsa = rsa_key; return 0; } else if (alg_oid.len == sizeof(mg_ec_public_key_oid) && memcmp(alg_oid.value, mg_ec_public_key_oid, sizeof(mg_ec_public_key_oid)) == 0) { if (ec_key == NULL) return -1; if (mg_der_next(&alg_id, &alg_params) <= 0 || alg_params.type != 0x06) { MG_ERROR(("PKCS#8: invalid EC parameters OID")); return -1; } if (alg_params.len != sizeof(mg_secp256r1_oid) || memcmp(alg_params.value, mg_secp256r1_oid, sizeof(mg_secp256r1_oid)) != 0) { MG_ERROR(("PKCS#8: unsupported EC curve (only secp256r1 supported)")); return -1; } return mg_parse_ec_private_key(private_key_octets.value, private_key_octets.len, ec_key); } else { MG_ERROR(("PKCS#8: unsupported algorithm")); return -1; } } 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[6]; // number of wildcards + 1 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 *) mg_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) { mg_free(s); return -1; } der->buf = s; der->len = m; return 0; } static int mg_parse_pem_certs(const struct mg_str pem, struct mg_str **ders) { int count = 0; struct mg_str *certs = NULL; const char *p = pem.buf; const char *end = pem.buf + pem.len; const char *begin_marker = "-----BEGIN CERTIFICATE-----"; const char *end_marker = "-----END CERTIFICATE-----"; size_t begin_len = strlen(begin_marker); size_t end_len = strlen(end_marker); while (p < end) { const char *s, *begin = NULL, *finish = NULL; struct mg_str cert_pem, cert_der, *new_certs; int i; for (s = p; s <= end - (int) begin_len; s++) { if (memcmp(s, begin_marker, begin_len) == 0) { begin = s; break; } } if (begin == NULL) break; for (s = begin + begin_len; s <= end - (int) end_len; s++) { if (memcmp(s, end_marker, end_len) == 0) { finish = s + end_len; break; } } if (finish == NULL) { for (i = 0; i < count; i++) mg_free((void *) certs[i].buf); mg_free(certs); return -1; } cert_pem = mg_str_n(begin, (size_t) (finish - begin)); if (mg_parse_pem(cert_pem, mg_str_s("CERTIFICATE"), &cert_der) < 0) { for (i = 0; i < count; i++) mg_free((void *) certs[i].buf); mg_free(certs); return -1; } new_certs = (struct mg_str *) mg_calloc((size_t) count + 1, sizeof(*new_certs)); if (new_certs == NULL) { mg_free((void *) cert_der.buf); for (i = 0; i < count; i++) mg_free((void *) certs[i].buf); mg_free(certs); return -1; } if (count > 0) { memmove(new_certs, certs, (size_t) count * sizeof(struct mg_str)); mg_free(certs); } certs = new_certs; certs[count++] = cert_der; p = finish; } *ders = certs; return count; } size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) { struct mg_str der = mg_str_n(NULL, 0); size_t n = 0; if (buf == NULL || len < 32) return 0; // current mg_parse_ec_private_key() only handles 32-byte keys if (mg_parse_pem(key, mg_str_s("EC PRIVATE KEY"), &der) == 0) { if (mg_parse_ec_private_key((uint8_t *) der.buf, der.len, buf) == 0) n = 32; } else if (mg_parse_pem(key, mg_str_s("PRIVATE KEY"), &der) == 0) { if (mg_parse_pkcs8_key((uint8_t *) der.buf, der.len, NULL, buf) == 0) n = 32; } mg_free((void *) der.buf); return n; } static void sync_time_cancel(struct mg_connection *c) { c->fn = NULL; c->is_closing = 1; } static void sync_time_cb(struct mg_connection *c, int ev, void *ev_data) { if (ev == MG_EV_SNTP_TIME || (ev == MG_EV_CLOSE)) { struct mg_connection *tlsc = (struct mg_connection *) c->fn_data; struct tls_data *tls = (struct tls_data *) tlsc->tls; sync_time_cancel(c); // c = tls->timec tls->timec = NULL; tls->is_sntp_pending = false; if (ev == MG_EV_SNTP_TIME) mg_tls_handshake(tlsc); if (ev == MG_EV_CLOSE) mg_error(tlsc, "time sync failed"); } (void) ev_data; } 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 *) mg_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"); return; } strncpy((char *) tls->hostname, opts->name.buf, sizeof(tls->hostname) - 1); tls->hostname[opts->name.len] = 0; } // server CA certificate; parse PEM [bundle] or DER if (opts->ca.len > 0) { struct mg_str *all_certs = NULL; int cert_count = mg_parse_pem_certs(opts->ca, &all_certs); if (cert_count > 1 && c->is_client) { // use bundle for clients only tls->ca_bundle_len = (size_t) cert_count; tls->ca_bundle_der = all_certs; MG_VERBOSE(("%d-cert bundle", cert_count)); } else if (cert_count > 0) { tls->ca_der.buf = all_certs[0].buf; tls->ca_der.len = all_certs[0].len; mg_free(all_certs); } else { // parse again for a possible DER (or a truncated begin string) if (mg_parse_pem(opts->ca, mg_str_s("CERTIFICATE"), &tls->ca_der) < 0) { MG_ERROR(("Failed to load CA certificate")); goto xit; } } // ca_bundle_len != 0 && ca_der.len = 0 => bundle if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth } if (opts->cert.buf == NULL || opts->cert.len == 0) { MG_VERBOSE(("No certificate provided")); goto xit; } // parse PEM or DER certificate { struct mg_str *all_certs = NULL; int cert_count = mg_parse_pem_certs(opts->cert, &all_certs); if (cert_count > 0) { tls->cert_der.buf = all_certs[0].buf; tls->cert_der.len = all_certs[0].len; if (cert_count > 1) { tls->chain_len = (size_t) cert_count; tls->chain_der = all_certs; } else { mg_free(all_certs); } } else { // parse again for a possible DER (or a truncated begin string) 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 || opts->key.len == 0) { mg_error(c, "Certificate provided without a private key"); return; } if (mg_parse_pem(opts->key, mg_str_s("EC PRIVATE KEY"), &key) == 0) { // expect ASN.1 SEQUENCE=[INTEGER=1, BITSTRING of 32 bytes, ...] // 30 nn 02 01 01 04 20 [key] ... if (key.len < (2 + 5 + 32) || key.buf[0] != 0x30 || (key.buf[1] & 0x80) != 0 || memcmp(key.buf + 2, "\x02\x01\x01\x04\x20", 5) != 0) { mg_error(c, "EC private key: invalid ASN.1"); } else { memmove(tls->ec_key, key.buf + 7, 32); } mg_free((void *) key.buf); } else if (mg_parse_pem(opts->key, mg_str_s("RSA PRIVATE KEY"), &key) == 0) { struct mg_rsa_key rsa_key; // RSA private key found, store it for later use tls->rsa_key_der = key; // parse and validate the key structure // we keep the DER buffer, rsa_key just points into it if (mg_rsa_parse_key((const uint8_t *) key.buf, key.len, &rsa_key) < 0) { MG_ERROR(("Failed to parse RSA private key structure")); mg_free((void *) key.buf); tls->rsa_key_der = mg_str_n(NULL, 0); mg_error(c, "Invalid RSA private key format"); return; } MG_VERBOSE(("RSA key components:")); MG_VERBOSE((" n (modulus): %d bytes", (int) rsa_key.n.len)); MG_VERBOSE((" e (pubexp): %d bytes", (int) rsa_key.e.len)); MG_VERBOSE((" d (privexp): %d bytes", (int) rsa_key.d.len)); MG_VERBOSE((" p (prime1): %d bytes", (int) rsa_key.p.len)); MG_VERBOSE((" q (prime2): %d bytes", (int) rsa_key.q.len)); MG_VERBOSE((" dP: %d bytes", (int) rsa_key.dP.len)); MG_VERBOSE((" dQ: %d bytes", (int) rsa_key.dQ.len)); MG_VERBOSE((" qInv: %d bytes", (int) rsa_key.qInv.len)); // Copy parsed RSA key components to tls->rsa for signing operations tls->rsa = rsa_key; } else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) { if (mg_parse_pkcs8_key((const uint8_t *) key.buf, key.len, &tls->rsa, tls->ec_key) == 0) { if (tls->rsa.n.len > 0) { tls->rsa_key_der = key; MG_INFO(("Parsed PKCS#8 RSA private key: %d bytes", (int) key.len)); } else { mg_free((void *) key.buf); MG_VERBOSE(("Parsed PKCS#8 EC private key")); } } else { mg_free((void *) key.buf); mg_error(c, "Unsupported PKCS#8 private key format, algorithm, or curve"); return; } } else { mg_error( c, "Expected EC PRIVATE KEY, RSA PRIVATE KEY, or PRIVATE KEY (PKCS#8)"); return; } xit: if (mg_boot_timestamp_ms == 0 && !tls->skip_verification && (tls->ca_bundle_len > 0 || tls->ca_der.len > 0)) { struct mg_connection *timec; timec = mg_sntp_connect(c->mgr, NULL, sync_time_cb, c); if (timec == NULL) { mg_error(c, "time sync failed"); return; } tls->is_sntp_pending = true; tls->timec = timec; } } void mg_tls_free(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; size_t i; if (tls != NULL) { if (tls->timec != NULL) { sync_time_cancel(tls->timec); tls->timec = NULL; } mg_iobuf_free(&tls->send); if (tls->chain_der != NULL) { for (i = 0; i < tls->chain_len; i++) { mg_free((void *) tls->chain_der[i].buf); } mg_free(tls->chain_der); } else { mg_free((void *) tls->cert_der.buf); } if (tls->ca_bundle_der != NULL) { for (i = 0; i < tls->ca_bundle_len; i++) { mg_free((void *) tls->ca_bundle_der[i].buf); } mg_free(tls->ca_bundle_der); } mg_free((void *) tls->ca_der.buf); mg_free((void *) tls->rsa_key_der.buf); mg_bzero((unsigned char *) tls, sizeof(*tls)); // Wipe keys and secrets } mg_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; bool was_throttled = c->is_tls_throttled; // see #3074 if (!was_throttled) { // encrypt new data if (len > MG_IO_SIZE) len = MG_IO_SIZE; if (len > 16384) len = 16384; if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA)) return 0; // returning 0 means an OOM condition (iobuf couldn't resize), // yet this is so far recoverable, let the caller decide } // else, resend outstanding encrypted data in tls->send 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 last chunk fails to be sent, it needs to be flushed c->is_tls_throttled = (tls->send.len > 0 && n == MG_IO_WAIT); MG_VERBOSE(("%lu %ld %ld %ld %c %c", c->id, (long) len, (long) tls->send.len, n, was_throttled ? 'T' : 't', c->is_tls_throttled ? 'T' : 't')); if (n == MG_IO_ERR) return MG_IO_ERR; if (was_throttled) return MG_IO_WAIT; // sent throttled data instead return (long) len; // return len even when throttled, already encripted that } 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; unsigned char *recv_buf; size_t minlen; for (;;) { r = mg_tls_recv_record(c); if (r < 0) return r; if (tls->content_type == MG_TLS_APP_DATA) break; tls->recv_len = 0; mg_tls_drop_record(c); } if (buf == NULL || len == 0) return 0L; recv_buf = &c->rtls.buf[tls->recv_offset]; minlen = len < tls->recv_len ? len : tls->recv_len; memmove(buf, recv_buf, minlen); tls->recv_offset += 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) { struct tls_data *tls = (struct tls_data *) c->tls; return tls != NULL ? tls->recv_len : 0; } long mg_tls_flush(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; long n = 0; 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) return n; c->is_tls_throttled = tls->send.len > 0; return c->is_tls_throttled ? MG_IO_WAIT : 0; } void mg_tls_ctx_init(struct mg_mgr *mgr) { (void) mgr; } void mg_tls_ctx_free(struct mg_mgr *mgr) { (void) mgr; } #endif