Commit 1e60964a authored by jan.koester's avatar jan.koester
Browse files
parents c4b9b40f 763a8fad
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
libnetplus (20260528+23) unstable; urgency=medium

  * ecc: fix mod_n256 infinite loop in ECDSA signing — replace O(2^32)
    repeated subtraction with O(512) bit-by-bit long division
  * tls: implement ECDHE-ECDSA-AES128-GCM-SHA256 cipher suite for
    TLS 1.2 with Let's Encrypt ECDSA certificates
  * tls: fix TLS 1.3 key buffer overflow (16→32 bytes for AES-256)
  * tls: add GCM record encryption/decryption for TLS 1.2

 -- Jan Koester <jan.koester@tuxist.de>  Wed, 28 May 2026 12:00:00 +0200

libnetplus (20260521+22) unstable; urgency=medium

  * x509: add getNotAfter() method for certificate expiry checking
+27 −81
Original line number Diff line number Diff line
@@ -456,27 +456,15 @@ static const u256 SCALAR_N = {{
}};

// Reduction mod n (group order) - used for ECDSA scalar arithmetic
// Uses schoolbook division since n doesn't have a special form like p
// Bit-by-bit long division: O(512) iterations, each O(8) word ops
u256 mod_n256(const u512& in) {
    // For a 512-bit value, we do trial subtraction of n*2^i for i from 256 down to 0
    // This is slow but correct. For production, use Barrett reduction.
    
    // Work with extended precision
    uint64_t t[16];
    for (int i = 0; i < 16; i++) t[i] = in.w[i];
    
    // Reduce: while value >= n * 2^256, subtract n * 2^256
    // Then while value >= n * 2^224, subtract n * 2^224, etc.
    
    // Simple approach: convert to u256, handle overflow with subtraction
    // First, check if high 256 bits are non-zero
    // Fast path: if high 256 bits are zero, just reduce low 256 bits
    bool hasHigh = false;
    for (int i = 8; i < 16; i++) if (t[i] != 0) { hasHigh = true; break; }
    for (int i = 8; i < 16; i++) if (in.w[i] != 0) { hasHigh = true; break; }

    if (!hasHigh) {
        // Just low 256 bits - simple reduction
        u256 r;
        for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i];
        for (int i = 0; i < 8; i++) r.w[i] = in.w[i];
        while (u256_cmp(r, SCALAR_N) >= 0) {
            uint32_t br = 0;
            r = u256_sub_raw(r, SCALAR_N, br);
@@ -484,70 +472,28 @@ u256 mod_n256(const u512& in) {
        return r;
    }

    // Full 512-bit reduction using repeated subtraction
    // This is O(n) in the worst case but works for random inputs
    
    // Compute q ≈ floor(in / n) using high bits estimation
    // Then r = in - q*n and adjust
    
    // For simplicity, use a loop that processes word by word from high to low
    // Each iteration reduces the value by subtracting n shifted appropriately
    
    for (int shift = 256; shift >= 0; shift -= 32) {
        // Check if we can subtract n << shift
        int wordShift = shift / 32;
        
        while (true) {
            // Check if t >= n << shift
            bool canSubtract = false;
            
            // Compare t with n << wordShift
            int topWord = wordShift + 7;
            if (topWord < 16) {
                // Check from top
                bool greater = false;
                bool equal = true;
                for (int i = 15; i >= 0; i--) {
                    uint64_t nShifted = 0;
                    int ni = i - wordShift;
                    if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni];
                    
                    if (t[i] > nShifted) { greater = true; equal = false; break; }
                    if (t[i] < nShifted) { greater = false; equal = false; break; }
                }
                canSubtract = greater || equal;
            }
            
            if (!canSubtract) break;
            
            // Subtract n << wordShift from t
            int64_t borrow = 0;
            for (int i = 0; i < 16; i++) {
                uint64_t nShifted = 0;
                int ni = i - wordShift;
                if (ni >= 0 && ni < 8) nShifted = SCALAR_N.w[ni];
                
                int64_t diff = (int64_t)t[i] - (int64_t)nShifted - borrow;
                if (diff < 0) {
                    diff += 0x100000000LL;
                    borrow = 1;
                } else {
                    borrow = 0;
                }
                t[i] = (uint64_t)diff;
            }
        }
    // Bit-by-bit long division from MSB (bit 511) to LSB (bit 0)
    u256 r = {};
    for (int bit = 511; bit >= 0; bit--) {
        // Shift remainder left by 1
        uint32_t carry = 0;
        for (int i = 0; i < 8; i++) {
            uint32_t newCarry = r.w[i] >> 31;
            r.w[i] = (r.w[i] << 1) | carry;
            carry = newCarry;
        }

    // Result is now in t[0..7]
    u256 r;
    for (int i = 0; i < 8; i++) r.w[i] = (uint32_t)t[i];
        // Bring down next bit from input
        int wordIdx = bit / 32;
        int bitIdx  = bit % 32;
        r.w[0] |= (in.w[wordIdx] >> bitIdx) & 1;

    // Final normalization
    while (u256_cmp(r, SCALAR_N) >= 0) {
        // If carry (value >= 2^256 > n) or r >= n, subtract n
        if (carry || u256_cmp(r, SCALAR_N) >= 0) {
            uint32_t br = 0;
            r = u256_sub_raw(r, SCALAR_N, br);
        }
    }

    return r;
}
+291 −50
Original line number Diff line number Diff line
@@ -476,8 +476,22 @@ namespace netplus {
                handshakeStarted = true;
                is_tls13 = false;
                chosenSuite = 0;
                if (hasSuite(0x002F))      chosenSuite = 0x002F;
                else if (hasSuite(0x0035)) chosenSuite = 0x0035;

                // Prefer ECDHE-GCM suites (work with both RSA and ECDSA certs)
                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);

                if (have_ec_cert) {
                    // ECDSA certificate: only ECDHE_ECDSA suites
                    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
                    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");
@@ -857,6 +871,61 @@ namespace netplus {
                    }

                    sendHandshake(0x0b, certMsg);

                    // --- ServerKeyExchange for ECDHE suites ---
                    bool isECDHE = (chosenSuite == 0xC02B || chosenSuite == 0xC02C ||
                                    chosenSuite == 0xC02F || chosenSuite == 0xC030);
                    if (isECDHE) {
                        // Generate ephemeral ECDHE P-256 key pair
                        gen_tls13_p256_scalar(tls12_ecdhe_priv);
                        netplus::P256Point pub = netplus::scalar_mul_G(tls12_ecdhe_priv);
                        std::vector<uint8_t> pubBytes = netplus::encode_tls_point(pub);

                        // Build ServerKeyExchange params (ECParameters + ECPoint)
                        // ECParameters: curve_type(1) = named_curve(3), named_curve(2) = secp256r1(0x0017)
                        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)pubBytes.size()); // point length
                        params.insert(params.end(), pubBytes.begin(), pubBytes.end());

                        // Sign: SHA-256(client_random + server_random + params)
                        std::vector<uint8_t> toSign;
                        toSign.insert(toSign.end(), clientRandom.begin(), clientRandom.end());
                        toSign.insert(toSign.end(), serverRandom.begin(), serverRandom.end());
                        toSign.insert(toSign.end(), params.begin(), params.end());

                        std::vector<uint8_t> sig;
                        uint16_t sigAlg;

                        if (chosenSuite == 0xC02B || chosenSuite == 0xC02C) {
                            // ECDHE_ECDSA: sign with ECDSA
                            if (!selected_cert_bundle->has_ec_key)
                                throwSSL(NetException::Error, "ECDHE_ECDSA requires EC key");
                            std::vector<uint8_t> ecKey(selected_cert_bundle->ecPrivateKey,
                                                       selected_cert_bundle->ecPrivateKey + 32);
                            sig = sign_ecdsa_sha256(toSign, ecKey);
                            sigAlg = 0x0403; // ecdsa_secp256r1_sha256
                        } else {
                            // ECDHE_RSA: sign with RSA PKCS#1 v1.5
                            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
                        }

                        // Build ServerKeyExchange body: params + sig_algorithm(2) + sig_len(2) + sig
                        std::vector<uint8_t> skeBody;
                        skeBody.insert(skeBody.end(), params.begin(), params.end());
                        skeBody.push_back((sigAlg >> 8) & 0xFF);
                        skeBody.push_back(sigAlg & 0xFF);
                        skeBody.push_back((uint16_t(sig.size()) >> 8) & 0xFF);
                        skeBody.push_back(uint16_t(sig.size()) & 0xFF);
                        skeBody.insert(skeBody.end(), sig.begin(), sig.end());

                        sendHandshake(0x0c, skeBody); // ServerKeyExchange
                    }

                    sendHandshake(0x0e, {});

                    serverFlightQueued = true;
@@ -938,7 +1007,7 @@ namespace netplus {
                uint16_t finVer = ((uint16_t)finRec[1] << 8) | (uint16_t)finRec[2];
                std::vector<uint8_t> finFrag(finRec.begin() + 5, finRec.end());

                std::vector<uint8_t> finPT = decryptRecordCBC(0x16, finVer, finFrag);
                std::vector<uint8_t> finPT = decryptTLS12Record(0x16, finVer, finFrag);

                if (finPT.size() < 4 + 12 ||
                    finPT[0] != 0x14 || finPT[1] != 0x00 || finPT[2] != 0x00 || finPT[3] != 0x0c) {
@@ -1047,17 +1116,44 @@ namespace netplus {
                if (msg.size() != 4 + hlen)
                    throwSSL(NetException::Error, "ClientKeyExchange length mismatch");

                std::vector<uint8_t> preMaster;
                bool isECDHE = (chosenSuite == 0xC02B || chosenSuite == 0xC02C ||
                                chosenSuite == 0xC02F || chosenSuite == 0xC030);

                if (isECDHE) {
                    // ECDHE CKE: body = point_length(1) + uncompressed_point(65)
                    size_t off = 4;
                    if (hlen < 1)
                        throwSSL(NetException::Error, "ECDHE CKE body too short");
                    uint8_t ptLen = msg[off]; off++;
                    if (ptLen != 65 || off + ptLen > msg.size())
                        throwSSL(NetException::Error, "ECDHE CKE invalid point length");
                    if (msg[off] != 0x04)
                        throwSSL(NetException::Error, "ECDHE CKE point not uncompressed");

                    netplus::P256Point clientPub;
                    if (!netplus::decode_tls_point(clientPub, msg.data() + off, 65))
                        throwSSL(NetException::Error, "ECDHE CKE invalid EC point");
                    if (!netplus::is_on_curve(clientPub))
                        throwSSL(NetException::Error, "ECDHE CKE point not on curve");

                    // Compute shared secret
                    preMaster.resize(32);
                    if (!netplus::ecdh_shared_secret(preMaster.data(), tls12_ecdhe_priv, clientPub))
                        throwSSL(NetException::Error, "ECDHE shared secret computation failed");

                    tls12_is_gcm = true;
                } else {
                    // RSA CKE body: uint16 encLen + ciphertext
                    if (hlen < 2)
                    throwSSL(NetException::Error, "ClientKeyExchange body too short");
                        throwSSL(NetException::Error, "RSA CKE body too short");

                    size_t off = 4;

                    uint16_t encLen = (uint16_t(msg[off]) << 8) | msg[off + 1];
                    off += 2;

                    if (off + encLen > msg.size())
                    throwSSL(NetException::Error, "ClientKeyExchange truncated ciphertext");
                        throwSSL(NetException::Error, "RSA CKE truncated ciphertext");

                    const size_t kBytes = (selected_cert_bundle->rsa_key.n.bitLength() + 7) / 8;
                    if (encLen != kBytes) {
@@ -1066,47 +1162,62 @@ namespace netplus {
                            " kBytes=" + std::to_string(kBytes));
                    }

    #if SSL_DEBUG
    #endif

                    rsa::bigInt cipher = rsa::bigIntFromBytesBE(msg.data() + off, encLen);
                    rsa::bigInt plainBI = selected_cert_bundle->rsa_key.decrypt(cipher);

    #if SSL_DEBUG
    #endif

                    std::vector<uint8_t> pkcs1 = rsa::bigIntToBytesBE(plainBI, kBytes);
                std::vector<uint8_t> preMaster = extractPreMasterFromPkcs1(pkcs1);

    #if SSL_DEBUG
    #endif
                    preMaster = extractPreMasterFromPkcs1(pkcs1);
                }

                // master_secret = PRF(PMS, "master secret", client_random || server_random)
                std::vector<uint8_t> msSeed = clientRandom;
                msSeed.insert(msSeed.end(), serverRandom.begin(), serverRandom.end());
                masterSecret = prf(preMaster, "master secret", msSeed, 48);

    #if SSL_DEBUG
    #endif

                // key_block = PRF(master, "key expansion", server_random || client_random)
                std::vector<uint8_t> kbSeed = serverRandom;
                kbSeed.insert(kbSeed.end(), clientRandom.begin(), clientRandom.end());
                std::vector<uint8_t> keyBlock = prf(masterSecret, "key expansion", kbSeed, 72);

    #if SSL_DEBUG
    #endif
                if (tls12_is_gcm) {
                    // GCM key block: client_write_key + server_write_key + client_write_IV(4) + server_write_IV(4)
                    size_t keyLen = (chosenSuite == 0xC02C || chosenSuite == 0xC030) ? 32 : 16;
                    size_t kbLen = keyLen + keyLen + 4 + 4;
                    std::vector<uint8_t> keyBlock = prf(masterSecret, "key expansion", kbSeed, kbLen);

                    size_t k = 0;
                    std::vector<uint8_t> clientKey(keyBlock.begin() + k, keyBlock.begin() + k + keyLen); k += keyLen;
                    std::vector<uint8_t> serverKey(keyBlock.begin() + k, keyBlock.begin() + k + keyLen); k += keyLen;
                    std::memcpy(tls12_client_write_iv, keyBlock.data() + k, 4); k += 4;
                    std::memcpy(tls12_server_write_iv, keyBlock.data() + k, 4);

                    if (keyLen == 32) {
                        aes_recv = std::make_unique<aes256>(clientKey);
                        aes      = std::make_unique<aes256>(serverKey);
                    } else {
                        aes_recv = std::make_unique<aes128>(clientKey);
                        aes      = std::make_unique<aes128>(serverKey);
                    }
                } else {
                    // CBC key block: client_mac + server_mac + client_key + server_key
                    size_t keyLen = (chosenSuite == 0x0035) ? 32 : 16;
                    size_t kbLen = 20 + 20 + keyLen + keyLen;
                    std::vector<uint8_t> keyBlock = prf(masterSecret, "key expansion", kbSeed, kbLen);

                    size_t k = 0;
                    client_mac_key.assign(keyBlock.begin() + k, keyBlock.begin() + k + 20); k += 20;
                    mac_key.assign(keyBlock.begin() + k, keyBlock.begin() + k + 20);        k += 20;

                std::vector<uint8_t> clientKey(keyBlock.begin() + k, keyBlock.begin() + k + 16); k += 16;
                std::vector<uint8_t> serverKey(keyBlock.begin() + k, keyBlock.begin() + k + 16);
                    std::vector<uint8_t> clientKey(keyBlock.begin() + k, keyBlock.begin() + k + keyLen); k += keyLen;
                    std::vector<uint8_t> serverKey(keyBlock.begin() + k, keyBlock.begin() + k + keyLen);

                    if (keyLen == 32) {
                        aes_recv = std::make_unique<aes256>(clientKey);
                        aes      = std::make_unique<aes256>(serverKey);
                    } else {
                        aes_recv = std::make_unique<aes128>(clientKey);
                        aes      = std::make_unique<aes128>(serverKey);

                    }
                }

                hs_state = HsState::WAIT_CCS;
                return;
@@ -2238,7 +2349,7 @@ namespace netplus {
                    std::vector<uint8_t> finFrag(rec.begin() + 5, rec.end());

                    // Decrypt the record
                    std::vector<uint8_t> finPT = decryptRecordCBC(0x16, finVer, finFrag);
                    std::vector<uint8_t> finPT = decryptTLS12Record(0x16, finVer, finFrag);

                    if (finPT.size() < 4 + 12 ||
                        finPT[0] != 0x14 || finPT[1] != 0x00 || finPT[2] != 0x00 || finPT[3] != 0x0c) {
@@ -2368,7 +2479,7 @@ namespace netplus {
                if (!aes_recv)
                    throwSSL(NetException::Error, "aes_recv missing");

                std::vector<uint8_t> plain = decryptRecordCBC(outer_type, ver, frag);
                std::vector<uint8_t> plain = decryptTLS12Record(outer_type, ver, frag);

                if (plain.empty())
                    continue;
@@ -3166,6 +3277,8 @@ namespace netplus {
    // sendTLS12Record - Encrypt and send TLS 1.2 record
    // ============================================================================
    void tls::sendTLS12Record(uint8_t type, const std::vector<uint8_t>& plain) {
        if (tls12_is_gcm) { sendTLS12RecordGCM(type, plain); return; }

        const uint16_t ver = 0x0303;
        const size_t macLen = 20;
        const size_t blockSize = 16;
@@ -3236,6 +3349,8 @@ namespace netplus {
    }

    void tls::sendTLS12Record(uint8_t type, const uint8_t* plain_data, size_t plain_len) {
        if (tls12_is_gcm) { sendTLS12RecordGCM(type, plain_data, plain_len); return; }

        const uint16_t ver = 0x0303;
        const size_t macLen = 20;
        const size_t blockSize = 16;
@@ -3304,6 +3419,130 @@ namespace netplus {
        send_seq++;
    }

    // ============================================================================
    // decryptRecordGCM - Decrypt TLS 1.2 GCM record
    // ============================================================================
    std::vector<uint8_t> tls::decryptRecordGCM(
        uint8_t type, uint16_t ver, const std::vector<uint8_t>& payload) 
    {
        if (!aes_recv) {
            NetException e;
            e[NetException::Error] << "tls: decryptRecordGCM called but aes_recv is NULL";
            throw e;
        }

        // GCM record payload: explicit_nonce(8) + ciphertext + tag(16)
        if (payload.size() < 8 + 16) {
            NetException e;
            e[NetException::Error] << "tls: GCM record too short";
            throw e;
        }

        const uint8_t* explicit_nonce = payload.data();
        size_t ct_len = payload.size() - 8 - 16;
        const uint8_t* ct = payload.data() + 8;
        const uint8_t* tag = payload.data() + 8 + ct_len;

        // Nonce: implicit_iv(4) || explicit_nonce(8) = 12 bytes
        uint8_t nonce[12];
        const uint8_t* recv_iv = is_client ? tls12_server_write_iv : tls12_client_write_iv;
        std::memcpy(nonce, recv_iv, 4);
        std::memcpy(nonce + 4, explicit_nonce, 8);

        // AAD: seq(8) + type(1) + version(2) + plaintext_length(2) = 13 bytes
        uint8_t aad[13];
        seqToBytes(recv_seq, aad);
        aad[8] = type;
        aad[9] = uint8_t(ver >> 8);
        aad[10] = uint8_t(ver & 0xFF);
        aad[11] = uint8_t((ct_len >> 8) & 0xFF);
        aad[12] = uint8_t(ct_len & 0xFF);

        std::vector<uint8_t> plaintext(ct_len);
        uint8_t tag_copy[16];
        std::memcpy(tag_copy, tag, 16);

        if (!aes_recv->aes_gcm_decrypt(nonce, aad, 13, ct, ct_len, tag_copy, plaintext.data())) {
            NetException e;
            e[NetException::Error] << "tls: GCM tag verification failed";
            throw e;
        }

        recv_seq++;
        return plaintext;
    }

    // ============================================================================
    // decryptTLS12Record - Dispatch to CBC or GCM decrypt
    // ============================================================================
    std::vector<uint8_t> tls::decryptTLS12Record(
        uint8_t type, uint16_t ver, const std::vector<uint8_t>& payload)
    {
        if (tls12_is_gcm)
            return decryptRecordGCM(type, ver, payload);
        return decryptRecordCBC(type, ver, payload);
    }

    // ============================================================================
    // sendTLS12RecordGCM - Encrypt and send TLS 1.2 GCM record (vector overload)
    // ============================================================================
    void tls::sendTLS12RecordGCM(uint8_t type, const std::vector<uint8_t>& plain) {
        sendTLS12RecordGCM(type, plain.data(), plain.size());
    }

    // ============================================================================
    // sendTLS12RecordGCM - Encrypt and send TLS 1.2 GCM record (pointer overload)
    // ============================================================================
    void tls::sendTLS12RecordGCM(uint8_t type, const uint8_t* plain_data, size_t plain_len) {
        const uint16_t ver = 0x0303;

        if (!aes) {
            NetException e;
            e[NetException::Error] << "tls: sendTLS12RecordGCM: AES cipher not initialized";
            throw e;
        }

        // Build nonce: implicit_iv(4) || explicit_nonce(8) = 12 bytes
        // Use send_seq as explicit nonce
        uint8_t explicit_nonce[8];
        seqToBytes(send_seq, explicit_nonce);

        uint8_t nonce[12];
        const uint8_t* send_iv = is_client ? tls12_client_write_iv : tls12_server_write_iv;
        std::memcpy(nonce, send_iv, 4);
        std::memcpy(nonce + 4, explicit_nonce, 8);

        // AAD: seq(8) + type(1) + version(2) + plaintext_length(2) = 13 bytes
        uint8_t aad[13];
        seqToBytes(send_seq, aad);
        aad[8] = type;
        aad[9] = uint8_t(ver >> 8);
        aad[10] = uint8_t(ver & 0xFF);
        aad[11] = uint8_t((plain_len >> 8) & 0xFF);
        aad[12] = uint8_t(plain_len & 0xFF);

        // Encrypt
        std::vector<uint8_t> ct(plain_len);
        uint8_t tag[16];
        aes->aes_gcm_encrypt(nonce, aad, 13, plain_data, plain_len, ct.data(), tag);

        // Record payload: explicit_nonce(8) + ciphertext + tag(16)
        size_t payload_len = 8 + plain_len + 16;
        uint8_t recHdr[5];
        recHdr[0] = type;
        recHdr[1] = uint8_t(ver >> 8);
        recHdr[2] = uint8_t(ver & 0xFF);
        recHdr[3] = uint8_t((payload_len >> 8) & 0xFF);
        recHdr[4] = uint8_t(payload_len & 0xFF);

        queueRaw(recHdr, 5);
        queueRaw(explicit_nonce, 8);
        queueRaw(ct.data(), ct.size());
        queueRaw(tag, 16);

        send_seq++;
    }

    // ============================================================================
    // sendTLS13Record - Encrypt and send TLS 1.3 application data record
    // ============================================================================
@@ -3568,7 +3807,7 @@ namespace netplus {

            if (ccs_received && aes_recv) {
                uint16_t ver = (uint16_t(rec[1]) << 8) | uint16_t(rec[2]);
                fragment = decryptRecordCBC(recType, ver, fragment);
                fragment = decryptTLS12Record(recType, ver, fragment);
            }

            rx_handshake_buf.insert(rx_handshake_buf.end(), 
@@ -3613,8 +3852,10 @@ namespace netplus {
            throw e;
        };

        // Sanity: allow only TLS1.2 suites here
        if (chosenSuiteArg != 0x002F && chosenSuiteArg != 0x0035) {
        // Sanity: allow TLS1.2 suites (RSA + ECDHE-GCM)
        if (chosenSuiteArg != 0x002F && chosenSuiteArg != 0x0035 &&
            chosenSuiteArg != 0xC02B && chosenSuiteArg != 0xC02C &&
            chosenSuiteArg != 0xC02F && chosenSuiteArg != 0xC030) {
            throwSSL(netplus::NetException::Error, "Invalid TLS1.2 cipher suite selected");
        }

+33 −4

File changed.

Preview size limit exceeded, changes collapsed.

+5 −0
Original line number Diff line number Diff line
@@ -506,8 +506,13 @@ namespace netplus {
                        // Guard will re-arm on return
                        return;
                    }
                    std::cerr << "[TLS] connection error: " << e.what() << std::endl;
                    needClose = true;
                } catch (std::exception& e) {
                    std::cerr << "[TLS] std::exception: " << e.what() << std::endl;
                    needClose = true;
                } catch (...) {
                    std::cerr << "[TLS] unknown exception" << std::endl;
                    needClose = true;
                }
                if (needClose) {