Loading src/crypto/rsa.cpp +170 −1 Original line number Diff line number Diff line Loading @@ -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; } src/crypto/rsa.h +3 −0 Original line number Diff line number Diff line Loading @@ -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); Loading src/socket.h +1 −4 Original line number Diff line number Diff line Loading @@ -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 Loading src/ssl_temp.cpp +9 −6 Original line number Diff line number Diff line Loading @@ -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; }; Loading Loading @@ -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) }; Loading test/https_test.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading
src/crypto/rsa.cpp +170 −1 Original line number Diff line number Diff line Loading @@ -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; }
src/crypto/rsa.h +3 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
src/socket.h +1 −4 Original line number Diff line number Diff line Loading @@ -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 Loading
src/ssl_temp.cpp +9 −6 Original line number Diff line number Diff line Loading @@ -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; }; Loading Loading @@ -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) }; Loading
test/https_test.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading