Loading src/crypto/tls.cpp +104 −10 Changes for src/crypto/tls.cpp: 104 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -13,6 +13,8 @@ #include "sha.h" #include "base64.h" #include "rsa_pss_sha256.h" #include "rsa_pss_sha384.h" #include "rsa_pkcs1_sha384.h" #include "ecc_p256.h" #include "ecc_u256.h" #include "curve25519.h" Loading @@ -21,6 +23,7 @@ #include "../socket.h" #include "../exception.h" #include <algorithm> #include <map> #include <mutex> Loading Loading @@ -200,6 +203,61 @@ namespace netplus { return std::vector<uint8_t>(block.begin() + i, block.end()); } namespace { // What a TLS 1.2 ClientHello allows for ServerKeyExchange. Taken from the raw // message, so the choice needs no state beyond clientHelloRawBytes. struct Tls12Offer { bool hasGroups = false, hasSigalgs = false; std::vector<uint16_t> groups, sigalgs; }; Tls12Offer parseTls12Offer(const std::vector<uint8_t>& ch) { Tls12Offer o; auto be16 = [&](size_t p) { return uint16_t((uint16_t(ch[p]) << 8) | ch[p + 1]); }; auto list = [&](size_t p, size_t el, std::vector<uint16_t>& out) { if (el < 2) return; size_t n = std::min<size_t>(be16(p), el - 2); for (size_t i = 0; i + 1 < n; i += 2) out.push_back(be16(p + 2 + i)); }; size_t p = 4 + 2 + 32; // header, version, random if (p >= ch.size()) return o; p += 1 + size_t(ch[p]); // session id if (p + 2 > ch.size()) return o; p += 2 + size_t(be16(p)); // cipher suites if (p >= ch.size()) return o; p += 1 + size_t(ch[p]); // compression if (p + 2 > ch.size()) return o; size_t end = std::min(ch.size(), p + 2 + size_t(be16(p))); for (p += 2; p + 4 <= end;) { uint16_t et = be16(p); size_t el = be16(p + 2); p += 4; if (p + el > end) break; if (et == 0x000a) { o.hasGroups = true; list(p, el, o.groups); } if (et == 0x000d) { o.hasSigalgs = true; list(p, el, o.sigalgs); } p += el; } return o; } // ECDHE group in the client's order; without the extension P-256 as before. // 0: no ECDHE possible. uint16_t pickTls12Group(const Tls12Offer& o) { if (!o.hasGroups) return 0x0017; for (uint16_t g : o.groups) if (g == 0x001d || g == 0x0017) return g; return 0; } // signature for an RSA key in the client's order. 0: none in common. uint16_t pickTls12RsaSigalg(const Tls12Offer& o) { if (!o.hasSigalgs) return 0x0401; for (uint16_t a : o.sigalgs) if (a == 0x0401 || a == 0x0501 || a == 0x0804 || a == 0x0805) return a; return 0; } } void tls::handshake_after_accept(){ std::lock_guard<std::recursive_mutex> lk(mutex_); // Note: Caller (IOCP layer) must hold con::event_mutex to prevent concurrent access Loading Loading @@ -575,20 +633,30 @@ namespace netplus { bool have_ec_cert = (selected_cert_bundle && selected_cert_bundle->has_ec_key); bool have_rsa_cert = (selected_cert_bundle && selected_cert_bundle->rsa_key); // ECDHE only with a group and a signature the client accepts Tls12Offer offer = parseTls12Offer(ch); uint16_t ecdheGroup = pickTls12Group(offer); bool ecSigOk = !offer.hasSigalgs || std::find(offer.sigalgs.begin(), offer.sigalgs.end(), uint16_t(0x0403)) != offer.sigalgs.end(); bool rsaEcdhe = ecdheGroup != 0 && pickTls12RsaSigalg(offer) != 0; if (have_ec_cert) { // ECDSA certificate: only ECDHE_ECDSA suites if (ecdheGroup && ecSigOk) { if (hasSuite(0xC02C)) chosenSuite = 0xC02C; // ECDHE_ECDSA_AES_256_GCM_SHA384 else if (hasSuite(0xC02B)) chosenSuite = 0xC02B; // ECDHE_ECDSA_AES_128_GCM_SHA256 } } else if (have_rsa_cert) { // RSA certificate: prefer ECDHE_RSA, fall back to static RSA if (hasSuite(0xC030)) chosenSuite = 0xC030; // ECDHE_RSA_AES_256_GCM_SHA384 else if (hasSuite(0xC02F)) chosenSuite = 0xC02F; // ECDHE_RSA_AES_128_GCM_SHA256 if (rsaEcdhe && hasSuite(0xC030)) chosenSuite = 0xC030; // ECDHE_RSA_AES_256_GCM_SHA384 else if (rsaEcdhe && hasSuite(0xC02F)) chosenSuite = 0xC02F; // ECDHE_RSA_AES_128_GCM_SHA256 else if (hasSuite(0x002F)) chosenSuite = 0x002F; // RSA_AES_128_CBC_SHA else if (hasSuite(0x0035)) chosenSuite = 0x0035; // RSA_AES_256_CBC_SHA } if (chosenSuite == 0) throwSSL(NetException::Error, "No supported cipher suite"); selected_group = ecdheGroup; // The ClientHello starts the transcript for TLS 1.2 too (EMS session // hash and both Finished messages, full and abbreviated handshake). Loading Loading @@ -981,16 +1049,26 @@ namespace netplus { bool isECDHE = (chosenSuite == 0xC02B || chosenSuite == 0xC02C || chosenSuite == 0xC02F || chosenSuite == 0xC030); if (isECDHE) { // Generate ephemeral ECDHE P-256 key pair // Ephemeral key pair on the group chosen with the suite std::vector<uint8_t> pubBytes; if (selected_group == 0x001d) { server_priv_x25519.resize(32); fillRandomBytes(server_priv_x25519.data(), 32); if (!netplus::scalarmult_curve25519_base(server_pub_x25519, server_priv_x25519)) throwSSL(NetException::Error, "x25519 key generation failed"); pubBytes = server_pub_x25519; } else { gen_tls13_p256_scalar(tls12_ecdhe_priv); netplus::P256Point pub = netplus::P256Point::scalarMulBase(tls12_ecdhe_priv); std::vector<uint8_t> pubBytes = pub.encodeTls(); pubBytes = pub.encodeTls(); } // Build ServerKeyExchange params (ECParameters + ECPoint) // ECParameters: curve_type(1) = named_curve(3), named_curve(2) = secp256r1(0x0017) // ECParameters: curve_type(1) = named_curve(3), named_curve(2) std::vector<uint8_t> params; params.push_back(0x03); // named_curve params.push_back(0x00); params.push_back(0x17); // secp256r1 params.push_back(uint8_t(selected_group >> 8)); params.push_back(uint8_t(selected_group & 0xFF)); params.push_back((uint8_t)pubBytes.size()); // point length params.insert(params.end(), pubBytes.begin(), pubBytes.end()); Loading @@ -1012,11 +1090,17 @@ namespace netplus { sig = sign_ecdsa_sha256(toSign, ecKey); sigAlg = 0x0403; // ecdsa_secp256r1_sha256 } else { // ECDHE_RSA: sign with RSA PKCS#1 v1.5 // ECDHE_RSA: the client's first signature scheme we can do if (!selected_cert_bundle->rsa_key) throwSSL(NetException::Error, "ECDHE_RSA requires RSA key"); sig = sign_rsa_sha256_pkcs15(toSign, selected_cert_bundle->rsa_key); sigAlg = 0x0401; // rsa_pkcs1_sha256 netplus::rsa& rsaKey = selected_cert_bundle->rsa_key; sigAlg = pickTls12RsaSigalg(parseTls12Offer(clientHelloRawBytes)); switch (sigAlg) { case 0x0501: sig = rsa_pkcs1_sha384::sign(rsaKey, toSign); break; case 0x0804: sig = rsa_pss_sha256::sign(rsaKey, toSign); break; case 0x0805: sig = rsa_pss_sha384::sign(rsaKey, toSign); break; default: sig = sign_rsa_sha256_pkcs15(toSign, rsaKey); break; } } // Build ServerKeyExchange body: params + sig_algorithm(2) + sig_len(2) + sig Loading Loading @@ -1222,7 +1306,17 @@ namespace netplus { bool isECDHE = (chosenSuite == 0xC02B || chosenSuite == 0xC02C || chosenSuite == 0xC02F || chosenSuite == 0xC030); if (isECDHE) { if (isECDHE && selected_group == 0x001d) { // x25519 (RFC 8422 5.11): 32-byte keys, the shared secret is the premaster if (hlen != 33 || msg[4] != 32) throwSSL(NetException::Error, "ECDHE CKE invalid x25519 key"); std::vector<uint8_t> clientPub(msg.begin() + 5, msg.end()); if (!netplus::scalarmult_curve25519(preMaster, server_priv_x25519, clientPub)) throwSSL(NetException::Error, "ECDHE shared secret computation failed"); if (std::all_of(preMaster.begin(), preMaster.end(), [](uint8_t b) { return b == 0; })) throwSSL(NetException::Error, "ECDHE CKE: x25519 low-order key"); tls12_is_gcm = true; } else if (isECDHE) { // ECDHE CKE: body = point_length(1) + uncompressed_point(65) size_t off = 4; if (hlen < 1) Loading Loading
src/crypto/tls.cpp +104 −10 Changes for src/crypto/tls.cpp: 104 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -13,6 +13,8 @@ #include "sha.h" #include "base64.h" #include "rsa_pss_sha256.h" #include "rsa_pss_sha384.h" #include "rsa_pkcs1_sha384.h" #include "ecc_p256.h" #include "ecc_u256.h" #include "curve25519.h" Loading @@ -21,6 +23,7 @@ #include "../socket.h" #include "../exception.h" #include <algorithm> #include <map> #include <mutex> Loading Loading @@ -200,6 +203,61 @@ namespace netplus { return std::vector<uint8_t>(block.begin() + i, block.end()); } namespace { // What a TLS 1.2 ClientHello allows for ServerKeyExchange. Taken from the raw // message, so the choice needs no state beyond clientHelloRawBytes. struct Tls12Offer { bool hasGroups = false, hasSigalgs = false; std::vector<uint16_t> groups, sigalgs; }; Tls12Offer parseTls12Offer(const std::vector<uint8_t>& ch) { Tls12Offer o; auto be16 = [&](size_t p) { return uint16_t((uint16_t(ch[p]) << 8) | ch[p + 1]); }; auto list = [&](size_t p, size_t el, std::vector<uint16_t>& out) { if (el < 2) return; size_t n = std::min<size_t>(be16(p), el - 2); for (size_t i = 0; i + 1 < n; i += 2) out.push_back(be16(p + 2 + i)); }; size_t p = 4 + 2 + 32; // header, version, random if (p >= ch.size()) return o; p += 1 + size_t(ch[p]); // session id if (p + 2 > ch.size()) return o; p += 2 + size_t(be16(p)); // cipher suites if (p >= ch.size()) return o; p += 1 + size_t(ch[p]); // compression if (p + 2 > ch.size()) return o; size_t end = std::min(ch.size(), p + 2 + size_t(be16(p))); for (p += 2; p + 4 <= end;) { uint16_t et = be16(p); size_t el = be16(p + 2); p += 4; if (p + el > end) break; if (et == 0x000a) { o.hasGroups = true; list(p, el, o.groups); } if (et == 0x000d) { o.hasSigalgs = true; list(p, el, o.sigalgs); } p += el; } return o; } // ECDHE group in the client's order; without the extension P-256 as before. // 0: no ECDHE possible. uint16_t pickTls12Group(const Tls12Offer& o) { if (!o.hasGroups) return 0x0017; for (uint16_t g : o.groups) if (g == 0x001d || g == 0x0017) return g; return 0; } // signature for an RSA key in the client's order. 0: none in common. uint16_t pickTls12RsaSigalg(const Tls12Offer& o) { if (!o.hasSigalgs) return 0x0401; for (uint16_t a : o.sigalgs) if (a == 0x0401 || a == 0x0501 || a == 0x0804 || a == 0x0805) return a; return 0; } } void tls::handshake_after_accept(){ std::lock_guard<std::recursive_mutex> lk(mutex_); // Note: Caller (IOCP layer) must hold con::event_mutex to prevent concurrent access Loading Loading @@ -575,20 +633,30 @@ namespace netplus { bool have_ec_cert = (selected_cert_bundle && selected_cert_bundle->has_ec_key); bool have_rsa_cert = (selected_cert_bundle && selected_cert_bundle->rsa_key); // ECDHE only with a group and a signature the client accepts Tls12Offer offer = parseTls12Offer(ch); uint16_t ecdheGroup = pickTls12Group(offer); bool ecSigOk = !offer.hasSigalgs || std::find(offer.sigalgs.begin(), offer.sigalgs.end(), uint16_t(0x0403)) != offer.sigalgs.end(); bool rsaEcdhe = ecdheGroup != 0 && pickTls12RsaSigalg(offer) != 0; if (have_ec_cert) { // ECDSA certificate: only ECDHE_ECDSA suites if (ecdheGroup && ecSigOk) { if (hasSuite(0xC02C)) chosenSuite = 0xC02C; // ECDHE_ECDSA_AES_256_GCM_SHA384 else if (hasSuite(0xC02B)) chosenSuite = 0xC02B; // ECDHE_ECDSA_AES_128_GCM_SHA256 } } else if (have_rsa_cert) { // RSA certificate: prefer ECDHE_RSA, fall back to static RSA if (hasSuite(0xC030)) chosenSuite = 0xC030; // ECDHE_RSA_AES_256_GCM_SHA384 else if (hasSuite(0xC02F)) chosenSuite = 0xC02F; // ECDHE_RSA_AES_128_GCM_SHA256 if (rsaEcdhe && hasSuite(0xC030)) chosenSuite = 0xC030; // ECDHE_RSA_AES_256_GCM_SHA384 else if (rsaEcdhe && hasSuite(0xC02F)) chosenSuite = 0xC02F; // ECDHE_RSA_AES_128_GCM_SHA256 else if (hasSuite(0x002F)) chosenSuite = 0x002F; // RSA_AES_128_CBC_SHA else if (hasSuite(0x0035)) chosenSuite = 0x0035; // RSA_AES_256_CBC_SHA } if (chosenSuite == 0) throwSSL(NetException::Error, "No supported cipher suite"); selected_group = ecdheGroup; // The ClientHello starts the transcript for TLS 1.2 too (EMS session // hash and both Finished messages, full and abbreviated handshake). Loading Loading @@ -981,16 +1049,26 @@ namespace netplus { bool isECDHE = (chosenSuite == 0xC02B || chosenSuite == 0xC02C || chosenSuite == 0xC02F || chosenSuite == 0xC030); if (isECDHE) { // Generate ephemeral ECDHE P-256 key pair // Ephemeral key pair on the group chosen with the suite std::vector<uint8_t> pubBytes; if (selected_group == 0x001d) { server_priv_x25519.resize(32); fillRandomBytes(server_priv_x25519.data(), 32); if (!netplus::scalarmult_curve25519_base(server_pub_x25519, server_priv_x25519)) throwSSL(NetException::Error, "x25519 key generation failed"); pubBytes = server_pub_x25519; } else { gen_tls13_p256_scalar(tls12_ecdhe_priv); netplus::P256Point pub = netplus::P256Point::scalarMulBase(tls12_ecdhe_priv); std::vector<uint8_t> pubBytes = pub.encodeTls(); pubBytes = pub.encodeTls(); } // Build ServerKeyExchange params (ECParameters + ECPoint) // ECParameters: curve_type(1) = named_curve(3), named_curve(2) = secp256r1(0x0017) // ECParameters: curve_type(1) = named_curve(3), named_curve(2) std::vector<uint8_t> params; params.push_back(0x03); // named_curve params.push_back(0x00); params.push_back(0x17); // secp256r1 params.push_back(uint8_t(selected_group >> 8)); params.push_back(uint8_t(selected_group & 0xFF)); params.push_back((uint8_t)pubBytes.size()); // point length params.insert(params.end(), pubBytes.begin(), pubBytes.end()); Loading @@ -1012,11 +1090,17 @@ namespace netplus { sig = sign_ecdsa_sha256(toSign, ecKey); sigAlg = 0x0403; // ecdsa_secp256r1_sha256 } else { // ECDHE_RSA: sign with RSA PKCS#1 v1.5 // ECDHE_RSA: the client's first signature scheme we can do if (!selected_cert_bundle->rsa_key) throwSSL(NetException::Error, "ECDHE_RSA requires RSA key"); sig = sign_rsa_sha256_pkcs15(toSign, selected_cert_bundle->rsa_key); sigAlg = 0x0401; // rsa_pkcs1_sha256 netplus::rsa& rsaKey = selected_cert_bundle->rsa_key; sigAlg = pickTls12RsaSigalg(parseTls12Offer(clientHelloRawBytes)); switch (sigAlg) { case 0x0501: sig = rsa_pkcs1_sha384::sign(rsaKey, toSign); break; case 0x0804: sig = rsa_pss_sha256::sign(rsaKey, toSign); break; case 0x0805: sig = rsa_pss_sha384::sign(rsaKey, toSign); break; default: sig = sign_rsa_sha256_pkcs15(toSign, rsaKey); break; } } // Build ServerKeyExchange body: params + sig_algorithm(2) + sig_len(2) + sig Loading Loading @@ -1222,7 +1306,17 @@ namespace netplus { bool isECDHE = (chosenSuite == 0xC02B || chosenSuite == 0xC02C || chosenSuite == 0xC02F || chosenSuite == 0xC030); if (isECDHE) { if (isECDHE && selected_group == 0x001d) { // x25519 (RFC 8422 5.11): 32-byte keys, the shared secret is the premaster if (hlen != 33 || msg[4] != 32) throwSSL(NetException::Error, "ECDHE CKE invalid x25519 key"); std::vector<uint8_t> clientPub(msg.begin() + 5, msg.end()); if (!netplus::scalarmult_curve25519(preMaster, server_priv_x25519, clientPub)) throwSSL(NetException::Error, "ECDHE shared secret computation failed"); if (std::all_of(preMaster.begin(), preMaster.end(), [](uint8_t b) { return b == 0; })) throwSSL(NetException::Error, "ECDHE CKE: x25519 low-order key"); tls12_is_gcm = true; } else if (isECDHE) { // ECDHE CKE: body = point_length(1) + uncompressed_point(65) size_t off = 4; if (hlen < 1) Loading