Commit 5b3fdcda authored by jan.koester's avatar jan.koester
Browse files

dyn cipher select

parent bfee4359
Loading
Loading
Loading
Loading
+104 −10
Changes for src/crypto/tls.cpp: 104 added lines, 10 removed lines.
Original line number Diff line number Diff line
@@ -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"
@@ -21,6 +23,7 @@
#include "../socket.h"
#include "../exception.h"

#include <algorithm>
#include <map>
#include <mutex>

@@ -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
@@ -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).
@@ -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());

@@ -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
@@ -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)