Loading src/CMakeLists.txt +10 −4 Original line number Diff line number Diff line list(APPEND netplussrc exception.cpp connection.cpp ssl.cpp quick.cpp crypto/sha.cpp crypto/base64.cpp crypto/des.cpp crypto/aes.cpp Loading @@ -12,6 +9,12 @@ list(APPEND netplussrc crypto/x509.cpp crypto/ecc_u256.cpp crypto/ecc_p256.cpp crypto/hkdf.cpp crypto/rsa_pss_sha256.cpp exception.cpp connection.cpp ssl.cpp quick.cpp ) if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") Loading Loading @@ -57,6 +60,9 @@ set(headers crypto/x509.h crypto/ecc_u256.h crypto/ecc_p256.h crypto/hkdf.h crypto/rsa_pss_sha256.h crypto/sha.h ) add_library(netplus SHARED ${netplussrc} ${headers}) Loading src/crypto/aes.cpp +176 −1 Original line number Diff line number Diff line Loading @@ -382,5 +382,180 @@ std::vector<uint8_t> aes::decryptCBC(const std::vector<uint8_t>& ciphertext, return out; } } // namespace netplus // ---------- helpers ---------- static inline void xor_block_inplace(uint8_t out[16], const uint8_t b[16]) { for (int i=0;i<16;i++) out[i] ^= b[i]; } static inline void store_be64(uint8_t out[8], uint64_t x) { for (int i=0;i<8;i++) out[i] = (uint8_t)(x >> (56 - 8*i)); } // multiply in GF(2^128) with poly 0xE1 static void gcm_mul(uint8_t X[16], const uint8_t H[16]) { uint8_t Z[16] = {0}; uint8_t V[16]; std::memcpy(V, H, 16); for (int i = 0; i < 128; i++) { int xi = (X[i/8] >> (7 - (i%8))) & 1; if (xi) xor_block_inplace(Z, V); // shift V right by 1 uint8_t lsb = V[15] & 1; for (int j = 15; j > 0; j--) { V[j] = (uint8_t)((V[j] >> 1) | ((V[j-1] & 1) << 7)); } V[0] >>= 1; if (lsb) V[0] ^= 0xE1; } std::memcpy(X, Z, 16); } static void ghash_update(uint8_t Y[16], const uint8_t H[16], const uint8_t* data, size_t len) { while (len >= 16) { for (int i=0;i<16;i++) Y[i] ^= data[i]; gcm_mul(Y, H); data += 16; len -= 16; } if (len > 0) { uint8_t last[16] = {0}; std::memcpy(last, data, len); for (int i=0;i<16;i++) Y[i] ^= last[i]; gcm_mul(Y, H); } } static void ghash(uint8_t out[16], const uint8_t H[16], const uint8_t* aad, size_t aad_len, const uint8_t* ct, size_t ct_len) { uint8_t Y[16] = {0}; ghash_update(Y, H, aad, aad_len); ghash_update(Y, H, ct, ct_len); uint8_t lenblk[16]; store_be64(lenblk, (uint64_t)aad_len * 8); store_be64(lenblk + 8, (uint64_t)ct_len * 8); for (int i=0;i<16;i++) Y[i] ^= lenblk[i]; gcm_mul(Y, H); std::memcpy(out, Y, 16); } static inline void inc32(uint8_t ctr[16]) { uint32_t x = (uint32_t(ctr[12]) << 24) | (uint32_t(ctr[13]) << 16) | (uint32_t(ctr[14]) << 8) | (uint32_t(ctr[15])); x++; ctr[12] = (uint8_t)(x >> 24); ctr[13] = (uint8_t)(x >> 16); ctr[14] = (uint8_t)(x >> 8); ctr[15] = (uint8_t)(x); } static inline uint8_t ct_memeq_mask(const uint8_t* a, const uint8_t* b, size_t n) { uint8_t diff = 0; for (size_t i=0;i<n;i++) diff |= (a[i] ^ b[i]); return diff; // 0 if equal } // encrypt one AES block using existing aes::encrypt() static inline void aes_encrypt_block(netplus::aes& A, const uint8_t in[16], uint8_t out[16]) { block128 pt(in, in + 16); block128 ct = A.encrypt(pt); std::memcpy(out, ct.data(), 16); } // ---------- MEMBER GCM IMPLEMENTATION ---------- bool aes::aes_gcm_encrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* pt, size_t pt_len, uint8_t* ct, uint8_t tag16[16] ){ // H = AES_k(0^128) uint8_t H[16] = {0}; aes_encrypt_block(*this, H, H); // J0 = IV || 0x00000001 (TLS uses 96-bit IV) uint8_t J0[16] = {0}; std::memcpy(J0, iv12, 12); J0[15] = 1; // CTR keystream uint8_t ctr[16]; std::memcpy(ctr, J0, 16); size_t off = 0; while (off < pt_len) { inc32(ctr); uint8_t S[16]; aes_encrypt_block(*this, ctr, S); size_t take = (pt_len - off < 16) ? (pt_len - off) : 16; for (size_t i=0;i<take;i++) ct[off+i] = pt[off+i] ^ S[i]; off += take; } // GHASH uint8_t Sgh[16]; ghash(Sgh, H, aad, aad_len, ct, pt_len); // Tag = AES_k(J0) XOR GHASH uint8_t EJ0[16]; aes_encrypt_block(*this, J0, EJ0); for (int i=0;i<16;i++) tag16[i] = EJ0[i] ^ Sgh[i]; return true; } bool aes::aes_gcm_decrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* ct, size_t ct_len, const uint8_t tag16[16], uint8_t* pt ){ uint8_t H[16] = {0}; aes_encrypt_block(*this, H, H); uint8_t J0[16] = {0}; std::memcpy(J0, iv12, 12); J0[15] = 1; // expected tag uint8_t Sgh[16]; ghash(Sgh, H, aad, aad_len, ct, ct_len); uint8_t EJ0[16]; aes_encrypt_block(*this, J0, EJ0); uint8_t expTag[16]; for (int i=0;i<16;i++) expTag[i] = EJ0[i] ^ Sgh[i]; if (ct_memeq_mask(expTag, tag16, 16) != 0) return false; // CTR decrypt uint8_t ctr[16]; std::memcpy(ctr, J0, 16); size_t off = 0; while (off < ct_len) { inc32(ctr); uint8_t S[16]; aes_encrypt_block(*this, ctr, S); size_t take = (ct_len - off < 16) ? (ct_len - off) : 16; for (size_t i=0;i<take;i++) pt[off+i] = ct[off+i] ^ S[i]; off += take; } return true; } }; src/crypto/aes.h +17 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,23 @@ public: std::vector<uint8_t> decryptCBC(const std::vector<uint8_t>& ciphertext, const std::vector<uint8_t>& iv); bool aes_gcm_encrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* pt, size_t pt_len, uint8_t* ct, uint8_t tag16[16] ); bool aes_gcm_decrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* ct, size_t ct_len, const uint8_t tag16[16], uint8_t* pt ); }; } // namespace netplus src/crypto/hkdf.cpp 0 → 100644 +107 −0 Original line number Diff line number Diff line #include "hkdf.h" #include <stdexcept> #include <cstring> namespace netplus { HKDF_SHA256::HKDF_SHA256(Sha256Func sha256, HmacFunc hmac) : _sha256(std::move(sha256)), _hmac(std::move(hmac)) { if (!_sha256 || !_hmac) throw std::runtime_error("HKDF_SHA256: callbacks missing"); } std::vector<uint8_t> HKDF_SHA256::extract(const std::vector<uint8_t>& salt, const std::vector<uint8_t>& ikm) const { // RFC 5869: PRK = HMAC(salt, IKM) return _hmac(salt, ikm); } std::vector<uint8_t> HKDF_SHA256::expand(const std::vector<uint8_t>& prk, const std::vector<uint8_t>& info, size_t outLen) const { // RFC 5869 Expand if (outLen == 0) return {}; const size_t hashLen = 32; if (outLen > 255 * hashLen) throw std::runtime_error("HKDF expand: outLen too large"); std::vector<uint8_t> okm; okm.reserve(outLen); std::vector<uint8_t> t; uint8_t counter = 1; while (okm.size() < outLen) { std::vector<uint8_t> in; in.reserve(t.size() + info.size() + 1); in.insert(in.end(), t.begin(), t.end()); in.insert(in.end(), info.begin(), info.end()); in.push_back(counter); t = _hmac(prk, in); // 32 bytes size_t need = std::min(t.size(), outLen - okm.size()); okm.insert(okm.end(), t.begin(), t.begin() + need); counter++; if (counter == 0) throw std::runtime_error("HKDF expand: counter overflow"); } return okm; } std::vector<uint8_t> HKDF_SHA256::expand_label(const std::vector<uint8_t>& secret, const std::string& label, const std::vector<uint8_t>& context, size_t outLen) const { // TLS 1.3 HKDF-Expand-Label: // // struct { // uint16 length = outLen; // opaque label<7..255> = "tls13 " + label; // opaque context<0..255> = context; // } HkdfLabel; // // return HKDF-Expand(secret, HkdfLabel, outLen) std::string fullLabel = "tls13 " + label; if (fullLabel.size() > 255) throw std::runtime_error("HKDF expand_label: label too long"); if (context.size() > 255) throw std::runtime_error("HKDF expand_label: context too long"); if (outLen > 0xFFFF) throw std::runtime_error("HKDF expand_label: outLen too big"); std::vector<uint8_t> info; info.reserve(2 + 1 + fullLabel.size() + 1 + context.size()); // length info.push_back(uint8_t((outLen >> 8) & 0xFF)); info.push_back(uint8_t(outLen & 0xFF)); // label info.push_back(uint8_t(fullLabel.size())); info.insert(info.end(), fullLabel.begin(), fullLabel.end()); // context info.push_back(uint8_t(context.size())); info.insert(info.end(), context.begin(), context.end()); return expand(secret, info, outLen); } std::vector<uint8_t> HKDF_SHA256::hash_empty() const { std::vector<uint8_t> empty; return _sha256(empty); } } // namespace netplus src/crypto/hkdf.h 0 → 100644 +45 −0 Original line number Diff line number Diff line #pragma once #include <vector> #include <string> #include <functional> #include <cstdint> namespace netplus { class HKDF_SHA256 { public: // Callback types: // sha256(data) -> 32 bytes using Sha256Func = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&)>; // hmac(key,data) -> 32 bytes using HmacFunc = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, const std::vector<uint8_t>&)>; HKDF_SHA256(Sha256Func sha256, HmacFunc hmac); // HKDF-Extract(salt, IKM) -> PRK std::vector<uint8_t> extract(const std::vector<uint8_t>& salt, const std::vector<uint8_t>& ikm) const; // HKDF-Expand(PRK, info, L) -> OKM std::vector<uint8_t> expand(const std::vector<uint8_t>& prk, const std::vector<uint8_t>& info, size_t outLen) const; // TLS 1.3 HKDF-Expand-Label // HKDF-Expand-Label(secret, label, context, outLen) std::vector<uint8_t> expand_label(const std::vector<uint8_t>& secret, const std::string& label, const std::vector<uint8_t>& context, size_t outLen) const; // Utility: SHA256 over empty string (used in TLS 1.3 "derived") std::vector<uint8_t> hash_empty() const; private: Sha256Func _sha256; HmacFunc _hmac; }; } // namespace netplus Loading
src/CMakeLists.txt +10 −4 Original line number Diff line number Diff line list(APPEND netplussrc exception.cpp connection.cpp ssl.cpp quick.cpp crypto/sha.cpp crypto/base64.cpp crypto/des.cpp crypto/aes.cpp Loading @@ -12,6 +9,12 @@ list(APPEND netplussrc crypto/x509.cpp crypto/ecc_u256.cpp crypto/ecc_p256.cpp crypto/hkdf.cpp crypto/rsa_pss_sha256.cpp exception.cpp connection.cpp ssl.cpp quick.cpp ) if(${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") Loading Loading @@ -57,6 +60,9 @@ set(headers crypto/x509.h crypto/ecc_u256.h crypto/ecc_p256.h crypto/hkdf.h crypto/rsa_pss_sha256.h crypto/sha.h ) add_library(netplus SHARED ${netplussrc} ${headers}) Loading
src/crypto/aes.cpp +176 −1 Original line number Diff line number Diff line Loading @@ -382,5 +382,180 @@ std::vector<uint8_t> aes::decryptCBC(const std::vector<uint8_t>& ciphertext, return out; } } // namespace netplus // ---------- helpers ---------- static inline void xor_block_inplace(uint8_t out[16], const uint8_t b[16]) { for (int i=0;i<16;i++) out[i] ^= b[i]; } static inline void store_be64(uint8_t out[8], uint64_t x) { for (int i=0;i<8;i++) out[i] = (uint8_t)(x >> (56 - 8*i)); } // multiply in GF(2^128) with poly 0xE1 static void gcm_mul(uint8_t X[16], const uint8_t H[16]) { uint8_t Z[16] = {0}; uint8_t V[16]; std::memcpy(V, H, 16); for (int i = 0; i < 128; i++) { int xi = (X[i/8] >> (7 - (i%8))) & 1; if (xi) xor_block_inplace(Z, V); // shift V right by 1 uint8_t lsb = V[15] & 1; for (int j = 15; j > 0; j--) { V[j] = (uint8_t)((V[j] >> 1) | ((V[j-1] & 1) << 7)); } V[0] >>= 1; if (lsb) V[0] ^= 0xE1; } std::memcpy(X, Z, 16); } static void ghash_update(uint8_t Y[16], const uint8_t H[16], const uint8_t* data, size_t len) { while (len >= 16) { for (int i=0;i<16;i++) Y[i] ^= data[i]; gcm_mul(Y, H); data += 16; len -= 16; } if (len > 0) { uint8_t last[16] = {0}; std::memcpy(last, data, len); for (int i=0;i<16;i++) Y[i] ^= last[i]; gcm_mul(Y, H); } } static void ghash(uint8_t out[16], const uint8_t H[16], const uint8_t* aad, size_t aad_len, const uint8_t* ct, size_t ct_len) { uint8_t Y[16] = {0}; ghash_update(Y, H, aad, aad_len); ghash_update(Y, H, ct, ct_len); uint8_t lenblk[16]; store_be64(lenblk, (uint64_t)aad_len * 8); store_be64(lenblk + 8, (uint64_t)ct_len * 8); for (int i=0;i<16;i++) Y[i] ^= lenblk[i]; gcm_mul(Y, H); std::memcpy(out, Y, 16); } static inline void inc32(uint8_t ctr[16]) { uint32_t x = (uint32_t(ctr[12]) << 24) | (uint32_t(ctr[13]) << 16) | (uint32_t(ctr[14]) << 8) | (uint32_t(ctr[15])); x++; ctr[12] = (uint8_t)(x >> 24); ctr[13] = (uint8_t)(x >> 16); ctr[14] = (uint8_t)(x >> 8); ctr[15] = (uint8_t)(x); } static inline uint8_t ct_memeq_mask(const uint8_t* a, const uint8_t* b, size_t n) { uint8_t diff = 0; for (size_t i=0;i<n;i++) diff |= (a[i] ^ b[i]); return diff; // 0 if equal } // encrypt one AES block using existing aes::encrypt() static inline void aes_encrypt_block(netplus::aes& A, const uint8_t in[16], uint8_t out[16]) { block128 pt(in, in + 16); block128 ct = A.encrypt(pt); std::memcpy(out, ct.data(), 16); } // ---------- MEMBER GCM IMPLEMENTATION ---------- bool aes::aes_gcm_encrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* pt, size_t pt_len, uint8_t* ct, uint8_t tag16[16] ){ // H = AES_k(0^128) uint8_t H[16] = {0}; aes_encrypt_block(*this, H, H); // J0 = IV || 0x00000001 (TLS uses 96-bit IV) uint8_t J0[16] = {0}; std::memcpy(J0, iv12, 12); J0[15] = 1; // CTR keystream uint8_t ctr[16]; std::memcpy(ctr, J0, 16); size_t off = 0; while (off < pt_len) { inc32(ctr); uint8_t S[16]; aes_encrypt_block(*this, ctr, S); size_t take = (pt_len - off < 16) ? (pt_len - off) : 16; for (size_t i=0;i<take;i++) ct[off+i] = pt[off+i] ^ S[i]; off += take; } // GHASH uint8_t Sgh[16]; ghash(Sgh, H, aad, aad_len, ct, pt_len); // Tag = AES_k(J0) XOR GHASH uint8_t EJ0[16]; aes_encrypt_block(*this, J0, EJ0); for (int i=0;i<16;i++) tag16[i] = EJ0[i] ^ Sgh[i]; return true; } bool aes::aes_gcm_decrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* ct, size_t ct_len, const uint8_t tag16[16], uint8_t* pt ){ uint8_t H[16] = {0}; aes_encrypt_block(*this, H, H); uint8_t J0[16] = {0}; std::memcpy(J0, iv12, 12); J0[15] = 1; // expected tag uint8_t Sgh[16]; ghash(Sgh, H, aad, aad_len, ct, ct_len); uint8_t EJ0[16]; aes_encrypt_block(*this, J0, EJ0); uint8_t expTag[16]; for (int i=0;i<16;i++) expTag[i] = EJ0[i] ^ Sgh[i]; if (ct_memeq_mask(expTag, tag16, 16) != 0) return false; // CTR decrypt uint8_t ctr[16]; std::memcpy(ctr, J0, 16); size_t off = 0; while (off < ct_len) { inc32(ctr); uint8_t S[16]; aes_encrypt_block(*this, ctr, S); size_t take = (ct_len - off < 16) ? (ct_len - off) : 16; for (size_t i=0;i<take;i++) pt[off+i] = ct[off+i] ^ S[i]; off += take; } return true; } };
src/crypto/aes.h +17 −0 Original line number Diff line number Diff line Loading @@ -89,6 +89,23 @@ public: std::vector<uint8_t> decryptCBC(const std::vector<uint8_t>& ciphertext, const std::vector<uint8_t>& iv); bool aes_gcm_encrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* pt, size_t pt_len, uint8_t* ct, uint8_t tag16[16] ); bool aes_gcm_decrypt( const uint8_t iv12[12], const uint8_t* aad, size_t aad_len, const uint8_t* ct, size_t ct_len, const uint8_t tag16[16], uint8_t* pt ); }; } // namespace netplus
src/crypto/hkdf.cpp 0 → 100644 +107 −0 Original line number Diff line number Diff line #include "hkdf.h" #include <stdexcept> #include <cstring> namespace netplus { HKDF_SHA256::HKDF_SHA256(Sha256Func sha256, HmacFunc hmac) : _sha256(std::move(sha256)), _hmac(std::move(hmac)) { if (!_sha256 || !_hmac) throw std::runtime_error("HKDF_SHA256: callbacks missing"); } std::vector<uint8_t> HKDF_SHA256::extract(const std::vector<uint8_t>& salt, const std::vector<uint8_t>& ikm) const { // RFC 5869: PRK = HMAC(salt, IKM) return _hmac(salt, ikm); } std::vector<uint8_t> HKDF_SHA256::expand(const std::vector<uint8_t>& prk, const std::vector<uint8_t>& info, size_t outLen) const { // RFC 5869 Expand if (outLen == 0) return {}; const size_t hashLen = 32; if (outLen > 255 * hashLen) throw std::runtime_error("HKDF expand: outLen too large"); std::vector<uint8_t> okm; okm.reserve(outLen); std::vector<uint8_t> t; uint8_t counter = 1; while (okm.size() < outLen) { std::vector<uint8_t> in; in.reserve(t.size() + info.size() + 1); in.insert(in.end(), t.begin(), t.end()); in.insert(in.end(), info.begin(), info.end()); in.push_back(counter); t = _hmac(prk, in); // 32 bytes size_t need = std::min(t.size(), outLen - okm.size()); okm.insert(okm.end(), t.begin(), t.begin() + need); counter++; if (counter == 0) throw std::runtime_error("HKDF expand: counter overflow"); } return okm; } std::vector<uint8_t> HKDF_SHA256::expand_label(const std::vector<uint8_t>& secret, const std::string& label, const std::vector<uint8_t>& context, size_t outLen) const { // TLS 1.3 HKDF-Expand-Label: // // struct { // uint16 length = outLen; // opaque label<7..255> = "tls13 " + label; // opaque context<0..255> = context; // } HkdfLabel; // // return HKDF-Expand(secret, HkdfLabel, outLen) std::string fullLabel = "tls13 " + label; if (fullLabel.size() > 255) throw std::runtime_error("HKDF expand_label: label too long"); if (context.size() > 255) throw std::runtime_error("HKDF expand_label: context too long"); if (outLen > 0xFFFF) throw std::runtime_error("HKDF expand_label: outLen too big"); std::vector<uint8_t> info; info.reserve(2 + 1 + fullLabel.size() + 1 + context.size()); // length info.push_back(uint8_t((outLen >> 8) & 0xFF)); info.push_back(uint8_t(outLen & 0xFF)); // label info.push_back(uint8_t(fullLabel.size())); info.insert(info.end(), fullLabel.begin(), fullLabel.end()); // context info.push_back(uint8_t(context.size())); info.insert(info.end(), context.begin(), context.end()); return expand(secret, info, outLen); } std::vector<uint8_t> HKDF_SHA256::hash_empty() const { std::vector<uint8_t> empty; return _sha256(empty); } } // namespace netplus
src/crypto/hkdf.h 0 → 100644 +45 −0 Original line number Diff line number Diff line #pragma once #include <vector> #include <string> #include <functional> #include <cstdint> namespace netplus { class HKDF_SHA256 { public: // Callback types: // sha256(data) -> 32 bytes using Sha256Func = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&)>; // hmac(key,data) -> 32 bytes using HmacFunc = std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, const std::vector<uint8_t>&)>; HKDF_SHA256(Sha256Func sha256, HmacFunc hmac); // HKDF-Extract(salt, IKM) -> PRK std::vector<uint8_t> extract(const std::vector<uint8_t>& salt, const std::vector<uint8_t>& ikm) const; // HKDF-Expand(PRK, info, L) -> OKM std::vector<uint8_t> expand(const std::vector<uint8_t>& prk, const std::vector<uint8_t>& info, size_t outLen) const; // TLS 1.3 HKDF-Expand-Label // HKDF-Expand-Label(secret, label, context, outLen) std::vector<uint8_t> expand_label(const std::vector<uint8_t>& secret, const std::string& label, const std::vector<uint8_t>& context, size_t outLen) const; // Utility: SHA256 over empty string (used in TLS 1.3 "derived") std::vector<uint8_t> hash_empty() const; private: Sha256Func _sha256; HmacFunc _hmac; }; } // namespace netplus