├── .gitattributes ├── DebugMsg.cpp ├── DebugMsg.h ├── DeviceConfigWWW.cpp ├── DeviceConfigWWW.h ├── GRE.cpp ├── GRE.h ├── LICENSE ├── MSCHAP.cpp ├── MSCHAP.h ├── PPTP_Client.cpp ├── PPTP_Client.h ├── README.md ├── SS ├── dashboard.png ├── nodeMCU.jpg ├── setting.png └── winbox.png ├── TcpProxyServer.cpp ├── TcpProxyServer.h ├── VpnTcpProxy.ino ├── VpnTcpProxy.ino.nodemcu.bin ├── data ├── avatar2.png ├── font-awesome.min.css ├── fontawesome-webfont.woff2 ├── index.html ├── raleway.css ├── raleway.woff2 ├── setting.html └── w3.css ├── des.c ├── des.h ├── md4.c ├── md4.h ├── sha1.c └── sha1.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /DebugMsg.cpp: -------------------------------------------------------------------------------- 1 | #include "DebugMsg.h" 2 | 3 | 4 | uint8_t debugLevel_PPTP = DB_INFO; 5 | 6 | void db_printf(uint8_t level, char *fmt, ...) { 7 | char buf[256]; 8 | if (level <= debugLevel_PPTP) { 9 | va_list args; 10 | va_start(args, fmt); 11 | vsnprintf(buf, 256, fmt, args); 12 | va_end(args); 13 | Serial.print(buf); 14 | } 15 | } 16 | 17 | uint8_t db_getLevel() { 18 | return debugLevel_PPTP; 19 | 20 | } 21 | 22 | void db_setLevel(uint8_t lv) { 23 | debugLevel_PPTP = lv; 24 | 25 | } 26 | 27 | #ifndef HEXDUMP_COLS 28 | #define HEXDUMP_COLS 16 29 | #endif 30 | 31 | void db_printHex_(void *mem, unsigned int len) { 32 | unsigned int i, j; 33 | 34 | for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) { 35 | /* print offset */ 36 | if(i % HEXDUMP_COLS == 0) { 37 | printf("0x%06x: ", i); 38 | fflush(stdout); 39 | } 40 | 41 | /* print hex data */ 42 | if(i < len) { 43 | printf("%02x ", 0xFF & ((char*)mem)[i]); 44 | fflush(stdout); 45 | 46 | } else { /* end of block, just aligning for ASCII dump */ 47 | printf(" "); 48 | fflush(stdout); 49 | 50 | } 51 | 52 | /* print ASCII dump */ 53 | if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) { 54 | for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) { 55 | if(j >= len) { /* end of block, not really printing */ 56 | putchar(' '); 57 | 58 | } else if(isprint(((char*)mem)[j])) { /* printable char */ 59 | putchar(0xFF & ((char*)mem)[j]); 60 | 61 | } else { /* other char */ 62 | putchar('.'); 63 | 64 | } 65 | fflush(stdout); 66 | 67 | } 68 | putchar('\n'); 69 | fflush(stdout); 70 | } 71 | } 72 | fflush(stdout); 73 | } 74 | 75 | void db_printHex(uint8_t level, void *mem, unsigned int len) { 76 | if (level <= debugLevel_PPTP) { 77 | db_printHex_((uint8_t*)mem, len); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /DebugMsg.h: -------------------------------------------------------------------------------- 1 | #ifndef DebugMsg_h 2 | #define DebugMsg_h 3 | 4 | #include "Arduino.h" 5 | #define DB_INFO 1 6 | #define DB_DEBUG 10 7 | 8 | void db_printf(uint8_t level, char *fmt, ...); 9 | uint8_t db_getLevel(); 10 | void db_setLevel(uint8_t lv); 11 | void db_printHex(uint8_t level, void *mem, unsigned int len); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /DeviceConfigWWW.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #include 5 | #include 6 | #include "FS.h" 7 | #include "DeviceConfigWWW.h" 8 | 9 | extern "C" 10 | { 11 | //#include 12 | //#include 13 | #include 14 | } 15 | 16 | #define U_PART U_FS 17 | 18 | int WebConfigPort = WEB_CONFIG_PORT; 19 | AsyncWebServer server(WebConfigPort); 20 | size_t content_len; 21 | 22 | 23 | const char* ssid = "WiFi2.4"; 24 | const char* password = "12345678"; 25 | const char* pptp_server = "vpn.example.com"; 26 | const int pptp_port = 1723; 27 | const char *pptp_user = "pptp-user"; 28 | const char *pptp_password = "pptp-password"; 29 | const char *dest_server = "192.168.1.1"; 30 | 31 | struct DeviceConfig *pCfg; 32 | struct DeviceStatus *pStatus; 33 | 34 | void Web_bindDeviceStatus(struct DeviceStatus *deviceStatus) { 35 | 36 | pStatus = deviceStatus; 37 | pStatus->internet_connect = false; 38 | pStatus->vpn_connect = false; 39 | pStatus->wifi_mode = WIFI_MODE_STA; 40 | (pStatus->wifi_ip).addr = 0; 41 | (pStatus->vpn_local_ip).addr = 0; 42 | (pStatus->vpn_remote_ip).addr = 0; 43 | strcpy(pStatus->fw_date, __DATE__); 44 | strcpy(pStatus->fw_time, __TIME__); 45 | pStatus->uptime_sec = 0; 46 | pStatus->restart_timeout_sec = 0; 47 | strcpy(pStatus->wifi_mac, WiFi.macAddress().c_str()); 48 | pStatus->free_heap = 0; 49 | 50 | } 51 | 52 | const char *devConfigFilename = "/deviceconfig.bin"; 53 | void Web_makeDefaultDeviceConfig(struct DeviceConfig *cfg) { 54 | 55 | memset(cfg, 0, sizeof(struct DeviceConfig)); 56 | strcpy(cfg->wifi_ssid, ssid); 57 | strcpy(cfg->wifi_password, password); 58 | strcpy(cfg->pptp_server, pptp_server); 59 | strcpy(cfg->pptp_user, pptp_user); 60 | strcpy(cfg->pptp_password, pptp_password); 61 | strcpy(cfg->tcp_destination, dest_server); 62 | 63 | File fp = SPIFFS.open(devConfigFilename, "w"); 64 | int writeSize; 65 | if (!fp) { 66 | Serial.println("makeDefaultDeviceConfig() file create failed"); 67 | //makeDefaultDeviceConfig(); 68 | return; 69 | } 70 | 71 | writeSize = fp.write((byte*) cfg, sizeof(struct DeviceConfig)); 72 | fp.close(); 73 | } 74 | 75 | bool Web_saveDeviceConfig(struct DeviceConfig *cfg) { 76 | File fp = SPIFFS.open(devConfigFilename, "w"); 77 | int writeSize; 78 | if (!fp) { 79 | Serial.println("Web_saveDeviceConfig() file open failed"); 80 | //makeDefaultDeviceConfig(); 81 | return false; 82 | } 83 | 84 | writeSize = fp.write((byte*) cfg, sizeof(struct DeviceConfig)); 85 | fp.close(); 86 | return true; 87 | } 88 | 89 | void Web_readDeviceConfig(struct DeviceConfig *cfg) { 90 | int readSize; 91 | pCfg = cfg; 92 | if(!SPIFFS.begin()){ 93 | Serial.println("readDeviceConfig() An Error has occurred while mounting SPIFFS"); 94 | return; 95 | } 96 | 97 | if (!SPIFFS.exists(devConfigFilename)) { 98 | Serial.println("readDeviceConfig() Device Config File Not Exist"); 99 | Web_makeDefaultDeviceConfig(cfg); 100 | return; 101 | } 102 | 103 | File f = SPIFFS.open(devConfigFilename, "r"); 104 | if (!f) { 105 | Serial.println("readDeviceConfig() file open failed"); 106 | return; 107 | } 108 | 109 | readSize = f.readBytes((char*) cfg, sizeof(struct DeviceConfig)); //cast changed from byte* 110 | 111 | f.close(); 112 | } 113 | 114 | void handle_upload_file(AsyncWebServerRequest *request) { 115 | char* html = "
"; 116 | String info_str = ""; 117 | FSInfo fs_info; 118 | SPIFFS.info(fs_info); 119 | /* 120 | struct FSInfo { 121 | size_t totalBytes; 122 | size_t usedBytes; 123 | size_t blockSize; 124 | size_t pageSize; 125 | size_t maxOpenFiles; 126 | size_t maxPathLength; 127 | }; 128 | */ 129 | info_str = "SPIFFS Total Bytes: " + String(fs_info.totalBytes) + "
\n"; 130 | info_str += "SPIFFS Used Bytes: " + String(fs_info.usedBytes) + "
\n"; 131 | info_str += "SPIFFS Block Size: " + String(fs_info.blockSize) + "
\n"; 132 | info_str += "SPIFFS Page Size: " + String(fs_info.pageSize) + "
\n"; 133 | info_str += "SPIFFS Max Open File: " + String(fs_info.maxOpenFiles) + "
\n"; 134 | info_str += "SPIFFS Max Path Length: " + String(fs_info.maxPathLength) + "
\n"; 135 | 136 | info_str = String(html) + "
\n" + info_str; 137 | request->send(200, "text/html", info_str); 138 | } 139 | 140 | File fsUploadFile; // a File object to temporarily store the received file 141 | void handle_do_upload_file(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { 142 | if (!index){ 143 | Serial.println("Upload file"); 144 | String filename2 = ""; 145 | if(!filename.startsWith("/")) 146 | filename2 = "/" + filename; 147 | Serial.print("handle_do_upload_file Name: "); Serial.println(filename2); 148 | fsUploadFile = SPIFFS.open(filename2, "w"); // Open the file for writing in SPIFFS (create if it doesn't exist) 149 | 150 | } 151 | 152 | if(fsUploadFile) 153 | fsUploadFile.write(data, len); // Write the received bytes to the file 154 | 155 | if (final) { 156 | AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait. . ."); 157 | response->addHeader("Refresh", "20"); 158 | response->addHeader("Location", "/upload_file"); 159 | request->send(response); 160 | 161 | if(fsUploadFile) { // If the file was successfully created 162 | fsUploadFile.close(); // Close the file again 163 | Serial.println("handleFileUpload: OK"); 164 | } else { 165 | Serial.println("handleFileUpload: Fail"); 166 | } 167 | } 168 | } 169 | 170 | 171 | void handleUpdate(AsyncWebServerRequest *request) { 172 | char* html = "
"; 173 | request->send(200, "text/html", html); 174 | } 175 | 176 | void handleDoUpdate(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { 177 | if (!index){ 178 | Serial.println("Update"); 179 | content_len = request->contentLength(); 180 | // if filename includes spiffs, update the spiffs partition 181 | int cmd = (filename.indexOf("spiffs") > -1) ? U_PART : U_FLASH; 182 | #ifdef ESP8266 183 | Update.runAsync(true); 184 | if (!Update.begin(content_len, cmd)) { 185 | #else 186 | if (!Update.begin(UPDATE_SIZE_UNKNOWN, cmd)) { 187 | #endif 188 | Update.printError(Serial); 189 | } 190 | } 191 | 192 | if (Update.write(data, len) != len) { 193 | Update.printError(Serial); 194 | #ifdef ESP8266 195 | } else { 196 | Serial.printf("Progress: %d%%\n", (Update.progress()*100)/Update.size()); 197 | #endif 198 | } 199 | 200 | if (final) { 201 | AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots"); 202 | response->addHeader("Refresh", "20"); 203 | response->addHeader("Location", "/"); 204 | request->send(response); 205 | if (!Update.end(true)){ 206 | Update.printError(Serial); 207 | } else { 208 | Serial.println("Update complete"); 209 | Serial.flush(); 210 | ESP.restart(); 211 | } 212 | } 213 | } 214 | 215 | void web_device_config_handle(AsyncWebServerRequest *request) { 216 | String result = ""; 217 | 218 | result += "{"; 219 | result += "\"wifi_ssid\":\"" + String(pCfg->wifi_ssid) + "\","; 220 | result += "\"wifi_password\":\"" + String(pCfg->wifi_password) + "\","; 221 | result += "\"pptp_server\":\"" + String(pCfg->pptp_server) + "\","; 222 | result += "\"pptp_user\":\"" + String(pCfg->pptp_user) + "\","; 223 | result += "\"pptp_password\":\"" + String(pCfg->pptp_password) + "\","; 224 | result += "\"tcp_destination\":\"" + String(pCfg->tcp_destination) + "\""; 225 | result +="}"; 226 | 227 | request->send(200, "text/html", result); 228 | } 229 | 230 | void web_device_status_handle(AsyncWebServerRequest *request) { 231 | String result = "", tmp = ""; 232 | struct in_addr address; 233 | 234 | result += "{"; 235 | 236 | result += "\"internet_connect\":\"" + String(pStatus->internet_connect) + "\","; 237 | result += "\"vpn_connect\":\"" + String(pStatus->vpn_connect) + "\","; 238 | result += "\"wifi_mode\":\"" + String(pStatus->wifi_mode==WIFI_MODE_AP?"AP":"Station") + "\","; 239 | 240 | address.s_addr = (pStatus->wifi_ip).addr; 241 | result += "\"wifi_ip\":\"" + String(inet_ntoa(address)) + "\","; 242 | 243 | address.s_addr = (pStatus->vpn_local_ip).addr; 244 | result += "\"vpn_local_ip\":\"" + String(inet_ntoa(address)) + "\","; 245 | 246 | address.s_addr = (pStatus->vpn_remote_ip).addr; 247 | result += "\"vpn_remote_ip\":\"" + String(inet_ntoa(address)) + "\","; 248 | 249 | result += "\"fw_date\":\"" + String(pStatus->fw_date) + "\","; 250 | result += "\"fw_time\":\"" + String(pStatus->fw_time) + "\","; 251 | result += "\"uptime_sec\":\"" + String(pStatus->uptime_sec) + "\","; 252 | result += "\"restart_timeout_sec\":\"" + String(pStatus->restart_timeout_sec) + "\","; 253 | result += "\"wifi_mac\":\"" + String(pStatus->wifi_mac) + "\","; 254 | result += "\"free_heap\":\"" + String(pStatus->free_heap) + "\""; 255 | result +="}"; 256 | 257 | request->send(200, "text/html", result); 258 | } 259 | 260 | const char* http_username = "admin"; 261 | const char* http_password = "admin"; 262 | void Web_init() { 263 | //server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {request->redirect("/update");}); 264 | server.on("/upload_file", HTTP_GET, [](AsyncWebServerRequest *request){handle_upload_file(request);}); 265 | server.on("/do_upload_file", HTTP_POST, 266 | [](AsyncWebServerRequest *request) {request->redirect("/upload_file");}, 267 | [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, 268 | size_t len, bool final) {handle_do_upload_file(request, filename, index, data, len, final);} 269 | ); 270 | server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){handleUpdate(request);}); 271 | server.on("/doUpdate", HTTP_POST, 272 | [](AsyncWebServerRequest *request) {}, 273 | [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, 274 | size_t len, bool final) {handleDoUpdate(request, filename, index, data, len, final);} 275 | ); 276 | server.on("/device_config", HTTP_GET, [](AsyncWebServerRequest *request){web_device_config_handle(request);}); 277 | server.on("/device_status", HTTP_GET, [](AsyncWebServerRequest *request){web_device_status_handle(request);}); 278 | 279 | server.addHandler(new SPIFFSEditor(http_username,http_password)); 280 | server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ 281 | request->send(200, "text/plain", String(ESP.getFreeHeap())); 282 | }); 283 | server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html"); 284 | 285 | server.onNotFound([](AsyncWebServerRequest *request){request->send(404);}); 286 | 287 | server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){ 288 | if(!index) 289 | Serial.printf("UploadStart: %s\n", filename.c_str()); 290 | Serial.printf("%s", (const char*)data); 291 | if(final) 292 | Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len); 293 | }); 294 | server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ 295 | if(!index) 296 | Serial.printf("BodyStart: %u\n", total); 297 | Serial.printf("%s", (const char*)data); 298 | if(index + len == total) 299 | Serial.printf("BodyEnd: %u\n", total); 300 | }); 301 | 302 | // Send a POST request to /post with a form field message set to 303 | server.on("/setting-req", HTTP_POST, [](AsyncWebServerRequest *request){ 304 | String message; 305 | if (request->hasParam("wifi_ssid", true)) { 306 | message = request->getParam("wifi_ssid", true)->value(); 307 | strcpy(pCfg->wifi_ssid, message.c_str()); 308 | 309 | } 310 | 311 | if (request->hasParam("wifi_password", true)) { 312 | message = request->getParam("wifi_password", true)->value(); 313 | strcpy(pCfg->wifi_password, message.c_str()); 314 | 315 | } 316 | 317 | if (request->hasParam("pptp_server", true)) { 318 | message = request->getParam("pptp_server", true)->value(); 319 | strcpy(pCfg->pptp_server, message.c_str()); 320 | 321 | } 322 | 323 | if (request->hasParam("pptp_user", true)) { 324 | message = request->getParam("pptp_user", true)->value(); 325 | strcpy(pCfg->pptp_user, message.c_str()); 326 | 327 | } 328 | 329 | if (request->hasParam("pptp_password", true)) { 330 | message = request->getParam("pptp_password", true)->value(); 331 | strcpy(pCfg->pptp_password, message.c_str()); 332 | 333 | } 334 | 335 | if (request->hasParam("tcp_destination", true)) { 336 | message = request->getParam("tcp_destination", true)->value(); 337 | strcpy(pCfg->tcp_destination, message.c_str()); 338 | 339 | } 340 | 341 | AsyncWebServerResponse *response = request->beginResponse(302); //Sends 302 move temporaya 342 | response->addHeader("Location", "setting.html"); 343 | request->send(response); 344 | }); 345 | 346 | server.begin(); 347 | } 348 | -------------------------------------------------------------------------------- /DeviceConfigWWW.h: -------------------------------------------------------------------------------- 1 | #ifndef DEVICE_CONFIG_WWW_h 2 | #define DEVICE_CONFIG_WWW_h 3 | 4 | struct DeviceConfig { 5 | char wifi_ssid[50]; 6 | char wifi_password[50]; 7 | char pptp_server[100]; 8 | char pptp_user[50]; 9 | char pptp_password[50]; 10 | char tcp_destination[50]; 11 | }; 12 | 13 | #define WIFI_MODE_AP 0 14 | #define WIFI_MODE_STA 1 15 | struct DeviceStatus { 16 | bool internet_connect; 17 | bool vpn_connect; 18 | uint8_t wifi_mode; 19 | uint8_t free1; 20 | ip_addr_t wifi_ip; 21 | ip_addr_t vpn_local_ip; 22 | ip_addr_t vpn_remote_ip; 23 | char fw_date[50]; 24 | char fw_time[50]; 25 | uint32_t uptime_sec; 26 | uint32_t restart_timeout_sec; 27 | char wifi_mac[18]; 28 | uint32_t free_heap; 29 | }; 30 | 31 | #define WEB_CONFIG_PORT 8555 32 | 33 | void Web_bindDeviceStatus(struct DeviceStatus *deviceStatus); 34 | void Web_makeDefaultDeviceConfig(struct DeviceConfig *cfg); 35 | bool Web_saveDeviceConfig(struct DeviceConfig *cfg); 36 | void Web_readDeviceConfig(struct DeviceConfig *cfg); 37 | void Web_init(); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /GRE.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "GRE.h" 3 | #include 4 | 5 | #include "DebugMsg.h" 6 | 7 | struct GreSeqPacket *_greHeader; 8 | char _servername[100]; 9 | ip_addr_t _serverIP; 10 | int _serverport; 11 | 12 | 13 | uint16_t _peerCallId; 14 | uint8_t _readBuffer[1500]; 15 | int _readBufferLength; 16 | bool _readAvailable; 17 | 18 | uint32_t _ackNumber; 19 | uint32_t _lastAckNumber; 20 | 21 | struct raw_pcb *_greControlBlock; 22 | 23 | uint16_t GRE_protocolType; 24 | uint8_t GRE_payloadLength; 25 | uint16_t GRE_callId; 26 | uint32_t GRE_sequenceNumber; 27 | int (*GRE_recvCallback)(uint8_t *, int); 28 | 29 | bool _greSetup(); 30 | bool _pppLcpConfig(); 31 | int _getPppLcpConfigOptions(uint8_t *buf, int bufsize); 32 | // LWIP callback run when a gre response is received (static wrapper) 33 | static uint8_t _greReceivedStatic(void *gre, raw_pcb *pcb, pbuf *packetBuffer, const ip_addr_t * addr); 34 | // LWIP callback run when a gre response is received 35 | uint8_t GreReceived(pbuf * packetBuffer, const ip_addr_t * addr); 36 | 37 | 38 | void GRE_setProtocolType(uint16_t pro) { 39 | GRE_protocolType = pro; 40 | } 41 | 42 | //void GRE_set 43 | 44 | void _initVariable() { 45 | GRE_sequenceNumber = 0; //0x12345678; 46 | _ackNumber = 0xffffffff; 47 | _lastAckNumber = 0xffffffff; 48 | _readAvailable = false; 49 | GRE_recvCallback = nullptr; 50 | } 51 | 52 | int GRE_write(uint8_t *data, int length) { 53 | int packetSize = 0; 54 | int greSize; 55 | bool actField = false; 56 | uint16_t flag = 0; 57 | uint32_t *pSeqNumber; 58 | uint32_t *pAckNumber; 59 | uint8_t *pPayload; 60 | 61 | db_printf(DB_DEBUG, "GRE_write() Begin\n"); 62 | 63 | flag = 0x3001; 64 | if (_ackNumber != _lastAckNumber) { 65 | actField = true; 66 | flag = 0x3081; 67 | } 68 | greSize = sizeof(struct GrePacket); 69 | greSize += 4; //Write Packet need Sequence number field 70 | if (actField) greSize += 4; // Need for act number field 71 | 72 | packetSize = greSize + length; 73 | db_printf(DB_DEBUG, "GRE_write() Length = %d\n", packetSize); 74 | 75 | // Allocate packet buffer structure. Buffer memory is allocated as one 76 | // large chunk. This includes protocol headers as well. 77 | struct pbuf * packetBuffer = pbuf_alloc( PBUF_IP, packetSize, PBUF_RAM); 78 | if(packetBuffer == nullptr) { 79 | db_printf(DB_DEBUG, "GRE_write() pbuf alloc fail\n"); 80 | return -1; 81 | } 82 | 83 | // Check if packet buffer correctly created 84 | if((packetBuffer->len != packetBuffer->tot_len) || 85 | (packetBuffer->next != nullptr)) { 86 | 87 | // Free packet buffer memory and exit 88 | db_printf(DB_DEBUG, "GRE_write() pbuf alloc not valid\n"); 89 | pbuf_free(packetBuffer); 90 | return -2; 91 | } 92 | 93 | struct GrePacket *gre = (struct GrePacket *)packetBuffer->payload; 94 | gre->flagsAndVersion = htons(flag); 95 | gre->protocolType = htons(GRE_protocolType); 96 | gre->payloadLength = htons(length); 97 | gre->callId = htons(GRE_callId); 98 | 99 | pSeqNumber = (uint32_t *)packetBuffer->payload; 100 | pSeqNumber += sizeof(struct GrePacket)/4; 101 | *pSeqNumber = htonl(GRE_sequenceNumber++); 102 | pAckNumber = pSeqNumber; 103 | if (actField == true) { 104 | pAckNumber += 1; 105 | *pAckNumber = htonl(_ackNumber); 106 | _lastAckNumber = _ackNumber; 107 | } 108 | 109 | pPayload = (uint8_t*)(pAckNumber + 1); 110 | memcpy(pPayload, data, length); 111 | 112 | // Finally, send the packet and register timestamp 113 | raw_sendto(_greControlBlock, packetBuffer, &_serverIP); 114 | 115 | // Free packet buffer memory 116 | pbuf_free(packetBuffer); 117 | 118 | db_printf(DB_DEBUG, "GRE_write() Success\n"); 119 | return length; 120 | } 121 | 122 | int GRE_writeAck() { 123 | int packetSize = 0; 124 | int greSize; 125 | bool actField = false; 126 | uint16_t flag = 0; 127 | uint32_t *pSeqNumber; 128 | uint32_t *pAckNumber; 129 | uint8_t *pPayload; 130 | struct GreAckPacket *gre; 131 | 132 | db_printf(DB_DEBUG, "GRE_writeAck() Begin\n"); 133 | 134 | flag = 0x2081; 135 | if (_ackNumber == _lastAckNumber) { 136 | return 0; 137 | } 138 | 139 | // Allocate packet buffer structure. Buffer memory is allocated as one 140 | // large chunk. This includes protocol headers as well. 141 | struct pbuf * packetBuffer = pbuf_alloc( PBUF_IP, 12, PBUF_RAM); 142 | if(packetBuffer == nullptr) { 143 | db_printf(DB_DEBUG, "GRE_writeAck() pbuf alloc fail\n"); 144 | return -1; 145 | } 146 | 147 | // Check if packet buffer correctly created 148 | if((packetBuffer->len != packetBuffer->tot_len) || 149 | (packetBuffer->next != nullptr)) { 150 | 151 | // Free packet buffer memory and exit 152 | db_printf(DB_DEBUG, "GRE_writeAck() pbuf alloc not valid\n"); 153 | pbuf_free(packetBuffer); 154 | return -2; 155 | } 156 | 157 | gre = (struct GreAckPacket *)packetBuffer->payload; 158 | gre->flagsAndVersion = htons(flag); 159 | gre->protocolType = htons(GRE_protocolType); 160 | gre->payloadLength = htons(0); 161 | gre->callId = htons(GRE_callId); 162 | gre->acknowledgmentNumber = htonl(_ackNumber); 163 | 164 | _lastAckNumber = _ackNumber; 165 | 166 | // Finally, send the packet and register timestamp 167 | raw_sendto(_greControlBlock, packetBuffer, &_serverIP); 168 | 169 | // Free packet buffer memory 170 | pbuf_free(packetBuffer); 171 | 172 | db_printf(DB_DEBUG, "GRE_writeAck() Success\n"); 173 | return 12; 174 | } 175 | 176 | int GRE_read(uint8_t *data, int length) { 177 | if (_readAvailable == false) { 178 | return 0; 179 | } 180 | 181 | memcpy(data, _readBuffer, _readBufferLength); 182 | _readAvailable = false; 183 | int ret = _readBufferLength; 184 | _readBufferLength = 0; 185 | return ret; 186 | } 187 | 188 | bool GRE_init(const char *servername, int port) { 189 | 190 | db_printf(DB_DEBUG, "GRE_init() Begin\n"); 191 | _initVariable(); 192 | 193 | strcpy(_servername, servername); 194 | _serverport = port; 195 | 196 | IPAddress ip; 197 | if(WiFi.hostByName(_servername, ip) == false) { 198 | // Unable to resolve hostname 199 | db_printf(DB_DEBUG, "GRE_init() Resolve Server name fail\n"); 200 | return false; 201 | } 202 | _serverIP = ip; 203 | 204 | if (_greSetup() != true) { 205 | db_printf(DB_DEBUG, "GRE_init() _greSetup Failed\n"); 206 | return false; 207 | } 208 | 209 | db_printf(DB_DEBUG, "GRE_init() Success\n"); 210 | return true; 211 | } 212 | 213 | 214 | 215 | bool _greSetup() { 216 | 217 | db_printf(DB_DEBUG, "_greSetup() Begin\n"); 218 | 219 | // Create new GRE detection data 220 | _greControlBlock = raw_new(IP_PROTO_GRE); 221 | if (_greControlBlock == nullptr) { 222 | db_printf(DB_DEBUG, "_greSetup() raw_new() fail\n"); 223 | return false; 224 | } 225 | 226 | // When LWIP detects a packet corresponding to specified protocol control 227 | // block, the GreReceivedStatic callback is executed 228 | raw_recv( _greControlBlock, _greReceivedStatic, NULL); 229 | 230 | // Selects the local interfaces where detection will be made. 231 | // In this case, all local interfaces 232 | raw_bind(_greControlBlock, IP_ADDR_ANY); 233 | 234 | db_printf(DB_DEBUG, "_greSetup() Success\n"); 235 | return true; 236 | } 237 | 238 | ////////////////////////////////////////////////////////////////////////////// 239 | // LWIP callback run when a gre response is received (static wrapper) 240 | static uint8_t _greReceivedStatic(void *gre, raw_pcb *pcb, pbuf *packetBuffer, const ip_addr_t * addr) { 241 | db_printf(DB_DEBUG, "_greReceivedStatic() Begin\n"); 242 | 243 | // Check parameters 244 | if( 245 | //gre == nullptr || 246 | pcb == nullptr || 247 | packetBuffer == nullptr || 248 | addr == nullptr) 249 | { 250 | // 0 is returned to raw_recv. In this way the packet will be matched 251 | // against further PCBs and/or forwarded to other protocol layers. 252 | db_printf(DB_DEBUG, "_greReceivedStatic() nullptr\n"); 253 | return 0; 254 | } 255 | 256 | db_printf(DB_DEBUG, "_greReceivedStatic() End\n"); 257 | return GreReceived(packetBuffer, addr); 258 | } 259 | 260 | ////////////////////////////////////////////////////////////////////////////// 261 | // LWIP callback run when a gre response is received 262 | uint8_t GreReceived(pbuf * packetBuffer, const ip_addr_t * addr) { 263 | db_printf(DB_DEBUG, "GreReceived() Begin\n"); 264 | 265 | // Check parameters 266 | if(packetBuffer == nullptr || addr == nullptr) 267 | { 268 | // Not free the packet, and return zero. The packet will be matched against 269 | // further PCBs and/or forwarded to other protocol layers. 270 | db_printf(DB_DEBUG, "GreReceived() error 1\n"); 271 | return 0; 272 | } 273 | 274 | // Save IPv4 header structure to read ttl value 275 | struct ip_hdr * ip = (struct ip_hdr *)packetBuffer->payload; 276 | if(ip == nullptr) 277 | { 278 | // Not free the packet, and return zero. The packet will be matched against 279 | // further PCBs and/or forwarded to other protocol layers. 280 | db_printf(DB_DEBUG, "GreReceived() error 2\n"); 281 | return 0; 282 | } 283 | 284 | // Move the ->payload pointer skipping the IPv4 header of the packet with 285 | // pbuf_header function. If such function fails, it returns nonzero 286 | if (pbuf_header(packetBuffer, -PBUF_IP_HLEN) != 0) 287 | { 288 | // Not free the packet, and return zero. The packet will be matched against 289 | // further PCBs and/or forwarded to other protocol layers. 290 | db_printf(DB_DEBUG, "GreReceived() error 3\n"); 291 | return 0; 292 | } 293 | 294 | struct GrePacket *greHeader = (struct GrePacket *)packetBuffer->payload; 295 | if(greHeader == nullptr) 296 | { 297 | // Restore original position of ->payload pointer 298 | pbuf_header(packetBuffer, PBUF_IP_HLEN); 299 | 300 | // Not free the packet, and return zero. The packet will be matched against 301 | // further PCBs and/or forwarded to other protocol layers. 302 | db_printf(DB_DEBUG, "GreReceived() error 4\n"); 303 | return 0; 304 | } 305 | 306 | db_printf(DB_DEBUG, "GreReceived() Process Header\n"); 307 | int greHeaderSize = sizeof(GrePacket); 308 | bool seqField = false; 309 | bool ackField = false; 310 | uint16_t flag = ntohs(greHeader->flagsAndVersion); 311 | int payloadSize = 0; 312 | 313 | payloadSize = ntohs(greHeader->payloadLength); 314 | 315 | pbuf_header(packetBuffer, -greHeaderSize); 316 | 317 | // flag seq number present 318 | if ( flag & 0x1000) { 319 | char * pack = (char *)packetBuffer->payload; 320 | _ackNumber = (pack[0] << 24) | (pack[1] << 16) | (pack[2] << 8) | pack[3]; 321 | pbuf_header(packetBuffer, -4); 322 | } 323 | 324 | // flag ack number present 325 | if ( flag & 0x0080) { 326 | pbuf_header(packetBuffer, -4); 327 | } 328 | 329 | _readAvailable = false; 330 | memcpy(_readBuffer, (uint8_t*)packetBuffer->payload, payloadSize); 331 | _readBufferLength = payloadSize; 332 | _readAvailable = true; 333 | 334 | uint8_t *cbData = nullptr; 335 | if (GRE_recvCallback != nullptr) { 336 | 337 | int cbRet = (*GRE_recvCallback)((uint8_t*)packetBuffer->payload, payloadSize); 338 | if (cbRet == 1) { 339 | GRE_write((uint8_t*)packetBuffer->payload, payloadSize); 340 | } 341 | // Just send GRE Ack 342 | if (cbRet == 2) { 343 | GRE_writeAck(); 344 | } 345 | 346 | } else { 347 | db_printf(DB_DEBUG, "GreReceived() GRE_recvCallback is nullptr\n"); 348 | } 349 | 350 | // Eat the packet by calling pbuf_free() and returning non-zero. 351 | // The packet will not be passed to other raw PCBs or other protocol layers. 352 | pbuf_free(packetBuffer); 353 | db_printf(DB_DEBUG, "GreReceived() End\n"); 354 | return 1; 355 | 356 | } 357 | 358 | int GRE_available() { 359 | if (_readAvailable == false) 360 | return 0; 361 | 362 | return _readBufferLength; 363 | } 364 | -------------------------------------------------------------------------------- /GRE.h: -------------------------------------------------------------------------------- 1 | #ifndef GRE_h 2 | #define GRE_h 3 | 4 | extern "C" 5 | { 6 | #include 7 | #include 8 | } 9 | 10 | #define IP_PROTO_GRE 47 11 | struct GrePacket { 12 | uint16_t flagsAndVersion; 13 | uint16_t protocolType; 14 | uint16_t payloadLength; 15 | uint16_t callId; 16 | }; 17 | 18 | struct GreAckPacket { 19 | uint16_t flagsAndVersion; 20 | uint16_t protocolType; 21 | uint16_t payloadLength; 22 | uint16_t callId; 23 | uint32_t acknowledgmentNumber; 24 | }; 25 | 26 | bool GRE_init(const char *servername, int port); 27 | int GRE_write(uint8_t *data, int length); 28 | int GRE_writeAck(); 29 | int GRE_read(uint8_t *data, int length); 30 | int GRE_available(); 31 | void GRE_setProtocolType(uint16_t pro); 32 | 33 | extern uint16_t GRE_protocolType; 34 | extern uint8_t GRE_payloadLength; 35 | extern uint16_t GRE_callId; 36 | extern uint32_t GRE_sequenceNumber; 37 | extern int (*GRE_recvCallback)(uint8_t *, int); 38 | 39 | 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 sun89 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MSCHAP.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "MSCHAP.h" 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "sha1.h" 11 | #include "md4.h" 12 | #include "des.h" 13 | #include "DebugMsg.h" 14 | 15 | int ChallengeHash( uint8_t PeerChallenge[16], uint8_t AuthenticatorChallenge[16], char *UserName, uint8_t Challenge[8]); 16 | int NtPasswordHash(char *PasswordASCII,uint8_t PasswordHash[16] ); 17 | int ChallengeResponse(uint8_t Challenge[8], uint8_t PasswordHash[16], uint8_t Response[24] ); 18 | int DesEncrypt(uint8_t Clear[8], uint8_t Key[7], uint8_t Cypher[8] ); 19 | 20 | int GenerateNTResponse(uint8_t AuthenticatorChallenge[16], uint8_t PeerChallenge[16], char *UserName, char *Password, uint8_t Response[24], uint8_t mschapVersion) { 21 | uint8_t Challenge[8]; 22 | uint8_t PasswordHash[16]; 23 | char *p = Password; 24 | 25 | if ( mschapVersion == 2) { 26 | ChallengeHash( PeerChallenge, AuthenticatorChallenge, UserName, Challenge); 27 | } else if (mschapVersion == 1) { 28 | memcpy(Challenge, AuthenticatorChallenge, 8); 29 | } else { 30 | db_printf(DB_DEBUG, "GenerateNTResponse() Unknown CHAP Version %d\n", mschapVersion); 31 | return 0; 32 | } 33 | 34 | NtPasswordHash( p, PasswordHash); 35 | ChallengeResponse( Challenge, PasswordHash, Response ); 36 | 37 | db_printf(DB_DEBUG, "GenerateNTResponse():: . . ."); 38 | db_printHex(DB_DEBUG, Response, 24); 39 | return 1; 40 | } 41 | 42 | int ChallengeHash( uint8_t PeerChallenge[16], uint8_t AuthenticatorChallenge[16], char *UserName, uint8_t Challenge[8]) { 43 | 44 | uint8_t Digest[20]; 45 | SHA1_CTX ctx; 46 | 47 | /* 48 | * SHAInit(), SHAUpdate() and SHAFinal() functions are an 49 | * implementation of Secure Hash Algorithm (SHA-1) [11]. These are 50 | * available in public domain or can be licensed from 51 | * RSA Data Security, Inc. 52 | */ 53 | 54 | sha1_init(&ctx); 55 | sha1_update(&ctx, PeerChallenge, 16); 56 | sha1_update(&ctx, AuthenticatorChallenge, 16); 57 | 58 | /* 59 | * Only the user name (as presented by the peer and 60 | * excluding any prepended domain name) 61 | * is used as input to SHAUpdate(). 62 | */ 63 | 64 | sha1_update(&ctx, (const BYTE*)UserName, strlen(UserName)); 65 | sha1_final(&ctx, Digest); 66 | memcpy(Challenge, Digest, 8); 67 | 68 | db_printf(DB_DEBUG, "SHA1 Digest: "); 69 | db_printHex(DB_DEBUG, Digest, 20); 70 | db_printf(DB_DEBUG, "SHA1 Challenge: "); 71 | db_printHex(DB_DEBUG, Challenge, 8); 72 | return 1; 73 | 74 | } 75 | 76 | 77 | 78 | int NtPasswordHash(char *PasswordASCII, uint8_t PasswordHash[16] ) { 79 | char passUnicode[256 * 2]; 80 | int i; 81 | 82 | 83 | db_printf(DB_DEBUG, "Password ASCII\n"); 84 | db_printHex(DB_DEBUG, PasswordASCII, strlen(PasswordASCII)); 85 | 86 | memset(passUnicode, 0, 256*2); 87 | // Convert Ascii to Unicode 88 | for (i = 0; i < strlen(PasswordASCII); i++) { 89 | passUnicode[i * 2] = PasswordASCII[i]; 90 | } 91 | 92 | 93 | db_printf(DB_DEBUG, "Password Unicode\n"); 94 | db_printHex(DB_DEBUG, passUnicode, 256*2); 95 | /* 96 | * Use the MD4 algorithm [5] to irreversibly hash Password 97 | * into PasswordHash. Only the password is hashed without 98 | * including any terminating 0. 99 | */ 100 | 101 | auth_md4Sum(PasswordHash, (const unsigned char *)passUnicode, strlen(PasswordASCII) * 2 ); 102 | 103 | db_printf(DB_DEBUG, "NtPasswordHash: "); 104 | db_printHex(DB_DEBUG, PasswordHash, 16); 105 | return 1; 106 | } 107 | 108 | 109 | 110 | int HashNtPasswordHash( uint8_t PasswordHash[16], uint8_t PasswordHashHash[16] ) { 111 | 112 | /* 113 | * Use the MD4 algorithm [5] to irreversibly hash 114 | * PasswordHash into PasswordHashHash. 115 | */ 116 | 117 | auth_md4Sum(PasswordHashHash, (const unsigned char *)PasswordHash, 16 ); 118 | 119 | db_printf(DB_DEBUG, "HashNtPasswordHash: "); 120 | db_printHex(DB_DEBUG, PasswordHashHash, 16); 121 | return 1; 122 | 123 | } 124 | 125 | 126 | int getBit(uint8_t data, int bitNumber) { 127 | int bit = data & (1 << (7 - bitNumber) ); 128 | if (bit != 0) 129 | return 1; 130 | return 0; 131 | } 132 | 133 | void setBit(uint8_t *data, int bitNumber) { 134 | 135 | data[0] |= 1 << (7 - bitNumber); 136 | } 137 | 138 | int DesKey56BitTo64Bit(uint8_t key64[8], uint8_t key56[7]) { 139 | 140 | uint8_t bitValue; 141 | uint8_t *pout = key64; 142 | uint8_t *pin = key56; 143 | 144 | int bit56Number = 0; 145 | 146 | int bit64Number = 0; 147 | int loopCount = 56; 148 | int bit1Count = 0; 149 | 150 | memset(key64, 0, 8); 151 | 152 | while (loopCount > 0) { 153 | loopCount--; 154 | 155 | bitValue = getBit(*pin, bit56Number); 156 | 157 | if (bitValue) { 158 | setBit(pout, bit64Number); 159 | bit1Count++; 160 | } 161 | 162 | bit56Number++; 163 | if (bit56Number > 7) { 164 | bit56Number = 0; 165 | pin++; 166 | } 167 | 168 | bit64Number++; 169 | if (bit64Number > 6) { 170 | 171 | // Odd Parity set 172 | if (bit1Count % 2 == 0) setBit(pout, 7); 173 | 174 | bit1Count = 0; 175 | bit64Number = 0; 176 | pout++; 177 | } 178 | 179 | 180 | } 181 | } 182 | 183 | 184 | int ChallengeResponse(uint8_t Challenge[8], uint8_t PasswordHash[16], uint8_t Response[24] ) { 185 | //Set ZPasswordHash to PasswordHash zero-padded to 21 octets 186 | uint8_t ZPasswordHash[21]; 187 | uint8_t key[3][8]; 188 | 189 | memset(ZPasswordHash, 0, 21); 190 | memcpy(ZPasswordHash, PasswordHash, 16); 191 | //uint8_t dumm[8] = { 0x89 ,0xD6 ,0x80 ,0x01 ,0x01 ,0x01 ,0x01 ,0x01 }; 192 | 193 | DesKey56BitTo64Bit(key[0], &ZPasswordHash[0]); 194 | DesKey56BitTo64Bit(key[1], &ZPasswordHash[7]); 195 | DesKey56BitTo64Bit(key[2], &ZPasswordHash[14]); 196 | 197 | DesEncrypt( Challenge, key[0], &Response[0]); 198 | DesEncrypt( Challenge, key[1], &Response[8]); 199 | DesEncrypt( Challenge, key[2], &Response[16]); 200 | 201 | return 1; 202 | 203 | } 204 | 205 | int DesEncrypt(uint8_t Clear[8], uint8_t Key[7], uint8_t Cypher[8] ) { 206 | 207 | /* 208 | * Use the DES encryption algorithm [4] in ECB mode [10] 209 | * to encrypt Clear into Cypher such that Cypher can 210 | * only be decrypted back to Clear by providing Key. 211 | * Note that the DES algorithm takes as input a 64-bit 212 | * stream where the 8th, 16th, 24th, etc. bits are 213 | * parity bits ignored by the encrypting algorithm. 214 | * Unless you write your own DES to accept 56-bit input 215 | * without parity, you will need to insert the parity bits 216 | * yourself. 217 | */ 218 | 219 | BYTE schedule[16][6]; 220 | des_key_setup(Key, schedule, DES_ENCRYPT); 221 | des_crypt(Clear, Cypher, schedule); 222 | 223 | db_printf(DB_DEBUG, "DesEncrypt:: Start\n"); 224 | db_printf(DB_DEBUG, "Clear Data. . ."); 225 | db_printHex(DB_DEBUG, Clear, 8); 226 | db_printf(DB_DEBUG, "Key . . ."); 227 | db_printHex(DB_DEBUG, Key, 7); 228 | db_printf(DB_DEBUG, "Cypher Data. . ."); 229 | db_printHex(DB_DEBUG, Cypher, 8); 230 | return 1; 231 | } 232 | 233 | /////////////////////////// MSCHAP V1 /////////////////////////////////////////////// 234 | int LmPasswordHash(char *Password,uint8_t PasswordHash[16] ); 235 | int DesHash( uint8_t Clear[7], uint8_t Cypher[8] ); 236 | 237 | int LmChallengeResponse( uint8_t Challenge[8], char *Password, uint8_t Response[24] ) { 238 | uint8_t PasswordHash[16]; 239 | int i; 240 | 241 | // MSCHAP1 MAX Password is 14 242 | if (strlen(Password) > 14) { 243 | return 0; 244 | } 245 | 246 | 247 | 248 | LmPasswordHash( Password, PasswordHash); 249 | ChallengeResponse( Challenge, PasswordHash, Response ); 250 | 251 | return 1; 252 | } 253 | 254 | int LmPasswordHash(char *Password,uint8_t PasswordHash[16] ) { 255 | char upperPass[15]; 256 | int i; 257 | 258 | memset(upperPass, 0, 15); 259 | 260 | i = 0; 261 | while(Password[i] != '\0') { 262 | upperPass[i] = toupper(Password[i]); 263 | i++; 264 | } 265 | 266 | DesHash( (uint8_t*)&upperPass[0], &PasswordHash[0] ); 267 | DesHash( (uint8_t*)&upperPass[7], &PasswordHash[8] ); 268 | return 1; 269 | 270 | } 271 | 272 | int DesHash( uint8_t Clear[7], uint8_t Cypher[8] ) { 273 | /* 274 | * Make Cypher an irreversibly encrypted form of Clear by 275 | * encrypting known text using Clear as the secret key. 276 | * The known text consists of the string 277 | * 278 | * KGS!@#$% 279 | */ 280 | 281 | char StdText[] = "KGS!@#$%"; 282 | DesEncrypt( (uint8_t*)StdText, Clear, Cypher ); 283 | return 1; 284 | 285 | } 286 | 287 | ////////////////////////////////////////////////////////////////////////////////// 288 | 289 | int GenerateAuthenticatorResponse(char *PasswordASCII, uint8_t NtResponse[24], uint8_t PeerChallenge[16], uint8_t AuthenticatorChallenge[16], char *UserName, uint8_t *AuthenticatorResponse ) { 290 | 291 | uint8_t PasswordHash[16]; 292 | uint8_t PasswordHashHash[16]; 293 | uint8_t Challenge[8]; 294 | uint8_t Digest[20]; 295 | SHA1_CTX ctx; 296 | 297 | /* 298 | * "Magic" constants used in response generation 299 | */ 300 | 301 | uint8_t Magic1[39] = 302 | {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 303 | 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 304 | 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 305 | 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74}; 306 | 307 | 308 | uint8_t Magic2[41] = 309 | {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 310 | 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 311 | 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 312 | 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 313 | 0x6E}; 314 | 315 | int i; 316 | 317 | 318 | /* 319 | * Hash the password with MD4 320 | */ 321 | 322 | NtPasswordHash( PasswordASCII, PasswordHash ); 323 | 324 | /* 325 | * Now hash the hash 326 | */ 327 | 328 | HashNtPasswordHash( PasswordHash, PasswordHashHash); 329 | 330 | sha1_init(&ctx); 331 | sha1_update(&ctx, PasswordHashHash, 16); 332 | sha1_update(&ctx, NtResponse, 24); 333 | sha1_update(&ctx, Magic1, 39); 334 | sha1_final(&ctx, Digest); 335 | 336 | ChallengeHash( PeerChallenge, AuthenticatorChallenge, UserName, Challenge); 337 | 338 | sha1_init(&ctx); 339 | sha1_update(&ctx, Digest, 20); 340 | sha1_update(&ctx, Challenge, 8); 341 | sha1_update(&ctx, Magic2, 41); 342 | sha1_final(&ctx, Digest); 343 | 344 | /* 345 | * Encode the value of 'Digest' as "S=" followed by 346 | * 40 ASCII hexadecimal digits and return it in 347 | * AuthenticatorResponse. 348 | * For example, 349 | * "S=0123456789ABCDEF0123456789ABCDEF01234567" 350 | */ 351 | 352 | AuthenticatorResponse[0] = 'S'; 353 | AuthenticatorResponse[1] = '='; 354 | AuthenticatorResponse += 2; 355 | for (i = 0; i < 20; i++) { 356 | sprintf((char*)AuthenticatorResponse, "%02X", Digest[i]); 357 | AuthenticatorResponse += 2; 358 | } 359 | 360 | db_printf(DB_DEBUG, "AuthenticatorResponse: %s\n", AuthenticatorResponse); 361 | return 1; 362 | 363 | } 364 | 365 | 366 | bool CheckAuthenticatorResponse(char *Password, uint8_t NtResponse[24], uint8_t PeerChallenge[16], uint8_t AuthenticatorChallenge[16], char *UserName, uint8_t ReceivedResponse[42] ) { 367 | 368 | bool ResponseOK = false; 369 | uint8_t MyResponse[43]; 370 | 371 | GenerateAuthenticatorResponse( Password, NtResponse, PeerChallenge, AuthenticatorChallenge, UserName, MyResponse); 372 | 373 | db_printf(DB_DEBUG, "Received Response= %s\n", ReceivedResponse); 374 | db_printf(DB_DEBUG, "Calculate Response= %s\n", MyResponse); 375 | 376 | if (memcmp(MyResponse, ReceivedResponse, 42) == 0) 377 | ResponseOK = true; 378 | 379 | return ResponseOK; 380 | } 381 | 382 | /* run this program using the console pauser or add your own getch, system("pause") or input loop */ 383 | 384 | void MSCHAP_Test() { 385 | 386 | //uint8_t serverChallenge[16] = { 0x2f, 0x05, 0x3e, 0xae, 0xf1, 0x2f, 0xe6, 0x97, 0xf9, 0x2f, 0x29, 0x80, 0x07, 0x2c, 0x40, 0x11}; 387 | uint8_t serverChallenge[16] = { 0xe3 ,0xf6 ,0x74 ,0xb3 ,0xfc ,0x71 ,0x18 ,0xfe ,0x1c ,0x60 ,0xd6 ,0x2e ,0xb8 ,0xa0 ,0x06 ,0x70 }; 388 | //uint8_t serverChallenge[16] = { 0x5B ,0x5D ,0x7C ,0x7D ,0x7B ,0x3F ,0x2F ,0x3E ,0x3C ,0x2C ,0x60 ,0x21 ,0x32 ,0x26 ,0x26 ,0x28 }; 389 | 390 | //uint8_t peerChallenge[16] = { 0xcf, 0xbb, 0x78, 0x31, 0xfe, 0xf2, 0x42, 0x09, 0xd8, 0xa7, 0x63, 0xcf, 0x96, 0x25, 0x0c, 0x6c}; 391 | uint8_t peerChallenge[16] = { 0xed ,0x9b ,0xfb ,0xe9 ,0xe5 ,0x90 ,0x11 ,0x74 ,0x8c ,0x56 ,0x74 ,0x24 ,0x97 ,0xb9 ,0x40 ,0xed }; 392 | //uint8_t peerChallenge[16] = { 0x21 ,0x40 ,0x23 ,0x24 ,0x25 ,0x5E ,0x26 ,0x2A ,0x28 ,0x29 ,0x5F ,0x2B ,0x3A ,0x33 ,0x7C ,0x7E }; 393 | 394 | uint8_t NTResponse[24] = {0}; 395 | char user[100] = "ppp-test-o"; 396 | char password[100] = "ppp-password"; 397 | //char user[100] = "User"; 398 | //char password[100] = "clientPass"; 399 | char receiveResponse[] = "S=2EC8D2A3B4969F9C00215F467BB0C5307F0B765B"; 400 | 401 | printf("MSCHAP_Test\n"); 402 | 403 | uint8_t test7[7] = { 0x89 ,0xAE ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 }; 404 | uint8_t out8[8]; 405 | DesKey56BitTo64Bit(out8, test7); 406 | db_printHex(0, test7, 7); 407 | db_printHex(0, out8, 8); 408 | 409 | GenerateNTResponse(serverChallenge, peerChallenge, user, password, NTResponse, 2); //MS-CHAP-2 410 | CheckAuthenticatorResponse(password, NTResponse, peerChallenge, serverChallenge, user, (uint8_t*)receiveResponse); 411 | 412 | } 413 | 414 | 415 | void MSCHAP_Init(MSCHAP_CTX *ctx, uint8_t mschapVersion, uint8_t AuthenticatorChallenge[16]) { 416 | int i; 417 | uint8_t *p = ctx->PeerChallenge; 418 | 419 | db_printf(DB_DEBUG, "MSCHAP_Init() start\n"); 420 | 421 | ctx->version = mschapVersion; 422 | if (ctx->version == 1) { 423 | db_printf(DB_DEBUG, "MSCHAP_Init() Version = MS-CHAP-1\n"); 424 | } else if (ctx->version == 2) { 425 | db_printf(DB_DEBUG, "MSCHAP_Init() Version = MS-CHAP-2\n"); 426 | } else { 427 | db_printf(DB_DEBUG, "MSCHAP_Init() Version = %d --- Unknown!!!\n", ctx->version ); 428 | } 429 | 430 | memcpy(ctx->AuthenticatorChallenge, AuthenticatorChallenge, 16); 431 | memset(ctx->username, 0, 256); 432 | memset(ctx->password, 0, 256); 433 | 434 | // Random Peer Challenge 435 | for (i = 0; i < 16; i++) { 436 | p[i] = random(0, 0x100); 437 | } 438 | 439 | db_printf(DB_DEBUG, "AuthChallenge. . . \n"); 440 | db_printHex(DB_DEBUG, ctx->AuthenticatorChallenge, 16); 441 | db_printf(DB_DEBUG, "PeerChallenge. . . \n"); 442 | db_printHex(DB_DEBUG, ctx->PeerChallenge, 16); 443 | db_printf(DB_DEBUG, "MSCHAP_Init() end\n"); 444 | 445 | } 446 | 447 | bool MSCHAP_GetResponse(MSCHAP_CTX *ctx, char *username, char *password, uint8_t response[49]) { 448 | int ret; 449 | uint8_t lmChallenge[24]; 450 | memset(response, 0, 49); 451 | 452 | strcpy(ctx->username, username); 453 | strcpy(ctx->password, password); 454 | ret = GenerateNTResponse(ctx->AuthenticatorChallenge, ctx->PeerChallenge, ctx->username, ctx->password, ctx->NtResponse, ctx->version); 455 | if (ret == 0) { 456 | 457 | return false; 458 | } 459 | 460 | if (ctx->version == 2) { 461 | memcpy(response, ctx->PeerChallenge, 16); 462 | memcpy(&response[24], ctx->NtResponse, 24); 463 | db_printf(DB_DEBUG, "MSCHAP_GetResponse() Response. . .\n"); 464 | db_printHex(DB_DEBUG, response, 49); 465 | return true; 466 | } else if (ctx->version == 1) { 467 | LmChallengeResponse( ctx->AuthenticatorChallenge, ctx->password, lmChallenge ); 468 | memcpy(response, lmChallenge, 24); 469 | memcpy(&response[24], ctx->NtResponse, 24); 470 | response[48] = 1; 471 | return true; 472 | } 473 | 474 | return false; 475 | } 476 | 477 | bool MSCHAP_CheckAuthenticatorResponse(MSCHAP_CTX *ctx, uint8_t ReceivedResponse[42]) { 478 | bool ret = CheckAuthenticatorResponse(ctx->password, ctx->NtResponse, ctx->PeerChallenge, ctx->AuthenticatorChallenge, ctx->username, (uint8_t*)ReceivedResponse); 479 | db_printf(DB_DEBUG, "MSCHAP_CheckAuthenticatorResponse() Result = %s\n", ret==0?"false":"true"); 480 | return ret; 481 | } 482 | -------------------------------------------------------------------------------- /MSCHAP.h: -------------------------------------------------------------------------------- 1 | #ifndef MSCHAP_h 2 | #define MSCHAP_h 3 | 4 | #include 5 | 6 | typedef struct { 7 | uint8_t AuthenticatorChallenge[16]; 8 | uint8_t PeerChallenge[16]; 9 | char username[256]; 10 | char password[256]; 11 | uint8_t NtResponse[24]; 12 | uint8_t version; 13 | } MSCHAP_CTX; 14 | 15 | void MSCHAP_Init(MSCHAP_CTX *ctx, uint8_t mschapVersion, uint8_t AuthenticatorChallenge[16]); 16 | bool MSCHAP_GetResponse(MSCHAP_CTX *ctx, char *username, char *password, uint8_t response[49]); 17 | bool MSCHAP_CheckAuthenticatorResponse(MSCHAP_CTX *ctx, uint8_t ReceivedResponse[42]); 18 | void MSCHAP_Test(); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /PPTP_Client.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PPTP_Client.h" 3 | #include 4 | #include "ESPAsyncTCP.h" 5 | #include "SyncClient.h" 6 | #include "md5.h" 7 | #include "DebugMsg.h" 8 | #include "MSCHAP.h" 9 | 10 | void printIP(ip_addr_t ip) { 11 | printf("%d.", ip.addr &0xff); 12 | printf("%d.", (ip.addr >> 8) & 0xff); 13 | printf("%d.", (ip.addr >> 16) & 0xff); 14 | printf("%d", (ip.addr >> 24) & 0xff); 15 | 16 | 17 | 18 | fflush(stdout); 19 | } 20 | 21 | #define PPTPC_printHexDB(level, mem, len) db_printHex(level, mem, len) 22 | 23 | struct StartControlConnection _pptpStartConn; 24 | struct OutGoingCallRequest _pptpOutCallReq; 25 | struct OutGoingCallReply _pptpOutCallReply; 26 | struct SetLinkInfo _pptpSetLinkInfo; 27 | 28 | SyncClient _syncClient; 29 | bool _pptpConnected; 30 | char PPTPC_servername[100]; 31 | ip_addr_t PPTPC_serverIP; 32 | int PPTPC_serverport; 33 | char PPTPC_username[50]; 34 | char PPTPC_password[50]; 35 | 36 | uint16_t PPTPC_authenProtocol; 37 | #define CHAP_MD5 0x05 38 | #define CHAP_MSCHAP1 0x80 39 | #define CHAP_MSCHAP2 0x81 40 | uint8_t PPTPC_authenChapMode; 41 | uint16_t PPTPC_callId; 42 | uint16_t PPTPC_peerCallId; 43 | uint8_t PPTPC_chapIdentifier; 44 | uint8_t PPTPC_chapChallenge[256]; 45 | int PPTPC_chapChallengeSize; 46 | bool PPTPC_chapAuthenStatus; 47 | bool PPTPC_chapAuthenResponse; 48 | md5_context_t PPTPC_md5Context; 49 | MSCHAP_CTX mschap_ctx; 50 | 51 | static bool lcpComplete; 52 | static bool papComplete; 53 | static bool papLoginState; 54 | static bool cbcpComplete; 55 | static int mplsIdentifier; 56 | ip_addr_t localIP; 57 | ip_addr_t remoteIP; 58 | ip_addr_t netmask; 59 | 60 | uint32_t waitTimeoutMs = 5000; 61 | 62 | void PPTPC_buildStartControlConnectionRequest(struct StartControlConnection *req); 63 | void PPTPC_buildOutGoingCallRequest(struct OutGoingCallRequest *req); 64 | void PPTPC_buildSetLinkInfoRequest(struct SetLinkInfo *req); 65 | bool PPTPC_startControlConnection(); 66 | bool PPTPC_outGoingCall(); 67 | 68 | bool PPTPC_pppLcpConfig(); 69 | bool PPTPC_pppPap(); 70 | bool PPTPC_pppCbcp(); 71 | bool PPTPC_pppIpcp(); 72 | bool PPTPC_pppMplscp(); 73 | int PPTPC_pppPapOptions(uint8_t *buff, char *username, char *password); 74 | int PPTPC_getPppLcpConfigOptions(uint8_t *buf, int bufsize); 75 | 76 | bool PPTPC_pptpInterfaceInit(); 77 | 78 | static int PPTPC_receiveCallback(uint8_t *data, int length); 79 | 80 | void PPTPC_init(const char *server, int port, const char *user, const char *password) { 81 | _pptpConnected = false; 82 | papComplete = false; 83 | lcpComplete = false; 84 | papLoginState = false; 85 | cbcpComplete = false; 86 | mplsIdentifier = -1; 87 | PPTPC_chapChallengeSize = 0; 88 | PPTPC_chapAuthenStatus = false; 89 | PPTPC_chapAuthenResponse = false; 90 | PPTPC_authenChapMode = 0; 91 | 92 | localIP.addr = 0x00000000; 93 | remoteIP.addr = 0x0000000; 94 | netmask.addr = 0xffffffff; 95 | strcpy(PPTPC_servername, server); 96 | PPTPC_serverport = port; 97 | strcpy(PPTPC_username, user); 98 | strcpy(PPTPC_password, password); 99 | 100 | IPAddress ip; 101 | if(WiFi.hostByName(PPTPC_servername, ip) == false) { 102 | // Unable to resolve hostname 103 | db_printf(DB_INFO, "Resolve Server name fail\n"); 104 | return; 105 | } 106 | PPTPC_serverIP = ip; 107 | 108 | } 109 | 110 | void PPTPC_reinit() { 111 | _pptpConnected = false; 112 | papComplete = false; 113 | lcpComplete = false; 114 | papLoginState = false; 115 | cbcpComplete = false; 116 | mplsIdentifier = -1; 117 | PPTPC_chapChallengeSize = 0; 118 | PPTPC_chapAuthenStatus = false; 119 | PPTPC_chapAuthenResponse = false; 120 | PPTPC_authenChapMode = 0; 121 | 122 | localIP.addr = 0x00000000; 123 | remoteIP.addr = 0x0000000; 124 | netmask.addr = 0xffffffff; 125 | 126 | } 127 | 128 | bool PPTPC_startControlConnection() { 129 | struct StartControlConnection *conn; 130 | 131 | db_printf(DB_DEBUG, "PPTPC_startControlConnection() Begin\n"); 132 | conn = &_pptpStartConn; 133 | PPTPC_buildStartControlConnectionRequest(conn); 134 | 135 | if(_syncClient.write((uint8_t*)conn, sizeof(struct StartControlConnection)) <= 0){ 136 | db_printf(DB_DEBUG, "PPTPC_startControlConnection() Write state 1 fail\n"); 137 | return false; 138 | 139 | } 140 | 141 | while(_syncClient.connected() && _syncClient.available() < 156){ 142 | delay(10); 143 | } 144 | 145 | if (_syncClient.available() != 156) { 146 | db_printf(DB_DEBUG, "PPTPC_startControlConnection() Expect 156 returnn but %d\n", _syncClient.available()); 147 | return false; 148 | } 149 | 150 | memset(conn, 0, sizeof(struct StartControlConnection)); 151 | _syncClient.read((uint8_t*)conn, sizeof(_pptpStartConn)); 152 | 153 | db_printf(DB_DEBUG, "Server Hostname: %s\n", conn->hostname); 154 | db_printf(DB_DEBUG, "Server Vender: %s\n", conn->venderString); 155 | 156 | if (conn->resultCode != 1) { 157 | db_printf(DB_DEBUG, "PPTPC_startControlConnection() result code error %d\n", conn->resultCode); 158 | return false; 159 | } 160 | 161 | db_printf(DB_DEBUG, "PPTPC_startControlConnection() Success\n"); 162 | return true; 163 | } 164 | 165 | bool PPTPC_outGoingCall() { 166 | struct OutGoingCallRequest *conn; 167 | struct OutGoingCallReply *resp; 168 | 169 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() Begin\n"); 170 | 171 | conn = &_pptpOutCallReq; 172 | resp = &_pptpOutCallReply; 173 | 174 | PPTPC_buildOutGoingCallRequest(conn); 175 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() Use Call ID: %d\n", ntohs(conn->callId)); 176 | 177 | if(_syncClient.write((uint8_t*)conn, sizeof(struct OutGoingCallRequest)) <= 0){ 178 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() Write state 1 fail\n"); 179 | return false; 180 | 181 | } 182 | 183 | while(_syncClient.connected() && _syncClient.available() < 32){ 184 | delay(10); 185 | } 186 | 187 | if (_syncClient.available() != 32) { 188 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() Expect 32 returnn but %d\n", _syncClient.available()); 189 | return false; 190 | } 191 | 192 | memset(resp, 0, sizeof(struct OutGoingCallReply)); 193 | _syncClient.read((uint8_t*)resp, sizeof(OutGoingCallReply)); 194 | 195 | if (resp->resultCode != 1) { 196 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() result code error %d\n", resp->resultCode); 197 | return false; 198 | } 199 | 200 | PPTPC_peerCallId = ntohs(resp->callId); 201 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() Server(peer) Call ID: %d\n", PPTPC_peerCallId); 202 | db_printf(DB_DEBUG, "PPTPC_outGoingCall() Success\n"); 203 | return true; 204 | } 205 | 206 | bool PPTPC_setLinkInfo() { 207 | struct SetLinkInfo *conn; 208 | struct SetLinkInfo *resp; 209 | 210 | db_printf(DB_DEBUG, "PPTPC_setLinkInfo() Begin\n"); 211 | 212 | conn = &_pptpSetLinkInfo; 213 | resp = &_pptpSetLinkInfo; 214 | 215 | PPTPC_buildSetLinkInfoRequest(conn); 216 | 217 | if(_syncClient.write((uint8_t*)conn, sizeof(struct SetLinkInfo)) <= 0){ 218 | db_printf(DB_DEBUG, "PPTPC_setLinkInfo() Write state 1 fail\n"); 219 | return false; 220 | 221 | } 222 | 223 | while(_syncClient.connected() && _syncClient.available() < 24){ 224 | delay(10); 225 | } 226 | 227 | if (_syncClient.available() != 24) { 228 | db_printf(DB_DEBUG, "PPTPC_setLinkInfo() Expect 24 returnn but %d\n", _syncClient.available()); 229 | return false; 230 | } 231 | 232 | memset(resp, 0, sizeof(struct SetLinkInfo)); 233 | _syncClient.read((uint8_t*)resp, sizeof(SetLinkInfo)); 234 | 235 | if (ntohs(resp->peerCallId) != PPTPC_callId) { 236 | db_printf(DB_DEBUG, "PPTPC_setLinkInfo() callId error %d\n", ntohs(resp->peerCallId)); 237 | return false; 238 | } 239 | 240 | db_printf(DB_DEBUG, "PPTPC_setLinkInfo() Success\n"); 241 | return true; 242 | } 243 | 244 | int PPTPC_getPppLcpConfigOptions(uint8_t *buf, int bufsize) { 245 | buf[0] = 0x01; // Max Receive Unit 246 | buf[1] = 0x04; // length = 4 247 | buf[2] = 0x05; // | 248 | buf[3] = 0x78; // |- 1400 249 | 250 | buf[4] = 0x05; // Magic Number 251 | buf[5] = 0x06; // length = 6 252 | buf[6] = 0x1c; // | 253 | buf[7] = 0xb4; // | 254 | buf[8] = 0x54; // | 255 | buf[9] = 0x69; // | - 0x1cb45469 256 | 257 | buf[10] = 0x0d; // Callback 258 | buf[11] = 0x03; // length = 3 259 | buf[12] = 0x06; // Operation: Location is determined during CBCP negotiation(6) 260 | 261 | return 13; 262 | } 263 | 264 | bool PPTPC_pppLcpConfig() { 265 | int greSize; 266 | int packetSize = 0; 267 | uint8_t lcpOptions[30]; 268 | int lcpOptionsLength; 269 | struct PtpPacket *ptp; 270 | struct PppLcpPacket *lcp; 271 | 272 | db_printf(DB_DEBUG, "PPTPC_pppLcpConfig() Begin\n"); 273 | memset(lcpOptions, 0, 30); 274 | 275 | ptp = (struct PtpPacket*)lcpOptions; 276 | ptp->address = 0xff; 277 | ptp->control = 0x03; 278 | ptp->protocol = htons(0xc021); // LCP Protocol 279 | 280 | lcpOptionsLength = PPTPC_getPppLcpConfigOptions(&lcpOptions[8], 30); 281 | 282 | lcp = (struct PppLcpPacket*)&lcpOptions[4]; 283 | lcp->code = 0x01; // config request 284 | lcp->identifier = 0x00; //id 285 | lcp->length = htons(lcpOptionsLength + 4); 286 | 287 | int ret = GRE_write(lcpOptions, lcpOptionsLength + 4 + 4); 288 | db_printf(DB_DEBUG, "PPTPC_pppLcpConfig() GRE_write Length = %d\n", ret); 289 | 290 | uint32_t tm = millis(); 291 | while (lcpComplete != true) { 292 | delay(100); 293 | db_printf(DB_DEBUG, ". "); 294 | if (millis() - tm > waitTimeoutMs) { 295 | db_printf(DB_DEBUG, "PPTPC_pppLcpConfig() lcpComplete Wait timeout!!!\n"); 296 | break; 297 | } 298 | } 299 | 300 | if (lcpComplete == true) { 301 | db_printf(DB_DEBUG, "PPTPC_pppLcpConfig() Success\n"); 302 | return true; 303 | } 304 | 305 | db_printf(DB_DEBUG, "PPTPC_pppLcpConfig() Fail\n"); 306 | return false; 307 | } 308 | 309 | int PPTPC_pppPapOptions(uint8_t *buff, char *username, char *password) { 310 | int len; 311 | len = 1 + strlen(username) + 1 + strlen(password); 312 | buff[0] = strlen(username); 313 | buff++; 314 | memcpy(buff, username, strlen(username)); 315 | buff += strlen(username); 316 | buff[0] = strlen(password); 317 | buff++; 318 | memcpy(buff, password, strlen(password)); 319 | 320 | return len; 321 | } 322 | 323 | bool PPTPC_pppChapMd5() { 324 | uint8_t md5Result[16]; 325 | int pppSize; 326 | int chapSize; 327 | uint8_t buff[50]; 328 | struct PtpPacket *ptp; 329 | 330 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() Begin\n"); 331 | memset(buff, 0, 50); 332 | 333 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() Wait for chap challenge\n"); 334 | while (PPTPC_chapChallengeSize <= 0) { 335 | db_printf(DB_DEBUG, "+ "); 336 | delay(100); 337 | } 338 | 339 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() Calc MD5\n"); 340 | MD5Init(&PPTPC_md5Context); 341 | MD5Update(&PPTPC_md5Context, (uint8_t*)&PPTPC_chapIdentifier, 1); 342 | MD5Update(&PPTPC_md5Context, (uint8_t*)PPTPC_password, strlen(PPTPC_password)); 343 | MD5Update(&PPTPC_md5Context, (uint8_t*)PPTPC_chapChallenge, PPTPC_chapChallengeSize); 344 | MD5Final(md5Result, &PPTPC_md5Context); 345 | 346 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() CHAP response...\n"); 347 | PPTPC_printHexDB(DB_DEBUG, md5Result, 16); 348 | chapSize = 1+1+2+1+16+strlen(PPTPC_username); 349 | pppSize = 4 + chapSize; 350 | buff[0] = 0xff; 351 | 352 | buff[1] = 0x03; 353 | 354 | buff[2] = 0xc2; 355 | buff[3] = 0x23; 356 | 357 | buff[4] = 0x02; 358 | 359 | buff[5] = PPTPC_chapIdentifier; 360 | 361 | buff[6] = chapSize >> 8; 362 | buff[7] = chapSize & 0xff; 363 | 364 | buff[8] = 16; 365 | 366 | int i; 367 | for (i=0; i< 16; i++) { 368 | buff[i+9] = md5Result[i]; 369 | } 370 | 371 | memcpy(&buff[i +9], PPTPC_username, strlen(PPTPC_username)); 372 | 373 | int ret = GRE_write(buff, pppSize); 374 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() GRE_write length = %d\n", ret); 375 | 376 | uint32_t tm = millis(); 377 | while (PPTPC_chapAuthenResponse != true) { 378 | delay(100); 379 | db_printf(DB_DEBUG, "+ "); 380 | if (millis() - tm > 10000) { 381 | db_printf(DB_DEBUG, "Timout!!! "); 382 | break; 383 | } 384 | } 385 | 386 | if (PPTPC_chapAuthenStatus == true) { 387 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() Login Complete\n"); 388 | return true; 389 | } 390 | 391 | db_printf(DB_DEBUG, "PPTPC_pppChapMd5() Login Fail\n"); 392 | return false; 393 | } 394 | 395 | bool PPTPC_pppChapMsChap() { 396 | uint8_t chapResponse[100]; 397 | int pppSize; 398 | int chapSize; 399 | uint8_t buff[100]; 400 | struct PtpPacket *ptp; 401 | 402 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() Begin\n"); 403 | memset(buff, 0, 200); 404 | 405 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() Wait for chap challenge\n"); 406 | while (PPTPC_chapChallengeSize <= 0) { 407 | db_printf(DB_DEBUG, "+ "); 408 | delay(100); 409 | } 410 | 411 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() Calc MSCHAP2\n"); 412 | MSCHAP_GetResponse(&mschap_ctx, PPTPC_username, PPTPC_password, chapResponse); 413 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() CHAP response...\n"); 414 | PPTPC_printHexDB(DB_DEBUG, chapResponse, 49); 415 | chapSize = 1+1+2+1+49+strlen(PPTPC_username); 416 | pppSize = 4 + chapSize; 417 | buff[0] = 0xff; 418 | 419 | buff[1] = 0x03; 420 | 421 | buff[2] = 0xc2; 422 | buff[3] = 0x23; 423 | 424 | buff[4] = 0x02; 425 | 426 | buff[5] = PPTPC_chapIdentifier; 427 | 428 | buff[6] = chapSize >> 8; 429 | buff[7] = chapSize & 0xff; 430 | 431 | buff[8] = 49; 432 | 433 | int i; 434 | for (i=0; i< 49; i++) { 435 | buff[i+9] = chapResponse[i]; 436 | } 437 | 438 | memcpy(&buff[i +9], PPTPC_username, strlen(PPTPC_username)); 439 | 440 | int ret = GRE_write(buff, pppSize); 441 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() GRE_write length = %d\n", ret); 442 | 443 | uint32_t tm = millis(); 444 | while (PPTPC_chapAuthenResponse != true) { 445 | delay(100); 446 | db_printf(DB_DEBUG, "+ "); 447 | if (millis() - tm > 10000) { 448 | db_printf(DB_DEBUG, "Timout!!! "); 449 | break; 450 | } 451 | } 452 | 453 | if (PPTPC_chapAuthenStatus == true) { 454 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() Login Complete\n"); 455 | return true; 456 | } 457 | 458 | db_printf(DB_DEBUG, "PPTPC_pppChapMsChap() Login Fail\n"); 459 | return false; 460 | } 461 | 462 | bool PPTPC_pppPap() { 463 | int pppSize; 464 | int papSize; 465 | uint8_t buff[50]; 466 | struct PtpPacket *ptp; 467 | struct PppPapPacket *pap; 468 | 469 | db_printf(DB_DEBUG, "PPTPC_pppPap() Begin\n"); 470 | 471 | memset(buff, 0, 50); 472 | 473 | papSize = 4 + PPTPC_pppPapOptions(&buff[8], PPTPC_username, PPTPC_password); 474 | pppSize = 4 + papSize; 475 | 476 | ptp = (struct PtpPacket*)buff; 477 | ptp->address = 0xff; 478 | ptp->control = 0x03; 479 | ptp->protocol = htons(0xc023); // PPP PAP Protocol 480 | 481 | pap = (struct PppPapPacket*)&buff[4]; 482 | pap->code = 0x01; // authen request 483 | pap->identifier = 0x00; //id 484 | pap->length = htons(papSize); 485 | 486 | int ret = GRE_write(buff, pppSize); 487 | db_printf(DB_DEBUG, "PPTPC_pppPap() GRE_write length = %d\n", ret); 488 | 489 | while (papComplete != true) { 490 | delay(100); 491 | db_printf(DB_DEBUG, ". "); 492 | } 493 | 494 | if (papLoginState == true) { 495 | db_printf(DB_DEBUG, "PPTPC_pppPap() Login Complete\n"); 496 | return true; 497 | 498 | } 499 | 500 | db_printf(DB_DEBUG, "PPTPC_pppPap() Login Fail (user/pass Wrong)\n"); 501 | return false; 502 | } 503 | 504 | 505 | bool PPTPC_pppCbcp() { 506 | db_printf(DB_DEBUG, "PPTPC_pppCbcp() Begin\n"); 507 | while (cbcpComplete != true) { 508 | delay(100); 509 | db_printf(DB_DEBUG, ". "); 510 | } 511 | 512 | db_printf(DB_DEBUG, "PPTPC_pppCbcp() Complete\n"); 513 | return true; 514 | } 515 | 516 | bool PPTPC_pppIpcp() { 517 | int pppSize; 518 | int ipcpSize; 519 | uint8_t buff[50], *options; 520 | struct PtpPacket *ptp; 521 | struct PppPapPacket *ipcp; 522 | int ret; 523 | 524 | db_printf(DB_DEBUG, "PPTPC_pppIpcp() Begin\n"); 525 | 526 | while (remoteIP.addr == 0x00000000) { 527 | delay(100); 528 | db_printf(DB_DEBUG, ". "); 529 | } 530 | 531 | db_printf(DB_DEBUG, "PPTPC_pppIpcp() Remote IP Address: %d.%d.%d.%d\n", remoteIP.addr & 0xff, (remoteIP.addr >> 8) & 0xff, (remoteIP.addr >> 16) & 0xff, (remoteIP.addr >> 24) & 0xff); 532 | db_printf(DB_DEBUG, "PPTPC_pppIpcp() Get Remote IP Complete\n"); 533 | 534 | memset(buff, 0, 50); 535 | 536 | options = &buff[8]; 537 | *options++ = 0x03; // type ip 538 | *options++ = 6; // ip option length 539 | 540 | 541 | ipcpSize = 4 + 6; 542 | pppSize = 4 + ipcpSize; 543 | 544 | ptp = (struct PtpPacket*)buff; 545 | ptp->address = 0xff; 546 | ptp->control = 0x03; 547 | ptp->protocol = htons(0x8021); // PPP IPCP Protocol 548 | 549 | ipcp = (struct PppPapPacket*)&buff[4]; 550 | ipcp->code = 0x01; // authen request 551 | ipcp->identifier = 0x00; //id 552 | ipcp->length = htons(ipcpSize); 553 | 554 | ret = GRE_write(buff, pppSize); 555 | db_printf(DB_DEBUG, "PPTPC_pppIpcp() GRE_write length=%d\n", ret); 556 | 557 | while (localIP.addr == 0x00000000) { 558 | delay(100); 559 | db_printf(DB_DEBUG, ". "); 560 | } 561 | 562 | db_printf(DB_DEBUG, "PPTPC_pppIpcp() Local IP Address: %d.%d.%d.%d\n", localIP.addr & 0xff, (localIP.addr >> 8) & 0xff, (localIP.addr >> 16) & 0xff, (localIP.addr >> 24) & 0xff); 563 | db_printf(DB_DEBUG, "PPTPC_pppIpcp() Success\n"); 564 | return true; 565 | } 566 | 567 | bool PPTPC_pppMplscp() { 568 | int pppSize; 569 | int mplscpSize; 570 | uint8_t buff[50], *options; 571 | struct PtpPacket *ptp; 572 | struct PppPapPacket *mplscp; 573 | int ret; 574 | 575 | db_printf(DB_DEBUG, "PPTPC_pppMplscp() Begin\n"); 576 | 577 | while (mplsIdentifier < 0) { 578 | delay(100); 579 | db_printf(DB_DEBUG, ". "); 580 | } 581 | 582 | db_printf(DB_DEBUG, "PPTPC_pppMplscp() Receive MPLSCP ID: %d\n", mplsIdentifier); 583 | 584 | //Next sent Protocol reject for MPLS 585 | memset(buff, 0, 50); 586 | 587 | options = &buff[10]; 588 | *options++ = 0x01; // Config request 589 | *options++ = mplsIdentifier; // id 590 | *options++ = 0x00; // | 591 | *options++ = 0x04; // |- length 592 | 593 | 594 | mplscpSize = 6 + 4; 595 | pppSize = 4 + mplscpSize; 596 | 597 | ptp = (struct PtpPacket*)buff; 598 | ptp->address = 0xff; 599 | ptp->control = 0x03; 600 | ptp->protocol = htons(0xc021); // PPP LCP Protocol 601 | 602 | mplscp = (struct PppPapPacket*)&buff[4]; 603 | mplscp->code = 0x08; // Protocol reject 604 | mplscp->identifier = 0x10; //id 605 | mplscp->length = htons(mplscpSize); 606 | buff[8] = 0x82; // | 607 | buff[9] = 0x81; // |- mplscp 608 | 609 | ret = GRE_write(buff, pppSize); 610 | db_printf(DB_DEBUG, "PPTPC_pppMplscp() GRE_write length=%d\n", ret); 611 | 612 | db_printf(DB_DEBUG, "PPTPC_pppMplscp() Success\n"); 613 | return true; 614 | } 615 | 616 | int PPTPC_writeData(uint8_t *data, int length) { 617 | int pppSize; 618 | int ipv4Size; 619 | uint8_t buff[1450], *p; 620 | struct PtpPacket *ptp; 621 | int ret; 622 | 623 | db_printf(DB_DEBUG, "PPTPC_writeData() Begin\n"); 624 | 625 | memset(buff, 0, 1450); 626 | 627 | ipv4Size = length; 628 | pppSize = 4 + ipv4Size; 629 | 630 | ptp = (struct PtpPacket*)buff; 631 | ptp->address = 0xff; 632 | ptp->control = 0x03; 633 | ptp->protocol = htons(0x0021); // PPP IPv4 Protocol 634 | 635 | p = &buff[4]; 636 | memcpy(p, data, length); 637 | 638 | ret = GRE_write(buff, pppSize); 639 | db_printf(DB_DEBUG, "PPTPC_writeData() GRE_write len = %d Success\n", ret); 640 | 641 | 642 | return length; 643 | } 644 | 645 | #define PPTP_IF_TASK_PRIO 1 646 | #define PPTP_IF_TASK_QUEUE_SIZE 2 647 | os_event_t pptpInterfaceTaskQueue[PPTP_IF_TASK_QUEUE_SIZE]; 648 | 649 | struct netif pptpLwip_netif; 650 | 651 | //ESP stack -> GRE 652 | err_t ICACHE_FLASH_ATTR PPTPC_interfaceOutput(struct netif *netif, struct pbuf *p, const ip_addr_t *ipaddr) { 653 | uint8_t *data = (uint8_t*)p->payload; 654 | 655 | db_printf(DB_DEBUG, "PPTPC_interfaceOutput() ESP->VPN Length=%d Begin\n", p->len); 656 | 657 | if (data[9] == 0x2f) { 658 | db_printf(DB_DEBUG, "PPTPC_interfaceOutput() skip GRE\n"); 659 | return 0; 660 | } 661 | 662 | PPTPC_printHexDB( DB_DEBUG, (uint8_t*)p->payload, p->len); 663 | db_printf(DB_DEBUG, "\n"); 664 | 665 | PPTPC_writeData((uint8_t*)p->payload, p->len); 666 | db_printf(DB_DEBUG, "PPTPC_interfaceOutput() Success\n"); 667 | return 0; 668 | } 669 | 670 | // GRE -> ESP Stack 671 | void ICACHE_FLASH_ATTR PPTPC_interfaceInput(uint8_t *data, uint32_t len) { 672 | 673 | db_printf(DB_DEBUG, "PPTPC_interfaceInput() VPN->ESP Length=%d Begin\n", len); 674 | 675 | struct pbuf *pb; 676 | 677 | pb = pbuf_alloc(PBUF_LINK, len, PBUF_RAM); 678 | if (pb == NULL) { 679 | db_printf(DB_DEBUG, "PPTPC_interfaceInput() pbuf_alloc is null\n"); 680 | return; 681 | } 682 | 683 | PPTPC_printHexDB( DB_DEBUG, data, len); 684 | 685 | pbuf_take(pb, data, len); 686 | // Post it to the send task 687 | system_os_post(PPTP_IF_TASK_PRIO, 0, (os_param_t)pb); 688 | db_printf(DB_DEBUG, "PPTPC_interfaceInput() Success\n"); 689 | } 690 | 691 | err_t ICACHE_FLASH_ATTR PPTPC_pptpLwipInit(struct netif *netif) { 692 | db_printf(DB_DEBUG, "PPTPC_pptpLwipInit() Begin\n"); 693 | //NETIF_INIT_SNMP(netif, snmp_ifType_other, 0); 694 | netif->name[0] = 'p'; 695 | netif->name[1] = 't'; 696 | 697 | netif->output = PPTPC_interfaceOutput; 698 | netif->mtu = 1400; 699 | netif->flags = NETIF_FLAG_LINK_UP; 700 | 701 | db_printf(DB_DEBUG, "PPTPC_pptpLwipInit() Success\n"); 702 | return 0; 703 | } 704 | 705 | static void ICACHE_FLASH_ATTR pptpInterfaceTask(os_event_t *e) { 706 | db_printf(DB_DEBUG, "pptpInterfaceTask() Begin\n"); 707 | struct pbuf *pb = (struct pbuf *)e->par; 708 | if (pb == NULL) { 709 | db_printf(DB_DEBUG, "pptpInterfaceTask() pb us null\n"); 710 | return; 711 | } 712 | 713 | if (pptpLwip_netif.input(pb, &pptpLwip_netif) != ERR_OK) { 714 | db_printf(DB_DEBUG, "pptpInterfaceTask() pptpLwip_netif.input != OK\n"); 715 | pbuf_free(pb); 716 | } 717 | 718 | db_printf(DB_DEBUG, "pptpInterfaceTask() End\n"); 719 | } 720 | 721 | bool PPTPC_pptpInterfaceInit() { 722 | 723 | system_os_task(pptpInterfaceTask, PPTP_IF_TASK_PRIO, pptpInterfaceTaskQueue, PPTP_IF_TASK_QUEUE_SIZE); 724 | 725 | netif_add(&pptpLwip_netif, &localIP, &netmask, &remoteIP, NULL, PPTPC_pptpLwipInit, ip_input); 726 | netif_set_up(&pptpLwip_netif); 727 | 728 | 729 | return true; 730 | } 731 | 732 | bool PPTPC_connect() { 733 | 734 | db_printf(DB_DEBUG, "PPTPC_connect() Begin\n"); 735 | if(!_syncClient.connect(PPTPC_servername, PPTPC_serverport)){ 736 | db_printf(DB_INFO, "PPTPC_connect() syncClient.connect() Fail\n"); 737 | return false; 738 | } 739 | 740 | if (PPTPC_startControlConnection() != true) { 741 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_startControlConnection() Fail\n"); 742 | return false; 743 | } 744 | 745 | if (PPTPC_outGoingCall() != true) { 746 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_outGoingCall() Fail\n"); 747 | return false; 748 | } 749 | 750 | if (PPTPC_setLinkInfo() != true) { 751 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_setLinkInfo() Fail\n"); 752 | return false; 753 | } 754 | 755 | 756 | // Begin GRE 757 | if (GRE_init( PPTPC_servername, PPTPC_serverport) != true) { 758 | db_printf(DB_INFO, "PPTPC_connect() GRE_init() Fail\n"); 759 | return false; 760 | } 761 | GRE_callId = PPTPC_peerCallId; 762 | GRE_protocolType = 0x880b; // PPP 763 | GRE_recvCallback = &PPTPC_receiveCallback; 764 | 765 | if (PPTPC_pppLcpConfig() != true) { 766 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppLcpConfig() Fail\n"); 767 | return false; 768 | } 769 | 770 | if (PPTPC_authenProtocol == 0xc023) { // PAP Authen 771 | if (PPTPC_pppPap() != true) { 772 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppPap() Fail\n"); 773 | return false; 774 | } 775 | } else if (PPTPC_authenProtocol == 0xc223) { // CHAP Authen 776 | 777 | // CHAP-MD5 Authen 778 | if (PPTPC_authenChapMode == CHAP_MD5) { 779 | if (PPTPC_pppChapMd5() != true) { 780 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppChapMd5() Fail\n"); 781 | return false; 782 | } 783 | } else if ((PPTPC_authenChapMode == CHAP_MSCHAP2) || (PPTPC_authenChapMode == CHAP_MSCHAP1)) { // MS-CHAP Authen 784 | if (PPTPC_pppChapMsChap() != true) { 785 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppChapMsChap() Fail\n"); 786 | return false; 787 | } 788 | } else { 789 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppChap() Unknown, Fail\n"); 790 | return false; 791 | } 792 | } else { 793 | db_printf(DB_INFO, "PPTPC_connect() Fail Unknown Authen type 0x%04X\n", PPTPC_authenProtocol); 794 | return false; 795 | } 796 | 797 | if (PPTPC_pppCbcp() != true) { 798 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppCbcp() Fail\n"); 799 | return false; 800 | } 801 | 802 | if (PPTPC_pppIpcp() != true) { 803 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppIpcp Failed\n"); 804 | return false; 805 | } 806 | 807 | /* 808 | if (PPTPC_pppMplscp() != true) { 809 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pppMplscp Failed\n"); 810 | return false; 811 | } 812 | */ 813 | 814 | // Begin to link pptp tunel to lwip 815 | if (PPTPC_pptpInterfaceInit() != true) { 816 | db_printf(DB_INFO, "PPTPC_connect() PPTPC_pptpInterfaceInit() Failed\n"); 817 | return false; 818 | } 819 | 820 | db_printf(DB_DEBUG, "PPTPC_connect() Success\n"); 821 | _pptpConnected = true; 822 | return true; 823 | } 824 | 825 | void PPTPC_buildSetLinkInfoRequest(struct SetLinkInfo *req){ 826 | memset(req, 0, sizeof(struct SetLinkInfo)); 827 | req->length = htons (24); 828 | req->pptpMessageType = htons(1); 829 | req->magic = htonl(0x1a2b3c4d); 830 | req->controlMessageType = htons(15); 831 | req->peerCallId = htons(PPTPC_peerCallId); 832 | req->sendAccm = htonl(0xffffffff); 833 | req->receiveAccm = htonl(0xffffffff); 834 | 835 | } 836 | 837 | void PPTPC_buildOutGoingCallRequest(struct OutGoingCallRequest *req) { 838 | PPTPC_callId = random(0x10000); // 16 bit random 839 | memset(req, 0, sizeof(struct OutGoingCallRequest)); 840 | req->length = htons (168); 841 | req->pptpMessageType = htons(1); 842 | req->magic = htonl(0x1a2b3c4d); 843 | req->controlMessageType = htons(7); 844 | req->callId = htons(PPTPC_callId); 845 | req->callSerialNumber = htons(9); 846 | req->minimumBps = htonl(300); 847 | req->maximumBps = htonl(1000000000UL); 848 | req->bearerType = htonl(3); 849 | req->framingType = htonl(3); 850 | req->recvWindowSize = htons(64); 851 | req->processingDelay = htons(0); 852 | 853 | } 854 | 855 | void PPTPC_buildStartControlConnectionRequest(struct StartControlConnection *req) { 856 | memset(req, 0, sizeof(struct StartControlConnection)); 857 | req->length = htons (156); 858 | req->pptpMessageType = htons(1); 859 | req->magic = htonl(0x1a2b3c4d); 860 | req->controlMessageType = htons(1); 861 | req->protocolVersion = htons(0x100); // 01.00 862 | req->framingCapabilities = htonl(1); 863 | req->bearerCapabilities = htonl(1); 864 | req->maximumChannel = htons(0); 865 | req->firmwareRevision = htons(0); 866 | strcpy(req->venderString, "ESP8266-Sun89-Natthapol89.com"); 867 | } 868 | 869 | bool PPTPC_checkLcpRequest(uint8_t *lcpOptions, int length) { 870 | uint8_t *p = lcpOptions; 871 | uint8_t type; 872 | uint8_t len; 873 | uint8_t db; 874 | uint16_t ds; 875 | uint32_t dl; 876 | 877 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Begin\n"); 878 | 879 | while(length > 0) { 880 | type = *p; 881 | len = *(p+1); 882 | if (type == 0x01) { // Type: Maximum MRU 883 | if (len != 4) { 884 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() error MRU len != 4\n"); 885 | return false; 886 | } 887 | 888 | ds = *(p + 3) | (*(p + 2) << 8); 889 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Max MRU=%d\n", ds); 890 | if (ds > 1450) { 891 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() error Max MRU > 1450\n"); 892 | return false; 893 | } 894 | 895 | } else if (type == 0x03) { // type: Authen protocol 896 | ds = *(p + 3) | (*(p + 2) << 8); 897 | if (ds == 0xc023) { // PAP 898 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Authen mode = PAP\n"); 899 | 900 | } else if (ds == 0xc223) { // CHAP 901 | PPTPC_authenChapMode = p[4]; 902 | if (p[4] == CHAP_MD5) { // CHAP-MD5 903 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Authen mode = CHAP-MD5\n"); 904 | 905 | } else if (p[4] == CHAP_MSCHAP1) { //MS-CHAP-1 906 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Authen mode = MS-CHAP-1\n"); 907 | 908 | } else if (p[4] == CHAP_MSCHAP2) { //MS-CHAP-2 909 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Authen mode = MS-CHAP-2\n"); 910 | 911 | } else { 912 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Authen mode = CHAP (Fail)\n"); 913 | return false; 914 | } 915 | } else { 916 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Authen mode = Unknown 0x%04X\n", ds); 917 | } 918 | 919 | PPTPC_authenProtocol = ds; 920 | 921 | } else if (type == 0x05) { //type: Magic number 922 | if (len != 6) { 923 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Error Magic Number len != 6\n"); 924 | return false; 925 | } 926 | 927 | dl = p[5] | (p[4] << 8) | (p[3] << 16) | (p[2] << 24); 928 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Magic Number = 0x%X\n", dl); 929 | 930 | } else { 931 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Error Unknown option type =%d\n", type); 932 | //return false; 933 | } 934 | 935 | p = p + len; 936 | length = length - len; 937 | } 938 | 939 | db_printf(DB_DEBUG, "PPTPC_checkLcpRequest() Success\n"); 940 | return true; 941 | } 942 | 943 | static int PPTPC_receiveCallback(uint8_t *data, int length) { 944 | struct PtpPacket *ptp; 945 | struct PppLcpPacket *lcp; 946 | struct PppPapPacket *pap; 947 | uint8_t *lcpOptions; 948 | int optionLength; 949 | uint8_t buff0[30]; 950 | uint8_t *src = data; 951 | ip_addr_t *ip; 952 | 953 | //Serial.println("Callback"); 954 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() Begin\n"); 955 | 956 | ptp = (struct PtpPacket *)data; 957 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() PTP Address: 0x%02X, Control: 0x%02X, Protocol: 0x%04X\n", ptp->address, ptp->control, ntohs(ptp->protocol)); 958 | 959 | data += 4; //Skip ptp data 960 | 961 | // PPP LCP 962 | if (ntohs(ptp->protocol) == 0xc021) { 963 | lcp = (struct PppLcpPacket*)data; 964 | optionLength = ntohs(lcp->length) - 4; 965 | data +=4; 966 | 967 | db_printf(DB_DEBUG, "LCP Code: 0x%02X, Identifier: 0x%02X, Length: %d\n", lcp-> code, lcp->identifier, ntohs(lcp->length)); 968 | 969 | // LCP Configuration Request 970 | if (lcp->code == 0x01) { 971 | 972 | if (PPTPC_checkLcpRequest(data, optionLength) == true) { 973 | lcp->code = 0x02; //LCP Config Ack 974 | lcpComplete = true; 975 | } else { 976 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() ** Fail: LCP Option from server not support by client\n"); 977 | lcp->code = 0x03; //LCP Config Nack 978 | 979 | } 980 | return 1; // tell parent to write gre answer 981 | 982 | } 983 | 984 | // LCP Echo Request 985 | if (lcp->code == 0x09) { 986 | lcp->code = 0x0a; //LCP echo reply 987 | return 1; // tell parent to write gre answer 988 | 989 | } 990 | } 991 | 992 | // PPP PAP 993 | if (ntohs(ptp->protocol) == 0xc023) { 994 | pap = (struct PppPapPacket*)data; 995 | optionLength = ntohs(pap->length) - 4; 996 | data +=4; 997 | 998 | db_printf(DB_DEBUG, "PAP Code: 0x%02X, Identifier: 0x%02X, Length: %d\n", pap->code, pap->identifier, ntohs(pap->length)); 999 | 1000 | // PAP Authen Ack 1001 | if (pap->code == 0x02) { 1002 | papComplete = true; 1003 | papLoginState = true; 1004 | return 0; 1005 | } 1006 | 1007 | // PAP Authen Nack (user/pass fail) 1008 | if (pap->code == 0x03) { 1009 | papComplete = true; 1010 | papLoginState = false; 1011 | return 0; 1012 | } 1013 | } 1014 | 1015 | // PPP CHAP 1016 | if (ntohs(ptp->protocol) == 0xc223) { 1017 | lcp = (struct PppLcpPacket*)data; 1018 | 1019 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() CHAP Code: 0x%02X, Identifier: 0x%02X, Length: %d\n", lcp-> code, lcp->identifier, ntohs(lcp->length)); 1020 | 1021 | // Challenge 1022 | if (lcp->code == 0x01) { 1023 | 1024 | memcpy(PPTPC_chapChallenge, data + 5, *(data +4)); 1025 | PPTPC_chapIdentifier = *(data + 1); 1026 | PPTPC_chapChallengeSize = *(data +4); 1027 | 1028 | if (PPTPC_authenChapMode == CHAP_MSCHAP2) { 1029 | MSCHAP_Init(&mschap_ctx, 2, PPTPC_chapChallenge); 1030 | } else if (PPTPC_authenChapMode == CHAP_MSCHAP1) { 1031 | MSCHAP_Init(&mschap_ctx, 1, PPTPC_chapChallenge); 1032 | } 1033 | return 0; // tell parent to not write gre answer 1034 | } 1035 | 1036 | // Chap Success 1037 | if (lcp->code == 0x03) { 1038 | PPTPC_chapAuthenStatus = true; 1039 | PPTPC_chapAuthenResponse = true; 1040 | return 0; 1041 | } 1042 | 1043 | // Chap Failure (May be User/Password incorrect) 1044 | if (lcp->code == 0x04) { 1045 | PPTPC_chapAuthenStatus = false; 1046 | PPTPC_chapAuthenResponse = true; 1047 | return 0; 1048 | } 1049 | 1050 | } 1051 | 1052 | // PPP CBCP 1053 | if (ntohs(ptp->protocol) == 0xc029) { 1054 | lcp = (struct PppLcpPacket*)data; 1055 | 1056 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() CBCP Code: 0x%02X, Identifier: 0x%02X, Length: %d\n", lcp-> code, lcp->identifier, ntohs(lcp->length)); 1057 | 1058 | // CBCP Request 1059 | if (lcp->code == 0x01) { 1060 | lcp->code = 0x02; //CBCP Response 1061 | return 1; // tell parent to write gre answer 1062 | } 1063 | 1064 | // CBCP Ack 1065 | if (lcp->code == 0x03) { 1066 | cbcpComplete = true; 1067 | return 0; // tell parent to not write gre answer 1068 | } 1069 | 1070 | } 1071 | 1072 | // PPP MPLSCP 1073 | if (ntohs(ptp->protocol) == 0x8281) { 1074 | lcp = (struct PppLcpPacket*)data; 1075 | 1076 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() MPLSCP Code: 0x%02X, Identifier: 0x%02X, Length: %d\n", lcp-> code, lcp->identifier, ntohs(lcp->length)); 1077 | 1078 | // MPLS Request 1079 | if (lcp->code == 0x01) { 1080 | mplsIdentifier = lcp->identifier; 1081 | return 0; // tell parent to not write gre answer 1082 | } 1083 | 1084 | } 1085 | 1086 | // PPP IPCP (Internet protocol Control Protocol) 1087 | if (ntohs(ptp->protocol) == 0x8021) { 1088 | lcp = (struct PppLcpPacket*)data; 1089 | uint8_t *opt = data + 4; 1090 | 1091 | db_printf(DB_DEBUG, "PPTPC_receiveCallback() IPCP Code: 0x%02X, Identifier: 0x%02X, Length: %d\n", lcp-> code, lcp->identifier, ntohs(lcp->length)); 1092 | 1093 | // 0x01 IPCP Request (Server request to set own ip), 0x02 IPCP Ack 1094 | if (lcp->code == 0x01 || lcp->code == 0x02) { 1095 | optionLength = lcp->length; 1096 | while (optionLength > 0) { 1097 | if (*opt == 0x03) { // IP Address 1098 | if (lcp->code == 0x01) { // assigned server ip 1099 | remoteIP.addr = *(uint32_t*)(opt + 2); 1100 | //printf("Remote IP Address: "); 1101 | //printIP(remoteIP); 1102 | //printf("\n"); 1103 | } 1104 | if (lcp->code == 0x02) { // assigned client ip 1105 | localIP.addr = *(uint32_t*)(opt + 2); 1106 | //printf("Local IP Address: "); 1107 | //printIP(localIP); 1108 | //printf("\n"); 1109 | } 1110 | 1111 | break; 1112 | } 1113 | optionLength -= *(opt + 1); 1114 | opt += *(opt + 1); 1115 | } 1116 | lcp->code = 0x02; //IPCP Ack 1117 | return 1; // tell parent to write gre answer 1118 | } 1119 | 1120 | // IPCP Nack, Server assigned IP to client 1121 | if (lcp->code == 0x03) { 1122 | lcp->code = 0x01; // new request 1123 | lcp->identifier = lcp->identifier + 1; // increment ident 1124 | return 1; // tell parent to write gre answer 1125 | } 1126 | 1127 | } 1128 | 1129 | // PPP IPv4 Protocol) 1130 | if (ntohs(ptp->protocol) == 0x0021) { 1131 | PPTPC_interfaceInput(data, length - 4); 1132 | return 0; 1133 | } 1134 | 1135 | return 2; 1136 | } 1137 | 1138 | static uint32_t handleTmSec; 1139 | static uint32_t cntTmSec; 1140 | void PPTPC_handle() { 1141 | uint32_t sec = millis() / 1000; 1142 | if (sec < handleTmSec) { 1143 | handleTmSec = sec; 1144 | return; 1145 | } 1146 | 1147 | if (sec - handleTmSec > 1) { 1148 | handleTmSec = sec; 1149 | 1150 | if (!_syncClient.connected()) { 1151 | printf( "PPTPC_handle() PPTP Disconnected --> Reset...\n"); 1152 | fflush(stdout); 1153 | ESP.reset(); 1154 | } 1155 | 1156 | cntTmSec++; 1157 | if (cntTmSec >= 10) { 1158 | cntTmSec = 0; 1159 | PPTPC_setLinkInfo(); 1160 | db_printf(DB_DEBUG, "PPTPC_setLinkInfo() at Sec %d\n", handleTmSec); 1161 | } 1162 | } 1163 | 1164 | } 1165 | -------------------------------------------------------------------------------- /PPTP_Client.h: -------------------------------------------------------------------------------- 1 | #ifndef PPTP_Client_h 2 | #define PPTP_Client_h 3 | 4 | //#include "Arduino.h" 5 | #include "ESPAsyncTCP.h" 6 | #include "SyncClient.h" 7 | #include "GRE.h" 8 | 9 | extern "C" 10 | { 11 | #include 12 | #include 13 | } 14 | 15 | struct PtpPacket { 16 | uint8_t address; 17 | uint8_t control; 18 | uint16_t protocol; 19 | }; 20 | 21 | struct PppLcpPacket { 22 | uint8_t code; 23 | uint8_t identifier; 24 | uint16_t length; 25 | }; 26 | 27 | struct PppPapPacket { 28 | uint8_t code; 29 | uint8_t identifier; 30 | uint16_t length; 31 | }; 32 | 33 | 34 | 35 | 36 | struct StartControlConnection { 37 | uint16_t length; 38 | uint16_t pptpMessageType; 39 | uint32_t magic; 40 | uint16_t controlMessageType; 41 | uint16_t reserved0; 42 | uint16_t protocolVersion; 43 | //uint16_t reserved1; 44 | uint8_t resultCode; 45 | uint8_t errorCode; 46 | uint32_t framingCapabilities; 47 | uint32_t bearerCapabilities; 48 | uint16_t maximumChannel; 49 | uint16_t firmwareRevision; 50 | char hostname[64]; 51 | char venderString[64]; 52 | }; 53 | 54 | struct OutGoingCallRequest { 55 | uint16_t length; 56 | uint16_t pptpMessageType; 57 | uint32_t magic; 58 | uint16_t controlMessageType; 59 | uint16_t reserved0; 60 | uint16_t callId; 61 | uint16_t callSerialNumber; 62 | uint32_t minimumBps; 63 | uint32_t maximumBps; 64 | uint32_t bearerType; 65 | uint32_t framingType; 66 | uint16_t recvWindowSize; 67 | uint16_t processingDelay; 68 | uint16_t phoneNumberLength; 69 | uint16_t reserved1; 70 | char phoneNumber[64]; 71 | char subAddress[64]; 72 | }; 73 | 74 | struct OutGoingCallReply { 75 | uint16_t length; 76 | uint16_t pptpMessageType; 77 | uint32_t magic; 78 | uint16_t controlMessageType; 79 | uint16_t reserved0; 80 | uint16_t callId; 81 | uint16_t peerCallId; 82 | uint8_t resultCode; 83 | uint8_t errorCode; 84 | uint16_t causeCode; 85 | uint32_t connectSpeed; 86 | uint16_t recvWindowSize; 87 | uint16_t processingDelay; 88 | uint16_t phyChannelId; 89 | }; 90 | 91 | 92 | struct SetLinkInfo { 93 | uint16_t length; 94 | uint16_t pptpMessageType; 95 | uint32_t magic; 96 | uint16_t controlMessageType; 97 | uint16_t reserved0; 98 | uint16_t peerCallId; 99 | uint16_t reserved1; 100 | uint32_t sendAccm; 101 | uint32_t receiveAccm; 102 | }; 103 | 104 | void PPTPC_init(const char *server, int port, const char *user, const char *password); 105 | bool PPTPC_connect(); 106 | int PPTPC_writeData(uint8_t *data, int length); 107 | //static int PPTPC_receiveCallback(uint8_t *data, int length); 108 | bool PPTPC_setLinkInfo(); 109 | void PPTPC_handle(); 110 | 111 | extern ip_addr_t localIP; 112 | extern ip_addr_t remoteIP; 113 | extern ip_addr_t netmask; 114 | 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VpnTcpProxy 2 | PPTP VPN + TCP Proxy For ESP8266 3 | 4 | ## How to Use 5 | Just download this project and open with Arduino 6 | 7 | ## Require Library 8 | 1. [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) 9 | 2. [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) 10 | 11 | ## Default Configuration 12 | - If can't connect to AP, Device will active SoftAP mode with SSID=VpnTcpProxy no password (Reboot every 5 minute) 13 | - Default Config can see in file DeviceConfig.cpp 14 | 15 | const char* ssid = "WiFi2.4"; 16 | const char* password = "12345678"; 17 | const char* pptp_server = "vpn.example.com"; 18 | const int pptp_port = 1723; 19 | const char *pptp_user = "pptp-user"; 20 | const char *pptp_password = "pptp-password"; 21 | const char *dest_server = "192.168.1.1"; 22 | 23 | ## Web File 24 | - Store in ESP8266 flash by SPIFFS 25 | - Directory "data" is Web File 26 | -------------------------------------------------------------------------------- /SS/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/SS/dashboard.png -------------------------------------------------------------------------------- /SS/nodeMCU.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/SS/nodeMCU.jpg -------------------------------------------------------------------------------- /SS/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/SS/setting.png -------------------------------------------------------------------------------- /SS/winbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/SS/winbox.png -------------------------------------------------------------------------------- /TcpProxyServer.cpp: -------------------------------------------------------------------------------- 1 | #include "TcpProxyServer.h" 2 | #include "DebugMsg.h" 3 | 4 | #include "Arduino.h" 5 | #include 6 | 7 | extern "C" 8 | { 9 | #include 10 | #include 11 | #include 12 | } 13 | 14 | #define TcpProxyServer_printHex(level, mem, len) db_printHex(level, mem, len) 15 | 16 | struct raw_pcb *tcpControlBlock; 17 | ip_addr_t destServerIP; 18 | ip_addr_t clientAddress; 19 | ip_addr_t myIP; 20 | uint16_t reservedPort; 21 | uint16_t pptpPort = 1723; 22 | 23 | bool TcpProxyServer_setReservedPort(unsigned short port) { 24 | reservedPort = port; 25 | return true; 26 | } 27 | 28 | bool TcpProxyServer_setDestinationServer(const char *destServer) { 29 | IPAddress tmpip; 30 | if(WiFi.hostByName(destServer, tmpip) == false) { 31 | // Unable to resolve hostname 32 | db_printf(DB_INFO, "TcpProxyServer_setDestinationServer() Resolve Server name fail\n"); 33 | return false; 34 | } 35 | destServerIP = tmpip; 36 | return true; 37 | } 38 | 39 | bool TcpProxyServer_begin(const char *destServer, unsigned short _reservedPort) { 40 | db_printf(DB_DEBUG, "TcpProxyServer_begin() Start\n"); 41 | reservedPort = 0; 42 | destServerIP.addr = 0; 43 | clientAddress.addr = 0; 44 | myIP.addr = 0; 45 | 46 | myIP = WiFi.localIP(); 47 | 48 | IPAddress tmpip; 49 | if(WiFi.hostByName(destServer, tmpip) == false) { 50 | // Unable to resolve hostname 51 | db_printf(DB_INFO, "TcpProxyServer_begin() Resolve Server name fail\n"); 52 | return false; 53 | } 54 | destServerIP = tmpip; 55 | reservedPort = _reservedPort; 56 | 57 | db_printf(DB_DEBUG, "TcpProxyServer_begin() End\n"); 58 | return true; 59 | 60 | } 61 | 62 | 63 | 64 | bool TCP_write(ip_addr_t *ip, pbuf *packetBuffer) { 65 | db_printf(DB_DEBUG, "TCP_write() Begin\n"); 66 | 67 | // Finally, send the packet and register timestamp 68 | raw_sendto(tcpControlBlock, packetBuffer, ip); 69 | 70 | db_printf(DB_DEBUG, "TCP_write() Success\n"); 71 | return true; 72 | } 73 | 74 | struct TcpPseudoHeader { 75 | uint32_t src; 76 | uint32_t dest; 77 | uint8_t reserved; 78 | uint8_t protocol; 79 | uint16_t tcpLength; 80 | }; 81 | 82 | void fillChecksum(uint8_t * tcpPacket, ip_addr_t *srcIP, ip_addr_t *destIP, int length) { 83 | struct TcpPseudoHeader pseudoHeader; 84 | uint8_t *p = tcpPacket; 85 | uint32_t chksum; 86 | uint8_t *psum; 87 | uint16_t finalsum; 88 | 89 | db_printf(DB_DEBUG, "fillChecksum() Start\n"); 90 | 91 | memset(&pseudoHeader, 0, sizeof(struct TcpPseudoHeader)); 92 | 93 | // Clear Checksum 94 | p[16] = 0; 95 | p[17] = 0; 96 | 97 | pseudoHeader.src = srcIP->addr; 98 | pseudoHeader.dest = destIP->addr; 99 | pseudoHeader.protocol = 6; // TCP 100 | pseudoHeader.tcpLength = htons(length); 101 | 102 | chksum = 0; 103 | psum = (uint8_t*)&pseudoHeader; 104 | for (int i =0; i < 12; i+=2) { 105 | uint16_t chunk = (psum[0] << 8) | psum[1]; 106 | db_printf(DB_DEBUG, "fillChecksum() HDR Word = 0x%04X\n", chunk); 107 | chksum += chunk; 108 | psum += 2; 109 | } 110 | db_printf(DB_DEBUG, "fillChecksum() Pseudo Header Sum = 0x%08X\n", chksum); 111 | 112 | psum = (uint8_t *)tcpPacket; 113 | while (length > 1) { 114 | uint16_t chunk = (*psum << 8) | *(psum + 1); 115 | db_printf(DB_DEBUG, "fillChecksum() TCP Word = 0x%04X\n", chunk); 116 | chksum += chunk; 117 | psum += 2; 118 | length -= 2; 119 | } 120 | 121 | //if any bytes left, pad the bytes and add 122 | if(length > 0) { 123 | uint16_t chunk = (uint16_t)(*psum) << 8; 124 | db_printf(DB_DEBUG, "fillChecksum() Last Word = 0x%04X\n", chunk); 125 | chksum += chunk; 126 | } 127 | 128 | //printf("All Sum = 0x%08X\n", chksum); 129 | db_printf(DB_DEBUG, "fillChecksum() All Sum = 0x%08X\n", chksum); 130 | 131 | finalsum = (chksum & 0xffff) + (chksum >> 16); 132 | 133 | //Fold 32-bit sum to 16 bits: add carrier to result 134 | while (chksum >> 16) { 135 | chksum = (chksum & 0xffff) + (chksum >> 16); 136 | } 137 | chksum = ~chksum; 138 | finalsum = (uint16_t)chksum; 139 | db_printf(DB_DEBUG, "fillChecksum() Final Sum = 0x%04X\n", finalsum); 140 | p[16] = finalsum >> 8; 141 | p[17] = finalsum & 0xff; 142 | 143 | db_printf(DB_DEBUG, "fillChecksum() End\n"); 144 | } 145 | 146 | 147 | ip_addr_t vpnClientIP; 148 | static uint8_t tcpReceivedStatic(void *tcp, raw_pcb *pcb, pbuf *packetBuffer, const ip_addr_t * addr) { 149 | 150 | db_printf(DB_DEBUG, "tcpReceivedStatic() Start\n"); 151 | // Check parameters 152 | if( 153 | //gre == nullptr || 154 | pcb == nullptr || 155 | packetBuffer == nullptr || 156 | addr == nullptr) 157 | { 158 | // 0 is returned to raw_recv. In this way the packet will be matched 159 | // against further PCBs and/or forwarded to other protocol layers. 160 | db_printf(DB_DEBUG, "tcpReceivedStatic() nullptr\n"); 161 | return 0; 162 | } 163 | 164 | // Save IPv4 header structure to read ttl value 165 | struct ip_hdr * ip = (struct ip_hdr *)packetBuffer->payload; 166 | if(ip == nullptr) 167 | { 168 | // Not free the packet, and return zero. The packet will be matched against 169 | // further PCBs and/or forwarded to other protocol layers. 170 | db_printf(DB_DEBUG, "tcpReceivedStatic() error 2\n"); 171 | return 0; 172 | } 173 | 174 | /** Source IP address of current_header */ 175 | ip_addr_t current_iphdr_src; 176 | 177 | 178 | /** Destination IP address of current_header */ 179 | ip_addr_t current_iphdr_dest; 180 | ip_addr_t tmp_ip; 181 | 182 | struct in_addr address; 183 | /* copy IP addresses to aligned ip_addr_t */ 184 | ip_addr_copy(current_iphdr_dest, ip->dest); 185 | ip_addr_copy(current_iphdr_src, ip->src); 186 | 187 | address.s_addr=current_iphdr_src.addr; 188 | db_printf(DB_DEBUG, "tcpReceivedStatic() Source Address: %s\n", inet_ntoa(address)); 189 | 190 | address.s_addr=current_iphdr_dest.addr; 191 | db_printf(DB_DEBUG, "tcpReceivedStatic() Destinarion Address: %s\n", inet_ntoa(address)); 192 | 193 | uint8_t *p = (uint8_t *)packetBuffer->payload; 194 | uint16_t destPort = (p[22] << 8) | p[23]; 195 | uint16_t srcPort = (p[20] << 8) | p[21]; 196 | uint16_t tcpFlag = (p[32] << 8) | p[33]; 197 | db_printf(DB_DEBUG, "tcpReceivedStatic() Source Port: %d\n", srcPort); 198 | db_printf(DB_DEBUG, "tcpReceivedStatic() Destination Port: %d\n", destPort); 199 | db_printf(DB_DEBUG, "tcpReceivedStatic() Packet Length Before: %d\n", packetBuffer->len); 200 | db_printf(DB_DEBUG, "TCP Flag: "); 201 | if (tcpFlag & 0x0010) db_printf(DB_DEBUG, "ack,"); 202 | if (tcpFlag & 0x0008) db_printf(DB_DEBUG, "push,"); 203 | if (tcpFlag & 0x0004) db_printf(DB_DEBUG, "reset,"); 204 | if (tcpFlag & 0x0002) db_printf(DB_DEBUG, "sync,"); 205 | if (tcpFlag & 0x0001) db_printf(DB_DEBUG, "fin,"); 206 | db_printf(DB_DEBUG, "\n"); 207 | 208 | TcpProxyServer_printHex(DB_DEBUG, ip, packetBuffer->len); 209 | 210 | // Port 'reservedPort' reserve for Web setting 211 | if ((destPort != reservedPort) && (destPort != pptpPort) && (srcPort != pptpPort)) { 212 | pbuf_header(packetBuffer, -PBUF_IP_HLEN); 213 | db_printf(DB_DEBUG, "tcpReceivedStatic() Packet Length After: %d\n", packetBuffer->len); 214 | 215 | // Request From Client -> Forward to Destination server 216 | if (current_iphdr_src.addr != destServerIP.addr) { 217 | 218 | clientAddress.addr = current_iphdr_src.addr; 219 | vpnClientIP.addr = current_iphdr_dest.addr; 220 | fillChecksum((uint8_t *)packetBuffer->payload, &myIP, &destServerIP, packetBuffer->len); 221 | TcpProxyServer_printHex(DB_DEBUG, (uint8_t *)packetBuffer->payload, packetBuffer->len); 222 | 223 | TCP_write(&destServerIP, packetBuffer); 224 | 225 | 226 | 227 | } else { // Response From Destination Server -> Forward back to client 228 | 229 | fillChecksum((uint8_t *)packetBuffer->payload, &vpnClientIP, &clientAddress, packetBuffer->len); 230 | TcpProxyServer_printHex(DB_DEBUG, (uint8_t *)packetBuffer->payload, packetBuffer->len); 231 | TCP_write(&clientAddress, packetBuffer); 232 | } 233 | 234 | db_printf(DB_DEBUG, "tcpReceivedStatic() End - Proxy Processs Complete\n"); 235 | 236 | // Eat Packet 237 | pbuf_free(packetBuffer); 238 | return 1; 239 | } else if (destPort == reservedPort) { 240 | db_printf(DB_DEBUG, "tcpReceivedStatic() None Proxy (Reserved Port)\n"); 241 | 242 | } 243 | 244 | db_printf(DB_DEBUG, "tcpReceivedStatic() End - No Proxy this\n"); 245 | return 0; 246 | } 247 | 248 | bool TcpProxyServer_start() { 249 | struct in_addr address; 250 | 251 | db_printf(DB_DEBUG, "TcpProxyServer_start() Begin\n"); 252 | 253 | if(destServerIP.addr == 0x00000000) { 254 | // Unable to resolve hostname 255 | db_printf(DB_INFO, "TcpProxyServer_start() Resolve Server name fail\n"); 256 | return false; 257 | } 258 | 259 | address.s_addr=destServerIP.addr; 260 | db_printf(DB_DEBUG, "TcpProxyServer_start() Destination Server Address: %s\n", inet_ntoa(address)); 261 | 262 | tcpControlBlock = raw_new(IP_PROTO_TCP); 263 | if (tcpControlBlock == nullptr) { 264 | db_printf(DB_DEBUG, "TcpProxyServer_start() raw_new() fail\n"); 265 | return false; 266 | } 267 | 268 | // When LWIP detects a packet corresponding to specified protocol control 269 | // block, the GreReceivedStatic callback is executed 270 | raw_recv( tcpControlBlock, tcpReceivedStatic, NULL); 271 | 272 | // Selects the local interfaces where detection will be made. 273 | // In this case, all local interfaces 274 | raw_bind(tcpControlBlock, IP_ADDR_ANY); 275 | 276 | db_printf(DB_DEBUG, "TcpProxyServer_start() Success\n"); 277 | return true; 278 | } 279 | -------------------------------------------------------------------------------- /TcpProxyServer.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPPROXYSERVER_H 2 | #define TCPPROXYSERVER_H 3 | 4 | bool TcpProxyServer_begin(const char *destServer, unsigned short _reservedPort); 5 | bool TcpProxyServer_start(); 6 | bool TcpProxyServer_setReservedPort(unsigned short port); 7 | bool TcpProxyServer_setDestinationServer(const char *destServer); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /VpnTcpProxy.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | //#include 4 | #include "DeviceConfigWWW.h" 5 | #include "PPTP_Client.h" 6 | #include "TcpProxyServer.h" 7 | #include "DebugMsg.h" 8 | 9 | const int led = LED_BUILTIN; 10 | 11 | #define PPTP_PORT 1723 12 | 13 | struct DeviceConfig deviceConfigWeb; 14 | struct DeviceConfig deviceConfigRun; 15 | struct DeviceStatus deviceStatus; 16 | 17 | 18 | #define SAVECFG_REQ 0x01 19 | #define SAVECFG_REBOOT 0x02 20 | uint8_t saveConfigFlag; 21 | bool devEmergencyMode; 22 | bool pptpStatus = false; 23 | int debug = 1; 24 | uint32_t uptime_ms, uptime_sec; 25 | uint32_t ms; 26 | 27 | const char *ap_ssid = "VpnTcpProxy"; 28 | const char *ap_password = ""; 29 | void wifiModeAP() { 30 | WiFi.softAP(ap_ssid, ap_password); 31 | IPAddress IP = WiFi.softAPIP(); 32 | Serial.print("AP IP address: "); 33 | Serial.println(IP); 34 | } 35 | 36 | void setRestartTimeout(uint32_t tmout_after_sec) { 37 | deviceStatus.restart_timeout_sec = deviceStatus.uptime_sec + tmout_after_sec; 38 | printf("setRestartTimeout() input=%d, set restart when uptime is %d\n", tmout_after_sec, deviceStatus.restart_timeout_sec); 39 | } 40 | 41 | void setup(){ 42 | 43 | saveConfigFlag = 0; 44 | devEmergencyMode = false; 45 | pinMode(led, OUTPUT); 46 | digitalWrite(led, HIGH); 47 | 48 | Serial.begin(115200); 49 | Serial.print("\n"); 50 | Serial.print("ESP PPTP Client Version: "); 51 | Serial.print(__DATE__); 52 | Serial.print(", "); 53 | Serial.println(__TIME__); 54 | 55 | Serial.println("Reading Device Config From File"); 56 | Web_readDeviceConfig(&deviceConfigWeb); 57 | Web_bindDeviceStatus(&deviceStatus); 58 | memcpy(&deviceConfigRun, &deviceConfigWeb, sizeof(struct DeviceConfig)); 59 | printf("Device Config Data. . .\n"); 60 | printf("SSID : %s\n", deviceConfigRun.wifi_ssid); 61 | printf("Password : %s\n", deviceConfigRun.wifi_password); 62 | printf("PPTP Server: %s\n", deviceConfigRun.pptp_server); 63 | printf(" - User : %s\n", deviceConfigRun.pptp_user); 64 | printf(" - Pass : %s\n", deviceConfigRun.pptp_password); 65 | printf("Dest Server: %s\n", deviceConfigRun.tcp_destination); 66 | Serial.println("Read Device Config From File Complete"); 67 | 68 | WiFi.begin(deviceConfigRun.wifi_ssid, deviceConfigRun.wifi_password); 69 | if (WiFi.waitForConnectResult() != WL_CONNECTED) { 70 | Serial.printf("WiFi Failed! -> Change to AP Mode\n"); 71 | devEmergencyMode = true; 72 | wifiModeAP(); 73 | deviceStatus.wifi_mode = WIFI_MODE_AP; 74 | deviceStatus.wifi_ip.addr = WiFi.softAPIP(); 75 | deviceStatus.internet_connect = false; 76 | } else { 77 | Serial.printf("WiFi Connected!\n"); 78 | Serial.println(WiFi.localIP()); 79 | deviceStatus.wifi_mode = WIFI_MODE_STA; 80 | deviceStatus.wifi_ip.addr = WiFi.localIP(); 81 | deviceStatus.internet_connect = true; 82 | } 83 | 84 | if (devEmergencyMode == false) { 85 | Serial.println("PPTP Client Init"); 86 | PPTPC_init(deviceConfigRun.pptp_server, PPTP_PORT, deviceConfigRun.pptp_user, deviceConfigRun.pptp_password); 87 | pptpStatus = PPTPC_connect(); 88 | Serial.print("pptp connect return: "); 89 | Serial.println(pptpStatus); 90 | if (pptpStatus == true) { 91 | Serial.println("PPTP Client Init OK"); 92 | deviceStatus.vpn_connect = true; 93 | deviceStatus.vpn_local_ip.addr = localIP.addr; 94 | deviceStatus.vpn_remote_ip.addr = remoteIP.addr; 95 | } else { 96 | Serial.println("PPTP Client Init Fail, Set restart in 60 Sec"); 97 | setRestartTimeout(60); 98 | } 99 | } else { 100 | Serial.println("Skip PPTP Client Init in Emergency Mode"); 101 | } 102 | 103 | Serial.println("Web Config Init"); 104 | if(!SPIFFS.begin()){ 105 | Serial.println("An Error has occurred while mounting SPIFFS"); 106 | //return; 107 | } 108 | Web_init(); 109 | Serial.println("Web Config Init OK"); 110 | 111 | Serial.println("Tcp Proxy Init"); 112 | TcpProxyServer_begin( deviceConfigRun.tcp_destination, WEB_CONFIG_PORT); 113 | TcpProxyServer_start(); 114 | Serial.println("Tcp Proxy Init OK"); 115 | 116 | digitalWrite(led, LOW); 117 | Serial.println("Starting Loop..."); 118 | ms = millis(); 119 | } 120 | 121 | 122 | void handleUptime() { 123 | uint32_t tmp = millis(); 124 | if (tmp - uptime_ms > 1000) { 125 | uptime_ms = tmp; 126 | uptime_sec++; 127 | 128 | deviceStatus.uptime_sec = uptime_sec; 129 | 130 | if (deviceStatus.restart_timeout_sec == deviceStatus.uptime_sec) { 131 | Serial.println("Uptime == Restart Timeout -> Reboot ESP"); 132 | ESP.reset(); 133 | } 134 | 135 | deviceStatus.free_heap = ESP.getFreeHeap(); 136 | } 137 | } 138 | 139 | void handleDeviceConfig() { 140 | 141 | if (strcmp(deviceConfigWeb.wifi_ssid, deviceConfigRun.wifi_ssid) != 0){ 142 | strcpy(deviceConfigRun.wifi_ssid, deviceConfigWeb.wifi_ssid); 143 | printf("WiFi SSID Changed to %s\n", deviceConfigWeb.wifi_ssid); 144 | saveConfigFlag = SAVECFG_REQ | SAVECFG_REBOOT; 145 | } 146 | 147 | if (strcmp(deviceConfigWeb.wifi_password, deviceConfigRun.wifi_password) != 0){ 148 | strcpy(deviceConfigRun.wifi_password, deviceConfigWeb.wifi_password); 149 | printf("WiFi Password Changed to %s\n", deviceConfigWeb.wifi_password); 150 | saveConfigFlag = SAVECFG_REQ | SAVECFG_REBOOT; 151 | } 152 | 153 | if (strcmp(deviceConfigWeb.pptp_server, deviceConfigRun.pptp_server) != 0){ 154 | strcpy(deviceConfigRun.pptp_server, deviceConfigWeb.pptp_server); 155 | printf("PPTP Server Changed to %s\n", deviceConfigWeb.pptp_server); 156 | saveConfigFlag = SAVECFG_REQ | SAVECFG_REBOOT; 157 | } 158 | 159 | if (strcmp(deviceConfigWeb.pptp_user, deviceConfigRun.pptp_user) != 0){ 160 | strcpy(deviceConfigRun.pptp_user, deviceConfigWeb.pptp_user); 161 | printf("PPTP User Changed to %s\n", deviceConfigWeb.pptp_user); 162 | saveConfigFlag = SAVECFG_REQ | SAVECFG_REBOOT; 163 | } 164 | 165 | if (strcmp(deviceConfigWeb.pptp_password, deviceConfigRun.pptp_password) != 0){ 166 | strcpy(deviceConfigRun.pptp_password, deviceConfigWeb.pptp_password); 167 | printf("PPTP Password Changed to %s\n", deviceConfigWeb.pptp_password); 168 | saveConfigFlag = SAVECFG_REQ | SAVECFG_REBOOT; 169 | } 170 | 171 | 172 | 173 | if (strcmp(deviceConfigWeb.tcp_destination, deviceConfigRun.tcp_destination) != 0){ 174 | strcpy(deviceConfigRun.tcp_destination, deviceConfigWeb.tcp_destination); 175 | printf("TCP Destination Changed to %s\n", deviceConfigWeb.tcp_destination); 176 | TcpProxyServer_setDestinationServer(deviceConfigWeb.tcp_destination); 177 | saveConfigFlag = SAVECFG_REQ; 178 | } 179 | 180 | if (saveConfigFlag != 0) { 181 | if (saveConfigFlag & SAVECFG_REQ) { 182 | Web_saveDeviceConfig(&deviceConfigWeb); 183 | Serial.println("Save Device Config to flash successfully"); 184 | if (saveConfigFlag & SAVECFG_REBOOT) { 185 | Serial.println("Need to reboot to apply change -> Reboot ESP"); 186 | delay(2000); 187 | ESP.reset(); 188 | } 189 | } 190 | saveConfigFlag = 0; 191 | } 192 | } 193 | 194 | void loop(){ 195 | 196 | handleUptime(); 197 | handleDeviceConfig(); 198 | delay(50); // delay 50ms 199 | 200 | if (devEmergencyMode == false) { 201 | if (pptpStatus == true) // run only when pptp init success 202 | PPTPC_handle(); 203 | } else { 204 | if (millis() - ms > 5 * 60 * 1000) { // uptime > 5 minute 205 | Serial.println("Maximum runtime in Emergency Mode is 5 Minute -> Reboot ESP"); 206 | ESP.reset(); 207 | } 208 | } 209 | 210 | if (Serial.available() > 0) { 211 | int ch = Serial.read(); 212 | if (ch == 'r') { 213 | Serial.println("Serial request to Reset Configuration and Reboot"); 214 | SPIFFS.remove("/deviceconfig.bin"); 215 | delay(1000); 216 | ESP.reset(); 217 | } 218 | Serial.print("Change Debug Message to "); 219 | if (debug == 1) debug = 10; 220 | else debug = 1; 221 | Serial.println(debug); 222 | db_setLevel(debug); 223 | } 224 | while (Serial.available() > 0) Serial.read(); 225 | 226 | if ( (WiFi.status() != WL_CONNECTED) && (devEmergencyMode == false) ) { 227 | Serial.println("WiFi Disconnected -> Reboot ESP"); 228 | ESP.reset(); 229 | } 230 | 231 | 232 | } 233 | -------------------------------------------------------------------------------- /VpnTcpProxy.ino.nodemcu.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/VpnTcpProxy.ino.nodemcu.bin -------------------------------------------------------------------------------- /data/avatar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/data/avatar2.png -------------------------------------------------------------------------------- /data/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/data/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /data/raleway.css: -------------------------------------------------------------------------------- 1 | /* cyrillic-ext */ 2 | @font-face { 3 | font-family: 'Raleway'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url(raleway.woff2) format('woff2'); 7 | unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; 8 | } 9 | /* cyrillic */ 10 | @font-face { 11 | font-family: 'Raleway'; 12 | font-style: normal; 13 | font-weight: 400; 14 | src: url(raleway.woff2) format('woff2'); 15 | unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; 16 | } 17 | /* vietnamese */ 18 | @font-face { 19 | font-family: 'Raleway'; 20 | font-style: normal; 21 | font-weight: 400; 22 | src: url(raleway.woff2) format('woff2'); 23 | unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; 24 | } 25 | /* latin-ext */ 26 | @font-face { 27 | font-family: 'Raleway'; 28 | font-style: normal; 29 | font-weight: 400; 30 | src: url(raleway.woff2) format('woff2'); 31 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 32 | } 33 | /* latin */ 34 | @font-face { 35 | font-family: 'Raleway'; 36 | font-style: normal; 37 | font-weight: 400; 38 | src: url(raleway.woff2) format('woff2'); 39 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 40 | } 41 | -------------------------------------------------------------------------------- /data/raleway.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sun89/VpnTcpProxy/9f2b105cd54d3ef0ff2a1b5fdeb32d8f5245c1f6/data/raleway.woff2 -------------------------------------------------------------------------------- /data/setting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VPN TCP Proxy 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 24 | 25 |
26 | 27 | VPN TCP Proxy - Setting 28 |
29 | 30 | 31 | 54 | 55 | 56 | 57 |
58 | 59 | 60 |
61 | 62 | 63 |
64 |
Setting
65 |
66 | 67 | 68 |
69 |
70 |
WiFi
71 | 72 |
73 |
74 | 75 | 76 |
77 |
78 | 79 | 80 |
81 |
82 | 83 |
PPTP
84 |
85 |
86 | 87 | 88 |
89 |
90 | 91 | 92 |
93 |
94 | 95 | 96 |
97 |
98 | 99 |
Destination Server (For TCP Proxy)
100 |
101 |
102 | 103 | 104 |
105 |
106 | 107 |
108 |
109 |
110 | 113 |
114 | 121 |
122 |
123 |
124 |
125 | 126 | 127 | 128 | 129 | 132 | 133 | 134 |
135 | 136 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /data/w3.css: -------------------------------------------------------------------------------- 1 | /* W3.CSS 4.14 November 2020 by Jan Egil and Borge Refsnes */ 2 | html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit} 3 | /* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */ 4 | html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0} 5 | article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item} 6 | audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline} 7 | audio:not([controls]){display:none;height:0}[hidden],template{display:none} 8 | a{background-color:transparent}a:active,a:hover{outline-width:0} 9 | abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted} 10 | b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000} 11 | small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} 12 | sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none} 13 | code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible} 14 | button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold} 15 | button,input{overflow:visible}button,select{text-transform:none} 16 | button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button} 17 | button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0} 18 | button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText} 19 | fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} 20 | legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto} 21 | [type=checkbox],[type=radio]{padding:0} 22 | [type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto} 23 | [type=search]{-webkit-appearance:textfield;outline-offset:-2px} 24 | [type=search]::-webkit-search-decoration{-webkit-appearance:none} 25 | ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit} 26 | /* End extract */ 27 | html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden} 28 | h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}.w3-serif{font-family:serif} 29 | h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px} 30 | hr{border:0;border-top:1px solid #eee;margin:20px 0} 31 | .w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit} 32 | .w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc} 33 | .w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1} 34 | .w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1} 35 | .w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center} 36 | .w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top} 37 | .w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px} 38 | .w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap} 39 | .w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)} 40 | .w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} 41 | .w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none} 42 | .w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none} 43 | .w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%} 44 | .w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none} 45 | .w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block} 46 | .w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s} 47 | .w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%} 48 | .w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc} 49 | .w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer} 50 | .w3-dropdown-hover:hover .w3-dropdown-content{display:block} 51 | .w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000} 52 | .w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000} 53 | .w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1} 54 | .w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px} 55 | .w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto} 56 | .w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%} 57 | .w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%} 58 | .w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px} 59 | .w3-main,#main{transition:margin-left .4s} 60 | .w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)} 61 | .w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px} 62 | .w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto} 63 | .w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0} 64 | .w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left} 65 | .w3-bar .w3-button{white-space:normal} 66 | .w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0} 67 | .w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%} 68 | .w3-responsive{display:block;overflow-x:auto} 69 | .w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before, 70 | .w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both} 71 | .w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%} 72 | .w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%} 73 | .w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%} 74 | .w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%} 75 | @media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%} 76 | .w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%} 77 | .w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}} 78 | @media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%} 79 | .w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%} 80 | .w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}} 81 | .w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px} 82 | .w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px} 83 | .w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell} 84 | .w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom} 85 | .w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important} 86 | @media (max-width:1205px){.w3-auto{max-width:95%}} 87 | @media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px} 88 | .w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative} 89 | .w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center} 90 | .w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}} 91 | @media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}} 92 | @media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}} 93 | @media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}} 94 | @media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}} 95 | .w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0} 96 | .w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2} 97 | .w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0} 98 | .w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0} 99 | .w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)} 100 | .w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)} 101 | .w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)} 102 | .w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} 103 | .w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)} 104 | .w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none} 105 | .w3-display-position{position:absolute} 106 | .w3-circle{border-radius:50%} 107 | .w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px} 108 | .w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px} 109 | .w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px} 110 | .w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px} 111 | .w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word} 112 | .w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%} 113 | .w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)} 114 | .w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)} 115 | .w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}} 116 | .w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}} 117 | .w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}} 118 | .w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}} 119 | .w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}} 120 | .w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}} 121 | .w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}} 122 | .w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}} 123 | .w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important} 124 | .w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1} 125 | .w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75} 126 | .w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)} 127 | .w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)} 128 | .w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)} 129 | .w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important} 130 | .w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important} 131 | .w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important} 132 | .w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important} 133 | .w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important} 134 | .w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important} 135 | .w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important} 136 | .w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important} 137 | .w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important} 138 | .w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important} 139 | .w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important} 140 | .w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important} 141 | .w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important} 142 | .w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important} 143 | .w3-padding-64{padding-top:64px!important;padding-bottom:64px!important} 144 | .w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important} 145 | .w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important} 146 | .w3-left{float:left!important}.w3-right{float:right!important} 147 | .w3-button:hover{color:#000!important;background-color:#ccc!important} 148 | .w3-transparent,.w3-hover-none:hover{background-color:transparent!important} 149 | .w3-hover-none:hover{box-shadow:none!important} 150 | /* Colors */ 151 | .w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important} 152 | .w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important} 153 | .w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important} 154 | .w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important} 155 | .w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important} 156 | .w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important} 157 | .w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important} 158 | .w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important} 159 | .w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important} 160 | .w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important} 161 | .w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important} 162 | .w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important} 163 | .w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important} 164 | .w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important} 165 | .w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important} 166 | .w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important} 167 | .w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important} 168 | .w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important} 169 | .w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important} 170 | .w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important} 171 | .w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important} 172 | .w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important} 173 | .w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important} 174 | .w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important} 175 | .w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important} 176 | .w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important} 177 | .w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important} 178 | .w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important} 179 | .w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important} 180 | .w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important} 181 | .w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important} 182 | .w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important} 183 | .w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important} 184 | .w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important} 185 | .w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important} 186 | .w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important} 187 | .w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important} 188 | .w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important} 189 | .w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important} 190 | .w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important} 191 | .w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important} 192 | .w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important} 193 | .w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important} 194 | .w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important} 195 | .w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important} 196 | .w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important} 197 | .w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important} 198 | .w3-text-red,.w3-hover-text-red:hover{color:#f44336!important} 199 | .w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important} 200 | .w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important} 201 | .w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important} 202 | .w3-text-white,.w3-hover-text-white:hover{color:#fff!important} 203 | .w3-text-black,.w3-hover-text-black:hover{color:#000!important} 204 | .w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important} 205 | .w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important} 206 | .w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important} 207 | .w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important} 208 | .w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important} 209 | .w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important} 210 | .w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important} 211 | .w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important} 212 | .w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important} 213 | .w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important} 214 | .w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important} 215 | .w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important} 216 | .w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important} 217 | .w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important} 218 | .w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important} 219 | .w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important} 220 | .w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important} 221 | .w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important} 222 | .w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important} 223 | .w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important} 224 | .w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important} 225 | .w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important} 226 | .w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important} 227 | .w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important} 228 | .w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important} 229 | .w3-border-black,.w3-hover-border-black:hover{border-color:#000!important} 230 | .w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important} 231 | .w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important} 232 | .w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important} 233 | .w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important} 234 | .w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important} -------------------------------------------------------------------------------- /des.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: des.c 3 | * Author: Brad Conte (brad AT radconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the DES encryption algorithm. 7 | Modes of operation (such as CBC) are not included. 8 | The formal NIST algorithm specification can be found here: 9 | * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf 10 | *********************************************************************/ 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | //#include 15 | #include "des.h" 16 | 17 | /****************************** MACROS ******************************/ 18 | // Obtain bit "b" from the left and shift it "c" places from the right 19 | #define BITNUM(a,b,c) (((a[(b)/8] >> (7 - (b%8))) & 0x01) << (c)) 20 | #define BITNUMINTR(a,b,c) ((((a) >> (31 - (b))) & 0x00000001) << (c)) 21 | #define BITNUMINTL(a,b,c) ((((a) << (b)) & 0x80000000) >> (c)) 22 | 23 | // This macro converts a 6 bit block with the S-Box row defined as the first and last 24 | // bits to a 6 bit block with the row defined by the first two bits. 25 | #define SBOXBIT(a) (((a) & 0x20) | (((a) & 0x1f) >> 1) | (((a) & 0x01) << 4)) 26 | 27 | /**************************** VARIABLES *****************************/ 28 | static const BYTE sbox1[64] = { 29 | 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 30 | 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 31 | 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 32 | 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 33 | }; 34 | static const BYTE sbox2[64] = { 35 | 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 36 | 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 37 | 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 38 | 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 39 | }; 40 | static const BYTE sbox3[64] = { 41 | 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 42 | 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 43 | 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 44 | 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 45 | }; 46 | static const BYTE sbox4[64] = { 47 | 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 48 | 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 49 | 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 50 | 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 51 | }; 52 | static const BYTE sbox5[64] = { 53 | 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 54 | 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 55 | 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 56 | 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 57 | }; 58 | static const BYTE sbox6[64] = { 59 | 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 60 | 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 61 | 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 62 | 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 63 | }; 64 | static const BYTE sbox7[64] = { 65 | 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 66 | 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 67 | 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 68 | 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 69 | }; 70 | static const BYTE sbox8[64] = { 71 | 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 72 | 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 73 | 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 74 | 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 75 | }; 76 | 77 | /*********************** FUNCTION DEFINITIONS ***********************/ 78 | // Initial (Inv)Permutation step 79 | void IP(WORD state[], const BYTE in[]) 80 | { 81 | state[0] = BITNUM(in,57,31) | BITNUM(in,49,30) | BITNUM(in,41,29) | BITNUM(in,33,28) | 82 | BITNUM(in,25,27) | BITNUM(in,17,26) | BITNUM(in,9,25) | BITNUM(in,1,24) | 83 | BITNUM(in,59,23) | BITNUM(in,51,22) | BITNUM(in,43,21) | BITNUM(in,35,20) | 84 | BITNUM(in,27,19) | BITNUM(in,19,18) | BITNUM(in,11,17) | BITNUM(in,3,16) | 85 | BITNUM(in,61,15) | BITNUM(in,53,14) | BITNUM(in,45,13) | BITNUM(in,37,12) | 86 | BITNUM(in,29,11) | BITNUM(in,21,10) | BITNUM(in,13,9) | BITNUM(in,5,8) | 87 | BITNUM(in,63,7) | BITNUM(in,55,6) | BITNUM(in,47,5) | BITNUM(in,39,4) | 88 | BITNUM(in,31,3) | BITNUM(in,23,2) | BITNUM(in,15,1) | BITNUM(in,7,0); 89 | 90 | state[1] = BITNUM(in,56,31) | BITNUM(in,48,30) | BITNUM(in,40,29) | BITNUM(in,32,28) | 91 | BITNUM(in,24,27) | BITNUM(in,16,26) | BITNUM(in,8,25) | BITNUM(in,0,24) | 92 | BITNUM(in,58,23) | BITNUM(in,50,22) | BITNUM(in,42,21) | BITNUM(in,34,20) | 93 | BITNUM(in,26,19) | BITNUM(in,18,18) | BITNUM(in,10,17) | BITNUM(in,2,16) | 94 | BITNUM(in,60,15) | BITNUM(in,52,14) | BITNUM(in,44,13) | BITNUM(in,36,12) | 95 | BITNUM(in,28,11) | BITNUM(in,20,10) | BITNUM(in,12,9) | BITNUM(in,4,8) | 96 | BITNUM(in,62,7) | BITNUM(in,54,6) | BITNUM(in,46,5) | BITNUM(in,38,4) | 97 | BITNUM(in,30,3) | BITNUM(in,22,2) | BITNUM(in,14,1) | BITNUM(in,6,0); 98 | } 99 | 100 | void InvIP(WORD state[], BYTE in[]) 101 | { 102 | in[0] = BITNUMINTR(state[1],7,7) | BITNUMINTR(state[0],7,6) | BITNUMINTR(state[1],15,5) | 103 | BITNUMINTR(state[0],15,4) | BITNUMINTR(state[1],23,3) | BITNUMINTR(state[0],23,2) | 104 | BITNUMINTR(state[1],31,1) | BITNUMINTR(state[0],31,0); 105 | 106 | in[1] = BITNUMINTR(state[1],6,7) | BITNUMINTR(state[0],6,6) | BITNUMINTR(state[1],14,5) | 107 | BITNUMINTR(state[0],14,4) | BITNUMINTR(state[1],22,3) | BITNUMINTR(state[0],22,2) | 108 | BITNUMINTR(state[1],30,1) | BITNUMINTR(state[0],30,0); 109 | 110 | in[2] = BITNUMINTR(state[1],5,7) | BITNUMINTR(state[0],5,6) | BITNUMINTR(state[1],13,5) | 111 | BITNUMINTR(state[0],13,4) | BITNUMINTR(state[1],21,3) | BITNUMINTR(state[0],21,2) | 112 | BITNUMINTR(state[1],29,1) | BITNUMINTR(state[0],29,0); 113 | 114 | in[3] = BITNUMINTR(state[1],4,7) | BITNUMINTR(state[0],4,6) | BITNUMINTR(state[1],12,5) | 115 | BITNUMINTR(state[0],12,4) | BITNUMINTR(state[1],20,3) | BITNUMINTR(state[0],20,2) | 116 | BITNUMINTR(state[1],28,1) | BITNUMINTR(state[0],28,0); 117 | 118 | in[4] = BITNUMINTR(state[1],3,7) | BITNUMINTR(state[0],3,6) | BITNUMINTR(state[1],11,5) | 119 | BITNUMINTR(state[0],11,4) | BITNUMINTR(state[1],19,3) | BITNUMINTR(state[0],19,2) | 120 | BITNUMINTR(state[1],27,1) | BITNUMINTR(state[0],27,0); 121 | 122 | in[5] = BITNUMINTR(state[1],2,7) | BITNUMINTR(state[0],2,6) | BITNUMINTR(state[1],10,5) | 123 | BITNUMINTR(state[0],10,4) | BITNUMINTR(state[1],18,3) | BITNUMINTR(state[0],18,2) | 124 | BITNUMINTR(state[1],26,1) | BITNUMINTR(state[0],26,0); 125 | 126 | in[6] = BITNUMINTR(state[1],1,7) | BITNUMINTR(state[0],1,6) | BITNUMINTR(state[1],9,5) | 127 | BITNUMINTR(state[0],9,4) | BITNUMINTR(state[1],17,3) | BITNUMINTR(state[0],17,2) | 128 | BITNUMINTR(state[1],25,1) | BITNUMINTR(state[0],25,0); 129 | 130 | in[7] = BITNUMINTR(state[1],0,7) | BITNUMINTR(state[0],0,6) | BITNUMINTR(state[1],8,5) | 131 | BITNUMINTR(state[0],8,4) | BITNUMINTR(state[1],16,3) | BITNUMINTR(state[0],16,2) | 132 | BITNUMINTR(state[1],24,1) | BITNUMINTR(state[0],24,0); 133 | } 134 | 135 | WORD f(WORD state, const BYTE key[]) 136 | { 137 | BYTE lrgstate[6]; //,i; 138 | WORD t1,t2; 139 | 140 | // Expantion Permutation 141 | t1 = BITNUMINTL(state,31,0) | ((state & 0xf0000000) >> 1) | BITNUMINTL(state,4,5) | 142 | BITNUMINTL(state,3,6) | ((state & 0x0f000000) >> 3) | BITNUMINTL(state,8,11) | 143 | BITNUMINTL(state,7,12) | ((state & 0x00f00000) >> 5) | BITNUMINTL(state,12,17) | 144 | BITNUMINTL(state,11,18) | ((state & 0x000f0000) >> 7) | BITNUMINTL(state,16,23); 145 | 146 | t2 = BITNUMINTL(state,15,0) | ((state & 0x0000f000) << 15) | BITNUMINTL(state,20,5) | 147 | BITNUMINTL(state,19,6) | ((state & 0x00000f00) << 13) | BITNUMINTL(state,24,11) | 148 | BITNUMINTL(state,23,12) | ((state & 0x000000f0) << 11) | BITNUMINTL(state,28,17) | 149 | BITNUMINTL(state,27,18) | ((state & 0x0000000f) << 9) | BITNUMINTL(state,0,23); 150 | 151 | lrgstate[0] = (t1 >> 24) & 0x000000ff; 152 | lrgstate[1] = (t1 >> 16) & 0x000000ff; 153 | lrgstate[2] = (t1 >> 8) & 0x000000ff; 154 | lrgstate[3] = (t2 >> 24) & 0x000000ff; 155 | lrgstate[4] = (t2 >> 16) & 0x000000ff; 156 | lrgstate[5] = (t2 >> 8) & 0x000000ff; 157 | 158 | // Key XOR 159 | lrgstate[0] ^= key[0]; 160 | lrgstate[1] ^= key[1]; 161 | lrgstate[2] ^= key[2]; 162 | lrgstate[3] ^= key[3]; 163 | lrgstate[4] ^= key[4]; 164 | lrgstate[5] ^= key[5]; 165 | 166 | // S-Box Permutation 167 | state = (sbox1[SBOXBIT(lrgstate[0] >> 2)] << 28) | 168 | (sbox2[SBOXBIT(((lrgstate[0] & 0x03) << 4) | (lrgstate[1] >> 4))] << 24) | 169 | (sbox3[SBOXBIT(((lrgstate[1] & 0x0f) << 2) | (lrgstate[2] >> 6))] << 20) | 170 | (sbox4[SBOXBIT(lrgstate[2] & 0x3f)] << 16) | 171 | (sbox5[SBOXBIT(lrgstate[3] >> 2)] << 12) | 172 | (sbox6[SBOXBIT(((lrgstate[3] & 0x03) << 4) | (lrgstate[4] >> 4))] << 8) | 173 | (sbox7[SBOXBIT(((lrgstate[4] & 0x0f) << 2) | (lrgstate[5] >> 6))] << 4) | 174 | sbox8[SBOXBIT(lrgstate[5] & 0x3f)]; 175 | 176 | // P-Box Permutation 177 | state = BITNUMINTL(state,15,0) | BITNUMINTL(state,6,1) | BITNUMINTL(state,19,2) | 178 | BITNUMINTL(state,20,3) | BITNUMINTL(state,28,4) | BITNUMINTL(state,11,5) | 179 | BITNUMINTL(state,27,6) | BITNUMINTL(state,16,7) | BITNUMINTL(state,0,8) | 180 | BITNUMINTL(state,14,9) | BITNUMINTL(state,22,10) | BITNUMINTL(state,25,11) | 181 | BITNUMINTL(state,4,12) | BITNUMINTL(state,17,13) | BITNUMINTL(state,30,14) | 182 | BITNUMINTL(state,9,15) | BITNUMINTL(state,1,16) | BITNUMINTL(state,7,17) | 183 | BITNUMINTL(state,23,18) | BITNUMINTL(state,13,19) | BITNUMINTL(state,31,20) | 184 | BITNUMINTL(state,26,21) | BITNUMINTL(state,2,22) | BITNUMINTL(state,8,23) | 185 | BITNUMINTL(state,18,24) | BITNUMINTL(state,12,25) | BITNUMINTL(state,29,26) | 186 | BITNUMINTL(state,5,27) | BITNUMINTL(state,21,28) | BITNUMINTL(state,10,29) | 187 | BITNUMINTL(state,3,30) | BITNUMINTL(state,24,31); 188 | 189 | // Return the final state value 190 | return(state); 191 | } 192 | 193 | void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode) 194 | { 195 | WORD i, j, to_gen, C, D; 196 | const WORD key_rnd_shift[16] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; 197 | const WORD key_perm_c[28] = {56,48,40,32,24,16,8,0,57,49,41,33,25,17, 198 | 9,1,58,50,42,34,26,18,10,2,59,51,43,35}; 199 | const WORD key_perm_d[28] = {62,54,46,38,30,22,14,6,61,53,45,37,29,21, 200 | 13,5,60,52,44,36,28,20,12,4,27,19,11,3}; 201 | const WORD key_compression[48] = {13,16,10,23,0,4,2,27,14,5,20,9, 202 | 22,18,11,3,25,7,15,6,26,19,12,1, 203 | 40,51,30,36,46,54,29,39,50,44,32,47, 204 | 43,48,38,55,33,52,45,41,49,35,28,31}; 205 | 206 | // Permutated Choice #1 (copy the key in, ignoring parity bits). 207 | for (i = 0, j = 31, C = 0; i < 28; ++i, --j) 208 | C |= BITNUM(key,key_perm_c[i],j); 209 | for (i = 0, j = 31, D = 0; i < 28; ++i, --j) 210 | D |= BITNUM(key,key_perm_d[i],j); 211 | 212 | // Generate the 16 subkeys. 213 | for (i = 0; i < 16; ++i) { 214 | C = ((C << key_rnd_shift[i]) | (C >> (28-key_rnd_shift[i]))) & 0xfffffff0; 215 | D = ((D << key_rnd_shift[i]) | (D >> (28-key_rnd_shift[i]))) & 0xfffffff0; 216 | 217 | // Decryption subkeys are reverse order of encryption subkeys so 218 | // generate them in reverse if the key schedule is for decryption useage. 219 | if (mode == DES_DECRYPT) 220 | to_gen = 15 - i; 221 | else /*(if mode == DES_ENCRYPT)*/ 222 | to_gen = i; 223 | // Initialize the array 224 | for (j = 0; j < 6; ++j) 225 | schedule[to_gen][j] = 0; 226 | for (j = 0; j < 24; ++j) 227 | schedule[to_gen][j/8] |= BITNUMINTR(C,key_compression[j],7 - (j%8)); 228 | for ( ; j < 48; ++j) 229 | schedule[to_gen][j/8] |= BITNUMINTR(D,key_compression[j] - 28,7 - (j%8)); 230 | } 231 | } 232 | 233 | void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]) 234 | { 235 | WORD state[2],idx,t; 236 | 237 | IP(state,in); 238 | 239 | for (idx=0; idx < 15; ++idx) { 240 | t = state[1]; 241 | state[1] = f(state[1],key[idx]) ^ state[0]; 242 | state[0] = t; 243 | } 244 | // Perform the final loop manually as it doesn't switch sides 245 | state[0] = f(state[1],key[15]) ^ state[0]; 246 | 247 | InvIP(state,out); 248 | } 249 | 250 | void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode) 251 | { 252 | if (mode == DES_ENCRYPT) { 253 | des_key_setup(&key[0],schedule[0],mode); 254 | des_key_setup(&key[8],schedule[1], DES_DECRYPT); 255 | des_key_setup(&key[16],schedule[2],mode); 256 | } 257 | else /*if (mode == DES_DECRYPT*/ { 258 | des_key_setup(&key[16],schedule[0],mode); 259 | des_key_setup(&key[8],schedule[1], DES_ENCRYPT); 260 | des_key_setup(&key[0],schedule[2],mode); 261 | } 262 | } 263 | 264 | void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]) 265 | { 266 | des_crypt(in,out,key[0]); 267 | des_crypt(out,out,key[1]); 268 | des_crypt(out,out,key[2]); 269 | } 270 | -------------------------------------------------------------------------------- /des.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: des.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding DES implementation. 7 | Note that encryption and decryption are defined by how 8 | the key setup is performed, the actual en/de-cryption is 9 | performed by the same function. 10 | *********************************************************************/ 11 | 12 | #ifndef DES_H 13 | #define DESH 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /*************************** HEADER FILES ***************************/ 20 | #include 21 | 22 | /****************************** MACROS ******************************/ 23 | #define DES_BLOCK_SIZE 8 // DES operates on 8 bytes at a time 24 | 25 | /**************************** DATA TYPES ****************************/ 26 | typedef unsigned char BYTE; // 8-bit byte 27 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines 28 | 29 | typedef enum { 30 | DES_ENCRYPT = 0, 31 | DES_DECRYPT 32 | } DES_MODE; 33 | 34 | /*********************** FUNCTION DECLARATIONS **********************/ 35 | void des_key_setup(const BYTE key[], BYTE schedule[][6], DES_MODE mode); 36 | void des_crypt(const BYTE in[], BYTE out[], const BYTE key[][6]); 37 | 38 | void three_des_key_setup(const BYTE key[], BYTE schedule[][16][6], DES_MODE mode); 39 | void three_des_crypt(const BYTE in[], BYTE out[], const BYTE key[][16][6]); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | #endif // DES_H 45 | -------------------------------------------------------------------------------- /md4.c: -------------------------------------------------------------------------------- 1 | /* ========================================================================== ** 2 | * 3 | * MD4.c 4 | * 5 | * Copyright: 6 | * Copyright (C) 2003-2005 by Christopher R. Hertel 7 | * 8 | * Email: crh@ubiqx.mn.org 9 | * 10 | * $Id: MD4.c,v 0.11 2005/06/08 18:35:05 crh Exp $ 11 | * 12 | * -------------------------------------------------------------------------- ** 13 | * 14 | * Description: 15 | * Implements the MD4 hash algorithm, as described in RFC 1320. 16 | * 17 | * -------------------------------------------------------------------------- ** 18 | * 19 | * License: 20 | * 21 | * This library is free software; you can redistribute it and/or 22 | * modify it under the terms of the GNU Lesser General Public 23 | * License as published by the Free Software Foundation; either 24 | * version 2.1 of the License, or (at your option) any later version. 25 | * 26 | * This library is distributed in the hope that it will be useful, 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 29 | * Lesser General Public License for more details. 30 | * 31 | * You should have received a copy of the GNU Lesser General Public 32 | * License along with this library; if not, write to the Free Software 33 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 34 | * 35 | * -------------------------------------------------------------------------- ** 36 | * 37 | * Notes: 38 | * 39 | * None of this will make any sense unless you're studying RFC 1320 as you 40 | * read the code. 41 | * 42 | * MD4 is described in RFC 1320 (which superceeds RFC 1186). 43 | * The MD*5* algorithm is described in RFC 1321 (that's 1320 + 1). 44 | * MD4 is very similar to MD5, but not quite similar enough to justify 45 | * putting the two into a single module. 46 | * 47 | * There are three primary motivations for this particular implementation. 48 | * 1) Programmer's pride. I wanted to be able to say I'd done it, and I 49 | * wanted to learn from the experience. 50 | * 2) Portability. I wanted an implementation that I knew to be portable 51 | * to a reasonable number of platforms. In particular, the algorithm 52 | * is designed with little-endian platforms in mind, and I wanted an 53 | * endian-agnostic implementation. 54 | * 3) Compactness. While not an overriding goal, I thought it worth-while 55 | * to see if I could reduce the overall size of the result. This is in 56 | * keeping with my hopes that this library will be suitable for use in 57 | * some embedded environments. 58 | * Beyond that, cleanliness and clarity are always worth pursuing. 59 | * 60 | * As mentioned above, the code really only makes sense if you are familiar 61 | * with the MD4 algorithm or are using RFC 1320 as a guide. This code is 62 | * quirky, however, so you'll want to be reading carefully. 63 | * 64 | * -------------------------------------------------------------------------- ** 65 | * 66 | * References: 67 | * IETF RFC 1320: The MD4 Message-Digest Algorithm 68 | * Ron Rivest. IETF, April, 1992 69 | * 70 | * ========================================================================== ** 71 | */ 72 | 73 | #include "MD4.h" 74 | #include 75 | 76 | /* -------------------------------------------------------------------------- ** 77 | * Static Constants: 78 | * 79 | * Round2_k - In each round, there is a value (given the label 'k') used 80 | * to indicate the longword stored in X[] that should be used 81 | * per iteration. In round one, k is simply 0, 1, 2, ... but 82 | * it gets a little more complicated in round 2. I actually 83 | * figured out a simple algorithm to calculate the correct k 84 | * in round 2, but storing the sequence seems much easier. 85 | * 86 | * Round3_k - There's probably a way to calculate this sequence on the 87 | * fly as well. Storing it seems easier and quicker (from 88 | * a running program point of view). 89 | * 90 | * S[][] - In each round there is a left rotate operation performed as 91 | * part of the 16 permutations. The number of bits varies in 92 | * a repeating patter. This array keeps track of the patterns 93 | * used in each round. 94 | */ 95 | 96 | static const uint8_t Round2_k[] = 97 | { 98 | 0, 4, 8, 12, 99 | 1, 5, 9, 13, 100 | 2, 6, 10, 14, 101 | 3, 7, 11, 15 102 | }; 103 | 104 | static const uint8_t Round3_k[] = 105 | { 106 | 0, 8, 4, 12, 107 | 2, 10, 6, 14, 108 | 1, 9, 5, 13, 109 | 3, 11, 7, 15 110 | }; 111 | 112 | static const uint8_t S[3][4] = 113 | { 114 | { 3, 7, 11, 19 }, /* round 0 */ 115 | { 3, 5, 9, 13 }, /* round 1 */ 116 | { 3, 9, 11, 15 } /* round 2 */ 117 | }; 118 | 119 | 120 | /* -------------------------------------------------------------------------- ** 121 | * Macros: 122 | * md4F(), md4G(), and md4H() are described in RFC 1320. 123 | * All of these operations are bitwise, and so not impacted by endian-ness. 124 | * 125 | * GetLongByte() 126 | * Extract one byte from a (32-bit) longword. A value of 0 for 127 | * indicates the lowest order byte, while 3 indicates the highest order 128 | * byte. 129 | * 130 | */ 131 | 132 | #define md4F( X, Y, Z ) ( ((X) & (Y)) | ((~(X)) & (Z)) ) 133 | #define md4G( X, Y, Z ) ( ((X) & (Y)) | ((X) & (Z)) | ((Y) & (Z)) ) 134 | #define md4H( X, Y, Z ) ( (X) ^ (Y) ^ (Z) ) 135 | 136 | #define GetLongByte( L, idx ) ((uchar)(( L >> (((idx) & 0x03) << 3) ) & 0xFF)) 137 | 138 | 139 | /* -------------------------------------------------------------------------- ** 140 | * Static Functions: 141 | */ 142 | 143 | static void Permute( uint32_t ABCD[4], const uchar block[64] ) 144 | /* ------------------------------------------------------------------------ ** 145 | * Permute the ABCD "registers" using the 64-byte as a driver. 146 | * 147 | * Input: ABCD - Pointer to an array of four unsigned longwords. 148 | * block - An array of bytes, 64 bytes in size. 149 | * 150 | * Output: none. 151 | * 152 | * Notes: The MD4 algorithm operates on a set of four longwords stored 153 | * (conceptually) in four "registers". It is easy to imagine a 154 | * simple MD4 chip that would operate this way. In any case, 155 | * the mangling of the contents of those registers is driven by 156 | * the input message. The message is chopped into 64-byte chunks 157 | * and each chunk is used to manipulate the contents of the 158 | * registers. 159 | * 160 | * The MD4 Algorithm also calls for padding the input to ensure 161 | * that it is a multiple of 64 bytes in length. The last 16 162 | * bytes of the padding space are used to store the message 163 | * length (the length of the original message, before padding, 164 | * expressed in terms of bits). If there is not enough room 165 | * for 16 bytes worth of bitcount (eg., if the original message 166 | * was 122 bytes long) then the block is padded to the end with 167 | * zeros and passed to this function. Then *another* block is 168 | * filled with zeros except for the last 16 bytes which contain 169 | * the length. 170 | * 171 | * Oh... and the algorithm requires that there be at least one 172 | * padding byte. The first padding byte has a value of 0x80, 173 | * and any others are 0x00. 174 | * 175 | * ------------------------------------------------------------------------ ** 176 | */ 177 | { 178 | int round; 179 | int i, j; 180 | uint8_t s; 181 | uint32_t a, b, c, d; 182 | uint32_t KeepABCD[4]; 183 | uint32_t X[16]; 184 | 185 | /* Store the current ABCD values for later re-use. 186 | */ 187 | for( i = 0; i < 4; i++ ) 188 | KeepABCD[i] = ABCD[i]; 189 | 190 | /* Convert the input block into an array of unsigned longs, taking care 191 | * to read the block in Little Endian order (the algorithm assumes this). 192 | * The uint32_t values are then handled in host order. 193 | */ 194 | for( i = 0, j = 0; i < 16; i++ ) 195 | { 196 | X[i] = (uint32_t)block[j++]; 197 | X[i] |= ((uint32_t)block[j++] << 8); 198 | X[i] |= ((uint32_t)block[j++] << 16); 199 | X[i] |= ((uint32_t)block[j++] << 24); 200 | } 201 | 202 | /* This loop performs the three rounds of permutation. 203 | * The three rounds are each very similar. The differences are in three 204 | * areas: 205 | * - The function (F, G, or H) used to perform bitwise permutations on 206 | * the registers, 207 | * - The order in which values from X[] are chosen. 208 | * - Changes to the number of bits by which the registers are rotated. 209 | * This implementation uses a switch statement to deal with some of the 210 | * differences between rounds. Other differences are handled by storing 211 | * values in arrays and using the round number to select the correct set 212 | * of values. 213 | * 214 | * (My implementation appears to be a poor compromise between speed, size, 215 | * and clarity. Ugh. [crh]) 216 | */ 217 | for( round = 0; round < 3; round++ ) 218 | { 219 | for( i = 0; i < 16; i++ ) 220 | { 221 | j = (4 - (i % 4)) & 0x3; /* handles the rotation of ABCD. */ 222 | s = S[round][i%4]; /* is the bit shift for this iteration. */ 223 | 224 | b = ABCD[(j+1) & 0x3]; /* Copy the b,c,d values per ABCD rotation. */ 225 | c = ABCD[(j+2) & 0x3]; /* This isn't really necessary, it just looks */ 226 | d = ABCD[(j+3) & 0x3]; /* clean & will hopefully be optimized away. */ 227 | 228 | /* The actual perumation function. 229 | * This is broken out to minimize the code within the switch(). 230 | */ 231 | switch( round ) 232 | { 233 | case 0: 234 | /* round 1 */ 235 | a = md4F( b, c, d ) + X[i]; 236 | break; 237 | case 1: 238 | /* round 2 */ 239 | /* The value 0x5A827999 is trunc( (2^30) * sqrt(2) ). */ 240 | a = md4G( b, c, d ) + X[ Round2_k[i] ] + 0x5A827999; 241 | break; 242 | default: 243 | /* round 3 */ 244 | /* The value 0x6ED9EBA1 is trunc( (2^30) * sqrt(3) ). */ 245 | a = md4H( b, c, d ) + X[ Round3_k[i] ] + 0x6ED9EBA1; 246 | break; 247 | } 248 | a = 0xFFFFFFFF & ( ABCD[j] + a ); 249 | ABCD[j] = 0xFFFFFFFF & (( a << s ) | ( a >> (32 - s) )); 250 | } 251 | } 252 | 253 | /* Use the stored original A, B, C, D values to perform 254 | * one last convolution. 255 | */ 256 | for( i = 0; i < 4; i++ ) 257 | ABCD[i] = 0xFFFFFFFF & ( ABCD[i] + KeepABCD[i] ); 258 | 259 | } /* Permute */ 260 | 261 | 262 | /* -------------------------------------------------------------------------- ** 263 | * Functions: 264 | */ 265 | 266 | uchar *auth_md4Sum( uchar *dst, const uchar *src, const int srclen ) 267 | /* ------------------------------------------------------------------------ ** 268 | * Compute an MD4 message digest. 269 | * 270 | * Input: dst - Destination buffer into which the result will be 271 | * written. Must be 16 bytes long. 272 | * src - Source data block to be MD4'd. 273 | * srclen - The length, in bytes, of the source block. 274 | * (Note that the length is given in bytes, not bits.) 275 | * 276 | * Output: A pointer to a 16-byte array of unsigned characters which 277 | * contains the calculated MD4 message digest. (Same as ). 278 | * 279 | * Notes: This implementation takes only a single block. It does not 280 | * create or keep context, so there is no way to perform the MD4 281 | * over multiple blocks of data. This is okay for SMB, because 282 | * the MD4 algorithm is used only in creating the NTLM hash and 283 | * the NTLM Session Key, both of which have single-chunk input. 284 | * 285 | * The MD4 algorithm is designed to work on data with of 286 | * arbitrary *bit* length. Most implementations, this one 287 | * included, handle the input data in byte-sized chunks. 288 | * 289 | * The MD4 algorithm does much of its work using four-byte 290 | * words, and so can be tuned for speed based on the endian-ness 291 | * of the host. This implementation is intended to be 292 | * endian-neutral, which may make it a teeny bit slower than 293 | * others. 294 | * 295 | * ------------------------------------------------------------------------ ** 296 | */ 297 | { 298 | uchar block[64]; /* Scratch space. */ 299 | int i; 300 | uint32_t l; 301 | uint32_t m; 302 | uint32_t len = (uint32_t)srclen; 303 | uint32_t ABCD[4] = /* ABCD[] contains the four 4-byte "registers" that are */ 304 | { /* manipulated to produce the MD4 digest. The input */ 305 | 0x67452301, /* () acts upon the registers, not the other way */ 306 | 0xefcdab89, /* 'round. The initial values are those given in RFC */ 307 | 0x98badcfe, /* 1320 (pg.3). Note, however, that RFC 1320 provides */ 308 | 0x10325476 /* these values as bytes, not as longwords, and the */ 309 | }; /* bytes are arranged in little-endian order as if they */ 310 | /* were the bytes of (little endian) 32-bit ints. */ 311 | /* That's confusing as all getout. */ 312 | /* The values given here are provided as 32-bit values */ 313 | /* in C language format, so they are endian-agnostic. */ 314 | 315 | /* MD4 takes the input in 64-byte chunks and uses each chunk to drive the 316 | * manglement of the data in the ABCD[] array. So... 317 | * Figure out how many complete 64-byte chunks there are in and send 318 | * each to the Permute() function. Note that if the input is shorter than 319 | * 64-bytes (as is the case with things like passwords) then we'll wind up 320 | * skipping this loop. 321 | */ 322 | m = len & ~(uint32_t)63; /* Number of bytes in whole 64-byte chunks. */ 323 | for( l = 0; l < m; l += 64 ) 324 | { 325 | Permute( ABCD, &src[l] ); 326 | } 327 | 328 | /* Copy the remainder of the bytes into . 329 | * The byte of immediately following the end of the copied 330 | * bytes will be set to 0x80. Fill the rest of with zeros. 331 | */ 332 | for( i = 0; i < 64; i++, l++ ) 333 | { 334 | if( l < len ) 335 | block[i] = src[l]; /* Copy bytes from to block. */ 336 | else 337 | { 338 | if( l == len ) 339 | block[i] = 0x80; /* We've written len+1 bytes here. */ 340 | else /* l > len */ 341 | block[i] = 0; /* Filling the tail-end of block with zeros. */ 342 | } 343 | } 344 | 345 | /* If there is *no room* for the 8-byte length field, then we MD4 this 346 | * sub-block, clear it out again, and add the length field later. If 347 | * there *is* room for the length, then we skip this step so that the 348 | * length is added to the current sub-block. 349 | */ 350 | if( (l - len) <= 8 ) /* Must be '<=' because we wrote len+1 bytes */ 351 | { 352 | Permute( ABCD, block ); 353 | for( i = 0; i < 64; i++ ) /* Re-clear the buffer. */ 354 | block[i] = 0; 355 | } 356 | 357 | /* Write the length (in bits--which is * 8) to the length field. 358 | * The length field is an 8-byte, little-endian value. 359 | * First we multiply the length by 8 (shift 3) to get a bit count. 360 | * Then copy the calculated bitlength, in little-endian order, to the 361 | * length field in the block. Just to be sure, grab the tree high-order 362 | * bits of and copy those as well (only necessary for very large 363 | * blocks). Finally, perform the last Permutation on the sub-block. 364 | */ 365 | l = len << 3; 366 | for( i = 0; i < 4; i++ ) 367 | block[56+i] |= GetLongByte( l, i ); 368 | block[60] = ((GetLongByte( len, 3 ) & 0xE0) >> 5); /* 'len' is not a typo. */ 369 | Permute( ABCD, block ); 370 | 371 | /* Now copy the result into the output buffer and we're done. 372 | */ 373 | for( i = 0; i < 4; i++ ) 374 | { 375 | dst[ 0+i] = GetLongByte( ABCD[0], i ); 376 | dst[ 4+i] = GetLongByte( ABCD[1], i ); 377 | dst[ 8+i] = GetLongByte( ABCD[2], i ); 378 | dst[12+i] = GetLongByte( ABCD[3], i ); 379 | } 380 | 381 | return( dst ); 382 | } /* auth_md4Sum */ 383 | 384 | 385 | /* ========================================================================== */ 386 | -------------------------------------------------------------------------------- /md4.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTH_MD4_H 2 | #define AUTH_MD4_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* ========================================================================== ** 9 | * 10 | * MD4.h 11 | * 12 | * Copyright: 13 | * Copyright (C) 2003-2005 by Christopher R. Hertel 14 | * 15 | * Email: crh@ubiqx.mn.org 16 | * 17 | * $Id: MD4.h,v 0.6 2005/06/08 18:35:05 crh Exp $ 18 | * 19 | * -------------------------------------------------------------------------- ** 20 | * 21 | * Description: 22 | * Implements the MD4 hash algorithm, as described in RFC 1320. 23 | * 24 | * -------------------------------------------------------------------------- ** 25 | * 26 | * License: 27 | * 28 | * This library is free software; you can redistribute it and/or 29 | * modify it under the terms of the GNU Lesser General Public 30 | * License as published by the Free Software Foundation; either 31 | * version 2.1 of the License, or (at your option) any later version. 32 | * 33 | * This library is distributed in the hope that it will be useful, 34 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 36 | * Lesser General Public License for more details. 37 | * 38 | * You should have received a copy of the GNU Lesser General Public 39 | * License along with this library; if not, write to the Free Software 40 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 41 | * 42 | * -------------------------------------------------------------------------- ** 43 | * 44 | * Notes: 45 | * 46 | * None of this will make any sense unless you're studying RFC 1320 as you 47 | * read the code. 48 | * 49 | * MD4 is described in RFC 1320 (which superceeds RFC 1186). 50 | * The MD*5* algorithm is described in RFC 1321 (that's 1320 + 1). 51 | * MD4 is very similar to MD5, but not quite similar enough to justify 52 | * putting the two into a single module. 53 | * 54 | * There are three primary motivations for this particular implementation. 55 | * 1) Programmer's pride. I wanted to be able to say I'd done it, and I 56 | * wanted to learn from the experience. 57 | * 2) Portability. I wanted an implementation that I knew to be portable 58 | * to a reasonable number of platforms. In particular, the algorithm 59 | * is designed with little-endian platforms in mind, and I wanted an 60 | * endian-agnostic implementation. 61 | * 3) Compactness. While not an overriding goal, I thought it worth-while 62 | * to see if I could reduce the overall size of the result. This is in 63 | * keeping with my hopes that this library will be suitable for use in 64 | * some embedded environments. 65 | * Beyond that, cleanliness and clarity are always worth pursuing. 66 | * 67 | * As mentioned above, the code really only makes sense if you are familiar 68 | * with the MD4 algorithm or are using RFC 1320 as a guide. This code is 69 | * quirky, however, so you'll want to be reading carefully. 70 | * 71 | * -------------------------------------------------------------------------- ** 72 | * 73 | * References: 74 | * IETF RFC 1320: The MD4 Message-Digest Algorithm 75 | * Ron Rivest. IETF, April, 1992 76 | * 77 | * ========================================================================== ** 78 | */ 79 | 80 | //#include "auth_common.h" 81 | 82 | 83 | /* -------------------------------------------------------------------------- ** 84 | * Functions: 85 | */ 86 | 87 | typedef unsigned char uchar; 88 | 89 | uchar *auth_md4Sum( uchar *dst, const uchar *src, const int srclen ); 90 | /* ------------------------------------------------------------------------ ** 91 | * Compute an MD4 message digest. 92 | * 93 | * Input: dst - Destination buffer into which the result will be 94 | * written. Must be 16 bytes long. 95 | * src - Source data block to be MD4'd. 96 | * srclen - The length, in bytes, of the source block. 97 | * (Note that the length is given in bytes, not bits.) 98 | * 99 | * Output: A pointer to a 16-byte array of unsigned characters which 100 | * contains the calculated MD4 message digest. (Same as ). 101 | * 102 | * Notes: This implementation takes only a single block. It does not 103 | * create or keep context, so there is no way to perform the MD4 104 | * over multiple blocks of data. This is okay for SMB, because 105 | * the MD4 algorithm is used only in creating the NTLM hash and 106 | * the NTLM Session Key, both of which have single-chunk input. 107 | * 108 | * The MD4 algorithm is designed to work on data with of 109 | * arbitrary *bit* length. Most implementations, this one 110 | * included, handle the input data in byte-sized chunks. 111 | * 112 | * The MD4 algorithm does much of its work using four-byte 113 | * words, and so can be tuned for speed based on the endian-ness 114 | * of the host. This implementation is intended to be 115 | * endian-neutral, which may make it a teeny bit slower than 116 | * others. 117 | * 118 | * ------------------------------------------------------------------------ ** 119 | */ 120 | 121 | #ifdef __cplusplus 122 | } 123 | #endif 124 | 125 | /* ========================================================================== */ 126 | #endif /* AUTH_MD4_H */ 127 | -------------------------------------------------------------------------------- /sha1.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha1.c 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the SHA1 hashing algorithm. 7 | Algorithm specification can be found here: 8 | * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf 9 | This implementation uses little endian byte order. 10 | *********************************************************************/ 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | //#include 15 | #include "sha1.h" 16 | 17 | /****************************** MACROS ******************************/ 18 | #define ROTLEFT(a, b) ((a << b) | (a >> (32 - b))) 19 | 20 | /*********************** FUNCTION DEFINITIONS ***********************/ 21 | void sha1_transform(SHA1_CTX *ctx, const BYTE data[]) 22 | { 23 | WORD a, b, c, d, e, i, j, t, m[80]; 24 | 25 | for (i = 0, j = 0; i < 16; ++i, j += 4) 26 | m[i] = (data[j] << 24) + (data[j + 1] << 16) + (data[j + 2] << 8) + (data[j + 3]); 27 | for ( ; i < 80; ++i) { 28 | m[i] = (m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]); 29 | m[i] = (m[i] << 1) | (m[i] >> 31); 30 | } 31 | 32 | a = ctx->state[0]; 33 | b = ctx->state[1]; 34 | c = ctx->state[2]; 35 | d = ctx->state[3]; 36 | e = ctx->state[4]; 37 | 38 | for (i = 0; i < 20; ++i) { 39 | t = ROTLEFT(a, 5) + ((b & c) ^ (~b & d)) + e + ctx->k[0] + m[i]; 40 | e = d; 41 | d = c; 42 | c = ROTLEFT(b, 30); 43 | b = a; 44 | a = t; 45 | } 46 | for ( ; i < 40; ++i) { 47 | t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[1] + m[i]; 48 | e = d; 49 | d = c; 50 | c = ROTLEFT(b, 30); 51 | b = a; 52 | a = t; 53 | } 54 | for ( ; i < 60; ++i) { 55 | t = ROTLEFT(a, 5) + ((b & c) ^ (b & d) ^ (c & d)) + e + ctx->k[2] + m[i]; 56 | e = d; 57 | d = c; 58 | c = ROTLEFT(b, 30); 59 | b = a; 60 | a = t; 61 | } 62 | for ( ; i < 80; ++i) { 63 | t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[3] + m[i]; 64 | e = d; 65 | d = c; 66 | c = ROTLEFT(b, 30); 67 | b = a; 68 | a = t; 69 | } 70 | 71 | ctx->state[0] += a; 72 | ctx->state[1] += b; 73 | ctx->state[2] += c; 74 | ctx->state[3] += d; 75 | ctx->state[4] += e; 76 | } 77 | 78 | void sha1_init(SHA1_CTX *ctx) 79 | { 80 | ctx->datalen = 0; 81 | ctx->bitlen = 0; 82 | ctx->state[0] = 0x67452301; 83 | ctx->state[1] = 0xEFCDAB89; 84 | ctx->state[2] = 0x98BADCFE; 85 | ctx->state[3] = 0x10325476; 86 | ctx->state[4] = 0xc3d2e1f0; 87 | ctx->k[0] = 0x5a827999; 88 | ctx->k[1] = 0x6ed9eba1; 89 | ctx->k[2] = 0x8f1bbcdc; 90 | ctx->k[3] = 0xca62c1d6; 91 | } 92 | 93 | void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len) 94 | { 95 | size_t i; 96 | 97 | for (i = 0; i < len; ++i) { 98 | ctx->data[ctx->datalen] = data[i]; 99 | ctx->datalen++; 100 | if (ctx->datalen == 64) { 101 | sha1_transform(ctx, ctx->data); 102 | ctx->bitlen += 512; 103 | ctx->datalen = 0; 104 | } 105 | } 106 | } 107 | 108 | void sha1_final(SHA1_CTX *ctx, BYTE hash[]) 109 | { 110 | WORD i; 111 | 112 | i = ctx->datalen; 113 | 114 | // Pad whatever data is left in the buffer. 115 | if (ctx->datalen < 56) { 116 | ctx->data[i++] = 0x80; 117 | while (i < 56) 118 | ctx->data[i++] = 0x00; 119 | } 120 | else { 121 | ctx->data[i++] = 0x80; 122 | while (i < 64) 123 | ctx->data[i++] = 0x00; 124 | sha1_transform(ctx, ctx->data); 125 | memset(ctx->data, 0, 56); 126 | } 127 | 128 | // Append to the padding the total message's length in bits and transform. 129 | ctx->bitlen += ctx->datalen * 8; 130 | ctx->data[63] = ctx->bitlen; 131 | ctx->data[62] = ctx->bitlen >> 8; 132 | ctx->data[61] = ctx->bitlen >> 16; 133 | ctx->data[60] = ctx->bitlen >> 24; 134 | ctx->data[59] = ctx->bitlen >> 32; 135 | ctx->data[58] = ctx->bitlen >> 40; 136 | ctx->data[57] = ctx->bitlen >> 48; 137 | ctx->data[56] = ctx->bitlen >> 56; 138 | sha1_transform(ctx, ctx->data); 139 | 140 | // Since this implementation uses little endian byte ordering and MD uses big endian, 141 | // reverse all the bytes when copying the final state to the output hash. 142 | for (i = 0; i < 4; ++i) { 143 | hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; 144 | hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; 145 | hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; 146 | hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; 147 | hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /sha1.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha1.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA1_H 10 | #define SHA1_H 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /*************************** HEADER FILES ***************************/ 17 | #include 18 | 19 | /****************************** MACROS ******************************/ 20 | #define SHA1_BLOCK_SIZE 20 // SHA1 outputs a 20 byte digest 21 | 22 | /**************************** DATA TYPES ****************************/ 23 | typedef unsigned char BYTE; // 8-bit byte 24 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines 25 | 26 | typedef struct { 27 | BYTE data[64]; 28 | WORD datalen; 29 | unsigned long long bitlen; 30 | WORD state[5]; 31 | WORD k[4]; 32 | } SHA1_CTX; 33 | 34 | /*********************** FUNCTION DECLARATIONS **********************/ 35 | void sha1_init(SHA1_CTX *ctx); 36 | void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len); 37 | void sha1_final(SHA1_CTX *ctx, BYTE hash[]); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | #endif // SHA1_H 43 | --------------------------------------------------------------------------------