├── .gitignore ├── README.md ├── linux └── CMakeLists.txt ├── src ├── client_finish.cpp ├── client_finish.h ├── client_hello.cpp ├── client_hello.h ├── const.h ├── custom_defs.h ├── handshakeHasher.cpp ├── handshakeHasher.h ├── logger.hpp ├── main.cpp ├── mmtls.cpp ├── mmtls.h ├── mmtls_openssl.h ├── mmtls_short.cpp ├── mmtls_short.h ├── record.cpp ├── record.h ├── server_finish.cpp ├── server_finish.h ├── server_hello.cpp ├── server_hello.h ├── session.cpp ├── session.h ├── session_ticket.cpp ├── session_ticket.h ├── signature.cpp ├── signature.h ├── utility.cpp └── utility.h └── windows ├── mmtls.sln ├── mmtls.vcxproj ├── mmtls.vcxproj.filters └── mmtls.vcxproj.user /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | *.exe 3 | *.pdb 4 | *.dll 5 | *.obj 6 | windows/Bin 7 | windows/x64 8 | windows/mmtls 9 | **/session 10 | **/build/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 参考[gommtls](https://github.com/duo/gommtls)实现的C++版本微信mmtls握手协议。 3 | 支持Linux和Windows,在**AMD64**架构下编译通过,x86和ARM未做测试。 4 | 5 | ### Build 6 | #### Windows 7 | 在开始构建之前,需要安装OpenSSL,然后使用**Visual Studio**进行构建,注意修改**包含目录**和**库目录**。 8 | #### Linux 9 | 需要预先安装**cmake**和**libssl-dev**,然后使用cmake进行构建: 10 | ```shell 11 | cd linux 12 | mkdir build 13 | cd bulid 14 | cmake .. 15 | make 16 | ``` 17 | ### Disclaimer 18 | 代码仅作学习交流使用,请勿用于非法用途。 -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # cmake needs this line 2 | cmake_minimum_required(VERSION 3.1) 3 | 4 | # Define project name 5 | project(mmtls) 6 | 7 | set(CMAKE_PREFIX_PATH "/usr/bin") 8 | 9 | find_package(OpenSSL REQUIRED) 10 | execute_process( 11 | COMMAND openssl version 12 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 13 | OUTPUT_VARIABLE OPENSSL_VERSION 14 | ) 15 | message(STATUS "OpenSSL library status:") 16 | message(STATUS ${OPENSSL_VERSION}) 17 | 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-variable") 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-conversion-null") 21 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith") 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-truncation") 23 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") 24 | set(CMAKE_CXX_STANDARD 17) 25 | 26 | include_directories("../src") 27 | include_directories("/usr/include") 28 | link_directories("/usr/lib/x86_64-linux-gnu") 29 | 30 | # Declare the executable target built from your sources 31 | add_executable( 32 | mmtls 33 | ../src/main.cpp 34 | ../src/mmtls.cpp 35 | ../src/mmtls_short.cpp 36 | ../src/client_hello.cpp 37 | ../src/server_hello.cpp 38 | ../src/server_finish.cpp 39 | ../src/client_finish.cpp 40 | ../src/record.cpp 41 | ../src/session.cpp 42 | ../src/session_ticket.cpp 43 | ../src/utility.cpp 44 | ../src/logger.hpp 45 | ../src/handshakeHasher.cpp 46 | ../src/signature.cpp 47 | ) 48 | 49 | # Link your application with OpenSSL libraries 50 | target_link_libraries(mmtls libcrypto.a) 51 | target_link_libraries(mmtls libssl.a) 52 | -------------------------------------------------------------------------------- /src/client_finish.cpp: -------------------------------------------------------------------------------- 1 | #include "client_finish.h" 2 | 3 | clientFinish clientFinish::newClientFinish(const byteArray& data) { 4 | clientFinish cf; 5 | cf.reversed = 0x14; 6 | cf.data = data; 7 | return cf; 8 | } 9 | 10 | byteArray clientFinish::serialize() { 11 | byteArray result; 12 | UINT32 dLen = (UINT32)(data.size() + 3); 13 | result.push_back((dLen >> 24) & 0xff); 14 | result.push_back((dLen >> 16) & 0xff); 15 | result.push_back((dLen >> 8) & 0xff); 16 | result.push_back((dLen >> 0) & 0xff); 17 | result.push_back(reversed); 18 | dLen = (uint32)data.size(); 19 | result.push_back((dLen >> 8) & 0xff); 20 | result.push_back((dLen >> 0) & 0xff); 21 | result.insert(result.end(), data.begin(), data.end()); 22 | return result; 23 | } -------------------------------------------------------------------------------- /src/client_finish.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | 4 | struct clientFinishTag { 5 | BYTE reversed = 0; 6 | byteArray data; 7 | }; 8 | 9 | class clientFinish : public clientFinishTag { 10 | public: 11 | static clientFinish newClientFinish(const byteArray& data); 12 | byteArray serialize(); 13 | }; -------------------------------------------------------------------------------- /src/client_hello.cpp: -------------------------------------------------------------------------------- 1 | #include "client_hello.h" 2 | #include "const.h" 3 | #include "utility.h" 4 | 5 | #if defined(_WIN32) 6 | #include 7 | #pragma warning(disable: 26451) 8 | #else 9 | #include 10 | #include 11 | #endif 12 | 13 | #ifndef OPENSSL3 14 | clientHello clientHello::newECDHEHello(const EC_KEY* cliPubKey, const EC_KEY* cliVerKey) { 15 | clientHello ch; 16 | ch.protocolVersion = ProtocolVersion; 17 | ch.timestamp = (uint32)time(0); 18 | ch.random = getRandom(32); 19 | ch.cipherSuites.push_back(TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff); 20 | BYTE* pointBuf = nullptr; 21 | size_t bLen = EC_KEY_key2buf(cliPubKey, POINT_CONVERSION_UNCOMPRESSED, &pointBuf, nullptr); 22 | byteArray pubArr(pointBuf, pointBuf + bLen); 23 | OPENSSL_free(pointBuf); 24 | pointBuf = nullptr; 25 | bLen = EC_KEY_key2buf(cliVerKey, POINT_CONVERSION_UNCOMPRESSED, &pointBuf, nullptr); 26 | byteArray verArr(pointBuf, pointBuf + bLen); 27 | OPENSSL_free(pointBuf); 28 | pointBuf = nullptr; 29 | ch.extensions[TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff] = { pubArr,verArr }; 30 | return ch; 31 | } 32 | 33 | clientHello clientHello::newPskOneHello(const EC_KEY* cliPubKey, const EC_KEY* cliVerKey, sessionTicket& ticket) { 34 | clientHello ch; 35 | ch.protocolVersion = ProtocolVersion; 36 | ch.timestamp = (uint32)time(0); 37 | ch.random = getRandom(32); 38 | ch.cipherSuites.push_back(TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff); 39 | ch.cipherSuites.push_back(TLS_PSK_WITH_AES_128_GCM_SHA256); 40 | sessionTicket& t = ticket; 41 | t.ticketAgeAdd = byteArray(); 42 | byteArray ticketData = t.serialize(); 43 | ch.extensions[TLS_PSK_WITH_AES_128_GCM_SHA256] = { ticketData }; 44 | BYTE* pointBuf = nullptr; 45 | size_t bLen = EC_KEY_key2buf(cliPubKey, POINT_CONVERSION_UNCOMPRESSED, &pointBuf, nullptr); 46 | byteArray pubArr(pointBuf, pointBuf + bLen); 47 | OPENSSL_free(pointBuf); 48 | pointBuf = nullptr; 49 | bLen = EC_KEY_key2buf(cliVerKey, POINT_CONVERSION_UNCOMPRESSED, &pointBuf, nullptr); 50 | byteArray verArr(pointBuf, pointBuf + bLen); 51 | OPENSSL_free(pointBuf); 52 | pointBuf = nullptr; 53 | ch.extensions[TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff] = { pubArr,verArr }; 54 | return ch; 55 | } 56 | #else 57 | clientHello clientHello::newECDHEHello(const EVP_PKEY* cliPubKey, const EVP_PKEY* cliVerKey) { 58 | clientHello ch; 59 | ch.protocolVersion = ProtocolVersion; 60 | ch.timestamp = (uint32)time(0); 61 | ch.random = getRandom(32); 62 | ch.cipherSuites.push_back(TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff); 63 | std::string szCliPubKey, szCliVerKey; 64 | EVP_EC_KEY_key2buf(cliPubKey, szCliPubKey); 65 | byteArray pubArr(szCliPubKey.begin(), szCliPubKey.end()); 66 | EVP_EC_KEY_key2buf(cliVerKey, szCliVerKey); 67 | byteArray verArr(szCliVerKey.begin(), szCliVerKey.end()); 68 | ch.extensions[TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff] = { pubArr,verArr }; 69 | return ch; 70 | } 71 | 72 | clientHello clientHello::newPskOneHello(const EVP_PKEY* cliPubKey, const EVP_PKEY* cliVerKey, sessionTicket& ticket) { 73 | clientHello ch; 74 | ch.protocolVersion = ProtocolVersion; 75 | ch.timestamp = (uint32)time(0); 76 | ch.random = getRandom(32); 77 | ch.cipherSuites.push_back(TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff); 78 | ch.cipherSuites.push_back(TLS_PSK_WITH_AES_128_GCM_SHA256); 79 | sessionTicket& t = ticket; 80 | t.ticketAgeAdd = byteArray(); 81 | byteArray ticketData = t.serialize(); 82 | ch.extensions[TLS_PSK_WITH_AES_128_GCM_SHA256] = { ticketData }; 83 | std::string szCliPubKey, szCliVerKey; 84 | EVP_EC_KEY_key2buf(cliPubKey, szCliPubKey); 85 | byteArray pubArr(szCliPubKey.begin(), szCliPubKey.end()); 86 | EVP_EC_KEY_key2buf(cliVerKey, szCliVerKey); 87 | byteArray verArr(szCliVerKey.begin(), szCliVerKey.end()); 88 | ch.extensions[TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff] = { pubArr,verArr }; 89 | return ch; 90 | } 91 | #endif 92 | 93 | clientHello clientHello::newPskZeroHello(sessionTicket& ticket) { 94 | clientHello ch; 95 | ch.protocolVersion = ProtocolVersion; 96 | ch.timestamp = (uint32)time(0); 97 | ch.random = getRandom(32); 98 | ch.cipherSuites.push_back(TLS_PSK_WITH_AES_128_GCM_SHA256); 99 | sessionTicket& t = ticket; 100 | t.ticketAgeAdd = byteArray(); 101 | byteArray ticketData = t.serialize(); 102 | ch.extensions[TLS_PSK_WITH_AES_128_GCM_SHA256] = { ticketData }; 103 | return ch; 104 | } 105 | byteArray clientHello::serialize() { 106 | byteArray result(4, 0x00); // total length 107 | result.push_back(0x01); // flag 108 | // protocol version, little endian 109 | result.push_back((protocolVersion >> 0) & 0xff); 110 | result.push_back((protocolVersion >> 8) & 0xff); 111 | // cipher suites 112 | result.push_back(cipherSuites.size() & 0xff); 113 | for (auto cipherSuite : cipherSuites) { 114 | result.push_back((cipherSuite >> 8) & 0xff); 115 | result.push_back((cipherSuite >> 0) & 0xff); 116 | } 117 | // random 118 | for (unsigned i = 0; i < random.size(); i++) { 119 | result.push_back(random[i]); 120 | } 121 | // timestamp, big endian 122 | for (int i = 0; i < 4; i++) { 123 | result.push_back((timestamp >> (24 - i * 8)) & 0xff); 124 | } 125 | unsigned cipherPos = (unsigned)result.size(); 126 | for (int i = 0; i < 4; i++) { 127 | result.push_back(0); 128 | } 129 | result.push_back(cipherSuites.size() & 0xff); 130 | for (int si = (int)(cipherSuites.size() - 1); si >= 0; si--) { 131 | uint16 cipher = cipherSuites[si]; 132 | if (cipher == TLS_PSK_WITH_AES_128_GCM_SHA256) { 133 | unsigned pskPos = (unsigned)result.size(); 134 | for (int i = 0; i < 4; i++) { 135 | result.push_back(0x00); 136 | } 137 | result.push_back(0x00); 138 | result.push_back(0x0F); 139 | result.push_back(0x01); 140 | unsigned keyPos = (unsigned)result.size(); 141 | for (int i = 0; i < 4; i++) { 142 | result.push_back(0); 143 | } 144 | auto& extension = extensions[cipher][0]; 145 | for (unsigned i = 0; i < extension.size(); i++) { 146 | result.push_back(extension[i]); 147 | } 148 | unsigned keyLen = (unsigned)result.size() - keyPos - 4; 149 | for (int i = 0; i < 4; i++) { 150 | result[keyPos + i] = ((keyLen >> (24 - i * 8)) & 0xff); 151 | } 152 | unsigned pskLen = (unsigned)result.size() - pskPos - 4; 153 | for (int i = 0; i < 4; i++) { 154 | result[pskPos + i] = ((pskLen >> (24 - i * 8)) & 0xff); 155 | } 156 | } 157 | else if (cipher == (TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 & 0xffff)) { 158 | unsigned ecdsaPos = (unsigned)result.size(); 159 | for (int i = 0; i < 4; i++) { 160 | result.push_back(0x00); 161 | } 162 | result.push_back(0x00); 163 | result.push_back(0x10); 164 | result.push_back(extensions[cipher].size() & 0xff); 165 | uint32 keyFlag = 5; 166 | for (auto& extension : extensions[cipher]) { 167 | unsigned keyPos = (unsigned)result.size(); 168 | for (int i = 0; i < 4; i++) { 169 | result.push_back(0x00); 170 | } 171 | for (int i = 0; i < 4; i++) { 172 | result.push_back((keyFlag >> (24 - i * 8)) & 0xff); 173 | } 174 | keyFlag++; 175 | result.push_back((extension.size() >> 8) & 0xff); 176 | result.push_back((extension.size() >> 0) & 0xff); 177 | for (unsigned i = 0; i < extension.size(); i++) { 178 | result.push_back(extension[i]); 179 | } 180 | unsigned keyLen = (unsigned)result.size() - keyPos - 4; 181 | for (int i = 0; i < 4; i++) { 182 | result[keyPos + i] = ((keyLen >> (24 - i * 8)) & 0xff); 183 | } 184 | } 185 | byteArray magic = { 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04 }; 186 | result.insert(result.end(), magic.begin(), magic.end()); 187 | // ecdsa length 188 | unsigned ecdsaLen = (unsigned)result.size() - ecdsaPos - 4; 189 | for (int i = 0; i < 4; i++) { 190 | result[ecdsaPos + i] = ((ecdsaLen >> (24 - i * 8)) & 0xff); 191 | } 192 | } 193 | else { 194 | throw std::runtime_error(("cipher(" + std::to_string(cipher) + ") not support").c_str()); 195 | } 196 | } 197 | // cipher length 198 | unsigned cLen = (unsigned)result.size() - cipherPos - 4; 199 | for (int i = 0; i < 4; i++) { 200 | result[cipherPos + i] = ((cLen >> (24 - i * 8)) & 0xff); 201 | } 202 | // struct length 203 | unsigned tLen = (unsigned)result.size() - 4; 204 | for (int i = 0; i < 4; i++) { 205 | result[i] = ((tLen >> (24 - i * 8)) & 0xff); 206 | } 207 | return result; 208 | } -------------------------------------------------------------------------------- /src/client_hello.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | #include "session_ticket.h" 4 | #include "mmtls_openssl.h" 5 | 6 | struct clientHelloTag { 7 | uint16 protocolVersion = 0; 8 | uint16Array cipherSuites; 9 | byteArray random; 10 | UINT32 timestamp; 11 | std::map> extensions; 12 | }; 13 | 14 | class clientHello : public clientHelloTag { 15 | public: 16 | #ifndef OPENSSL3 17 | static clientHello newECDHEHello(const EC_KEY* cliPubKey, const EC_KEY* cliVerKey); 18 | static clientHello newPskOneHello(const EC_KEY* cliPubKey, const EC_KEY* cliVerKey, sessionTicket& ticket); 19 | #else 20 | static clientHello newECDHEHello(const EVP_PKEY* cliPubKey, const EVP_PKEY* cliVerKey); 21 | static clientHello newPskOneHello(const EVP_PKEY* cliPubKey, const EVP_PKEY* cliVerKey, sessionTicket& ticket); 22 | #endif 23 | static clientHello newPskZeroHello(sessionTicket& ticket); 24 | byteArray serialize(); 25 | }; -------------------------------------------------------------------------------- /src/const.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __MMTLS_CONST__ 3 | #define __MMTLS_CONST__ 4 | #include "mmtls_openssl.h" 5 | #include "custom_defs.h" 6 | #if !defined(_WIN32) 7 | #include 8 | #endif 9 | #include 10 | 11 | constexpr uint16 ProtocolVersion = 0xF104; 12 | constexpr uint16 TLS_PSK_WITH_AES_128_GCM_SHA256 = 0xA8; 13 | constexpr uint8 MagicAbort = 0x15; 14 | constexpr uint8 MagicHandshake = 0x16; 15 | constexpr uint8 MagicRecord = 0x17; 16 | constexpr uint8 MagicSystem = 0x19; 17 | 18 | constexpr uint32 TCP_NoopRequest = 0x6; 19 | constexpr uint32 TCP_NoopResponse = 0x3B9ACA06; 20 | constexpr int ServerEcdhCurve = NID_X9_62_prime256v1; 21 | constexpr char ServerEcdhX[] = "1da177b6a5ed34dabb3f2b047697ca8bbeb78c68389ced43317a298d77316d54"; 22 | constexpr char ServerEcdhY[] = "4175c032bc573d5ce4b3ac0b7f2b9a8d48ca4b990ce2fa3ce75cc9d12720fa35"; 23 | #ifndef OPENSSL3 24 | extern EC_GROUP* curve; 25 | extern EC_KEY* ServerEcdh; 26 | #else 27 | extern EC_GROUP* curve; 28 | extern const char* curveName; 29 | extern EVP_PKEY* ServerEcdh; 30 | #endif 31 | 32 | inline std::string bytesFromHex(const std::string& _Src) { 33 | std::string _Out; 34 | for (unsigned int i = 0; i < _Src.length(); i += 2) { 35 | _Out.push_back(std::stoi(_Src.substr(i, 2), 0, 16)); 36 | } 37 | return _Out; 38 | } 39 | 40 | inline std::string toHexString(const std::string& _Src) { 41 | std::string _dst; 42 | char tmp[4] = { 0 }; 43 | for (unsigned int i = 0; i < _Src.length(); i++) { 44 | memset(tmp, 0, 4); 45 | unsigned char b = _Src[i]; 46 | #if defined(_WIN32) 47 | sprintf_s(tmp, "%02X", b); 48 | #else 49 | sprintf(tmp, "%02X", b); 50 | #endif 51 | _dst += std::string(tmp, 2); 52 | } 53 | return _dst; 54 | } 55 | 56 | constexpr uint32 AEAD_TAG_LEN = 16; 57 | #endif -------------------------------------------------------------------------------- /src/custom_defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if defined(_WIN32) 3 | #include 4 | #include 5 | #pragma warning(disable:26495) 6 | #else 7 | #include 8 | #endif 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef _WIN32 15 | typedef unsigned char BYTE; 16 | typedef unsigned int UINT32; 17 | typedef unsigned short USHORT; 18 | typedef int INT32; 19 | typedef int SOCKET; 20 | #endif // !_WIN32 21 | 22 | #ifndef INVALID_SOCKET 23 | #define INVALID_SOCKET ~0 24 | #endif 25 | 26 | #ifndef SOCKET_ERROR 27 | #define SOCKET_ERROR -1 28 | #endif 29 | 30 | typedef std::vector byteArray; 31 | typedef unsigned short uint16; 32 | typedef unsigned char uint8; 33 | typedef unsigned int uint32; 34 | typedef std::vector uint16Array; -------------------------------------------------------------------------------- /src/handshakeHasher.cpp: -------------------------------------------------------------------------------- 1 | #include "handshakeHasher.h" 2 | #include "const.h" 3 | 4 | int HandshakeHasher::Sum(byteArray& digest, const byteArray& extraInfo) { 5 | if(extraInfo.size() > 0) 6 | m_Content.insert(m_Content.end(), extraInfo.begin(), extraInfo.end()); 7 | byteArray result(EVP_MAX_MD_SIZE, 0); 8 | unsigned dLen = 0; 9 | EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); 10 | if (1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), nullptr)) 11 | return -1; 12 | if (1 != EVP_DigestUpdate(mdctx, m_Content.data(), m_Content.size())) 13 | return -1; 14 | if (1 != EVP_DigestFinal_ex(mdctx, result.data(), &dLen)) 15 | return -1; 16 | EVP_MD_CTX_free(mdctx); 17 | digest = byteArray(result.begin(), result.begin() + dLen); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /src/handshakeHasher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | #include "mmtls_openssl.h" 4 | 5 | class HandshakeHasher { 6 | public: 7 | HandshakeHasher(const EVP_MD* evpMd) : m_EvpMd(evpMd) { }; 8 | virtual ~HandshakeHasher() {}; 9 | virtual int Write(const byteArray& info) { 10 | m_Content.insert(m_Content.end(), info.begin(), info.end()); 11 | return (int)m_Content.size(); 12 | }; 13 | virtual void reset() { 14 | m_Content.clear(); 15 | }; 16 | virtual int Sum(byteArray& digest, const byteArray& extraInfo = {}); 17 | private: 18 | byteArray m_Content; 19 | const EVP_MD* m_EvpMd = nullptr; 20 | }; -------------------------------------------------------------------------------- /src/logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined(_WIN32) 8 | #include 9 | #else 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #endif 16 | 17 | constexpr unsigned MAX_LOG_SIZE = 0xffff; 18 | 19 | inline void LOG(const char* level, const char* file, const char* function, int line, const char* _Format, ...) { 20 | static std::mutex mtx; 21 | std::string szFile(file); 22 | #if defined(_WIN32) 23 | auto pos = szFile.find_last_of("\\"); 24 | #else 25 | auto pos = szFile.find_last_of("/"); 26 | #endif 27 | szFile = szFile.substr(pos + 1); 28 | struct tm time_tm = { 0 }; 29 | auto now = std::chrono::system_clock::now(); 30 | uint64_t dis_millseconds = std::chrono::duration_cast(now.time_since_epoch()).count() 31 | - std::chrono::duration_cast(now.time_since_epoch()).count() * 1000; 32 | time_t tt = std::chrono::system_clock::to_time_t(now); 33 | #if defined(_WIN32) 34 | _localtime64_s(&time_tm, &tt); 35 | #else 36 | localtime_r(&tt, &time_tm); 37 | #endif 38 | char strTime[30] = { 0 }; 39 | #if defined(_WIN32) 40 | sprintf_s(strTime, "%d-%02d-%02d %02d:%02d:%02d.%03d", time_tm.tm_year + 1900, 41 | time_tm.tm_mon + 1, time_tm.tm_mday, time_tm.tm_hour, 42 | time_tm.tm_min, time_tm.tm_sec, (int)dis_millseconds); 43 | #else 44 | snprintf(strTime, sizeof(strTime), "%d-%02d-%02d %02d:%02d:%02d.%03d", time_tm.tm_year + 1900, 45 | time_tm.tm_mon + 1, time_tm.tm_mday, time_tm.tm_hour, 46 | time_tm.tm_min, time_tm.tm_sec, (int)dis_millseconds); 47 | #endif 48 | std::unique_ptr msg_buf = std::make_unique(MAX_LOG_SIZE); 49 | va_list args; 50 | va_start(args, _Format); 51 | #if defined(_WIN32) 52 | vsprintf_s(msg_buf.get(), MAX_LOG_SIZE, _Format, args); 53 | #else 54 | vsnprintf(msg_buf.get(), MAX_LOG_SIZE, _Format, args); 55 | #endif 56 | va_end(args); 57 | std::string msg(msg_buf.get()); 58 | memset(msg_buf.get(), 0, MAX_LOG_SIZE); 59 | #if defined(_WIN32) 60 | const char* log_format = "%s|%s|%lu|%s[line:%d] - %s : %s"; 61 | #else 62 | const char* log_format = "%s|%s|%llu|%s[line:%d] - %s : %s"; 63 | #endif 64 | std::thread::id id = std::this_thread::get_id(); 65 | #if defined(_WIN32) 66 | sprintf_s(msg_buf.get(), MAX_LOG_SIZE, log_format, strTime, level, *(unsigned int*)&id, szFile.c_str(), line, function, msg.c_str()); 67 | #else 68 | snprintf(msg_buf.get(), MAX_LOG_SIZE, log_format, strTime, level, *(unsigned long long*)&id, szFile.c_str(), line, function, msg.c_str()); 69 | #endif 70 | std::string output(msg_buf.get()); 71 | mtx.lock(); 72 | try { 73 | std::cout << output << std::endl; 74 | } 75 | catch (...) { 76 | } 77 | mtx.unlock(); 78 | } 79 | 80 | #define LL_INFO(_Format, ...) LOG("INFO",__FILE__,__FUNCTION__,__LINE__,_Format, ##__VA_ARGS__) 81 | #define LL_WARNING(_Format, ...) LOG("WARNING",__FILE__,__FUNCTION__,__LINE__,_Format, ##__VA_ARGS__) 82 | #define LL_DEBUG(_Format, ...) LOG("DEBUG",__FILE__,__FUNCTION__,__LINE__,_Format, ##__VA_ARGS__) 83 | #define LL_ERROR(_Format, ...) LOG("ERROR",__FILE__,__FUNCTION__,__LINE__,_Format, ##__VA_ARGS__) -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mmtls.h" 2 | #include "session.h" 3 | #include "mmtls_short.h" 4 | 5 | int main() 6 | { 7 | InitServerEcdh(); 8 | #if defined(_WIN32) 9 | WSADATA wsaData = { 0 }; 10 | if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 11 | goto wrapup; 12 | #endif 13 | { 14 | int err = 0; 15 | MMTLSClient client; 16 | Session session; 17 | err = Session::loadSession("session", session); 18 | if (err == 0) 19 | client.session = &session; 20 | err = client.HandShake("long.weixin.qq.com"); 21 | if(err >= 0) 22 | client.session->Save("session"); 23 | if(err >= 0) 24 | err = client.Noop(); 25 | client.Close(); 26 | } 27 | { 28 | int err = 0; 29 | MMTLSClientShort client; 30 | Session session; 31 | err = Session::loadSession("session", session); 32 | if (err == 0) 33 | client.session = &session; 34 | byteArray resp; 35 | err = client.Request("dns.weixin.qq.com.cn", "/cgi-bin/micromsg-bin/newgetdns", {}, resp); 36 | client.Close(); 37 | } 38 | 39 | #if defined(_WIN32) 40 | wrapup: 41 | WSACleanup(); 42 | #endif 43 | UnInitServerEcdh(); 44 | return 0; 45 | } -------------------------------------------------------------------------------- /src/mmtls.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc545w/mmtls/a4b90d2bcc0395636bbd7a3ee1204ec00c3dc88d/src/mmtls.cpp -------------------------------------------------------------------------------- /src/mmtls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | #include "session.h" 4 | #include "client_hello.h" 5 | #include "server_hello.h" 6 | #include "record.h" 7 | #include "handshakeHasher.h" 8 | #include "mmtls_openssl.h" 9 | #if defined(_WIN32) 10 | #include 11 | #else 12 | 13 | #endif 14 | #include 15 | 16 | void InitServerEcdh(); 17 | void UnInitServerEcdh(); 18 | 19 | struct MMTLSClientTag { 20 | SOCKET conn = NULL; 21 | std::atomic status = 0; 22 | #ifndef OPENSSL3 23 | EC_KEY* publicEcdh = nullptr; 24 | EC_KEY* verifyEcdh = nullptr; 25 | EC_KEY* serverEcdh = nullptr; 26 | #else 27 | EVP_PKEY* publicEcdh = nullptr; 28 | EVP_PKEY* verifyEcdh = nullptr; 29 | EVP_PKEY* serverEcdh = nullptr; 30 | #endif 31 | HandshakeHasher* handshakeHasher = nullptr; 32 | UINT32 serverSeqNum = 0; 33 | UINT32 clientSeqNum = 0; 34 | Session* session = nullptr; 35 | }; 36 | 37 | class MMTLSClient : public MMTLSClientTag { 38 | public: 39 | MMTLSClient(); 40 | ~MMTLSClient(); 41 | int HandShake(const std::string& host); 42 | int Noop(); 43 | int Close(); 44 | int reset(); 45 | bool handshakeComplete(); 46 | int sendClientHello(clientHello& hello); 47 | int readServerHello(serverHello& hello); 48 | int readSignature(trafficKeyPair& trafficKey); 49 | int readNewSessionTicket(const byteArray& comKey, const trafficKeyPair& trafficKey); 50 | int readServerFinish(const byteArray& comKey, const trafficKeyPair& trafficKey); 51 | int sendClientFinish(const byteArray& comKey, const trafficKeyPair& trafficKey); 52 | int sendNoop(); 53 | int readNoop(); 54 | int readRecord(mmtlsRecord& record); 55 | byteArray computeEphemeralSecret(const EC_POINT* serverPublicKey, const BIGNUM* publicEcdhPrivateKey); 56 | int computeTrafficKey(const byteArray& shareKey, const byteArray& info, trafficKeyPair& pair); 57 | bool verifyEcdsa(const byteArray& data); 58 | byteArray hkdfExpand(const std::string& prefix, HandshakeHasher* const hash); 59 | byteArray hmac(const byteArray& k, const byteArray& d); 60 | int genKeyPairs(); 61 | private: 62 | bool m_bIsNewSession = false; 63 | }; -------------------------------------------------------------------------------- /src/mmtls_openssl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __MMTLS_OPENSSL_H__ 3 | #define __MMTLS_OPENSSL_H__ 1 4 | #include 5 | 6 | #if OPENSSL_VERSION_NUMBER >= 0x3000000fL 7 | #include 8 | #include 9 | #include 10 | #else 11 | #ifndef OPENSSL_API_LEVEL 12 | #define OPENSSL_API_LEVEL 0 13 | #endif // OPENSSL_API_LEVEL 14 | #endif // OPENSSL_VERSION_NUMBER >= 0x3000000fL 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #if (OPENSSL_API_LEVEL >= 30000 && !defined(OPENSSL3)) 28 | #ifndef NO_USE_OPENSSL3 29 | #define OPENSSL3 30 | #else 31 | #pragma warning(disable:4996) // deprecated apis 32 | #endif // NO_USE_OPENSSL3 33 | #endif // (OPENSSL_API_LEVEL >= 30000 && !defined(OPENSSL3)) 34 | 35 | #endif // __MMTLS_OPENSSL_H__ -------------------------------------------------------------------------------- /src/mmtls_short.cpp: -------------------------------------------------------------------------------- 1 | #include "mmtls_short.h" 2 | #include "utility.h" 3 | #include 4 | #include "signature.h" 5 | #include "server_finish.h" 6 | #include "client_finish.h" 7 | #include "client_hello.h" 8 | #include "const.h" 9 | #include "logger.hpp" 10 | 11 | #if !defined(_WIN32) 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | static byteArray hkdfExpand(const EVP_MD* hasher, const byteArray& preudorandomKey, const byteArray& info, int length) 22 | { 23 | EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); 24 | byteArray result(length, 0); 25 | size_t outlen = length, ret = 0; 26 | ret = EVP_PKEY_derive_init(pctx) <= 0 27 | || EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0 28 | || EVP_PKEY_CTX_set_hkdf_md(pctx, hasher) <= 0 29 | || EVP_PKEY_CTX_set1_hkdf_key(pctx, preudorandomKey.data(), (unsigned)preudorandomKey.size()) <= 0 30 | || EVP_PKEY_CTX_add1_hkdf_info(pctx, info.data(), (unsigned)info.size()) <= 0 31 | || EVP_PKEY_derive(pctx, result.data(), &outlen) <= 0; 32 | EVP_PKEY_CTX_free(pctx); 33 | return result; 34 | } 35 | 36 | MMTLSClientShort::MMTLSClientShort() { 37 | handshakeHasher = new HandshakeHasher(EVP_sha256()); 38 | } 39 | 40 | MMTLSClientShort::~MMTLSClientShort() { 41 | if (handshakeHasher) 42 | delete handshakeHasher; 43 | } 44 | 45 | int MMTLSClientShort::Request(const std::string& host, const std::string& path, const byteArray& req, byteArray& resp) { 46 | LL_INFO("Short link request begin!!!!"); 47 | sockaddr_in serverAddress = { 0 }; 48 | serverAddress.sin_family = AF_INET; 49 | serverAddress.sin_port = htons(80); 50 | const std::string ip = getHostByName(host); 51 | int rc = 0; 52 | byteArray httpPacket; 53 | byteArray response; 54 | trafficKeyPair trafficKey; 55 | mmtlsRecord dataRecord; 56 | packetReader = nullptr; 57 | packetReaderEnd = nullptr; 58 | if (session == nullptr) { 59 | rc = -1; 60 | goto wrapup; 61 | } 62 | if (conn == NULL) { 63 | // ´´½¨socket 64 | conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 65 | if (conn == INVALID_SOCKET) { 66 | rc = -1; 67 | goto wrapup; 68 | } 69 | #if defined(_WIN32) 70 | InetPtonA(AF_INET, ip.c_str(), &serverAddress.sin_addr.s_addr); 71 | #else 72 | inet_pton(AF_INET, ip.c_str(), &serverAddress.sin_addr.s_addr); 73 | #endif 74 | if (connect(conn, reinterpret_cast(&serverAddress), sizeof(sockaddr)) == SOCKET_ERROR) 75 | { 76 | rc = -1; 77 | goto wrapup; 78 | } 79 | } 80 | rc = packHttp(host, path, req, httpPacket); 81 | if (rc < 0) 82 | goto wrapup; 83 | rc = send(conn, (char*)httpPacket.data(), (int)httpPacket.size(), 0); 84 | if (rc <= 0) { 85 | rc = -1; 86 | goto wrapup; 87 | } 88 | rc = parseResponse(conn, response); 89 | if (rc < 0) 90 | goto wrapup; 91 | packetReader = response.data(); 92 | packetReaderEnd = response.data() + response.size(); 93 | rc = readServerHello(); 94 | if (rc < 0) 95 | goto wrapup; 96 | rc = computeTrafficKey(session->pskAccess, hkdfExpand("handshake key expansion", handshakeHasher), trafficKey); 97 | if (rc < 0) 98 | goto wrapup; 99 | session->appKey = trafficKey; 100 | rc = readServerFinish(); 101 | if (rc < 0) 102 | goto wrapup; 103 | rc = readDataRecord(dataRecord); 104 | if (rc < 0) 105 | goto wrapup; 106 | rc = readAbort(); 107 | if (rc < 0) 108 | goto wrapup; 109 | resp = dataRecord.data; 110 | wrapup: 111 | packetReader = nullptr; 112 | packetReaderEnd = nullptr; 113 | LL_INFO("Short link request end!!!!error_code: %d", rc); 114 | return rc; 115 | } 116 | 117 | int MMTLSClientShort::Close() { 118 | if (conn != NULL) { 119 | #if defined(_WIN32) 120 | closesocket(conn); 121 | #else 122 | close(conn); 123 | #endif 124 | conn = NULL; 125 | } 126 | return 0; 127 | } 128 | 129 | int MMTLSClientShort::packHttp(const std::string& host, const std::string& path, const byteArray& req, byteArray& resp) { 130 | int rc = 0; 131 | byteArray tlsPayload, datPart; 132 | rc = genDataPart(host, path, req, datPart); 133 | if (rc < 0) 134 | return rc; 135 | clientHello hello = clientHello::newPskZeroHello(session->tk.tickets[0]); 136 | byteArray helloPart = hello.serialize(); 137 | handshakeHasher->Write(helloPart); 138 | trafficKeyPair earlyKey; 139 | rc = earlyDataKey(session->pskAccess, session->tk.tickets[0], earlyKey); 140 | if (rc < 0) 141 | return rc; 142 | byteArray recordData = mmtlsRecord::createSystemRecord(helloPart).serialize(); 143 | tlsPayload.insert(tlsPayload.end(), recordData.begin(), recordData.end()); 144 | clientSeqNum++; 145 | // Extensions 146 | byteArray extensionsPart = { 147 | 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 148 | 0x0b, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x12, 149 | 0x00, 0x00, 0x00, 0x00 150 | }; 151 | for (int i = 0; i < 4; i++) { 152 | extensionsPart[16LL + i] = (hello.timestamp >> (24 - i * 8)) & 0xff; 153 | } 154 | handshakeHasher->Write(extensionsPart); 155 | mmtlsRecord extensionsRecord = mmtlsRecord::createSystemRecord(extensionsPart); 156 | rc = extensionsRecord.encrypt(earlyKey, clientSeqNum); 157 | if (rc < 0) 158 | return rc; 159 | recordData = extensionsRecord.serialize(); 160 | tlsPayload.insert(tlsPayload.end(), recordData.begin(), recordData.end()); 161 | clientSeqNum++; 162 | // Request 163 | mmtlsRecord requestRecord = mmtlsRecord::createRawDataRecord(datPart); 164 | rc = requestRecord.encrypt(earlyKey, clientSeqNum); 165 | if (rc < 0) 166 | return rc; 167 | recordData = requestRecord.serialize(); 168 | tlsPayload.insert(tlsPayload.end(), recordData.begin(), recordData.end()); 169 | clientSeqNum++; 170 | // Abort 171 | byteArray abortPart = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x01 }; 172 | mmtlsRecord abortRecord = mmtlsRecord::createAbortRecord(abortPart); 173 | abortRecord.encrypt(earlyKey, clientSeqNum); 174 | recordData = abortRecord.serialize(); 175 | tlsPayload.insert(tlsPayload.end(), recordData.begin(), recordData.end()); 176 | clientSeqNum++; 177 | byteArray header; 178 | rc = buildRequestHeader(host, (int)tlsPayload.size(), header); 179 | if (rc < 0) 180 | return rc; 181 | header.insert(header.end(), tlsPayload.begin(), tlsPayload.end()); 182 | resp = std::move(header); 183 | LL_INFO(toHexString(std::string(resp.begin(), resp.end())).c_str()); 184 | return rc; 185 | } 186 | 187 | int MMTLSClientShort::genDataPart(const std::string& host, const std::string& path, const byteArray& req, byteArray& resp) { 188 | int rc = 0; 189 | byteArray result(4, 0); 190 | rc = writeU16LenData(result, byteArray(path.begin(), path.end())); 191 | if (rc < 0) 192 | return rc; 193 | rc = writeU16LenData(result, byteArray(host.begin(), host.end())); 194 | if (rc < 0) 195 | return rc; 196 | rc = writeU32LenData(result, req); 197 | if (rc < 0) 198 | return rc; 199 | uint32 length = (uint32)result.size() - 4; 200 | for (int i = 0; i < 4; i++) { 201 | result[i] = (length >> (24 - i * 8)) & 0xff; 202 | } 203 | resp = std::move(result); 204 | return rc; 205 | } 206 | 207 | int MMTLSClientShort::buildRequestHeader(const std::string& host, int length, byteArray& resp) { 208 | int rc = 0; 209 | byteArray randArr = getRandom(2); 210 | int randName = (0 << 24) | (0 << 16) | (randArr[0] << 8) | (randArr[1] << 0); 211 | std::string _headerFormat = "POST /mmtls/%08x HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nCache-Control: no-cache\r\nConnection: Keep-Alive\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\nUpgrade: mmtls\r\nUser-Agent: MicroMessenger Client\r\n\r\n"; 212 | size_t bLen = _headerFormat.length() + host.length() + 8 + 11; 213 | char* buffer = new char[bLen](); 214 | #if defined(_WIN32) 215 | sprintf_s(buffer, bLen, _headerFormat.c_str(), randName, host.c_str(), length); 216 | #else 217 | snprintf(buffer, bLen, _headerFormat.c_str(), randName, host.c_str(), length); 218 | #endif 219 | std::string szHeader(buffer); 220 | delete[] buffer; 221 | resp = byteArray(szHeader.begin(), szHeader.end()); 222 | return rc; 223 | } 224 | 225 | int MMTLSClientShort::parseResponse(SOCKET connection, byteArray& resp) { 226 | int rc = 0; 227 | char buf[1024] = { 0 }; 228 | byteArray result; 229 | while (1) { 230 | memset(buf, 0, sizeof(buf)); 231 | rc = recv(connection, buf, sizeof(buf), 0); 232 | if (rc < 0) 233 | return rc; 234 | if (rc == 0) 235 | break; 236 | result.insert(result.end(), buf, buf + rc); 237 | } 238 | // skip response header 239 | std::string szResp(result.begin(), result.end()); 240 | size_t pos = 0; 241 | for (int i = 0; i <= (int)(szResp.length() - 4); i++) { 242 | std::string tmp = szResp.substr(i, 4); 243 | if (tmp == "\r\n\r\n") { 244 | pos = i; 245 | break; 246 | } 247 | } 248 | if (pos == 0) 249 | return -1; 250 | szRespHeader = szResp.substr(0, pos + 4); 251 | result = byteArray(result.begin() + pos + 4, result.end()); 252 | resp = std::move(result); 253 | LL_INFO(toHexString(std::string(resp.begin(), resp.end())).c_str()); 254 | return rc; 255 | } 256 | 257 | int MMTLSClientShort::readServerHello() { 258 | int rc = 0; 259 | mmtlsRecord serverHelloRecord = mmtlsRecord::readRecord(packetReader, packetReaderEnd, rc); 260 | if (rc < 0) 261 | return rc; 262 | packetReader += (5 + (size_t)serverHelloRecord.length); 263 | handshakeHasher->Write(serverHelloRecord.data); 264 | serverSeqNum++; 265 | LL_INFO(toHexString(std::string(serverHelloRecord.data.begin(), serverHelloRecord.data.end())).c_str()); 266 | return rc; 267 | } 268 | 269 | int MMTLSClientShort::readServerFinish() { 270 | int rc = 0; 271 | mmtlsRecord record = mmtlsRecord::readRecord(packetReader, packetReaderEnd, rc); 272 | if (rc < 0) 273 | return rc; 274 | packetReader += (5 + (size_t)record.length); 275 | rc = record.decrypt(session->appKey, serverSeqNum); 276 | if (rc < 0) 277 | return rc; 278 | // TODO: verify server finished 279 | serverSeqNum++; 280 | LL_INFO(toHexString(std::string(record.data.begin(), record.data.end())).c_str()); 281 | return rc; 282 | } 283 | 284 | int MMTLSClientShort::readDataRecord(mmtlsRecord& record) { 285 | int rc = 0; 286 | while (true) { 287 | mmtlsRecord tmpRecord = mmtlsRecord::readRecord(packetReader, packetReaderEnd, rc); 288 | if (rc < 0) 289 | return rc; 290 | if (tmpRecord.recordType == MagicAbort) 291 | break; 292 | packetReader += (5 + (size_t)tmpRecord.length); 293 | rc = tmpRecord.decrypt(session->appKey, serverSeqNum); 294 | if (rc < 0) 295 | return rc; 296 | record.recordType = tmpRecord.recordType; 297 | record.version = tmpRecord.version; 298 | record.length += tmpRecord.length; 299 | record.data.insert(record.data.end(), tmpRecord.data.begin(), tmpRecord.data.end()); 300 | serverSeqNum++; 301 | } 302 | LL_INFO("read data record finish, data size: %llu", record.data.size()); 303 | return rc; 304 | } 305 | 306 | int MMTLSClientShort::readAbort() { 307 | int rc = 0; 308 | mmtlsRecord record = mmtlsRecord::readRecord(packetReader, packetReaderEnd, rc); 309 | if (rc < 0) 310 | return rc; 311 | packetReader += (5 + (size_t)record.length); 312 | rc = record.decrypt(session->appKey, serverSeqNum); 313 | if (rc < 0) 314 | return rc; 315 | serverSeqNum++; 316 | LL_INFO(toHexString(std::string(record.data.begin(), record.data.end())).c_str()); 317 | return rc; 318 | } 319 | 320 | int MMTLSClientShort::earlyDataKey(const byteArray& pskAccess, const sessionTicket& ticket, trafficKeyPair& pair) { 321 | int rc = 0; 322 | byteArray trafficKey = ::hkdfExpand(EVP_sha256(), pskAccess, hkdfExpand("early data key expansion", handshakeHasher), 28); 323 | pair.clientKey = byteArray(trafficKey.begin(), trafficKey.begin() + 16); 324 | pair.clientNonce = byteArray(trafficKey.begin() + 16, trafficKey.end()); 325 | return rc; 326 | } 327 | 328 | int MMTLSClientShort::computeTrafficKey(const byteArray& shareKey, const byteArray& info, trafficKeyPair& pair) { 329 | int rc = 0; 330 | byteArray trafficKey = ::hkdfExpand(EVP_sha256(), shareKey, info, 28); 331 | pair.serverKey = byteArray(trafficKey.begin(), trafficKey.begin() + 16); 332 | pair.serverNonce = byteArray(trafficKey.begin() + 16, trafficKey.end()); 333 | return rc; 334 | } 335 | 336 | byteArray MMTLSClientShort::hkdfExpand(const std::string& prefix, const HandshakeHasher* hash) { 337 | byteArray result(prefix.begin(), prefix.end()); 338 | if (hash != nullptr) { 339 | byteArray hashSum; 340 | int rc = handshakeHasher->Sum(hashSum); 341 | if (rc >= 0) 342 | result.insert(result.end(), hashSum.begin(), hashSum.end()); 343 | } 344 | return result; 345 | } 346 | 347 | byteArray MMTLSClientShort::hmac(const byteArray& k, const byteArray& d) { 348 | byteArray result(SHA256_DIGEST_LENGTH, 0); 349 | #ifndef OPENSSL3 350 | HMAC_CTX* ctx = HMAC_CTX_new(); 351 | unsigned outlen = 0, ret = 0; 352 | ret = HMAC_Init_ex(ctx, k.data(), (unsigned)k.size(), EVP_sha256(), NULL); 353 | ret = HMAC_Update(ctx, d.data(), d.size()); 354 | ret = HMAC_Final(ctx, result.data(), &outlen); 355 | HMAC_CTX_free(ctx); 356 | #else 357 | unsigned int dLen = 0; 358 | HMAC(EVP_sha256(), k.data(), (int)k.size(), d.data(), d.size(), result.data(), &dLen); 359 | byteArray digest(result.begin(), result.begin() + dLen); 360 | result = std::move(digest); 361 | #endif 362 | return result; 363 | } 364 | -------------------------------------------------------------------------------- /src/mmtls_short.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "session.h" 3 | #include "custom_defs.h" 4 | #include "record.h" 5 | #include "mmtls_openssl.h" 6 | #include 7 | #include "handshakeHasher.h" 8 | 9 | struct MMTLSClientShortTag { 10 | SOCKET conn = NULL; 11 | std::atomic status = 0; 12 | BYTE* packetReader = nullptr; 13 | HandshakeHasher* handshakeHasher = nullptr; 14 | uint32 serverSeqNum = 0; 15 | uint32 clientSeqNum = 0; 16 | Session* session = nullptr; 17 | }; 18 | 19 | class MMTLSClientShort : public MMTLSClientShortTag { 20 | public: 21 | MMTLSClientShort(); 22 | ~MMTLSClientShort(); 23 | int Request(const std::string& host, const std::string& path, const byteArray& req, byteArray& resp); 24 | int Close(); 25 | int packHttp(const std::string& host, const std::string& path, const byteArray& req, byteArray& resp); 26 | int genDataPart(const std::string& host, const std::string& path, const byteArray& req, byteArray& resp); 27 | int buildRequestHeader(const std::string& host, int length, byteArray& resp); 28 | int parseResponse(SOCKET conn, byteArray& resp); 29 | int readServerHello(); 30 | int readServerFinish(); 31 | int readDataRecord(mmtlsRecord& record); 32 | int readAbort(); 33 | int earlyDataKey(const byteArray& pskAccess, const sessionTicket& ticket, trafficKeyPair& pair); 34 | int computeTrafficKey(const byteArray& shareKey, const byteArray& info, trafficKeyPair& pair); 35 | byteArray hkdfExpand(const std::string& prefix, const HandshakeHasher* hash); 36 | byteArray hmac(const byteArray& k, const byteArray& d); 37 | private: 38 | BYTE* packetReaderEnd = nullptr; 39 | std::string szRespHeader; 40 | }; -------------------------------------------------------------------------------- /src/record.cpp: -------------------------------------------------------------------------------- 1 | #include "record.h" 2 | #include "utility.h" 3 | #include "const.h" 4 | #include "mmtls_openssl.h" 5 | #include 6 | 7 | byteArray dataRecord::serialize() { 8 | byteArray result; 9 | unsigned length = (unsigned)data.size() + 16; 10 | for (int i = 0; i < 4; i++) { 11 | result.push_back((length >> (24 - i * 8)) & 0xff); 12 | } 13 | result.insert(result.end(), { 0x00,0x10 }); 14 | result.insert(result.end(), { 0x00,0x01 }); 15 | for (int i = 0; i < 4; i++) { 16 | result.push_back((dataType >> (24 - i * 8)) & 0xff); 17 | } 18 | for (int i = 0; i < 4; i++) { 19 | result.push_back((seq >> (24 - i * 8)) & 0xff); 20 | } 21 | if (data.size() > 0) 22 | result.insert(result.end(), data.begin(), data.end()); 23 | return result; 24 | } 25 | 26 | mmtlsRecord mmtlsRecord::createRecord(uint8 recordType, const byteArray& data) { 27 | mmtlsRecord r; 28 | r.recordType = recordType; 29 | r.version = ProtocolVersion; 30 | r.length = data.size() & 0xffff; 31 | r.data = data; 32 | return r; 33 | } 34 | mmtlsRecord mmtlsRecord::readRecord(BYTE* pBufBegin, BYTE* pBufEnd, int& err) { 35 | mmtlsRecord r; 36 | BYTE* lBuf = pBufBegin; 37 | r.recordType = lBuf[0]; 38 | lBuf++; 39 | r.version = (lBuf[0] << 8) | lBuf[1]; 40 | lBuf += 2; 41 | r.length = (lBuf[0] << 8) | lBuf[1]; 42 | lBuf += 2; 43 | r.data = byteArray(lBuf, lBuf + r.length); 44 | lBuf += r.length; 45 | if (lBuf > pBufEnd) 46 | err = -1; 47 | return r; 48 | } 49 | 50 | byteArray mmtlsRecord::serialize() { 51 | byteArray result; 52 | result.push_back(recordType); 53 | result.push_back((version >> 8) & 0xff); 54 | result.push_back((version >> 0) & 0xff); 55 | result.push_back((length >> 8) & 0xff); 56 | result.push_back((length >> 0) & 0xff); 57 | result.insert(result.end(), data.begin(), data.end()); 58 | return result; 59 | } 60 | 61 | int mmtlsRecord::encrypt(const trafficKeyPair& keys, uint32 clientSeqNum) { 62 | byteArray nonce(keys.clientNonce.begin(), keys.clientNonce.end()); 63 | if (nonce.size() == 0) 64 | return -1; 65 | xorNonce(nonce, clientSeqNum); 66 | byteArray auddit(4, 0); 67 | for (int i = 0; i < 4; i++) { 68 | auddit.push_back((clientSeqNum >> (24 - i * 8)) & 0xff); 69 | } 70 | auddit.push_back(recordType); 71 | auddit.push_back((version >> 8) & 0xff); 72 | auddit.push_back((version >> 0) & 0xff); 73 | // GCM add 16-byte tag 74 | uint16 fillLen = length + AEAD_TAG_LEN; 75 | auddit.push_back((fillLen >> 8) & 0xff); 76 | auddit.push_back((fillLen >> 0) & 0xff); 77 | // aead encrypt 78 | EVP_CIPHER_CTX* ctx = nullptr; 79 | int len = 0; 80 | if (!(ctx = EVP_CIPHER_CTX_new())) 81 | return -1; 82 | /* Initialise the encryption operation. */ 83 | const EVP_CIPHER* cipher = EVP_aes_128_gcm(); 84 | byteArray ciphertext(data.size(), 0); 85 | if (1 != EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL)) 86 | return -1; 87 | if (1 != EVP_CIPHER_CTX_set_key_length(ctx, (unsigned)keys.clientKey.size())) 88 | return -1; 89 | if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (unsigned)nonce.size(), NULL)) 90 | return -1; 91 | if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, keys.clientKey.data(), nonce.data())) 92 | return -1; 93 | if (1 != EVP_EncryptUpdate(ctx, NULL, &len, auddit.data(), (unsigned)auddit.size())) 94 | return -1; 95 | if (1 != EVP_EncryptUpdate(ctx, ciphertext.data(), &len, data.data(), (unsigned)data.size())) 96 | return -1; 97 | int ciphertext_len = len; 98 | if (1 != EVP_EncryptFinal_ex(ctx, ciphertext.data() + len, &len)) 99 | return -1; 100 | ciphertext_len += len; 101 | BYTE tag[AEAD_TAG_LEN] = { 0 }; 102 | if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AEAD_TAG_LEN, tag)) 103 | return -1; 104 | EVP_CIPHER_CTX_free(ctx); 105 | byteArray dst(ciphertext.begin(), ciphertext.begin() + ciphertext_len); 106 | dst.insert(dst.end(), tag, tag + sizeof(tag)); 107 | data = std::move(dst); 108 | length = (uint16)data.size(); 109 | return 0; 110 | } 111 | 112 | int mmtlsRecord::decrypt(const trafficKeyPair& keys, uint32 serverSeqNum) { 113 | byteArray nonce(keys.serverNonce.begin(), keys.serverNonce.end()); 114 | xorNonce(nonce, serverSeqNum); 115 | byteArray auddit(4, 0); 116 | for (int i = 0; i < 4; i++) { 117 | auddit.push_back((serverSeqNum >> (24 - i * 8)) & 0xff); 118 | } 119 | auddit.push_back(recordType); 120 | auddit.push_back((version >> 8) & 0xff); 121 | auddit.push_back((version >> 0) & 0xff); 122 | auddit.push_back((length >> 8) & 0xff); 123 | auddit.push_back((length >> 0) & 0xff); 124 | // aead decrypt 125 | EVP_CIPHER_CTX* ctx = nullptr; 126 | int len = 0; 127 | if (!(ctx = EVP_CIPHER_CTX_new())) 128 | return -1; 129 | const EVP_CIPHER* cipher = EVP_aes_128_gcm(); 130 | /* Initialise the encryption operation. */ 131 | byteArray plaintext(data.size(), 0); 132 | if (1 != EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL)) 133 | return -1; 134 | if (1 != EVP_CIPHER_CTX_set_key_length(ctx, (unsigned)keys.serverKey.size())) 135 | return -1; 136 | if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, (unsigned)nonce.size(), NULL)) 137 | return -1; 138 | if (1 != EVP_DecryptInit_ex(ctx, NULL, NULL, keys.serverKey.data(), nonce.data())) 139 | return -1; 140 | if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AEAD_TAG_LEN, data.data() + data.size() - AEAD_TAG_LEN)) 141 | return -1; 142 | if (1 != EVP_DecryptUpdate(ctx, NULL, &len, auddit.data(), (unsigned)auddit.size())) 143 | return -1; 144 | if (1 != EVP_DecryptUpdate(ctx, plaintext.data(), &len, data.data(), (unsigned)data.size() - AEAD_TAG_LEN)) 145 | return -1; 146 | int plaintext_len = len; 147 | int rc = EVP_DecryptFinal_ex(ctx, plaintext.data() + len, &len); 148 | EVP_CIPHER_CTX_free(ctx); 149 | if (rc > 0) { 150 | plaintext_len += len; 151 | } 152 | else { 153 | // tag verify failed. 154 | return -1; 155 | } 156 | byteArray dst(plaintext.begin(), plaintext.begin() + plaintext_len); 157 | data = std::move(dst); 158 | length = (uint16)data.size(); 159 | return 0; 160 | } -------------------------------------------------------------------------------- /src/record.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | #include "session.h" 4 | #include "const.h" 5 | 6 | struct dataRecordTag { 7 | uint32 dataType = 0; 8 | uint32 seq = 0; 9 | byteArray data; 10 | }; 11 | 12 | struct mmtlsRecordTag { 13 | uint8 recordType = 0; 14 | uint16 version = 0; 15 | uint16 length = 0; 16 | byteArray data; 17 | }; 18 | 19 | class dataRecord : public dataRecordTag { 20 | public: 21 | dataRecord(uint32 dataType, uint32 seq, const byteArray& data){ 22 | this->dataType = dataType; 23 | this->seq = seq; 24 | this->data = data; 25 | }; 26 | byteArray serialize(); 27 | }; 28 | 29 | class mmtlsRecord : public mmtlsRecordTag { 30 | public: 31 | static mmtlsRecord createRecord(uint8 recordType, const byteArray& data); 32 | static mmtlsRecord readRecord(BYTE* pBuf, BYTE* pBufEnd, int& err); 33 | static mmtlsRecord createAbortRecord(const byteArray& data) { 34 | return mmtlsRecord::createRecord(MagicAbort, data); 35 | } 36 | static mmtlsRecord createHandshakeRecord(const byteArray& data) { 37 | return mmtlsRecord::createRecord(MagicHandshake, data); 38 | } 39 | static mmtlsRecord createDataRecord(uint32 dataType, uint32 seq, const byteArray& data) { 40 | dataRecord r(dataType,seq,data); 41 | return mmtlsRecord::createRecord(MagicRecord, r.serialize()); 42 | } 43 | static mmtlsRecord createRawDataRecord(const byteArray& data) { 44 | return mmtlsRecord::createRecord(MagicRecord, data); 45 | } 46 | static mmtlsRecord createSystemRecord(const byteArray& data) { 47 | return mmtlsRecord::createRecord(MagicSystem, data); 48 | } 49 | byteArray serialize(); 50 | int encrypt(const trafficKeyPair& keys, uint32 clientSeqNum); 51 | int decrypt(const trafficKeyPair& keys, uint32 serverSeqNum); 52 | }; -------------------------------------------------------------------------------- /src/server_finish.cpp: -------------------------------------------------------------------------------- 1 | #include "server_finish.h" 2 | 3 | serverFinish serverFinish::readServerFinish(BYTE* pBuf, int& err) { 4 | BYTE* lBuf = pBuf; 5 | serverFinish s; 6 | lBuf += 4; 7 | s.reversed = lBuf[0]; 8 | lBuf++; 9 | uint16 length = (lBuf[0] << 8) | lBuf[1]; 10 | lBuf += 2; 11 | s.data = byteArray(lBuf, lBuf + length); 12 | lBuf += length; 13 | err = 0; 14 | return s; 15 | } -------------------------------------------------------------------------------- /src/server_finish.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | 4 | struct serverFinishTag { 5 | BYTE reversed = 0; 6 | byteArray data; 7 | }; 8 | 9 | class serverFinish : public serverFinishTag { 10 | public: 11 | static serverFinish readServerFinish(BYTE* pBuf, int& err); 12 | }; -------------------------------------------------------------------------------- /src/server_hello.cpp: -------------------------------------------------------------------------------- 1 | #include "const.h" 2 | #include "server_hello.h" 3 | #include "utility.h" 4 | 5 | serverHello serverHello::readServerHello(const byteArray& buf, int& err) { 6 | err = 0; 7 | serverHello hello; 8 | BYTE* lBuf = const_cast(buf.data()); 9 | uint32 packLen = 0; 10 | packLen = (lBuf[0] << 24) | (lBuf[1] << 16) | (lBuf[2] << 8) | (lBuf[3] << 0); 11 | lBuf += 4; 12 | if (buf.size() != (size_t)(packLen + 4)) { 13 | throw std::runtime_error("data corrupted"); 14 | } 15 | // skip flag 16 | lBuf++; 17 | hello.protocolVersion = (lBuf[0] << 8) | lBuf[1]; 18 | lBuf += 2; 19 | hello.cipherSuites = (lBuf[0] << 8) | lBuf[1]; 20 | lBuf += 2; 21 | // skip server random 22 | lBuf += 32; 23 | // skip exntensions package length 24 | lBuf += 4; 25 | // skip extensions count 26 | lBuf++; 27 | // skip extension package length 28 | lBuf += 4; 29 | // skip extension type 30 | lBuf += 2; 31 | // skip extension array index 32 | lBuf += 4; 33 | uint16 keyLen = (lBuf[0] << 8) | lBuf[1]; 34 | lBuf += 2; 35 | byteArray ecPoint(lBuf, lBuf + keyLen); 36 | lBuf += keyLen; 37 | #ifndef OPENSSL3 38 | hello.publicKey = EC_KEY_new_by_curve_name(ServerEcdhCurve); 39 | int rc = EC_KEY_oct2key(hello.publicKey, ecPoint.data(), ecPoint.size(), nullptr); 40 | #else 41 | hello.publicKey = EVP_PKEY_new(); 42 | int rc = EVP_EC_KEY_oct2key(hello.publicKey, ecPoint.data(), ecPoint.size()); 43 | #endif 44 | if (!rc) { 45 | err = -1; 46 | } 47 | return hello; 48 | } -------------------------------------------------------------------------------- /src/server_hello.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "mmtls_openssl.h" 3 | #include "custom_defs.h" 4 | 5 | struct serverHelloTag { 6 | uint16 protocolVersion = 0; 7 | uint16 cipherSuites = 0; 8 | #ifndef OPENSSL3 9 | EC_KEY* publicKey = nullptr; 10 | #else 11 | EVP_PKEY* publicKey = nullptr; 12 | #endif 13 | }; 14 | 15 | class serverHello : public serverHelloTag { 16 | public: 17 | static serverHello readServerHello(const byteArray& buf, int& err); 18 | }; -------------------------------------------------------------------------------- /src/session.cpp: -------------------------------------------------------------------------------- 1 | #include "session.h" 2 | #include "utility.h" 3 | 4 | #if !defined(_WIN32) 5 | #include 6 | #endif 7 | 8 | Session::Session(const newSessionTicket& tickets, const byteArray& pskAccess, const byteArray& pskRefresh) { 9 | this->tk = tickets; 10 | this->pskAccess = pskAccess; 11 | this->pskRefresh = pskRefresh; 12 | } 13 | 14 | bool Session::Save(const std::string& path) { 15 | std::ofstream ofs(path, std::ios::binary | std::ios::trunc); 16 | if (!ofs.good()) 17 | return false; 18 | byteArray buf; 19 | writeU16LenData(buf, pskAccess); 20 | writeU16LenData(buf, pskRefresh); 21 | byteArray ticketBytes = tk.serialize(); 22 | buf.insert(buf.end(), ticketBytes.begin(), ticketBytes.end()); 23 | ofs.write((char*)buf.data(), buf.size()); 24 | ofs.close(); 25 | return true; 26 | } 27 | 28 | int Session::loadSession(const std::string& path, Session& s) { 29 | int err = 0; 30 | std::ifstream ifs(path, std::ios::binary); 31 | if (!ifs.good()) { 32 | err = -1; 33 | return err; 34 | } 35 | ifs.seekg(0, std::ios::end); 36 | size_t fLen = ifs.tellg(); 37 | ifs.seekg(0, std::ios::beg); 38 | std::unique_ptr buffer = std::make_unique(fLen); 39 | ifs.read(buffer.get(), fLen); 40 | ifs.close(); 41 | BYTE* pBuf = (BYTE*)buffer.get(); 42 | BYTE* pBufEnd = pBuf + fLen; 43 | UINT32 length = 0; 44 | s.pskAccess = readU16LenData(pBuf, length); 45 | pBuf += (sizeof(USHORT) + length); 46 | s.pskRefresh = readU16LenData(pBuf, length); 47 | pBuf += (sizeof(USHORT) + length); 48 | byteArray ticketBytes(pBuf, pBufEnd); 49 | s.tk = readNewSessionTicket(ticketBytes, err); 50 | return err; 51 | } -------------------------------------------------------------------------------- /src/session.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "session_ticket.h" 3 | 4 | struct trafficKeyPairTag { 5 | byteArray clientKey; 6 | byteArray serverKey; 7 | byteArray clientNonce; 8 | byteArray serverNonce; 9 | }; 10 | 11 | class trafficKeyPair : public trafficKeyPairTag { 12 | }; 13 | 14 | struct SessionTag { 15 | newSessionTicket tk; 16 | byteArray pskAccess; 17 | byteArray pskRefresh; 18 | trafficKeyPair appKey; 19 | }; 20 | 21 | class Session : public SessionTag { 22 | public: 23 | Session() {}; 24 | Session(const newSessionTicket& tickets, const byteArray& pskAccess, const byteArray& pskRefresh); 25 | bool Save(const std::string& path); 26 | static int loadSession(const std::string& path, Session& s); 27 | }; -------------------------------------------------------------------------------- /src/session_ticket.cpp: -------------------------------------------------------------------------------- 1 | #include "session_ticket.h" 2 | #include "utility.h" 3 | 4 | sessionTicket readSessionTicket(const byteArray& buf, int& err) { 5 | sessionTicket t; 6 | BYTE* pBuf = const_cast(buf).data(); 7 | UINT32 length = 0; 8 | t.ticketType = pBuf[0]; 9 | pBuf++; 10 | t.ticketLifeTime = (pBuf[0] << 24) | (pBuf[1] << 16) | (pBuf[2] << 8) | (pBuf[3] << 0); 11 | pBuf += 4; 12 | t.ticketAgeAdd = readU16LenData(pBuf, length); 13 | pBuf += (sizeof(USHORT) + length); 14 | t.reversed = (pBuf[0] << 24) | (pBuf[1] << 16) | (pBuf[2] << 8) | (pBuf[3] << 0); 15 | pBuf += 4; 16 | t.nonce = readU16LenData(pBuf, length); 17 | pBuf += (sizeof(USHORT) + length); 18 | t.ticket = readU16LenData(pBuf, length); 19 | pBuf += (sizeof(USHORT) + length); 20 | return t; 21 | } 22 | 23 | newSessionTicket readNewSessionTicket(const byteArray& buf, int& err) { 24 | newSessionTicket t; 25 | BYTE* pBuf = const_cast(buf).data(); 26 | BYTE* pEndBuf = const_cast(buf).data() + buf.size(); 27 | UINT32 length = 0; 28 | length = (pBuf[0] << 24) | (pBuf[1] << 16) | (pBuf[2] << 8) | (pBuf[3] << 0); 29 | pBuf += 4; 30 | t.reversed = pBuf[0]; 31 | pBuf++; 32 | t.count = pBuf[0]; 33 | pBuf++; 34 | for (BYTE i = 0; i < t.count; i++) { 35 | length = (pBuf[0] << 24) | (pBuf[1] << 16) | (pBuf[2] << 8) | (pBuf[3] << 0); 36 | pBuf += 4; 37 | if (pBuf + length > pEndBuf) 38 | { 39 | err = -1; 40 | break; 41 | } 42 | byteArray data(pBuf, pBuf + length); 43 | pBuf += length; 44 | auto ticket = readSessionTicket(data, err); 45 | t.tickets.push_back(ticket); 46 | } 47 | return t; 48 | } 49 | 50 | byteArray sessionTicket::serialize() { 51 | byteArray result; 52 | result.push_back(this->ticketType); 53 | for (unsigned i = 0; i < sizeof(this->ticketLifeTime); i++) { 54 | result.push_back((this->ticketLifeTime >> (24 - i * 8)) & 0xff); 55 | } 56 | writeU16LenData(result, this->ticketAgeAdd); 57 | for (unsigned i = 0; i < sizeof(this->ticketLifeTime); i++) { 58 | result.push_back((this->reversed >> (24 - i * 8)) & 0xff); 59 | } 60 | writeU16LenData(result, this->nonce); 61 | writeU16LenData(result, this->ticket); 62 | return result; 63 | } 64 | 65 | byteArray newSessionTicket::serialize() { 66 | byteArray result; 67 | for (int i = 0; i < 4; i++) 68 | result.push_back(0x0); 69 | result.push_back(0x04); 70 | result.push_back(this->tickets.size() & 0xff); 71 | for (auto& ticket : this->tickets) { 72 | auto vBytes = ticket.serialize(); 73 | writeU32LenData(result, vBytes); 74 | } 75 | UINT32 dLen = (UINT32)(result.size() - 4); 76 | result[0] = (dLen >> 24) & 0xff; 77 | result[1] = (dLen >> 16) & 0xff; 78 | result[2] = (dLen >> 8) & 0xff; 79 | result[3] = (dLen >> 0) & 0xff; 80 | return result; 81 | } 82 | 83 | byteArray newSessionTicket::Export() { 84 | byteArray result; 85 | if (this->tickets.size() == 0) 86 | return result; 87 | auto data = this->tickets[0].serialize(); 88 | writeU32LenData(result, data); 89 | return result; 90 | } -------------------------------------------------------------------------------- /src/session_ticket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | 4 | struct sessionTicketTag { 5 | BYTE ticketType = 0; // reversed unknonw 6 | UINT32 ticketLifeTime = 0; 7 | byteArray ticketAgeAdd; 8 | UINT32 reversed = 0; // always 0x48 9 | byteArray nonce; // 12 bytes nonce 10 | byteArray ticket; 11 | }; 12 | 13 | class sessionTicket : public sessionTicketTag { 14 | public: 15 | byteArray serialize(); 16 | }; 17 | 18 | struct newSessionTicketTag { 19 | BYTE reversed = 0; 20 | BYTE count = 0; 21 | std::vector tickets; 22 | }; 23 | 24 | class newSessionTicket : public newSessionTicketTag { 25 | public: 26 | byteArray serialize(); 27 | byteArray Export(); 28 | }; 29 | 30 | sessionTicket readSessionTicket(const byteArray& buf, int& err); 31 | newSessionTicket readNewSessionTicket(const byteArray& buf, int& err); -------------------------------------------------------------------------------- /src/signature.cpp: -------------------------------------------------------------------------------- 1 | #include "signature.h" 2 | 3 | signature signature::readSignature(BYTE* pBuf, int& err) { 4 | signature sign; 5 | BYTE* lBuf = pBuf; 6 | // skip package length 7 | lBuf += 4; 8 | // static 0x0f 9 | sign.Type = lBuf[0]; 10 | lBuf++; 11 | uint16 length = (lBuf[0] << 8) | lBuf[1]; 12 | lBuf += 2; 13 | sign.EcdsaSignature = byteArray(lBuf, lBuf + length); 14 | lBuf += length; 15 | err = 0; 16 | return sign; 17 | } -------------------------------------------------------------------------------- /src/signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | 4 | struct signatureTag { 5 | BYTE Type = 0; 6 | byteArray EcdsaSignature; 7 | }; 8 | 9 | class signature : public signatureTag { 10 | public: 11 | static signature readSignature(BYTE* pBuf, int& err); 12 | }; -------------------------------------------------------------------------------- /src/utility.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ljc545w/mmtls/a4b90d2bcc0395636bbd7a3ee1204ec00c3dc88d/src/utility.cpp -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "custom_defs.h" 3 | #include "mmtls_openssl.h" 4 | 5 | byteArray getRandom(int n); 6 | void xorNonce(byteArray& nonce, UINT32 seq); 7 | byteArray readU16LenData(BYTE* pBuf, UINT32& refLen); 8 | UINT32 writeU16LenData(byteArray& dst, const byteArray& src); 9 | UINT32 writeU32LenData(byteArray& dst, const byteArray& src); 10 | const std::string getHostByName(const std::string& hostName); 11 | 12 | #ifdef OPENSSL3 13 | int EVP_EC_KEY_oct2key(EVP_PKEY* key, const unsigned char* buf, size_t len); 14 | int EVP_EC_KEY_key2buf(const EVP_PKEY* key, std::string& outData); 15 | int EVP_EC_KEY_get0_public_key(const EC_GROUP* curve, const EVP_PKEY* key, EC_POINT** ppEcPoint); 16 | int EVP_EC_KEY_get0_private_key(const EVP_PKEY* key, BIGNUM** ppBigNum); 17 | #endif -------------------------------------------------------------------------------- /windows/mmtls.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32602.291 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mmtls", "mmtls.vcxproj", "{CF831D17-C4C9-43C6-837A-F23FCABA29A9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Debug|x64.ActiveCfg = Debug|x64 17 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Debug|x64.Build.0 = Debug|x64 18 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Debug|x86.ActiveCfg = Debug|Win32 19 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Debug|x86.Build.0 = Debug|Win32 20 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Release|x64.ActiveCfg = Release|x64 21 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Release|x64.Build.0 = Release|x64 22 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Release|x86.ActiveCfg = Release|Win32 23 | {CF831D17-C4C9-43C6-837A-F23FCABA29A9}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {E1ABDA75-AEE1-4E7E-9508-9D1D60BB536E} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /windows/mmtls.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 17.0 56 | Win32Proj 57 | {cf831d17-c4c9-43c6-837a-f23fcaba29a9} 58 | mmtls 59 | 10.0 60 | 61 | 62 | 63 | Application 64 | true 65 | v143 66 | Unicode 67 | 68 | 69 | Application 70 | false 71 | v143 72 | true 73 | Unicode 74 | 75 | 76 | Application 77 | true 78 | v143 79 | Unicode 80 | 81 | 82 | Application 83 | false 84 | v143 85 | true 86 | Unicode 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | D:\Program Files\OpenSSL-Win64-3.0\include;../src;$(IncludePath) 108 | $(ProjectName)d 109 | $(SolutionDir)Bin\$(Platform)\ 110 | 111 | 112 | D:\Program Files\OpenSSL-Win64-3.0\include;../src;$(IncludePath) 113 | $(ProjectName) 114 | $(SolutionDir)Bin\$(Platform)\ 115 | 116 | 117 | mmtls 118 | 119 | 120 | mmtls 121 | 122 | 123 | 124 | Level3 125 | true 126 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | 129 | 130 | Console 131 | true 132 | 133 | 134 | 135 | 136 | Level3 137 | true 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | true 142 | 143 | 144 | Console 145 | true 146 | true 147 | true 148 | 149 | 150 | 151 | 152 | Level3 153 | true 154 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 155 | true 156 | 4996 157 | 158 | 159 | Console 160 | true 161 | D:\Program Files\OpenSSL-Win64-3.0\lib\VC\x64\MDd;%(AdditionalLibraryDirectories) 162 | ws2_32.lib;Crypt32.lib;libssl_static.lib;libcrypto_static.lib;%(AdditionalDependencies) 163 | $(SolutionDir)Bin\$(Platform)\$(TargetName)$(TargetExt) 164 | 165 | 166 | 167 | 168 | Level3 169 | true 170 | true 171 | true 172 | WIN32_LEAN_AND_MEAN;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 173 | true 174 | 175 | 176 | Console 177 | true 178 | true 179 | true 180 | D:\Program Files\OpenSSL-Win64-3.0\lib\VC\x64\MD;%(AdditionalLibraryDirectories) 181 | ws2_32.lib;Crypt32.lib;libssl_static.lib;libcrypto_static.lib;%(AdditionalDependencies) 182 | $(SolutionDir)Bin\$(Platform)\$(TargetName)$(TargetExt) 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /windows/mmtls.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | 源文件 23 | 24 | 25 | 源文件 26 | 27 | 28 | 源文件 29 | 30 | 31 | 源文件 32 | 33 | 34 | 源文件 35 | 36 | 37 | 源文件 38 | 39 | 40 | 源文件 41 | 42 | 43 | 源文件 44 | 45 | 46 | 源文件 47 | 48 | 49 | 源文件 50 | 51 | 52 | 源文件 53 | 54 | 55 | 源文件 56 | 57 | 58 | 59 | 60 | 头文件 61 | 62 | 63 | 头文件 64 | 65 | 66 | 头文件 67 | 68 | 69 | 头文件 70 | 71 | 72 | 头文件 73 | 74 | 75 | 头文件 76 | 77 | 78 | 头文件 79 | 80 | 81 | 头文件 82 | 83 | 84 | 头文件 85 | 86 | 87 | 头文件 88 | 89 | 90 | 头文件 91 | 92 | 93 | 头文件 94 | 95 | 96 | 头文件 97 | 98 | 99 | 头文件 100 | 101 | 102 | 源文件 103 | 104 | 105 | -------------------------------------------------------------------------------- /windows/mmtls.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------