Loading src/crypto/tls.cpp +101 −5 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ #include <fstream> #include "tls.h" #include "ecdsa_verify.h" #include "x509.h" #include "sha.h" #include "base64.h" #include "rsa_pss_sha256.h" Loading Loading @@ -36,6 +38,44 @@ void setCertRequest(const netplus::tls *t, std::vector<uint8_t> context) { } // true (and the context) once if a certificate was asked for // Client, TLS 1.2: the leaf certificate's EC key, for the ServerKeyExchange // signature (kept here for the same ABI reason) struct EcKey { std::string curve; std::vector<uint8_t> pub; }; std::map<const netplus::tls *, EcKey> g_ecKeys; // Client, TLS 1.2: the ECDHE group the server chose (secp256r1 or x25519) std::map<const netplus::tls *, uint16_t> g_ecdheGroups; void setEcdheGroup(const netplus::tls *t, uint16_t g) { std::lock_guard<std::mutex> l(g_certRequestsLock); g_ecdheGroups[t] = g; } uint16_t takeEcdheGroup(const netplus::tls *t) { std::lock_guard<std::mutex> l(g_certRequestsLock); auto it = g_ecdheGroups.find(t); if (it == g_ecdheGroups.end()) return 0x0017; uint16_t g = it->second; g_ecdheGroups.erase(it); return g; } void setEcKey(const netplus::tls *t, EcKey k) { std::lock_guard<std::mutex> l(g_certRequestsLock); g_ecKeys[t] = std::move(k); } bool takeEcKey(const netplus::tls *t, EcKey &k) { std::lock_guard<std::mutex> l(g_certRequestsLock); auto it = g_ecKeys.find(t); if (it == g_ecKeys.end()) return false; k = std::move(it->second); g_ecKeys.erase(it); return true; } 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); Loading Loading @@ -2349,7 +2389,11 @@ namespace netplus { serverCert.loadFromBuffer(derCert); if (serverCert.isECKey()) { server_cert_is_ecdsa = true; // EC public key will be used in ECDHE; no per-cert key needed here // its key checks the ServerKeyExchange signature EcKey k; k.curve = serverCert.getECCurveOID(); if (k.curve.empty()) k.curve = netplus::CurveP256; if (serverCert.extractECPublicKey(k.pub)) setEcKey(this, std::move(k)); } else { server_cert_is_ecdsa = false; if (!serverCert.extractPublicKey(peer_rsa)) { Loading Loading @@ -2432,8 +2476,13 @@ namespace netplus { } size_t off = 4; // skip handshake header uint8_t curve_type = msg[off++]; // 0x03 = named_curve (void)curve_type; off += 2; // skip named_curve (0x0017 = secp256r1) uint16_t group = (uint16_t(msg[off]) << 8) | msg[off + 1]; off += 2; // secp256r1, or x25519 (offered in supported_groups for TLS 1.3, so a // TLS 1.2 server may pick it for ECDHE too) if (curve_type != 3 || (group != 0x0017 && group != 0x001d)) throwSSL(NetException::Error, "ServerKeyExchange: unsupported ECDHE group " + std::to_string(group)); setEcdheGroup(this, group); uint8_t pubkey_len = msg[off++]; if (off + pubkey_len > msg.size()) { NetException e; Loading @@ -2441,7 +2490,35 @@ namespace netplus { throw e; } tls12_srv_ecdhe_pub.assign(msg.begin() + off, msg.begin() + off + pubkey_len); // Signature is after the key — skip verification (like cert verification) off += pubkey_len; // The signature (RFC 5246 7.4.3, RFC 8422 5.4): over client_random | // server_random | the ECDH parameters, with the certificate's key. // Without it a man in the middle could put in an ECDH key of its own // while passing the real certificate through. { EcKey key; bool haveKey = takeEcKey(this, key); if (trust_policy.verifyPeer) { if (off + 4 > msg.size()) throwSSL(NetException::Error, "ServerKeyExchange without signature"); uint8_t hashAlg = msg[off], sigAlg = msg[off + 1]; size_t sigLen = (size_t(msg[off + 2]) << 8) | msg[off + 3]; if (off + 4 + sigLen > msg.size()) throwSSL(NetException::Error, "ServerKeyExchange signature truncated"); std::vector<uint8_t> signedData = clientRandom; signedData.insert(signedData.end(), serverRandom.begin(), serverRandom.end()); signedData.insert(signedData.end(), msg.begin() + 4, msg.begin() + off); std::vector<uint8_t> digest = hashAlg == 4 ? sha256_hash(signedData) : hashAlg == 5 ? sha384_hash(signedData) : std::vector<uint8_t>(); if (sigAlg != 3 || digest.empty()) throwSSL(NetException::Error, "ServerKeyExchange: unsupported signature algorithm"); std::vector<uint8_t> sig(msg.begin() + off + 4, msg.begin() + off + 4 + sigLen); if (!haveKey || !netplus::ecdsa_verify_digest(key.curve, key.pub, digest, sig)) throwSSL(NetException::Error, "ServerKeyExchange signature invalid"); } } // Add ServerKeyExchange to transcript (RFC 5246) handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end()); Loading Loading @@ -2497,7 +2574,23 @@ namespace netplus { std::vector<uint8_t> preMaster; std::vector<uint8_t> ckeBody; if (server_cert_is_ecdsa && !tls12_srv_ecdhe_pub.empty()) { uint16_t group = server_cert_is_ecdsa ? takeEcdheGroup(this) : 0; if (group == 0x001d && !tls12_srv_ecdhe_pub.empty()) { // ECDHE over x25519 (RFC 8422 5.11): 32-byte keys, the shared secret is the premaster if (tls12_srv_ecdhe_pub.size() != 32) throwSSL(NetException::Error, "ECDHE CKE: bad x25519 server key"); std::vector<uint8_t> priv(32), pub, shared; fillRandomBytes(priv.data(), priv.size()); if (!netplus::scalarmult_curve25519_base(pub, priv) || !netplus::scalarmult_curve25519(shared, priv, tls12_srv_ecdhe_pub)) throwSSL(NetException::Error, "ECDHE CKE: x25519 failed"); bool zero = true; for (uint8_t b : shared) zero = zero && b == 0; if (zero) throwSSL(NetException::Error, "ECDHE CKE: x25519 gave a zero secret"); preMaster = shared; ckeBody.push_back(uint8_t(pub.size())); ckeBody.insert(ckeBody.end(), pub.begin(), pub.end()); } else if (server_cert_is_ecdsa && !tls12_srv_ecdhe_pub.empty()) { // ECDHE-ECDSA: generate ephemeral client key pair and compute shared secret uint8_t cli_priv[32]; for (int retry = 0; retry < 10; ++retry) { Loading Loading @@ -4930,6 +5023,9 @@ namespace netplus { { std::vector<uint8_t> stale; takeCertRequest(this, stale); EcKey staleKey; takeEcKey(this, staleKey); takeEcdheGroup(this); } // Build ClientHello message body Loading Loading
src/crypto/tls.cpp +101 −5 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ #include <fstream> #include "tls.h" #include "ecdsa_verify.h" #include "x509.h" #include "sha.h" #include "base64.h" #include "rsa_pss_sha256.h" Loading Loading @@ -36,6 +38,44 @@ void setCertRequest(const netplus::tls *t, std::vector<uint8_t> context) { } // true (and the context) once if a certificate was asked for // Client, TLS 1.2: the leaf certificate's EC key, for the ServerKeyExchange // signature (kept here for the same ABI reason) struct EcKey { std::string curve; std::vector<uint8_t> pub; }; std::map<const netplus::tls *, EcKey> g_ecKeys; // Client, TLS 1.2: the ECDHE group the server chose (secp256r1 or x25519) std::map<const netplus::tls *, uint16_t> g_ecdheGroups; void setEcdheGroup(const netplus::tls *t, uint16_t g) { std::lock_guard<std::mutex> l(g_certRequestsLock); g_ecdheGroups[t] = g; } uint16_t takeEcdheGroup(const netplus::tls *t) { std::lock_guard<std::mutex> l(g_certRequestsLock); auto it = g_ecdheGroups.find(t); if (it == g_ecdheGroups.end()) return 0x0017; uint16_t g = it->second; g_ecdheGroups.erase(it); return g; } void setEcKey(const netplus::tls *t, EcKey k) { std::lock_guard<std::mutex> l(g_certRequestsLock); g_ecKeys[t] = std::move(k); } bool takeEcKey(const netplus::tls *t, EcKey &k) { std::lock_guard<std::mutex> l(g_certRequestsLock); auto it = g_ecKeys.find(t); if (it == g_ecKeys.end()) return false; k = std::move(it->second); g_ecKeys.erase(it); return true; } 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); Loading Loading @@ -2349,7 +2389,11 @@ namespace netplus { serverCert.loadFromBuffer(derCert); if (serverCert.isECKey()) { server_cert_is_ecdsa = true; // EC public key will be used in ECDHE; no per-cert key needed here // its key checks the ServerKeyExchange signature EcKey k; k.curve = serverCert.getECCurveOID(); if (k.curve.empty()) k.curve = netplus::CurveP256; if (serverCert.extractECPublicKey(k.pub)) setEcKey(this, std::move(k)); } else { server_cert_is_ecdsa = false; if (!serverCert.extractPublicKey(peer_rsa)) { Loading Loading @@ -2432,8 +2476,13 @@ namespace netplus { } size_t off = 4; // skip handshake header uint8_t curve_type = msg[off++]; // 0x03 = named_curve (void)curve_type; off += 2; // skip named_curve (0x0017 = secp256r1) uint16_t group = (uint16_t(msg[off]) << 8) | msg[off + 1]; off += 2; // secp256r1, or x25519 (offered in supported_groups for TLS 1.3, so a // TLS 1.2 server may pick it for ECDHE too) if (curve_type != 3 || (group != 0x0017 && group != 0x001d)) throwSSL(NetException::Error, "ServerKeyExchange: unsupported ECDHE group " + std::to_string(group)); setEcdheGroup(this, group); uint8_t pubkey_len = msg[off++]; if (off + pubkey_len > msg.size()) { NetException e; Loading @@ -2441,7 +2490,35 @@ namespace netplus { throw e; } tls12_srv_ecdhe_pub.assign(msg.begin() + off, msg.begin() + off + pubkey_len); // Signature is after the key — skip verification (like cert verification) off += pubkey_len; // The signature (RFC 5246 7.4.3, RFC 8422 5.4): over client_random | // server_random | the ECDH parameters, with the certificate's key. // Without it a man in the middle could put in an ECDH key of its own // while passing the real certificate through. { EcKey key; bool haveKey = takeEcKey(this, key); if (trust_policy.verifyPeer) { if (off + 4 > msg.size()) throwSSL(NetException::Error, "ServerKeyExchange without signature"); uint8_t hashAlg = msg[off], sigAlg = msg[off + 1]; size_t sigLen = (size_t(msg[off + 2]) << 8) | msg[off + 3]; if (off + 4 + sigLen > msg.size()) throwSSL(NetException::Error, "ServerKeyExchange signature truncated"); std::vector<uint8_t> signedData = clientRandom; signedData.insert(signedData.end(), serverRandom.begin(), serverRandom.end()); signedData.insert(signedData.end(), msg.begin() + 4, msg.begin() + off); std::vector<uint8_t> digest = hashAlg == 4 ? sha256_hash(signedData) : hashAlg == 5 ? sha384_hash(signedData) : std::vector<uint8_t>(); if (sigAlg != 3 || digest.empty()) throwSSL(NetException::Error, "ServerKeyExchange: unsupported signature algorithm"); std::vector<uint8_t> sig(msg.begin() + off + 4, msg.begin() + off + 4 + sigLen); if (!haveKey || !netplus::ecdsa_verify_digest(key.curve, key.pub, digest, sig)) throwSSL(NetException::Error, "ServerKeyExchange signature invalid"); } } // Add ServerKeyExchange to transcript (RFC 5246) handshake_transcript.insert(handshake_transcript.end(), msg.begin(), msg.end()); Loading Loading @@ -2497,7 +2574,23 @@ namespace netplus { std::vector<uint8_t> preMaster; std::vector<uint8_t> ckeBody; if (server_cert_is_ecdsa && !tls12_srv_ecdhe_pub.empty()) { uint16_t group = server_cert_is_ecdsa ? takeEcdheGroup(this) : 0; if (group == 0x001d && !tls12_srv_ecdhe_pub.empty()) { // ECDHE over x25519 (RFC 8422 5.11): 32-byte keys, the shared secret is the premaster if (tls12_srv_ecdhe_pub.size() != 32) throwSSL(NetException::Error, "ECDHE CKE: bad x25519 server key"); std::vector<uint8_t> priv(32), pub, shared; fillRandomBytes(priv.data(), priv.size()); if (!netplus::scalarmult_curve25519_base(pub, priv) || !netplus::scalarmult_curve25519(shared, priv, tls12_srv_ecdhe_pub)) throwSSL(NetException::Error, "ECDHE CKE: x25519 failed"); bool zero = true; for (uint8_t b : shared) zero = zero && b == 0; if (zero) throwSSL(NetException::Error, "ECDHE CKE: x25519 gave a zero secret"); preMaster = shared; ckeBody.push_back(uint8_t(pub.size())); ckeBody.insert(ckeBody.end(), pub.begin(), pub.end()); } else if (server_cert_is_ecdsa && !tls12_srv_ecdhe_pub.empty()) { // ECDHE-ECDSA: generate ephemeral client key pair and compute shared secret uint8_t cli_priv[32]; for (int retry = 0; retry < 10; ++retry) { Loading Loading @@ -4930,6 +5023,9 @@ namespace netplus { { std::vector<uint8_t> stale; takeCertRequest(this, stale); EcKey staleKey; takeEcKey(this, staleKey); takeEcdheGroup(this); } // Build ClientHello message body Loading