Commit 3f5b3ab1 authored by jan.koester's avatar jan.koester
Browse files

der file support

parent caaaf9a7
Loading
Loading
Loading
Loading
+170 −1
Original line number Diff line number Diff line
@@ -919,5 +919,174 @@ namespace netplus {
        return out;
    }

    // Helper function to parse a DER length field
    static bool parseDERLength(const uint8_t* data, size_t maxLen, size_t& outLength, size_t& outBytesRead) {
        if (maxLen < 1) return false;
        
};
        outLength = data[0];
        outBytesRead = 1;
        
        // Long form
        if (outLength & 0x80) {
            int numOctets = static_cast<int>(outLength & 0x7F);
            if (numOctets <= 0 || numOctets > 4) return false;
            if (maxLen < 1 + static_cast<size_t>(numOctets)) return false;
            
            outLength = 0;
            for (int i = 0; i < numOctets; ++i) {
                outLength = (outLength << 8) | data[1 + i];
            }
            outBytesRead = 1 + numOctets;
        }
        
        return true;
    }

    // Helper function to parse a DER INTEGER
    static bool parseDERInteger(const uint8_t* data, size_t maxLen, std::vector<uint8_t>& outBytes, size_t& outBytesRead) {
        if (maxLen < 2 || data[0] != 0x02) {  // 0x02 = INTEGER tag
            return false;
        }
        
        size_t length = 0;
        size_t lenBytesRead = 0;
        if (!parseDERLength(data + 1, maxLen - 1, length, lenBytesRead)) {
            return false;
        }
        
        if (1 + lenBytesRead + length > maxLen) {
            return false;
        }
        
        const uint8_t* intData = data + 1 + lenBytesRead;
        outBytes.clear();
        
        // Skip leading zero byte if present (DER encoding convention for non-negative integers)
        if (length > 0 && intData[0] == 0x00) {
            outBytes.assign(intData + 1, intData + length);
        } else {
            outBytes.assign(intData, intData + length);
        }
        
        outBytesRead = 1 + lenBytesRead + length;
        return true;
    }

    // Static function to load RSA key from DER-encoded data
    bool rsa::loadRsaFromDerFile(const std::vector<uint8_t>& derData, netplus::rsa& out_rsa) {
        if (derData.empty()) return false;
        
        // DER format for PKCS#1 RSAPrivateKey:
        // RSAPrivateKey ::= SEQUENCE {
        //   version           INTEGER,
        //   modulus           INTEGER,     -- n
        //   publicExponent    INTEGER,     -- e
        //   privateExponent   INTEGER,     -- d
        //   prime1            INTEGER,     -- p
        //   prime2            INTEGER,     -- q
        //   exponent1         INTEGER,     -- d mod (p-1)
        //   exponent2         INTEGER,     -- d mod (q-1)
        //   coefficient       INTEGER      -- (inverse of q) mod p
        // }
        //
        // DER format for PKCS#8 PrivateKeyInfo:
        // PrivateKeyInfo ::= SEQUENCE {
        //   version             INTEGER,
        //   algorithm           AlgorithmIdentifier,
        //   PrivateKey          OCTET STRING
        // }
        
        // For public key (SubjectPublicKeyInfo):
        // SubjectPublicKeyInfo ::= SEQUENCE {
        //   algorithm           AlgorithmIdentifier,
        //   subjectPublicKey    BIT STRING
        // }
        
        const uint8_t* data = derData.data();
        size_t size = derData.size();
        size_t cursor = 0;
        
        // Must start with SEQUENCE (0x30)
        if (size < 2 || data[cursor] != 0x30) {
            return false;
        }
        cursor++;
        
        // Parse SEQUENCE length
        size_t seqLength = 0;
        size_t lenBytesRead = 0;
        if (!parseDERLength(data + cursor, size - cursor, seqLength, lenBytesRead)) {
            return false;
        }
        cursor += lenBytesRead;
        
        if (cursor + seqLength > size) {
            return false;
        }
        
        std::vector<uint8_t> n, e, d;
        
        // Try to parse as PKCS#1 RSAPrivateKey first
        size_t tempCursor = cursor;
        bool isPKCS1Private = false;
        
        // Check for version (INTEGER 0)
        if (tempCursor < cursor + seqLength && data[tempCursor] == 0x02) {
            std::vector<uint8_t> version;
            size_t intBytesRead = 0;
            if (parseDERInteger(data + tempCursor, seqLength - (tempCursor - cursor), version, intBytesRead)) {
                tempCursor += intBytesRead;
                
                // Try to parse n, e, d
                if (parseDERInteger(data + tempCursor, seqLength - (tempCursor - cursor), n, intBytesRead)) {
                    tempCursor += intBytesRead;
                    
                    if (parseDERInteger(data + tempCursor, seqLength - (tempCursor - cursor), e, intBytesRead)) {
                        tempCursor += intBytesRead;
                        
                        if (parseDERInteger(data + tempCursor, seqLength - (tempCursor - cursor), d, intBytesRead)) {
                            isPKCS1Private = true;
                        }
                    }
                }
            }
        }
        
        if (isPKCS1Private && !n.empty() && !e.empty() && !d.empty()) {
            // Successfully parsed PKCS#1 private key
            out_rsa.setRsaKeyFromRaw(n, e, d);
            return true;
        }
        
        // Try to parse as public key (might be SubjectPublicKeyInfo with BIT STRING, or just RSAPublicKey SEQUENCE)
        if (data[cursor] == 0x30) {
            // Could be either RSAPublicKey or algorithm identifier
            cursor++;
            size_t innerSeqLength = 0;
            if (!parseDERLength(data + cursor, size - cursor, innerSeqLength, lenBytesRead)) {
                return false;
            }
            cursor += lenBytesRead;
            
            if (cursor + innerSeqLength > size) {
                return false;
            }
            
            // Try to parse modulus and exponent
            size_t pubCursor = cursor;
            if (parseDERInteger(data + pubCursor, innerSeqLength - (pubCursor - cursor), n, intBytesRead)) {
                pubCursor += intBytesRead;
                
                if (parseDERInteger(data + pubCursor, innerSeqLength - (pubCursor - cursor), e, intBytesRead)) {
                    if (!n.empty() && !e.empty()) {
                        // Create a public-only key
                        d.clear();  // No private exponent
                        out_rsa.setRsaKeyFromRaw(n, e, d);
                        return true;
                    }
                }
            }
        }
        
        return false;
    }
