Loading src/crypto/tls.cpp +100 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,33 @@ #include "../socket.h" #include "../exception.h" #include <map> #include <mutex> namespace { // Client: the server sent a CertificateRequest; we answer with an empty // Certificate (no client certificate), echoing the TLS 1.3 request // context. Kept here rather than in netplus::tls so that its layout -- // the library's ABI -- stays the same. Cleared at every ClientHello. std::mutex g_certRequestsLock; std::map<const netplus::tls *, std::vector<uint8_t>> g_certRequests; void setCertRequest(const netplus::tls *t, std::vector<uint8_t> context) { std::lock_guard<std::mutex> l(g_certRequestsLock); g_certRequests[t] = std::move(context); } // true (and the context) once if a certificate was asked for bool takeCertRequest(const netplus::tls *t, std::vector<uint8_t> &context) { std::lock_guard<std::mutex> l(g_certRequestsLock); auto it = g_certRequests.find(t); if (it == g_certRequests.end()) return false; context = std::move(it->second); g_certRequests.erase(it); return true; } } // namespace #define SSL_LOG(x) ((void)0) #define TLSDBG(x) ((void)0) Loading Loading @@ -1965,6 +1992,19 @@ namespace netplus { note("ssl::handshake_after_connect: wait TLS1.3 Certificate"); } // RFC 8446 §4.3.2: a CertificateRequest may come first (Active // Directory's LDAPS asks for an optional client certificate). // Remember its context; SEND_FINISHED answers with an empty // Certificate -- we have none to offer. if (msg.size() >= 5 && msg[0] == 0x0d) { size_t ctxLen = msg[4]; if (5 + ctxLen > msg.size()) throwSSL(NetException::Error, "TLS1.3 CertificateRequest truncated"); setCertRequest(this, std::vector<uint8_t>(msg.begin() + 5, msg.begin() + 5 + ctxLen)); handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end()); break; // the Certificate comes next } if (msg.size() < 4 || msg[0] != 0x0b) { NetException e; e[NetException::Error] << "TLS1.3 expected Certificate got " << int(msg.empty()? -1 : msg[0]); Loading Loading @@ -2186,6 +2226,25 @@ namespace netplus { // Determine hash length based on cipher suite size_t cli_hash_len = (chosenSuite == 0x1302) ? 48 : 32; // Asked for a certificate: an empty Certificate (no // CertificateVerify then), part of the transcript the // Finished below covers (RFC 8446 §4.4.2) std::vector<uint8_t> certContext; if (takeCertRequest(this, certContext)) { std::vector<uint8_t> body; body.push_back(uint8_t(certContext.size())); body.insert(body.end(), certContext.begin(), certContext.end()); body.push_back(0x00); body.push_back(0x00); body.push_back(0x00); // certificate_list: empty std::vector<uint8_t> cert_msg; cert_msg.push_back(0x0b); cert_msg.push_back(uint8_t(body.size() >> 16)); cert_msg.push_back(uint8_t(body.size() >> 8)); cert_msg.push_back(uint8_t(body.size())); cert_msg.insert(cert_msg.end(), body.begin(), body.end()); handshake_transcript.insert(handshake_transcript.end(), cert_msg.begin(), cert_msg.end()); tls13_send_record(0x16, cert_msg, /*handshake_keys=*/true); } // Build client Finished verify_data std::vector<uint8_t> cli_th; if (chosenSuite == 0x1302) { Loading Loading @@ -2302,6 +2361,30 @@ namespace netplus { } } // Full chain-of-trust check, as in the TLS 1.3 path: every // certificate of the list, leaf first. { std::vector<std::vector<uint8_t>> presented; size_t p = 7, end = 7 + listLen; while (p + 3 <= end) { uint32_t len = (uint32_t(certMsg[p]) << 16) | (uint32_t(certMsg[p+1]) << 8) | uint32_t(certMsg[p+2]); p += 3; if (p + len > end) { NetException e; e[NetException::Error] << "Certificate entry truncated"; throw e; } presented.emplace_back(certMsg.begin() + p, certMsg.begin() + p + len); p += len; } if (trust_policy.verifyPeer && presented.empty()) { NetException e; e[NetException::Error] << "TLS1.2: server sent no certificate"; throw e; } verifyPeerChain(presented, trust_policy); } // Add Certificate to transcript (RFC 5246) handshake_transcript.insert(handshake_transcript.end(), certMsg.begin(), certMsg.end()); Loading Loading @@ -2384,6 +2467,14 @@ namespace netplus { if (msg.empty()) note("ssl::handshake_after_connect: wait SHD"); // RFC 5246 §7.4.4: CertificateRequest before ServerHelloDone; // answered with an empty Certificate in CLI_SEND_CKE if (msg[0] == 0x0d) { setCertRequest(this, {}); handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end()); break; } if (msg[0] != 0x0e) { NetException e; e[NetException::Error] << "expected ServerHelloDone got type=" << int(msg[0]); Loading Loading @@ -2466,6 +2557,10 @@ namespace netplus { ckeBody.insert(ckeBody.end(), ciphertext.begin(), ciphertext.end()); } // asked for a certificate: an empty one first (RFC 5246 §7.4.6) std::vector<uint8_t> certContext; if (takeCertRequest(this, certContext)) sendHandshake(0x0b, std::vector<uint8_t>{0x00, 0x00, 0x00}); sendHandshake(0x10, ckeBody); // Derive master_secret (sendHandshake above already added CKE to transcript) Loading Loading @@ -4832,6 +4927,11 @@ namespace netplus { // ================================================================= void tls::queueClientHello() { { std::vector<uint8_t> stale; takeCertRequest(this, stale); } // Build ClientHello message body std::vector<uint8_t> ch; Loading Loading
src/crypto/tls.cpp +100 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,33 @@ #include "../socket.h" #include "../exception.h" #include <map> #include <mutex> namespace { // Client: the server sent a CertificateRequest; we answer with an empty // Certificate (no client certificate), echoing the TLS 1.3 request // context. Kept here rather than in netplus::tls so that its layout -- // the library's ABI -- stays the same. Cleared at every ClientHello. std::mutex g_certRequestsLock; std::map<const netplus::tls *, std::vector<uint8_t>> g_certRequests; void setCertRequest(const netplus::tls *t, std::vector<uint8_t> context) { std::lock_guard<std::mutex> l(g_certRequestsLock); g_certRequests[t] = std::move(context); } // true (and the context) once if a certificate was asked for bool takeCertRequest(const netplus::tls *t, std::vector<uint8_t> &context) { std::lock_guard<std::mutex> l(g_certRequestsLock); auto it = g_certRequests.find(t); if (it == g_certRequests.end()) return false; context = std::move(it->second); g_certRequests.erase(it); return true; } } // namespace #define SSL_LOG(x) ((void)0) #define TLSDBG(x) ((void)0) Loading Loading @@ -1965,6 +1992,19 @@ namespace netplus { note("ssl::handshake_after_connect: wait TLS1.3 Certificate"); } // RFC 8446 §4.3.2: a CertificateRequest may come first (Active // Directory's LDAPS asks for an optional client certificate). // Remember its context; SEND_FINISHED answers with an empty // Certificate -- we have none to offer. if (msg.size() >= 5 && msg[0] == 0x0d) { size_t ctxLen = msg[4]; if (5 + ctxLen > msg.size()) throwSSL(NetException::Error, "TLS1.3 CertificateRequest truncated"); setCertRequest(this, std::vector<uint8_t>(msg.begin() + 5, msg.begin() + 5 + ctxLen)); handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end()); break; // the Certificate comes next } if (msg.size() < 4 || msg[0] != 0x0b) { NetException e; e[NetException::Error] << "TLS1.3 expected Certificate got " << int(msg.empty()? -1 : msg[0]); Loading Loading @@ -2186,6 +2226,25 @@ namespace netplus { // Determine hash length based on cipher suite size_t cli_hash_len = (chosenSuite == 0x1302) ? 48 : 32; // Asked for a certificate: an empty Certificate (no // CertificateVerify then), part of the transcript the // Finished below covers (RFC 8446 §4.4.2) std::vector<uint8_t> certContext; if (takeCertRequest(this, certContext)) { std::vector<uint8_t> body; body.push_back(uint8_t(certContext.size())); body.insert(body.end(), certContext.begin(), certContext.end()); body.push_back(0x00); body.push_back(0x00); body.push_back(0x00); // certificate_list: empty std::vector<uint8_t> cert_msg; cert_msg.push_back(0x0b); cert_msg.push_back(uint8_t(body.size() >> 16)); cert_msg.push_back(uint8_t(body.size() >> 8)); cert_msg.push_back(uint8_t(body.size())); cert_msg.insert(cert_msg.end(), body.begin(), body.end()); handshake_transcript.insert(handshake_transcript.end(), cert_msg.begin(), cert_msg.end()); tls13_send_record(0x16, cert_msg, /*handshake_keys=*/true); } // Build client Finished verify_data std::vector<uint8_t> cli_th; if (chosenSuite == 0x1302) { Loading Loading @@ -2302,6 +2361,30 @@ namespace netplus { } } // Full chain-of-trust check, as in the TLS 1.3 path: every // certificate of the list, leaf first. { std::vector<std::vector<uint8_t>> presented; size_t p = 7, end = 7 + listLen; while (p + 3 <= end) { uint32_t len = (uint32_t(certMsg[p]) << 16) | (uint32_t(certMsg[p+1]) << 8) | uint32_t(certMsg[p+2]); p += 3; if (p + len > end) { NetException e; e[NetException::Error] << "Certificate entry truncated"; throw e; } presented.emplace_back(certMsg.begin() + p, certMsg.begin() + p + len); p += len; } if (trust_policy.verifyPeer && presented.empty()) { NetException e; e[NetException::Error] << "TLS1.2: server sent no certificate"; throw e; } verifyPeerChain(presented, trust_policy); } // Add Certificate to transcript (RFC 5246) handshake_transcript.insert(handshake_transcript.end(), certMsg.begin(), certMsg.end()); Loading Loading @@ -2384,6 +2467,14 @@ namespace netplus { if (msg.empty()) note("ssl::handshake_after_connect: wait SHD"); // RFC 5246 §7.4.4: CertificateRequest before ServerHelloDone; // answered with an empty Certificate in CLI_SEND_CKE if (msg[0] == 0x0d) { setCertRequest(this, {}); handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end()); break; } if (msg[0] != 0x0e) { NetException e; e[NetException::Error] << "expected ServerHelloDone got type=" << int(msg[0]); Loading Loading @@ -2466,6 +2557,10 @@ namespace netplus { ckeBody.insert(ckeBody.end(), ciphertext.begin(), ciphertext.end()); } // asked for a certificate: an empty one first (RFC 5246 §7.4.6) std::vector<uint8_t> certContext; if (takeCertRequest(this, certContext)) sendHandshake(0x0b, std::vector<uint8_t>{0x00, 0x00, 0x00}); sendHandshake(0x10, ckeBody); // Derive master_secret (sendHandshake above already added CKE to transcript) Loading Loading @@ -4832,6 +4927,11 @@ namespace netplus { // ================================================================= void tls::queueClientHello() { { std::vector<uint8_t> stale; takeCertRequest(this, stale); } // Build ClientHello message body std::vector<uint8_t> ch; Loading