diff --git a/mongoose.c b/mongoose.c index d87f81d0..2a71d65c 100644 --- a/mongoose.c +++ b/mongoose.c @@ -12474,6 +12474,19 @@ int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, #if MG_TLS == MG_TLS_BUILTIN +// 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 @@ -12490,6 +12503,8 @@ int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, #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 { @@ -12524,6 +12539,17 @@ struct tls_enc { 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 @@ -12541,13 +12567,17 @@ struct tls_data { uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t x25519_sec[32]; // x25519 secret between the handshake 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 - struct mg_str cert_der; // certificate in DER format - struct mg_str ca_der; // CA certificate - uint8_t ec_key[32]; // EC private key - char hostname[254]; // matching hostname + 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 + struct mg_str cert_der; // certificate in DER format + struct mg_str ca_der; // CA certificate + 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 uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to @@ -12952,7 +12982,20 @@ static int mg_tls_recv_record(struct mg_connection *c) { } 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 - MG_INFO(("TLS ALERT packet received")); + if (rio->len >= 7) { + uint8_t level = rio->buf[5], desc = rio->buf[6]; + 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" + : "unknown")); + } else { + MG_INFO(("TLS ALERT packet received (short)")); + } mg_tls_drop_record(c); } else { mg_error(c, "unexpected packet"); @@ -13162,8 +13205,7 @@ static bool mg_tls_server_send_ext(struct mg_connection *c) { // signature algorithms we actually support: // rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 static const uint8_t secp256r1_sig_algs[12] = { - 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01 -}; + 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01}; static bool mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; @@ -13186,28 +13228,47 @@ 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 n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0); - uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n); + size_t total_size = tls->cert_der.len + 5; + for (size_t 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; + } + uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + total_size); bool res; if (cert == NULL) return false; cert[0] = MG_TLS_CERTIFICATE; // handshake header - MG_STORE_BE24(cert + 1, n + 9); - cert[4] = 0; // request context - MG_STORE_BE24(cert + 5, n + 5); // 3 bytes: cert (s) length - MG_STORE_BE24(cert + 8, tls->cert_der.len); // 3 bytes: first cert len + 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 + size_t offset = 8; + MG_STORE_BE24(cert + offset, tls->cert_der.len); // 3 bytes: first cert len + offset += 3; // bytes 11+ are certificate in DER format - memmove(cert + 11, tls->cert_der.buf, tls->cert_der.len); - MG_STORE_BE16(cert + 11 + tls->cert_der.len, - 0); // certificate extensions (none) - if (send_ca) { - size_t offset = 13 + tls->cert_der.len; - MG_STORE_BE24(cert + offset, tls->ca_der.len); // 3 bytes: CA cert length - memmove(cert + offset + 3, tls->ca_der.buf, - tls->ca_der.len); // CA cert data - MG_STORE_BE16(cert + 11 + n, 0); // certificate extensions (none) + 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 (size_t 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; } - mg_sha256_update(&tls->sha256, cert, 13 + n); - res = mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE); + 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; } @@ -13234,41 +13295,253 @@ static void finish_SHA256(const MG_UECC_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 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); + size_t chunk_len = mask_len - generated; + size_t chunk = (chunk_len < sizeof(digest) ? chunk_len : sizeof(digest)); + memmove(mask + generated, digest, chunk); + generated += chunk; + counter++; + } +} + +static unsigned mg_tls_rsa_bits(const struct mg_str *n) { + size_t i = 0; + unsigned bits = 0; + while (i < n->len && n->buf[i] == 0) i++; + if (i == n->len) return 0; + bits = (unsigned) ((n->len - i) * 8); + { + uint8_t byte = (uint8_t) n->buf[i]; + while ((byte & 0x80U) == 0) { + bits--; + 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; + size_t saltlen = hashlen; + size_t dblen; + size_t pslen; + 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 (size_t 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) { +#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")); + + size_t nlen = tls->rsa.n.len; + + int 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) { + 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 + // Standard RSA: s = em^d mod n + memset(sig, 0, tls->rsa.n.len); + int ret = mg_rsa_mod_pow((const uint8_t *) tls->rsa.n.buf, tls->rsa.n.len, + (const uint8_t *) tls->rsa.d.buf, tls->rsa.d.len, em, + emlen, sig, tls->rsa.n.len); + 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[tls->rsa.n.len - 4], sig[tls->rsa.n.len - 3], + sig[tls->rsa.n.len - 2], sig[tls->rsa.n.len - 1])); + } + return ret == 0; +#endif +} + static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; - // server certificate verify packet - uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; - size_t sigsz, verifysz = 0; - uint8_t hash[32] = {0}, tmp[2 * 32 + 64] = {0}; - struct SHA256_HashContext ctx = { - {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, - {{0}, 0, 0, {0}}}; - int neg1, neg2; - uint8_t sig[64] = {0}; + uint8_t hash[32] = {0}; mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash, is_client); - mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig, - mg_uecc_secp256r1()); + if (tls->rsa.n.len > 0 && tls->rsa.d.len > 0) { + // RSA certificate verify packet + size_t emlen = tls->rsa.n.len; + size_t verifysz = 8U + emlen; + uint8_t em[512]; // Max for 4096-bit RSA + uint8_t verify[520]; // 8 + 512 max - 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); + // Check bounds + if (emlen > sizeof(em) || verifysz > sizeof(verify)) { + MG_ERROR(("RSA key too large for static buffers")); + return false; + } - sigsz = (size_t) (70 + neg1 + neg2); - verifysz = 8U + sigsz; - verify[3] = (uint8_t) (sigsz + 4); - verify[7] = (uint8_t) sigsz; + if (!mg_tls_pss_encode(hash, sizeof(hash), &tls->rsa.n, em)) { + MG_ERROR(("Failed PSS encode")); + return false; + } - mg_sha256_update(&tls->sha256, verify, verifysz); - return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE); + // 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, (uint32_t) (emlen + 4)); + MG_STORE_BE16(verify + 4, 0x0804); + MG_STORE_BE16(verify + 6, (uint16_t) 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) { @@ -13675,16 +13948,17 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz, return -1; } while (mg_der_next(&field, &name) > 0) { - if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address + if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address MG_DEBUG(("Found SAN, IP: %M", mg_print_ip4, name.value)); - if (!server_ip->is_ip6 && *((uint32_t *) name.value) == server_ip->addr.ip4) + if (!server_ip->is_ip6 && + *((uint32_t *) name.value) == server_ip->addr.ip4) return 1; // and matches the one we're connected to - } else { // this is a text SAN + } else { // this is a text SAN MG_DEBUG(("Found SAN, (%u): %.*s", name.type, name.len, name.value)); if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len), NULL)) return 1; // and matches the host name - } // TODO(): add IPv6 comparison, more items ? + } // TODO(): add IPv6 comparison, more items ? } return -1; } @@ -14055,7 +14329,9 @@ static bool mg_tls_server_handshake(struct mg_connection *c) { 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 (!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; @@ -14096,7 +14372,7 @@ 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 (c->is_closing) return; // we don't clear rx buf, so ignore what's left if (c->is_client) { // will clear is_hs when sending last chunk res = mg_tls_client_handshake(c); @@ -14114,6 +14390,310 @@ void mg_tls_handshake(struct mg_connection *c) { // 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; + 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 (end - *p < len_bytes) { + MG_VERBOSE(("DER INT: not enough bytes for length")); + return -1; + } + len = 0; + for (uint8_t 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 (end - *p < (long) len) { + MG_VERBOSE(("DER INT: length exceeds remaining bytes")); + return -1; + } + + // The encoded length tells us how many bytes to consume from the stream + const uint8_t *value_start = *p; + const uint8_t *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, + value_end - value_start != 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 +// } +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)); + + // Debug: show first few bytes + MG_DEBUG( + ("RSA key DER first 16 bytes: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x", + der[0], der[1], der[2], der[3], der[4], der[5], der[6], der[7], der[8], + der[9], der[10], der[11], der[12], der[13], der[14], der[15])); + + // 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 len_bytes = seq_len & 0x7F; + MG_DEBUG(("Long form length: %d bytes", len_bytes)); + if (end - p < len_bytes) { + MG_ERROR(("Not enough bytes for long form length")); + return -1; + } + seq_len = 0; + for (uint8_t i = 0; i < len_bytes; i++) { + seq_len = (seq_len << 8) | p[i]; + } + p += len_bytes; + } + + MG_DEBUG( + ("SEQUENCE length: %u, total DER size: %u", seq_len, (unsigned) dersz)); + + if (end - p < (long) 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_DEBUG(("Before version: offset=%d, bytes: %02x %02x %02x %02x", + (int) (p - der), p[0], p[1], p[2], p[3])); + 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_DEBUG(("Before n: offset=%d, bytes: %02x %02x %02x %02x %02x %02x", + (int) (p - der), p[0], p[1], p[2], p[3], p[4], p[5])); + if (mg_rsa_parse_der_int(&p, end, &key->n) < 0) { + MG_ERROR(("Failed to parse n (modulus)")); + return -1; + } + MG_DEBUG(("Parsed n: %d bytes, offset now=%d, consumed=%d bytes total", + (int) key->n.len, (int) (p - der), (int) (p - der))); + MG_DEBUG((" First 8 bytes of n: %02x %02x %02x %02x %02x %02x %02x %02x", + (unsigned char) key->n.buf[0], (unsigned char) key->n.buf[1], + (unsigned char) key->n.buf[2], (unsigned char) key->n.buf[3], + (unsigned char) key->n.buf[4], (unsigned char) key->n.buf[5], + (unsigned char) key->n.buf[6], (unsigned char) key->n.buf[7])); + MG_DEBUG((" 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_DEBUG(("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 tls_data *tls) { + 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 (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; + } + tls->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 (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, tls->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; @@ -14147,6 +14727,70 @@ static int mg_parse_pem(const struct mg_str pem, const struct mg_str label, 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 *begin = NULL; + for (const char *s = p; s <= end - (int) begin_len; s++) { + if (memcmp(s, begin_marker, begin_len) == 0) { + begin = s; + break; + } + } + if (begin == NULL) break; + + const char *finish = NULL; + for (const char *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 (int i = 0; i < count; i++) mg_free((void *) certs[i].buf); + mg_free(certs); + return -1; + } + + struct mg_str cert_pem = mg_str_n(begin, (size_t) (finish - begin)); + struct mg_str cert_der; + + if (mg_parse_pem(cert_pem, mg_str_s("CERTIFICATE"), &cert_der) < 0) { + for (int i = 0; i < count; i++) mg_free((void *) certs[i].buf); + mg_free(certs); + return -1; + } + + struct mg_str *new_certs = + (struct mg_str *) mg_calloc((size_t) count + 1, sizeof(struct mg_str)); + if (new_certs == NULL) { + mg_free((void *) cert_der.buf); + for (int 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; +} + void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { struct mg_str key; struct tls_data *tls = @@ -14190,9 +14834,23 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { } // parse PEM or DER certificate - if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->cert_der) < 0) { - MG_ERROR(("Failed to load certificate")); - return; + 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 { + 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 @@ -14217,10 +14875,51 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { } 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) { + // RSA private key found, store it for later use + tls->rsa_key_der = key; + MG_INFO(("Parsed RSA private key: %d bytes", (int) key.len)); + + // parse and validate the key structure + // we keep the DER buffer, rsa_key just points into it + struct mg_rsa_key rsa_key; + 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) { - mg_error(c, "PKCS8 private key format is not supported"); + if (mg_parse_pkcs8_key((const uint8_t *) key.buf, key.len, tls) == 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_INFO(("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 or PRIVATE KEY"); + mg_error( + c, "Expected EC PRIVATE KEY, RSA PRIVATE KEY, or PRIVATE KEY (PKCS#8)"); } } @@ -14228,8 +14927,16 @@ void mg_tls_free(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; if (tls != NULL) { mg_iobuf_free(&tls->send); - mg_free((void *) tls->cert_der.buf); + if (tls->chain_der != NULL) { + for (size_t 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); + } mg_free((void *) tls->ca_der.buf); + mg_free((void *) tls->rsa_key_der.buf); } mg_free(c->tls); c->tls = NULL; @@ -14245,7 +14952,7 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { 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 + } // 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); @@ -17796,7 +18503,6 @@ NS_INTERNAL bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) { #endif } -#if 0 /** * @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts. * @@ -17809,6 +18515,7 @@ NS_INTERNAL bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) { * @param qInv [in] CRT's qInv bigint * @return The result of the CRT operation */ +#if 1 NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, bigint *p, bigint *q, bigint *qInv) { bigint *m1, *m2, *h; @@ -17835,7 +18542,6 @@ NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, } #endif - // Proper lib usage: // - BI_CTX *c = bi_initialize() // - allocate bigints (e.g.: calling bi_import(), int_to_bi(), ...) @@ -17864,6 +18570,50 @@ int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t return 0; } +int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, + const uint8_t *dP, size_t dP_len, + const uint8_t *dQ, size_t dQ_len, + const uint8_t *p, size_t p_len, + const uint8_t *q, size_t q_len, + const uint8_t *qInv, size_t qInv_len, + uint8_t *signature, size_t sig_len) { + BI_CTX *ctx; + bigint *em_bi, *dP_bi, *dQ_bi, *p_bi, *q_bi, *qInv_bi, *result_bi; + int ret = -1; + + ctx = bi_initialize(); + if (ctx == NULL) { + return -1; + } + + em_bi = bi_import(ctx, em, em_len); + dP_bi = bi_import(ctx, dP, dP_len); + dQ_bi = bi_import(ctx, dQ, dQ_len); + p_bi = bi_import(ctx, p, p_len); + q_bi = bi_import(ctx, q, q_len); + qInv_bi = bi_import(ctx, qInv, qInv_len); + + if (em_bi == NULL || dP_bi == NULL || dQ_bi == NULL || + p_bi == NULL || q_bi == NULL || qInv_bi == NULL) { + goto cleanup; + } + + bi_set_mod(ctx, bi_clone(ctx, p_bi), BIGINT_P_OFFSET); + bi_set_mod(ctx, bi_clone(ctx, q_bi), BIGINT_Q_OFFSET); + + result_bi = bi_crt(ctx, em_bi, dP_bi, dQ_bi, p_bi, q_bi, qInv_bi); + if (result_bi == NULL) { + goto cleanup; + } + bi_export(ctx, result_bi, signature, sig_len); + ret = 0; // Success! +cleanup: + bi_free_mod(ctx, BIGINT_P_OFFSET); // cloned p_bi stored in mod context + bi_free_mod(ctx, BIGINT_Q_OFFSET); // cloned q_bi stored in mod context + bi_terminate(ctx); + return ret; +} + #endif /* MG_TLS == MG_TLS_BUILTIN */ #ifdef MG_ENABLE_LINES diff --git a/mongoose.h b/mongoose.h index b9f79665..5b8a22f7 100644 --- a/mongoose.h +++ b/mongoose.h @@ -2739,7 +2739,13 @@ PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz); - +int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, + const uint8_t *dP, size_t dP_len, + const uint8_t *dQ, size_t dQ_len, + const uint8_t *p, size_t p_len, + const uint8_t *q, size_t q_len, + const uint8_t *qInv, size_t qInv_len, + uint8_t *signature, size_t sig_len); #endif // TLS_RSA_H @@ -3944,41 +3950,6 @@ struct mg_tcpip_driver_tms570_data { -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 - -struct mg_tcpip_driver_xmc7_data { - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - uint8_t phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 3 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_xmc7_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_xmc7; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - - #if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC struct mg_tcpip_driver_xmc_data { @@ -4025,6 +3996,41 @@ struct mg_tcpip_driver_xmc_data { #endif + +#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 + +struct mg_tcpip_driver_xmc7_data { + int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 + uint8_t phy_addr; +}; + +#ifndef MG_TCPIP_PHY_ADDR +#define MG_TCPIP_PHY_ADDR 0 +#endif + +#ifndef MG_DRIVER_MDC_CR +#define MG_DRIVER_MDC_CR 3 +#endif + +#define MG_TCPIP_DRIVER_INIT(mgr) \ + do { \ + static struct mg_tcpip_driver_xmc7_data driver_data_; \ + static struct mg_tcpip_if mif_; \ + driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ + driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ + mif_.ip = MG_TCPIP_IP; \ + mif_.mask = MG_TCPIP_MASK; \ + mif_.gw = MG_TCPIP_GW; \ + mif_.driver = &mg_tcpip_driver_xmc7; \ + mif_.driver_data = &driver_data_; \ + MG_SET_MAC_ADDRESS(mif_.mac); \ + mg_tcpip_init(mgr, &mif_); \ + MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \ + } while (0) + +#endif + + #ifdef __cplusplus } #endif diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 431fa27f..22067e51 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -12,6 +12,19 @@ #if MG_TLS == MG_TLS_BUILTIN +// 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 @@ -28,6 +41,8 @@ #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 { @@ -62,6 +77,17 @@ struct tls_enc { 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 @@ -79,13 +105,17 @@ struct tls_data { uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t x25519_sec[32]; // x25519 secret between the handshake 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 - struct mg_str cert_der; // certificate in DER format - struct mg_str ca_der; // CA certificate - uint8_t ec_key[32]; // EC private key - char hostname[254]; // matching hostname + 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 + struct mg_str cert_der; // certificate in DER format + struct mg_str ca_der; // CA certificate + 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 uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to @@ -490,7 +520,20 @@ static int mg_tls_recv_record(struct mg_connection *c) { } 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 - MG_INFO(("TLS ALERT packet received")); + if (rio->len >= 7) { + uint8_t level = rio->buf[5], desc = rio->buf[6]; + 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" + : "unknown")); + } else { + MG_INFO(("TLS ALERT packet received (short)")); + } mg_tls_drop_record(c); } else { mg_error(c, "unexpected packet"); @@ -700,8 +743,7 @@ static bool mg_tls_server_send_ext(struct mg_connection *c) { // signature algorithms we actually support: // rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 static const uint8_t secp256r1_sig_algs[12] = { - 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01 -}; + 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01}; static bool mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; @@ -724,28 +766,47 @@ 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 n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0); - uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n); + size_t total_size = tls->cert_der.len + 5; + for (size_t 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; + } + uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + total_size); bool res; if (cert == NULL) return false; cert[0] = MG_TLS_CERTIFICATE; // handshake header - MG_STORE_BE24(cert + 1, n + 9); - cert[4] = 0; // request context - MG_STORE_BE24(cert + 5, n + 5); // 3 bytes: cert (s) length - MG_STORE_BE24(cert + 8, tls->cert_der.len); // 3 bytes: first cert len + 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 + size_t offset = 8; + MG_STORE_BE24(cert + offset, tls->cert_der.len); // 3 bytes: first cert len + offset += 3; // bytes 11+ are certificate in DER format - memmove(cert + 11, tls->cert_der.buf, tls->cert_der.len); - MG_STORE_BE16(cert + 11 + tls->cert_der.len, - 0); // certificate extensions (none) - if (send_ca) { - size_t offset = 13 + tls->cert_der.len; - MG_STORE_BE24(cert + offset, tls->ca_der.len); // 3 bytes: CA cert length - memmove(cert + offset + 3, tls->ca_der.buf, - tls->ca_der.len); // CA cert data - MG_STORE_BE16(cert + 11 + n, 0); // certificate extensions (none) + 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 (size_t 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; } - mg_sha256_update(&tls->sha256, cert, 13 + n); - res = mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE); + 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; } @@ -772,41 +833,253 @@ static void finish_SHA256(const MG_UECC_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 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); + size_t chunk_len = mask_len - generated; + size_t chunk = (chunk_len < sizeof(digest) ? chunk_len : sizeof(digest)); + memmove(mask + generated, digest, chunk); + generated += chunk; + counter++; + } +} + +static unsigned mg_tls_rsa_bits(const struct mg_str *n) { + size_t i = 0; + unsigned bits = 0; + while (i < n->len && n->buf[i] == 0) i++; + if (i == n->len) return 0; + bits = (unsigned) ((n->len - i) * 8); + { + uint8_t byte = (uint8_t) n->buf[i]; + while ((byte & 0x80U) == 0) { + bits--; + 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; + size_t saltlen = hashlen; + size_t dblen; + size_t pslen; + 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 (size_t 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) { +#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")); + + size_t nlen = tls->rsa.n.len; + + int 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) { + 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 + // Standard RSA: s = em^d mod n + memset(sig, 0, tls->rsa.n.len); + int ret = mg_rsa_mod_pow((const uint8_t *) tls->rsa.n.buf, tls->rsa.n.len, + (const uint8_t *) tls->rsa.d.buf, tls->rsa.d.len, em, + emlen, sig, tls->rsa.n.len); + 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[tls->rsa.n.len - 4], sig[tls->rsa.n.len - 3], + sig[tls->rsa.n.len - 2], sig[tls->rsa.n.len - 1])); + } + return ret == 0; +#endif +} + static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; - // server certificate verify packet - uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; - size_t sigsz, verifysz = 0; - uint8_t hash[32] = {0}, tmp[2 * 32 + 64] = {0}; - struct SHA256_HashContext ctx = { - {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, - {{0}, 0, 0, {0}}}; - int neg1, neg2; - uint8_t sig[64] = {0}; + uint8_t hash[32] = {0}; mg_tls_calc_cert_verify_hash(c, (uint8_t *) hash, is_client); - mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig, - mg_uecc_secp256r1()); + if (tls->rsa.n.len > 0 && tls->rsa.d.len > 0) { + // RSA certificate verify packet + size_t emlen = tls->rsa.n.len; + size_t verifysz = 8U + emlen; + uint8_t em[512]; // Max for 4096-bit RSA + uint8_t verify[520]; // 8 + 512 max - 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); + // Check bounds + if (emlen > sizeof(em) || verifysz > sizeof(verify)) { + MG_ERROR(("RSA key too large for static buffers")); + return false; + } - sigsz = (size_t) (70 + neg1 + neg2); - verifysz = 8U + sigsz; - verify[3] = (uint8_t) (sigsz + 4); - verify[7] = (uint8_t) sigsz; + if (!mg_tls_pss_encode(hash, sizeof(hash), &tls->rsa.n, em)) { + MG_ERROR(("Failed PSS encode")); + return false; + } - mg_sha256_update(&tls->sha256, verify, verifysz); - return mg_tls_encrypt(c, verify, verifysz, MG_TLS_HANDSHAKE); + // 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, (uint32_t) (emlen + 4)); + MG_STORE_BE16(verify + 4, 0x0804); + MG_STORE_BE16(verify + 6, (uint16_t) 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) { @@ -1213,16 +1486,17 @@ static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz, return -1; } while (mg_der_next(&field, &name) > 0) { - if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address + if (name.type == 0x87 && name.len == 4) { // this is an IPv4 address MG_DEBUG(("Found SAN, IP: %M", mg_print_ip4, name.value)); - if (!server_ip->is_ip6 && *((uint32_t *) name.value) == server_ip->addr.ip4) + if (!server_ip->is_ip6 && + *((uint32_t *) name.value) == server_ip->addr.ip4) return 1; // and matches the one we're connected to - } else { // this is a text SAN + } else { // this is a text SAN MG_DEBUG(("Found SAN, (%u): %.*s", name.type, name.len, name.value)); if (mg_match(mg_str(server_name), mg_str_n((char *) name.value, name.len), NULL)) return 1; // and matches the host name - } // TODO(): add IPv6 comparison, more items ? + } // TODO(): add IPv6 comparison, more items ? } return -1; } @@ -1593,7 +1867,9 @@ static bool mg_tls_server_handshake(struct mg_connection *c) { 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 (!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; @@ -1634,7 +1910,7 @@ 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 (c->is_closing) return; // we don't clear rx buf, so ignore what's left if (c->is_client) { // will clear is_hs when sending last chunk res = mg_tls_client_handshake(c); @@ -1652,6 +1928,310 @@ void mg_tls_handshake(struct mg_connection *c) { // 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; + 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 (end - *p < len_bytes) { + MG_VERBOSE(("DER INT: not enough bytes for length")); + return -1; + } + len = 0; + for (uint8_t 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 (end - *p < (long) len) { + MG_VERBOSE(("DER INT: length exceeds remaining bytes")); + return -1; + } + + // The encoded length tells us how many bytes to consume from the stream + const uint8_t *value_start = *p; + const uint8_t *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, + value_end - value_start != 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 +// } +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)); + + // Debug: show first few bytes + MG_DEBUG( + ("RSA key DER first 16 bytes: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x", + der[0], der[1], der[2], der[3], der[4], der[5], der[6], der[7], der[8], + der[9], der[10], der[11], der[12], der[13], der[14], der[15])); + + // 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 len_bytes = seq_len & 0x7F; + MG_DEBUG(("Long form length: %d bytes", len_bytes)); + if (end - p < len_bytes) { + MG_ERROR(("Not enough bytes for long form length")); + return -1; + } + seq_len = 0; + for (uint8_t i = 0; i < len_bytes; i++) { + seq_len = (seq_len << 8) | p[i]; + } + p += len_bytes; + } + + MG_DEBUG( + ("SEQUENCE length: %u, total DER size: %u", seq_len, (unsigned) dersz)); + + if (end - p < (long) 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_DEBUG(("Before version: offset=%d, bytes: %02x %02x %02x %02x", + (int) (p - der), p[0], p[1], p[2], p[3])); + 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_DEBUG(("Before n: offset=%d, bytes: %02x %02x %02x %02x %02x %02x", + (int) (p - der), p[0], p[1], p[2], p[3], p[4], p[5])); + if (mg_rsa_parse_der_int(&p, end, &key->n) < 0) { + MG_ERROR(("Failed to parse n (modulus)")); + return -1; + } + MG_DEBUG(("Parsed n: %d bytes, offset now=%d, consumed=%d bytes total", + (int) key->n.len, (int) (p - der), (int) (p - der))); + MG_DEBUG((" First 8 bytes of n: %02x %02x %02x %02x %02x %02x %02x %02x", + (unsigned char) key->n.buf[0], (unsigned char) key->n.buf[1], + (unsigned char) key->n.buf[2], (unsigned char) key->n.buf[3], + (unsigned char) key->n.buf[4], (unsigned char) key->n.buf[5], + (unsigned char) key->n.buf[6], (unsigned char) key->n.buf[7])); + MG_DEBUG((" 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_DEBUG(("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 tls_data *tls) { + 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 (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; + } + tls->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 (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, tls->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; @@ -1685,6 +2265,70 @@ static int mg_parse_pem(const struct mg_str pem, const struct mg_str label, 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 *begin = NULL; + for (const char *s = p; s <= end - (int) begin_len; s++) { + if (memcmp(s, begin_marker, begin_len) == 0) { + begin = s; + break; + } + } + if (begin == NULL) break; + + const char *finish = NULL; + for (const char *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 (int i = 0; i < count; i++) mg_free((void *) certs[i].buf); + mg_free(certs); + return -1; + } + + struct mg_str cert_pem = mg_str_n(begin, (size_t) (finish - begin)); + struct mg_str cert_der; + + if (mg_parse_pem(cert_pem, mg_str_s("CERTIFICATE"), &cert_der) < 0) { + for (int i = 0; i < count; i++) mg_free((void *) certs[i].buf); + mg_free(certs); + return -1; + } + + struct mg_str *new_certs = + (struct mg_str *) mg_calloc((size_t) count + 1, sizeof(struct mg_str)); + if (new_certs == NULL) { + mg_free((void *) cert_der.buf); + for (int 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; +} + void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { struct mg_str key; struct tls_data *tls = @@ -1728,9 +2372,23 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { } // parse PEM or DER certificate - if (mg_parse_pem(opts->cert, mg_str_s("CERTIFICATE"), &tls->cert_der) < 0) { - MG_ERROR(("Failed to load certificate")); - return; + 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 { + 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 @@ -1755,10 +2413,51 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { } 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) { + // RSA private key found, store it for later use + tls->rsa_key_der = key; + MG_INFO(("Parsed RSA private key: %d bytes", (int) key.len)); + + // parse and validate the key structure + // we keep the DER buffer, rsa_key just points into it + struct mg_rsa_key rsa_key; + 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) { - mg_error(c, "PKCS8 private key format is not supported"); + if (mg_parse_pkcs8_key((const uint8_t *) key.buf, key.len, tls) == 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_INFO(("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 or PRIVATE KEY"); + mg_error( + c, "Expected EC PRIVATE KEY, RSA PRIVATE KEY, or PRIVATE KEY (PKCS#8)"); } } @@ -1766,8 +2465,16 @@ void mg_tls_free(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; if (tls != NULL) { mg_iobuf_free(&tls->send); - mg_free((void *) tls->cert_der.buf); + if (tls->chain_der != NULL) { + for (size_t 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); + } mg_free((void *) tls->ca_der.buf); + mg_free((void *) tls->rsa_key_der.buf); } mg_free(c->tls); c->tls = NULL; @@ -1783,7 +2490,7 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { 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 + } // 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); diff --git a/src/tls_rsa.c b/src/tls_rsa.c index 752bf9ea..8bc0d88a 100644 --- a/src/tls_rsa.c +++ b/src/tls_rsa.c @@ -1520,7 +1520,6 @@ NS_INTERNAL bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) { #endif } -#if 0 /** * @brief Use the Chinese Remainder Theorem to quickly perform RSA decrypts. * @@ -1533,6 +1532,7 @@ NS_INTERNAL bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) { * @param qInv [in] CRT's qInv bigint * @return The result of the CRT operation */ +#if 1 NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, bigint *p, bigint *q, bigint *qInv) { bigint *m1, *m2, *h; @@ -1559,7 +1559,6 @@ NS_INTERNAL bigint *bi_crt(BI_CTX *ctx, bigint *bi, bigint *dP, bigint *dQ, } #endif - // Proper lib usage: // - BI_CTX *c = bi_initialize() // - allocate bigints (e.g.: calling bi_import(), int_to_bi(), ...) @@ -1588,4 +1587,48 @@ int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t return 0; } +int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, + const uint8_t *dP, size_t dP_len, + const uint8_t *dQ, size_t dQ_len, + const uint8_t *p, size_t p_len, + const uint8_t *q, size_t q_len, + const uint8_t *qInv, size_t qInv_len, + uint8_t *signature, size_t sig_len) { + BI_CTX *ctx; + bigint *em_bi, *dP_bi, *dQ_bi, *p_bi, *q_bi, *qInv_bi, *result_bi; + int ret = -1; + + ctx = bi_initialize(); + if (ctx == NULL) { + return -1; + } + + em_bi = bi_import(ctx, em, em_len); + dP_bi = bi_import(ctx, dP, dP_len); + dQ_bi = bi_import(ctx, dQ, dQ_len); + p_bi = bi_import(ctx, p, p_len); + q_bi = bi_import(ctx, q, q_len); + qInv_bi = bi_import(ctx, qInv, qInv_len); + + if (em_bi == NULL || dP_bi == NULL || dQ_bi == NULL || + p_bi == NULL || q_bi == NULL || qInv_bi == NULL) { + goto cleanup; + } + + bi_set_mod(ctx, bi_clone(ctx, p_bi), BIGINT_P_OFFSET); + bi_set_mod(ctx, bi_clone(ctx, q_bi), BIGINT_Q_OFFSET); + + result_bi = bi_crt(ctx, em_bi, dP_bi, dQ_bi, p_bi, q_bi, qInv_bi); + if (result_bi == NULL) { + goto cleanup; + } + bi_export(ctx, result_bi, signature, sig_len); + ret = 0; // Success! +cleanup: + bi_free_mod(ctx, BIGINT_P_OFFSET); // cloned p_bi stored in mod context + bi_free_mod(ctx, BIGINT_Q_OFFSET); // cloned q_bi stored in mod context + bi_terminate(ctx); + return ret; +} + #endif /* MG_TLS == MG_TLS_BUILTIN */ diff --git a/src/tls_rsa.h b/src/tls_rsa.h index 63ffdffc..c25e80b3 100644 --- a/src/tls_rsa.h +++ b/src/tls_rsa.h @@ -3,5 +3,11 @@ #include "arch.h" int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz); - +int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, + const uint8_t *dP, size_t dP_len, + const uint8_t *dQ, size_t dQ_len, + const uint8_t *p, size_t p_len, + const uint8_t *q, size_t q_len, + const uint8_t *qInv, size_t qInv_len, + uint8_t *signature, size_t sig_len); #endif // TLS_RSA_H