+3 −0
Original line number Diff line number Diff line
@@ -119,6 +119,9 @@ namespace netplus {
        static bigInt bytesToBigIntBE(const std::vector<uint8_t>& bytes);
        static bigInt bigIntFromBytesBE (const uint8_t* bytes, size_t len);
        static std::vector<uint8_t> bigIntToBytesBE(const bigInt& x, size_t outLen);
        
        // Load RSA key from DER-encoded data
        static bool loadRsaFromDerFile(const std::vector<uint8_t>& derData, netplus::rsa& out_rsa);
    private:
        bigInt n, e, d;
        bool isProbablyPrime(const bigInt& n, int k);
+1 −4
Original line number Diff line number Diff line
@@ -337,11 +337,8 @@ namespace netplus {
		void prime_read();
#endif

		bool loadServerPrivateKeyDer(const std::string& keyDerPath);
		bool loadServerPrivateKey(const std::vector<uint8_t>& keyData);

		// ✅ Static helper to load RSA key from DER bytes into rsa object
		static bool _loadRsaFromDer(const std::vector<uint8_t>& derData, netplus::rsa& out_rsa);
		static bool loadRsaFromDer(const std::vector<uint8_t>& derData, netplus::rsa& out_rsa);

		bool hasPendingWrite() const override {
			// Return true if there's queued data not yet sent
+9 −6
Original line number Diff line number Diff line
@@ -1890,10 +1890,11 @@ void netplus::ssl::handshake_after_accept(){
                return false;
            };

            // ✅ TLS 1.3 chosen only if we can actually do it
            // ✅ TLS 1.3 cipher suite preference: AES-256 > AES-128
            auto pickTls13Suite = [&]() -> uint16_t {
                // preference order (you can reorder if you want)
                if (hasSuite(0x1301)) return 0x1301; // AES_128_GCM_SHA256
                // Server prefers stronger cipher suite first
                if (hasSuite(0x1302)) return 0x1302; // AES_256_GCM_SHA384 (PREFERRED)
                if (hasSuite(0x1301)) return 0x1301; // Fallback: AES_128_GCM_SHA256
                return 0;
            };

@@ -3132,12 +3133,14 @@ void netplus::ssl::_queueClientHello(){

    // ------------------------------------------------------------
    // CipherSuites: include TLS 1.3 + TLS 1.2
    // TLS 1.3 suite: 0x1301 (TLS_AES_128_GCM_SHA256)
    // Keep your TLS 1.2 suite(s) for fallback.
    // PREFERENCE: 0x1302 (TLS_AES_256_GCM_SHA384) - 256-bit encryption
    //    FALLBACK:  0x1301 (TLS_AES_128_GCM_SHA256) - 128-bit encryption
    //    TLS 1.2:   0x002F (TLS_RSA_WITH_AES_128_CBC_SHA) - legacy
    // ------------------------------------------------------------
    std::vector<uint8_t> suites = {
        0x00, 0xFF, // renegotiation SCSV
        0x13, 0x01, // TLS_AES_128_GCM_SHA256  (TLS 1.3)
        0x13, 0x02, // TLS_AES_256_GCM_SHA384  (TLS 1.3) PREFERRED
        0x13, 0x01, // TLS_AES_128_GCM_SHA256  (TLS 1.3) fallback
        0x00, 0x2F  // TLS_RSA_WITH_AES_128_CBC_SHA (TLS 1.2 fallback)
    };

+1 −1
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ int main() {
        bundle2.cert = cert2;
        bundle2.privateKeyDer = std::vector<uint8_t>(test2_key_der.begin(), test2_key_der.end());
        // Pre-load RSA key into bundle
        if (!netplus::ssl::_loadRsaFromDer(bundle2.privateKeyDer, bundle2.rsa_key)) {
        if (!netplus::ssl::loadRsaFromDer(bundle2.privateKeyDer, bundle2.rsa_key)) {
             std::cerr << "Failed to load RSA key 2!" << std::endl;
             std::cerr.flush();
             return 1;